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