├── .env.example ├── .eslintrc.js ├── .gitignore ├── .prettierrc ├── README.md ├── nest-cli.json ├── package-lock.json ├── package.json ├── src ├── app.controller.ts ├── app.module.ts ├── app.service.ts ├── config │ ├── config.module.ts │ └── config.service.ts ├── dto │ ├── getQueryDto.ts │ └── response.dto.ts ├── entities │ ├── client.entity.ts │ ├── product.entity.ts │ ├── sale.entity.ts │ └── user.entity.ts ├── main.ts ├── modules │ ├── client │ │ ├── client.controller.ts │ │ ├── client.module.ts │ │ ├── client.service.ts │ │ └── dto │ │ │ ├── createClient.dto.ts │ │ │ └── updateClient.dto.ts │ ├── product │ │ ├── dto │ │ │ ├── createProduct.dto.ts │ │ │ └── updateProduct.dto.ts │ │ ├── product.controller.ts │ │ ├── product.module.ts │ │ └── product.service.ts │ ├── sale │ │ ├── dto │ │ │ ├── createSale.dto.ts │ │ │ ├── getSales.dto.ts │ │ │ └── updateSale.dto.ts │ │ ├── sale.controller.ts │ │ ├── sale.module.ts │ │ └── sale.service.ts │ └── user │ │ ├── dto │ │ ├── createUser.dto.ts │ │ ├── getUsers.dto.ts │ │ └── updateUser.dto.ts │ │ ├── user.controller.ts │ │ ├── user.module.ts │ │ └── user.service.ts └── repositories │ ├── client.repository.ts │ ├── product.repository.ts │ ├── sale.repository.ts │ └── user.repository.ts ├── test ├── app.e2e-spec.ts └── jest-e2e.json ├── tsconfig.build.json └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | MONGO_HOST=localhost:27017 2 | MONGO_PASSWORD=strongpassword 3 | MONGO_USER=admin 4 | MONGO_DATABASE=testing 5 | PORT=3000 -------------------------------------------------------------------------------- /.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/eslint-recommended', 10 | 'plugin:@typescript-eslint/recommended', 11 | 'prettier', 12 | 'prettier/@typescript-eslint', 13 | ], 14 | root: true, 15 | env: { 16 | node: true, 17 | jest: true, 18 | }, 19 | rules: { 20 | '@typescript-eslint/interface-name-prefix': 'off', 21 | '@typescript-eslint/explicit-function-return-type': 'off', 22 | '@typescript-eslint/explicit-module-boundary-types': 'off', 23 | '@typescript-eslint/no-explicit-any': 'off', 24 | }, 25 | }; 26 | -------------------------------------------------------------------------------- /.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 -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "printWidth": 180, 4 | "editor.formatOnSave": true, 5 | "proseWrap": "always", 6 | "tabWidth": 4, 7 | "requireConfig": false, 8 | "useTabs": false, 9 | "trailingComma": "all", 10 | "bracketSpacing": true, 11 | "jsxBracketSameLine": true, 12 | "semi": true, 13 | "arrowParens": "always" 14 | } 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Nest Logo 3 |

4 | 5 | [travis-image]: https://api.travis-ci.org/nestjs/nest.svg?branch=master 6 | [travis-url]: https://travis-ci.org/nestjs/nest 7 | [linux-image]: https://img.shields.io/travis/nestjs/nest/master.svg?label=linux 8 | [linux-url]: https://travis-ci.org/nestjs/nest 9 | 10 |

A progressive Node.js framework for building efficient and scalable server-side applications, heavily inspired by Angular.

11 |

12 | NPM Version 13 | Package License 14 | NPM Downloads 15 | Travis 16 | Linux 17 | Coverage 18 | Gitter 19 | Backers on Open Collective 20 | Sponsors on Open Collective 21 | 22 | 23 |

