├── .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 |
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 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
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 |
--------------------------------------------------------------------------------