├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── .vscode └── settings.json ├── README.md ├── docker-compose.yml ├── nest-cli.json ├── package-lock.json ├── package.json ├── src ├── app.controller.spec.ts ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── common │ ├── parse-int.pipe.spec.ts │ └── parse-int.pipe.ts ├── config.ts ├── database │ ├── database.module.ts │ └── migrations │ │ └── 1616007004884-init.ts ├── enviroments.ts ├── main.ts ├── products │ ├── controllers │ │ ├── brands.controller.ts │ │ ├── categories.controller.ts │ │ └── products.controller.ts │ ├── dtos │ │ ├── brand.dtos.ts │ │ ├── category.dtos.ts │ │ └── products.dtos.ts │ ├── entities │ │ ├── brand.entity.ts │ │ ├── category.entity.ts │ │ └── product.entity.ts │ ├── products.module.ts │ └── services │ │ ├── brands.service.ts │ │ ├── categories.service.ts │ │ └── products.service.ts ├── recap.ts └── users │ ├── controllers │ ├── customers.controller.ts │ ├── order-item.controller.ts │ ├── orders.controller.ts │ └── users.controller.ts │ ├── dtos │ ├── customer.dto.ts │ ├── order-item.dto.ts │ ├── order.dto.ts │ └── user.dto.ts │ ├── entities │ ├── customer.entity.ts │ ├── order-item.entity.ts │ ├── order.entity.ts │ └── user.entity.ts │ ├── services │ ├── customers.service.ts │ ├── order-item.service.ts │ ├── orders.service.ts │ └── users.service.ts │ └── users.module.ts ├── test ├── app.e2e-spec.ts └── jest-e2e.json ├── tsconfig.build.json └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | parserOptions: { 4 | project: 'tsconfig.json', 5 | sourceType: 'module', 6 | }, 7 | plugins: ['@typescript-eslint/eslint-plugin'], 8 | extends: [ 9 | 'plugin:@typescript-eslint/recommended', 10 | 'plugin:prettier/recommended', 11 | ], 12 | root: true, 13 | env: { 14 | node: true, 15 | jest: true, 16 | }, 17 | ignorePatterns: ['.eslintrc.js'], 18 | rules: { 19 | '@typescript-eslint/interface-name-prefix': 'off', 20 | '@typescript-eslint/explicit-function-return-type': 'off', 21 | '@typescript-eslint/explicit-module-boundary-types': 'off', 22 | '@typescript-eslint/no-explicit-any': 'off', 23 | }, 24 | }; 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # compiled output 2 | /dist 3 | /node_modules 4 | 5 | # Logs 6 | logs 7 | *.log 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | lerna-debug.log* 12 | 13 | # OS 14 | .DS_Store 15 | 16 | # Tests 17 | /coverage 18 | /.nyc_output 19 | 20 | # IDEs and editors 21 | /.idea 22 | .project 23 | .classpath 24 | .c9/ 25 | *.launch 26 | .settings/ 27 | *.sublime-workspace 28 | 29 | # IDE - VSCode 30 | .vscode/* 31 | !.vscode/settings.json 32 | !.vscode/tasks.json 33 | !.vscode/launch.json 34 | !.vscode/extensions.json 35 | 36 | *.env 37 | /postgres_data 38 | /mysql_data -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "all" 4 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456 6 | [circleci-url]: https://circleci.com/gh/nestjs/nest 7 | 8 |

A progressive Node.js framework for building efficient and scalable server-side applications.

9 |

10 | NPM Version 11 | Package License 12 | NPM Downloads 13 | CircleCI 14 | Coverage 15 | Discord 16 | Backers on Open Collective 17 | Sponsors on Open Collective 18 | 19 | Support us 20 | 21 |