24 | 26 | 27 | ## Description 28 | 29 | [Nest](https://github.com/nestjs/nest) framework TypeScript starter repository. 30 | 31 | This is a basic project in NestJS using MongoDB and mongoose (https://github.com/Cfvillarroel/nestjs-mongodb-app) 32 | 33 | ## Installation 34 | 35 | ```bash 36 | $ npm install 37 | ``` 38 | 39 | ## Running the app 40 | 41 | ```bash 42 | # development 43 | $ npm run start 44 | 45 | # watch mode 46 | $ npm run start:dev 47 | 48 | # production mode 49 | $ npm run start:prod 50 | ``` 51 | 52 | ## Test 53 | 54 | ```bash 55 | # unit tests 56 | $ npm run test 57 | 58 | # e2e tests 59 | $ npm run test:e2e 60 | 61 | # test coverage 62 | $ npm run test:cov 63 | ``` 64 | 65 | ## Support 66 | 67 | 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 68 | to join them, please [read more here](https://docs.nestjs.com/support). 69 | 70 | ## Stay in touch 71 | 72 | - Author - [Kamil Myśliwiec](https://kamilmysliwiec.com) 73 | - Website - [https://nestjs.com](https://nestjs.com/) 74 | - Twitter - [@nestframework](https://twitter.com/nestframework) 75 | 76 | ## License 77 | 78 | Nest is [MIT licensed](LICENSE). 79 | -------------------------------------------------------------------------------- /nest-cli.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": "@nestjs/schematics", 3 | "sourceRoot": "src" 4 | } 5 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nestjs-mongodb-app", 3 | "version": "0.0.1", 4 | "description": "", 5 | "author": "CfVillarroel", 6 | "private": true, 7 | "license": "MIT", 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 | }, 23 | "dependencies": { 24 | "@nestjs/core": "^8.2.3", 25 | "@nestjs/mapped-types": "^1.0.0", 26 | "@nestjs/mongoose": "^9.0.1", 27 | "@nestjs/platform-express": "^8.2.3", 28 | "class-validator": "^0.13.2", 29 | "dotenv": "^10.0.0", 30 | "mongoose": "^6.0.14", 31 | "reflect-metadata": "^0.1.13", 32 | "rimraf": "^3.0.2", 33 | "rxjs": "^7.4.0" 34 | }, 35 | "devDependencies": { 36 | "@nestjs/common": "^8.2.3", 37 | "@nestjs/schematics": "^8.0.5", 38 | "@nestjs/testing": "^8.2.3", 39 | "@types/express": "^4.17.13", 40 | "@types/jest": "^27.0.3", 41 | "@types/node": "^16.11.11", 42 | "@types/supertest": "^2.0.11", 43 | "@typescript-eslint/eslint-plugin": "^5.5.0", 44 | "@typescript-eslint/parser": "^5.5.0", 45 | "eslint": "^8.3.0", 46 | "eslint-config-prettier": "^8.3.0", 47 | "eslint-plugin-import": "^2.25.3", 48 | "jest": "^27.4.1", 49 | "prettier": "^2.5.0", 50 | "supertest": "^4.0.2", 51 | "ts-jest": "^27.0.7", 52 | "ts-loader": "^9.2.6", 53 | "ts-node": "^10.4.0", 54 | "tsconfig-paths": "^3.12.0", 55 | "typescript": "^4.5.2" 56 | }, 57 | "jest": { 58 | "moduleFileExtensions": [ 59 | "js", 60 | "json", 61 | "ts" 62 | ], 63 | "rootDir": "src", 64 | "testRegex": ".spec.ts$", 65 | "transform": { 66 | "^.+\\.(t|j)s$": "ts-jest" 67 | }, 68 | "coverageDirectory": "../coverage", 69 | "testEnvironment": "node" 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app.controller.ts: -------------------------------------------------------------------------------- 1 | import { Controller, Get } 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 | -------------------------------------------------------------------------------- /src/app.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | 4 | import { AppController } from './app.controller'; 5 | import { AppService } from './app.service'; 6 | import { ConfigModule } from './config/config.module'; 7 | import { ConfigService } from './config/config.service'; 8 | import { ClientModule } from './modules/client/client.module'; 9 | import { ProductModule } from './modules/product/product.module'; 10 | import { SaleModule } from './modules/sale/sale.module'; 11 | import { UserModule } from './modules/user/user.module'; 12 | 13 | @Module({ 14 | imports: [ 15 | ConfigModule, 16 | // MongoDB Connection 17 | MongooseModule.forRootAsync({ 18 | inject: [ConfigService], 19 | useFactory: async (configService: ConfigService) => configService.getMongoConfig(), 20 | }), 21 | ClientModule, 22 | ProductModule, 23 | SaleModule, 24 | UserModule, 25 | ], 26 | controllers: [AppController], 27 | providers: [AppService], 28 | }) 29 | export class AppModule {} 30 | -------------------------------------------------------------------------------- /src/app.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | 3 | @Injectable() 4 | export class AppService { 5 | getHello(): string { 6 | return 'Hello World!'; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /src/config/config.module.ts: -------------------------------------------------------------------------------- 1 | import { Global, Module } from '@nestjs/common'; 2 | 3 | import { ConfigService } from './config.service'; 4 | 5 | @Global() 6 | @Module({ 7 | providers: [ 8 | { 9 | provide: ConfigService, 10 | useValue: new ConfigService(), 11 | }, 12 | ], 13 | exports: [ConfigService], 14 | }) 15 | export class ConfigModule { } 16 | -------------------------------------------------------------------------------- /src/config/config.service.ts: -------------------------------------------------------------------------------- 1 | import * as dotenv from 'dotenv'; 2 | 3 | export class ConfigService { 4 | private readonly envConfig: Record; 5 | constructor() { 6 | const result = dotenv.config(); 7 | 8 | if (result.error) { 9 | this.envConfig = process.env; 10 | } else { 11 | this.envConfig = result.parsed; 12 | } 13 | } 14 | 15 | public get(key: string): string { 16 | return this.envConfig[key]; 17 | } 18 | 19 | public async getPortConfig() { 20 | return this.get('PORT'); 21 | } 22 | 23 | public async getMongoConfig() { 24 | return { 25 | uri: 'mongodb+srv://' + this.get('MONGO_USER') + ':' + this.get('MONGO_PASSWORD') + '@' + this.get('MONGO_HOST') + '/' + this.get('MONGO_DATABASE'), 26 | useNewUrlParser: true, 27 | useUnifiedTopology: true, 28 | }; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/dto/getQueryDto.ts: -------------------------------------------------------------------------------- 1 | import { IsOptional } from 'class-validator'; 2 | import { Schema as MongooseSchema } from 'mongoose'; 3 | 4 | export class GetQueryDto { 5 | @IsOptional() 6 | id: MongooseSchema.Types.ObjectId; 7 | 8 | @IsOptional() 9 | from?: number; 10 | 11 | @IsOptional() 12 | limit?: number; 13 | } 14 | -------------------------------------------------------------------------------- /src/dto/response.dto.ts: -------------------------------------------------------------------------------- 1 | export class ResponseDto { 2 | ok: boolean; 3 | data?: any; 4 | message?: string; 5 | error?: any; 6 | nTotal?: number; 7 | } 8 | -------------------------------------------------------------------------------- /src/entities/client.entity.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { Document, Schema as MongooseSchema } from 'mongoose'; 3 | 4 | import { User } from './user.entity'; 5 | 6 | @Schema() 7 | export class Client extends Document { 8 | @Prop({ required: true, unique: true, message: 'Name must be unique' }) 9 | name: string; 10 | 11 | @Prop({ required: true }) 12 | contactNumber: string; 13 | 14 | @Prop({ default: Date.now }) 15 | updatedAt: Date; 16 | 17 | @Prop({ default: Date.now }) 18 | createdAt: Date; 19 | 20 | @Prop({ type: MongooseSchema.Types.ObjectId, ref: User.name }) 21 | user: MongooseSchema.Types.ObjectId; 22 | } 23 | 24 | export const ClientSchema = SchemaFactory.createForClass(Client); 25 | -------------------------------------------------------------------------------- /src/entities/product.entity.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { Document, Schema as MongooseSchema } from 'mongoose'; 3 | 4 | import { Client } from './client.entity'; 5 | import { User } from './user.entity'; 6 | 7 | @Schema() 8 | export class Product extends Document { 9 | @Prop({ type: MongooseSchema.Types.ObjectId, required: false, ref: User.name }) 10 | user: MongooseSchema.Types.ObjectId; 11 | 12 | @Prop({ type: String }) 13 | productName: string; 14 | 15 | @Prop({ type: String }) 16 | status: string; 17 | 18 | @Prop({ type: MongooseSchema.Types.ObjectId, required: false, ref: Client.name }) 19 | client: any; 20 | 21 | @Prop({ type: Date, default: Date.now }) 22 | updatedAt: Date; 23 | 24 | @Prop({ type: Date, default: Date.now }) 25 | createdAt: Date; 26 | } 27 | 28 | export const ProductSchema = SchemaFactory.createForClass(Product); 29 | -------------------------------------------------------------------------------- /src/entities/sale.entity.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { Document, Schema as MongooseSchema } from 'mongoose'; 3 | 4 | import { Client } from './client.entity'; 5 | import { Product } from './product.entity'; 6 | import { User } from './user.entity'; 7 | 8 | @Schema() 9 | export class Sale extends Document { 10 | @Prop({ type: MongooseSchema.Types.ObjectId, required: false, ref: User.name }) 11 | user: MongooseSchema.Types.ObjectId; 12 | 13 | @Prop({ type: MongooseSchema.Types.ObjectId, required: false, ref: Product.name }) 14 | product: MongooseSchema.Types.ObjectId; 15 | 16 | @Prop({ type: MongooseSchema.Types.ObjectId, required: false, ref: Client.name }) 17 | client: MongooseSchema.Types.ObjectId; 18 | 19 | @Prop({ type: Number }) 20 | total: number; 21 | 22 | @Prop({ type: String }) 23 | productName: string; 24 | 25 | @Prop({ type: Date, default: Date.now }) 26 | updatedAt: Date; 27 | 28 | @Prop({ type: Date, default: Date.now }) 29 | createdAt: Date; 30 | } 31 | 32 | export const SaleSchema = SchemaFactory.createForClass(Sale); 33 | -------------------------------------------------------------------------------- /src/entities/user.entity.ts: -------------------------------------------------------------------------------- 1 | import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; 2 | import { Document } from 'mongoose'; 3 | 4 | @Schema() 5 | export class User extends Document { 6 | @Prop({ required: true, unique: true }) 7 | name: string; 8 | 9 | @Prop({ required: true, unique: true }) 10 | email: string; 11 | 12 | @Prop({ required: true, enum: ['ADMIN', 'USER'] }) 13 | role: string; 14 | 15 | @Prop({ default: Date.now }) 16 | createdAt: Date; 17 | } 18 | 19 | export const UserSchema = SchemaFactory.createForClass(User); 20 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { NestFactory } from '@nestjs/core'; 2 | 3 | import { AppModule } from './app.module'; 4 | import { ConfigService } from './config/config.service'; 5 | 6 | async function bootstrap() { 7 | const app = await NestFactory.create(AppModule); 8 | const config = new ConfigService(); 9 | await app.listen(await config.getPortConfig()); 10 | } 11 | bootstrap(); 12 | -------------------------------------------------------------------------------- /src/modules/client/client.controller.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, Body, Controller, Get, HttpStatus, Param, Post, Query, Res } from '@nestjs/common'; 2 | import { InjectConnection } from '@nestjs/mongoose'; 3 | import { Response } from 'express'; 4 | import { Connection, Schema as MongooseSchema } from 'mongoose'; 5 | import { GetQueryDto } from '../../dto/getQueryDto'; 6 | import { ClientService } from './client.service'; 7 | import { CreateClientDto } from './dto/createClient.dto'; 8 | 9 | @Controller('client') 10 | export class ClientController { 11 | constructor(@InjectConnection() private readonly mongoConnection: Connection, private clientService: ClientService) {} 12 | 13 | @Post('/createClient') 14 | async createClient(@Body() createClientDto: CreateClientDto, @Res() res: Response) { 15 | const session = await this.mongoConnection.startSession(); 16 | session.startTransaction(); 17 | try { 18 | const newClient = await this.clientService.createClient(createClientDto, session); 19 | await session.commitTransaction(); 20 | return res.status(HttpStatus.CREATED).send(newClient); 21 | } catch (error) { 22 | await session.abortTransaction(); 23 | throw new BadRequestException(error); 24 | } finally { 25 | session.endSession(); 26 | } 27 | } 28 | 29 | @Get('/getClients') 30 | async getClients(@Query() getQueryDto: GetQueryDto, @Res() res: Response) { 31 | const clients: any = await this.clientService.getClients(getQueryDto); 32 | return res.status(HttpStatus.OK).send(clients); 33 | } 34 | 35 | @Get('/getClientById/:id') 36 | async getClientById(@Param('id') id: MongooseSchema.Types.ObjectId, @Res() res: Response) { 37 | const client: any = await this.clientService.getClientById(id); 38 | return res.status(HttpStatus.OK).send(client); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/modules/client/client.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | 4 | import { Client, ClientSchema } from '../../entities/client.entity'; 5 | import { User, UserSchema } from '../../entities/user.entity'; 6 | import { ClientRepository } from '../../repositories/client.repository'; 7 | import { UserModule } from '../user/user.module'; 8 | import { ClientController } from './client.controller'; 9 | import { ClientService } from './client.service'; 10 | 11 | @Module({ 12 | imports: [ 13 | UserModule, 14 | MongooseModule.forFeature([{ name: User.name, schema: UserSchema }]), 15 | MongooseModule.forFeature([{ name: Client.name, schema: ClientSchema }]), 16 | ], 17 | controllers: [ClientController], 18 | providers: [ClientService, ClientRepository], 19 | exports: [ClientService, ClientRepository], 20 | }) 21 | export class ClientModule {} 22 | -------------------------------------------------------------------------------- /src/modules/client/client.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { ClientSession, Schema as MongooseSchema } from 'mongoose'; 3 | import { GetQueryDto } from '../../dto/getQueryDto'; 4 | import { ClientRepository } from '../../repositories/client.repository'; 5 | import { UserService } from '../user/user.service'; 6 | import { CreateClientDto } from './dto/createClient.dto'; 7 | 8 | @Injectable() 9 | export class ClientService { 10 | constructor(private readonly clientRepository: ClientRepository, private readonly userService: UserService) {} 11 | 12 | async createClient(createClientDto: CreateClientDto, session: ClientSession) { 13 | const getUser: any = await this.userService.getUserById(createClientDto.userId); 14 | 15 | if (getUser.role === 'ADMIN') { 16 | return await this.clientRepository.createClient(createClientDto, session); 17 | } else { 18 | throw new UnauthorizedException('Incorrect Role'); 19 | } 20 | } 21 | 22 | async getClients(getQueryDto: GetQueryDto) { 23 | return await this.clientRepository.getClients(getQueryDto); 24 | } 25 | 26 | async getClientById(id: MongooseSchema.Types.ObjectId) { 27 | return await this.clientRepository.getClientById(id); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/modules/client/dto/createClient.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | import { Schema as MongooseSchema } from 'mongoose'; 3 | 4 | export class CreateClientDto { 5 | @IsString() 6 | @IsNotEmpty() 7 | name: string; 8 | 9 | @IsString() 10 | @IsNotEmpty() 11 | contactNumber: string; 12 | 13 | @IsString() 14 | @IsNotEmpty() 15 | userId: MongooseSchema.Types.ObjectId; 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/client/dto/updateClient.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreateClientDto } from './createClient.dto'; 4 | 5 | export class UpdateClientDto extends PartialType(CreateClientDto) { } 6 | -------------------------------------------------------------------------------- /src/modules/product/dto/createProduct.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsOptional } from 'class-validator'; 2 | import { Schema as MongooseSchema } from 'mongoose'; 3 | 4 | export class CreateProductDto { 5 | @IsOptional() 6 | productName: string; 7 | @IsOptional() 8 | userId: MongooseSchema.Types.ObjectId; 9 | @IsOptional() 10 | id: MongooseSchema.Types.ObjectId; 11 | @IsOptional() 12 | status: string; 13 | @IsOptional() 14 | clientId: MongooseSchema.Types.ObjectId; 15 | } 16 | -------------------------------------------------------------------------------- /src/modules/product/dto/updateProduct.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | import { CreateProductDto } from './createProduct.dto'; 3 | 4 | export class UpdateProductDto extends PartialType(CreateProductDto) {} 5 | -------------------------------------------------------------------------------- /src/modules/product/product.controller.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, Body, Controller, Get, HttpStatus, Param, Post, Put, Query, Res } from '@nestjs/common'; 2 | import { InjectConnection } from '@nestjs/mongoose'; 3 | import { Response } from 'express'; 4 | import { Connection, Schema as MongooseSchema } from 'mongoose'; 5 | import { GetQueryDto } from '../../dto/getQueryDto'; 6 | import { CreateProductDto } from './dto/createProduct.dto'; 7 | import { UpdateProductDto } from './dto/updateProduct.dto'; 8 | import { ProductService } from './product.service'; 9 | 10 | @Controller('product') 11 | export class ProductController { 12 | constructor(@InjectConnection() private readonly mongoConnection: Connection, private productService: ProductService) {} 13 | 14 | @Post('/createProduct') 15 | async createProduct(@Body() createProductDto: CreateProductDto, @Res() res: Response) { 16 | const session = await this.mongoConnection.startSession(); 17 | session.startTransaction(); 18 | try { 19 | const newProduct: any = await this.productService.createProduct(createProductDto, session); 20 | await session.commitTransaction(); 21 | return res.status(HttpStatus.OK).send(newProduct); 22 | } catch (error) { 23 | await session.abortTransaction(); 24 | throw new BadRequestException(error); 25 | } finally { 26 | session.endSession(); 27 | } 28 | } 29 | 30 | @Put('/updateProduct/:id') 31 | async updateProduct(@Param('id') id: MongooseSchema.Types.ObjectId, @Body() updateProductDto: UpdateProductDto, @Res() res: Response) { 32 | const session = await this.mongoConnection.startSession(); 33 | session.startTransaction(); 34 | try { 35 | const newProduct: any = await this.productService.updateProduct(updateProductDto, session); 36 | await session.commitTransaction(); 37 | return res.status(HttpStatus.OK).send(newProduct); 38 | } catch (error) { 39 | await session.abortTransaction(); 40 | throw new BadRequestException(error); 41 | } finally { 42 | session.endSession(); 43 | } 44 | } 45 | 46 | @Get('/getProductById/:id') 47 | async getProductById(@Param('id') id: MongooseSchema.Types.ObjectId, @Res() res: Response) { 48 | const storage: any = await this.productService.getProductById(id); 49 | return res.status(HttpStatus.OK).send(storage); 50 | } 51 | 52 | @Get('/getProducts') 53 | async getAllProducts(@Query() getQueryDto: GetQueryDto, @Res() res: any) { 54 | const storages: any = await this.productService.getProducts(getQueryDto); 55 | return res.status(HttpStatus.OK).send(storages); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/modules/product/product.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | 4 | import { Product, ProductSchema } from '../../entities/product.entity'; 5 | import { ProductRepository } from '../../repositories/product.repository'; 6 | import { ClientModule } from '../client/client.module'; 7 | import { UserModule } from '../user/user.module'; 8 | import { ProductController } from './product.controller'; 9 | import { ProductService } from './product.service'; 10 | 11 | @Module({ 12 | imports: [UserModule, ClientModule, MongooseModule.forFeature([{ name: Product.name, schema: ProductSchema }])], 13 | controllers: [ProductController], 14 | providers: [ProductService, ProductRepository], 15 | exports: [ProductService, ProductRepository], 16 | }) 17 | export class ProductModule {} 18 | -------------------------------------------------------------------------------- /src/modules/product/product.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { ClientSession, Schema as MongooseSchema } from 'mongoose'; 3 | import { GetQueryDto } from 'src/dto/getQueryDto'; 4 | import { ProductRepository } from '../../repositories/product.repository'; 5 | import { CreateProductDto } from './dto/createProduct.dto'; 6 | import { UpdateProductDto } from './dto/updateProduct.dto'; 7 | 8 | @Injectable() 9 | export class ProductService { 10 | constructor(private productRepository: ProductRepository) {} 11 | 12 | async createProduct(createProductDto: CreateProductDto, session: ClientSession) { 13 | return await this.productRepository.createProduct(createProductDto, session); 14 | } 15 | 16 | async getProductById(productId: MongooseSchema.Types.ObjectId) { 17 | return await this.productRepository.getProductById(productId); 18 | } 19 | 20 | async getProducts(getQueryDto: GetQueryDto) { 21 | return await this.productRepository.getProducts(getQueryDto); 22 | } 23 | 24 | async updateProduct(updateProductDto: UpdateProductDto, session: ClientSession) { 25 | return await this.productRepository.updateProduct(updateProductDto, session); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/modules/sale/dto/createSale.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty } from 'class-validator'; 2 | import { Schema as MongooseSchema } from 'mongoose'; 3 | 4 | export class CreateSaleDto { 5 | @IsNotEmpty() 6 | clientId: MongooseSchema.Types.ObjectId; 7 | 8 | @IsNotEmpty() 9 | productId: MongooseSchema.Types.ObjectId; 10 | 11 | @IsNotEmpty() 12 | userId: MongooseSchema.Types.ObjectId; 13 | 14 | @IsNotEmpty() 15 | total: number; 16 | } 17 | -------------------------------------------------------------------------------- /src/modules/sale/dto/getSales.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsOptional } from 'class-validator'; 2 | import { Schema as MongooseSchema } from 'mongoose'; 3 | 4 | export class GetSalesDto { 5 | @IsOptional() 6 | id: MongooseSchema.Types.ObjectId 7 | 8 | @IsOptional() 9 | from: number 10 | 11 | @IsOptional() 12 | limit: number 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/sale/dto/updateSale.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | import { IsNotEmpty, IsOptional } from 'class-validator'; 3 | 4 | import { CreateSaleDto } from './createSale.dto'; 5 | 6 | export class UpdateSaleDto extends PartialType(CreateSaleDto) { 7 | @IsNotEmpty() 8 | payedAmount: number; 9 | 10 | @IsOptional() 11 | observation: string; 12 | } 13 | -------------------------------------------------------------------------------- /src/modules/sale/sale.controller.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, Body, Controller, Get, HttpStatus, Param, Post, Query, Res } from '@nestjs/common'; 2 | import { InjectConnection } from '@nestjs/mongoose'; 3 | import { Connection, Schema as MongooseSchema } from 'mongoose'; 4 | import { GetQueryDto } from '../../dto/getQueryDto'; 5 | import { CreateSaleDto } from './dto/createSale.dto'; 6 | import { SaleService } from './sale.service'; 7 | 8 | @Controller('sale') 9 | export class SaleController { 10 | constructor(@InjectConnection() private readonly mongoConnection: Connection, private saleService: SaleService) {} 11 | 12 | @Post('/createSale') 13 | async createSale(@Body() createSaleDto: CreateSaleDto, @Res() res: any) { 14 | const session = await this.mongoConnection.startSession(); 15 | session.startTransaction(); 16 | try { 17 | const newProduct: any = await this.saleService.createSale(createSaleDto, session); 18 | await session.commitTransaction(); 19 | return res.status(HttpStatus.OK).send(newProduct); 20 | } catch (error) { 21 | await session.abortTransaction(); 22 | throw new BadRequestException(error); 23 | } finally { 24 | session.endSession(); 25 | } 26 | } 27 | 28 | @Get('/getSaleById/:id') 29 | async getSaleById(@Param('id') id: MongooseSchema.Types.ObjectId, @Query() getQueryDto: GetQueryDto, @Res() res: any) { 30 | const storage: any = await this.saleService.getSaleById(id); 31 | return res.status(HttpStatus.OK).send(storage); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/modules/sale/sale.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | 4 | import { Sale, SaleSchema } from '../../entities/sale.entity'; 5 | import { SaleRepository } from '../../repositories/sale.repository'; 6 | import { ClientModule } from '../client/client.module'; 7 | import { ProductModule } from '../product/product.module'; 8 | import { UserModule } from '../user/user.module'; 9 | import { SaleController } from './sale.controller'; 10 | import { SaleService } from './sale.service'; 11 | 12 | @Module({ 13 | imports: [UserModule, ProductModule, ClientModule, MongooseModule.forFeature([{ name: Sale.name, schema: SaleSchema }])], 14 | controllers: [SaleController], 15 | providers: [SaleService, SaleRepository], 16 | exports: [SaleService, SaleRepository], 17 | }) 18 | export class SaleModule {} 19 | -------------------------------------------------------------------------------- /src/modules/sale/sale.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable, UnauthorizedException } from '@nestjs/common'; 2 | import { ClientSession, Schema as MongooseSchema } from 'mongoose'; 3 | import { SaleRepository } from '../../repositories/sale.repository'; 4 | import { UpdateProductDto } from '../product/dto/updateProduct.dto'; 5 | import { ProductService } from '../product/product.service'; 6 | import { UserService } from '../user/user.service'; 7 | import { CreateSaleDto } from './dto/createSale.dto'; 8 | 9 | @Injectable() 10 | export class SaleService { 11 | constructor(private saleRepository: SaleRepository, private readonly userService: UserService, private readonly productService: ProductService) {} 12 | 13 | async createSale(createSaleDto: CreateSaleDto, session: ClientSession) { 14 | const { userId, productId, clientId } = createSaleDto; 15 | 16 | const getUser: any = await this.userService.getUserById(userId); 17 | 18 | if (getUser.role !== 'ADMIN') { 19 | throw new UnauthorizedException('Incorrect Role'); 20 | } 21 | 22 | const product = await this.productService.getProductById(productId); 23 | const createdSale = await this.saleRepository.createSale(createSaleDto, product, userId, session); 24 | 25 | const updateProductDto: UpdateProductDto = { 26 | id: product._id, 27 | status: 'SOLD', 28 | clientId: clientId, 29 | }; 30 | await this.productService.updateProduct(updateProductDto, session); 31 | 32 | return createdSale; 33 | } 34 | 35 | async getSaleById(saleId: MongooseSchema.Types.ObjectId) { 36 | return await this.saleRepository.getSaleById(saleId); 37 | } 38 | 39 | async getSales(query: { from: number; limit: number }) { 40 | return await this.saleRepository.getSales(query); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/modules/user/dto/createUser.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsString } from 'class-validator'; 2 | 3 | export class CreateUserDto { 4 | @IsString() 5 | @IsNotEmpty() 6 | name: string; 7 | 8 | @IsString() 9 | @IsNotEmpty() 10 | email: string; 11 | 12 | @IsString() 13 | @IsNotEmpty() 14 | role: any; 15 | } 16 | -------------------------------------------------------------------------------- /src/modules/user/dto/getUsers.dto.ts: -------------------------------------------------------------------------------- 1 | import { IsNotEmpty, IsOptional, IsPositive } from 'class-validator'; 2 | 3 | export class GetUsersDto { 4 | @IsNotEmpty() 5 | role: string; 6 | 7 | @IsOptional() 8 | from: number; 9 | 10 | @IsOptional() 11 | @IsPositive() 12 | limit: number; 13 | } 14 | -------------------------------------------------------------------------------- /src/modules/user/dto/updateUser.dto.ts: -------------------------------------------------------------------------------- 1 | import { PartialType } from '@nestjs/mapped-types'; 2 | 3 | import { CreateUserDto } from './createUser.dto'; 4 | 5 | export class UpdateUserDto extends PartialType(CreateUserDto) {} 6 | -------------------------------------------------------------------------------- /src/modules/user/user.controller.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, Body, Controller, Get, HttpStatus, Param, Post, Res } from '@nestjs/common'; 2 | import { InjectConnection } from '@nestjs/mongoose'; 3 | import { Response } from 'express'; 4 | import { Connection, Schema as MongooseSchema } from 'mongoose'; 5 | import { CreateUserDto } from './dto/createUser.dto'; 6 | import { UserService } from './user.service'; 7 | 8 | @Controller('user') 9 | export class UserController { 10 | constructor(@InjectConnection() private readonly mongoConnection: Connection, private userService: UserService) {} 11 | 12 | @Post('/createUser') 13 | async createUser(@Body() createUserDto: CreateUserDto, @Res() res: Response) { 14 | const session = await this.mongoConnection.startSession(); 15 | session.startTransaction(); 16 | try { 17 | const newUser: any = await this.userService.createUser(createUserDto, session); 18 | await session.commitTransaction(); 19 | return res.status(HttpStatus.CREATED).send(newUser); 20 | } catch (error) { 21 | await session.abortTransaction(); 22 | throw new BadRequestException(error); 23 | } finally { 24 | session.endSession(); 25 | } 26 | } 27 | 28 | @Get('/getUserById/:id') 29 | async getCompanyById(@Param('id') id: MongooseSchema.Types.ObjectId, @Res() res: Response) { 30 | const user: any = await this.userService.getUserById(id); 31 | return res.status(HttpStatus.OK).send(user); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/modules/user/user.module.ts: -------------------------------------------------------------------------------- 1 | import { Module } from '@nestjs/common'; 2 | import { MongooseModule } from '@nestjs/mongoose'; 3 | 4 | import { User, UserSchema } from '../../entities/user.entity'; 5 | import { UserRepository } from '../../repositories/user.repository'; 6 | import { UserController } from './user.controller'; 7 | import { UserService } from './user.service'; 8 | 9 | @Module({ 10 | imports: [MongooseModule.forFeature([{ name: User.name, schema: UserSchema }])], 11 | controllers: [UserController], 12 | providers: [UserService, UserRepository], 13 | exports: [UserService, UserRepository], 14 | }) 15 | export class UserModule {} 16 | -------------------------------------------------------------------------------- /src/modules/user/user.service.ts: -------------------------------------------------------------------------------- 1 | import { Injectable } from '@nestjs/common'; 2 | import { ClientSession, Schema as MongooseSchema } from 'mongoose'; 3 | import { UserRepository } from '../../repositories/user.repository'; 4 | import { CreateUserDto } from './dto/createUser.dto'; 5 | 6 | @Injectable() 7 | export class UserService { 8 | constructor(private readonly userRepository: UserRepository) {} 9 | 10 | async createUser(createUserDto: CreateUserDto, session: ClientSession) { 11 | const createdUser = await this.userRepository.createUser(createUserDto, session); 12 | return createdUser; 13 | } 14 | 15 | async getUserById(id: MongooseSchema.Types.ObjectId) { 16 | return await this.userRepository.getUserById(id); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/repositories/client.repository.ts: -------------------------------------------------------------------------------- 1 | import { ConflictException, InternalServerErrorException, NotFoundException } from '@nestjs/common'; 2 | import { InjectModel } from '@nestjs/mongoose'; 3 | import { ClientSession, Model, Schema as MongooseSchema } from 'mongoose'; 4 | import { GetQueryDto } from '../dto/getQueryDto'; 5 | import { ResponseDto } from '../dto/response.dto'; 6 | import { Client } from '../entities/client.entity'; 7 | import { CreateClientDto } from '../modules/client/dto/createClient.dto'; 8 | 9 | export class ClientRepository { 10 | constructor( 11 | @InjectModel(Client.name) 12 | private readonly clientModel: Model, 13 | ) {} 14 | 15 | async createClient(createClientDto: CreateClientDto, session: ClientSession) { 16 | let client = await this.getClientByName(createClientDto.name); 17 | 18 | if (client) { 19 | throw new ConflictException('Client Already Exists!'); 20 | } 21 | 22 | client = new this.clientModel({ 23 | name: createClientDto.name, 24 | contactNumber: createClientDto.contactNumber, 25 | user: createClientDto.userId, 26 | }); 27 | 28 | try { 29 | client = await client.save({ session }); 30 | } catch (error) { 31 | throw new InternalServerErrorException('Error al consultar la BD', error); 32 | } 33 | 34 | return client; 35 | } 36 | 37 | async getClients(query: GetQueryDto) { 38 | let from = query.from || 0; 39 | from = Number(from); 40 | 41 | let limit = query.limit || 0; 42 | limit = Number(limit); 43 | 44 | let clients: Client[]; 45 | 46 | try { 47 | if (limit === 0) { 48 | clients = await this.clientModel 49 | .find() 50 | .populate('client') 51 | .skip(from) 52 | .sort({ createdAt: -1 }) 53 | .exec(); 54 | } else { 55 | clients = await this.clientModel 56 | .find() 57 | .populate('client') 58 | .skip(from) 59 | .limit(limit) 60 | .sort({ createdAt: -1 }) 61 | .exec(); 62 | } 63 | 64 | let response: ResponseDto; 65 | 66 | if (clients.length > 0) { 67 | response = { 68 | ok: true, 69 | data: clients, 70 | message: 'Get Clients Ok!', 71 | }; 72 | } else { 73 | response = { 74 | ok: true, 75 | data: [], 76 | message: 'No hay clientes', 77 | }; 78 | } 79 | return response; 80 | } catch (error) { 81 | throw new InternalServerErrorException('Error al intentar consultar los clientes', error); 82 | } 83 | } 84 | 85 | async getClientById(id: MongooseSchema.Types.ObjectId) { 86 | let client; 87 | try { 88 | client = await this.clientModel.findById(id).exec(); 89 | } catch (error) { 90 | throw new InternalServerErrorException('No existe el registro con id' + id, error); 91 | } 92 | 93 | if (!client) { 94 | throw new NotFoundException('The client with this id does not exist'); 95 | } 96 | 97 | return client; 98 | } 99 | 100 | async getClientByName(name: string): Promise { 101 | let client; 102 | 103 | try { 104 | client = await this.clientModel.find({ name }); 105 | } catch (error) { 106 | throw new InternalServerErrorException('Error connecting to MongoDB', error); 107 | } 108 | 109 | return client; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/repositories/product.repository.ts: -------------------------------------------------------------------------------- 1 | import { ConflictException, InternalServerErrorException, NotFoundException } from '@nestjs/common'; 2 | import { InjectModel } from '@nestjs/mongoose'; 3 | import { ClientSession, Model, Schema as MongooseSchema } from 'mongoose'; 4 | import { GetQueryDto } from '../dto/getQueryDto'; 5 | import { Product } from '../entities/product.entity'; 6 | import { CreateProductDto } from '../modules/product/dto/createProduct.dto'; 7 | import { UpdateProductDto } from '../modules/product/dto/updateProduct.dto'; 8 | 9 | export class ProductRepository { 10 | constructor(@InjectModel(Product.name) private readonly productModel: Model) {} 11 | 12 | async createProduct(createProductDto: CreateProductDto, session: ClientSession) { 13 | let product = new this.productModel({ 14 | user: createProductDto.userId, 15 | productName: createProductDto.productName, 16 | status: 'CREATED', 17 | client: null, 18 | }); 19 | try { 20 | product = await product.save({ session }); 21 | } catch (error) { 22 | throw new InternalServerErrorException(error); 23 | } 24 | 25 | return product; 26 | } 27 | 28 | async updateProduct(updateProduct: UpdateProductDto, session: ClientSession) { 29 | const actualDate = new Date(); 30 | actualDate.toUTCString(); 31 | 32 | const updateData = { 33 | status: updateProduct.status, 34 | client: updateProduct.clientId, 35 | updatedAt: actualDate, 36 | }; 37 | 38 | let product; 39 | try { 40 | product = await this.productModel 41 | .findOneAndUpdate({ _id: updateProduct.id }, updateData, { 42 | new: true, 43 | }) 44 | .session(session) 45 | .exec(); 46 | } catch (error) { 47 | throw new InternalServerErrorException(error); 48 | } 49 | 50 | if (!product) { 51 | throw new ConflictException('Error trying to update product'); 52 | } 53 | 54 | return product; 55 | } 56 | 57 | async getProducts(query: GetQueryDto) { 58 | let from = query.from || 0; 59 | from = Number(from); 60 | 61 | let limit = query.limit || 0; 62 | limit = Number(limit); 63 | 64 | let products: Product[]; 65 | 66 | try { 67 | if (limit === 0) { 68 | products = await this.productModel 69 | .find() 70 | .populate('client') 71 | .populate('user', 'name email') 72 | .skip(from) 73 | .sort({ createdAt: -1 }) 74 | .exec(); 75 | } else { 76 | products = await this.productModel 77 | .find() 78 | .populate('client') 79 | .populate('user', 'name email') 80 | .skip(from) 81 | .limit(limit) 82 | .sort({ createdAt: -1 }) 83 | .exec(); 84 | } 85 | 86 | let response; 87 | 88 | if (products.length > 0) { 89 | response = { 90 | ok: true, 91 | data: products, 92 | message: 'Get Products Ok!', 93 | }; 94 | } else { 95 | response = { 96 | ok: true, 97 | data: [], 98 | message: 'No hay products', 99 | }; 100 | } 101 | return response; 102 | } catch (error) { 103 | throw new InternalServerErrorException(error); 104 | } 105 | } 106 | 107 | async getProductById(id: MongooseSchema.Types.ObjectId) { 108 | let product; 109 | try { 110 | product = await this.productModel.findById(id).exec(); 111 | } catch (error) { 112 | throw new InternalServerErrorException(error); 113 | } 114 | 115 | if (!product) { 116 | throw new NotFoundException('The product with this id does not exist'); 117 | } 118 | 119 | return product; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/repositories/sale.repository.ts: -------------------------------------------------------------------------------- 1 | import { BadRequestException, InternalServerErrorException, NotFoundException } from '@nestjs/common'; 2 | import { InjectModel } from '@nestjs/mongoose'; 3 | import { ClientSession, Model, Schema as MongooseSchema } from 'mongoose'; 4 | import { Product } from '../entities/product.entity'; 5 | import { Sale } from '../entities/sale.entity'; 6 | import { CreateSaleDto } from '../modules/sale/dto/createSale.dto'; 7 | 8 | export class SaleRepository { 9 | constructor(@InjectModel(Sale.name) private readonly saleModel: Model) {} 10 | 11 | async createSale(createSaleDto: CreateSaleDto, product: Product, userId: MongooseSchema.Types.ObjectId, session: ClientSession) { 12 | let sale = new this.saleModel({ 13 | user: userId, 14 | product: product._id, 15 | client: createSaleDto.clientId, 16 | productName: product.productName, 17 | total: createSaleDto.total, 18 | }); 19 | 20 | try { 21 | sale = await sale.save({ session }); 22 | } catch (error) { 23 | throw new InternalServerErrorException(error); 24 | } 25 | 26 | if (!sale) { 27 | throw new BadRequestException('Sale not created'); 28 | } 29 | 30 | return sale; 31 | } 32 | 33 | async getSales(query: { from: number; limit: number }) { 34 | let from = query.from || 0; 35 | from = Number(from); 36 | 37 | let limit = query.limit || 0; 38 | limit = Number(limit); 39 | 40 | let sales: Sale[]; 41 | 42 | try { 43 | if (limit === 0) { 44 | sales = await this.saleModel 45 | .find() 46 | .populate('sale') 47 | .populate('product') 48 | .populate('client') 49 | .populate('user', 'name email') 50 | .skip(from) 51 | .sort({ createdAt: -1 }) 52 | .exec(); 53 | } else { 54 | sales = await this.saleModel 55 | .find() 56 | .populate('sale') 57 | .populate('product') 58 | .populate('client') 59 | .populate('user', 'name email') 60 | .skip(from) 61 | .limit(limit) 62 | .sort({ createdAt: -1 }) 63 | .exec(); 64 | } 65 | 66 | let response; 67 | 68 | if (sales.length > 0) { 69 | response = { 70 | ok: true, 71 | data: sales, 72 | message: 'Get Sales Ok!', 73 | }; 74 | } else { 75 | response = { 76 | ok: true, 77 | data: [], 78 | message: 'No hay sales', 79 | }; 80 | } 81 | return response; 82 | } catch (error) { 83 | throw new InternalServerErrorException(error); 84 | } 85 | } 86 | 87 | async getSaleById(id: MongooseSchema.Types.ObjectId) { 88 | let sale; 89 | try { 90 | sale = await this.saleModel 91 | .findById(id) 92 | .populate('product') 93 | .populate('client') 94 | .populate('user', 'name email') 95 | .exec(); 96 | } catch (error) { 97 | throw new BadRequestException(error); 98 | } 99 | 100 | if (!sale) { 101 | throw new NotFoundException('Sale not found'); 102 | } 103 | 104 | return sale; 105 | } 106 | 107 | async getSaleByProductId(productId: MongooseSchema.Types.ObjectId) { 108 | let sale; 109 | try { 110 | sale = await this.saleModel.find({ product: productId }).exec(); 111 | } catch (error) { 112 | throw new InternalServerErrorException(error); 113 | } 114 | 115 | if (!sale) { 116 | throw new NotFoundException('Sale not found'); 117 | } 118 | 119 | return sale; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/repositories/user.repository.ts: -------------------------------------------------------------------------------- 1 | import { ConflictException, InternalServerErrorException, NotFoundException } from '@nestjs/common'; 2 | import { InjectModel } from '@nestjs/mongoose'; 3 | import { ClientSession, Model, Schema as MongooseSchema } from 'mongoose'; 4 | import { User } from '../entities/user.entity'; 5 | import { CreateUserDto } from '../modules/user/dto/createUser.dto'; 6 | 7 | export class UserRepository { 8 | constructor(@InjectModel(User.name) private readonly userModel: Model) {} 9 | 10 | async createUser(createUserDto: CreateUserDto, session: ClientSession) { 11 | let user = await this.getUserByEmail(createUserDto.email); 12 | 13 | if (user) { 14 | throw new ConflictException('User already exists'); 15 | } 16 | 17 | user = new this.userModel({ 18 | name: createUserDto.name, 19 | email: createUserDto.email, 20 | role: createUserDto.role, 21 | }); 22 | 23 | try { 24 | user = await user.save({ session }); 25 | } catch (error) { 26 | throw new InternalServerErrorException(error); 27 | } 28 | 29 | if (!user) { 30 | throw new ConflictException('User not created'); 31 | } 32 | 33 | return user; 34 | } 35 | 36 | async getUserById(id: MongooseSchema.Types.ObjectId) { 37 | let user; 38 | try { 39 | user = await this.userModel.findById({ _id: id }); 40 | } catch (error) { 41 | throw new InternalServerErrorException(error); 42 | } 43 | 44 | if (!user) { 45 | throw new NotFoundException('User not found'); 46 | } 47 | 48 | return user; 49 | } 50 | 51 | async getUserByEmail(email: string) { 52 | let user; 53 | try { 54 | user = await this.userModel.findOne({ email }, 'name email img role').exec(); 55 | } catch (error) { 56 | throw new InternalServerErrorException(error); 57 | } 58 | 59 | return user; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /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 | --------------------------------------------------------------------------------