diff --git a/package-lock.json b/package-lock.json index 6c3ae692..2c7efb84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,8 @@ "@nestjs/swagger": "^7.3.1", "@prisma/client": "^5.13.0", "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "date-fns": "^3.6.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.0", @@ -2639,6 +2641,11 @@ "@types/superagent": "^8.1.0" } }, + "node_modules/@types/validator": { + "version": "13.11.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.11.10.tgz", + "integrity": "sha512-e2PNXoXLr6Z+dbfx5zSh9TRlXJrELycxiaXznp4S5+D2M3b9bqJEitNHA5923jhnB2zzFiZHa2f0SI1HoIahpg==" + }, "node_modules/@types/yargs": { "version": "17.0.32", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.32.tgz", @@ -3807,6 +3814,21 @@ "integrity": "sha512-a3KdPAANPbNE4ZUv9h6LckSl9zLsYOP4MBmhIPkRaeyybt+r4UghLvq+xw/YwUcC1gqylCkL4rdVs3Lwupjm4Q==", "dev": true }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==" + }, + "node_modules/class-validator": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.1.tgz", + "integrity": "sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==", + "dependencies": { + "@types/validator": "^13.11.8", + "libphonenumber-js": "^1.10.53", + "validator": "^13.9.0" + } + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6883,6 +6905,11 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.11.1.tgz", + "integrity": "sha512-Wze1LPwcnzvcKGcRHFGFECTaLzxOtujwpf924difr5zniyYv1C2PiW0419qDR7m8lKDxsImu5mwxFuXhXpjmvw==" + }, "node_modules/light-my-request": { "version": "5.13.0", "resolved": "https://registry.npmjs.org/light-my-request/-/light-my-request-5.13.0.tgz", @@ -9652,6 +9679,14 @@ "node": ">=10.12.0" } }, + "node_modules/validator": { + "version": "13.12.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz", + "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", diff --git a/package.json b/package.json index 3212f2fd..c413855b 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,8 @@ "@nestjs/swagger": "^7.3.1", "@prisma/client": "^5.13.0", "bcrypt": "^5.1.1", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.1", "date-fns": "^3.6.0", "passport-jwt": "^4.0.1", "reflect-metadata": "^0.2.0", diff --git a/src/main.ts b/src/main.ts index eaf3c678..a3e28539 100644 --- a/src/main.ts +++ b/src/main.ts @@ -6,6 +6,7 @@ import { import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { AppModule } from './app.module'; +import { ValidationPipe } from '@nestjs/common'; async function bootstrap() { const fastifyAdapter = new FastifyAdapter(); @@ -14,11 +15,20 @@ async function bootstrap() { fastifyAdapter, ); + app.useGlobalPipes( + new ValidationPipe({ + whitelist: true, + forbidNonWhitelisted: true, + }), + ); + const config = new DocumentBuilder() .setTitle('SOS - Rio Grande do Sul') .setDescription('...') .setVersion('1.0') + .addBearerAuth() .build(); + const document = SwaggerModule.createDocument(app, config); SwaggerModule.setup('api', app, document); diff --git a/src/sessions/dtos/LoginSessionDTO.ts b/src/sessions/dtos/LoginSessionDTO.ts new file mode 100644 index 00000000..2c688972 --- /dev/null +++ b/src/sessions/dtos/LoginSessionDTO.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class LoginSessionDTO { + @ApiProperty({ type: 'string', example: 'John' }) + @IsNotEmpty() + @IsString() + readonly login = ''; + + @ApiProperty({ type: 'string', example: 'john123' }) + @IsNotEmpty() + @IsString() + readonly password = ''; +} diff --git a/src/sessions/sessions.controller.ts b/src/sessions/sessions.controller.ts index a29a3f9e..56c65429 100644 --- a/src/sessions/sessions.controller.ts +++ b/src/sessions/sessions.controller.ts @@ -10,11 +10,19 @@ import { Request, UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { UserGuard } from '@/guards/user.guard'; import { ServerResponse } from '../utils'; import { SessionsService } from './sessions.service'; +import { LoginSessionDTO } from './dtos/LoginSessionDTO'; @ApiTags('Sessões') @Controller('sessions') @@ -23,14 +31,17 @@ export class SessionsController { constructor(private readonly sessionService: SessionsService) {} + @ApiBadRequestResponse() + @ApiInternalServerErrorResponse() + @ApiOkResponse() @Post('') async login( - @Body() body, + @Body() body: LoginSessionDTO, @Headers('x-real-ip') ip: string, @Headers('user-agent') userAgent: string, ) { try { - const data = await this.sessionService.login({ ...body, ip, userAgent }); + const data = await this.sessionService.login(body, ip, userAgent); return new ServerResponse(200, 'Successfully logged in', data); } catch (err: any) { this.logger.error(`Failed to login ${err}`); @@ -41,6 +52,10 @@ export class SessionsController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiInternalServerErrorResponse() + @ApiOkResponse() @Get('') @UseGuards(UserGuard) async show(@Request() req) { @@ -54,6 +69,10 @@ export class SessionsController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiInternalServerErrorResponse() + @ApiOkResponse() @Delete('') @UseGuards(UserGuard) async delete(@Request() req) { diff --git a/src/sessions/sessions.service.ts b/src/sessions/sessions.service.ts index 200104b6..a83c9684 100644 --- a/src/sessions/sessions.service.ts +++ b/src/sessions/sessions.service.ts @@ -4,6 +4,7 @@ import * as bcrypt from 'bcrypt'; import { PrismaService } from '../prisma/prisma.service'; import { LoginSchema, TokenPayload } from './types'; +import { LoginSessionDTO } from './dtos/LoginSessionDTO'; @Injectable() export class SessionsService { @@ -12,8 +13,16 @@ export class SessionsService { private readonly jwtService: JwtService, ) {} - async login(body: any) { - const { login, password, ip, userAgent } = LoginSchema.parse(body); + async login( + body: LoginSessionDTO, + ipHeaders: string, + userAgentHeaders: string, + ) { + const { login, password, ip, userAgent } = LoginSchema.parse({ + ...body, + ipHeaders, + userAgentHeaders, + }); const user = await this.prismaService.user.findUnique({ where: { login }, }); diff --git a/src/shelter-managers/dtos/CreateShelterManagerDTO.ts b/src/shelter-managers/dtos/CreateShelterManagerDTO.ts new file mode 100644 index 00000000..d2f90a03 --- /dev/null +++ b/src/shelter-managers/dtos/CreateShelterManagerDTO.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateShelterManagerDTO { + @ApiProperty({ type: 'string', example: 'ID do Abrigo' }) + @IsNotEmpty() + @IsString() + readonly shelterId = ''; + + @ApiProperty({ type: 'string', example: 'ID do Usuário' }) + @IsNotEmpty() + @IsString() + readonly userId = ''; +} diff --git a/src/shelter-managers/shelter-managers.controller.ts b/src/shelter-managers/shelter-managers.controller.ts index adea36c2..b16c397e 100644 --- a/src/shelter-managers/shelter-managers.controller.ts +++ b/src/shelter-managers/shelter-managers.controller.ts @@ -9,13 +9,22 @@ import { Query, UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { ShelterManagersService } from './shelter-managers.service'; import { ServerResponse } from '../utils'; import { AdminGuard } from '@/guards/admin.guard'; +import { CreateShelterManagerDTO } from './dtos/CreateShelterManagerDTO'; @ApiTags('Admin de Abrigo') +@ApiInternalServerErrorResponse() @Controller('shelter/managers') export class ShelterManagersController { private logger = new Logger(ShelterManagersController.name); @@ -24,9 +33,13 @@ export class ShelterManagersController { private readonly shelterManagerServices: ShelterManagersService, ) {} + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiOkResponse() @Post('') @UseGuards(AdminGuard) - async store(@Body() body) { + async store(@Body() body: CreateShelterManagerDTO) { try { await this.shelterManagerServices.store(body); return new ServerResponse(200, 'Successfully added manager to shelter'); @@ -36,6 +49,7 @@ export class ShelterManagersController { } } + @ApiOkResponse() @Get(':shelterId') async index( @Param('shelterId') shelterId: string, diff --git a/src/shelter-managers/shelter-managers.service.ts b/src/shelter-managers/shelter-managers.service.ts index 648e9719..753dcb52 100644 --- a/src/shelter-managers/shelter-managers.service.ts +++ b/src/shelter-managers/shelter-managers.service.ts @@ -1,14 +1,14 @@ -import { z } from 'zod'; import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateShelterManagerSchema } from './types'; +import { CreateShelterManagerDTO } from './dtos/CreateShelterManagerDTO'; @Injectable() export class ShelterManagersService { constructor(private readonly prismaService: PrismaService) {} - async store(body: z.infer) { + async store(body: CreateShelterManagerDTO) { const { shelterId, userId } = CreateShelterManagerSchema.parse(body); await this.prismaService.shelterManagers.create({ data: { diff --git a/src/shelter-supply/dtos/CreateShelterSupplyDTO.ts b/src/shelter-supply/dtos/CreateShelterSupplyDTO.ts new file mode 100644 index 00000000..c06aa276 --- /dev/null +++ b/src/shelter-supply/dtos/CreateShelterSupplyDTO.ts @@ -0,0 +1,47 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { + IsEnum, + IsNotEmpty, + IsNumber, + IsOptional, + IsString, + Min, +} from 'class-validator'; +import { SupplyPriority } from 'src/supply/types'; + +export class CreateShelterSupplyDTO { + constructor() { + this.priority = SupplyPriority.UnderControl; + } + + @ApiProperty({ type: 'string', example: 'ID do Abrigo' }) + @IsNotEmpty() + @IsString() + readonly shelterId = ''; + + @ApiProperty({ type: 'string', example: 'ID do Suprimento' }) + @IsNotEmpty() + @IsString() + readonly supplyId = ''; + + @ApiProperty({ + description: 'Prioridade de suprimento', + enum: SupplyPriority, + }) + @IsNotEmpty() + @IsEnum(SupplyPriority) + @Transform((value) => Number(value.value)) + readonly priority: SupplyPriority; + + @ApiProperty({ + required: false, + type: 'number', + example: 1, + }) + @IsOptional() + @IsNumber() + @Min(1) + @Transform((value) => Number(value.value)) + readonly quantity?: number; +} diff --git a/src/shelter-supply/dtos/UpdateManyShelterSupplyDTO.ts b/src/shelter-supply/dtos/UpdateManyShelterSupplyDTO.ts new file mode 100644 index 00000000..1801219c --- /dev/null +++ b/src/shelter-supply/dtos/UpdateManyShelterSupplyDTO.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ArrayMinSize, IsArray, IsNotEmpty, IsString } from 'class-validator'; + +export class UpdateManyShelterSupplySchemaDTO { + @ApiProperty({ + type: [String], + example: ['ID do Suprimento', 'ID do Suprimento 2'], + }) + @IsArray() + @ArrayMinSize(1) + @IsNotEmpty() + @IsString({ each: true }) + readonly ids!: string; +} diff --git a/src/shelter-supply/dtos/UpdateShelterSupplyDTO.ts b/src/shelter-supply/dtos/UpdateShelterSupplyDTO.ts new file mode 100644 index 00000000..746789a1 --- /dev/null +++ b/src/shelter-supply/dtos/UpdateShelterSupplyDTO.ts @@ -0,0 +1,27 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { Transform } from 'class-transformer'; +import { IsEnum, IsOptional, IsNumber, Min } from 'class-validator'; +import { SupplyPriority } from 'src/supply/types'; + +export class UpdateShelterSupplyDTO { + @ApiProperty({ + required: false, + description: 'Prioridade de suprimento', + enum: SupplyPriority, + }) + @IsOptional() + @IsEnum(SupplyPriority) + @Transform((value) => Number(value.value)) + readonly priority?: SupplyPriority; + + @ApiProperty({ + required: false, + type: 'number', + example: 1, + }) + @IsOptional() + @IsNumber() + @Min(1) + @Transform((value) => Number(value.value)) + readonly quantity?: number; +} diff --git a/src/shelter-supply/shelter-supply.controller.ts b/src/shelter-supply/shelter-supply.controller.ts index e2d3df69..f39b4d8f 100644 --- a/src/shelter-supply/shelter-supply.controller.ts +++ b/src/shelter-supply/shelter-supply.controller.ts @@ -9,19 +9,31 @@ import { Put, UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { ShelterSupplyService } from './shelter-supply.service'; import { ServerResponse } from '../utils'; import { DistributionCenterGuard } from '@/guards/distribution-center.guard'; +import { CreateShelterSupplyDTO } from './dtos/CreateShelterSupplyDTO'; +import { UpdateShelterSupplyDTO } from './dtos/UpdateShelterSupplyDTO'; +import { UpdateManyShelterSupplySchemaDTO } from './dtos/UpdateManyShelterSupplyDTO'; @ApiTags('Suprimento de abrigos') +@ApiInternalServerErrorResponse() @Controller('shelter/supplies') export class ShelterSupplyController { private logger = new Logger(ShelterSupplyController.name); constructor(private readonly shelterSupplyService: ShelterSupplyService) {} + @ApiOkResponse() @Get(':shelterId') async index(@Param('shelterId') shelterId: string) { try { @@ -33,8 +45,10 @@ export class ShelterSupplyController { } } + @ApiBadRequestResponse() + @ApiOkResponse() @Post('') - async store(@Body() body) { + async store(@Body() body: CreateShelterSupplyDTO) { try { const data = await this.shelterSupplyService.store(body); return new ServerResponse( @@ -48,17 +62,20 @@ export class ShelterSupplyController { } } + @ApiBadRequestResponse() + @ApiOkResponse() @Put(':shelterId/:supplyId') async update( - @Body() body, + @Body() body: UpdateShelterSupplyDTO, @Param('shelterId') shelterId: string, @Param('supplyId') supplyId: string, ) { try { - const data = await this.shelterSupplyService.update({ - where: { shelterId, supplyId }, - data: body, - }); + const data = await this.shelterSupplyService.update( + body, + shelterId, + supplyId, + ); return new ServerResponse( 200, 'Successfully updated shelter supply', @@ -70,14 +87,18 @@ export class ShelterSupplyController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiOkResponse() @Put(':shelterId/supplies/many') @UseGuards(DistributionCenterGuard) - async updateMany(@Body() body, @Param('shelterId') shelterId: string) { + async updateMany( + @Body() body: UpdateManyShelterSupplySchemaDTO, + @Param('shelterId') shelterId: string, + ) { try { - const data = await this.shelterSupplyService.updateMany({ - shelterId, - ...body, - }); + const data = await this.shelterSupplyService.updateMany(body, shelterId); return new ServerResponse( 200, 'Successfully updated many shelter supplies', diff --git a/src/shelter-supply/shelter-supply.service.ts b/src/shelter-supply/shelter-supply.service.ts index ef4cf97a..c785e2b1 100644 --- a/src/shelter-supply/shelter-supply.service.ts +++ b/src/shelter-supply/shelter-supply.service.ts @@ -1,4 +1,3 @@ -import { z } from 'zod'; import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; @@ -8,6 +7,9 @@ import { UpdateShelterSupplySchema, } from './types'; import { SupplyPriority } from '../supply/types'; +import { CreateShelterSupplyDTO } from './dtos/CreateShelterSupplyDTO'; +import { UpdateShelterSupplyDTO } from './dtos/UpdateShelterSupplyDTO'; +import { UpdateManyShelterSupplySchemaDTO } from './dtos/UpdateManyShelterSupplyDTO'; @Injectable() export class ShelterSupplyService { @@ -31,7 +33,7 @@ export class ShelterSupplyService { }); } - async store(body: z.infer) { + async store(body: CreateShelterSupplyDTO) { const { shelterId, priority, supplyId, quantity } = CreateShelterSupplySchema.parse(body); await this.handleUpdateShelterSum(shelterId, 0, priority); @@ -46,22 +48,31 @@ export class ShelterSupplyService { }); } - async update(body: z.infer) { - const { data, where } = UpdateShelterSupplySchema.parse(body); - const { priority, quantity } = data; + async update( + body: UpdateShelterSupplyDTO, + shelterId: string, + supplyId: string, + ) { + const { + shelterId: shelterIdParse, + supplyId: supplyIdParse, + priority, + quantity, + } = UpdateShelterSupplySchema.parse({ ...body, shelterId, supplyId }); if (priority !== null && priority !== undefined) { const shelterSupply = await this.prismaService.shelterSupply.findFirst({ where: { - shelterId: where.shelterId, - supplyId: where.supplyId, + shelterId: shelterIdParse, + supplyId: supplyIdParse, }, select: { priority: true, }, }); + if (shelterSupply) await this.handleUpdateShelterSum( - where.shelterId, + shelterIdParse, shelterSupply.priority, priority, ); @@ -69,22 +80,26 @@ export class ShelterSupplyService { await this.prismaService.shelterSupply.update({ where: { - shelterId_supplyId: where, + shelterId_supplyId: { + shelterId: shelterIdParse, + supplyId: supplyIdParse, + }, }, data: { - ...data, + priority: priority ?? undefined, quantity: priority !== SupplyPriority.UnderControl ? quantity : null, updatedAt: new Date().toISOString(), }, }); } - async updateMany(body: z.infer) { - const { ids, shelterId } = UpdateManyShelterSupplySchema.parse(body); + async updateMany(body: UpdateManyShelterSupplySchemaDTO, shelterId: string) { + const { ids, shelterId: shelterIdParsed } = + UpdateManyShelterSupplySchema.parse({ ...body, shelterId }); const supplies = await this.prismaService.shelterSupply.findMany({ where: { - shelterId, + shelterId: shelterIdParsed, supplyId: { in: ids, }, @@ -99,7 +114,7 @@ export class ShelterSupplyService { await this.prismaService.$transaction([ this.prismaService.shelter.update({ where: { - id: shelterId, + id: shelterIdParsed, }, data: { prioritySum: { @@ -110,7 +125,7 @@ export class ShelterSupplyService { }), this.prismaService.shelterSupply.updateMany({ where: { - shelterId, + shelterId: shelterIdParsed, supplyId: { in: ids, }, diff --git a/src/shelter-supply/types.ts b/src/shelter-supply/types.ts index 1ff25f90..9b38d423 100644 --- a/src/shelter-supply/types.ts +++ b/src/shelter-supply/types.ts @@ -25,23 +25,18 @@ const CreateShelterSupplySchema = ShelterSupplySchema.pick({ }); const UpdateShelterSupplySchema = z.object({ - data: z - .object({ - priority: z.union([ - z.literal(SupplyPriority.UnderControl), - z.literal(SupplyPriority.Remaining), - z.literal(SupplyPriority.Needing), - z.literal(SupplyPriority.Urgent), - ]), - quantity: z.number().nullable().optional(), - shelterId: z.string(), - supplyId: z.string(), - }) - .partial(), - where: z.object({ - shelterId: z.string(), - supplyId: z.string(), - }), + shelterId: z.string(), + supplyId: z.string(), + priority: z + .union([ + z.literal(SupplyPriority.UnderControl), + z.literal(SupplyPriority.Remaining), + z.literal(SupplyPriority.Needing), + z.literal(SupplyPriority.Urgent), + ]) + .nullable() + .optional(), + quantity: z.number().nullable().optional(), }); const UpdateManyShelterSupplySchema = z.object({ diff --git a/src/shelter/dtos/CreateShelterDTO.ts b/src/shelter/dtos/CreateShelterDTO.ts new file mode 100644 index 00000000..c6579370 --- /dev/null +++ b/src/shelter/dtos/CreateShelterDTO.ts @@ -0,0 +1,62 @@ +import { + IsOptional, + IsString, + IsBoolean, + IsNumber, + Min, + IsNotEmpty, +} from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; + +export class CreateShelterDTO { + @ApiProperty({ type: 'string', example: 'Nome do Abrigo' }) + @IsNotEmpty() + @IsString() + readonly name = ''; + + @ApiProperty({ required: false, type: 'string', example: 'PIX do Abrigo' }) + @IsOptional() + @IsString() + readonly pix?: string; + + @ApiProperty({ type: 'string', example: 'Endereço do Abrigo' }) + @IsNotEmpty() + @IsString() + readonly address = ''; + + @ApiProperty({ required: false, type: 'boolean', example: true }) + @IsOptional() + @IsBoolean() + readonly petFriendly?: boolean; + + @ApiProperty({ required: false, type: 'number', example: 10 }) + @IsOptional() + @IsNumber() + @Min(0) + readonly shelteredPeople?: number; + + @ApiProperty({ required: false, type: 'number', example: 123.456 }) + @IsOptional() + @IsNumber() + readonly latitude?: number; + + @ApiProperty({ required: false, type: 'number', example: 654.321 }) + @IsOptional() + @IsNumber() + readonly longitude?: number; + + @ApiProperty({ required: false, type: 'number', example: 50 }) + @IsOptional() + @IsNumber() + @Min(0) + readonly capacity?: number; + + @ApiProperty({ + required: false, + type: 'string', + example: 'Contato do Abrigo', + }) + @IsOptional() + @IsString() + readonly contact?: string; +} diff --git a/src/shelter/dtos/FullUpdateShelterDTO.ts b/src/shelter/dtos/FullUpdateShelterDTO.ts new file mode 100644 index 00000000..757a98ec --- /dev/null +++ b/src/shelter/dtos/FullUpdateShelterDTO.ts @@ -0,0 +1,70 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { + IsOptional, + IsString, + IsBoolean, + IsNumber, + Min, +} from 'class-validator'; + +export class FullUpdateShelterDTO { + @ApiProperty({ required: false, type: 'string', example: 'Nome do Abrigo' }) + @IsOptional() + @IsString() + readonly name?: string; + + @ApiProperty({ required: false, type: 'string', example: 'PIX do Abrigo' }) + @IsOptional() + @IsString() + readonly pix?: string; + + @ApiProperty({ + required: false, + type: 'string', + example: 'Endereço do Abrigo', + }) + @IsOptional() + @IsString() + readonly address?: string; + + @ApiProperty({ required: false, type: 'boolean', example: true }) + @IsOptional() + @IsBoolean() + readonly petFriendly?: boolean; + + @ApiProperty({ required: false, type: 'number', example: 10 }) + @IsOptional() + @IsNumber() + @Min(0) + readonly shelteredPeople?: number; + + @ApiProperty({ required: false, type: 'number', example: 123.456 }) + @IsOptional() + @IsNumber() + readonly latitude?: number; + + @ApiProperty({ required: false, type: 'number', example: 654.321 }) + @IsOptional() + @IsNumber() + readonly longitude?: number; + + @ApiProperty({ required: false, type: 'number', example: 50 }) + @IsOptional() + @IsNumber() + @Min(0) + readonly capacity?: number; + + @ApiProperty({ + required: false, + type: 'string', + example: 'Contato do Abrigo', + }) + @IsOptional() + @IsString() + readonly contact?: string; + + @ApiProperty({ required: false, type: 'boolean' }) + @IsOptional() + @IsBoolean() + readonly verified?: boolean; +} diff --git a/src/shelter/dtos/ShelterQuerysDTO.ts b/src/shelter/dtos/ShelterQuerysDTO.ts new file mode 100644 index 00000000..b435dd3f --- /dev/null +++ b/src/shelter/dtos/ShelterQuerysDTO.ts @@ -0,0 +1,125 @@ +import { + IsOptional, + IsString, + IsNumber, + IsIn, + IsEnum, + ValidateNested, +} from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; +import { SupplyPriority } from 'src/supply/types'; +import { Transform } from 'class-transformer'; +import { ShelterStatus } from '../types/search.types'; + +export class ShelterQueryDTO { + @ApiProperty({ + required: false, + description: 'Quantidade de resultados por página', + }) + @IsOptional() + @IsNumber() + @Transform((value) => Number(value.value)) + readonly perPage?: number; + + @ApiProperty({ required: false, description: 'Número da página' }) + @IsOptional() + @IsNumber() + @Transform((value) => Number(value.value)) + readonly page?: number; + + @ApiProperty({ required: false, description: 'Termo de busca' }) + @IsOptional() + @IsString() + readonly search?: string; + + @ApiProperty({ + required: false, + description: 'Ordem dos resultados', + enum: ['asc', 'desc'], + }) + @IsOptional() + @IsIn(['asc', 'desc']) + readonly order?: 'asc' | 'desc'; + + @ApiProperty({ + required: false, + description: 'Critério de ordenação dos resultados', + }) + @IsOptional() + @IsString() + readonly orderBy?: string; + + @ApiProperty({ + required: false, + description: 'IDs de categoria de suprimento', + }) + @IsOptional() + @IsString({ each: true }) + readonly supplyCategoryIds?: string[]; + + @ApiProperty({ + required: false, + description: 'Prioridade de suprimento', + enum: SupplyPriority, + }) + @IsOptional() + @IsEnum(SupplyPriority) + @Transform((value) => Number(value.value)) + readonly priority?: SupplyPriority; + + @ApiProperty({ required: false, description: 'IDs de suprimento' }) + @IsOptional() + @IsString({ each: true }) + readonly supplyIds?: string[]; + + @ApiProperty({ + required: false, + description: 'Status do abrigo', + }) + @IsOptional() + @IsIn(['available', 'unavailable', 'waiting'], { each: true }) + readonly shelterStatus?: ShelterStatus[]; + + @ApiProperty({ + required: false, + description: 'Informação sobre tags do abrigo', + }) + @IsOptional() + @ValidateNested() + readonly tags?: Record< + 'NeedVolunteers' | 'NeedDonations' | 'RemainingSupplies', + number | undefined + >; + + @ApiProperty({ required: false, description: 'Lista de cidades' }) + @IsOptional() + @IsString({ each: true }) + readonly cities?: string[]; + + @ApiProperty({ + required: false, + description: 'Latitude', + }) + @IsOptional() + @IsNumber() + @Transform((value) => Number(value.value)) + readonly latitude?: number; + + @ApiProperty({ + required: false, + description: 'Longitude', + }) + @IsOptional() + @IsNumber() + @Transform((value) => Number(value.value)) + readonly longitude?: number; + + @ApiProperty({ + required: false, + description: 'Raio em metros', + }) + @IsOptional() + @IsNumber() + @Transform((value) => Number(value.value)) + readonly radiusInMeters?: number; +} diff --git a/src/shelter/dtos/UpdateShelterDTO.ts b/src/shelter/dtos/UpdateShelterDTO.ts new file mode 100644 index 00000000..2a7ba016 --- /dev/null +++ b/src/shelter/dtos/UpdateShelterDTO.ts @@ -0,0 +1,7 @@ +import { PickType } from '@nestjs/swagger'; +import { CreateShelterDTO } from './CreateShelterDTO'; + +export class UpdateShelterDTO extends PickType(CreateShelterDTO, [ + 'petFriendly', + 'shelteredPeople', +]) {} diff --git a/src/shelter/shelter.controller.ts b/src/shelter/shelter.controller.ts index 24603857..3bf3d751 100644 --- a/src/shelter/shelter.controller.ts +++ b/src/shelter/shelter.controller.ts @@ -10,23 +10,37 @@ import { Query, UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { ShelterService } from './shelter.service'; import { ServerResponse } from '../utils'; import { StaffGuard } from '@/guards/staff.guard'; import { ApplyUser } from '@/guards/apply-user.guard'; import { UserDecorator } from '@/decorators/UserDecorator/user.decorator'; +import { ShelterQueryDTO } from './dtos/ShelterQuerysDTO'; +import { CreateShelterDTO } from './dtos/CreateShelterDTO'; +import { UpdateShelterDTO } from './dtos/UpdateShelterDTO'; +import { FullUpdateShelterDTO } from './dtos/FullUpdateShelterDTO'; @ApiTags('Abrigos') +@ApiInternalServerErrorResponse() @Controller('shelters') export class ShelterController { private logger = new Logger(ShelterController.name); constructor(private readonly shelterService: ShelterService) {} + @ApiBadRequestResponse() + @ApiOkResponse() @Get('') - async index(@Query() query) { + async index(@Query() query: ShelterQueryDTO) { try { const data = await this.shelterService.index(query); return new ServerResponse(200, 'Successfully get shelters', data); @@ -36,6 +50,8 @@ export class ShelterController { } } + @ApiBadRequestResponse() + @ApiOkResponse() @Get('cities') async cities() { try { @@ -47,6 +63,8 @@ export class ShelterController { } } + @ApiBearerAuth() + @ApiOkResponse() @Get(':id') @UseGuards(ApplyUser) async show(@UserDecorator() user: any, @Param('id') id: string) { @@ -61,9 +79,13 @@ export class ShelterController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiOkResponse() @Post('') @UseGuards(StaffGuard) - async store(@Body() body) { + async store(@Body() body: CreateShelterDTO) { try { const data = await this.shelterService.store(body); return new ServerResponse(200, 'Successfully created shelter', data); @@ -73,8 +95,10 @@ export class ShelterController { } } + @ApiBadRequestResponse() + @ApiOkResponse() @Put(':id') - async update(@Param('id') id: string, @Body() body) { + async update(@Param('id') id: string, @Body() body: UpdateShelterDTO) { try { const data = await this.shelterService.update(id, body); return new ServerResponse(200, 'Successfully updated shelter', data); @@ -84,9 +108,16 @@ export class ShelterController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiOkResponse() @Put(':id/admin') @UseGuards(StaffGuard) - async fullUpdate(@Param('id') id: string, @Body() body) { + async fullUpdate( + @Param('id') id: string, + @Body() body: FullUpdateShelterDTO, + ) { try { const data = await this.shelterService.fullUpdate(id, body); return new ServerResponse(200, 'Successfully updated shelter', data); diff --git a/src/shelter/shelter.service.ts b/src/shelter/shelter.service.ts index 74d03860..538bf564 100644 --- a/src/shelter/shelter.service.ts +++ b/src/shelter/shelter.service.ts @@ -2,19 +2,22 @@ import { Injectable } from '@nestjs/common'; import { Prisma } from '@prisma/client'; import { DefaultArgs } from '@prisma/client/runtime/library'; import * as qs from 'qs'; -import { z } from 'zod'; import { PrismaService } from '../prisma/prisma.service'; -import { SupplyPriority } from '../supply/types'; -import { SearchSchema } from '../types'; -import { ShelterSearch, parseTagResponse } from './ShelterSearch'; -import { ShelterSearchPropsSchema } from './types/search.types'; import { CreateShelterSchema, FullUpdateShelterSchema, UpdateShelterSchema, } from './types/types'; import { subDays } from 'date-fns'; +import { SearchSchema } from 'src/types'; +import { ShelterSearch, parseTagResponse } from './ShelterSearch'; +import { SupplyPriority } from '../supply/types'; +import { ShelterSearchPropsSchema } from './types/search.types'; +import { CreateShelterDTO } from './dtos/CreateShelterDTO'; +import { ShelterQueryDTO } from './dtos/ShelterQuerysDTO'; +import { UpdateShelterDTO } from './dtos/UpdateShelterDTO'; +import { FullUpdateShelterDTO } from './dtos/FullUpdateShelterDTO'; @Injectable() export class ShelterService { @@ -24,7 +27,7 @@ export class ShelterService { this.loadVoluntaryIds(); } - async store(body: z.infer) { + async store(body: CreateShelterDTO) { const payload = CreateShelterSchema.parse(body); await this.prismaService.shelter.create({ @@ -36,7 +39,7 @@ export class ShelterService { }); } - async update(id: string, body: z.infer) { + async update(id: string, body: UpdateShelterDTO) { const payload = UpdateShelterSchema.parse(body); await this.prismaService.shelter.update({ where: { @@ -49,7 +52,7 @@ export class ShelterService { }); } - async fullUpdate(id: string, body: z.infer) { + async fullUpdate(id: string, body: FullUpdateShelterDTO) { const payload = FullUpdateShelterSchema.parse(body); await this.prismaService.shelter.update({ where: { @@ -114,7 +117,7 @@ export class ShelterService { return data; } - async index(query: any) { + async index(query: ShelterQueryDTO) { const { order, orderBy, diff --git a/src/supply-categories/dtos/CreateSupplyCategoryDTO.ts b/src/supply-categories/dtos/CreateSupplyCategoryDTO.ts new file mode 100644 index 00000000..96b6a863 --- /dev/null +++ b/src/supply-categories/dtos/CreateSupplyCategoryDTO.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateSupplyCategoryDTO { + @ApiProperty({ type: 'string', example: 'Nome da categoria do suprimento' }) + @IsNotEmpty() + @IsString() + readonly name = ''; +} diff --git a/src/supply-categories/dtos/UpdateSupplyCategoryDTO.ts b/src/supply-categories/dtos/UpdateSupplyCategoryDTO.ts new file mode 100644 index 00000000..b1ccfea1 --- /dev/null +++ b/src/supply-categories/dtos/UpdateSupplyCategoryDTO.ts @@ -0,0 +1,13 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateSupplyCategoryDTO { + @ApiProperty({ + required: false, + type: 'string', + example: 'Nome da categoria do suprimento', + }) + @IsOptional() + @IsString() + readonly name?: string; +} diff --git a/src/supply-categories/supply-categories.controller.ts b/src/supply-categories/supply-categories.controller.ts index 96106050..2bee6772 100644 --- a/src/supply-categories/supply-categories.controller.ts +++ b/src/supply-categories/supply-categories.controller.ts @@ -9,13 +9,23 @@ import { Put, UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { SupplyCategoriesService } from './supply-categories.service'; import { ServerResponse } from '../utils'; import { AdminGuard } from '@/guards/admin.guard'; +import { CreateSupplyCategoryDTO } from './dtos/CreateSupplyCategoryDTO'; +import { UpdateSupplyCategoryDTO } from './dtos/UpdateSupplyCategoryDTO'; @ApiTags('Categoria de Suprimentos') +@ApiInternalServerErrorResponse() @Controller('supply-categories') export class SupplyCategoriesController { private logger = new Logger(SupplyCategoriesController.name); @@ -24,6 +34,7 @@ export class SupplyCategoriesController { private readonly supplyCategoryServices: SupplyCategoriesService, ) {} + @ApiOkResponse() @Get('') async index() { try { @@ -39,9 +50,13 @@ export class SupplyCategoriesController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiOkResponse() @Post('') @UseGuards(AdminGuard) - async store(@Body() body) { + async store(@Body() body: CreateSupplyCategoryDTO) { try { const data = await this.supplyCategoryServices.store(body); return new ServerResponse( @@ -55,9 +70,13 @@ export class SupplyCategoriesController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiOkResponse() @Put(':id') @UseGuards(AdminGuard) - async update(@Param('id') id: string, @Body() body) { + async update(@Param('id') id: string, @Body() body: UpdateSupplyCategoryDTO) { try { const data = await this.supplyCategoryServices.update(id, body); return new ServerResponse( diff --git a/src/supply-categories/supply-categories.service.ts b/src/supply-categories/supply-categories.service.ts index 4ebd796a..2ef0b85d 100644 --- a/src/supply-categories/supply-categories.service.ts +++ b/src/supply-categories/supply-categories.service.ts @@ -1,17 +1,18 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; -import { z } from 'zod'; import { CreateSupplyCategorySchema, UpdateSupplyCategorySchema, } from './types'; +import { CreateSupplyCategoryDTO } from './dtos/CreateSupplyCategoryDTO'; +import { UpdateSupplyCategoryDTO } from './dtos/UpdateSupplyCategoryDTO'; @Injectable() export class SupplyCategoriesService { constructor(private readonly prismaService: PrismaService) {} - async store(body: z.infer) { + async store(body: CreateSupplyCategoryDTO) { const payload = CreateSupplyCategorySchema.parse(body); await this.prismaService.supplyCategory.create({ data: { @@ -21,7 +22,7 @@ export class SupplyCategoriesService { }); } - async update(id: string, body: z.infer) { + async update(id: string, body: UpdateSupplyCategoryDTO) { const payload = UpdateSupplyCategorySchema.parse(body); await this.prismaService.supplyCategory.update({ where: { diff --git a/src/supply/dtos/CreateSupplyDTO.ts b/src/supply/dtos/CreateSupplyDTO.ts new file mode 100644 index 00000000..27e4fba1 --- /dev/null +++ b/src/supply/dtos/CreateSupplyDTO.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateSupplyDTO { + @ApiProperty({ type: 'string', example: 'ID da Categoria do Suprimento' }) + @IsNotEmpty() + @IsString() + readonly supplyCategoryId = ''; + + @ApiProperty({ type: 'string', example: 'Nome do Suprimento' }) + @IsNotEmpty() + @IsString() + readonly name = ''; +} diff --git a/src/supply/dtos/UpdateSupplyDTO.ts b/src/supply/dtos/UpdateSupplyDTO.ts new file mode 100644 index 00000000..75c442ed --- /dev/null +++ b/src/supply/dtos/UpdateSupplyDTO.ts @@ -0,0 +1,22 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateSupplyDTO { + @ApiProperty({ + required: false, + type: 'string', + example: 'ID da Categoria do Suprimento', + }) + @IsOptional() + @IsString() + readonly supplyCategoryId?: string; + + @ApiProperty({ + required: false, + type: 'string', + example: 'Nome do Suprimento', + }) + @IsOptional() + @IsString() + readonly name?: string; +} diff --git a/src/supply/supply.controller.ts b/src/supply/supply.controller.ts index 55133484..3e0a77b6 100644 --- a/src/supply/supply.controller.ts +++ b/src/supply/supply.controller.ts @@ -8,18 +8,27 @@ import { Post, Put, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiInternalServerErrorResponse, + ApiOkResponse, + ApiTags, +} from '@nestjs/swagger'; import { ServerResponse } from '../utils'; import { SupplyService } from './supply.service'; +import { CreateSupplyDTO } from './dtos/CreateSupplyDTO'; +import { UpdateSupplyDTO } from './dtos/UpdateSupplyDTO'; @ApiTags('Suprimentos') +@ApiInternalServerErrorResponse() @Controller('supplies') export class SupplyController { private logger = new Logger(SupplyController.name); constructor(private readonly supplyServices: SupplyService) {} + @ApiOkResponse() @Get('') async index() { try { @@ -31,8 +40,10 @@ export class SupplyController { } } + @ApiBadRequestResponse() + @ApiOkResponse() @Post('') - async store(@Body() body) { + async store(@Body() body: CreateSupplyDTO) { try { const data = await this.supplyServices.store(body); return new ServerResponse(200, 'Successfully created supply', data); @@ -42,8 +53,10 @@ export class SupplyController { } } + @ApiBadRequestResponse() + @ApiOkResponse() @Put(':id') - async update(@Param('id') id: string, @Body() body) { + async update(@Param('id') id: string, @Body() body: UpdateSupplyDTO) { try { const data = await this.supplyServices.update(id, body); return new ServerResponse(200, 'Successfully updated supply', data); diff --git a/src/supply/supply.service.ts b/src/supply/supply.service.ts index a3caf57e..081c12b6 100644 --- a/src/supply/supply.service.ts +++ b/src/supply/supply.service.ts @@ -1,14 +1,15 @@ -import z from 'zod'; import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; import { CreateSupplySchema, UpdateSupplySchema } from './types'; +import { CreateSupplyDTO } from './dtos/CreateSupplyDTO'; +import { UpdateSupplyDTO } from './dtos/UpdateSupplyDTO'; @Injectable() export class SupplyService { constructor(private readonly prismaService: PrismaService) {} - async store(body: z.infer) { + async store(body: CreateSupplyDTO) { const payload = CreateSupplySchema.parse(body); return await this.prismaService.supply.create({ data: { @@ -18,7 +19,7 @@ export class SupplyService { }); } - async update(id: string, body: z.infer) { + async update(id: string, body: UpdateSupplyDTO) { const payload = UpdateSupplySchema.parse(body); await this.prismaService.supply.update({ where: { diff --git a/src/users/dtos/CreateUserDTO.ts b/src/users/dtos/CreateUserDTO.ts new file mode 100644 index 00000000..20d786c2 --- /dev/null +++ b/src/users/dtos/CreateUserDTO.ts @@ -0,0 +1,19 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; + +export class CreateUserDTO { + @ApiProperty({ type: 'string', example: 'John' }) + @IsNotEmpty() + @IsString() + readonly name = ''; + + @ApiProperty({ type: 'string', example: 'Doe' }) + @IsNotEmpty() + @IsString() + readonly lastName = ''; + + @ApiProperty({ type: 'string', example: '(55) 99671-6164' }) + @IsNotEmpty() + @IsString() + readonly phone = ''; +} diff --git a/src/users/dtos/UpdateUserDTO.ts b/src/users/dtos/UpdateUserDTO.ts new file mode 100644 index 00000000..f601a049 --- /dev/null +++ b/src/users/dtos/UpdateUserDTO.ts @@ -0,0 +1,29 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsOptional, IsString } from 'class-validator'; + +export class UpdateUserDTO { + @ApiProperty({ type: 'string', example: 'John' }) + @IsOptional() + @IsString() + readonly name?: string; + + @ApiProperty({ type: 'string', example: 'Doe' }) + @IsOptional() + @IsString() + readonly lastName?: string; + + @ApiProperty({ type: 'string', example: 'John' }) + @IsOptional() + @IsString() + readonly login?: string; + + @ApiProperty({ type: 'string', example: 'john123' }) + @IsOptional() + @IsString() + readonly password?: string; + + @ApiProperty({ type: 'string', example: '(55) 99671-6164' }) + @IsOptional() + @IsString() + readonly phone?: string; +} diff --git a/src/users/users.controller.ts b/src/users/users.controller.ts index 5d4a0dc2..3e86a7a7 100644 --- a/src/users/users.controller.ts +++ b/src/users/users.controller.ts @@ -9,12 +9,21 @@ import { Req, UseGuards, } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; +import { + ApiBadRequestResponse, + ApiBearerAuth, + ApiCreatedResponse, + ApiInternalServerErrorResponse, + ApiTags, + ApiUnauthorizedResponse, +} from '@nestjs/swagger'; import { UserGuard } from '@/guards/user.guard'; import { ServerResponse } from '../utils'; import { UsersService } from './users.service'; import { AdminGuard } from '@/guards/admin.guard'; +import { CreateUserDTO } from './dtos/CreateUserDTO'; +import { UpdateUserDTO } from './dtos/UpdateUserDTO'; @ApiTags('Usuários') @Controller('users') @@ -23,9 +32,14 @@ export class UsersController { constructor(private readonly userServices: UsersService) {} + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiInternalServerErrorResponse() + @ApiCreatedResponse() @Post('') @UseGuards(AdminGuard) - async store(@Body() body) { + async store(@Body() body: CreateUserDTO) { try { await this.userServices.store(body); return new ServerResponse(201, 'Successfully created user'); @@ -35,9 +49,14 @@ export class UsersController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiInternalServerErrorResponse() + @ApiCreatedResponse() @Put(':id') @UseGuards(AdminGuard) - async update(@Body() body, @Param('id') id: string) { + async update(@Body() body: UpdateUserDTO, @Param('id') id: string) { try { await this.userServices.update(id, body); return new ServerResponse(201, 'Successfully updated user'); @@ -47,9 +66,14 @@ export class UsersController { } } + @ApiBearerAuth() + @ApiUnauthorizedResponse() + @ApiBadRequestResponse() + @ApiInternalServerErrorResponse() + @ApiCreatedResponse() @Put('') @UseGuards(UserGuard) - async selfUpdate(@Body() body, @Req() req) { + async selfUpdate(@Body() body: UpdateUserDTO, @Req() req) { try { const { userId } = req.user; await this.userServices.update(userId, body); diff --git a/src/users/users.service.ts b/src/users/users.service.ts index 49e5df91..48cf95c9 100644 --- a/src/users/users.service.ts +++ b/src/users/users.service.ts @@ -1,13 +1,15 @@ import { Injectable } from '@nestjs/common'; import { PrismaService } from '../prisma/prisma.service'; +import { CreateUserDTO } from './dtos/CreateUserDTO'; +import { UpdateUserDTO } from './dtos/UpdateUserDTO'; import { CreateUserSchema, UpdateUserSchema } from './types'; @Injectable() export class UsersService { constructor(private readonly prismaService: PrismaService) {} - async store(body: any) { + async store(body: CreateUserDTO) { const { name, lastName, phone } = CreateUserSchema.parse(body); await this.prismaService.user.create({ data: { @@ -21,7 +23,7 @@ export class UsersService { }); } - async update(id: string, body: any) { + async update(id: string, body: UpdateUserDTO) { const payload = UpdateUserSchema.parse(body); await this.prismaService.user.update({ where: {