22 | 24 | 25 | ## Description 26 | 27 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 28 | 29 | ## Installation 30 | 31 | ```bash 32 | $ npm install 33 | ``` 34 | 35 | ## Running the app 36 | 37 | ```bash 38 | # development 39 | $ npm run start 40 | 41 | # watch mode 42 | $ npm run start:dev 43 | 44 | # production mode 45 | $ npm run start:prod 46 | ``` 47 | 48 | ## Test 49 | 50 | ```bash 51 | # unit tests 52 | $ npm run test 53 | 54 | # e2e tests 55 | $ npm run test:e2e 56 | 57 | # test coverage 58 | $ npm run test:cov 59 | ``` 60 | 61 | ## Support 62 | 63 | Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support). 64 | 65 | ## Stay in touch 66 | 67 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 68 | - Website - [https://nestjs.com](https://nestjs.com/) 69 | - Twitter - [@nestframework](https://twitter.com/nestframework) 70 | 71 | ## License 72 | 73 | Nest is [MIT licensed](LICENSE). 74 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | 3 | services: 4 | postgres: 5 | image: postgres:13 6 | environment: 7 | - POSTGRES_DB=my_db 8 | - POSTGRES_USER=root 9 | - POSTGRES_PASSWORD=123456 10 | ports: 11 | - '5432:5432' 12 | volumes: 13 | - ./postgres_data:/var/lib/postgresql/data 14 | 15 | pgadmin: 16 | image: dpage/pgadmin4 17 | environment: 18 | - PGADMIN_DEFAULT_EMAIL=root@admin.com 19 | - PGADMIN_DEFAULT_PASSWORD=root 20 | ports: 21 | - "5050:80" 22 | depends_on: 23 | - postgres 24 | 25 | mysql: 26 | image: mysql:5 27 | environment: 28 | - MYSQL_DATABASE=my_db 29 | - MYSQL_USER=root 30 | - MYSQL_ROOT_PASSWORD=123456 31 | ports: 32 | - '3306:3306' 33 | volumes: 34 | - ./mysql_data:/var/lib/mysql 35 | 36 | phpmyadmin: 37 | image: phpmyadmin/phpmyadmin 38 | environment: 39 | - MYSQL_ROOT_PASSWORD=123456 40 | - PMA_HOST=mysql 41 | ports: 42 | - '8080:80' 43 | depends_on: 44 | - mysql -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src", 4 | "compilerOptions": { 5 | "plugins": ["@nestjs/swagger/plugin"] 6 | } 7 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "platzi-store", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "", 6 | "private": true, 7 | "license": "UNLICENSED", 8 | "scripts": { 9 | "prebuild": "rimraf dist", 10 | "build": "nest build", 11 | "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", 12 | "start": "nest start", 13 | "start:dev": "nest start --watch", 14 | "start:debug": "nest start --debug --watch", 15 | "start:prod": "node dist/main", 16 | "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix", 17 | "test": "jest", 18 | "test:watch": "jest --watch", 19 | "test:cov": "jest --coverage", 20 | "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", 21 | "test:e2e": "jest --config ./test/jest-e2e.json", 22 | "typeorm": "ts-node -r tsconfig-paths/register ./node_modules/typeorm/cli.js", 23 | "migrations:generate": "npm run typeorm -- migration:generate -n", 24 | "migrations:run": "npm run typeorm -- migration:run", 25 | "migrations:show": "npm run typeorm -- migration:show", 26 | "migrations:drop": "npm run typeorm -- schema:drop" 27 | }, 28 | "dependencies": { 29 | "@nestjs/common": "^7.6.13", 30 | "@nestjs/config": "^0.6.3", 31 | "@nestjs/core": "^7.6.13", 32 | "@nestjs/mapped-types": "^0.3.0", 33 | "@nestjs/platform-express": "^7.6.13", 34 | "@nestjs/swagger": "^4.7.16", 35 | "@nestjs/typeorm": "^7.1.5", 36 | "class-transformer": "^0.4.0", 37 | "class-validator": "^0.13.1", 38 | "joi": "^17.4.0", 39 | "mysql2": "^2.2.5", 40 | "pg": "^8.5.1", 41 | "reflect-metadata": "^0.1.13", 42 | "rimraf": "^3.0.2", 43 | "rxjs": "^6.6.6", 44 | "swagger-ui-express": "^4.1.6", 45 | "typeorm": "^0.2.31" 46 | }, 47 | "devDependencies": { 48 | "@nestjs/cli": "^7.5.6", 49 | "@nestjs/schematics": "^7.2.7", 50 | "@nestjs/testing": "^7.6.13", 51 | "@types/express": "^4.17.11", 52 | "@types/jest": "^26.0.20", 53 | "@types/node": "^14.14.31", 54 | "@types/pg": "^7.14.11", 55 | "@types/supertest": "^2.0.10", 56 | "@typescript-eslint/eslint-plugin": "^4.15.2", 57 | "@typescript-eslint/parser": "^4.15.2", 58 | "eslint": "^7.20.0", 59 | "eslint-config-prettier": "^8.1.0", 60 | "eslint-plugin-prettier": "^3.3.1", 61 | "jest": "^26.6.3", 62 | "prettier": "^2.2.1", 63 | "supertest": "^6.1.3", 64 | "ts-jest": "^26.5.2", 65 | "ts-loader": "^8.0.17", 66 | "ts-node": "^9.1.1", 67 | "tsconfig-paths": "^3.9.0", 68 | "typescript": "^4.1.5" 69 | }, 70 | "jest": { 71 | "moduleFileExtensions": [ 72 | "js", 73 | "json", 74 | "ts" 75 | ], 76 | "rootDir": "src", 77 | "testRegex": ".*\\.spec\\.ts$", 78 | "transform": { 79 | "^.+\\.(t|j)s$": "ts-jest" 80 | }, 81 | "collectCoverageFrom": [ 82 | "**/*.(t|j)s" 83 | ], 84 | "coverageDirectory": "../coverage", 85 | "testEnvironment": "node" 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/app.controller.spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { AppController } from './app.controller'; 3 | import { AppService } from './app.service'; 4 | 5 | describe('AppController', () => { 6 | let appController: AppController; 7 | 8 | beforeEach(async () => { 9 | const app: TestingModule = await Test.createTestingModule({ 10 | controllers: [AppController], 11 | providers: [AppService], 12 | }).compile(); 13 | 14 | appController = app.get(AppController); 15 | }); 16 | 17 | describe('root', () => { 18 | it('should return "Hello World!"', () => { 19 | expect(appController.getHello()).toBe('Hello World!'); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get, Param, Query } from '@nestjs/common'; 2 | import { AppService } from './app.service'; 3 | 4 | @Controller() 5 | export class AppController { 6 | constructor(private readonly appService: AppService) {} 7 | 8 | @Get() 9 | getHello(): string { 10 | return this.appService.getHello(); 11 | } 12 | 13 | @Get('nuevo') 14 | newEndpoint() { 15 | return 'yo soy nuevo'; 16 | } 17 | 18 | @Get('/ruta/') 19 | hello() { 20 | return 'con /sas/'; 21 | } 22 | 23 | @Get('tasks') 24 | tasks() { 25 | return this.appService.getTasks(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, HttpModule, HttpService } from '@nestjs/common'; 2 | import { ConfigModule } from '@nestjs/config'; 3 | import * as Joi from 'joi'; 4 | 5 | import { AppController } from './app.controller'; 6 | import { AppService } from './app.service'; 7 | import { UsersModule } from './users/users.module'; 8 | import { ProductsModule } from './products/products.module'; 9 | import { DatabaseModule } from './database/database.module'; 10 | import { enviroments } from './enviroments'; 11 | import config from './config'; 12 | 13 | @Module({ 14 | imports: [ 15 | ConfigModule.forRoot({ 16 | envFilePath: enviroments[process.env.NODE_ENV] || '.env', 17 | load: [config], 18 | isGlobal: true, 19 | validationSchema: Joi.object({ 20 | API_KEY: Joi.number().required(), 21 | DATABASE_NAME: Joi.string().required(), 22 | DATABASE_PORT: Joi.number().required(), 23 | }), 24 | }), 25 | HttpModule, 26 | UsersModule, 27 | ProductsModule, 28 | DatabaseModule, 29 | ], 30 | controllers: [AppController], 31 | providers: [ 32 | AppService, 33 | { 34 | provide: 'TASKS', 35 | useFactory: async (http: HttpService) => { 36 | const tasks = await http 37 | .get('https://jsonplaceholder.typicode.com/todos') 38 | .toPromise(); 39 | return tasks.data; 40 | }, 41 | inject: [HttpService], 42 | }, 43 | ], 44 | }) 45 | export class AppModule {} 46 | -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, Inject } from '@nestjs/common'; 2 | import { ConfigType } from '@nestjs/config'; 3 | import { Client } from 'pg'; 4 | 5 | import config from './config'; 6 | 7 | @Injectable() 8 | export class AppService { 9 | constructor( 10 | // @Inject('API_KEY') private apiKey: string, 11 | @Inject('PG') private clientPg: Client, 12 | @Inject('TASKS') private tasks: any[], 13 | @Inject(config.KEY) private configService: ConfigType, 14 | ) {} 15 | getHello(): string { 16 | const apiKey = this.configService.apiKey; 17 | const name = this.configService.database.name; 18 | return `Hello World! ${apiKey} ${name}`; 19 | } 20 | 21 | getTasks() { 22 | return new Promise((resolve, reject) => { 23 | this.clientPg.query('SELECT * FROM tasks', (err, res) => { 24 | if (err) { 25 | reject(err); 26 | } 27 | resolve(res.rows); 28 | }); 29 | }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/common/parse-int.pipe.spec.ts: -------------------------------------------------------------------------------- 1 | import { ParseIntPipe } from './parse-int.pipe'; 2 | 3 | describe('ParseIntPipe', () => { 4 | it('should be defined', () => { 5 | expect(new ParseIntPipe()).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /src/common/parse-int.pipe.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ArgumentMetadata, 3 | Injectable, 4 | PipeTransform, 5 | BadRequestException, 6 | } from '@nestjs/common'; 7 | 8 | @Injectable() 9 | export class ParseIntPipe implements PipeTransform { 10 | transform(value: string, metadata: ArgumentMetadata) { 11 | const val = parseInt(value, 10); 12 | if (isNaN(val)) { 13 | throw new BadRequestException(`${value} is not an number`); 14 | } 15 | return val; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { registerAs } from '@nestjs/config'; 2 | 3 | export default registerAs('config', () => { 4 | return { 5 | database: { 6 | name: process.env.DATABASE_NAME, 7 | port: process.env.DATABASE_PORT, 8 | }, 9 | postgres: { 10 | dbName: process.env.POSTGRES_DB, 11 | port: parseInt(process.env.POSTGRES_PORT, 10), 12 | password: process.env.POSTGRES_PASSWORD, 13 | user: process.env.POSTGRES_USER, 14 | host: process.env.POSTGRES_HOST, 15 | }, 16 | mysql: { 17 | dbName: process.env.MYSQL_DATABASE, 18 | port: parseInt(process.env.MYSQL_PORT, 10), 19 | password: process.env.MYSQL_ROOT_PASSWORD, 20 | user: process.env.MYSQL_USER, 21 | host: process.env.MYSQL_HOST, 22 | }, 23 | apiKey: process.env.API_KEY, 24 | }; 25 | }); 26 | -------------------------------------------------------------------------------- /src/database/database.module.ts: -------------------------------------------------------------------------------- 1 | import { Module, Global } from '@nestjs/common'; 2 | import { ConfigType } from '@nestjs/config'; 3 | import { Client } from 'pg'; 4 | import { TypeOrmModule } from '@nestjs/typeorm'; 5 | 6 | import config from '../config'; 7 | 8 | const API_KEY = '12345634'; 9 | const API_KEY_PROD = 'PROD1212121SA'; 10 | 11 | // client.query('SELECT * FROM tasks', (err, res) => { 12 | // console.error(err); 13 | // console.log(res.rows); 14 | // }); 15 | 16 | @Global() 17 | @Module({ 18 | imports: [ 19 | TypeOrmModule.forRootAsync({ 20 | inject: [config.KEY], 21 | useFactory: (configService: ConfigType) => { 22 | const { user, host, dbName, password, port } = configService.postgres; 23 | return { 24 | type: 'postgres', 25 | host, 26 | port, 27 | username: user, 28 | password, 29 | database: dbName, 30 | synchronize: false, 31 | autoLoadEntities: true, 32 | }; 33 | }, 34 | }), 35 | ], 36 | providers: [ 37 | { 38 | provide: 'API_KEY', 39 | useValue: process.env.NODE_ENV === 'prod' ? API_KEY_PROD : API_KEY, 40 | }, 41 | { 42 | provide: 'PG', 43 | useFactory: (configService: ConfigType) => { 44 | const { user, host, dbName, password, port } = configService.postgres; 45 | const client = new Client({ 46 | user, 47 | host, 48 | database: dbName, 49 | password, 50 | port, 51 | }); 52 | client.connect(); 53 | return client; 54 | }, 55 | inject: [config.KEY], 56 | }, 57 | ], 58 | exports: ['API_KEY', 'PG', TypeOrmModule], 59 | }) 60 | export class DatabaseModule {} 61 | -------------------------------------------------------------------------------- /src/database/migrations/1616007004884-init.ts: -------------------------------------------------------------------------------- 1 | import {MigrationInterface, QueryRunner} from "typeorm"; 2 | 3 | export class init1616007004884 implements MigrationInterface { 4 | name = 'init1616007004884' 5 | 6 | public async up(queryRunner: QueryRunner): Promise { 7 | await queryRunner.query(`CREATE TABLE "category" ("id" SERIAL NOT NULL, "name" character varying(255) NOT NULL, "createAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "updateAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "UQ_23c05c292c439d77b0de816b500" UNIQUE ("name"), CONSTRAINT "PK_9c4e4a89e3674fc9f382d733f03" PRIMARY KEY ("id"))`); 8 | await queryRunner.query(`CREATE TABLE "products" ("id" SERIAL NOT NULL, "name" character varying(255) NOT NULL, "description" text NOT NULL, "price" integer NOT NULL, "stock" integer NOT NULL, "image" character varying NOT NULL, "create_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "update_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "brand_id" integer, CONSTRAINT "UQ_4c9fb58de893725258746385e16" UNIQUE ("name"), CONSTRAINT "PK_0806c755e0aca124e67c0cf6d7d" PRIMARY KEY ("id"))`); 9 | await queryRunner.query(`CREATE INDEX "IDX_75895eeb1903f8a17816dafe0a" ON "products" ("price") `); 10 | await queryRunner.query(`CREATE INDEX "IDX_4fbc36ad745962e5c11001e1a8" ON "products" ("price", "stock") `); 11 | await queryRunner.query(`CREATE TABLE "brand" ("id" SERIAL NOT NULL, "name" character varying(255) NOT NULL, "image" character varying(255) NOT NULL, "createAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "updateAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "UQ_5f468ae5696f07da025138e38f7" UNIQUE ("name"), CONSTRAINT "PK_a5d20765ddd942eb5de4eee2d7f" PRIMARY KEY ("id"))`); 12 | await queryRunner.query(`CREATE TABLE "user" ("id" SERIAL NOT NULL, "email" character varying(255) NOT NULL, "password" character varying(255) NOT NULL, "role" character varying(100) NOT NULL, "createAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "updateAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "customer_id" integer, CONSTRAINT "REL_d72eb2a5bbff4f2533a5d4caff" UNIQUE ("customer_id"), CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id"))`); 13 | await queryRunner.query(`CREATE TABLE "order_item" ("id" SERIAL NOT NULL, "createAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "updateAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "quantity" integer NOT NULL, "productId" integer, "orderId" integer, CONSTRAINT "PK_d01158fe15b1ead5c26fd7f4e90" PRIMARY KEY ("id"))`); 14 | await queryRunner.query(`CREATE TABLE "order" ("id" SERIAL NOT NULL, "createAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "updateAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "customerId" integer, CONSTRAINT "PK_1031171c13130102495201e3e20" PRIMARY KEY ("id"))`); 15 | await queryRunner.query(`CREATE TABLE "customer" ("id" SERIAL NOT NULL, "name" character varying(255) NOT NULL, "lastName" character varying(255) NOT NULL, "phone" character varying(255) NOT NULL, "createAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, "updateAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT CURRENT_TIMESTAMP, CONSTRAINT "PK_a7a13f4cacb744524e44dfdad32" PRIMARY KEY ("id"))`); 16 | await queryRunner.query(`CREATE TABLE "products_categories" ("product_id" integer NOT NULL, "category_id" integer NOT NULL, CONSTRAINT "PK_634f5e1b5983772473fe0ec0008" PRIMARY KEY ("product_id", "category_id"))`); 17 | await queryRunner.query(`CREATE INDEX "IDX_f2c76a4306a82c696d620f81f0" ON "products_categories" ("product_id") `); 18 | await queryRunner.query(`CREATE INDEX "IDX_19fe0fe8c2fcf1cbe1a80f639f" ON "products_categories" ("category_id") `); 19 | await queryRunner.query(`ALTER TABLE "products" ADD CONSTRAINT "FK_1530a6f15d3c79d1b70be98f2be" FOREIGN KEY ("brand_id") REFERENCES "brand"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); 20 | await queryRunner.query(`ALTER TABLE "user" ADD CONSTRAINT "FK_d72eb2a5bbff4f2533a5d4caff9" FOREIGN KEY ("customer_id") REFERENCES "customer"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); 21 | await queryRunner.query(`ALTER TABLE "order_item" ADD CONSTRAINT "FK_904370c093ceea4369659a3c810" FOREIGN KEY ("productId") REFERENCES "products"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); 22 | await queryRunner.query(`ALTER TABLE "order_item" ADD CONSTRAINT "FK_646bf9ece6f45dbe41c203e06e0" FOREIGN KEY ("orderId") REFERENCES "order"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); 23 | await queryRunner.query(`ALTER TABLE "order" ADD CONSTRAINT "FK_124456e637cca7a415897dce659" FOREIGN KEY ("customerId") REFERENCES "customer"("id") ON DELETE NO ACTION ON UPDATE NO ACTION`); 24 | await queryRunner.query(`ALTER TABLE "products_categories" ADD CONSTRAINT "FK_f2c76a4306a82c696d620f81f08" FOREIGN KEY ("product_id") REFERENCES "products"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); 25 | await queryRunner.query(`ALTER TABLE "products_categories" ADD CONSTRAINT "FK_19fe0fe8c2fcf1cbe1a80f639f1" FOREIGN KEY ("category_id") REFERENCES "category"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); 26 | } 27 | 28 | public async down(queryRunner: QueryRunner): Promise { 29 | await queryRunner.query(`ALTER TABLE "products_categories" DROP CONSTRAINT "FK_19fe0fe8c2fcf1cbe1a80f639f1"`); 30 | await queryRunner.query(`ALTER TABLE "products_categories" DROP CONSTRAINT "FK_f2c76a4306a82c696d620f81f08"`); 31 | await queryRunner.query(`ALTER TABLE "order" DROP CONSTRAINT "FK_124456e637cca7a415897dce659"`); 32 | await queryRunner.query(`ALTER TABLE "order_item" DROP CONSTRAINT "FK_646bf9ece6f45dbe41c203e06e0"`); 33 | await queryRunner.query(`ALTER TABLE "order_item" DROP CONSTRAINT "FK_904370c093ceea4369659a3c810"`); 34 | await queryRunner.query(`ALTER TABLE "user" DROP CONSTRAINT "FK_d72eb2a5bbff4f2533a5d4caff9"`); 35 | await queryRunner.query(`ALTER TABLE "products" DROP CONSTRAINT "FK_1530a6f15d3c79d1b70be98f2be"`); 36 | await queryRunner.query(`DROP INDEX "IDX_19fe0fe8c2fcf1cbe1a80f639f"`); 37 | await queryRunner.query(`DROP INDEX "IDX_f2c76a4306a82c696d620f81f0"`); 38 | await queryRunner.query(`DROP TABLE "products_categories"`); 39 | await queryRunner.query(`DROP TABLE "customer"`); 40 | await queryRunner.query(`DROP TABLE "order"`); 41 | await queryRunner.query(`DROP TABLE "order_item"`); 42 | await queryRunner.query(`DROP TABLE "user"`); 43 | await queryRunner.query(`DROP TABLE "brand"`); 44 | await queryRunner.query(`DROP INDEX "IDX_4fbc36ad745962e5c11001e1a8"`); 45 | await queryRunner.query(`DROP INDEX "IDX_75895eeb1903f8a17816dafe0a"`); 46 | await queryRunner.query(`DROP TABLE "products"`); 47 | await queryRunner.query(`DROP TABLE "category"`); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/enviroments.ts: -------------------------------------------------------------------------------- 1 | export const enviroments = { 2 | dev: '.env', 3 | stag: '.stag.env', 4 | prod: '.prod.env', 5 | }; 6 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory, Reflector } from '@nestjs/core'; 2 | import { ValidationPipe, ClassSerializerInterceptor } from '@nestjs/common'; 3 | import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; 4 | import { AppModule } from './app.module'; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule); 8 | app.useGlobalPipes( 9 | new ValidationPipe({ 10 | whitelist: true, 11 | forbidNonWhitelisted: true, 12 | transformOptions: { 13 | enableImplicitConversion: true, 14 | }, 15 | }), 16 | ); 17 | app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector))); 18 | 19 | const config = new DocumentBuilder() 20 | .setTitle('API') 21 | .setDescription('PLATZI STORE') 22 | .setVersion('1.0') 23 | .build(); 24 | const document = SwaggerModule.createDocument(app, config); 25 | SwaggerModule.setup('docs', app, document); 26 | await app.listen(3000); 27 | } 28 | bootstrap(); 29 | -------------------------------------------------------------------------------- /src/products/controllers/brands.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | Post, 6 | Body, 7 | Put, 8 | Delete, 9 | ParseIntPipe, 10 | } from '@nestjs/common'; 11 | import { ApiTags } from '@nestjs/swagger'; 12 | 13 | import { BrandsService } from '../services/brands.service'; 14 | import { CreateBrandDto, UpdateBrandDto } from '../dtos/brand.dtos'; 15 | 16 | @ApiTags('brands') 17 | @Controller('brands') 18 | export class BrandsController { 19 | constructor(private brandsService: BrandsService) {} 20 | 21 | @Get() 22 | findAll() { 23 | return this.brandsService.findAll(); 24 | } 25 | 26 | @Get(':id') 27 | get(@Param('id', ParseIntPipe) id: number) { 28 | return this.brandsService.findOne(id); 29 | } 30 | 31 | @Post() 32 | create(@Body() payload: CreateBrandDto) { 33 | return this.brandsService.create(payload); 34 | } 35 | 36 | @Put(':id') 37 | update( 38 | @Param('id', ParseIntPipe) id: number, 39 | @Body() payload: UpdateBrandDto, 40 | ) { 41 | return this.brandsService.update(id, payload); 42 | } 43 | 44 | @Delete(':id') 45 | remove(@Param('id', ParseIntPipe) id: number) { 46 | return this.brandsService.remove(+id); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/products/controllers/categories.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | Post, 6 | Body, 7 | Put, 8 | Delete, 9 | ParseIntPipe, 10 | } from '@nestjs/common'; 11 | 12 | import { CategoriesService } from '../services/categories.service'; 13 | import { CreateCategoryDto, UpdateCategoryDto } from './../dtos/category.dtos'; 14 | 15 | @Controller('categories') 16 | export class CategoriesController { 17 | constructor(private categoriesService: CategoriesService) {} 18 | 19 | @Get() 20 | findAll() { 21 | return this.categoriesService.findAll(); 22 | } 23 | 24 | @Get(':id') 25 | get(@Param('id', ParseIntPipe) id: number) { 26 | return this.categoriesService.findOne(id); 27 | } 28 | 29 | @Post() 30 | create(@Body() payload: CreateCategoryDto) { 31 | return this.categoriesService.create(payload); 32 | } 33 | 34 | @Put(':id') 35 | update( 36 | @Param('id', ParseIntPipe) id: number, 37 | @Body() payload: UpdateCategoryDto, 38 | ) { 39 | return this.categoriesService.update(id, payload); 40 | } 41 | 42 | @Delete(':id') 43 | remove(@Param('id', ParseIntPipe) id: number) { 44 | return this.categoriesService.remove(+id); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/products/controllers/products.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Query, 5 | Param, 6 | Post, 7 | Body, 8 | Put, 9 | Delete, 10 | HttpStatus, 11 | HttpCode, 12 | Res, 13 | // ParseIntPipe, 14 | } from '@nestjs/common'; 15 | import { Response } from 'express'; 16 | import { ApiTags, ApiOperation } from '@nestjs/swagger'; 17 | 18 | import { ParseIntPipe } from '../../common/parse-int.pipe'; 19 | import { 20 | CreateProductDto, 21 | UpdateProductDto, 22 | FilterProductsDto, 23 | } from '../dtos/products.dtos'; 24 | import { ProductsService } from './../services/products.service'; 25 | 26 | @ApiTags('products') 27 | @Controller('products') 28 | export class ProductsController { 29 | constructor(private productsService: ProductsService) {} 30 | 31 | @Get() 32 | @ApiOperation({ summary: 'List of products' }) 33 | getProducts(@Query() params: FilterProductsDto) { 34 | return this.productsService.findAll(params); 35 | } 36 | 37 | @Get('filter') 38 | getProductFilter() { 39 | return `yo soy un filter`; 40 | } 41 | 42 | @Get(':productId') 43 | @HttpCode(HttpStatus.ACCEPTED) 44 | getOne(@Param('productId', ParseIntPipe) productId: number) { 45 | // response.status(200).send({ 46 | // message: `product ${productId}`, 47 | // }); 48 | return this.productsService.findOne(productId); 49 | } 50 | 51 | @Post() 52 | create(@Body() payload: CreateProductDto) { 53 | return this.productsService.create(payload); 54 | } 55 | 56 | @Put(':id') 57 | update(@Param('id') id: number, @Body() payload: UpdateProductDto) { 58 | return this.productsService.update(id, payload); 59 | } 60 | 61 | @Put(':id/category/:categoryId') 62 | addCategoryToProduct( 63 | @Param('id') id: number, 64 | @Param('categoryId', ParseIntPipe) categoryId: number, 65 | ) { 66 | return this.productsService.addCategoryToProduct(id, categoryId); 67 | } 68 | 69 | @Delete(':id') 70 | delete(@Param('id') id: number) { 71 | return this.productsService.remove(id); 72 | } 73 | 74 | @Delete(':id/category/:categoryId') 75 | deleteCategory( 76 | @Param('id', ParseIntPipe) id: number, 77 | @Param('categoryId', ParseIntPipe) categoryId: number, 78 | ) { 79 | return this.productsService.removeCategoryByProduct(id, categoryId); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/products/dtos/brand.dtos.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsUrl, IsNotEmpty } from 'class-validator'; 2 | import { PartialType } from '@nestjs/swagger'; 3 | 4 | export class CreateBrandDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | readonly name: string; 8 | 9 | @IsUrl() 10 | @IsNotEmpty() 11 | readonly image: string; 12 | } 13 | 14 | export class UpdateBrandDto extends PartialType(CreateBrandDto) {} 15 | -------------------------------------------------------------------------------- /src/products/dtos/category.dtos.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty } from 'class-validator'; 2 | import { PartialType } from '@nestjs/swagger'; 3 | 4 | export class CreateCategoryDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | readonly name: string; 8 | } 9 | 10 | export class UpdateCategoryDto extends PartialType(CreateCategoryDto) {} 11 | -------------------------------------------------------------------------------- /src/products/dtos/products.dtos.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsNumber, 4 | IsUrl, 5 | IsNotEmpty, 6 | IsPositive, 7 | IsArray, 8 | IsOptional, 9 | Min, 10 | ValidateIf, 11 | } from 'class-validator'; 12 | import { PartialType, ApiProperty } from '@nestjs/swagger'; 13 | 14 | export class CreateProductDto { 15 | @IsString() 16 | @IsNotEmpty() 17 | @ApiProperty({ description: `product's name` }) 18 | readonly name: string; 19 | 20 | @IsString() 21 | @IsNotEmpty() 22 | @ApiProperty() 23 | readonly description: string; 24 | 25 | @IsNumber() 26 | @IsNotEmpty() 27 | @IsPositive() 28 | @ApiProperty() 29 | readonly price: number; 30 | 31 | @IsNumber() 32 | @IsNotEmpty() 33 | @ApiProperty() 34 | readonly stock: number; 35 | 36 | @IsUrl() 37 | @IsNotEmpty() 38 | @ApiProperty() 39 | readonly image: string; 40 | 41 | @IsPositive() 42 | @IsNotEmpty() 43 | @ApiProperty() 44 | readonly brandId: number; 45 | 46 | @IsArray() 47 | @IsNotEmpty() 48 | @ApiProperty() 49 | readonly categoriesIds: number[]; 50 | } 51 | 52 | export class UpdateProductDto extends PartialType(CreateProductDto) {} 53 | 54 | export class FilterProductsDto { 55 | @IsOptional() 56 | @IsPositive() 57 | limit: number; 58 | 59 | @IsOptional() 60 | @Min(0) 61 | offset: number; 62 | 63 | @IsOptional() 64 | @IsPositive() 65 | minPrice: number; 66 | 67 | @ValidateIf((item) => item.minPrice) 68 | @IsPositive() 69 | maxPrice: number; 70 | } 71 | -------------------------------------------------------------------------------- /src/products/entities/brand.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | CreateDateColumn, 6 | UpdateDateColumn, 7 | OneToMany, 8 | } from 'typeorm'; 9 | 10 | import { Product } from './product.entity'; 11 | 12 | @Entity() 13 | export class Brand { 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | @Column({ type: 'varchar', length: 255, unique: true }) 18 | name: string; 19 | 20 | @Column({ type: 'varchar', length: 255 }) 21 | image: string; 22 | 23 | @CreateDateColumn({ 24 | type: 'timestamptz', 25 | default: () => 'CURRENT_TIMESTAMP', 26 | }) 27 | createAt: Date; 28 | 29 | @UpdateDateColumn({ 30 | type: 'timestamptz', 31 | default: () => 'CURRENT_TIMESTAMP', 32 | }) 33 | updateAt: Date; 34 | 35 | @OneToMany(() => Product, (product) => product.brand) 36 | products: Product[]; 37 | } 38 | -------------------------------------------------------------------------------- /src/products/entities/category.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | Entity, 4 | Column, 5 | CreateDateColumn, 6 | UpdateDateColumn, 7 | ManyToMany, 8 | } from 'typeorm'; 9 | 10 | import { Product } from './product.entity'; 11 | 12 | @Entity() 13 | export class Category { 14 | @PrimaryGeneratedColumn() 15 | id: number; 16 | 17 | @Column({ type: 'varchar', length: 255, unique: true }) 18 | name: string; 19 | 20 | @CreateDateColumn({ 21 | type: 'timestamptz', 22 | default: () => 'CURRENT_TIMESTAMP', 23 | }) 24 | createAt: Date; 25 | 26 | @UpdateDateColumn({ 27 | type: 'timestamptz', 28 | default: () => 'CURRENT_TIMESTAMP', 29 | }) 30 | updateAt: Date; 31 | 32 | @ManyToMany(() => Product, (product) => product.categories) 33 | products: Product[]; 34 | } 35 | -------------------------------------------------------------------------------- /src/products/entities/product.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | Column, 4 | Entity, 5 | CreateDateColumn, 6 | UpdateDateColumn, 7 | ManyToOne, 8 | ManyToMany, 9 | JoinTable, 10 | Index, 11 | JoinColumn, 12 | } from 'typeorm'; 13 | 14 | import { Brand } from './brand.entity'; 15 | import { Category } from './category.entity'; 16 | 17 | @Entity({ name: 'products' }) 18 | @Index(['price', 'stock']) 19 | export class Product { 20 | @PrimaryGeneratedColumn() 21 | id: number; 22 | 23 | @Column({ type: 'varchar', length: 255, unique: true }) 24 | name: string; 25 | 26 | @Column({ type: 'text' }) 27 | description: string; 28 | 29 | @Index() 30 | @Column({ type: 'int' }) 31 | price: number; 32 | 33 | @Column({ type: 'int' }) 34 | stock: number; 35 | 36 | @Column({ type: 'varchar' }) 37 | image: string; 38 | 39 | @CreateDateColumn({ 40 | name: 'create_at', 41 | type: 'timestamptz', 42 | default: () => 'CURRENT_TIMESTAMP', 43 | }) 44 | createAt: Date; 45 | 46 | @UpdateDateColumn({ 47 | name: 'update_at', 48 | type: 'timestamptz', 49 | default: () => 'CURRENT_TIMESTAMP', 50 | }) 51 | updateAt: Date; 52 | 53 | @ManyToOne(() => Brand, (brand) => brand.products) 54 | @JoinColumn({ name: 'brand_id' }) 55 | brand: Brand; 56 | 57 | @ManyToMany(() => Category, (category) => category.products) 58 | @JoinTable({ 59 | name: 'products_categories', 60 | joinColumn: { 61 | name: 'product_id', 62 | }, 63 | inverseJoinColumn: { 64 | name: 'category_id', 65 | }, 66 | }) 67 | categories: Category[]; 68 | } 69 | -------------------------------------------------------------------------------- /src/products/products.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { ProductsController } from './controllers/products.controller'; 5 | import { ProductsService } from './services/products.service'; 6 | import { Product } from './entities/product.entity'; 7 | import { BrandsController } from './controllers/brands.controller'; 8 | import { BrandsService } from './services/brands.service'; 9 | import { Brand } from './entities/brand.entity'; 10 | import { CategoriesController } from './controllers/categories.controller'; 11 | import { CategoriesService } from './services/categories.service'; 12 | import { Category } from './entities/category.entity'; 13 | 14 | @Module({ 15 | imports: [TypeOrmModule.forFeature([Product, Brand, Category])], 16 | controllers: [ProductsController, CategoriesController, BrandsController], 17 | providers: [ProductsService, BrandsService, CategoriesService], 18 | exports: [ProductsService, TypeOrmModule], 19 | }) 20 | export class ProductsModule {} 21 | -------------------------------------------------------------------------------- /src/products/services/brands.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | 5 | import { Brand } from '../entities/brand.entity'; 6 | import { CreateBrandDto, UpdateBrandDto } from '../dtos/brand.dtos'; 7 | 8 | @Injectable() 9 | export class BrandsService { 10 | constructor(@InjectRepository(Brand) private brandsRepo: Repository) {} 11 | 12 | findAll() { 13 | return this.brandsRepo.find(); 14 | } 15 | 16 | findOne(id: number) { 17 | const product = this.brandsRepo.findOne({ 18 | relations: ['products'], 19 | }); 20 | if (!product) { 21 | throw new NotFoundException(`Brand #${id} not found`); 22 | } 23 | return product; 24 | } 25 | 26 | create(data: CreateBrandDto) { 27 | const newBrand = this.brandsRepo.create(data); 28 | return this.brandsRepo.save(newBrand); 29 | } 30 | 31 | async update(id: number, changes: UpdateBrandDto) { 32 | const brand = await this.findOne(id); 33 | this.brandsRepo.merge(brand, changes); 34 | return this.brandsRepo.save(brand); 35 | } 36 | 37 | remove(id: number) { 38 | return this.brandsRepo.delete(id); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/products/services/categories.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | 5 | import { Category } from '../entities/category.entity'; 6 | import { CreateCategoryDto, UpdateCategoryDto } from '../dtos/category.dtos'; 7 | 8 | @Injectable() 9 | export class CategoriesService { 10 | constructor( 11 | @InjectRepository(Category) private categoryRepo: Repository, 12 | ) {} 13 | 14 | findAll() { 15 | return this.categoryRepo.find(); 16 | } 17 | 18 | async findOne(id: number) { 19 | const category = this.categoryRepo.findOne(id, { 20 | relations: ['products'], 21 | }); 22 | if (!category) { 23 | throw new NotFoundException(`Category #${id} not found`); 24 | } 25 | return category; 26 | } 27 | 28 | create(data: CreateCategoryDto) { 29 | const newCategory = this.categoryRepo.create(data); 30 | return this.categoryRepo.save(newCategory); 31 | } 32 | 33 | async update(id: number, changes: UpdateCategoryDto) { 34 | const category = await this.findOne(id); 35 | this.categoryRepo.merge(category, changes); 36 | return this.categoryRepo.save(category); 37 | } 38 | 39 | remove(id: number) { 40 | return this.categoryRepo.delete(id); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/products/services/products.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository, Between, FindConditions } from 'typeorm'; 4 | 5 | import { Product } from './../entities/product.entity'; 6 | import { Category } from './../entities/category.entity'; 7 | import { Brand } from './../entities/brand.entity'; 8 | import { 9 | CreateProductDto, 10 | UpdateProductDto, 11 | FilterProductsDto, 12 | } from './../dtos/products.dtos'; 13 | 14 | @Injectable() 15 | export class ProductsService { 16 | constructor( 17 | @InjectRepository(Product) private productRepo: Repository, 18 | @InjectRepository(Brand) private brandRepo: Repository, 19 | @InjectRepository(Category) private categoryRepo: Repository, 20 | ) {} 21 | 22 | findAll(params?: FilterProductsDto) { 23 | if (params) { 24 | const where: FindConditions = {}; 25 | const { limit, offset } = params; 26 | const { maxPrice, minPrice } = params; 27 | if (minPrice && maxPrice) { 28 | where.price = Between(minPrice, maxPrice); 29 | } 30 | return this.productRepo.find({ 31 | relations: ['brand'], 32 | where, 33 | take: limit, 34 | skip: offset, 35 | }); 36 | } 37 | return this.productRepo.find({ 38 | relations: ['brand'], 39 | }); 40 | } 41 | 42 | async findOne(id: number) { 43 | const product = await this.productRepo.findOne(id, { 44 | relations: ['brand', 'categories'], 45 | }); 46 | if (!product) { 47 | throw new NotFoundException(`Product #${id} not found`); 48 | } 49 | return product; 50 | } 51 | 52 | async create(data: CreateProductDto) { 53 | // const newProduct = new Product(); 54 | // newProduct.image = data.image; 55 | // newProduct.name = data.name; 56 | // newProduct.description = data.description; 57 | // newProduct.price = data.price; 58 | // newProduct.stock = data.stock; 59 | // newProduct.image = data.image; 60 | const newProduct = this.productRepo.create(data); 61 | if (data.brandId) { 62 | const brand = await this.brandRepo.findOne(data.brandId); 63 | newProduct.brand = brand; 64 | } 65 | if (data.categoriesIds) { 66 | const categories = await this.categoryRepo.findByIds(data.categoriesIds); 67 | newProduct.categories = categories; 68 | } 69 | return this.productRepo.save(newProduct); 70 | } 71 | 72 | async update(id: number, changes: UpdateProductDto) { 73 | const product = await this.productRepo.findOne(id); 74 | if (changes.brandId) { 75 | const brand = await this.brandRepo.findOne(changes.brandId); 76 | product.brand = brand; 77 | } 78 | if (changes.categoriesIds) { 79 | const categories = await this.categoryRepo.findByIds( 80 | changes.categoriesIds, 81 | ); 82 | product.categories = categories; 83 | } 84 | this.productRepo.merge(product, changes); 85 | return this.productRepo.save(product); 86 | } 87 | 88 | async removeCategoryByProduct(productId: number, categoryId: number) { 89 | const product = await this.productRepo.findOne(productId, { 90 | relations: ['categories'], 91 | }); 92 | product.categories = product.categories.filter( 93 | (item) => item.id !== categoryId, 94 | ); 95 | return this.productRepo.save(product); 96 | } 97 | 98 | async addCategoryToProduct(productId: number, categoryId: number) { 99 | const product = await this.productRepo.findOne(productId, { 100 | relations: ['categories'], 101 | }); 102 | const category = await this.categoryRepo.findOne(categoryId); 103 | product.categories.push(category); 104 | return this.productRepo.save(product); 105 | } 106 | 107 | remove(id: number) { 108 | return this.productRepo.delete(id); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/recap.ts: -------------------------------------------------------------------------------- 1 | const myName = 'Nicolas'; 2 | const myAge = 12; 3 | const suma = (a: number, b: number) => { 4 | return a + b; 5 | }; 6 | suma(12, 23); 7 | 8 | class Persona { 9 | constructor(private age: number, private name: string) {} 10 | 11 | getSummary() { 12 | return `my name is ${this.name}, ${this.age}`; 13 | } 14 | } 15 | 16 | const nicolas = new Persona(15, 'nicolas'); 17 | nicolas.getSummary(); 18 | -------------------------------------------------------------------------------- /src/users/controllers/customers.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | Post, 6 | Body, 7 | Put, 8 | Delete, 9 | ParseIntPipe, 10 | } from '@nestjs/common'; 11 | 12 | import { CustomersService } from '../services/customers.service'; 13 | import { CreateCustomerDto, UpdateCustomerDto } from '../dtos/customer.dto'; 14 | 15 | @Controller('customers') 16 | export class CustomerController { 17 | constructor(private customersService: CustomersService) {} 18 | 19 | @Get() 20 | findAll() { 21 | return this.customersService.findAll(); 22 | } 23 | 24 | @Get(':id') 25 | get(@Param('id', ParseIntPipe) id: number) { 26 | return this.customersService.findOne(id); 27 | } 28 | 29 | @Post() 30 | create(@Body() payload: CreateCustomerDto) { 31 | return this.customersService.create(payload); 32 | } 33 | 34 | @Put(':id') 35 | update( 36 | @Param('id', ParseIntPipe) id: number, 37 | @Body() payload: UpdateCustomerDto, 38 | ) { 39 | return this.customersService.update(id, payload); 40 | } 41 | 42 | @Delete(':id') 43 | remove(@Param('id', ParseIntPipe) id: number) { 44 | return this.customersService.remove(+id); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/users/controllers/order-item.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Post, 4 | Body, 5 | Put, 6 | Param, 7 | Delete, 8 | ParseIntPipe, 9 | } from '@nestjs/common'; 10 | 11 | import { 12 | CreateOrderItemDto, 13 | UpdateOrderItemDto, 14 | } from './../dtos/order-item.dto'; 15 | import { OrderItemService } from './../services/order-item.service'; 16 | 17 | @Controller('order-item') 18 | export class OrderItemController { 19 | constructor(private itemsService: OrderItemService) {} 20 | 21 | @Post() 22 | create(@Body() payload: CreateOrderItemDto) { 23 | return this.itemsService.create(payload); 24 | } 25 | 26 | @Put(':id') 27 | update( 28 | @Param('id', ParseIntPipe) id: number, 29 | @Body() payload: UpdateOrderItemDto, 30 | ) { 31 | return this.itemsService.update(id, payload); 32 | } 33 | 34 | @Delete(':id') 35 | remove(@Param('id', ParseIntPipe) id: number) { 36 | return this.itemsService.remove(+id); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/users/controllers/orders.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | Post, 6 | Body, 7 | Put, 8 | Delete, 9 | ParseIntPipe, 10 | } from '@nestjs/common'; 11 | 12 | import { OrdersService } from './../services/orders.service'; 13 | import { CreateOrderDto, UpdateOrderDto } from './../dtos/order.dto'; 14 | 15 | @Controller('orders') 16 | export class OrdersController { 17 | constructor(private orderService: OrdersService) {} 18 | 19 | @Get() 20 | findAll() { 21 | return this.orderService.findAll(); 22 | } 23 | 24 | @Get(':id') 25 | get(@Param('id', ParseIntPipe) id: number) { 26 | return this.orderService.findOne(id); 27 | } 28 | 29 | @Post() 30 | create(@Body() payload: CreateOrderDto) { 31 | return this.orderService.create(payload); 32 | } 33 | 34 | @Put(':id') 35 | update( 36 | @Param('id', ParseIntPipe) id: number, 37 | @Body() payload: UpdateOrderDto, 38 | ) { 39 | return this.orderService.update(id, payload); 40 | } 41 | 42 | @Delete(':id') 43 | remove(@Param('id', ParseIntPipe) id: number) { 44 | return this.orderService.remove(+id); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/users/controllers/users.controller.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Controller, 3 | Get, 4 | Param, 5 | Post, 6 | Body, 7 | Put, 8 | Delete, 9 | ParseIntPipe, 10 | } from '@nestjs/common'; 11 | 12 | import { UsersService } from '../services/users.service'; 13 | import { CreateUserDto, UpdateUserDto } from '../dtos/user.dto'; 14 | 15 | @Controller('users') 16 | export class UsersController { 17 | constructor(private usersService: UsersService) {} 18 | 19 | @Get() 20 | findAll() { 21 | return this.usersService.findAll(); 22 | } 23 | 24 | @Get('tasks') 25 | getTasks() { 26 | return this.usersService.getTasks(); 27 | } 28 | 29 | @Get(':id') 30 | get(@Param('id', ParseIntPipe) id: number) { 31 | return this.usersService.findOne(id); 32 | } 33 | 34 | @Get(':id/orders') 35 | getOrders(@Param('id', ParseIntPipe) id: number) { 36 | return this.usersService.getOrderByUser(id); 37 | } 38 | 39 | @Post() 40 | create(@Body() payload: CreateUserDto) { 41 | return this.usersService.create(payload); 42 | } 43 | 44 | @Put(':id') 45 | update( 46 | @Param('id', ParseIntPipe) id: number, 47 | @Body() payload: UpdateUserDto, 48 | ) { 49 | return this.usersService.update(id, payload); 50 | } 51 | 52 | @Delete(':id') 53 | remove(@Param('id', ParseIntPipe) id: number) { 54 | return this.usersService.remove(+id); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/users/dtos/customer.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsString, IsNotEmpty, IsPhoneNumber } from 'class-validator'; 2 | import { PartialType } from '@nestjs/swagger'; 3 | 4 | export class CreateCustomerDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | readonly name: string; 8 | 9 | @IsString() 10 | @IsNotEmpty() 11 | readonly lastName: string; 12 | 13 | @IsPhoneNumber() 14 | @IsNotEmpty() 15 | readonly phone: string; 16 | } 17 | 18 | export class UpdateCustomerDto extends PartialType(CreateCustomerDto) {} 19 | -------------------------------------------------------------------------------- /src/users/dtos/order-item.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsPositive } from 'class-validator'; 2 | import { ApiProperty, PartialType } from '@nestjs/swagger'; 3 | 4 | export class CreateOrderItemDto { 5 | @IsPositive() 6 | @IsNotEmpty() 7 | @ApiProperty() 8 | readonly orderId: number; 9 | 10 | @IsPositive() 11 | @IsNotEmpty() 12 | @ApiProperty() 13 | readonly productId: number; 14 | 15 | @IsPositive() 16 | @IsNotEmpty() 17 | @ApiProperty() 18 | readonly quantity: number; 19 | } 20 | 21 | export class UpdateOrderItemDto extends PartialType(CreateOrderItemDto) {} 22 | -------------------------------------------------------------------------------- /src/users/dtos/order.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsPositive } from 'class-validator'; 2 | import { ApiProperty, PartialType } from '@nestjs/swagger'; 3 | 4 | export class CreateOrderDto { 5 | @IsPositive() 6 | @IsNotEmpty() 7 | @ApiProperty() 8 | readonly customerId: number; 9 | } 10 | 11 | export class UpdateOrderDto extends PartialType(CreateOrderDto) {} 12 | -------------------------------------------------------------------------------- /src/users/dtos/user.dto.ts: -------------------------------------------------------------------------------- 1 | import { 2 | IsString, 3 | IsNotEmpty, 4 | IsEmail, 5 | Length, 6 | IsPositive, 7 | IsOptional, 8 | } from 'class-validator'; 9 | import { PartialType, ApiProperty } from '@nestjs/swagger'; 10 | 11 | export class CreateUserDto { 12 | @IsString() 13 | @IsEmail() 14 | @ApiProperty({ description: 'the email of user' }) 15 | readonly email: string; 16 | 17 | @IsString() 18 | @IsNotEmpty() 19 | @Length(6) 20 | @ApiProperty() 21 | readonly password: string; 22 | 23 | @IsNotEmpty() 24 | @ApiProperty() 25 | readonly role: string; 26 | 27 | @IsOptional() 28 | @IsPositive() 29 | @ApiProperty() 30 | readonly customerId: number; 31 | } 32 | 33 | export class UpdateUserDto extends PartialType(CreateUserDto) {} 34 | -------------------------------------------------------------------------------- /src/users/entities/customer.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | CreateDateColumn, 6 | UpdateDateColumn, 7 | OneToOne, 8 | OneToMany, 9 | } from 'typeorm'; 10 | 11 | import { User } from './user.entity'; 12 | import { Order } from './order.entity'; 13 | 14 | @Entity() 15 | export class Customer { 16 | @PrimaryGeneratedColumn() 17 | id: number; 18 | 19 | @Column({ type: 'varchar', length: 255 }) 20 | name: string; 21 | 22 | @Column({ type: 'varchar', length: 255 }) 23 | lastName: string; 24 | 25 | @Column({ type: 'varchar', length: 255 }) 26 | phone: string; 27 | 28 | @CreateDateColumn({ 29 | type: 'timestamptz', 30 | default: () => 'CURRENT_TIMESTAMP', 31 | }) 32 | createAt: Date; 33 | 34 | @UpdateDateColumn({ 35 | type: 'timestamptz', 36 | default: () => 'CURRENT_TIMESTAMP', 37 | }) 38 | updateAt: Date; 39 | 40 | @OneToOne(() => User, (user) => user.customer, { nullable: true }) 41 | user: User; 42 | 43 | @OneToMany(() => Order, (order) => order.customer) 44 | orders: Order[]; 45 | } 46 | -------------------------------------------------------------------------------- /src/users/entities/order-item.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | UpdateDateColumn, 4 | CreateDateColumn, 5 | Entity, 6 | Column, 7 | ManyToOne, 8 | } from 'typeorm'; 9 | 10 | import { Exclude } from 'class-transformer'; 11 | 12 | import { Product } from '../../products/entities/product.entity'; 13 | import { Order } from './order.entity'; 14 | 15 | @Entity() 16 | export class OrderItem { 17 | @PrimaryGeneratedColumn() 18 | id: number; 19 | 20 | @Exclude() 21 | @CreateDateColumn({ 22 | type: 'timestamptz', 23 | default: () => 'CURRENT_TIMESTAMP', 24 | }) 25 | createAt: Date; 26 | 27 | @Exclude() 28 | @UpdateDateColumn({ 29 | type: 'timestamptz', 30 | default: () => 'CURRENT_TIMESTAMP', 31 | }) 32 | updateAt: Date; 33 | 34 | @Column({ type: 'int' }) 35 | quantity: number; 36 | 37 | @ManyToOne(() => Product) 38 | product: Product; 39 | 40 | @ManyToOne(() => Order, (order) => order.items) 41 | order: Order; 42 | } 43 | -------------------------------------------------------------------------------- /src/users/entities/order.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | PrimaryGeneratedColumn, 3 | UpdateDateColumn, 4 | CreateDateColumn, 5 | ManyToOne, 6 | Entity, 7 | OneToMany, 8 | } from 'typeorm'; 9 | import { Customer } from './customer.entity'; 10 | import { OrderItem } from './order-item.entity'; 11 | 12 | import { Exclude, Expose } from 'class-transformer'; 13 | 14 | @Entity() 15 | export class Order { 16 | @PrimaryGeneratedColumn() 17 | id: number; 18 | 19 | @CreateDateColumn({ 20 | type: 'timestamptz', 21 | default: () => 'CURRENT_TIMESTAMP', 22 | }) 23 | createAt: Date; 24 | 25 | @UpdateDateColumn({ 26 | type: 'timestamptz', 27 | default: () => 'CURRENT_TIMESTAMP', 28 | }) 29 | updateAt: Date; 30 | 31 | @ManyToOne(() => Customer, (customer) => customer.orders) 32 | customer: Customer; 33 | 34 | @Exclude() 35 | @OneToMany(() => OrderItem, (item) => item.order) 36 | items: OrderItem[]; 37 | 38 | @Expose() 39 | get products() { 40 | if (this.items) { 41 | return this.items 42 | .filter((item) => !!item) 43 | .map((item) => ({ 44 | ...item.product, 45 | quantity: item.quantity, 46 | itemId: item.id, 47 | })); 48 | } 49 | return []; 50 | } 51 | 52 | @Expose() 53 | get total() { 54 | if (this.items) { 55 | return this.items 56 | .filter((item) => !!item) 57 | .reduce((total, item) => { 58 | const totalItem = item.product.price * item.quantity; 59 | return total + totalItem; 60 | }, 0); 61 | } 62 | return 0; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /src/users/entities/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | Column, 4 | PrimaryGeneratedColumn, 5 | CreateDateColumn, 6 | UpdateDateColumn, 7 | OneToOne, 8 | JoinColumn, 9 | } from 'typeorm'; 10 | 11 | import { Customer } from './customer.entity'; 12 | 13 | @Entity() 14 | export class User { 15 | @PrimaryGeneratedColumn() 16 | id: number; 17 | 18 | @Column({ type: 'varchar', length: 255 }) 19 | email: string; 20 | 21 | @Column({ type: 'varchar', length: 255 }) 22 | password: string; // encript 23 | 24 | @Column({ type: 'varchar', length: 100 }) 25 | role: string; 26 | 27 | @CreateDateColumn({ 28 | type: 'timestamptz', 29 | default: () => 'CURRENT_TIMESTAMP', 30 | }) 31 | createAt: Date; 32 | 33 | @UpdateDateColumn({ 34 | type: 'timestamptz', 35 | default: () => 'CURRENT_TIMESTAMP', 36 | }) 37 | updateAt: Date; 38 | 39 | @OneToOne(() => Customer, (customer) => customer.user, { nullable: true }) 40 | @JoinColumn({ name: 'customer_id' }) 41 | customer: Customer; 42 | } 43 | -------------------------------------------------------------------------------- /src/users/services/customers.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | 5 | import { Customer } from '../entities/customer.entity'; 6 | import { CreateCustomerDto, UpdateCustomerDto } from '../dtos/customer.dto'; 7 | 8 | @Injectable() 9 | export class CustomersService { 10 | constructor( 11 | @InjectRepository(Customer) private customerRepo: Repository, 12 | ) {} 13 | 14 | findAll() { 15 | return this.customerRepo.find(); 16 | } 17 | 18 | async findOne(id: number) { 19 | const customer = await this.customerRepo.findOne(id); 20 | if (!customer) { 21 | throw new NotFoundException(`Customer #${id} not found`); 22 | } 23 | return customer; 24 | } 25 | 26 | create(data: CreateCustomerDto) { 27 | const newCustomer = this.customerRepo.create(data); 28 | return this.customerRepo.save(newCustomer); 29 | } 30 | 31 | async update(id: number, changes: UpdateCustomerDto) { 32 | const customer = await this.findOne(id); 33 | this.customerRepo.merge(customer, changes); 34 | return this.customerRepo.save(customer); 35 | } 36 | 37 | remove(id: number) { 38 | return this.customerRepo.delete(id); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/users/services/order-item.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | 5 | import { Order } from './../entities/order.entity'; 6 | import { OrderItem } from './../entities/order-item.entity'; 7 | import { Product } from './../../products/entities/product.entity'; 8 | import { 9 | CreateOrderItemDto, 10 | UpdateOrderItemDto, 11 | } from './../dtos/order-item.dto'; 12 | 13 | @Injectable() 14 | export class OrderItemService { 15 | constructor( 16 | @InjectRepository(Order) private orderRepo: Repository, 17 | @InjectRepository(OrderItem) private itemRepo: Repository, 18 | @InjectRepository(Product) private productRepo: Repository, 19 | ) {} 20 | 21 | async create(data: CreateOrderItemDto) { 22 | const order = await this.orderRepo.findOne(data.orderId); 23 | const product = await this.productRepo.findOne(data.productId); 24 | const item = new OrderItem(); 25 | item.order = order; 26 | item.product = product; 27 | item.quantity = data.quantity; 28 | return this.itemRepo.save(item); 29 | } 30 | 31 | async update(id: number, changes: UpdateOrderItemDto) { 32 | const item = await this.itemRepo.findOne(id); 33 | if (changes.orderId) { 34 | const order = await this.orderRepo.findOne(changes.orderId); 35 | item.order = order; 36 | } 37 | if (changes.productId) { 38 | const product = await this.productRepo.findOne(changes.productId); 39 | item.product = product; 40 | } 41 | this.itemRepo.merge(item, changes); 42 | return this.itemRepo.save(item); 43 | } 44 | 45 | remove(id: number) { 46 | return this.itemRepo.delete(id); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/users/services/orders.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | 5 | import { Order } from './../entities/order.entity'; 6 | import { Customer } from './../entities/customer.entity'; 7 | import { CreateOrderDto, UpdateOrderDto } from './../dtos/order.dto'; 8 | 9 | @Injectable() 10 | export class OrdersService { 11 | constructor( 12 | @InjectRepository(Order) private orderRepo: Repository, 13 | @InjectRepository(Customer) private customerRepo: Repository, 14 | ) {} 15 | 16 | findAll() { 17 | return this.orderRepo.find(); 18 | } 19 | 20 | async findOne(id: number) { 21 | const order = await this.orderRepo.findOne(id, { 22 | relations: ['items', 'items.product'], 23 | }); 24 | if (!order) { 25 | throw new NotFoundException('not found'); 26 | } 27 | return order; 28 | } 29 | 30 | async create(data: CreateOrderDto) { 31 | const order = new Order(); 32 | if (data.customerId) { 33 | const customer = await this.customerRepo.findOne(data.customerId); 34 | order.customer = customer; 35 | } 36 | return this.orderRepo.save(order); 37 | } 38 | 39 | async update(id: number, changes: UpdateOrderDto) { 40 | const order = await this.orderRepo.findOne(id); 41 | if (changes.customerId) { 42 | const customer = await this.customerRepo.findOne(changes.customerId); 43 | order.customer = customer; 44 | } 45 | return this.orderRepo.save(order); 46 | } 47 | 48 | remove(id: number) { 49 | return this.orderRepo.delete(id); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/users/services/users.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, NotFoundException, Inject } from '@nestjs/common'; 2 | import { InjectRepository } from '@nestjs/typeorm'; 3 | import { Repository } from 'typeorm'; 4 | import { ConfigService } from '@nestjs/config'; 5 | import { Client } from 'pg'; 6 | 7 | import { User } from '../entities/user.entity'; 8 | import { Order } from '../entities/order.entity'; 9 | import { CreateUserDto, UpdateUserDto } from '../dtos/user.dto'; 10 | 11 | import { ProductsService } from './../../products/services/products.service'; 12 | import { CustomersService } from './customers.service'; 13 | 14 | @Injectable() 15 | export class UsersService { 16 | constructor( 17 | private productsService: ProductsService, 18 | private configService: ConfigService, 19 | @Inject('PG') private clientPg: Client, 20 | @InjectRepository(User) private userRepo: Repository, 21 | private customersService: CustomersService, 22 | ) {} 23 | 24 | findAll() { 25 | const apiKey = this.configService.get('API_KEY'); 26 | const dbName = this.configService.get('DATABASE_NAME'); 27 | console.log(apiKey, dbName); 28 | return this.userRepo.find({ 29 | relations: ['customer'], 30 | }); 31 | } 32 | 33 | async findOne(id: number) { 34 | const user = await this.userRepo.findOne(id); 35 | if (!user) { 36 | throw new NotFoundException(`User #${id} not found`); 37 | } 38 | return user; 39 | } 40 | 41 | async create(data: CreateUserDto) { 42 | const newUser = this.userRepo.create(data); 43 | if (data.customerId) { 44 | const customer = await this.customersService.findOne(data.customerId); 45 | newUser.customer = customer; 46 | } 47 | return this.userRepo.save(newUser); 48 | } 49 | 50 | async update(id: number, changes: UpdateUserDto) { 51 | const user = await this.findOne(id); 52 | this.userRepo.merge(user, changes); 53 | return this.userRepo.save(user); 54 | } 55 | 56 | remove(id: number) { 57 | return this.userRepo.delete(id); 58 | } 59 | 60 | async getOrderByUser(id: number) { 61 | const user = this.findOne(id); 62 | return { 63 | date: new Date(), 64 | user, 65 | products: await this.productsService.findAll(), 66 | }; 67 | } 68 | 69 | getTasks() { 70 | return new Promise((resolve, reject) => { 71 | this.clientPg.query('SELECT * FROM tasks', (err, res) => { 72 | if (err) { 73 | reject(err); 74 | } 75 | resolve(res.rows); 76 | }); 77 | }); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/users/users.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { TypeOrmModule } from '@nestjs/typeorm'; 3 | 4 | import { CustomerController } from './controllers/customers.controller'; 5 | import { CustomersService } from './services/customers.service'; 6 | import { Customer } from './entities/customer.entity'; 7 | import { UsersController } from './controllers/users.controller'; 8 | import { UsersService } from './services/users.service'; 9 | import { User } from './entities/user.entity'; 10 | 11 | import { Order } from './entities/order.entity'; 12 | import { OrderItem } from './entities/order-item.entity'; 13 | 14 | import { ProductsModule } from '../products/products.module'; 15 | import { OrdersService } from './services/orders.service'; 16 | import { OrdersController } from './controllers/orders.controller'; 17 | import { OrderItemController } from './controllers/order-item.controller'; 18 | import { OrderItemService } from './services/order-item.service'; 19 | 20 | @Module({ 21 | imports: [ 22 | ProductsModule, 23 | TypeOrmModule.forFeature([User, Customer, Order, OrderItem]), 24 | ], 25 | controllers: [CustomerController, UsersController, OrdersController, OrderItemController], 26 | providers: [CustomersService, UsersService, OrdersService, OrderItemService], 27 | }) 28 | export class UsersModule {} 29 | -------------------------------------------------------------------------------- /test/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { Test, TestingModule } from '@nestjs/testing'; 2 | import { INestApplication } from '@nestjs/common'; 3 | import * as request from 'supertest'; 4 | import { AppModule } from './../src/app.module'; 5 | 6 | describe('AppController (e2e)', () => { 7 | let app: INestApplication; 8 | 9 | beforeEach(async () => { 10 | const moduleFixture: TestingModule = await Test.createTestingModule({ 11 | imports: [AppModule], 12 | }).compile(); 13 | 14 | app = moduleFixture.createNestApplication(); 15 | await app.init(); 16 | }); 17 | 18 | it('/ (GET)', () => { 19 | return request(app.getHttpServer()) 20 | .get('/') 21 | .expect(200) 22 | .expect('Hello World!'); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /test/jest-e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "moduleFileExtensions": ["js", "json", "ts"], 3 | "rootDir": ".", 4 | "testEnvironment": "node", 5 | "testRegex": ".e2e-spec.ts$", 6 | "transform": { 7 | "^.+\\.(t|j)s$": "ts-jest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "commonjs", 4 | "declaration": true, 5 | "removeComments": true, 6 | "emitDecoratorMetadata": true, 7 | "experimentalDecorators": true, 8 | "allowSyntheticDefaultImports": true, 9 | "target": "es2017", 10 | "sourceMap": true, 11 | "outDir": "./dist", 12 | "baseUrl": "./", 13 | "incremental": true 14 | } 15 | } 16 | --------------------------------------------------------------------------------