Skip to content

Commit 971ab98

Browse files
committed
docs: adding swagger decorators into controller and main with your examples and customized ones
1 parent ce9ba09 commit 971ab98

15 files changed

Lines changed: 553 additions & 14 deletions

backend/src/auth/auth.controller.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,61 @@
11
import { Body, Controller, Get, Post, UseGuards } from '@nestjs/common';
2+
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
23
import { AuthService } from './auth.service';
34
import { UserLoginDto } from './dtos/user-login.dto';
45
import { UserRegisterDto } from './dtos/user-register.dto';
56
import { ResponseUserDto } from '../modules/users/dtos/response-user.dto';
67
import { HashUserPasswordPipe } from '../common/pipes/hash-user-password.pipe';
78
import { GetUser } from './decorators/get-user.decorator';
89
import { JwtAuthGuard } from './guards/jwt-auth.guard';
10+
import {
11+
CreatedResponseDecorator,
12+
OkResponseDecorator,
13+
ValidationBadRequestResponseDecorator,
14+
UsuarioUnauthorizedResponseDecorator,
15+
ConflictResponseDecorator,
16+
userResponseExample,
17+
} from '../common/decorators/swagger-api.decorator';
918

19+
@ApiTags('Auth')
1020
@Controller('auth')
1121
export class AuthController {
1222
constructor(private readonly authService: AuthService) {}
1323

1424
@UseGuards(JwtAuthGuard)
25+
@ApiBearerAuth('access-token')
1526
@Get('profile')
27+
@OkResponseDecorator('Perfil do usuário retornado com sucesso', {
28+
user_id: 'user-123',
29+
username: 'joao',
30+
})
31+
@UsuarioUnauthorizedResponseDecorator()
1632
profile(@GetUser() user: Record<string, any>) {
1733
return user;
1834
}
1935

2036
@Post('login')
37+
@OkResponseDecorator('Login realizado com sucesso', {
38+
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
39+
payload: {
40+
sub: 'user-123',
41+
username: 'joao',
42+
},
43+
})
44+
@ValidationBadRequestResponseDecorator()
45+
@UsuarioUnauthorizedResponseDecorator()
2146
async login(
2247
@Body() userLoginDto: UserLoginDto,
2348
): Promise<Record<any, string>> {
2449
return await this.authService.login(userLoginDto);
2550
}
2651

2752
@Post('register')
53+
@CreatedResponseDecorator('Usuário registrado com sucesso', {
54+
...userResponseExample,
55+
password: undefined,
56+
})
57+
@ValidationBadRequestResponseDecorator()
58+
@ConflictResponseDecorator('Username já existe')
2859
async register(
2960
@Body(HashUserPasswordPipe) userRegisterDto: UserRegisterDto,
3061
): Promise<ResponseUserDto> {

backend/src/auth/dtos/user-login.dto.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
import { IsNotEmpty, IsString } from 'class-validator';
2+
import { ApiProperty } from '@nestjs/swagger';
23

34
export class UserLoginDto {
5+
@ApiProperty({
6+
description: 'Nome de usuário',
7+
example: 'joao',
8+
type: String,
9+
})
410
@IsString()
511
@IsNotEmpty()
612
username: string;
713

14+
@ApiProperty({
15+
description: 'Senha do usuário',
16+
example: 'senha123',
17+
type: String,
18+
})
819
@IsString()
920
@IsNotEmpty()
1021
password: string;
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
1-
import { IsNotEmpty, IsString } from 'class-validator';
1+
import { IsNotEmpty, IsString, MinLength } from 'class-validator';
2+
import { ApiProperty } from '@nestjs/swagger';
23

34
export class UserRegisterDto {
5+
@ApiProperty({
6+
description: 'Nome de usuário (único)',
7+
example: 'joao',
8+
type: String,
9+
minLength: 3,
10+
})
411
@IsString()
512
@IsNotEmpty()
13+
@MinLength(3)
614
username: string;
715

16+
@ApiProperty({
17+
description: 'Senha do usuário',
18+
example: 'senha123',
19+
type: String,
20+
minLength: 6,
21+
})
822
@IsString()
923
@IsNotEmpty()
24+
@MinLength(6)
1025
password: string;
1126
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
import { applyDecorators } from '@nestjs/common';
2+
import {
3+
ApiBadRequestResponse,
4+
ApiConflictResponse,
5+
ApiCreatedResponse,
6+
ApiForbiddenResponse,
7+
ApiNotFoundResponse,
8+
ApiOkResponse,
9+
ApiUnauthorizedResponse,
10+
} from '@nestjs/swagger';
11+
import { Prisma } from '@prisma/client';
12+
13+
export type SwaggerParamsType = {
14+
description: string;
15+
example: Record<string, any>;
16+
};
17+
18+
export function CreatedResponseDecorator(
19+
description: string,
20+
example: Record<string, any>,
21+
) {
22+
return applyDecorators(
23+
ApiCreatedResponse({
24+
description,
25+
example,
26+
}),
27+
);
28+
}
29+
30+
export function OkResponseDecorator(
31+
description: string,
32+
example: Record<string, any>,
33+
) {
34+
return applyDecorators(
35+
ApiOkResponse({
36+
description,
37+
example,
38+
}),
39+
);
40+
}
41+
42+
export function UsuarioUnauthorizedResponseDecorator() {
43+
return applyDecorators(
44+
ApiUnauthorizedResponse({
45+
description: 'Usuário não autenticado',
46+
example: {
47+
statusCode: 401,
48+
message: `Unauthorized`,
49+
},
50+
}),
51+
);
52+
}
53+
54+
export function ValidationBadRequestResponseDecorator() {
55+
return applyDecorators(
56+
ApiBadRequestResponse({
57+
example: {
58+
statusCode: 400,
59+
message: ['dto message error'],
60+
error: 'Bad Request',
61+
},
62+
}),
63+
);
64+
}
65+
66+
export function ConflictResponseDecorator(text: string) {
67+
return applyDecorators(
68+
ApiConflictResponse({
69+
description: text,
70+
example: {
71+
statusCode: 409,
72+
message: text,
73+
},
74+
}),
75+
);
76+
}
77+
78+
export function NotFoundEntityResponseDecorator(
79+
entity: string,
80+
field: string = 'field_example',
81+
) {
82+
return applyDecorators(
83+
ApiNotFoundResponse({
84+
description: `${entity} não encontrado`,
85+
example: {
86+
statusCode: 404,
87+
message: `Nenhum ${entity} encontrado com os atributos: [${field}: ${field}]`,
88+
},
89+
}),
90+
);
91+
}
92+
93+
export function ForbiddenResponseDecorator(text: string) {
94+
return applyDecorators(
95+
ApiForbiddenResponse({
96+
description: text,
97+
example: { statusCode: 403, message: text },
98+
}),
99+
);
100+
}
101+
102+
export const userResponseExample = {
103+
id: 'user-123',
104+
username: 'joao',
105+
created_at: '2025-01-01T00:00:00.000Z',
106+
updated_at: '2025-01-01T00:00:00.000Z',
107+
deleted_at: null,
108+
} as const;
109+
110+
export const collaboratorResponseExample = {
111+
id: 'collab-123',
112+
name: 'Maria Silva',
113+
user_id: 'user-123',
114+
created_at: '2025-01-01T00:00:00.000Z',
115+
updated_at: '2025-01-01T00:00:00.000Z',
116+
deleted_at: null,
117+
user: userResponseExample,
118+
} as const;
119+
120+
export const projectResponseExample = {
121+
id: 'proj-123',
122+
name: 'Projeto XPTO',
123+
created_at: '2025-01-01T00:00:00.000Z',
124+
updated_at: '2025-01-01T00:00:00.000Z',
125+
deleted_at: null,
126+
tasks: [],
127+
} as const;
128+
129+
export const taskResponseExample = {
130+
id: 'task-123',
131+
name: 'Implementar autenticação',
132+
description: 'Criar sistema de login com JWT',
133+
project_id: 'proj-123',
134+
created_at: '2025-01-01T00:00:00.000Z',
135+
updated_at: '2025-01-01T00:00:00.000Z',
136+
deleted_at: null,
137+
project: {
138+
id: 'proj-123',
139+
name: 'Projeto XPTO',
140+
},
141+
} as const;
142+
143+
export const timeTrackerResponseExample = {
144+
id: 'tt-123',
145+
start_date: '2025-01-15T08:00:00.000Z',
146+
end_date: '2025-01-15T17:00:00.000Z',
147+
timezone_id: 'America/Sao_Paulo',
148+
task_id: 'task-123',
149+
collaborator_id: 'collab-123',
150+
created_at: '2025-01-01T00:00:00.000Z',
151+
updated_at: '2025-01-01T00:00:00.000Z',
152+
deleted_at: null,
153+
tasks: {
154+
id: 'task-123',
155+
name: 'Implementar autenticação',
156+
},
157+
collaborators: {
158+
id: 'collab-123',
159+
name: 'Maria Silva',
160+
},
161+
} as const;

backend/src/main.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,17 @@ async function bootstrap() {
1010
.setDescription('Time Tracker API documentation')
1111
.setVersion('1.0')
1212
.addTag('time-tracker')
13+
.addBearerAuth(
14+
{
15+
type: 'http',
16+
scheme: 'bearer',
17+
bearerFormat: 'JWT',
18+
name: 'Authorization',
19+
description: 'Insira o token JWT aqui',
20+
in: 'header',
21+
},
22+
'access-token',
23+
)
1324
.build();
1425
const documentFactory = () => SwaggerModule.createDocument(app, config);
1526
SwaggerModule.setup('api', app, documentFactory);

backend/src/modules/collaborators/collaborators.controller.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,29 +7,66 @@ import {
77
Delete,
88
Post,
99
} from '@nestjs/common';
10+
import { ApiTags } from '@nestjs/swagger';
1011
import { CollaboratorsService } from './collaborators.service';
1112
import { UpdateCollaboratorDto } from './dtos/update-collaborator.dto';
1213
import { CreateCollaboratorDto } from './dtos/create-collaborator.dto';
14+
import {
15+
CreatedResponseDecorator,
16+
OkResponseDecorator,
17+
ValidationBadRequestResponseDecorator,
18+
NotFoundEntityResponseDecorator,
19+
ConflictResponseDecorator,
20+
UsuarioUnauthorizedResponseDecorator,
21+
collaboratorResponseExample,
22+
} from '../../common/decorators/swagger-api.decorator';
1323

24+
@ApiTags('Collaborators')
1425
@Controller('collaborators')
1526
export class CollaboratorsController {
1627
constructor(private readonly collaboratorsService: CollaboratorsService) {}
1728

1829
@Post()
30+
@CreatedResponseDecorator(
31+
'Colaborador criado com sucesso',
32+
collaboratorResponseExample,
33+
)
34+
@ValidationBadRequestResponseDecorator()
35+
@ConflictResponseDecorator('Nome do colaborador já existe')
36+
@UsuarioUnauthorizedResponseDecorator()
1937
async create(@Body() createCollaboratorDto: CreateCollaboratorDto) {
2038
return await this.collaboratorsService.create(createCollaboratorDto);
2139
}
40+
2241
@Get()
42+
@OkResponseDecorator('Lista de colaboradores retornada com sucesso', [
43+
collaboratorResponseExample,
44+
])
45+
@UsuarioUnauthorizedResponseDecorator()
2346
async findAll() {
2447
return this.collaboratorsService.findAll();
2548
}
2649

2750
@Get(':id')
51+
@OkResponseDecorator(
52+
'Colaborador encontrado com sucesso',
53+
collaboratorResponseExample,
54+
)
55+
@NotFoundEntityResponseDecorator('Colaborador', 'id')
56+
@UsuarioUnauthorizedResponseDecorator()
2857
async findOne(@Param('id') id: string) {
2958
return this.collaboratorsService.findOne(id);
3059
}
3160

3261
@Patch(':id')
62+
@OkResponseDecorator(
63+
'Colaborador atualizado com sucesso',
64+
collaboratorResponseExample,
65+
)
66+
@ValidationBadRequestResponseDecorator()
67+
@NotFoundEntityResponseDecorator('Colaborador', 'id')
68+
@ConflictResponseDecorator('Nome do colaborador já existe')
69+
@UsuarioUnauthorizedResponseDecorator()
3370
async update(
3471
@Param('id') id: string,
3572
@Body() updateCollaboratorDto: UpdateCollaboratorDto,
@@ -38,11 +75,23 @@ export class CollaboratorsController {
3875
}
3976

4077
@Patch('active/:id')
78+
@OkResponseDecorator(
79+
'Colaborador reativado com sucesso',
80+
collaboratorResponseExample,
81+
)
82+
@NotFoundEntityResponseDecorator('Colaborador', 'id')
83+
@UsuarioUnauthorizedResponseDecorator()
4184
async active(@Param('id') id: string) {
4285
return await this.collaboratorsService.active(id);
4386
}
4487

4588
@Delete(':id')
89+
@OkResponseDecorator('Colaborador removido com sucesso (soft delete)', {
90+
...collaboratorResponseExample,
91+
deletedAt: '2025-01-01T00:00:00.000Z',
92+
})
93+
@NotFoundEntityResponseDecorator('Colaborador', 'id')
94+
@UsuarioUnauthorizedResponseDecorator()
4695
async softRemove(@Param('id') id: string) {
4796
return this.collaboratorsService.softRemove(id);
4897
}

0 commit comments

Comments
 (0)