diff --git a/src/app.controller.ts b/src/app.controller.ts index 9e9fc65..dd26426 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,13 +1,15 @@ import { Controller, Get } from '@nestjs/common'; +import { ApiOkResponse } from '@nestjs/swagger'; @Controller() export class AppController { + @ApiOkResponse({ description: 'Health check endpoint' }) @Get('/health-check') healthCheck(): { status: string } { return { status: 'ok' }; } - // return a plain text string as a response to the GET request to the root path + @ApiOkResponse({ description: 'Hello World endpoint' }) @Get() getHello(): string { return 'Hello World!'; diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 5851294..7c96901 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -14,11 +14,14 @@ import { GetUser } from './decorator'; import { User } from '@prisma/client'; import { AccessTokenGuard, RefreshTokenGuard } from './guard'; import { Tokens } from './types'; +import { ApiOkResponse, ApiTags } from '@nestjs/swagger'; +@ApiTags('auth') @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} + @ApiOkResponse({ description: 'succes signup endpoint' }) @Post('signup') @HttpCode(HttpStatus.CREATED) signup(@Body() dto: SignUpDto): Promise { @@ -31,6 +34,7 @@ export class AuthController { return this.authService.login(dto); } + @ApiOkResponse({ description: 'check token endpoint' }) @Get('check') @UseGuards(AccessTokenGuard) checkToken(@GetUser() user: User) { diff --git a/src/auth/dto/email.dto.ts b/src/auth/dto/email.dto.ts index 12549ad..0552efc 100644 --- a/src/auth/dto/email.dto.ts +++ b/src/auth/dto/email.dto.ts @@ -1,6 +1,8 @@ import { IsEmail, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class EmailDto { + @ApiProperty({ example: 'username@example.com', description: 'User email' }) @IsEmail() @IsNotEmpty() email: string; diff --git a/src/auth/dto/login.dto.ts b/src/auth/dto/login.dto.ts index ad13045..7327624 100644 --- a/src/auth/dto/login.dto.ts +++ b/src/auth/dto/login.dto.ts @@ -1,10 +1,17 @@ import { IsEmail, IsNotEmpty, IsString, Length } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class LoginDto { + @ApiProperty({ example: 'username@example.com', description: 'User email' }) @IsEmail() @IsNotEmpty() email: string; + @ApiProperty({ + example: 'password', + description: 'User password', + minLength: 6, + }) @IsString() @IsNotEmpty() @Length(6) diff --git a/src/auth/dto/signup.dto.ts b/src/auth/dto/signup.dto.ts index c72ca4d..cf3b385 100644 --- a/src/auth/dto/signup.dto.ts +++ b/src/auth/dto/signup.dto.ts @@ -5,20 +5,32 @@ import { IsString, MinLength, } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class SignUpDto { + @ApiProperty({ example: 'username@example.com', description: 'User email' }) @IsEmail() @IsNotEmpty() email: string; + @ApiProperty({ example: 'John Doe', description: 'User full name' }) @IsString() @IsNotEmpty() fullName: string; + @ApiProperty({ + example: '2000-01-01', + description: 'User birthday in ISO 8601 format', + }) @IsISO8601() @IsNotEmpty() birthday: string; + @ApiProperty({ + example: 'password', + description: 'User password', + minLength: 6, + }) @IsString() @IsNotEmpty() @MinLength(6) diff --git a/src/birthday/birthday.controller.ts b/src/birthday/birthday.controller.ts index ccd815f..a7577e8 100644 --- a/src/birthday/birthday.controller.ts +++ b/src/birthday/birthday.controller.ts @@ -16,8 +16,10 @@ import { AccessTokenGuard, VerificationGuard } from '../auth/guard'; import { GetUser } from '../auth/decorator'; import { CreateBirthdayDto, EditBirthdayDto } from './dto'; import { User } from '@prisma/client'; +import { ApiTags } from '@nestjs/swagger'; @UseGuards(AccessTokenGuard, VerificationGuard) +@ApiTags('birthday') @Controller('birthday') export class BirthdayController { constructor(private birthdayService: BirthdayService) {} diff --git a/src/birthday/dto/create-birthday.dto.ts b/src/birthday/dto/create-birthday.dto.ts index 01d881d..d39c7b6 100644 --- a/src/birthday/dto/create-birthday.dto.ts +++ b/src/birthday/dto/create-birthday.dto.ts @@ -1,18 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; import { IsOptional, IsString, IsISO8601, IsNotEmpty } from 'class-validator'; export class CreateBirthdayDto { + @ApiProperty({ example: 'john doe', description: 'full name' }) @IsString() @IsNotEmpty() name: string; + @ApiProperty({ example: '2000-01-01', description: 'birthday' }) @IsISO8601() @IsNotEmpty() birthday: string; + @ApiProperty({ example: 'father', description: 'relationship' }) @IsString() @IsOptional() relationship?: string; + @ApiProperty({ example: 'notes', description: 'notes' }) @IsString() @IsOptional() notes?: string; diff --git a/src/main.ts b/src/main.ts index 35dbcb4..96b84ab 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,6 +2,7 @@ import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { ValidationPipe } from '@nestjs/common'; import { Logger } from 'nestjs-pino'; +import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; async function bootstrap() { // cors: true = allow all the request from any domain @@ -10,6 +11,15 @@ async function bootstrap() { bufferLogs: true, }); + const config = new DocumentBuilder() + .addBearerAuth() + .setTitle('Birthday Database API') + .setDescription('Birthday Database API description') + .setVersion('1.0') + .build(); + const document = SwaggerModule.createDocument(app, config); + SwaggerModule.setup('api', app, document); + app.useLogger(app.get(Logger)); // [whitelist: true] = remove all the properties that are not defined in the DTO diff --git a/src/user/dto/change-password.dto.ts b/src/user/dto/change-password.dto.ts index f09d4f0..874210c 100644 --- a/src/user/dto/change-password.dto.ts +++ b/src/user/dto/change-password.dto.ts @@ -1,9 +1,12 @@ import { IsString, MinLength } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class ChangePasswordDto { + @ApiProperty({ example: 'password', description: 'current password' }) @IsString() currentPassword: string; + @ApiProperty({ example: 'newPassword', description: 'new password' }) @IsString() @MinLength(6) newPassword: string; diff --git a/src/user/dto/edit-user.dto.ts b/src/user/dto/edit-user.dto.ts index 6ef3013..db2c131 100644 --- a/src/user/dto/edit-user.dto.ts +++ b/src/user/dto/edit-user.dto.ts @@ -1,11 +1,13 @@ import { IsOptional, IsString, IsISO8601, IsNotEmpty } from 'class-validator'; +import { ApiProperty } from '@nestjs/swagger'; export class EditUserDto { - @IsString() + @ApiProperty({ example: 'john doe', description: 'full name' }) @IsOptional() @IsNotEmpty() fullName?: string; + @ApiProperty({ example: '2000-01-01', description: 'birthday' }) @IsISO8601() @IsOptional() birthday?: string; diff --git a/src/user/user.controller.ts b/src/user/user.controller.ts index de3d736..d788250 100644 --- a/src/user/user.controller.ts +++ b/src/user/user.controller.ts @@ -15,8 +15,10 @@ import { GetUser } from '../auth/decorator'; import { ChangePasswordDto, EditUserDto } from './dto'; import { UserService } from './user.service'; import { User } from '@prisma/client'; +import { ApiTags } from '@nestjs/swagger'; @UseGuards(AccessTokenGuard) +@ApiTags('users') @Controller('users') export class UserController { constructor(private userService: UserService) {} @@ -37,27 +39,26 @@ export class UserController { return this.userService.editUser(userId, dto); } - @Delete(':id') - @HttpCode(HttpStatus.NO_CONTENT) - async deleteUser( + @Patch(':id/change-password') + async changePassword( + @Body() changePasswordDto: ChangePasswordDto, @GetUser() user: User, @Param('id', ParseIntPipe) userId: number, ) { this.checkIfUserIsAuthorized(user, userId); - await this.userService.deleteUser(userId); - return; + return this.userService.changePassword(userId, changePasswordDto); } - @Patch(':id/change-password') - async changePassword( - @Body() changePasswordDto: ChangePasswordDto, + @Delete(':id') + @HttpCode(HttpStatus.NO_CONTENT) + async deleteUser( @GetUser() user: User, @Param('id', ParseIntPipe) userId: number, ) { this.checkIfUserIsAuthorized(user, userId); - return this.userService.changePassword(userId, changePasswordDto); + await this.userService.deleteUser(userId); + return; } - /** * Check if user is authorized to perform action * @param user user from access token