diff --git a/src/app.module.ts b/src/app.module.ts index 117b39a333563369b1b4d9a3a322a93011ed1eb4..ed2937ac9fe292368779678bdd2c6c8121733ca3 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 0000000000000000000000000000000000000000..c4620b7d728f3f172028bab56380de66e094c0f7 --- /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 a946614e106906a5e7958b5f284df1e53e6ad116..bf7f9ff096122e86e38387baf5d7b80092794f7b 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 273eb67f65ffb09c99cacc0cb9ec7d2bacfa63d3..261d451aa46e0b263d9b8d3dc4811abc3e0dafd4 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 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..d21fc2d639b410a30148ebf9bd884ccea569d3d6 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 0000000000000000000000000000000000000000..4097f1e2b7647a6f1f8253e6f3690b404d60ab10 --- /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 4b2a04f7d7290e4a2073b2ca8a6179d553180d18..ed03662b16869f52a5840b233be953cb1ecbe99a 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 a24ccc5081c7ea9697acca9c0eca57b1670e7f89..8d40c28a36275093db88ba1b57c7a03ca670e677 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 6dc3c761b78fa7dd0826c9d00c3c881f21b253aa..e11d14169bdb3af7eedc37dc2419c5dad35a2849 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 41a31db625ccf28f069f66f070628421af51c460..e97344a5d049104065cd802bd97b5ba6e7bb5b4b 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 2e768bb6722e8a0cb548809b084fb630629737ee..c668ff0a83f72a598dabcae0803933e99b134124 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 fe0f91cddf0bf8c3f48fca994b10b4452f971ab2..15d4e92893e83c8ee09b62be480144d5cb62bb78 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