From 8f6d76b57cad1c68a45918ac720537d82d88a252 Mon Sep 17 00:00:00 2001 From: Nabilsenko <96882497+Nabilsenko@users.noreply.github.com> Date: Mon, 21 Nov 2022 16:09:54 +0100 Subject: [PATCH] feat : added people crud using promises --- src/app.module.ts | 4 +- src/interceptors/http.interceptor.ts | 64 +++++++++++++++++++++++++ src/main.ts | 9 +++- src/people/dao/people.dao.ts | 66 ++++++++++++++++++++++---- src/people/dto/create-people.dto.ts | 27 +++++++++++ src/people/dto/update-people.dto.ts | 23 +++++++++ src/people/entities/people.entity.ts | 9 +++- src/people/people.controller.ts | 71 ++++++++++++++++++---------- src/people/people.module.ts | 18 +++++-- src/people/people.service.ts | 55 ++++++++++----------- src/people/people.types.ts | 4 +- src/people/schemas/people.schema.ts | 38 ++++++++------- 12 files changed, 296 insertions(+), 92 deletions(-) create mode 100644 src/interceptors/http.interceptor.ts create mode 100644 src/people/dto/update-people.dto.ts diff --git a/src/app.module.ts b/src/app.module.ts index 117b39a..ed2937a 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -4,8 +4,6 @@ import { mongodb } from './config'; import { PeopleModule } from './people/people.module'; @Module({ - imports: [MongooseModule.forRoot(mongodb.uri), AppModule, PeopleModule], - controllers: [], - providers: [], + imports: [PeopleModule, MongooseModule.forRoot(mongodb.uri)], }) export class AppModule {} diff --git a/src/interceptors/http.interceptor.ts b/src/interceptors/http.interceptor.ts new file mode 100644 index 0000000..c4620b7 --- /dev/null +++ b/src/interceptors/http.interceptor.ts @@ -0,0 +1,64 @@ +import { + CallHandler, + ExecutionContext, + Injectable, + Logger, + NestInterceptor, + } from '@nestjs/common'; + import { merge, Observable, of } from 'rxjs'; + import { filter, map, mergeMap, tap } from 'rxjs/operators'; + import { FastifyReply } from 'fastify'; + + @Injectable() + export class HttpInterceptor implements NestInterceptor { + /** + * Class constructor + * @param _logger + */ + constructor(private readonly _logger: Logger) {} + + /** + * Intercepts all HTTP requests and responses + * + * @param context + * @param next + */ + intercept = ( + context: ExecutionContext, + next: CallHandler, + ): Observable<any> => { + const cls = context.getClass(); + const handler = context.getHandler(); + const response: FastifyReply = context + .switchToHttp() + .getResponse<FastifyReply>(); + const logCtx = `${cls.name}.${handler.name}`; + + return next.handle().pipe( + map((_) => of(_)), + mergeMap((obs: Observable<any>) => + merge( + obs.pipe( + filter((_) => !!_), + map((_) => _), + ), + obs.pipe( + filter((_) => !_), + tap(() => response.status(204)), + map((_) => _), + ), + ), + ), + tap({ + next: (_) => + this._logger.log(!!_ ? JSON.stringify(_) : 'NO CONTENT', logCtx), + error: (_) => + this._logger.error( + _?.message ?? 'unspecified error', + JSON.stringify(_), + logCtx, + ), + }), + ); + }; + } \ No newline at end of file diff --git a/src/main.ts b/src/main.ts index a946614..bf7f9ff 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,9 +1,16 @@ +import { ValidationPipe } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { server } from './config'; async function bootstrap() { const app = await NestFactory.create(AppModule); + await app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: true, + }), + ); await app.listen(server.port); } -bootstrap(); +bootstrap(); \ No newline at end of file diff --git a/src/people/dao/people.dao.ts b/src/people/dao/people.dao.ts index 273eb67..261d451 100644 --- a/src/people/dao/people.dao.ts +++ b/src/people/dao/people.dao.ts @@ -1,19 +1,69 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; import { InjectModel } from '@nestjs/mongoose'; import { Model } from 'mongoose'; -import { from, map, Observable } from 'rxjs'; -/*import { CreatePeopleDto } from '../dto/create-people.dto'; -import { UpdatePeopleDto } from '../dto/update-people.dto';*/ +import { CreatePeopleDto } from '../dto/create-people.dto'; +import { UpdatePeopleDto } from '../dto/update-people.dto'; import { People } from '../schemas/people.schema'; @Injectable() export class PeopleDao { constructor( @InjectModel(People.name) - private readonly _groupModel: Model<People>, + private readonly _peopleModel: Model<People>, ) {} - find = (): Observable<People[]> => - from(this._groupModel.find({})).pipe(map((people) => [].concat(people))); + find = (): Promise<People[]> => + new Promise((resolve, reject) => { + this._peopleModel.find({}, {}, {}, (err, value) => { + if (err) reject(err.message); + if (!value) reject('No values'); + resolve(value); + }); + }); -} \ No newline at end of file + findById = (id: string): Promise<People | void> => + new Promise((resolve, reject) => { + this._peopleModel.findOne({ id: id }, {}, {}, (err, value) => { + if (err) reject(err.message); + if (!value) reject(new NotFoundException()); + resolve(value); + }); + }); + + save = (people: CreatePeopleDto): Promise<People> => + new Promise((resolve, reject) => { + new this._peopleModel(people).save((err, value) => { + if (err) reject(err.message); + if (!value) reject(new InternalServerErrorException()); + resolve(value); + }); + }); + + findByIdAndUpdate = ( + id: string, + people: UpdatePeopleDto, + ): Promise<People | void> => + new Promise((resolve, reject) => { + this._peopleModel.updateOne( + { id: id }, + people, + { + new: true, + runValidators: true, + }, + (err, value) => { + if (err) reject(err.message); + if (value.matchedCount === 0) reject(new NotFoundException()); + resolve(value); + }, + ); + }); + + findByIdAndRemove = (id: string): Promise<People | void> => + new Promise((resolve, reject) => { + this._peopleModel.deleteOne({ id: id }, {}, (err) => { + if (err) reject(err.message); + resolve(); + }); + }); +} diff --git a/src/people/dto/create-people.dto.ts b/src/people/dto/create-people.dto.ts index e69de29..d21fc2d 100644 --- a/src/people/dto/create-people.dto.ts +++ b/src/people/dto/create-people.dto.ts @@ -0,0 +1,27 @@ +import { IsBoolean, IsString, IsNotEmpty } from 'class-validator'; + +export class CreatePeopleDto { + @IsString() + @IsNotEmpty() + id: string; + + @IsString() + @IsNotEmpty() + firstname: string; + + @IsString() + @IsNotEmpty() + lastname: string; + + @IsString() + @IsNotEmpty() + email: string; + + @IsString() + @IsNotEmpty() + passwordHash: string; + + @IsNotEmpty() + role: number; + +} \ No newline at end of file diff --git a/src/people/dto/update-people.dto.ts b/src/people/dto/update-people.dto.ts new file mode 100644 index 0000000..4097f1e --- /dev/null +++ b/src/people/dto/update-people.dto.ts @@ -0,0 +1,23 @@ +import { IsOptional, IsString, IsNotEmpty } from 'class-validator'; + +export class UpdatePeopleDto { + @IsString() + @IsOptional() + firstname: string; + + @IsString() + @IsOptional() + lastname: string; + + @IsString() + @IsOptional() + email: string; + + @IsString() + @IsOptional() + passwordHash: string; + + @IsOptional() + role: number; + +} \ No newline at end of file diff --git a/src/people/entities/people.entity.ts b/src/people/entities/people.entity.ts index 4b2a04f..ed03662 100644 --- a/src/people/entities/people.entity.ts +++ b/src/people/entities/people.entity.ts @@ -1,7 +1,14 @@ import { People } from '../schemas/people.schema'; export class PeopleEntity { - + _id: string; + id: string; + firstname: string; + lastname: string; + email: string; + passwordHash: string; + role: number; + constructor(partial: Partial<People>) { Object.assign(this, partial); } diff --git a/src/people/people.controller.ts b/src/people/people.controller.ts index a24ccc5..8d40c28 100644 --- a/src/people/people.controller.ts +++ b/src/people/people.controller.ts @@ -1,29 +1,50 @@ import { - Controller, - Get, - Post, - Put, - Delete, - Param, - Body, - UseInterceptors, - } from '@nestjs/common'; - import { Observable } from 'rxjs'; -// import { HttpInterceptor } from '../interceptors/http.interceptor'; + Controller, + Get, + Post, + Put, + Delete, + Param, + Body, + UseInterceptors, +} from '@nestjs/common'; +import { CreatePeopleDto } from './dto/create-people.dto'; +import { UpdatePeopleDto } from './dto/update-people.dto'; +import { HttpInterceptor } from '../interceptors/http.interceptor'; // import { CreatePeopleDto } from './dto/create-people.dto'; // import { UpdatePeopleDto } from './dto/update-people.dto'; - import { PeopleEntity } from './entities/people.entity'; - import { PeopleService } from './people.service'; - +import { PeopleEntity } from './entities/people.entity'; +import { PeopleService } from './people.service'; - @Controller('people') -// @UseInterceptors(HttpInterceptor) - export class PeopleController { - constructor(private readonly _peopleService: PeopleService) {} - - @Get() - findAll(): Observable<PeopleEntity[] | void> { - return this._peopleService.findAll(); - } - - } \ No newline at end of file +@Controller('people') +@UseInterceptors(HttpInterceptor) +export class PeopleController { + constructor(private readonly _peopleService: PeopleService) {} + + @Get() + findAll(): Promise<PeopleEntity[] | void> { + return this._peopleService.findAll(); + } + @Get(':id') + findOne(@Param() params: { id: string }): Promise<PeopleEntity | void> { + return this._peopleService.findOne(params.id); + } + + @Post() + create(@Body() createPeopleDto: CreatePeopleDto): Promise<PeopleEntity> { + return this._peopleService.create(createPeopleDto); + } + + @Put(':id') + update( + @Param() params: { id: string }, + @Body() updateGroupDto: UpdatePeopleDto, + ): Promise<PeopleEntity | void> { + return this._peopleService.update(params.id, updateGroupDto); + } + + @Delete(':id') + delete(@Param() params: { id: string }): Promise<PeopleEntity | void> { + return this._peopleService.delete(params.id); + } +} diff --git a/src/people/people.module.ts b/src/people/people.module.ts index 6dc3c76..e11d141 100644 --- a/src/people/people.module.ts +++ b/src/people/people.module.ts @@ -1,4 +1,16 @@ -import { Module } from '@nestjs/common'; +import { Logger, Module } from '@nestjs/common'; +import { MongooseModule } from '@nestjs/mongoose'; +import { PeopleDao } from './dao/people.dao'; +import { PeopleController } from './people.controller'; +import { PeopleService } from './people.service'; +import { People, PeopleSchema } from './schemas/people.schema'; -@Module({}) -export class PeopleModule {} +@Module({ + imports: [ + MongooseModule.forFeature([{ name: People.name, schema: PeopleSchema }]), + ], + controllers: [PeopleController], + providers: [PeopleService, PeopleDao, Logger], + }) + + export class PeopleModule {} \ No newline at end of file diff --git a/src/people/people.service.ts b/src/people/people.service.ts index 41a31db..e97344a 100644 --- a/src/people/people.service.ts +++ b/src/people/people.service.ts @@ -1,34 +1,27 @@ -import { - Injectable, - UnprocessableEntityException, - NotFoundException, - ConflictException, - } from '@nestjs/common'; - import { - Observable, - of, - filter, - map, - mergeMap, - defaultIfEmpty, - catchError, - throwError, - } from 'rxjs'; +import { Injectable } from '@nestjs/common'; import { PeopleDao } from './dao/people.dao'; -// import { HttpInterceptor } from '../interceptors/http.interceptor'; -// import { CreatePeopleDto } from './dto/create-people.dto'; -// import { UpdatePeopleDto } from './dto/update-people.dto'; +import { CreatePeopleDto } from './dto/create-people.dto'; +import { UpdatePeopleDto } from './dto/update-people.dto'; import { PeopleEntity } from './entities/people.entity'; - - @Injectable() - export class PeopleService { - constructor(private readonly _peopleDao: PeopleDao) {} - - findAll = (): Observable<PeopleEntity[] | void> => - this._peopleDao.find().pipe( - filter(Boolean), - map((people) => (people || []).map((person) => new PeopleEntity(person))), - defaultIfEmpty(undefined), - ); -} \ No newline at end of file +@Injectable() +export class PeopleService { + constructor(private readonly _peopleDao: PeopleDao) {} + + findAll = (): Promise<PeopleEntity[] | void> => this._peopleDao.find(); + + findOne = (id: string): Promise<PeopleEntity | void> => + this._peopleDao.findById(id); + + create = (people: CreatePeopleDto): Promise<PeopleEntity> => + this._peopleDao.save(people); + + update = ( + id: string, + people: UpdatePeopleDto, + ): Promise<PeopleEntity | void> => + this._peopleDao.findByIdAndUpdate(id, people); + + delete = (id: string): Promise<PeopleEntity | void> => + this._peopleDao.findByIdAndRemove(id); +} diff --git a/src/people/people.types.ts b/src/people/people.types.ts index 2e768bb..c668ff0 100644 --- a/src/people/people.types.ts +++ b/src/people/people.types.ts @@ -1,9 +1,9 @@ -export type Group = { +export type People = { _id: any; + id: string; firstname: string, lastname: string, email: string, passwordHash: string, role: number, - groups: string[] }; diff --git a/src/people/schemas/people.schema.ts b/src/people/schemas/people.schema.ts index fe0f91c..15d4e92 100644 --- a/src/people/schemas/people.schema.ts +++ b/src/people/schemas/people.schema.ts @@ -1,23 +1,30 @@ -import { - Prop, raw, Schema, SchemaFactory, -} from '@nestjs/mongoose'; import * as mongoose from 'mongoose'; +import { Document } from 'mongoose'; +import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'; + +export type PeopleDocument = People & Document; @Schema({ - toJSON: { - virtuals: true, - transform: (dpc: any, ret: any) => { - delete ret._id; - }, + toJSON: { + virtuals: true, + transform: (doc: any, ret: any) => { + delete ret._id; }, - versionKey: false, - + }, }) export class People { @Prop({ type: mongoose.Schema.Types.ObjectId, auto: true, - }) _id: any; + }) + _id: any; + + @Prop({ + type: String, + required: true, + trim: true, + }) + id: string; @Prop({ type: String, @@ -50,15 +57,10 @@ export class People { @Prop({ type: Number, required: true, - }) role: string; - - @Prop({ - type: [String], - required: true, - }) groups: string[]; + }) role: number; } export const PeopleSchema = SchemaFactory.createForClass(People); -PeopleSchema.index({ name: 1 }, { unique: true }); \ No newline at end of file +PeopleSchema.index({ id: 1 }, { unique: true }); \ No newline at end of file -- GitLab