Skip to content

Commit 407b3f8

Browse files
committed
feat: add req body validation & swagger docs to shelters routes
1 parent 9ccf1d7 commit 407b3f8

File tree

5 files changed

+186
-12
lines changed

5 files changed

+186
-12
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import {
2+
IsOptional,
3+
IsString,
4+
IsBoolean,
5+
IsNumber,
6+
Min,
7+
IsNotEmpty,
8+
} from 'class-validator';
9+
import { ApiProperty } from '@nestjs/swagger';
10+
11+
export class CreateShelterDTO {
12+
@ApiProperty({ type: 'string', example: 'Nome do Abrigo' })
13+
@IsNotEmpty()
14+
@IsString()
15+
readonly name = '';
16+
17+
@ApiProperty({ required: false, type: 'string', example: 'PIX do Abrigo' })
18+
@IsOptional()
19+
@IsString()
20+
readonly pix?: string;
21+
22+
@ApiProperty({ type: 'string', example: 'Endereço do Abrigo' })
23+
@IsNotEmpty()
24+
@IsString()
25+
readonly address = '';
26+
27+
@ApiProperty({ required: false, type: 'boolean', example: true })
28+
@IsOptional()
29+
@IsBoolean()
30+
readonly petFriendly?: boolean;
31+
32+
@ApiProperty({ required: false, type: 'number', example: 10 })
33+
@IsOptional()
34+
@IsNumber()
35+
@Min(0)
36+
readonly shelteredPeople?: number;
37+
38+
@ApiProperty({ required: false, type: 'number', example: 123.456 })
39+
@IsOptional()
40+
@IsNumber()
41+
readonly latitude?: number;
42+
43+
@ApiProperty({ required: false, type: 'number', example: 654.321 })
44+
@IsOptional()
45+
@IsNumber()
46+
readonly longitude?: number;
47+
48+
@ApiProperty({ required: false, type: 'number', example: 50 })
49+
@IsOptional()
50+
@IsNumber()
51+
@Min(0)
52+
readonly capacity?: number;
53+
54+
@ApiProperty({
55+
required: false,
56+
type: 'string',
57+
example: 'Contato do Abrigo',
58+
})
59+
@IsOptional()
60+
@IsString()
61+
readonly contact?: string;
62+
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { IsOptional, IsString, IsNumber, IsIn, IsEnum } from 'class-validator';
2+
import { ApiProperty } from '@nestjs/swagger';
3+
import { SupplyPriority } from 'src/supply/types';
4+
import { Transform } from 'class-transformer';
5+
6+
export class ShelterQueryDTO {
7+
@ApiProperty({
8+
required: false,
9+
description: 'Quantidade de resultados por página',
10+
})
11+
@IsOptional()
12+
@IsNumber()
13+
@Transform((value) => Number(value.value))
14+
perPage?: number;
15+
16+
@ApiProperty({ required: false, description: 'Número da página' })
17+
@IsOptional()
18+
@IsNumber()
19+
@Transform((value) => Number(value.value))
20+
page?: number;
21+
22+
@ApiProperty({ required: false, description: 'Termo de busca' })
23+
@IsOptional()
24+
@IsString()
25+
search?: string;
26+
27+
@ApiProperty({
28+
required: false,
29+
description: 'Ordem dos resultados',
30+
enum: ['asc', 'desc'],
31+
})
32+
@IsOptional()
33+
@IsIn(['asc', 'desc'])
34+
order?: 'asc' | 'desc';
35+
36+
@ApiProperty({
37+
required: false,
38+
description: 'Critério de ordenação dos resultados',
39+
})
40+
@IsOptional()
41+
@IsString()
42+
orderBy?: string;
43+
44+
@ApiProperty({
45+
required: false,
46+
description: 'IDs de categoria de suprimento',
47+
})
48+
@IsOptional()
49+
@IsString({ each: true })
50+
supplyCategoryIds?: string[];
51+
52+
@ApiProperty({
53+
required: false,
54+
description: 'Prioridade de suprimento',
55+
enum: SupplyPriority,
56+
})
57+
@IsOptional()
58+
@IsEnum(SupplyPriority)
59+
@Transform((value) => Number(value.value))
60+
priority?: SupplyPriority;
61+
62+
@ApiProperty({ required: false, description: 'IDs de suprimento' })
63+
@IsOptional()
64+
@IsString({ each: true })
65+
supplyIds?: string[];
66+
67+
@ApiProperty({
68+
required: false,
69+
description: 'Status do abrigo',
70+
})
71+
@IsOptional()
72+
@IsIn(['available', 'unavailable', 'waiting'], { each: true })
73+
shelterStatus?: ('available' | 'unavailable' | 'waiting')[]; // MUDAR !!!!
74+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { PickType } from '@nestjs/swagger';
2+
import { CreateShelterDTO } from './CreateShelterDTO';
3+
4+
export class UpdateShelterDTO extends PickType(CreateShelterDTO, [
5+
'petFriendly',
6+
'shelteredPeople',
7+
]) {}

src/shelter/shelter.controller.ts

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,23 +10,36 @@ import {
1010
Query,
1111
UseGuards,
1212
} from '@nestjs/common';
13-
import { ApiTags } from '@nestjs/swagger';
13+
import {
14+
ApiBadRequestResponse,
15+
ApiBearerAuth,
16+
ApiInternalServerErrorResponse,
17+
ApiOkResponse,
18+
ApiTags,
19+
ApiUnauthorizedResponse,
20+
} from '@nestjs/swagger';
1421

1522
import { ShelterService } from './shelter.service';
1623
import { ServerResponse } from '../utils';
1724
import { StaffGuard } from '@/guards/staff.guard';
1825
import { ApplyUser } from '@/guards/apply-user.guard';
1926
import { UserDecorator } from '@/decorators/UserDecorator/user.decorator';
27+
import { ShelterQueryDTO } from './dtos/ShelterQuerysDTO';
28+
import { CreateShelterDTO } from './dtos/CreateShelterDTO';
29+
import { UpdateShelterDTO } from './dtos/UpdateShelterDTO';
2030

2131
@ApiTags('Abrigos')
32+
@ApiInternalServerErrorResponse()
2233
@Controller('shelters')
2334
export class ShelterController {
2435
private logger = new Logger(ShelterController.name);
2536

2637
constructor(private readonly shelterService: ShelterService) {}
2738

39+
@ApiBadRequestResponse()
40+
@ApiOkResponse()
2841
@Get('')
29-
async index(@Query() query) {
42+
async index(@Query() query: ShelterQueryDTO) {
3043
try {
3144
const data = await this.shelterService.index(query);
3245
return new ServerResponse(200, 'Successfully get shelters', data);
@@ -36,6 +49,8 @@ export class ShelterController {
3649
}
3750
}
3851

52+
@ApiBadRequestResponse()
53+
@ApiOkResponse()
3954
@Get('cities')
4055
async cities() {
4156
try {
@@ -47,6 +62,8 @@ export class ShelterController {
4762
}
4863
}
4964

65+
@ApiBearerAuth()
66+
@ApiOkResponse()
5067
@Get(':id')
5168
@UseGuards(ApplyUser)
5269
async show(@UserDecorator() user: any, @Param('id') id: string) {
@@ -61,9 +78,13 @@ export class ShelterController {
6178
}
6279
}
6380

81+
@ApiBearerAuth()
82+
@ApiUnauthorizedResponse()
83+
@ApiBadRequestResponse()
84+
@ApiOkResponse()
6485
@Post('')
6586
@UseGuards(StaffGuard)
66-
async store(@Body() body) {
87+
async store(@Body() body: CreateShelterDTO) {
6788
try {
6889
const data = await this.shelterService.store(body);
6990
return new ServerResponse(200, 'Successfully created shelter', data);
@@ -73,8 +94,10 @@ export class ShelterController {
7394
}
7495
}
7596

97+
@ApiBadRequestResponse()
98+
@ApiOkResponse()
7699
@Put(':id')
77-
async update(@Param('id') id: string, @Body() body) {
100+
async update(@Param('id') id: string, @Body() body: UpdateShelterDTO) {
78101
try {
79102
const data = await this.shelterService.update(id, body);
80103
return new ServerResponse(200, 'Successfully updated shelter', data);
@@ -84,9 +107,13 @@ export class ShelterController {
84107
}
85108
}
86109

110+
@ApiBearerAuth()
111+
@ApiUnauthorizedResponse()
112+
@ApiBadRequestResponse()
113+
@ApiOkResponse()
87114
@Put(':id/admin')
88115
@UseGuards(StaffGuard)
89-
async fullUpdate(@Param('id') id: string, @Body() body) {
116+
async fullUpdate(@Param('id') id: string, @Body() body: UpdateShelterDTO) {
90117
try {
91118
const data = await this.shelterService.fullUpdate(id, body);
92119
return new ServerResponse(200, 'Successfully updated shelter', data);

src/shelter/shelter.service.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,19 @@ import * as qs from 'qs';
55
import { z } from 'zod';
66

77
import { PrismaService } from '../prisma/prisma.service';
8-
import { SupplyPriority } from '../supply/types';
9-
import { SearchSchema } from '../types';
10-
import { ShelterSearch, parseTagResponse } from './ShelterSearch';
11-
import { ShelterSearchPropsSchema } from './types/search.types';
128
import {
139
CreateShelterSchema,
1410
FullUpdateShelterSchema,
1511
UpdateShelterSchema,
1612
} from './types/types';
1713
import { subDays } from 'date-fns';
14+
import { SupplyPriority } from 'src/supply/types';
15+
import { SearchSchema } from 'src/types';
16+
import { ShelterSearch, parseTagResponse } from './ShelterSearch';
17+
import { CreateShelterDTO } from './dtos/CreateShelterDTO';
18+
import { ShelterQueryDTO } from './dtos/ShelterQuerysDTO';
19+
import { UpdateShelterDTO } from './dtos/UpdateShelterDTO';
20+
import { ShelterSearchPropsSchema } from './types/search.types';
1821

1922
@Injectable()
2023
export class ShelterService {
@@ -24,7 +27,7 @@ export class ShelterService {
2427
this.loadVoluntaryIds();
2528
}
2629

27-
async store(body: z.infer<typeof CreateShelterSchema>) {
30+
async store(body: CreateShelterDTO) {
2831
const payload = CreateShelterSchema.parse(body);
2932

3033
await this.prismaService.shelter.create({
@@ -36,7 +39,7 @@ export class ShelterService {
3639
});
3740
}
3841

39-
async update(id: string, body: z.infer<typeof UpdateShelterSchema>) {
42+
async update(id: string, body: UpdateShelterDTO) {
4043
const payload = UpdateShelterSchema.parse(body);
4144
await this.prismaService.shelter.update({
4245
where: {
@@ -114,7 +117,8 @@ export class ShelterService {
114117
return data;
115118
}
116119

117-
async index(query: any) {
120+
// ARRUMAR o queryData
121+
async index(query: ShelterQueryDTO) {
118122
const {
119123
order,
120124
orderBy,

0 commit comments

Comments
 (0)