├── .editorconfig
├── .env.example
├── .eslintignore
├── .eslintrc.json
├── .github
├── logo.png
├── orders-db-model.png
├── student-classes-db-model.png
└── typeorm_relations.json
├── .gitignore
├── .huskyrc.json
├── .lintstagedrc.json
├── CONTRIBUTING.md
├── README.md
├── commitlint.config.js
├── docker-compose.yml
├── jest.config.js
├── ormconfig.json
├── package.json
├── prettier.config.js
├── src
├── __tests__
│ └── App.spec.ts
├── modules
│ ├── classes
│ │ ├── dtos
│ │ │ └── ICreateClassDTO.ts
│ │ ├── infra
│ │ │ ├── http
│ │ │ │ ├── controllers
│ │ │ │ │ └── ClassesController.ts
│ │ │ │ └── routes
│ │ │ │ │ └── classes.routes.ts
│ │ │ ├── typeorm
│ │ │ │ ├── entities
│ │ │ │ │ └── Class.ts
│ │ │ │ └── repositories
│ │ │ │ │ └── ClassesRepository.ts
│ │ │ └── validators
│ │ │ │ └── createClass.ts
│ │ ├── repositories
│ │ │ └── IClassesRepository.ts
│ │ └── services
│ │ │ └── CreateClassService.ts
│ ├── customers
│ │ ├── dtos
│ │ │ └── ICreateCustomerDTO.ts
│ │ ├── infra
│ │ │ ├── http
│ │ │ │ ├── controller
│ │ │ │ │ └── CustomersController.ts
│ │ │ │ └── routes
│ │ │ │ │ └── customers.routes.ts
│ │ │ ├── typeorm
│ │ │ │ ├── entities
│ │ │ │ │ └── Customer.ts
│ │ │ │ └── repositories
│ │ │ │ │ └── CustomersRepository.ts
│ │ │ └── validators
│ │ │ │ └── createCustomer.ts
│ │ ├── repositories
│ │ │ └── ICustomersRepository.ts
│ │ └── services
│ │ │ └── CreateCustomerService.ts
│ ├── orders
│ │ ├── dtos
│ │ │ └── ICreateOrderDTO.ts
│ │ ├── infra
│ │ │ ├── http
│ │ │ │ ├── controller
│ │ │ │ │ └── OrdersController.ts
│ │ │ │ └── routes
│ │ │ │ │ └── orders.routes.ts
│ │ │ ├── typeorm
│ │ │ │ ├── entities
│ │ │ │ │ ├── Order.ts
│ │ │ │ │ └── OrdersProducts.ts
│ │ │ │ └── repositories
│ │ │ │ │ └── OrdersRepository.ts
│ │ │ └── validators
│ │ │ │ ├── createOrder.ts
│ │ │ │ └── showOrder.ts
│ │ ├── repositories
│ │ │ └── IOrdersRepository.ts
│ │ └── services
│ │ │ ├── CreateOrderService.ts
│ │ │ └── FindOrderService.ts
│ ├── products
│ │ ├── dtos
│ │ │ ├── ICreateProductDTO.ts
│ │ │ └── IUpdateProductsQuantityDTO.ts
│ │ ├── infra
│ │ │ ├── http
│ │ │ │ ├── controller
│ │ │ │ │ └── ProductsController.ts
│ │ │ │ └── routes
│ │ │ │ │ └── products.routes.ts
│ │ │ ├── typeorm
│ │ │ │ ├── entities
│ │ │ │ │ └── Product.ts
│ │ │ │ └── repositories
│ │ │ │ │ └── ProductsRepository.ts
│ │ │ └── validators
│ │ │ │ └── createProduct.ts
│ │ ├── repositories
│ │ │ └── IProductsRepository.ts
│ │ └── services
│ │ │ └── CreateProductService.ts
│ ├── students
│ │ ├── dtos
│ │ │ └── ICreateStudentDTO.ts
│ │ ├── infra
│ │ │ ├── http
│ │ │ │ ├── controllers
│ │ │ │ │ └── StudentsController.ts
│ │ │ │ └── routes
│ │ │ │ │ └── students.routes.ts
│ │ │ └── typeorm
│ │ │ │ ├── entities
│ │ │ │ ├── Student.ts
│ │ │ │ └── StudentClasses.ts
│ │ │ │ └── repositories
│ │ │ │ └── StudentsRepository.ts
│ │ ├── repositories
│ │ │ └── IStudentsRepository.ts
│ │ ├── services
│ │ │ └── CreateStudentService.ts
│ │ └── validators
│ │ │ └── createStudent.ts
│ └── teachers
│ │ ├── dtos
│ │ └── ICreateTeacherDTO.ts
│ │ ├── infra
│ │ ├── http
│ │ │ ├── controllers
│ │ │ │ └── TeachersController.ts
│ │ │ └── routes
│ │ │ │ └── teachers.routes.ts
│ │ └── typeorm
│ │ │ ├── entities
│ │ │ └── Teacher.ts
│ │ │ └── repositories
│ │ │ └── TeachersRepository.ts
│ │ ├── repositories
│ │ └── ITeachersRepository.ts
│ │ ├── services
│ │ └── CreateTeacherService.ts
│ │ └── validators
│ │ └── createTeacher.ts
└── shared
│ ├── container
│ └── index.ts
│ ├── errors
│ └── AppError.ts
│ ├── handlers
│ └── errorsHandler.ts
│ └── infra
│ ├── http
│ ├── app.ts
│ ├── routes
│ │ └── index.ts
│ └── server.ts
│ └── typeorm
│ ├── index.ts
│ └── migrations
│ ├── 1598266682467-create_customers_table.ts
│ ├── 1598269989216-CreateProductTable.ts
│ ├── 1598279750914-CreateOrderTable.ts
│ ├── 1598353275953-CreateOrderProductsTable.ts
│ ├── 1598359381489-UpdateOrdersProductsIdColumn.ts
│ ├── 1598364937210-UpdatePricePrecisions.ts
│ ├── 1598871336598-Student.ts
│ ├── 1598874390567-Teacher.ts
│ ├── 1598879822720-Classes.ts
│ ├── 1598883848517-AddNowDefaultToTimestampColumns.ts
│ └── 1598885739547-StudentClasses.ts
├── tsconfig.json
└── yarn.lock
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | indent_style = space
5 | indent_size = 2
6 | charset = utf-8
7 | trim_trailing_whitespace = true
8 | insert_final_newline = true
9 | end_of_line = lf
10 |
--------------------------------------------------------------------------------
/.env.example:
--------------------------------------------------------------------------------
1 | # Database
2 | POSTGRES_USER=
3 | POSTGRES_PASSWORD=
4 | POSTGRES_DATABASE=
5 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /*.js
2 | node_modules
3 | dist
--------------------------------------------------------------------------------
/.eslintrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "es6": true,
4 | "node": true,
5 | "jest": true
6 | },
7 | "extends": [
8 | "airbnb-base",
9 | "plugin:@typescript-eslint/recommended",
10 | "prettier/@typescript-eslint",
11 | "plugin:prettier/recommended"
12 | ],
13 | "globals": {
14 | "Atomics": "readonly",
15 | "SharedArrayBuffer": "readonly"
16 | },
17 | "parser": "@typescript-eslint/parser",
18 | "parserOptions": {
19 | "ecmaVersion": 2018,
20 | "sourceType": "module"
21 | },
22 | "plugins": ["@typescript-eslint", "prettier"],
23 | "rules": {
24 | "@typescript-eslint/no-unused-vars": [
25 | "error",
26 | {
27 | "argsIgnorePattern": "_"
28 | }
29 | ],
30 | "no-useless-constructor": "off",
31 | "@typescript-eslint/interface-name-prefix": [
32 | "error",
33 | {
34 | "prefixWithI": "always"
35 | }
36 | ],
37 | "@typescript-eslint/camelcase": "off",
38 | "prettier/prettier": "error",
39 | "class-methods-use-this": "off",
40 | "import/extensions": [
41 | "error",
42 | "ignorePackages",
43 | {
44 | "ts": "never"
45 | }
46 | ]
47 | },
48 | "settings": {
49 | "import/resolver": {
50 | "typescript": {}
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/.github/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LauraBeatris/typeorm-relations/4a180848f4912565040673a0e2571a152eb59f30/.github/logo.png
--------------------------------------------------------------------------------
/.github/orders-db-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LauraBeatris/typeorm-relations/4a180848f4912565040673a0e2571a152eb59f30/.github/orders-db-model.png
--------------------------------------------------------------------------------
/.github/student-classes-db-model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LauraBeatris/typeorm-relations/4a180848f4912565040673a0e2571a152eb59f30/.github/student-classes-db-model.png
--------------------------------------------------------------------------------
/.github/typeorm_relations.json:
--------------------------------------------------------------------------------
1 | {"_type":"export","__export_format":4,"__export_date":"2020-09-01T09:59:12.641Z","__export_source":"insomnia.desktop.app:v2020.3.3","resources":[{"_id":"req_c40f21fd833e41af9e6e7b55abb798e4","authentication":{},"body":{},"created":1590028776677,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1590028776677,"method":"GET","modified":1590354161030,"name":"List","parameters":[],"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/pokemons","_type":"request"},{"_id":"wrk_59ee7513e0524a20b7d12c2e771000a0","created":1590028718790,"description":"","modified":1590028718790,"name":"Deno Pokemons","parentId":null,"scope":null,"_type":"workspace"},{"_id":"req_4ab060866eb148758a3079ff5c4aa1e8","authentication":{},"body":{},"created":1590030184342,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1589888223169,"method":"GET","modified":1590405256595,"name":"Show","parameters":[],"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/pokemons/ss","_type":"request"},{"_id":"req_f4eae23412ee4f6aa653b97406c31bf7","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Piplup\",\n\t\"age\": 5, \n\t\"abilities\": [\"Water Canon\"]\n}"},"created":1590031301331,"description":"","headers":[{"id":"pair_23259700e097462f819daaa5421812cb","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1589817946415,"method":"POST","modified":1590061336895,"name":"Create","parameters":[],"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/pokemons","_type":"request"},{"_id":"req_fffa830eb27d4a558289bc2e9555523e","authentication":{},"body":{},"created":1590060742690,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1589853084792,"method":"DELETE","modified":1590060750555,"name":"Delete","parameters":[],"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/pokemons/pikachu","_type":"request"},{"_id":"req_3444eeb90193473189c5fd31f0832ff5","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"votes\": 7\n}"},"created":1590060846976,"description":"","headers":[{"id":"pair_23259700e097462f819daaa5421812cb","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1589782808038,"method":"PATCH","modified":1590089168275,"name":"Update","parameters":[],"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/projects/skylab","_type":"request"},{"_id":"req_e933513929ac42e8a56b6e6c9f8f6ecc","authentication":{},"body":{},"created":1590062056784,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1590062056784,"method":"GET","modified":1590062503901,"name":"List games","parameters":[],"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/pokemons/generations","_type":"request"},{"_id":"req_a0d8dc9ecc004126aca03c6f7fe62a4d","authentication":{"disabled":false,"token":"{{ provider_token }}","type":"bearer"},"body":{},"created":1587820160688,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1587820160688,"method":"GET","modified":1598117386938,"name":"List","parameters":[],"parentId":"fld_022c75daecbe497e8bffe850a6959d1a","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/appointments","_type":"request"},{"_id":"fld_022c75daecbe497e8bffe850a6959d1a","created":1587819040123,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1587819040123,"modified":1587819040123,"name":"Appointments","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"request_group"},{"_id":"wrk_113da797c6ae4cbd98178de7cb975733","created":1587818789248,"description":"","modified":1587818789248,"name":"Hotseat","parentId":null,"scope":null,"_type":"workspace"},{"_id":"req_c6a637c219b9439caa156f86091d6765","authentication":{"disabled":false,"token":"{{ customer_token }}","type":"bearer"},"body":{"mimeType":"application/json","text":"{\n\t\"provider_id\": \"{{ provider_id }}\",\n\t\"date\": \"2020-08-22 15:00\",\n\t\"type\": \"HAIR_CARE\"\n}"},"created":1587820478406,"description":"","headers":[{"id":"pair_3a329dbf05a34d4089be18e851030e10","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1587820160638,"method":"POST","modified":1598118356550,"name":"Create","parameters":[],"parentId":"fld_022c75daecbe497e8bffe850a6959d1a","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/appointments","_type":"request"},{"_id":"req_1299437331d0431bbdd0e2edc19030e2","authentication":{"token":"{{ provider_token }}","type":"bearer"},"body":{},"created":1596629769063,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1587820160738,"method":"GET","modified":1598118083962,"name":"List Provider Appointments","parameters":[{"description":"","id":"pair_330730d4440e4e658e4f43989cee837e","name":"day","value":"22"},{"description":"","id":"pair_73bdc4aa8d0a45bf96a9881e5a282501","name":"month","value":"8"},{"description":"","id":"pair_fb0d62fe1fb84391841ded24ac1fab9c","name":"year","value":"2020"}],"parentId":"fld_022c75daecbe497e8bffe850a6959d1a","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/appointments/me","_type":"request"},{"_id":"req_4a12cb990e494bc182432a49411d6d1d","authentication":{"token":"{{ token }}","type":"bearer"},"body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Prestador\", \n\t\"email\": \"prestador4@gmail.com\", \n\t\"password\": \"12345\",\n\t\"is_provider\": true\n}"},"created":1589648617598,"description":"","headers":[{"id":"pair_ed84ece388ea4d62b13bd105975ef2dd","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1589648617598,"method":"POST","modified":1597928019246,"name":"Create","parameters":[],"parentId":"fld_bd5425e38b3648a4ab9ae07b12f1de3f","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/users","_type":"request"},{"_id":"fld_bd5425e38b3648a4ab9ae07b12f1de3f","created":1589648614573,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1589648614573,"modified":1589648614573,"name":"Users","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"request_group"},{"_id":"req_bc323c4164a7414da3678a61ff5aa2b8","authentication":{"disabled":false,"token":"{{ customer_token }}","type":"bearer"},"body":{"mimeType":"multipart/form-data","params":[{"description":"","fileName":"/Users/lauraserafim/Downloads/share.png","id":"pair_a90ed8670890425f9ffa3f529141b7fe","name":"avatar","type":"file","value":""}]},"created":1589683104277,"description":"","headers":[{"description":"","id":"pair_5f6661ccfbea473a9f6932cf776922fb","name":"authentication","value":"{{ customer_token }}"},{"description":"","id":"pair_fc16dda8cc3a476e90e37e51fb5bc110","name":"Content-Type","value":"multipart/form-data"}],"isPrivate":false,"metaSortKey":-1589683104277,"method":"PATCH","modified":1598219948246,"name":"Update Avatar","parameters":[],"parentId":"fld_bd5425e38b3648a4ab9ae07b12f1de3f","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/users/avatar","_type":"request"},{"_id":"req_8540b9e829e344f6a3ddf29b89015797","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"email\": \"provedor@gmail.com\", \n\t\"password\": \"12345\"\n}"},"created":1589677530264,"description":"","headers":[{"id":"pair_0be95894caab4125a1ceddc0919ab650","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1589677530264,"method":"POST","modified":1596998822516,"name":"Create","parameters":[],"parentId":"fld_346bf953ec0b4c04a08739108bba32ef","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/sessions","_type":"request"},{"_id":"fld_346bf953ec0b4c04a08739108bba32ef","created":1589677525446,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1589677525446,"modified":1589677525446,"name":"Sessions","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"request_group"},{"_id":"req_afc90efda4f84366b47c34beee2725ad","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"token\": \"8b3f6c48-b49f-49a9-ab46-08b77d0935d0\",\n\t\"password\": \"123123\"\n}"},"created":1593912907107,"description":"","headers":[{"id":"pair_e35f381696da41c9af9d75da547124cc","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1593912907107,"method":"PATCH","modified":1596998348536,"name":"Reset","parameters":[],"parentId":"fld_b349c9476031471290f215c039787af9","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/password/reset","_type":"request"},{"_id":"fld_b349c9476031471290f215c039787af9","created":1593912899685,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1593912899685,"modified":1593912899685,"name":"Password ","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"request_group"},{"_id":"req_0bc924075209458fb3908da6ab63124d","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"email\": \"laurabeatriserafim@gmail.com\"\n}"},"created":1593912925140,"description":"","headers":[{"id":"pair_d06fd324ba7a43de94c1c4c1e3f769ea","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1593912925140,"method":"POST","modified":1597661584645,"name":"Send Recover Mail","parameters":[],"parentId":"fld_b349c9476031471290f215c039787af9","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/password/recover-request","_type":"request"},{"_id":"req_2bdf4618323a403d9b4d0036bb278213","authentication":{"token":"{{ customer_token }}","type":"bearer"},"body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Batatinha\", \t\n\t\"email\": \"batatinhaassada3@gmail.com\", \n\t\"password\": \"123456\",\n\t\"old_password\": \"123456\",\n\t\"password_confirmation\": \"123456\"\n}"},"created":1594478198117,"description":"","headers":[{"id":"pair_eaa1f0be1d5c4a45872192840f774f96","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1594478198117,"method":"PUT","modified":1596997670998,"name":"Update","parameters":[],"parentId":"fld_ce054115ae1148b683e28d367271dd75","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/profile ","_type":"request"},{"_id":"fld_ce054115ae1148b683e28d367271dd75","created":1594478193192,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1594478193192,"modified":1594478193192,"name":"Profile","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"request_group"},{"_id":"req_3a9141b2dabb45d7ab885fb5cfae5cd3","authentication":{"token":"{{ customer_token }}","type":"bearer"},"body":{},"created":1594480430816,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1594195561628.5,"method":"GET","modified":1596991795218,"name":"Show ","parameters":[],"parentId":"fld_ce054115ae1148b683e28d367271dd75","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/profile ","_type":"request"},{"_id":"req_2d825046ee4344e486f2c26ba21c9080","authentication":{"token":"{{ customer_token }}","type":"bearer"},"body":{},"created":1594501363288,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1594501363288,"method":"GET","modified":1597923051468,"name":"List Providers","parameters":[],"parentId":"fld_9653fbef6dc34ef894ee99aa8e69d0ef","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/providers","_type":"request"},{"_id":"fld_9653fbef6dc34ef894ee99aa8e69d0ef","created":1594501343256,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1594501343256,"modified":1594501343256,"name":"Providers","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"request_group"},{"_id":"req_bae73635c2c4435fae223f9582370008","authentication":{"token":"{{ customer_token }}","type":"bearer"},"body":{},"created":1594575717790,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1594489780702.5,"method":"GET","modified":1596996712045,"name":"Month Availability","parameters":[{"description":"","id":"pair_2df0bce30a46481d9bd6ece9ffa5f516","name":"month","value":"7"},{"description":"","id":"pair_598ffd6d17e245f6a01b4965439e0094","name":"year","value":"2020"}],"parentId":"fld_9653fbef6dc34ef894ee99aa8e69d0ef","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/providers/{{ provider_id }}/month-availability","_type":"request"},{"_id":"req_76022bac001b4266b7fb830303104300","authentication":{"token":"{{ customer_token }}","type":"bearer"},"body":{},"created":1594575719993,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1594483989409.75,"method":"GET","modified":1598118324237,"name":"Day Availability","parameters":[{"description":"","id":"pair_7699c51149a4410e872b0d074329720b","name":"month","value":"8"},{"description":"","id":"pair_3866b956f53042e6a19824191022a154","name":"year","value":"2020"},{"description":"","id":"pair_bcbeb46c08de4ab0847552f0a9d2fb7a","name":"day","value":"22"}],"parentId":"fld_9653fbef6dc34ef894ee99aa8e69d0ef","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/providers/{{ provider_id }}/day-availability","_type":"request"},{"_id":"req_46bd61688e654c4692e9e522a35d41de","authentication":{},"body":{},"created":1589282680911,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1589747669661,"method":"GET","modified":1590853347725,"name":"List","parameters":[{"description":"","id":"pair_472d3c1b357b4b3f9c65396e207341bd","name":"name","value":"test"}],"parentId":"fld_5e4cc5fff675449f940846f6d46d7a79","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/{{ resource }}","_type":"request"},{"_id":"fld_5e4cc5fff675449f940846f6d46d7a79","created":1589744770099,"description":"","environment":{"resource":"transactions"},"environmentPropertyOrder":{"&":["resource"]},"metaSortKey":-1589744770099,"modified":1590850460129,"name":"Transactions","parentId":"wrk_2bf4e9f997334a6b90b9943333a3c6d0","_type":"request_group"},{"_id":"wrk_2bf4e9f997334a6b90b9943333a3c6d0","created":1589282633836,"description":"","modified":1589734201881,"name":"GoFinances","parentId":null,"scope":null,"_type":"workspace"},{"_id":"req_1bdd39fd61184621bce47185cc307edb","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"title\": \"Spotify\",\n\t\"type\": \"{% prompt 'Type', 'What is the transaction type?', 'income', 'type', false, true %}\",\n\t\"value\": {% prompt 'value', 'What is the transaction value?', '0', 'value', false, true %},\n\t\"category\": \"{% prompt 'What is the transaction category?', 'Category', '\"general\"', 'category', false, true %}\"\n}"},"created":1589282730719,"description":"","headers":[{"id":"pair_a5d5b26794224aeb997e8382b3292741","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1589282680961,"method":"POST","modified":1590855372789,"name":"Create","parameters":[],"parentId":"fld_5e4cc5fff675449f940846f6d46d7a79","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/{{ resource }}","_type":"request"},{"_id":"req_1f3cf9cb979c43ada4cfa81f1568c133","authentication":{},"body":{},"created":1589744789396,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1589282680936,"method":"DELETE","modified":1590852911044,"name":"Delete","parameters":[],"parentId":"fld_5e4cc5fff675449f940846f6d46d7a79","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/{{ resource }}/{% response 'body', 'req_1bdd39fd61184621bce47185cc307edb', 'b64::JC5pZA==::46b', 'always' %}","_type":"request"},{"_id":"req_e027e259a8284d59b4183dcd922d8c0a","authentication":{},"body":{"mimeType":"multipart/form-data","params":[{"description":"","fileName":"/Users/lauraserafim/Desktop/file.csv","id":"pair_6256c002ee0a4ec783ac76d8874217e5","name":"file","type":"file","value":""},{"description":"","id":"pair_cb50f32f0b2843bf83de1e548d2716ef","name":"","value":""},{"description":"","id":"pair_079068a4ef884cb395ff0dbdf4143497","name":"","value":""}]},"created":1589747669611,"description":"","headers":[{"id":"pair_88fe46261893473d9d84d0c3bc37e1f0","name":"Content-Type","value":"multipart/form-data"}],"isPrivate":false,"metaSortKey":-1589747669611,"method":"POST","modified":1590850439877,"name":"Import CSV","parameters":[],"parentId":"fld_5e4cc5fff675449f940846f6d46d7a79","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/{{ resource }}/import","_type":"request"},{"_id":"req_2b017a251c91456fa62be682d8f86d05","authentication":{"token":"","type":"bearer"},"body":{"mimeType":"application/json","text":"{\n\t\"title\": \"Spotify\",\n\t\"type\": {% prompt 'Type', 'What is the transaction type?', 'income', 'type', false, true %},\n\t\"value\": {% prompt 'value', 'What is the transaction value?', '0', 'value', false, true %},\n\t\"category\": {% prompt 'What is the transaction category?', 'Category', 'general', 'category', false, true %}\n}"},"created":1590268752652,"description":"","headers":[{"id":"pair_674cc5494356430cb9c29b847a76960b","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1589282680948.5,"method":"PUT","modified":1590854799097,"name":"Update","parameters":[],"parentId":"fld_5e4cc5fff675449f940846f6d46d7a79","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/{{ resource }}/{% response 'body', 'req_1bdd39fd61184621bce47185cc307edb', 'b64::JC5pZA==::46b', 'no-history' %}","_type":"request"},{"_id":"req_d707e2de016047b2b0ddd18821211596","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Laura Beatris Teste\", \n\t\"email\": \"laura@gmail.com\"\n}"},"created":1598267903572,"description":"","headers":[{"id":"pair_9b155ed840e740419078f51030055de1","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1598267903572,"method":"POST","modified":1598882434651,"name":"Create","parameters":[],"parentId":"fld_0312f014b46b4251913f2943487eef94","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/customers","_type":"request"},{"_id":"fld_0312f014b46b4251913f2943487eef94","created":1598267856876,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1598267856876,"modified":1598267856876,"name":"Customers","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"request_group"},{"_id":"wrk_32b1ce3069e24641a06fa58895c46cc9","created":1598267850619,"description":"","modified":1598267850619,"name":"Orders ","parentId":null,"scope":null,"_type":"workspace"},{"_id":"req_6995ad2e665b48318ce515e402cf507f","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"customer_id\": \"c1d07a99-77e9-4aa5-aa1a-94127602150e\", \n\t\"products\": [\n\t\t{\n\t\t\t\"id\": \"d996708d-1ac5-4f1f-a335-fa0ecf2db9f2\",\n\t\t\t\"quantity\": 5\n\t\t}\n\t]\n}"},"created":1598279402844,"description":"","headers":[{"id":"pair_9b155ed840e740419078f51030055de1","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1598276841979,"method":"POST","modified":1598368467367,"name":"Create","parameters":[],"parentId":"fld_17600845b53a4e818ad11c98fc9415f3","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/orders","_type":"request"},{"_id":"fld_17600845b53a4e818ad11c98fc9415f3","created":1598279394883,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1598279394883,"modified":1598279394883,"name":"Orders","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"request_group"},{"_id":"req_d49a5dda4ddc4c23a8ba6819dc2d5e5a","authentication":{},"body":{},"created":1598356567800,"description":"","headers":[],"isPrivate":false,"metaSortKey":-1598276842029,"method":"GET","modified":1598368524885,"name":"Show","parameters":[],"parentId":"fld_17600845b53a4e818ad11c98fc9415f3","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/orders/5543edc2-691d-4332-bc8d-b976b80ad844","_type":"request"},{"_id":"req_795a5532d8504bc3bf66b26ffd400ffd","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Junior\",\n\t\"age\": 23, \n\t\"email\": \"juniorstudenteste2@gmail.com\"\n}"},"created":1598882333116,"description":"","headers":[{"id":"pair_db3d44aa0a7f4696b013dc5edfe6d222","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1598882333116,"method":"POST","modified":1598882743648,"name":"Create","parameters":[],"parentId":"fld_1dc771dd640e431193aff441e76f4773","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/students","_type":"request"},{"_id":"fld_1dc771dd640e431193aff441e76f4773","created":1598882025172,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1598882025172,"modified":1598882025172,"name":"Students","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"request_group"},{"_id":"req_9777cf02c06d40e19cae75fd88b43fba","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"subject\": \"Chemistry 23\", \n\t\"teacher_id\": \"8b0d5858-31b2-4f00-8a48-3efcc7a06bdb\",\n\t\"students\": [\n\t\t{\n\t\t\t\"id\": \"458df191-c698-4509-9c34-6d42394bb573\"\n\t\t}\n\t]\n}"},"created":1598882986479,"description":"","headers":[{"id":"pair_9aec37949a67451eaabebf6bf3778f04","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1598882986480,"method":"POST","modified":1598953393812,"name":"Create","parameters":[],"parentId":"fld_847ec48eed47477da6f5158f0b7ea345","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/classes","_type":"request"},{"_id":"fld_847ec48eed47477da6f5158f0b7ea345","created":1598882033318,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1598882033318,"modified":1598882033318,"name":"Classes","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"request_group"},{"_id":"req_b59c3d0f6fbc4066a28d5f8b08693adb","authentication":{},"body":{"mimeType":"application/json","text":"{\n\t\"name\": \"Laura\",\n\t\"age\": 23,\n\t\"email\": \"laurateacher1@gmail.com\"\n}"},"created":1598882052474,"description":"","headers":[{"id":"pair_0c060fa668d048f99ee2e6477f113d60","name":"Content-Type","value":"application/json"}],"isPrivate":false,"metaSortKey":-1598882052474,"method":"POST","modified":1598882835562,"name":"Create","parameters":[],"parentId":"fld_807d235cda3b42918285c8eb19d61a64","settingDisableRenderRequestBody":false,"settingEncodeUrl":true,"settingFollowRedirects":"global","settingRebuildPath":true,"settingSendCookies":true,"settingStoreCookies":true,"url":"{{ base_url }}/teachers","_type":"request"},{"_id":"fld_807d235cda3b42918285c8eb19d61a64","created":1598882045345,"description":"","environment":{},"environmentPropertyOrder":null,"metaSortKey":-1598882045345,"modified":1598882045345,"name":"Teachers","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"request_group"},{"_id":"env_a721eb55ccae012fcdcadc657e50d0228b5006be","color":null,"created":1590028718917,"data":{"base_url":"http://localhost:8000"},"dataPropertyOrder":{"&":["base_url"]},"isPrivate":false,"metaSortKey":1590028718917,"modified":1590028766612,"name":"Base Environment","parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","_type":"environment"},{"_id":"jar_a721eb55ccae012fcdcadc657e50d0228b5006be","cookies":[],"created":1590028718920,"modified":1590028718920,"name":"Default Jar","parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","_type":"cookie_jar"},{"_id":"spc_fa1143109074414b95de99a290b7ee66","contentType":"yaml","contents":"","created":1592789716864,"fileName":"Deno Pokemons","modified":1592789716864,"parentId":"wrk_59ee7513e0524a20b7d12c2e771000a0","_type":"api_spec"},{"_id":"env_7fa9a86d555a45c1ac07753ab0038765","color":"#04cb08","created":1590028756386,"data":{"base_url":"http://localhost:3333"},"dataPropertyOrder":{"&":["base_url"]},"isPrivate":false,"metaSortKey":1590028756386,"modified":1590354143532,"name":"dev","parentId":"env_a721eb55ccae012fcdcadc657e50d0228b5006be","_type":"environment"},{"_id":"env_ea5e663447ea0717cf24bdc6c49ca818e0679418","color":null,"created":1587818789278,"data":{},"dataPropertyOrder":null,"isPrivate":false,"metaSortKey":1587818789279,"modified":1587818789278,"name":"Base Environment","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"environment"},{"_id":"jar_ea5e663447ea0717cf24bdc6c49ca818e0679418","cookies":[],"created":1587818789284,"modified":1587818789284,"name":"Default Jar","parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"cookie_jar"},{"_id":"spc_63a1b3140c7947a6a002748a1b4ffe49","contentType":"yaml","contents":"","created":1592789716848,"fileName":"Hotseat","modified":1592789716848,"parentId":"wrk_113da797c6ae4cbd98178de7cb975733","_type":"api_spec"},{"_id":"env_3fdc0a190d644208aa1e3c2fbfeee160","color":"#5543cb","created":1587819246046,"data":{"base_url":"http://localhost:8080","customer_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIwM2JiYTY0Zi01MDk5LTQyOWQtYjA1ZS1hMjViMDg3OTJlNzQiLCJleHBpcmVzSW4iOiI3ZCIsImlhdCI6MTU5MzI3OTI1NH0.0Opx8jRc5PHhby3aAlJQl8rvUL6g3QGcmkoxY2EoN4Y","provider_id":"77f00f9a-3f75-41f2-a59e-1d0812f8e25b","provider_token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI3N2YwMGY5YS0zZjc1LTQxZjItYTU5ZS0xZDA4MTJmOGUyNWIiLCJpYXQiOjE1OTgxMTc2MjQsImV4cCI6MTU5ODIyNTYyNH0.nzKiUWFS6pH7FFh7ibr16gccFRhVUjgMQc_oT_BZt4g","token":"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI4MmIxOTQzMy1lNTUxLTQxOTgtOWI5Yi03ZjNlZjA4Zjg4NGIiLCJleHBpcmVzSW4iOiI3ZCIsImlhdCI6MTU5MjgyMTM1Mn0.IYHAu61Ku4Qy5E6u4Y-IesKpIgbvGsapRpdPCpkFpbM"},"dataPropertyOrder":{"&":["base_url","token","customer_token","provider_token","provider_id"]},"isPrivate":false,"metaSortKey":1587819246046,"modified":1598117834718,"name":"dev","parentId":"env_ea5e663447ea0717cf24bdc6c49ca818e0679418","_type":"environment"},{"_id":"env_22ac5389de44a248fc8a4b8e0e8904e16a284b45","color":null,"created":1589282633898,"data":{"base_url":"http://localhost:3333"},"dataPropertyOrder":{"&":["base_url"]},"isPrivate":false,"metaSortKey":1589282633898,"modified":1589282669220,"name":"Base Environment","parentId":"wrk_2bf4e9f997334a6b90b9943333a3c6d0","_type":"environment"},{"_id":"jar_22ac5389de44a248fc8a4b8e0e8904e16a284b45","cookies":[],"created":1589282633901,"modified":1589282633901,"name":"Default Jar","parentId":"wrk_2bf4e9f997334a6b90b9943333a3c6d0","_type":"cookie_jar"},{"_id":"spc_d973430a191a483380f40d1dcd6e05cb","contentType":"yaml","contents":"","created":1592789716851,"fileName":"GoFinances","modified":1592789716851,"parentId":"wrk_2bf4e9f997334a6b90b9943333a3c6d0","_type":"api_spec"},{"_id":"env_ddca97f6a9764b3db5241cc42f95ecab","color":"#c561cb","created":1590850305119,"data":{"base_url":"http://localhost:3333"},"dataPropertyOrder":{"&":["base_url"]},"isPrivate":false,"metaSortKey":1590850305119,"modified":1590850318408,"name":"dev","parentId":"env_22ac5389de44a248fc8a4b8e0e8904e16a284b45","_type":"environment"},{"_id":"env_996bcb51d207aea01293d4a0a5af1a7038a88cb3","color":null,"created":1598267850887,"data":{},"dataPropertyOrder":null,"isPrivate":false,"metaSortKey":1598267850888,"modified":1598267850887,"name":"Base Environment","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"environment"},{"_id":"jar_996bcb51d207aea01293d4a0a5af1a7038a88cb3","cookies":[],"created":1598267850891,"modified":1598267850891,"name":"Default Jar","parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"cookie_jar"},{"_id":"spc_280120db049545c6ab4f5ab08f534268","contentType":"yaml","contents":"","created":1598267850640,"fileName":"Orders ","modified":1598267850640,"parentId":"wrk_32b1ce3069e24641a06fa58895c46cc9","_type":"api_spec"},{"_id":"env_64f1e2ef012b4416af2b66ca4c64ad23","color":"#cb2500","created":1598267862050,"data":{"base_url":"http://localhost:3333","customer_id":"6e9eca43-8ea0-4a64-9d00-71c62632b8d1","teacher_id":""},"dataPropertyOrder":{"&":["base_url","customer_id","teacher_id"]},"isPrivate":false,"metaSortKey":1598267862050,"modified":1598882065630,"name":"dev","parentId":"env_996bcb51d207aea01293d4a0a5af1a7038a88cb3","_type":"environment"}]}
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .vscode/
3 | node_modules/
4 | build/
5 | tmp/
6 | temp/
7 |
--------------------------------------------------------------------------------
/.huskyrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "hooks": {
3 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
4 | "pre-commit": "lint-staged",
5 | "pre-push": "yarn lint"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.lintstagedrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "src/**/*.js": [
3 | "yarn lint",
4 | "git add"
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing Guidelines
2 |
3 | Thanks you so much for your interest in contributing to this project!
4 |
5 | ## About our deal
6 |
7 | Hi! I'm Laura and i'm the creator and maintainer of this project.
8 |
9 | If you encounter bugs, please **do** open an issue describing the bug and including steps to easily reproduce it (bonus points for a CodeSandbox that demonstrates the problem).
10 |
11 | If you have an idea for an enhancement, go ahead and share it via an issue, but please don't expect a timely response.
12 |
13 | This project is MIT-licensed, and this means that you can implement and use whatever enhancements you'd like.
14 |
15 | ## Commits and Code Standardization
16 |
17 | This project follow the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) specification. [Husky](https://github.com/typicode/husky) execute scrips according to git hooks in order to test if a developer is following the [Eslint Lint Rules](https://github.com/LauraBeatris/gofinances-api/blob/master/.eslintrc.js) and also the commits convention.
18 |
19 | ## Bug reports
20 |
21 | If you encounter a problem with this project, please open an issue. Be sure to include:
22 |
23 | - Package version
24 | - Node and Express versions
25 | - Brief but thorough description of the issue
26 | - Link to a CodeSandbox (or similar) demonstrating the problem (optional, but highly recommended, especially for complex problems)
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # TypeORM Relationships
6 |
7 | > Learn how to perform relationships in TypeORM
8 |
9 | > [](https://insomnia.rest/run/?label=Hotseat%20API&uri=https%3A%2F%2Fraw.githubusercontent.com%2FLauraBeatris%2Ftypeorm-relations%2Fmaster%2F.github%2Ftypeorm_relations.json)
10 |
11 | [](https://github.com/LauraBeatris)
12 | [](#)
13 | [](https://github.com/LauraBeatris/typeorm-relations/stargazers)
14 | [](https://github.com/LauraBeatris/typeorm-relations/network/members)
15 | [](https://github.com/LauraBeatris/typeorm-relations/graphs/contributors)
16 |
17 | # :pushpin: Table of Contents
18 |
19 | * [Features](#rocket-features)
20 | * [Database Model](#clipboard-features)
21 | * [Learning Sources](#orange_book-learning-sources)
22 | * [Installation](#construction_worker-installation)
23 | * [Getting Started](#runner-getting-started)
24 | * [FAQ](#postbox-faq)
25 | * [Found a bug? Missing a specific feature?](#bug-issues)
26 | * [Contributing](#tada-contributing)
27 | * [License](#closed_book-license)
28 |
29 | # :rocket: Features
30 |
31 | * 🛍 Store orders, products and customers
32 | * 📔 Store students, classes and teachers
33 |
34 | # :clipboard: Database Model
35 |
36 | In order to understand the relationships, I've created the following database models:
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | # :orange_book: Learning Sources
47 |
48 | - [Relations in TypeORM](https://orkhan.gitbook.io/typeorm/docs/relations)
49 | - [What are Many To Many Relations](https://typeorm.io/#/many-to-many-relations/what-are-many-to-many-relations)
50 | - [Many to Many Entities in TypeORM](https://www.youtube.com/watch?v=RH_es0awU_A)
51 | - [Saving Many to Many Relations with only one save call](https://typeorm.io/#/relations/cascades)
52 |
53 | # :construction_worker: Installation
54 |
55 | **You need to install [Node.js](https://nodejs.org/en/download/) and [Yarn](https://yarnpkg.com/) first, then in order to clone the project via HTTPS, run this command:**
56 |
57 | ```
58 | git clone https://github.com/LauraBeatris/typeorm-relations.git
59 | ```
60 |
61 | SSH URLs provide access to a Git repository via SSH, a secure protocol. If you use a SSH key registered in your Github account, clone the project using this command:
62 |
63 | ```
64 | git clone git@github.com:LauraBeatris/typeorm-relations.git
65 | ```
66 |
67 | **Install dependencies**
68 |
69 | ```
70 | yarn install
71 | ```
72 |
73 | Or
74 |
75 | ```
76 | npm install
77 | ```
78 |
79 | Create your enviroment variables based on the examples of ```.env.example```
80 |
81 | ```
82 | cp .env.example .env
83 | ```
84 |
85 | After copying the examples, make sure to fill the variables with new values.
86 |
87 | **Setup a database**
88 |
89 | Install [Postgres](https://www.postgresql.org/) to create a database or if you have [Docker](https://www.docker.com/) in your machine, fill the environment values related to database configurations and then run the following commands in order to create a Postgres container.
90 |
91 | ```docker-compose up```
92 |
93 | # :runner: Getting Started
94 |
95 | Run the transactions in order to configure the database schema
96 |
97 | ```yarn typeorm migration:run```
98 |
99 | Run the following command in order to start the application in a development environment:
100 |
101 | ```yarn dev:server```
102 |
103 | # :postbox: Faq
104 |
105 | **Question:** What are the tecnologies used in this project?
106 |
107 | **Answer:** The tecnologies used in this project are [NodeJS](https://nodejs.org/en/) + [Express Framework](http://expressjs.com/en/) to handle the server and [TypeORM](https://typeorm.io/#/)
108 |
109 | # :bug: Issues
110 |
111 | Feel free to **file a new issue** with a respective title and description on the the [TypeORM Relations](https://github.com/LauraBeatris/typeorm-relations/issues) repository. If you already found a solution to your problem, **I would love to review your pull request**! Have a look at our [contribution guidelines](https://github.com/LauraBeatris/typeorm-relations/blob/master/CONTRIBUTING.md) to find out about the coding standards.
112 |
113 | # :tada: Contributing
114 |
115 | Check out the [contributing](https://github.com/LauraBeatris/typeorm-relations/blob/master/CONTRIBUTING.md) page to see the best places to file issues, start discussions and begin contributing.
116 |
117 | # :closed_book: License
118 |
119 | Released in 2020.
120 | This project is under the [MIT license](https://github.com/LauraBeatris/typeorm-relations/master/LICENSE).
121 |
122 | Made with love by [Laura Beatris](https://github.com/LauraBeatris) 💜🚀
123 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | module.exports = { extends: ['@commitlint/config-conventional'] };
2 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '3.3'
2 |
3 | services:
4 | postgres:
5 | container_name: postgres
6 | image: postgres
7 | environment:
8 | POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
9 | POSTGRES_USER: ${POSTGRES_USER}
10 | POSTGRES_DB: ${POSTGRES_DATABASE}
11 | ports:
12 | - "5432:5432"
13 | restart: unless-stopped
14 |
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // All imported modules in your tests should be mocked automatically
3 | // automock: false,
4 |
5 | // Stop running tests after `n` failures
6 | // bail: 0,
7 |
8 | // Respect "browser" field in package.json when resolving modules
9 | // browser: false,
10 |
11 | // The directory where Jest should store its cached dependency information
12 | // cacheDirectory: "/private/var/folders/qv/s8ph22xx2fnfxdh3pq14t4d40000gn/T/jest_dx",
13 |
14 | // Automatically clear mock calls and instances between every test
15 | clearMocks: true,
16 |
17 | // Indicates whether the coverage information should be collected while executing the test
18 | // collectCoverage: false,
19 |
20 | // An array of glob patterns indicating a set of files for which coverage information should be collected
21 | // collectCoverageFrom: undefined,
22 |
23 | // The directory where Jest should output its coverage files
24 | // coverageDirectory: undefined,
25 |
26 | // An array of regexp pattern strings used to skip coverage collection
27 | // coveragePathIgnorePatterns: [
28 | // "/node_modules/"
29 | // ],
30 |
31 | // A list of reporter names that Jest uses when writing coverage reports
32 | // coverageReporters: [
33 | // "json",
34 | // "text",
35 | // "lcov",
36 | // "clover"
37 | // ],
38 |
39 | // An object that configures minimum threshold enforcement for coverage results
40 | // coverageThreshold: undefined,
41 |
42 | // A path to a custom dependency extractor
43 | // dependencyExtractor: undefined,
44 |
45 | // Make calling deprecated APIs throw helpful error messages
46 | // errorOnDeprecated: false,
47 |
48 | // Force coverage collection from ignored files using an array of glob patterns
49 | // forceCoverageMatch: [],
50 |
51 | // A path to a module which exports an async function that is triggered once before all test suites
52 | // globalSetup: undefined,
53 |
54 | // A path to a module which exports an async function that is triggered once after all test suites
55 | // globalTeardown: undefined,
56 |
57 | // A set of global variables that need to be available in all test environments
58 | // globals: {},
59 |
60 | // The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
61 | // maxWorkers: "50%",
62 |
63 | // An array of directory names to be searched recursively up from the requiring module's location
64 | // moduleDirectories: [
65 | // "node_modules"
66 | // ],
67 |
68 | // An array of file extensions your modules use
69 | // moduleFileExtensions: [
70 | // "js",
71 | // "json",
72 | // "jsx",
73 | // "ts",
74 | // "tsx",
75 | // "node"
76 | // ],
77 |
78 | // A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
79 | // moduleNameMapper: {},
80 |
81 | // An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
82 | // modulePathIgnorePatterns: [],
83 |
84 | // Activates notifications for test results
85 | // notify: false,
86 |
87 | // An enum that specifies notification mode. Requires { notify: true }
88 | // notifyMode: "failure-change",
89 |
90 | // A preset that is used as a base for Jest's configuration
91 | preset: 'ts-jest',
92 |
93 | // Run tests from one or more projects
94 | // projects: undefined,
95 |
96 | // Use this configuration option to add custom reporters to Jest
97 | // reporters: undefined,
98 |
99 | // Automatically reset mock state between every test
100 | // resetMocks: false,
101 |
102 | // Reset the module registry before running each individual test
103 | // resetModules: false,
104 |
105 | // A path to a custom resolver
106 | // resolver: undefined,
107 |
108 | // Automatically restore mock state between every test
109 | // restoreMocks: false,
110 |
111 | // The root directory that Jest should scan for tests and modules within
112 | // rootDir: undefined,
113 |
114 | // A list of paths to directories that Jest should use to search for files in
115 | // roots: [
116 | // ""
117 | // ],
118 |
119 | // Allows you to use a custom runner instead of Jest's default test runner
120 | // runner: "jest-runner",
121 |
122 | // The paths to modules that run some code to configure or set up the testing environment before each test
123 | // setupFiles: [],
124 |
125 | // A list of paths to modules that run some code to configure or set up the testing framework before each test
126 | // setupFilesAfterEnv: ['./jest.setup.ts'],
127 |
128 | // A list of paths to snapshot serializer modules Jest should use for snapshot testing
129 | // snapshotSerializers: [],
130 |
131 | // The test environment that will be used for testing
132 | testEnvironment: 'node',
133 |
134 | // Options that will be passed to the testEnvironment
135 | // testEnvironmentOptions: {},
136 |
137 | // Adds a location field to test results
138 | // testLocationInResults: false,
139 |
140 | // The glob patterns Jest uses to detect test files
141 | testMatch: ['**/src/__tests__/**/*.[jt]s?(x)'],
142 |
143 | // An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
144 | // testPathIgnorePatterns: [
145 | // "/node_modules/"
146 | // ],
147 |
148 | // The regexp pattern or array of patterns that Jest uses to detect test files
149 | // testRegex: [],
150 |
151 | // This option allows the use of a custom results processor
152 | // testResultsProcessor: undefined,
153 |
154 | // This option allows use of a custom test runner
155 | // testRunner: "jasmine2",
156 |
157 | // This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
158 | // testURL: "http://localhost",
159 |
160 | // Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
161 | // timers: "real",
162 |
163 | // A map from regular expressions to paths to transformers
164 | // transform: undefined,
165 |
166 | // An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
167 | // transformIgnorePatterns: [
168 | // "/node_modules/"
169 | // ],
170 |
171 | // An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
172 | // unmockedModulePathPatterns: undefined,
173 |
174 | // Indicates whether each individual test should be reported during the run
175 | // verbose: undefined,
176 |
177 | // An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
178 | // watchPathIgnorePatterns: [],
179 |
180 | // Whether to use watchman for file crawling
181 | // watchman: true,
182 | moduleNameMapper: {
183 | '^@modules/(.*)$': '/src/modules/$1',
184 | '^@config/(.*)$': '/src/config/$1',
185 | '^@shared/(.*)$': '/src/shared/$1',
186 | },
187 | };
188 |
--------------------------------------------------------------------------------
/ormconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "postgres",
3 | "host": "localhost",
4 | "port": "5432",
5 | "username": "postgres",
6 | "password": "docker",
7 | "database": "gostack_desafio09",
8 | "entities": ["./src/modules/**/infra/typeorm/entities/*.ts"],
9 | "migrations": ["./src/shared/infra/typeorm/migrations/*.ts"],
10 | "cli": {
11 | "migrationsDir": "./src/shared/infra/typeorm/migrations"
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gostack-desafio-09",
3 | "version": "1.0.0",
4 | "main": "index.js",
5 | "license": "MIT",
6 | "scripts": {
7 | "build": "tsc",
8 | "dev:server": "ts-node-dev -r tsconfig-paths/register --transpileOnly --ignore-watch node_modules src/shared/infra/http/server.ts",
9 | "typeorm": "ts-node-dev -r tsconfig-paths/register ./node_modules/typeorm/cli.js",
10 | "test": "cross-env NODE_ENV=test jest",
11 | "lint": "yarn eslint --ext .ts ./src --fix"
12 | },
13 | "dependencies": {
14 | "celebrate": "^12.2.0",
15 | "cors": "^2.8.5",
16 | "cross-env": "^7.0.2",
17 | "express": "^4.17.1",
18 | "express-async-errors": "^3.1.1",
19 | "pg": "^8.1.0",
20 | "reflect-metadata": "^0.1.10",
21 | "tsyringe": "^4.2.0",
22 | "typeorm": "0.2.24"
23 | },
24 | "devDependencies": {
25 | "@commitlint/cli": "^9.1.2",
26 | "@commitlint/config-conventional": "^9.1.2",
27 | "@types/cors": "^2.8.6",
28 | "@types/express": "^4.17.6",
29 | "@types/jest": "^25.2.1",
30 | "@types/node": "^8.0.29",
31 | "@types/supertest": "^2.0.9",
32 | "@typescript-eslint/eslint-plugin": "^2.31.0",
33 | "@typescript-eslint/parser": "^2.31.0",
34 | "cz-conventional-changelog": "^3.3.0",
35 | "eslint": "^7.0.0",
36 | "eslint-config-airbnb-base": "^14.1.0",
37 | "eslint-config-prettier": "^6.11.0",
38 | "eslint-import-resolver-typescript": "^2.0.0",
39 | "eslint-plugin-import": "^2.20.2",
40 | "eslint-plugin-prettier": "^3.1.3",
41 | "husky": "^4.2.5",
42 | "jest": ">=25.0.0 <26.0.0",
43 | "lint-staged": "^10.2.13",
44 | "prettier": "^2.0.5",
45 | "supertest": "^4.0.2",
46 | "ts-jest": "^25.5.1",
47 | "ts-node": "3.3.0",
48 | "ts-node-dev": "^1.0.0-pre.44",
49 | "tsconfig-paths": "^3.9.0",
50 | "typescript": "^3.8.3"
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/prettier.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | singleQuote: true,
3 | trailingComma: 'all',
4 | arrowParens: 'avoid',
5 | };
6 |
--------------------------------------------------------------------------------
/src/__tests__/App.spec.ts:
--------------------------------------------------------------------------------
1 | import request from 'supertest';
2 |
3 | import { Connection, getConnection, getRepository } from 'typeorm';
4 | import createConnection from '@shared/infra/typeorm/index';
5 |
6 | import Product from '@modules/products/infra/typeorm/entities/Product';
7 |
8 | import app from '@shared/infra/http/app';
9 |
10 | let connection: Connection;
11 |
12 | describe('App', () => {
13 | beforeAll(async () => {
14 | connection = await createConnection('test-connection');
15 |
16 | await connection.query('DROP TABLE IF EXISTS orders_products');
17 | await connection.query('DROP TABLE IF EXISTS orders');
18 | await connection.query('DROP TABLE IF EXISTS products');
19 | await connection.query('DROP TABLE IF EXISTS customers');
20 | await connection.query('DROP TABLE IF EXISTS student_classes');
21 | await connection.query('DROP TABLE IF EXISTS classes');
22 | await connection.query('DROP TABLE IF EXISTS students');
23 | await connection.query('DROP TABLE IF EXISTS teachers');
24 | await connection.query('DROP TABLE IF EXISTS migrations');
25 |
26 | await connection.runMigrations();
27 | });
28 |
29 | beforeEach(async () => {
30 | await connection.query('DELETE FROM orders_products');
31 | await connection.query('DELETE FROM orders');
32 | await connection.query('DELETE FROM products');
33 | await connection.query('DELETE FROM customers');
34 | });
35 |
36 | afterAll(async () => {
37 | const mainConnection = getConnection();
38 |
39 | await connection.close();
40 | await mainConnection.close();
41 | });
42 |
43 | it('should be able to create a new customer', async () => {
44 | const response = await request(app).post('/customers').send({
45 | name: 'Rocketseat',
46 | email: 'oi@rocketseat.com.br',
47 | });
48 |
49 | expect(response.body).toEqual(
50 | expect.objectContaining({
51 | name: 'Rocketseat',
52 | email: 'oi@rocketseat.com.br',
53 | }),
54 | );
55 | });
56 |
57 | it('should not be able to create a customer with one e-mail thats already registered', async () => {
58 | const customer = await request(app).post('/customers').send({
59 | name: 'Rocketseat',
60 | email: 'oi@rocketseat.com.br',
61 | });
62 |
63 | expect(customer.body).toEqual(
64 | expect.objectContaining({
65 | name: 'Rocketseat',
66 | email: 'oi@rocketseat.com.br',
67 | }),
68 | );
69 |
70 | const response = await request(app).post('/customers').send({
71 | name: 'Rocketseat',
72 | email: 'oi@rocketseat.com.br',
73 | });
74 |
75 | expect(response.status).toBe(400);
76 | });
77 |
78 | it('should be able to create a new product', async () => {
79 | const response = await request(app).post('/products').send({
80 | name: 'Produto 01',
81 | price: 500,
82 | quantity: 50,
83 | });
84 |
85 | expect(response.body).toEqual(
86 | expect.objectContaining({
87 | name: 'Produto 01',
88 | price: 500,
89 | quantity: 50,
90 | }),
91 | );
92 | });
93 |
94 | it('should not be able to create a duplicated product', async () => {
95 | const product = await request(app).post('/products').send({
96 | name: 'Produto 01',
97 | price: 500,
98 | quantity: 50,
99 | });
100 |
101 | expect(product.body).toEqual(
102 | expect.objectContaining({
103 | name: 'Produto 01',
104 | price: 500,
105 | quantity: 50,
106 | }),
107 | );
108 |
109 | const response = await request(app).post('/products').send({
110 | name: 'Produto 01',
111 | price: 500,
112 | quantity: 50,
113 | });
114 |
115 | expect(response.status).toBe(400);
116 | });
117 |
118 | it('should be able to create a new order', async () => {
119 | const product = await request(app).post('/products').send({
120 | name: 'Produto 01',
121 | price: 500,
122 | quantity: 50,
123 | });
124 |
125 | const customer = await request(app).post('/customers').send({
126 | name: 'Rocketseat',
127 | email: 'oi@rocketseat.com.br',
128 | });
129 |
130 | const response = await request(app)
131 | .post('/orders')
132 | .send({
133 | customer_id: customer.body.id,
134 | products: [
135 | {
136 | id: product.body.id,
137 | quantity: 5,
138 | },
139 | ],
140 | });
141 |
142 | expect(response.body).toEqual(
143 | expect.objectContaining({
144 | customer: expect.objectContaining({
145 | id: customer.body.id,
146 | name: 'Rocketseat',
147 | email: 'oi@rocketseat.com.br',
148 | }),
149 | orders_products: expect.arrayContaining([
150 | expect.objectContaining({
151 | product_id: product.body.id,
152 | price: '500.00',
153 | quantity: 5,
154 | }),
155 | ]),
156 | }),
157 | );
158 | });
159 |
160 | it('should not be able to create an order with a invalid customer', async () => {
161 | const response = await request(app).post('/orders').send({
162 | customer_id: '6a1922c8-af6e-470e-9a34-621cb0643911',
163 | });
164 |
165 | expect(response.status).toEqual(400);
166 | });
167 |
168 | it('should not be able to create an order with invalid products', async () => {
169 | const customer = await request(app).post('/customers').send({
170 | name: 'Rocketseat',
171 | email: 'oi@rocketseat.com.br',
172 | });
173 |
174 | const response = await request(app)
175 | .post('/orders')
176 | .send({
177 | customer_id: customer.body.id,
178 | products: [
179 | {
180 | id: '6a1922c8-af6e-470e-9a34-621cb0643911',
181 | },
182 | ],
183 | });
184 |
185 | expect(response.status).toEqual(400);
186 | });
187 |
188 | it('should not be able to create an order with products with insufficient quantities', async () => {
189 | const customer = await request(app).post('/customers').send({
190 | name: 'Rocketseat',
191 | email: 'oi@rocketseat.com.br',
192 | });
193 |
194 | const product = await request(app).post('/products').send({
195 | name: 'Produto 01',
196 | price: 500,
197 | quantity: 50,
198 | });
199 |
200 | const response = await request(app)
201 | .post('/orders')
202 | .send({
203 | customer_id: customer.body.id,
204 | products: [
205 | {
206 | id: product.body.id,
207 | quantity: 500,
208 | },
209 | ],
210 | });
211 |
212 | expect(response.status).toEqual(400);
213 | });
214 |
215 | it('should be able to subtract an product total quantity when it is ordered', async () => {
216 | const productsRepository = getRepository(Product);
217 |
218 | const customer = await request(app).post('/customers').send({
219 | name: 'Rocketseat',
220 | email: 'oi@rocketseat.com.br',
221 | });
222 |
223 | const product = await request(app).post('/products').send({
224 | name: 'Produto 01',
225 | price: 500,
226 | quantity: 50,
227 | });
228 |
229 | await request(app)
230 | .post('/orders')
231 | .send({
232 | customer_id: customer.body.id,
233 | products: [
234 | {
235 | id: product.body.id,
236 | quantity: 5,
237 | },
238 | ],
239 | });
240 |
241 | let foundProduct = await productsRepository.findOne(product.body.id);
242 |
243 | expect(foundProduct).toEqual(
244 | expect.objectContaining({
245 | quantity: 45,
246 | }),
247 | );
248 |
249 | await request(app)
250 | .post('/orders')
251 | .send({
252 | customer_id: customer.body.id,
253 | products: [
254 | {
255 | id: product.body.id,
256 | quantity: 5,
257 | },
258 | ],
259 | });
260 |
261 | foundProduct = await productsRepository.findOne(product.body.id);
262 |
263 | expect(foundProduct).toEqual(
264 | expect.objectContaining({
265 | quantity: 40,
266 | }),
267 | );
268 | });
269 |
270 | it('should be able to list one specific order', async () => {
271 | const customer = await request(app).post('/customers').send({
272 | name: 'Rocketseat',
273 | email: 'oi@rocketseat.com.br',
274 | });
275 |
276 | const product = await request(app).post('/products').send({
277 | name: 'Produto 01',
278 | price: 500,
279 | quantity: 50,
280 | });
281 |
282 | const order = await request(app)
283 | .post('/orders')
284 | .send({
285 | customer_id: customer.body.id,
286 | products: [
287 | {
288 | id: product.body.id,
289 | quantity: 5,
290 | },
291 | ],
292 | });
293 |
294 | const response = await request(app).get(`/orders/${order.body.id}`);
295 |
296 | expect(response.body).toEqual(
297 | expect.objectContaining({
298 | customer: expect.objectContaining({
299 | id: customer.body.id,
300 | name: 'Rocketseat',
301 | email: 'oi@rocketseat.com.br',
302 | }),
303 | orders_products: expect.arrayContaining([
304 | expect.objectContaining({
305 | product_id: product.body.id,
306 | price: '500.00',
307 | quantity: 5,
308 | }),
309 | ]),
310 | }),
311 | );
312 | });
313 | });
314 |
--------------------------------------------------------------------------------
/src/modules/classes/dtos/ICreateClassDTO.ts:
--------------------------------------------------------------------------------
1 | import Teacher from '@modules/teachers/infra/typeorm/entities/Teacher';
2 |
3 | export interface IStudent {
4 | student_id: string;
5 | }
6 |
7 | export default interface ICreateClassDTO {
8 | subject: string;
9 | teacher: Teacher;
10 | students: IStudent[];
11 | }
12 |
--------------------------------------------------------------------------------
/src/modules/classes/infra/http/controllers/ClassesController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import { container } from 'tsyringe';
3 |
4 | import CreateClassService from '@modules/classes/services/CreateClassService';
5 | import Class from '@modules/classes/infra/typeorm/entities/Class';
6 |
7 | class ClassesController {
8 | public async create(
9 | request: Request,
10 | response: Response,
11 | ): Promise> {
12 | const { subject, teacher_id, students } = request.body;
13 |
14 | const createClass = container.resolve(CreateClassService);
15 |
16 | const classRegister = await createClass.execute({
17 | subject,
18 | students,
19 | teacher_id,
20 | });
21 |
22 | return response.json(classRegister);
23 | }
24 | }
25 |
26 | export default ClassesController;
27 |
--------------------------------------------------------------------------------
/src/modules/classes/infra/http/routes/classes.routes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import ClassesController from '@modules/classes/infra/http/controllers/ClassesController';
4 |
5 | const classesRouter = Router();
6 | const classesController = new ClassesController();
7 |
8 | classesRouter.post('/', classesController.create);
9 |
10 | export default classesRouter;
11 |
--------------------------------------------------------------------------------
/src/modules/classes/infra/typeorm/entities/Class.ts:
--------------------------------------------------------------------------------
1 | import StudentClasses from '@modules/students/infra/typeorm/entities/StudentClasses';
2 | import Teacher from '@modules/teachers/infra/typeorm/entities/Teacher';
3 | import {
4 | Entity,
5 | Column,
6 | ManyToOne,
7 | OneToMany,
8 | JoinColumn,
9 | CreateDateColumn,
10 | UpdateDateColumn,
11 | PrimaryGeneratedColumn,
12 | } from 'typeorm';
13 |
14 | @Entity('classes')
15 | class Class {
16 | @Column('uuid')
17 | @PrimaryGeneratedColumn('uuid')
18 | id: string;
19 |
20 | @Column('text')
21 | subject: string;
22 |
23 | @ManyToOne(() => Teacher, teacher => teacher.classes)
24 | @JoinColumn({ name: 'teacher_id' })
25 | teacher: Teacher;
26 |
27 | @CreateDateColumn()
28 | created_at: Date;
29 |
30 | @OneToMany(() => StudentClasses, studentClasses => studentClasses.class, {
31 | cascade: ['insert'],
32 | })
33 | student_classes: StudentClasses[];
34 |
35 | @UpdateDateColumn()
36 | updated_at: Date;
37 | }
38 |
39 | export default Class;
40 |
--------------------------------------------------------------------------------
/src/modules/classes/infra/typeorm/repositories/ClassesRepository.ts:
--------------------------------------------------------------------------------
1 | import IClassesRepository from '@modules/classes/repositories/IClassesRepository';
2 | import { Repository, getRepository } from 'typeorm';
3 |
4 | import Class from '@modules/classes/infra/typeorm/entities/Class';
5 | import ICreateClassDTO from '@modules/classes/dtos/ICreateClassDTO';
6 |
7 | class ClassesRepository implements IClassesRepository {
8 | private ormRepository: Repository;
9 |
10 | constructor() {
11 | this.ormRepository = getRepository(Class);
12 | }
13 |
14 | public async create({
15 | subject,
16 | teacher,
17 | students,
18 | }: ICreateClassDTO): Promise {
19 | const createClass = this.ormRepository.create({
20 | subject,
21 | teacher,
22 | student_classes: students,
23 | });
24 |
25 | await this.ormRepository.save(createClass);
26 |
27 | return createClass;
28 | }
29 |
30 | public async findBySubject(subject: string): Promise {
31 | const findClassBySubject = await this.ormRepository.findOne({
32 | where: { subject },
33 | });
34 |
35 | return findClassBySubject;
36 | }
37 | }
38 |
39 | export default ClassesRepository;
40 |
--------------------------------------------------------------------------------
/src/modules/classes/infra/validators/createClass.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Segments, Joi } from 'celebrate';
2 |
3 | const createClassValidator = celebrate({
4 | [Segments.BODY]: {
5 | subject: Joi.string().required(),
6 | teacher_id: Joi.number().integer().positive().required(),
7 | },
8 | });
9 |
10 | export default createClassValidator;
11 |
--------------------------------------------------------------------------------
/src/modules/classes/repositories/IClassesRepository.ts:
--------------------------------------------------------------------------------
1 | import Class from '@modules/classes/infra/typeorm/entities/Class';
2 | import ICreateClassDTO from '@modules/classes/dtos/ICreateClassDTO';
3 |
4 | export default interface IClassesRepository {
5 | create(data: ICreateClassDTO): Promise;
6 | findBySubject(subject: string): Promise;
7 | }
8 |
--------------------------------------------------------------------------------
/src/modules/classes/services/CreateClassService.ts:
--------------------------------------------------------------------------------
1 | import { inject, injectable } from 'tsyringe';
2 |
3 | import ITeachersRepository from '@modules/teachers/repositories/ITeachersRepository';
4 | import IClassesRepository from '@modules/classes/repositories/IClassesRepository';
5 | import Class from '@modules/classes/infra/typeorm/entities/Class';
6 | import AppError from '@shared/errors/AppError';
7 | import IStudentsRepository from '@modules/students/repositories/IStudentsRepository';
8 |
9 | interface IStudent {
10 | id: string;
11 | }
12 |
13 | interface IRequest {
14 | subject: string;
15 | teacher_id: string;
16 | students: IStudent[];
17 | }
18 |
19 | @injectable()
20 | class CreateClassService {
21 | constructor(
22 | @inject('ClassesRepository')
23 | private classesRepository: IClassesRepository,
24 |
25 | @inject('TeachersRepository')
26 | private teachersRepository: ITeachersRepository,
27 |
28 | @inject('StudentsRepository')
29 | private studentsRepository: IStudentsRepository,
30 | ) {}
31 |
32 | public async execute({
33 | subject,
34 | teacher_id,
35 | students,
36 | }: IRequest): Promise {
37 | const findTeacher = await this.teachersRepository.findById(teacher_id);
38 |
39 | if (!findTeacher) {
40 | throw new AppError('Teacher not found');
41 | }
42 |
43 | const findClassWithSameSubject = await this.classesRepository.findBySubject(
44 | subject,
45 | );
46 |
47 | if (findClassWithSameSubject) {
48 | throw new AppError(
49 | "There's already a class with the same subject. Please, try it again with another subject",
50 | );
51 | }
52 |
53 | try {
54 | const findStudents = students.map(student =>
55 | this.studentsRepository.findOneOrFail(student.id),
56 | );
57 |
58 | await Promise.all(findStudents);
59 | } catch (error) {
60 | throw new AppError('Student not found');
61 | }
62 |
63 | const studentsIds = students.map(student => ({ student_id: student.id }));
64 |
65 | const createClass = await this.classesRepository.create({
66 | subject,
67 | teacher: findTeacher,
68 | students: studentsIds,
69 | });
70 |
71 | return createClass;
72 | }
73 | }
74 |
75 | export default CreateClassService;
76 |
--------------------------------------------------------------------------------
/src/modules/customers/dtos/ICreateCustomerDTO.ts:
--------------------------------------------------------------------------------
1 | export default interface ICreateCustomerDTO {
2 | name: string;
3 | email: string;
4 | }
5 |
--------------------------------------------------------------------------------
/src/modules/customers/infra/http/controller/CustomersController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import { container } from 'tsyringe';
3 |
4 | import CreateCustomerService from '@modules/customers/services/CreateCustomerService';
5 |
6 | import AppError from '@shared/errors/AppError';
7 | import Customer from '@modules/customers/infra/typeorm/entities/Customer';
8 |
9 | export default class CustomersController {
10 | public async create(
11 | request: Request,
12 | response: Response,
13 | ): Promise> {
14 | const { name, email } = request.body;
15 |
16 | const hasInvalidData = !name || !email;
17 |
18 | if (hasInvalidData) {
19 | throw new AppError(
20 | 'Please, provide valid data in order to create a customer',
21 | );
22 | }
23 |
24 | const createCustomer = container.resolve(CreateCustomerService);
25 |
26 | const customer = await createCustomer.execute({ name, email });
27 |
28 | return response.json(customer);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/modules/customers/infra/http/routes/customers.routes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import createCustomerValidator from '@modules/customers/infra/validators/createCustomer';
4 | import CustomersController from '@modules/customers/infra/http/controller/CustomersController';
5 |
6 | const customersRouter = Router();
7 | const customersController = new CustomersController();
8 |
9 | customersRouter.post('/', createCustomerValidator, customersController.create);
10 |
11 | export default customersRouter;
12 |
--------------------------------------------------------------------------------
/src/modules/customers/infra/typeorm/entities/Customer.ts:
--------------------------------------------------------------------------------
1 | import Order from '@modules/orders/infra/typeorm/entities/Order';
2 | import {
3 | Entity,
4 | Column,
5 | OneToMany,
6 | CreateDateColumn,
7 | UpdateDateColumn,
8 | PrimaryGeneratedColumn,
9 | } from 'typeorm';
10 |
11 | @Entity('customers')
12 | class Customer {
13 | @Column('uuid')
14 | @PrimaryGeneratedColumn('uuid')
15 | id: string;
16 |
17 | @Column()
18 | name: string;
19 |
20 | @Column()
21 | email: string;
22 |
23 | @OneToMany(() => Order, order => order.customer)
24 | order: Order;
25 |
26 | @CreateDateColumn()
27 | created_at: Date;
28 |
29 | @UpdateDateColumn()
30 | updated_at: Date;
31 | }
32 |
33 | export default Customer;
34 |
--------------------------------------------------------------------------------
/src/modules/customers/infra/typeorm/repositories/CustomersRepository.ts:
--------------------------------------------------------------------------------
1 | import { getRepository, Repository } from 'typeorm';
2 |
3 | import ICustomersRepository from '@modules/customers/repositories/ICustomersRepository';
4 | import ICreateCustomerDTO from '@modules/customers/dtos/ICreateCustomerDTO';
5 | import Customer from '@modules/customers/infra/typeorm/entities/Customer';
6 |
7 | class CustomersRepository implements ICustomersRepository {
8 | private ormRepository: Repository;
9 |
10 | constructor() {
11 | this.ormRepository = getRepository(Customer);
12 | }
13 |
14 | public async create({ name, email }: ICreateCustomerDTO): Promise {
15 | const customer = this.ormRepository.create({
16 | name,
17 | email,
18 | });
19 |
20 | await this.ormRepository.save(customer);
21 |
22 | return customer;
23 | }
24 |
25 | public async findById(id: string): Promise {
26 | const findCustomer = await this.ormRepository.findOne(id);
27 |
28 | return findCustomer;
29 | }
30 |
31 | public async findByEmail(email: string): Promise {
32 | const findCustomer = await this.ormRepository.findOne({
33 | where: {
34 | email,
35 | },
36 | });
37 |
38 | return findCustomer;
39 | }
40 | }
41 |
42 | export default CustomersRepository;
43 |
--------------------------------------------------------------------------------
/src/modules/customers/infra/validators/createCustomer.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Segments, Joi } from 'celebrate';
2 |
3 | const createCustomerValidator = celebrate({
4 | [Segments.BODY]: {
5 | name: Joi.string().required(),
6 | email: Joi.string().email().required(),
7 | },
8 | });
9 |
10 | export default createCustomerValidator;
11 |
--------------------------------------------------------------------------------
/src/modules/customers/repositories/ICustomersRepository.ts:
--------------------------------------------------------------------------------
1 | import Customer from '../infra/typeorm/entities/Customer';
2 |
3 | import ICreateCustomerDTO from '../dtos/ICreateCustomerDTO';
4 |
5 | export default interface ICustomersRepository {
6 | create(data: ICreateCustomerDTO): Promise;
7 | findByEmail(email: string): Promise;
8 | findById(id: string): Promise;
9 | }
10 |
--------------------------------------------------------------------------------
/src/modules/customers/services/CreateCustomerService.ts:
--------------------------------------------------------------------------------
1 | import { inject, injectable } from 'tsyringe';
2 |
3 | import AppError from '@shared/errors/AppError';
4 |
5 | import Customer from '../infra/typeorm/entities/Customer';
6 | import ICustomersRepository from '../repositories/ICustomersRepository';
7 |
8 | interface IRequest {
9 | name: string;
10 | email: string;
11 | }
12 |
13 | @injectable()
14 | class CreateCustomerService {
15 | constructor(
16 | @inject('CustomersRepository')
17 | private customersRepository: ICustomersRepository,
18 | ) {}
19 |
20 | public async execute({ name, email }: IRequest): Promise {
21 | const findCustomerWithSameEmail = await this.customersRepository.findByEmail(
22 | email,
23 | );
24 |
25 | if (findCustomerWithSameEmail) {
26 | throw new AppError(
27 | "There's already a customer registered with that email",
28 | );
29 | }
30 |
31 | const customer = await this.customersRepository.create({ name, email });
32 |
33 | return customer;
34 | }
35 | }
36 |
37 | export default CreateCustomerService;
38 |
--------------------------------------------------------------------------------
/src/modules/orders/dtos/ICreateOrderDTO.ts:
--------------------------------------------------------------------------------
1 | import Customer from '@modules/customers/infra/typeorm/entities/Customer';
2 |
3 | interface IProduct {
4 | product_id: string;
5 | price: number;
6 | quantity: number;
7 | }
8 |
9 | export default interface ICreateOrderDTO {
10 | customer: Customer;
11 | products: IProduct[];
12 | }
13 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/http/controller/OrdersController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | import { container } from 'tsyringe';
4 |
5 | import CreateOrderService from '@modules/orders/services/CreateOrderService';
6 | import FindOrderService from '@modules/orders/services/FindOrderService';
7 | import AppError from '@shared/errors/AppError';
8 |
9 | export default class OrdersController {
10 | public async show(request: Request, response: Response): Promise {
11 | const { id } = request.params;
12 |
13 | const findOrder = container.resolve(FindOrderService);
14 |
15 | const order = await findOrder.execute({ id });
16 |
17 | return response.json(order);
18 | }
19 |
20 | public async create(request: Request, response: Response): Promise {
21 | const { customer_id, products } = request.body;
22 |
23 | const hasInvalidData = !customer_id || !products || products.length <= 0;
24 |
25 | if (hasInvalidData) {
26 | throw new AppError(
27 | 'Please, provide valid data in order to create an order',
28 | );
29 | }
30 |
31 | const createOrder = container.resolve(CreateOrderService);
32 |
33 | const order = await createOrder.execute({ customer_id, products });
34 |
35 | return response.json(order);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/http/routes/orders.routes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import OrdersController from '@modules/orders/infra/http/controller/OrdersController';
4 | import createOrderValidator from '@modules/orders/infra/validators/createOrder';
5 | import showOrderValidator from '@modules/orders/infra/validators/showOrder';
6 |
7 | const ordersRouter = Router();
8 | const ordersController = new OrdersController();
9 |
10 | ordersRouter.post('/', createOrderValidator, ordersController.create);
11 | ordersRouter.get('/:id', showOrderValidator, ordersController.show);
12 |
13 | export default ordersRouter;
14 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/typeorm/entities/Order.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Column,
3 | Entity,
4 | ManyToOne,
5 | OneToMany,
6 | JoinColumn,
7 | CreateDateColumn,
8 | UpdateDateColumn,
9 | PrimaryGeneratedColumn,
10 | } from 'typeorm';
11 |
12 | import Customer from '@modules/customers/infra/typeorm/entities/Customer';
13 | import OrdersProducts from '@modules/orders/infra/typeorm/entities/OrdersProducts';
14 |
15 | @Entity('orders')
16 | class Order {
17 | @Column('uuid')
18 | @PrimaryGeneratedColumn('uuid')
19 | id: string;
20 |
21 | @ManyToOne(() => Customer, customer => customer.order, { eager: true })
22 | @JoinColumn({ name: 'customer_id' })
23 | customer: Customer;
24 |
25 | @OneToMany(() => OrdersProducts, ordersProducts => ordersProducts.order, {
26 | cascade: ['insert'],
27 | })
28 | orders_products: OrdersProducts[];
29 |
30 | @CreateDateColumn()
31 | created_at: Date;
32 |
33 | @UpdateDateColumn()
34 | updated_at: Date;
35 | }
36 |
37 | export default Order;
38 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/typeorm/entities/OrdersProducts.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | ManyToOne,
5 | JoinColumn,
6 | CreateDateColumn,
7 | UpdateDateColumn,
8 | PrimaryGeneratedColumn,
9 | } from 'typeorm';
10 |
11 | import Order from '@modules/orders/infra/typeorm/entities/Order';
12 | import Product from '@modules/products/infra/typeorm/entities/Product';
13 |
14 | @Entity('orders_products')
15 | class OrdersProducts {
16 | @Column('uuid')
17 | @PrimaryGeneratedColumn('uuid')
18 | id: string;
19 |
20 | @ManyToOne(() => Order, order => order.orders_products)
21 | @JoinColumn({ name: 'order_id' })
22 | order: Order;
23 |
24 | @ManyToOne(() => Product, product => product.orders_products)
25 | @JoinColumn({ name: 'product_id' })
26 | product: Product;
27 |
28 | @Column('uuid')
29 | product_id: string;
30 |
31 | @Column('uuid')
32 | order_id: string;
33 |
34 | @Column('decimal', { precision: 5, scale: 2 })
35 | price: number;
36 |
37 | @Column('integer')
38 | quantity: number;
39 |
40 | @CreateDateColumn()
41 | created_at: Date;
42 |
43 | @UpdateDateColumn()
44 | updated_at: Date;
45 | }
46 |
47 | export default OrdersProducts;
48 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/typeorm/repositories/OrdersRepository.ts:
--------------------------------------------------------------------------------
1 | import { getRepository, Repository } from 'typeorm';
2 |
3 | import IOrdersRepository from '@modules/orders/repositories/IOrdersRepository';
4 | import ICreateOrderDTO from '@modules/orders/dtos/ICreateOrderDTO';
5 | import Order from '@modules/orders/infra/typeorm/entities/Order';
6 |
7 | class OrdersRepository implements IOrdersRepository {
8 | private ormRepository: Repository;
9 |
10 | constructor() {
11 | this.ormRepository = getRepository(Order);
12 | }
13 |
14 | public async create({ customer, products }: ICreateOrderDTO): Promise {
15 | const order = this.ormRepository.create({
16 | customer,
17 | orders_products: products,
18 | });
19 |
20 | order.customer = customer;
21 |
22 | await this.ormRepository.save(order);
23 |
24 | return order;
25 | }
26 |
27 | public async findById(id: string): Promise {
28 | const order = await this.ormRepository.findOne({
29 | relations: ['orders_products', 'customer'],
30 | where: { id },
31 | });
32 |
33 | return order;
34 | }
35 | }
36 |
37 | export default OrdersRepository;
38 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/validators/createOrder.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Segments, Joi } from 'celebrate';
2 |
3 | const createOrderValidator = celebrate({
4 | [Segments.BODY]: {
5 | customer_id: Joi.string().uuid().required(),
6 | products: Joi.array()
7 | .items(
8 | Joi.object({
9 | id: Joi.string().uuid().required(),
10 | quantity: Joi.number().integer().positive().required(),
11 | }),
12 | )
13 | .required(),
14 | },
15 | });
16 |
17 | export default createOrderValidator;
18 |
--------------------------------------------------------------------------------
/src/modules/orders/infra/validators/showOrder.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Segments, Joi } from 'celebrate';
2 |
3 | const showOrderValidator = celebrate({
4 | [Segments.PARAMS]: {
5 | id: Joi.string().uuid().required(),
6 | },
7 | });
8 |
9 | export default showOrderValidator;
10 |
--------------------------------------------------------------------------------
/src/modules/orders/repositories/IOrdersRepository.ts:
--------------------------------------------------------------------------------
1 | import Order from '@modules/orders/infra/typeorm/entities/Order';
2 |
3 | import ICreateOrderDTO from '@modules/orders/dtos/ICreateOrderDTO';
4 |
5 | export default interface IOrdersRepository {
6 | create(data: ICreateOrderDTO): Promise;
7 | findById(id: string): Promise;
8 | }
9 |
--------------------------------------------------------------------------------
/src/modules/orders/services/CreateOrderService.ts:
--------------------------------------------------------------------------------
1 | import { inject, injectable } from 'tsyringe';
2 |
3 | import AppError from '@shared/errors/AppError';
4 |
5 | import IProductsRepository from '@modules/products/repositories/IProductsRepository';
6 | import ICustomersRepository from '@modules/customers/repositories/ICustomersRepository';
7 | import Order from '@modules/orders/infra/typeorm/entities/Order';
8 | import IOrdersRepository from '@modules/orders/repositories/IOrdersRepository';
9 |
10 | interface IProduct {
11 | id: string;
12 | quantity: number;
13 | }
14 |
15 | interface IRequest {
16 | customer_id: string;
17 | products: IProduct[];
18 | }
19 |
20 | @injectable()
21 | class CreateOrderService {
22 | constructor(
23 | @inject('OrdersRepository')
24 | private ordersRepository: IOrdersRepository,
25 |
26 | @inject('ProductsRepository')
27 | private productsRepository: IProductsRepository,
28 |
29 | @inject('CustomersRepository')
30 | private customersRepository: ICustomersRepository,
31 | ) {}
32 |
33 | public async execute({ customer_id, products }: IRequest): Promise {
34 | const customer = await this.customersRepository.findById(customer_id);
35 |
36 | if (!customer) {
37 | throw new AppError('Customer not found');
38 | }
39 |
40 | const productsIds = products.map(product => ({ id: product.id }));
41 |
42 | const originalProducts = await this.productsRepository.findAllById(
43 | productsIds,
44 | );
45 |
46 | if (!customer) {
47 | throw new AppError('Customer not found');
48 | }
49 |
50 | if (products.length <= 0) {
51 | throw new AppError('You must provide products to create an order');
52 | }
53 |
54 | const hasInvalidProducts =
55 | originalProducts.length <= 0 ||
56 | originalProducts.length !== products.length;
57 |
58 | if (hasInvalidProducts) {
59 | throw new AppError('Product(s) not found');
60 | }
61 |
62 | products.forEach(product => {
63 | const originalProduct = originalProducts.find(
64 | findProduct => findProduct.id === product.id,
65 | );
66 |
67 | const hasInsufficientQuantity =
68 | originalProduct && product.quantity > originalProduct.quantity;
69 |
70 | if (hasInsufficientQuantity) {
71 | throw new AppError(
72 | `The product ${originalProduct?.name} has insufficient quantity on the stock.`,
73 | );
74 | }
75 | });
76 |
77 | const orderProducts = products.map(product => {
78 | const originalProduct = originalProducts.find(
79 | findProduct => findProduct.id === product.id,
80 | );
81 |
82 | if (!originalProduct) {
83 | throw new AppError('Product not found');
84 | }
85 |
86 | return {
87 | product_id: originalProduct.id,
88 | quantity: product.quantity,
89 | price: originalProduct.price,
90 | };
91 | });
92 |
93 | const order = await this.ordersRepository.create({
94 | customer,
95 | products: orderProducts,
96 | });
97 |
98 | await this.productsRepository.updateQuantity(products);
99 |
100 | return order;
101 | }
102 | }
103 |
104 | export default CreateOrderService;
105 |
--------------------------------------------------------------------------------
/src/modules/orders/services/FindOrderService.ts:
--------------------------------------------------------------------------------
1 | import { inject, injectable } from 'tsyringe';
2 |
3 | import Order from '@modules/orders/infra/typeorm/entities/Order';
4 | import IOrdersRepository from '@modules/orders/repositories/IOrdersRepository';
5 |
6 | interface IRequest {
7 | id: string;
8 | }
9 |
10 | @injectable()
11 | class FindOrderService {
12 | constructor(
13 | @inject('OrdersRepository')
14 | private ordersRepository: IOrdersRepository,
15 | ) {}
16 |
17 | public async execute({ id }: IRequest): Promise {
18 | const order = await this.ordersRepository.findById(id);
19 |
20 | return order;
21 | }
22 | }
23 |
24 | export default FindOrderService;
25 |
--------------------------------------------------------------------------------
/src/modules/products/dtos/ICreateProductDTO.ts:
--------------------------------------------------------------------------------
1 | export default interface ICreateProductDTO {
2 | name: string;
3 | price: number;
4 | quantity: number;
5 | }
6 |
--------------------------------------------------------------------------------
/src/modules/products/dtos/IUpdateProductsQuantityDTO.ts:
--------------------------------------------------------------------------------
1 | export default interface IUpdateProductsQuantityDTO {
2 | id: string;
3 | quantity: number;
4 | }
5 |
--------------------------------------------------------------------------------
/src/modules/products/infra/http/controller/ProductsController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 | import { container } from 'tsyringe';
3 |
4 | import CreateProductService from '@modules/products/services/CreateProductService';
5 | import AppError from '@shared/errors/AppError';
6 | import Product from '@modules/products/infra/typeorm/entities/Product';
7 |
8 | export default class ProductsController {
9 | public async create(
10 | request: Request,
11 | response: Response,
12 | ): Promise> {
13 | const { name, price, quantity } = request.body;
14 |
15 | const hasInvalidData = !name || !price || !quantity;
16 |
17 | if (hasInvalidData) {
18 | throw new AppError(
19 | 'Please, provide valid data in order to create a product',
20 | );
21 | }
22 |
23 | const createProduct = container.resolve(CreateProductService);
24 |
25 | const product = await createProduct.execute({ name, price, quantity });
26 |
27 | return response.json(product);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/modules/products/infra/http/routes/products.routes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import ProductsController from '@modules/products/infra/http/controller/ProductsController';
4 | import createProductValidator from '../../validators/createProduct';
5 |
6 | const productsRouter = Router();
7 | const productsController = new ProductsController();
8 |
9 | productsRouter.post('/', createProductValidator, productsController.create);
10 |
11 | export default productsRouter;
12 |
--------------------------------------------------------------------------------
/src/modules/products/infra/typeorm/entities/Product.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | OneToMany,
5 | CreateDateColumn,
6 | UpdateDateColumn,
7 | PrimaryGeneratedColumn,
8 | } from 'typeorm';
9 |
10 | import OrdersProducts from '@modules/orders/infra/typeorm/entities/OrdersProducts';
11 |
12 | @Entity('products')
13 | class Product {
14 | @Column('uuid')
15 | @PrimaryGeneratedColumn('uuid')
16 | id: string;
17 |
18 | @Column()
19 | name: string;
20 |
21 | @Column('decimal', { precision: 5, scale: 2 })
22 | price: number;
23 |
24 | @Column('integer')
25 | quantity: number;
26 |
27 | @OneToMany(() => OrdersProducts, ordersProduct => ordersProduct.product)
28 | orders_products: OrdersProducts[];
29 |
30 | @CreateDateColumn()
31 | created_at: Date;
32 |
33 | @UpdateDateColumn()
34 | updated_at: Date;
35 | }
36 |
37 | export default Product;
38 |
--------------------------------------------------------------------------------
/src/modules/products/infra/typeorm/repositories/ProductsRepository.ts:
--------------------------------------------------------------------------------
1 | import { getRepository, Repository } from 'typeorm';
2 |
3 | import IProductsRepository from '@modules/products/repositories/IProductsRepository';
4 | import ICreateProductDTO from '@modules/products/dtos/ICreateProductDTO';
5 | import IUpdateProductsQuantityDTO from '@modules/products/dtos/IUpdateProductsQuantityDTO';
6 | import Product from '@modules/products/infra/typeorm/entities/Product';
7 | import AppError from '@shared/errors/AppError';
8 |
9 | interface IFindProducts {
10 | id: string;
11 | }
12 |
13 | class ProductsRepository implements IProductsRepository {
14 | private ormRepository: Repository;
15 |
16 | constructor() {
17 | this.ormRepository = getRepository(Product);
18 | }
19 |
20 | public async create({
21 | name,
22 | price,
23 | quantity,
24 | }: ICreateProductDTO): Promise {
25 | const product = this.ormRepository.create({
26 | name,
27 | price,
28 | quantity,
29 | });
30 |
31 | await this.ormRepository.save(product);
32 |
33 | return product;
34 | }
35 |
36 | public async findByName(name: string): Promise {
37 | const findProduct = await this.ormRepository.findOne({
38 | where: {
39 | name,
40 | },
41 | });
42 |
43 | return findProduct;
44 | }
45 |
46 | public async findAllById(productsIds: IFindProducts[]): Promise {
47 | const products = await this.ormRepository.findByIds(productsIds);
48 |
49 | return products;
50 | }
51 |
52 | public async updateQuantity(
53 | updateProductsData: IUpdateProductsQuantityDTO[],
54 | ): Promise {
55 | const productsIds = updateProductsData.map(product => product.id);
56 |
57 | const findProducts = await this.ormRepository.findByIds(productsIds);
58 |
59 | const updateProductsQuantity = findProducts.map(product => {
60 | const findUpdateProduct = updateProductsData.find(
61 | updateProduct => product.id === updateProduct.id,
62 | );
63 |
64 | if (!findUpdateProduct) {
65 | throw new AppError('Product not found');
66 | }
67 |
68 | return this.ormRepository.save({
69 | ...product,
70 | quantity: product.quantity - findUpdateProduct?.quantity,
71 | });
72 | });
73 |
74 | const products = await Promise.all(updateProductsQuantity);
75 |
76 | return products;
77 | }
78 | }
79 |
80 | export default ProductsRepository;
81 |
--------------------------------------------------------------------------------
/src/modules/products/infra/validators/createProduct.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Segments, Joi } from 'celebrate';
2 |
3 | const createProductValidator = celebrate({
4 | [Segments.BODY]: {
5 | name: Joi.string().required(),
6 | price: Joi.number().positive().required(),
7 | quantity: Joi.number().positive().required(),
8 | },
9 | });
10 |
11 | export default createProductValidator;
12 |
--------------------------------------------------------------------------------
/src/modules/products/repositories/IProductsRepository.ts:
--------------------------------------------------------------------------------
1 | import Product from '@modules/products/infra/typeorm/entities/Product';
2 | import ICreateProductDTO from '@modules/products/dtos/ICreateProductDTO';
3 | import IUpdateProductsQuantityDTO from '@modules/products/dtos/IUpdateProductsQuantityDTO';
4 |
5 | interface IFindProducts {
6 | id: string;
7 | }
8 |
9 | export default interface IProductsRepository {
10 | create(data: ICreateProductDTO): Promise;
11 | findByName(name: string): Promise;
12 | findAllById(productsIds: IFindProducts[]): Promise;
13 | updateQuantity(
14 | updateQuantityData: IUpdateProductsQuantityDTO[],
15 | ): Promise;
16 | }
17 |
--------------------------------------------------------------------------------
/src/modules/products/services/CreateProductService.ts:
--------------------------------------------------------------------------------
1 | import { inject, injectable } from 'tsyringe';
2 |
3 | import AppError from '@shared/errors/AppError';
4 | import Product from '@modules/products/infra/typeorm/entities/Product';
5 | import IProductsRepository from '@modules/products/repositories/IProductsRepository';
6 |
7 | interface IRequest {
8 | name: string;
9 | price: number;
10 | quantity: number;
11 | }
12 |
13 | @injectable()
14 | class CreateProductService {
15 | constructor(
16 | @inject('ProductsRepository')
17 | private productsRepository: IProductsRepository,
18 | ) {}
19 |
20 | public async execute({ name, price, quantity }: IRequest): Promise {
21 | const findProductWithSameName = await this.productsRepository.findByName(
22 | name,
23 | );
24 |
25 | if (findProductWithSameName) {
26 | throw new AppError(
27 | "There's already a product registered with that name. Please, try it again with another name.",
28 | );
29 | }
30 |
31 | const product = await this.productsRepository.create({
32 | name,
33 | price,
34 | quantity,
35 | });
36 |
37 | return product;
38 | }
39 | }
40 |
41 | export default CreateProductService;
42 |
--------------------------------------------------------------------------------
/src/modules/students/dtos/ICreateStudentDTO.ts:
--------------------------------------------------------------------------------
1 | export default interface ICreateStudentDTO {
2 | age: number;
3 | name: string;
4 | email: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/modules/students/infra/http/controllers/StudentsController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | import CreateStudentService from '@modules/students/services/CreateStudentService';
4 | import { container } from 'tsyringe';
5 | import Student from '../../typeorm/entities/Student';
6 |
7 | class StudentsController {
8 | public async create(
9 | request: Request,
10 | response: Response,
11 | ): Promise> {
12 | const { age, name, email } = request.body;
13 |
14 | const createStudent = container.resolve(CreateStudentService);
15 |
16 | const student = await createStudent.execute({ age, name, email });
17 |
18 | return response.json(student);
19 | }
20 | }
21 |
22 | export default StudentsController;
23 |
--------------------------------------------------------------------------------
/src/modules/students/infra/http/routes/students.routes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import createStudentValidator from '@modules/students/validators/createStudent';
4 | import StudentsController from '@modules/students/infra/http/controllers/StudentsController';
5 |
6 | const studentsRouter = Router();
7 | const studentsController = new StudentsController();
8 |
9 | studentsRouter.post('/', createStudentValidator, studentsController.create);
10 |
11 | export default studentsRouter;
12 |
--------------------------------------------------------------------------------
/src/modules/students/infra/typeorm/entities/Student.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | OneToMany,
5 | CreateDateColumn,
6 | UpdateDateColumn,
7 | PrimaryGeneratedColumn,
8 | } from 'typeorm';
9 | import StudentClasses from './StudentClasses';
10 |
11 | @Entity('students')
12 | class Student {
13 | @Column('uuid')
14 | @PrimaryGeneratedColumn('uuid')
15 | id: string;
16 |
17 | @Column('text')
18 | name: string;
19 |
20 | @Column('text')
21 | email: string;
22 |
23 | @Column('integer')
24 | age: number;
25 |
26 | @OneToMany(() => StudentClasses, studentClasses => studentClasses.student)
27 | student_classes: StudentClasses[];
28 |
29 | @CreateDateColumn()
30 | created_at: Date;
31 |
32 | @UpdateDateColumn()
33 | updated_at: Date;
34 | }
35 |
36 | export default Student;
37 |
--------------------------------------------------------------------------------
/src/modules/students/infra/typeorm/entities/StudentClasses.ts:
--------------------------------------------------------------------------------
1 | import Class from '@modules/classes/infra/typeorm/entities/Class';
2 | import {
3 | Entity,
4 | Column,
5 | JoinColumn,
6 | ManyToOne,
7 | CreateDateColumn,
8 | UpdateDateColumn,
9 | PrimaryGeneratedColumn,
10 | } from 'typeorm';
11 | import Student from './Student';
12 |
13 | @Entity('student_classes')
14 | class StudentClasses {
15 | @Column('uuid')
16 | @PrimaryGeneratedColumn('uuid')
17 | id: string;
18 |
19 | @ManyToOne(() => Student, student => student.student_classes)
20 | @JoinColumn({ name: 'student_id' })
21 | student: Student;
22 |
23 | @ManyToOne(() => Class, classEntity => classEntity.student_classes)
24 | @JoinColumn({ name: 'class_id' })
25 | class: Class;
26 |
27 | @Column('uuid')
28 | student_id: string;
29 |
30 | @Column('uuid')
31 | class_id: string;
32 |
33 | @CreateDateColumn()
34 | created_at: Date;
35 |
36 | @UpdateDateColumn()
37 | updated_at: Date;
38 | }
39 |
40 | export default StudentClasses;
41 |
--------------------------------------------------------------------------------
/src/modules/students/infra/typeorm/repositories/StudentsRepository.ts:
--------------------------------------------------------------------------------
1 | import { getRepository, Repository } from 'typeorm';
2 |
3 | import ICreateStudentDTO from '@modules/students/dtos/ICreateStudentDTO';
4 | import IStudentsRepository from '@modules/students/repositories/IStudentsRepository';
5 | import Student from '@modules/students/infra/typeorm/entities/Student';
6 |
7 | class StudentsRepository implements IStudentsRepository {
8 | private ormRepository: Repository;
9 |
10 | constructor() {
11 | this.ormRepository = getRepository(Student);
12 | }
13 |
14 | public async create({
15 | age,
16 | name,
17 | email,
18 | }: ICreateStudentDTO): Promise {
19 | const student = this.ormRepository.create({ name, email, age });
20 |
21 | await this.ormRepository.save(student);
22 |
23 | return student;
24 | }
25 |
26 | public async findByEmail(email: string): Promise {
27 | const student = await this.ormRepository.findOne({ where: { email } });
28 |
29 | return student;
30 | }
31 |
32 | public async findOneOrFail(id: string): Promise {
33 | const student = await this.ormRepository.findOneOrFail(id);
34 |
35 | return student;
36 | }
37 |
38 | public async findByIds(data: string[]): Promise {
39 | const students = await this.ormRepository.findByIds(data);
40 |
41 | return students;
42 | }
43 | }
44 |
45 | export default StudentsRepository;
46 |
--------------------------------------------------------------------------------
/src/modules/students/repositories/IStudentsRepository.ts:
--------------------------------------------------------------------------------
1 | import Student from '@modules/students/infra/typeorm/entities/Student';
2 | import ICreateStudentDTO from '@modules/students/dtos/ICreateStudentDTO';
3 |
4 | export default interface IStudentsRepository {
5 | create(data: ICreateStudentDTO): Promise;
6 | findByIds(data: string[]): Promise;
7 | findOneOrFail(id: string): Promise;
8 | findByEmail(email: string): Promise;
9 | }
10 |
--------------------------------------------------------------------------------
/src/modules/students/services/CreateStudentService.ts:
--------------------------------------------------------------------------------
1 | import AppError from '@shared/errors/AppError';
2 | import { inject, injectable } from 'tsyringe';
3 |
4 | import Student from '@modules/students/infra/typeorm/entities/Student';
5 | import IStudentsRepository from '@modules/students/repositories/IStudentsRepository';
6 |
7 | interface IRequest {
8 | age: number;
9 | name: string;
10 | email: string;
11 | }
12 |
13 | @injectable()
14 | class CreateStudentService {
15 | constructor(
16 | @inject('StudentsRepository')
17 | private studentsRepository: IStudentsRepository,
18 | ) {}
19 |
20 | public async execute({ age, name, email }: IRequest): Promise {
21 | const findStudentWithSameEmail = await this.studentsRepository.findByEmail(
22 | email,
23 | );
24 |
25 | if (findStudentWithSameEmail) {
26 | throw new AppError(
27 | "There's already a student registered with that email. Please, try it again with another email.",
28 | );
29 | }
30 |
31 | const student = await this.studentsRepository.create({ name, email, age });
32 |
33 | return student;
34 | }
35 | }
36 |
37 | export default CreateStudentService;
38 |
--------------------------------------------------------------------------------
/src/modules/students/validators/createStudent.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Joi, Segments } from 'celebrate';
2 |
3 | const createStudentValidator = celebrate({
4 | [Segments.BODY]: {
5 | age: Joi.number().integer().positive().required(),
6 | name: Joi.string().required(),
7 | email: Joi.string().email().required(),
8 | },
9 | });
10 |
11 | export default createStudentValidator;
12 |
--------------------------------------------------------------------------------
/src/modules/teachers/dtos/ICreateTeacherDTO.ts:
--------------------------------------------------------------------------------
1 | export default interface ICreateTeacherDTO {
2 | age: number;
3 | name: string;
4 | email: string;
5 | }
6 |
--------------------------------------------------------------------------------
/src/modules/teachers/infra/http/controllers/TeachersController.ts:
--------------------------------------------------------------------------------
1 | import { Request, Response } from 'express';
2 |
3 | import { container } from 'tsyringe';
4 |
5 | import Teacher from '@modules/teachers/infra/typeorm/entities/Teacher';
6 | import CreateTeacherService from '@modules/teachers/services/CreateTeacherService';
7 |
8 | class TeachersController {
9 | public async create(
10 | request: Request,
11 | response: Response,
12 | ): Promise> {
13 | const { age, name, email } = request.body;
14 |
15 | const createTeacher = container.resolve(CreateTeacherService);
16 |
17 | const teacher = await createTeacher.execute({ age, name, email });
18 |
19 | return response.json(teacher);
20 | }
21 | }
22 |
23 | export default TeachersController;
24 |
--------------------------------------------------------------------------------
/src/modules/teachers/infra/http/routes/teachers.routes.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import TeachersController from '@modules/teachers/infra/http/controllers/TeachersController';
4 | import createTeacherValidator from '@modules/teachers/validators/createTeacher';
5 |
6 | const teachersRouter = Router();
7 | const teachersController = new TeachersController();
8 |
9 | teachersRouter.post('/', createTeacherValidator, teachersController.create);
10 |
11 | export default teachersRouter;
12 |
--------------------------------------------------------------------------------
/src/modules/teachers/infra/typeorm/entities/Teacher.ts:
--------------------------------------------------------------------------------
1 | import {
2 | Entity,
3 | Column,
4 | OneToMany,
5 | CreateDateColumn,
6 | UpdateDateColumn,
7 | PrimaryGeneratedColumn,
8 | } from 'typeorm';
9 |
10 | import Class from '@modules/classes/infra/typeorm/entities/Class';
11 |
12 | @Entity('teachers')
13 | class Teacher {
14 | @Column('uuid')
15 | @PrimaryGeneratedColumn('uuid')
16 | id: string;
17 |
18 | @Column('text')
19 | name: string;
20 |
21 | @Column('text')
22 | email: string;
23 |
24 | @Column('integer')
25 | age: number;
26 |
27 | @OneToMany(() => Class, classRegister => classRegister.teacher)
28 | classes: Class[];
29 |
30 | @CreateDateColumn()
31 | created_at: Date;
32 |
33 | @UpdateDateColumn()
34 | updated_at: Date;
35 | }
36 |
37 | export default Teacher;
38 |
--------------------------------------------------------------------------------
/src/modules/teachers/infra/typeorm/repositories/TeachersRepository.ts:
--------------------------------------------------------------------------------
1 | import { getRepository, Repository } from 'typeorm';
2 |
3 | import ICreateTeacherDTO from '@modules/teachers/dtos/ICreateTeacherDTO';
4 | import ITeachersRepository from '@modules/teachers/repositories/ITeachersRepository';
5 | import Teacher from '@modules/teachers/infra/typeorm/entities/Teacher';
6 |
7 | class TeachersRepository implements ITeachersRepository {
8 | private ormRepository: Repository;
9 |
10 | constructor() {
11 | this.ormRepository = getRepository(Teacher);
12 | }
13 |
14 | public async create({
15 | age,
16 | name,
17 | email,
18 | }: ICreateTeacherDTO): Promise {
19 | const teacher = this.ormRepository.create({ name, email, age });
20 |
21 | await this.ormRepository.save(teacher);
22 |
23 | return teacher;
24 | }
25 |
26 | public async findByEmail(email: string): Promise {
27 | const teacher = await this.ormRepository.findOne({ where: { email } });
28 |
29 | return teacher;
30 | }
31 |
32 | public async findById(id: string): Promise {
33 | const teacher = await this.ormRepository.findOne(id);
34 |
35 | return teacher;
36 | }
37 | }
38 |
39 | export default TeachersRepository;
40 |
--------------------------------------------------------------------------------
/src/modules/teachers/repositories/ITeachersRepository.ts:
--------------------------------------------------------------------------------
1 | import Teacher from '@modules/teachers/infra/typeorm/entities/Teacher';
2 | import ICreateTeacherDTO from '@modules/teachers/dtos/ICreateTeacherDTO';
3 |
4 | export default interface ITeachersRepository {
5 | create(data: ICreateTeacherDTO): Promise;
6 | findById(id: string): Promise;
7 | findByEmail(email: string): Promise;
8 | }
9 |
--------------------------------------------------------------------------------
/src/modules/teachers/services/CreateTeacherService.ts:
--------------------------------------------------------------------------------
1 | import { inject, injectable } from 'tsyringe';
2 |
3 | import Teacher from '@modules/teachers/infra/typeorm/entities/Teacher';
4 | import ITeachersRepository from '@modules/teachers/repositories/ITeachersRepository';
5 | import AppError from '@shared/errors/AppError';
6 |
7 | interface IRequest {
8 | age: number;
9 | name: string;
10 | email: string;
11 | }
12 |
13 | @injectable()
14 | class CreateTeacherService {
15 | constructor(
16 | @inject('TeachersRepository')
17 | private teachersRepository: ITeachersRepository,
18 | ) {}
19 |
20 | public async execute({ age, name, email }: IRequest): Promise {
21 | const findTeacherWithSameEmail = await this.teachersRepository.findByEmail(
22 | email,
23 | );
24 |
25 | if (findTeacherWithSameEmail) {
26 | throw new AppError(
27 | "There's already a teacher registered with that email. Please, try it again with another email.",
28 | );
29 | }
30 |
31 | const teacher = await this.teachersRepository.create({ age, name, email });
32 |
33 | return teacher;
34 | }
35 | }
36 |
37 | export default CreateTeacherService;
38 |
--------------------------------------------------------------------------------
/src/modules/teachers/validators/createTeacher.ts:
--------------------------------------------------------------------------------
1 | import { celebrate, Segments, Joi } from 'celebrate';
2 |
3 | const createTeacherValidator = celebrate({
4 | [Segments.BODY]: {
5 | name: Joi.string().required(),
6 | email: Joi.string().email().required(),
7 | age: Joi.number().integer().positive().required(),
8 | },
9 | });
10 |
11 | export default createTeacherValidator;
12 |
--------------------------------------------------------------------------------
/src/shared/container/index.ts:
--------------------------------------------------------------------------------
1 | import { container } from 'tsyringe';
2 |
3 | import ICustomersRepository from '@modules/customers/repositories/ICustomersRepository';
4 | import CustomersRepository from '@modules/customers/infra/typeorm/repositories/CustomersRepository';
5 |
6 | import IProductsRepository from '@modules/products/repositories/IProductsRepository';
7 | import ProductsRepository from '@modules/products/infra/typeorm/repositories/ProductsRepository';
8 |
9 | import IOrdersRepository from '@modules/orders/repositories/IOrdersRepository';
10 | import OrdersRepository from '@modules/orders/infra/typeorm/repositories/OrdersRepository';
11 |
12 | import IStudentsRepository from '@modules/students/repositories/IStudentsRepository';
13 | import StudentsRepository from '@modules/students/infra/typeorm/repositories/StudentsRepository';
14 |
15 | import ITeachersRepository from '@modules/teachers/repositories/ITeachersRepository';
16 | import TeachersRepository from '@modules/teachers/infra/typeorm/repositories/TeachersRepository';
17 | import IClassesRepository from '@modules/classes/repositories/IClassesRepository';
18 | import ClassesRepository from '@modules/classes/infra/typeorm/repositories/ClassesRepository';
19 |
20 | container.registerSingleton(
21 | 'CustomersRepository',
22 | CustomersRepository,
23 | );
24 |
25 | container.registerSingleton(
26 | 'ProductsRepository',
27 | ProductsRepository,
28 | );
29 |
30 | container.registerSingleton(
31 | 'OrdersRepository',
32 | OrdersRepository,
33 | );
34 |
35 | container.registerSingleton(
36 | 'StudentsRepository',
37 | StudentsRepository,
38 | );
39 |
40 | container.registerSingleton(
41 | 'TeachersRepository',
42 | TeachersRepository,
43 | );
44 |
45 | container.registerSingleton(
46 | 'ClassesRepository',
47 | ClassesRepository,
48 | );
49 |
--------------------------------------------------------------------------------
/src/shared/errors/AppError.ts:
--------------------------------------------------------------------------------
1 | class AppError {
2 | public readonly message: string;
3 |
4 | public readonly statusCode: number;
5 |
6 | constructor(message: string, statusCode = 400) {
7 | this.message = message;
8 | this.statusCode = statusCode;
9 | }
10 | }
11 |
12 | export default AppError;
13 |
--------------------------------------------------------------------------------
/src/shared/handlers/errorsHandler.ts:
--------------------------------------------------------------------------------
1 | import { Response, Request, NextFunction } from 'express';
2 |
3 | import AppError from '@shared/errors/AppError';
4 |
5 | const errorsHandler = (
6 | error: Error,
7 | _request: Request,
8 | response: Response,
9 | _: NextFunction,
10 | ): Response => {
11 | if (error instanceof AppError) {
12 | return response.status(error.statusCode).json({
13 | status: 'error',
14 | message: error.message,
15 | });
16 | }
17 |
18 | // eslint-disable-next-line no-console
19 | console.error(error);
20 |
21 | return response.status(500).json({
22 | status: 'error',
23 | message: 'Internal Server Error',
24 | });
25 | };
26 |
27 | export default errorsHandler;
28 |
--------------------------------------------------------------------------------
/src/shared/infra/http/app.ts:
--------------------------------------------------------------------------------
1 | import 'reflect-metadata';
2 | import 'express-async-errors';
3 |
4 | import express from 'express';
5 | import cors from 'cors';
6 | import { errors as validationErrorsHandler } from 'celebrate';
7 |
8 | import createConnection from '@shared/infra/typeorm';
9 | import errorsHandler from '@shared/handlers/errorsHandler';
10 | import '@shared/container';
11 |
12 | import routes from './routes';
13 |
14 | createConnection();
15 |
16 | const app = express();
17 |
18 | app.use(cors());
19 | app.use(express.json());
20 | app.use(routes);
21 |
22 | app.use(validationErrorsHandler());
23 | app.use(errorsHandler);
24 |
25 | export default app;
26 |
--------------------------------------------------------------------------------
/src/shared/infra/http/routes/index.ts:
--------------------------------------------------------------------------------
1 | import { Router } from 'express';
2 |
3 | import customersRouter from '@modules/customers/infra/http/routes/customers.routes';
4 | import productsRouter from '@modules/products/infra/http/routes/products.routes';
5 | import ordersRouter from '@modules/orders/infra/http/routes/orders.routes';
6 |
7 | import studentsRouter from '@modules/students/infra/http/routes/students.routes';
8 | import teachersRouter from '@modules/teachers/infra/http/routes/teachers.routes';
9 | import classesRouter from '@modules/classes/infra/http/routes/classes.routes';
10 |
11 | const routes = Router();
12 |
13 | routes.use('/customers', customersRouter);
14 | routes.use('/products', productsRouter);
15 | routes.use('/orders', ordersRouter);
16 |
17 | routes.use('/students', studentsRouter);
18 | routes.use('/teachers', teachersRouter);
19 | routes.use('/classes', classesRouter);
20 |
21 | export default routes;
22 |
--------------------------------------------------------------------------------
/src/shared/infra/http/server.ts:
--------------------------------------------------------------------------------
1 | import app from './app';
2 |
3 | app.listen(3333, () => {
4 | // eslint-disable-next-line no-console
5 | console.log('🚀 Server started on port 3333!');
6 | });
7 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/index.ts:
--------------------------------------------------------------------------------
1 | import { createConnection, getConnectionOptions, Connection } from 'typeorm';
2 |
3 | export default async (name = 'default'): Promise => {
4 | const defaultOptions = await getConnectionOptions();
5 |
6 | return createConnection(
7 | Object.assign(defaultOptions, {
8 | name,
9 | database:
10 | process.env.NODE_ENV === 'test'
11 | ? 'gostack_desafio09_tests'
12 | : defaultOptions.database,
13 | }),
14 | );
15 | };
16 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598266682467-create_customers_table.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table } from 'typeorm';
2 |
3 | export default class CreateCustomersTable1598266682467
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.createTable(
7 | new Table({
8 | name: 'customers',
9 | columns: [
10 | {
11 | name: 'id',
12 | type: 'varchar',
13 | generationStrategy: 'uuid',
14 | default: 'uuid_generate_v4()',
15 | isPrimary: true,
16 | },
17 | {
18 | name: 'name',
19 | type: 'varchar',
20 | isNullable: false,
21 | },
22 | {
23 | name: 'email',
24 | type: 'varchar',
25 | isNullable: false,
26 | },
27 | {
28 | name: 'created_at',
29 | type: 'date',
30 | default: 'now()',
31 | },
32 | {
33 | name: 'updated_at',
34 | type: 'date',
35 | default: 'now()',
36 | },
37 | ],
38 | }),
39 | );
40 | }
41 |
42 | public async down(queryRunner: QueryRunner): Promise {
43 | await queryRunner.dropTable('customers');
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598269989216-CreateProductTable.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table } from 'typeorm';
2 |
3 | export default class CreateProductTable1598269989216
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.createTable(
7 | new Table({
8 | name: 'products',
9 | columns: [
10 | {
11 | name: 'id',
12 | type: 'varchar',
13 | default: 'uuid_generate_v4()',
14 | isPrimary: true,
15 | generationStrategy: 'uuid',
16 | },
17 | {
18 | name: 'name',
19 | type: 'varchar',
20 | isNullable: false,
21 | },
22 | {
23 | name: 'price',
24 | type: 'decimal',
25 | scale: 2,
26 | precision: 5,
27 | isNullable: false,
28 | },
29 | {
30 | name: 'quantity',
31 | type: 'integer',
32 | isNullable: false,
33 | },
34 | {
35 | name: 'created_at',
36 | type: 'date',
37 | default: 'now()',
38 | },
39 | {
40 | name: 'updated_at',
41 | type: 'date',
42 | default: 'now()',
43 | },
44 | ],
45 | }),
46 | );
47 | }
48 |
49 | public async down(queryRunner: QueryRunner): Promise {
50 | await queryRunner.dropTable('products');
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598279750914-CreateOrderTable.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table } from 'typeorm';
2 |
3 | export default class CreateOrderTable1598279750914
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.createTable(
7 | new Table({
8 | name: 'orders',
9 | columns: [
10 | {
11 | name: 'id',
12 | type: 'varchar',
13 | default: 'uuid_generate_v4()',
14 | isPrimary: true,
15 | generationStrategy: 'uuid',
16 | },
17 | {
18 | name: 'customer_id',
19 | type: 'varchar',
20 | default: 'uuid_generate_v4()',
21 | generationStrategy: 'uuid',
22 | },
23 | {
24 | name: 'created_at',
25 | type: 'timestamp',
26 | default: 'now()',
27 | },
28 | {
29 | name: 'updated_at',
30 | type: 'timestamp',
31 | default: 'now()',
32 | },
33 | ],
34 | foreignKeys: [
35 | {
36 | name: 'customer_order_fk',
37 | onUpdate: 'CASCADE',
38 | onDelete: 'CASCADE',
39 | columnNames: ['customer_id'],
40 | referencedColumnNames: ['id'],
41 | referencedTableName: 'customers',
42 | },
43 | ],
44 | }),
45 | );
46 | }
47 |
48 | public async down(queryRunner: QueryRunner): Promise {
49 | await queryRunner.dropTable('orders');
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598353275953-CreateOrderProductsTable.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table } from 'typeorm';
2 |
3 | export default class CreateOrderProductsTable1598353275953
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.createTable(
7 | new Table({
8 | name: 'orders_products',
9 | columns: [
10 | {
11 | name: 'id',
12 | type: 'uuid',
13 | isPrimary: true,
14 | generationStrategy: 'uuid',
15 | },
16 | {
17 | name: 'price',
18 | type: 'decimal',
19 | precision: 5,
20 | scale: 2,
21 | },
22 | {
23 | name: 'quantity',
24 | type: 'integer',
25 | },
26 | {
27 | name: 'order_id',
28 | type: 'varchar',
29 | },
30 | {
31 | name: 'product_id',
32 | type: 'varchar',
33 | },
34 | {
35 | name: 'created_at',
36 | type: 'timestamp',
37 | default: 'now()',
38 | },
39 | {
40 | name: 'updated_at',
41 | type: 'timestamp',
42 | default: 'now()',
43 | },
44 | ],
45 | foreignKeys: [
46 | {
47 | name: 'orders_order_products_fk',
48 | onUpdate: 'CASCADE',
49 | onDelete: 'CASCADE',
50 | columnNames: ['order_id'],
51 | referencedColumnNames: ['id'],
52 | referencedTableName: 'orders',
53 | },
54 | {
55 | name: 'order_products_products_fk',
56 | onUpdate: 'CASCADE',
57 | onDelete: 'CASCADE',
58 | columnNames: ['product_id'],
59 | referencedColumnNames: ['id'],
60 | referencedTableName: 'products',
61 | },
62 | ],
63 | }),
64 | );
65 | }
66 |
67 | public async down(queryRunner: QueryRunner): Promise {
68 | await queryRunner.dropTable('orders_products');
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598359381489-UpdateOrdersProductsIdColumn.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';
2 |
3 | export default class UpdateOrdersProductsIdColumn1598359381489
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.changeColumn(
7 | 'orders_products',
8 | 'id',
9 | new TableColumn({
10 | name: 'id',
11 | type: 'uuid',
12 | default: 'uuid_generate_v4()',
13 | isPrimary: true,
14 | generationStrategy: 'uuid',
15 | }),
16 | );
17 | }
18 |
19 | public async down(queryRunner: QueryRunner): Promise {
20 | await queryRunner.changeColumn(
21 | 'orders_products',
22 | 'id',
23 | new TableColumn({
24 | name: 'id',
25 | type: 'uuid',
26 | isPrimary: true,
27 | generationStrategy: 'uuid',
28 | }),
29 | );
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598364937210-UpdatePricePrecisions.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';
2 |
3 | export default class UpdatePricePrecisions1598364937210
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.changeColumn(
7 | 'orders_products',
8 | 'price',
9 | new TableColumn({
10 | name: 'price',
11 | type: 'decimal',
12 | precision: 10,
13 | scale: 2,
14 | }),
15 | );
16 |
17 | await queryRunner.changeColumn(
18 | 'products',
19 | 'price',
20 | new TableColumn({
21 | name: 'price',
22 | type: 'decimal',
23 | precision: 10,
24 | scale: 2,
25 | }),
26 | );
27 | }
28 |
29 | public async down(queryRunner: QueryRunner): Promise {
30 | await queryRunner.changeColumn(
31 | 'orders_products',
32 | 'price',
33 | new TableColumn({
34 | name: 'price',
35 | type: 'decimal',
36 | precision: 5,
37 | scale: 2,
38 | }),
39 | );
40 |
41 | await queryRunner.changeColumn(
42 | 'products',
43 | 'price',
44 | new TableColumn({
45 | name: 'price',
46 | type: 'decimal',
47 | precision: 5,
48 | scale: 2,
49 | }),
50 | );
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598871336598-Student.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm';
2 |
3 | export default class Student1598871336598 implements MigrationInterface {
4 | public async up(queryRunner: QueryRunner): Promise {
5 | await queryRunner.createTable(
6 | new Table({
7 | name: 'students',
8 | columns: [
9 | new TableColumn({
10 | name: 'id',
11 | type: 'uuid',
12 | default: 'uuid_generate_v4()',
13 | isPrimary: true,
14 | generationStrategy: 'uuid',
15 | }),
16 | new TableColumn({
17 | name: 'name',
18 | type: 'text',
19 | }),
20 | new TableColumn({
21 | name: 'email',
22 | type: 'text',
23 | }),
24 | new TableColumn({
25 | name: 'age',
26 | type: 'integer',
27 | }),
28 | new TableColumn({
29 | name: 'created_at',
30 | type: 'timestamp',
31 | default: 'now()',
32 | }),
33 | new TableColumn({
34 | name: 'updated_at',
35 | type: 'timestamp',
36 | default: 'now()',
37 | }),
38 | ],
39 | }),
40 | );
41 | }
42 |
43 | public async down(queryRunner: QueryRunner): Promise {
44 | await queryRunner.dropTable('students');
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598874390567-Teacher.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm';
2 |
3 | export default class Teacher1598874390567 implements MigrationInterface {
4 | public async up(queryRunner: QueryRunner): Promise {
5 | await queryRunner.createTable(
6 | new Table({
7 | name: 'teachers',
8 | columns: [
9 | new TableColumn({
10 | name: 'id',
11 | type: 'uuid',
12 | default: 'uuid_generate_v4()',
13 | isPrimary: true,
14 | generationStrategy: 'uuid',
15 | }),
16 | new TableColumn({
17 | name: 'name',
18 | type: 'text',
19 | }),
20 | new TableColumn({
21 | name: 'email',
22 | type: 'text',
23 | }),
24 | new TableColumn({
25 | name: 'age',
26 | type: 'integer',
27 | }),
28 | new TableColumn({
29 | name: 'created_at',
30 | type: 'timestamp',
31 | default: 'now()',
32 | }),
33 | new TableColumn({
34 | name: 'updated_at',
35 | type: 'timestamp',
36 | default: 'now()',
37 | }),
38 | ],
39 | }),
40 | );
41 | }
42 |
43 | public async down(queryRunner: QueryRunner): Promise {
44 | await queryRunner.dropTable('teachers');
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598879822720-Classes.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm';
2 |
3 | export default class Classes1598879822720 implements MigrationInterface {
4 | public async up(queryRunner: QueryRunner): Promise {
5 | await queryRunner.createTable(
6 | new Table({
7 | name: 'classes',
8 | columns: [
9 | new TableColumn({
10 | name: 'id',
11 | type: 'uuid',
12 | default: 'uuid_generate_v4()',
13 | isPrimary: true,
14 | generationStrategy: 'uuid',
15 | }),
16 | new TableColumn({
17 | name: 'subject',
18 | type: 'text',
19 | }),
20 | new TableColumn({
21 | name: 'teacher_id',
22 | type: 'uuid',
23 | }),
24 | new TableColumn({
25 | name: 'created_at',
26 | type: 'timestamp',
27 | }),
28 | new TableColumn({
29 | name: 'updated_at',
30 | type: 'timestamp',
31 | }),
32 | ],
33 | foreignKeys: [
34 | {
35 | name: 'class_teachers_fk',
36 | onUpdate: 'CASCADE',
37 | onDelete: 'SET NULL',
38 | columnNames: ['teacher_id'],
39 | referencedColumnNames: ['id'],
40 | referencedTableName: 'teachers',
41 | },
42 | ],
43 | }),
44 | );
45 | }
46 |
47 | public async down(queryRunner: QueryRunner): Promise {
48 | await queryRunner.dropTable('classes');
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598883848517-AddNowDefaultToTimestampColumns.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, TableColumn } from 'typeorm';
2 |
3 | export default class AddNowDefaultToTimestampColumns1598883848517
4 | implements MigrationInterface {
5 | public async up(queryRunner: QueryRunner): Promise {
6 | await queryRunner.changeColumn(
7 | 'classes',
8 | new TableColumn({
9 | name: 'created_at',
10 | type: 'timestamp',
11 | }),
12 | new TableColumn({
13 | name: 'created_at',
14 | type: 'timestamp',
15 | default: 'now()',
16 | }),
17 | );
18 |
19 | await queryRunner.changeColumn(
20 | 'classes',
21 | new TableColumn({
22 | name: 'updated_at',
23 | type: 'timestamp',
24 | }),
25 | new TableColumn({
26 | name: 'updated_at',
27 | type: 'timestamp',
28 | default: 'now()',
29 | }),
30 | );
31 | }
32 |
33 | public async down(queryRunner: QueryRunner): Promise {
34 | await queryRunner.changeColumn(
35 | 'classes',
36 | new TableColumn({
37 | name: 'created_at',
38 | type: 'timestamp',
39 | default: 'now()',
40 | }),
41 | new TableColumn({
42 | name: 'created_at',
43 | type: 'timestamp',
44 | }),
45 | );
46 |
47 | await queryRunner.changeColumn(
48 | 'classes',
49 | new TableColumn({
50 | name: 'updated_at',
51 | type: 'timestamp',
52 | default: 'now()',
53 | }),
54 | new TableColumn({
55 | name: 'updated_at',
56 | type: 'timestamp',
57 | }),
58 | );
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/shared/infra/typeorm/migrations/1598885739547-StudentClasses.ts:
--------------------------------------------------------------------------------
1 | import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm';
2 |
3 | export default class StudentClasses1598885739547 implements MigrationInterface {
4 | public async up(queryRunner: QueryRunner): Promise {
5 | await queryRunner.createTable(
6 | new Table({
7 | name: 'student_classes',
8 | columns: [
9 | new TableColumn({
10 | name: 'id',
11 | type: 'uuid',
12 | isPrimary: true,
13 | default: 'uuid_generate_v4()',
14 | generationStrategy: 'uuid',
15 | }),
16 | new TableColumn({
17 | name: 'student_id',
18 | type: 'uuid',
19 | }),
20 | new TableColumn({
21 | name: 'class_id',
22 | type: 'uuid',
23 | }),
24 | new TableColumn({
25 | name: 'updated_at',
26 | type: 'timestamp',
27 | default: 'now()',
28 | }),
29 | new TableColumn({
30 | name: 'created_at',
31 | type: 'timestamp',
32 | default: 'now()',
33 | }),
34 | ],
35 | foreignKeys: [
36 | {
37 | name: 'student_classes_students_fk',
38 | onUpdate: 'CASCADE',
39 | onDelete: 'CASCADE',
40 | columnNames: ['student_id'],
41 | referencedColumnNames: ['id'],
42 | referencedTableName: 'students',
43 | },
44 | {
45 | name: 'student_classes_classes_fk',
46 | onUpdate: 'CASCADE',
47 | onDelete: 'CASCADE',
48 | columnNames: ['class_id'],
49 | referencedColumnNames: ['id'],
50 | referencedTableName: 'classes',
51 | },
52 | ],
53 | }),
54 | );
55 | }
56 |
57 | public async down(queryRunner: QueryRunner): Promise {
58 | await queryRunner.dropTable('student_classes');
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Basic Options */
4 | // "incremental": true, /* Enable incremental compilation */
5 | "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */,
6 | "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */,
7 | // "lib": [], /* Specify library files to be included in the compilation. */
8 | // "allowJs": true, /* Allow javascript files to be compiled. */
9 | // "checkJs": true, /* Report errors in .js files. */
10 | // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */
11 | // "declaration": true, /* Generates corresponding '.d.ts' file. */
12 | // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
13 | // "sourceMap": true, /* Generates corresponding '.map' file. */
14 | // "outFile": "./", /* Concatenate and emit output to single file. */
15 | "outDir": "./dist" /* Redirect output structure to the directory. */,
16 | "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
17 | // "composite": true, /* Enable project compilation */
18 | // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
19 | // "removeComments": true, /* Do not emit comments to output. */
20 | // "noEmit": true, /* Do not emit outputs. */
21 | // "importHelpers": true, /* Import emit helpers from 'tslib'. */
22 | // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
23 | // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
24 |
25 | /* Strict Type-Checking Options */
26 | "strict": true /* Enable all strict type-checking options. */,
27 | // "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
28 | // "strictNullChecks": true, /* Enable strict null checks. */
29 | // "strictFunctionTypes": true, /* Enable strict checking of function types. */
30 | // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
31 | "strictPropertyInitialization": false /* Enable strict checking of property initialization in classes. */,
32 | // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
33 | // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
34 |
35 | /* Additional Checks */
36 | // "noUnusedLocals": true, /* Report errors on unused locals. */
37 | // "noUnusedParameters": true, /* Report errors on unused parameters. */
38 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
39 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
40 |
41 | /* Module Resolution Options */
42 | // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
43 | "baseUrl": "./src" /* Base directory to resolve non-absolute module names. */,
44 | "paths": {
45 | "@modules/*": ["modules/*"],
46 | "@config/*": ["config/*"],
47 | "@shared/*": ["shared/*"]
48 | } /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */,
49 | // "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
50 | // "typeRoots": [], /* List of folders to include type definitions from. */
51 | // "types": [], /* Type declaration files to be included in compilation. */
52 | // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
53 | "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
54 | // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
55 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
56 |
57 | /* Source Map Options */
58 | // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
59 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
60 | // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
61 | // "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
62 |
63 | /* Experimental Options */
64 | "experimentalDecorators": true /* Enables experimental support for ES7 decorators. */,
65 | "emitDecoratorMetadata": true /* Enables experimental support for emitting type metadata for decorators. */,
66 |
67 | /* Advanced Options */
68 | "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
69 | }
70 | }
71 |
--------------------------------------------------------------------------------