From 89aac9d4b6945f98b944e2e58b022ff47c4a61fb Mon Sep 17 00:00:00 2001 From: Philip Molares Date: Mon, 7 Feb 2022 01:00:32 +0100 Subject: [PATCH] refactor: use new openapi decorator Also remove fullapi decorator, because it's fully replaced by the openapi decorator. Signed-off-by: Philip Molares --- src/api/private/alias/alias.controller.ts | 33 +--- src/api/private/auth/auth.controller.ts | 23 +-- src/api/private/config/config.controller.ts | 4 +- .../private/me/history/history.controller.ts | 27 +-- src/api/private/me/me.controller.ts | 40 +--- src/api/private/media/media.controller.ts | 58 ++---- src/api/private/notes/notes.controller.ts | 59 ++---- src/api/private/tokens/tokens.controller.ts | 23 +-- src/api/public/alias/alias.controller.ts | 58 +++--- src/api/public/me/me.controller.ts | 76 ++++---- src/api/public/media/media.controller.ts | 51 ++--- .../monitoring/monitoring.controller.ts | 45 ++--- src/api/public/notes/notes.controller.ts | 176 +++++++++--------- src/api/utils/fullapi-decorator.ts | 20 -- 14 files changed, 248 insertions(+), 445 deletions(-) delete mode 100644 src/api/utils/fullapi-decorator.ts diff --git a/src/api/private/alias/alias.controller.ts b/src/api/private/alias/alias.controller.ts index 06876d736..dff7ca7a1 100644 --- a/src/api/private/alias/alias.controller.ts +++ b/src/api/private/alias/alias.controller.ts @@ -8,20 +8,13 @@ import { Body, Controller, Delete, - HttpCode, Param, Post, Put, UnauthorizedException, UseGuards, } from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiConflictResponse, - ApiNotFoundResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { SessionGuard } from '../../../identity/session.guard'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; @@ -33,15 +26,11 @@ import { NotesService } from '../../../notes/notes.service'; import { PermissionsService } from '../../../permissions/permissions.service'; import { User } from '../../../users/user.entity'; import { UsersService } from '../../../users/users.service'; -import { - badRequestDescription, - conflictDescription, - notFoundDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(SessionGuard) +@OpenApi(401) @ApiTags('alias') @Controller('alias') export class AliasController { @@ -56,10 +45,7 @@ export class AliasController { } @Post() - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiConflictResponse({ description: conflictDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(201, 400, 404, 409) async addAlias( @RequestUser() user: User, @Body() newAliasDto: AliasCreateDto, @@ -78,9 +64,7 @@ export class AliasController { } @Put(':alias') - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(200, 400, 404) async makeAliasPrimary( @RequestUser() user: User, @Param('alias') alias: string, @@ -100,12 +84,7 @@ export class AliasController { } @Delete(':alias') - @HttpCode(204) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) - @ApiBadRequestResponse({ - description: badRequestDescription, - }) + @OpenApi(204, 400, 404) async removeAlias( @RequestUser() user: User, @Param('alias') alias: string, diff --git a/src/api/private/auth/auth.controller.ts b/src/api/private/auth/auth.controller.ts index 26f152a4c..e6f564517 100644 --- a/src/api/private/auth/auth.controller.ts +++ b/src/api/private/auth/auth.controller.ts @@ -13,12 +13,7 @@ import { Req, UseGuards, } from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiConflictResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { Session } from 'express-session'; import { IdentityService } from '../../../identity/identity.service'; @@ -30,12 +25,8 @@ import { SessionGuard } from '../../../identity/session.guard'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { User } from '../../../users/user.entity'; import { UsersService } from '../../../users/users.service'; -import { - badRequestDescription, - conflictDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; import { LoginEnabledGuard } from '../../utils/login-enabled.guard'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RegistrationEnabledGuard } from '../../utils/registration-enabled.guard'; import { RequestUser } from '../../utils/request-user.decorator'; @@ -52,8 +43,7 @@ export class AuthController { @UseGuards(RegistrationEnabledGuard) @Post('local') - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiConflictResponse({ description: conflictDescription }) + @OpenApi(201, 400, 409) async registerUser(@Body() registerDto: RegisterDto): Promise { const user = await this.usersService.createUser( registerDto.username, @@ -65,8 +55,7 @@ export class AuthController { @UseGuards(LoginEnabledGuard, SessionGuard) @Put('local') - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200, 400, 401) async updatePassword( @RequestUser() user: User, @Body() changePasswordDto: UpdatePasswordDto, @@ -84,7 +73,7 @@ export class AuthController { @UseGuards(LoginEnabledGuard, LocalAuthGuard) @Post('local/login') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(201, 400, 401) login( @Req() request: Request & { session: { user: string } }, @Body() loginDto: LoginDto, @@ -95,7 +84,7 @@ export class AuthController { @UseGuards(SessionGuard) @Delete('logout') - @ApiBadRequestResponse({ description: badRequestDescription }) + @OpenApi(204, 400, 401) logout(@Req() request: Request & { session: Session }): void { request.session.destroy((err) => { if (err) { diff --git a/src/api/private/config/config.controller.ts b/src/api/private/config/config.controller.ts index 6ae4e4951..63bcede0b 100644 --- a/src/api/private/config/config.controller.ts +++ b/src/api/private/config/config.controller.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -9,6 +9,7 @@ import { ApiTags } from '@nestjs/swagger'; import { FrontendConfigDto } from '../../../frontend-config/frontend-config.dto'; import { FrontendConfigService } from '../../../frontend-config/frontend-config.service'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; +import { OpenApi } from '../../utils/openapi.decorator'; @ApiTags('config') @Controller('config') @@ -21,6 +22,7 @@ export class ConfigController { } @Get() + @OpenApi(200) async getFrontendConfig(): Promise { return await this.frontendConfigService.getFrontendConfig(); } diff --git a/src/api/private/me/history/history.controller.ts b/src/api/private/me/history/history.controller.ts index 22b92b582..e01a952ff 100644 --- a/src/api/private/me/history/history.controller.ts +++ b/src/api/private/me/history/history.controller.ts @@ -13,11 +13,7 @@ import { UseGuards, UseInterceptors, } from '@nestjs/common'; -import { - ApiNotFoundResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { HistoryEntryImportDto } from '../../../../history/history-entry-import.dto'; import { HistoryEntryUpdateDto } from '../../../../history/history-entry-update.dto'; @@ -27,15 +23,13 @@ import { SessionGuard } from '../../../../identity/session.guard'; import { ConsoleLoggerService } from '../../../../logger/console-logger.service'; import { Note } from '../../../../notes/note.entity'; import { User } from '../../../../users/user.entity'; -import { - notFoundDescription, - unauthorizedDescription, -} from '../../../utils/descriptions'; import { GetNoteInterceptor } from '../../../utils/get-note.interceptor'; +import { OpenApi } from '../../../utils/openapi.decorator'; import { RequestNote } from '../../../utils/request-note.decorator'; import { RequestUser } from '../../../utils/request-user.decorator'; @UseGuards(SessionGuard) +@OpenApi(401) @ApiTags('history') @Controller('/me/history') export class HistoryController { @@ -47,8 +41,7 @@ export class HistoryController { } @Get() - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(200, 404) async getHistory(@RequestUser() user: User): Promise { const foundEntries = await this.historyService.getEntriesByUser(user); return await Promise.all( @@ -57,8 +50,7 @@ export class HistoryController { } @Post() - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(201, 404) async setHistory( @RequestUser() user: User, @Body('history') history: HistoryEntryImportDto[], @@ -67,15 +59,13 @@ export class HistoryController { } @Delete() - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(204, 404) async deleteHistory(@RequestUser() user: User): Promise { await this.historyService.deleteHistory(user); } @Put(':noteIdOrAlias') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(200, 404) @UseInterceptors(GetNoteInterceptor) async updateHistoryEntry( @RequestNote() note: Note, @@ -91,8 +81,7 @@ export class HistoryController { } @Delete(':noteIdOrAlias') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(204, 404) @UseInterceptors(GetNoteInterceptor) async deleteHistoryEntry( @RequestNote() note: Note, diff --git a/src/api/private/me/me.controller.ts b/src/api/private/me/me.controller.ts index 950d90fef..eff71fb0b 100644 --- a/src/api/private/me/me.controller.ts +++ b/src/api/private/me/me.controller.ts @@ -1,23 +1,10 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ -import { - Body, - Controller, - Delete, - Get, - HttpCode, - Post, - UseGuards, -} from '@nestjs/common'; -import { - ApiInternalServerErrorResponse, - ApiNotFoundResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { Body, Controller, Delete, Get, Post, UseGuards } from '@nestjs/common'; +import { ApiTags } from '@nestjs/swagger'; import { SessionGuard } from '../../../identity/session.guard'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; @@ -26,14 +13,11 @@ import { MediaService } from '../../../media/media.service'; import { UserInfoDto } from '../../../users/user-info.dto'; import { User } from '../../../users/user.entity'; import { UsersService } from '../../../users/users.service'; -import { - internalServerErrorDescription, - notFoundDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(SessionGuard) +@OpenApi(401) @ApiTags('me') @Controller('me') export class MeController { @@ -45,13 +29,13 @@ export class MeController { this.logger.setContext(MeController.name); } @Get() - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200) getMe(@RequestUser() user: User): UserInfoDto { return this.userService.toUserDto(user); } @Get('media') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200) async getMyMedia(@RequestUser() user: User): Promise { const media = await this.mediaService.listUploadsByUser(user); return await Promise.all( @@ -60,12 +44,7 @@ export class MeController { } @Delete() - @HttpCode(204) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) + @OpenApi(204, 404, 500) async deleteUser(@RequestUser() user: User): Promise { const mediaUploads = await this.mediaService.listUploadsByUser(user); for (const mediaUpload of mediaUploads) { @@ -77,8 +56,7 @@ export class MeController { } @Post('profile') - @HttpCode(200) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200) async updateDisplayName( @RequestUser() user: User, @Body('name') newDisplayName: string, diff --git a/src/api/private/media/media.controller.ts b/src/api/private/media/media.controller.ts index f23e4df12..815aed235 100644 --- a/src/api/private/media/media.controller.ts +++ b/src/api/private/media/media.controller.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,6 @@ import { Controller, Delete, Headers, - HttpCode, Param, Post, UploadedFile, @@ -15,19 +14,7 @@ import { UseInterceptors, } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; -import { - ApiBadRequestResponse, - ApiBody, - ApiConsumes, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiHeader, - ApiInternalServerErrorResponse, - ApiNoContentResponse, - ApiNotFoundResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiBody, ApiConsumes, ApiHeader, ApiTags } from '@nestjs/swagger'; import { PermissionError } from '../../../errors/errors'; import { SessionGuard } from '../../../identity/session.guard'; @@ -38,18 +25,11 @@ import { MulterFile } from '../../../media/multer-file.interface'; import { Note } from '../../../notes/note.entity'; import { NotesService } from '../../../notes/notes.service'; import { User } from '../../../users/user.entity'; -import { - badRequestDescription, - forbiddenDescription, - internalServerErrorDescription, - notFoundDescription, - successfullyDeletedDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; -import { FullApi } from '../../utils/fullapi-decorator'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(SessionGuard) +@OpenApi(401) @ApiTags('media') @Controller('media') export class MediaController { @@ -79,18 +59,17 @@ export class MediaController { description: 'ID or alias of the parent note', }) @UseInterceptors(FileInterceptor('file')) - @HttpCode(201) - @ApiCreatedResponse({ - description: 'The file was uploaded successfully', - type: MediaUploadUrlDto, - }) - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiForbiddenResponse({ description: forbiddenDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) + @OpenApi( + { + code: 201, + description: 'The file was uploaded successfully', + dto: MediaUploadUrlDto, + }, + 400, + 403, + 404, + 500, + ) async uploadMedia( @UploadedFile() file: MulterFile, @Headers('HedgeDoc-Note') noteId: string, @@ -107,12 +86,7 @@ export class MediaController { } @Delete(':filename') - @HttpCode(204) - @ApiNoContentResponse({ description: successfullyDeletedDescription }) - @FullApi - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) + @OpenApi(204, 403, 404, 500) async deleteMedia( @RequestUser() user: User, @Param('filename') filename: string, diff --git a/src/api/private/notes/notes.controller.ts b/src/api/private/notes/notes.controller.ts index be4b39293..83edcb494 100644 --- a/src/api/private/notes/notes.controller.ts +++ b/src/api/private/notes/notes.controller.ts @@ -8,20 +8,12 @@ import { Controller, Delete, Get, - HttpCode, Param, Post, UseGuards, UseInterceptors, } from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiConflictResponse, - ApiInternalServerErrorResponse, - ApiNotFoundResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { HistoryService } from '../../../history/history.service'; import { SessionGuard } from '../../../identity/session.guard'; @@ -38,21 +30,16 @@ import { RevisionDto } from '../../../revisions/revision.dto'; import { RevisionsService } from '../../../revisions/revisions.service'; import { User } from '../../../users/user.entity'; import { UsersService } from '../../../users/users.service'; -import { - badRequestDescription, - conflictDescription, - internalServerErrorDescription, - notFoundDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; import { GetNoteInterceptor } from '../../utils/get-note.interceptor'; import { MarkdownBody } from '../../utils/markdown-body.decorator'; +import { OpenApi } from '../../utils/openapi.decorator'; import { Permissions } from '../../utils/permissions.decorator'; import { PermissionsGuard } from '../../utils/permissions.guard'; import { RequestNote } from '../../utils/request-note.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; -@UseGuards(SessionGuard) +@UseGuards(SessionGuard, PermissionsGuard) +@OpenApi(401, 403) @ApiTags('notes') @Controller('notes') export class NotesController { @@ -68,10 +55,9 @@ export class NotesController { } @Get(':noteIdOrAlias') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200) @Permissions(Permission.READ) @UseInterceptors(GetNoteInterceptor) - @UseGuards(PermissionsGuard) async getNote( @RequestUser() user: User, @RequestNote() note: Note, @@ -81,10 +67,9 @@ export class NotesController { } @Get(':noteIdOrAlias/media') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200) @Permissions(Permission.READ) @UseInterceptors(GetNoteInterceptor) - @UseGuards(PermissionsGuard) async getNotesMedia(@RequestNote() note: Note): Promise { const media = await this.mediaService.listUploadsByNote(note); return await Promise.all( @@ -93,10 +78,8 @@ export class NotesController { } @Post() - @HttpCode(201) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(201) @Permissions(Permission.CREATE) - @UseGuards(PermissionsGuard) async createNote( @RequestUser() user: User, @MarkdownBody() text: string, @@ -108,13 +91,8 @@ export class NotesController { } @Post(':noteAlias') - @HttpCode(201) - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiConflictResponse({ description: conflictDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(201, 400, 404, 409) @Permissions(Permission.CREATE) - @UseGuards(PermissionsGuard) async createNamedNote( @RequestUser() user: User, @Param('noteAlias') noteAlias: string, @@ -127,15 +105,9 @@ export class NotesController { } @Delete(':noteIdOrAlias') - @HttpCode(204) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) + @OpenApi(204, 404, 500) @Permissions(Permission.OWNER) @UseInterceptors(GetNoteInterceptor) - @UseGuards(PermissionsGuard) async deleteNote( @RequestUser() user: User, @RequestNote() note: Note, @@ -156,11 +128,9 @@ export class NotesController { } @Get(':noteIdOrAlias/revisions') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(200, 404) @Permissions(Permission.READ) @UseInterceptors(GetNoteInterceptor) - @UseGuards(PermissionsGuard) async getNoteRevisions( @RequestUser() user: User, @RequestNote() note: Note, @@ -174,12 +144,9 @@ export class NotesController { } @Delete(':noteIdOrAlias/revisions') - @HttpCode(204) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(204, 404) @Permissions(Permission.READ) @UseInterceptors(GetNoteInterceptor) - @UseGuards(PermissionsGuard) async purgeNoteRevisions( @RequestUser() user: User, @RequestNote() note: Note, @@ -197,11 +164,9 @@ export class NotesController { } @Get(':noteIdOrAlias/revisions/:revisionId') - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(200, 404) @Permissions(Permission.READ) @UseInterceptors(GetNoteInterceptor) - @UseGuards(PermissionsGuard) async getNoteRevision( @RequestUser() user: User, @RequestNote() note: Note, diff --git a/src/api/private/tokens/tokens.controller.ts b/src/api/private/tokens/tokens.controller.ts index e64348991..87ab494f9 100644 --- a/src/api/private/tokens/tokens.controller.ts +++ b/src/api/private/tokens/tokens.controller.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,17 +8,12 @@ import { Controller, Delete, Get, - HttpCode, Param, Post, UnauthorizedException, UseGuards, } from '@nestjs/common'; -import { - ApiNotFoundResponse, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiTags } from '@nestjs/swagger'; import { AuthTokenWithSecretDto } from '../../../auth/auth-token-with-secret.dto'; import { AuthTokenDto } from '../../../auth/auth-token.dto'; @@ -27,13 +22,11 @@ import { SessionGuard } from '../../../identity/session.guard'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; import { User } from '../../../users/user.entity'; import { TimestampMillis } from '../../../utils/timestamp'; -import { - notFoundDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(SessionGuard) +@OpenApi(401) @ApiTags('tokens') @Controller('tokens') export class TokensController { @@ -45,7 +38,7 @@ export class TokensController { } @Get() - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(200) async getUserTokens(@RequestUser() user: User): Promise { return (await this.authService.getTokensByUser(user)).map((token) => this.authService.toAuthTokenDto(token), @@ -53,7 +46,7 @@ export class TokensController { } @Post() - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) + @OpenApi(201) async postTokenRequest( @Body('label') label: string, @Body('validUntil') validUntil: TimestampMillis, @@ -63,9 +56,7 @@ export class TokensController { } @Delete('/:keyId') - @HttpCode(204) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(204, 404) async deleteToken( @RequestUser() user: User, @Param('keyId') keyId: string, diff --git a/src/api/public/alias/alias.controller.ts b/src/api/public/alias/alias.controller.ts index 355540775..a16f5a1cc 100644 --- a/src/api/public/alias/alias.controller.ts +++ b/src/api/public/alias/alias.controller.ts @@ -8,20 +8,13 @@ import { Body, Controller, Delete, - HttpCode, Param, Post, Put, UnauthorizedException, UseGuards, } from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiNoContentResponse, - ApiOkResponse, - ApiSecurity, - ApiTags, -} from '@nestjs/swagger'; +import { ApiSecurity, ApiTags } from '@nestjs/swagger'; import { TokenAuthGuard } from '../../../auth/token.strategy'; import { ConsoleLoggerService } from '../../../logger/console-logger.service'; @@ -32,11 +25,11 @@ import { AliasService } from '../../../notes/alias.service'; import { NotesService } from '../../../notes/notes.service'; import { PermissionsService } from '../../../permissions/permissions.service'; import { User } from '../../../users/user.entity'; -import { badRequestDescription } from '../../utils/descriptions'; -import { FullApi } from '../../utils/fullapi-decorator'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(TokenAuthGuard) +@OpenApi(401) @ApiTags('alias') @ApiSecurity('token') @Controller('alias') @@ -51,11 +44,15 @@ export class AliasController { } @Post() - @ApiOkResponse({ - description: 'The new alias', - type: AliasDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'The new alias', + dto: AliasDto, + }, + 403, + 404, + ) async addAlias( @RequestUser() user: User, @Body() newAliasDto: AliasCreateDto, @@ -74,11 +71,15 @@ export class AliasController { } @Put(':alias') - @ApiOkResponse({ - description: 'The updated alias', - type: AliasDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'The updated alias', + dto: AliasDto, + }, + 403, + 404, + ) async makeAliasPrimary( @RequestUser() user: User, @Param('alias') alias: string, @@ -98,14 +99,15 @@ export class AliasController { } @Delete(':alias') - @HttpCode(204) - @ApiNoContentResponse({ - description: 'The alias was deleted', - }) - @FullApi - @ApiBadRequestResponse({ - description: badRequestDescription, - }) + @OpenApi( + { + code: 204, + description: 'The alias was deleted', + }, + 400, + 403, + 404, + ) async removeAlias( @RequestUser() user: User, @Param('alias') alias: string, diff --git a/src/api/public/me/me.controller.ts b/src/api/public/me/me.controller.ts index b2460fc8a..1b42167ef 100644 --- a/src/api/public/me/me.controller.ts +++ b/src/api/public/me/me.controller.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -8,19 +8,11 @@ import { Controller, Delete, Get, - HttpCode, Put, UseGuards, UseInterceptors, } from '@nestjs/common'; -import { - ApiNoContentResponse, - ApiNotFoundResponse, - ApiOkResponse, - ApiSecurity, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiSecurity, ApiTags } from '@nestjs/swagger'; import { TokenAuthGuard } from '../../../auth/token.strategy'; import { HistoryEntryUpdateDto } from '../../../history/history-entry-update.dto'; @@ -35,16 +27,13 @@ import { NotesService } from '../../../notes/notes.service'; import { UserInfoDto } from '../../../users/user-info.dto'; import { User } from '../../../users/user.entity'; import { UsersService } from '../../../users/users.service'; -import { - notFoundDescription, - successfullyDeletedDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; import { GetNoteInterceptor } from '../../utils/get-note.interceptor'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestNote } from '../../utils/request-note.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(TokenAuthGuard) +@OpenApi(401) @ApiTags('me') @ApiSecurity('token') @Controller('me') @@ -60,22 +49,22 @@ export class MeController { } @Get() - @ApiOkResponse({ + @OpenApi({ + code: 200, description: 'The user information', - type: UserInfoDto, + dto: UserInfoDto, }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) getMe(@RequestUser() user: User): UserInfoDto { return this.usersService.toUserDto(user); } @Get('history') - @ApiOkResponse({ + @OpenApi({ + code: 200, description: 'The history entries of the user', isArray: true, - type: HistoryEntryDto, + dto: HistoryEntryDto, }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) async getUserHistory(@RequestUser() user: User): Promise { const foundEntries = await this.historyService.getEntriesByUser(user); return await Promise.all( @@ -85,12 +74,14 @@ export class MeController { @UseInterceptors(GetNoteInterceptor) @Get('history/:noteIdOrAlias') - @ApiOkResponse({ - description: 'The history entry of the user which points to the note', - type: HistoryEntryDto, - }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi( + { + code: 200, + description: 'The history entry of the user which points to the note', + dto: HistoryEntryDto, + }, + 404, + ) async getHistoryEntry( @RequestUser() user: User, @RequestNote() note: Note, @@ -101,12 +92,14 @@ export class MeController { @UseInterceptors(GetNoteInterceptor) @Put('history/:noteIdOrAlias') - @ApiOkResponse({ - description: 'The updated history entry', - type: HistoryEntryDto, - }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi( + { + code: 200, + description: 'The updated history entry', + dto: HistoryEntryDto, + }, + 404, + ) async updateHistoryEntry( @RequestUser() user: User, @RequestNote() note: Note, @@ -120,10 +113,7 @@ export class MeController { @UseInterceptors(GetNoteInterceptor) @Delete('history/:noteIdOrAlias') - @HttpCode(204) - @ApiNoContentResponse({ description: successfullyDeletedDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) + @OpenApi(204, 404) async deleteHistoryEntry( @RequestUser() user: User, @RequestNote() note: Note, @@ -133,12 +123,12 @@ export class MeController { } @Get('notes') - @ApiOkResponse({ + @OpenApi({ + code: 200, description: 'Metadata of all notes of the user', isArray: true, - type: NoteMetadataDto, + dto: NoteMetadataDto, }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) async getMyNotes(@RequestUser() user: User): Promise { const notes = this.notesService.getUserNotes(user); return await Promise.all( @@ -147,12 +137,12 @@ export class MeController { } @Get('media') - @ApiOkResponse({ + @OpenApi({ + code: 200, description: 'All media uploads of the user', isArray: true, - type: MediaUploadDto, + dto: MediaUploadDto, }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) async getMyMedia(@RequestUser() user: User): Promise { const media = await this.mediaService.listUploadsByUser(user); return await Promise.all( diff --git a/src/api/public/media/media.controller.ts b/src/api/public/media/media.controller.ts index 445a35e32..01e0e6cc0 100644 --- a/src/api/public/media/media.controller.ts +++ b/src/api/public/media/media.controller.ts @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ @@ -7,7 +7,6 @@ import { Controller, Delete, Headers, - HttpCode, Param, Post, UploadedFile, @@ -16,18 +15,11 @@ import { } from '@nestjs/common'; import { FileInterceptor } from '@nestjs/platform-express'; import { - ApiBadRequestResponse, ApiBody, ApiConsumes, - ApiCreatedResponse, - ApiForbiddenResponse, ApiHeader, - ApiInternalServerErrorResponse, - ApiNoContentResponse, - ApiNotFoundResponse, ApiSecurity, ApiTags, - ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { TokenAuthGuard } from '../../../auth/token.strategy'; @@ -39,18 +31,11 @@ import { MulterFile } from '../../../media/multer-file.interface'; import { Note } from '../../../notes/note.entity'; import { NotesService } from '../../../notes/notes.service'; import { User } from '../../../users/user.entity'; -import { - badRequestDescription, - forbiddenDescription, - internalServerErrorDescription, - notFoundDescription, - successfullyDeletedDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; -import { FullApi } from '../../utils/fullapi-decorator'; +import { OpenApi } from '../../utils/openapi.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; @UseGuards(TokenAuthGuard) +@OpenApi(401) @ApiTags('media') @ApiSecurity('token') @Controller('media') @@ -80,19 +65,18 @@ export class MediaController { name: 'HedgeDoc-Note', description: 'ID or alias of the parent note', }) - @ApiCreatedResponse({ - description: 'The file was uploaded successfully', - type: MediaUploadUrlDto, - }) - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiForbiddenResponse({ description: forbiddenDescription }) - @ApiNotFoundResponse({ description: notFoundDescription }) - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) + @OpenApi( + { + code: 201, + description: 'The file was uploaded successfully', + dto: MediaUploadUrlDto, + }, + 400, + 403, + 404, + 500, + ) @UseInterceptors(FileInterceptor('file')) - @HttpCode(201) async uploadMedia( @RequestUser() user: User, @UploadedFile() file: MulterFile, @@ -109,12 +93,7 @@ export class MediaController { } @Delete(':filename') - @HttpCode(204) - @ApiNoContentResponse({ description: successfullyDeletedDescription }) - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) - @FullApi + @OpenApi(204, 403, 404, 500) async deleteMedia( @RequestUser() user: User, @Param('filename') filename: string, diff --git a/src/api/public/monitoring/monitoring.controller.ts b/src/api/public/monitoring/monitoring.controller.ts index 8c5e4caa0..224932bad 100644 --- a/src/api/public/monitoring/monitoring.controller.ts +++ b/src/api/public/monitoring/monitoring.controller.ts @@ -1,27 +1,18 @@ /* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) + * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ import { Controller, Get, UseGuards } from '@nestjs/common'; -import { - ApiForbiddenResponse, - ApiOkResponse, - ApiProduces, - ApiSecurity, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiSecurity, ApiTags } from '@nestjs/swagger'; import { TokenAuthGuard } from '../../../auth/token.strategy'; import { MonitoringService } from '../../../monitoring/monitoring.service'; import { ServerStatusDto } from '../../../monitoring/server-status.dto'; -import { - forbiddenDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; +import { OpenApi } from '../../utils/openapi.decorator'; @UseGuards(TokenAuthGuard) +@OpenApi(401) @ApiTags('monitoring') @ApiSecurity('token') @Controller('monitoring') @@ -29,24 +20,28 @@ export class MonitoringController { constructor(private monitoringService: MonitoringService) {} @Get() - @ApiOkResponse({ - description: 'The server info', - type: ServerStatusDto, - }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiForbiddenResponse({ description: forbiddenDescription }) + @OpenApi( + { + code: 200, + description: 'The server info', + dto: ServerStatusDto, + }, + 403, + ) getStatus(): Promise { // TODO: toServerStatusDto. return this.monitoringService.getServerStatus(); } @Get('prometheus') - @ApiOkResponse({ - description: 'Prometheus compatible monitoring data', - }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiForbiddenResponse({ description: forbiddenDescription }) - @ApiProduces('text/plain') + @OpenApi( + { + code: 200, + description: 'Prometheus compatible monitoring data', + mimeType: 'text/plain', + }, + 403, + ) getPrometheusStatus(): string { return ''; } diff --git a/src/api/public/notes/notes.controller.ts b/src/api/public/notes/notes.controller.ts index a2fa1f772..68508ca4a 100644 --- a/src/api/public/notes/notes.controller.ts +++ b/src/api/public/notes/notes.controller.ts @@ -8,27 +8,13 @@ import { Controller, Delete, Get, - Header, - HttpCode, Param, Post, Put, UseGuards, UseInterceptors, } from '@nestjs/common'; -import { - ApiBadRequestResponse, - ApiConflictResponse, - ApiCreatedResponse, - ApiForbiddenResponse, - ApiInternalServerErrorResponse, - ApiNoContentResponse, - ApiOkResponse, - ApiProduces, - ApiSecurity, - ApiTags, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; +import { ApiSecurity, ApiTags } from '@nestjs/swagger'; import { TokenAuthGuard } from '../../../auth/token.strategy'; import { HistoryService } from '../../../history/history.service'; @@ -49,23 +35,16 @@ import { RevisionMetadataDto } from '../../../revisions/revision-metadata.dto'; import { RevisionDto } from '../../../revisions/revision.dto'; import { RevisionsService } from '../../../revisions/revisions.service'; import { User } from '../../../users/user.entity'; -import { - badRequestDescription, - conflictDescription, - forbiddenDescription, - internalServerErrorDescription, - successfullyDeletedDescription, - unauthorizedDescription, -} from '../../utils/descriptions'; -import { FullApi } from '../../utils/fullapi-decorator'; import { GetNoteInterceptor } from '../../utils/get-note.interceptor'; import { MarkdownBody } from '../../utils/markdown-body.decorator'; +import { OpenApi } from '../../utils/openapi.decorator'; import { Permissions } from '../../utils/permissions.decorator'; import { PermissionsGuard } from '../../utils/permissions.guard'; import { RequestNote } from '../../utils/request-note.decorator'; import { RequestUser } from '../../utils/request-user.decorator'; -@UseGuards(TokenAuthGuard) +@UseGuards(TokenAuthGuard, PermissionsGuard) +@OpenApi(401) @ApiTags('notes') @ApiSecurity('token') @Controller('notes') @@ -81,11 +60,8 @@ export class NotesController { } @Permissions(Permission.CREATE) - @UseGuards(TokenAuthGuard, PermissionsGuard) @Post() - @HttpCode(201) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiForbiddenResponse({ description: forbiddenDescription }) + @OpenApi(201, 403, 409) async createNote( @RequestUser() user: User, @MarkdownBody() text: string, @@ -98,13 +74,16 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.READ) - @UseGuards(PermissionsGuard) @Get(':noteIdOrAlias') - @ApiOkResponse({ - description: 'Get information about the newly created note', - type: NoteDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'Get information about the newly created note', + dto: NoteDto, + }, + 403, + 404, + ) async getNote( @RequestUser() user: User, @RequestNote() note: Note, @@ -116,15 +95,16 @@ export class NotesController { @Permissions(Permission.CREATE) @UseGuards(PermissionsGuard) @Post(':noteAlias') - @HttpCode(201) - @ApiCreatedResponse({ - description: 'Get information about the newly created note', - type: NoteDto, - }) - @ApiBadRequestResponse({ description: badRequestDescription }) - @ApiConflictResponse({ description: conflictDescription }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) - @ApiForbiddenResponse({ description: forbiddenDescription }) + @OpenApi( + { + code: 201, + description: 'Get information about the newly created note', + dto: NoteDto, + }, + 400, + 403, + 409, + ) async createNamedNote( @RequestUser() user: User, @Param('noteAlias') noteAlias: string, @@ -138,14 +118,8 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.OWNER) - @UseGuards(PermissionsGuard) @Delete(':noteIdOrAlias') - @HttpCode(204) - @ApiNoContentResponse({ description: successfullyDeletedDescription }) - @FullApi - @ApiInternalServerErrorResponse({ - description: internalServerErrorDescription, - }) + @OpenApi(204, 403, 404, 500) async deleteNote( @RequestUser() user: User, @RequestNote() note: Note, @@ -167,13 +141,16 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.WRITE) - @UseGuards(PermissionsGuard) @Put(':noteIdOrAlias') - @ApiOkResponse({ - description: 'The new, changed note', - type: NoteDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'The new, changed note', + dto: NoteDto, + }, + 403, + 404, + ) async updateNote( @RequestUser() user: User, @RequestNote() note: Note, @@ -187,14 +164,16 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.READ) - @UseGuards(PermissionsGuard) @Get(':noteIdOrAlias/content') - @ApiProduces('text/markdown') - @ApiOkResponse({ - description: 'The raw markdown content of the note', - }) - @FullApi - @Header('content-type', 'text/markdown') + @OpenApi( + { + code: 200, + description: 'The raw markdown content of the note', + mimeType: 'text/markdown', + }, + 403, + 404, + ) async getNoteContent( @RequestUser() user: User, @RequestNote() note: Note, @@ -204,13 +183,16 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.READ) - @UseGuards(PermissionsGuard) @Get(':noteIdOrAlias/metadata') - @ApiOkResponse({ - description: 'The metadata of the note', - type: NoteMetadataDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'The metadata of the note', + dto: NoteMetadataDto, + }, + 403, + 404, + ) async getNoteMetadata( @RequestUser() user: User, @RequestNote() note: Note, @@ -220,13 +202,16 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.OWNER) - @UseGuards(PermissionsGuard) @Put(':noteIdOrAlias/metadata/permissions') - @ApiOkResponse({ - description: 'The updated permissions of the note', - type: NotePermissionsDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'The updated permissions of the note', + dto: NotePermissionsDto, + }, + 403, + 404, + ) async updateNotePermissions( @RequestUser() user: User, @RequestNote() note: Note, @@ -239,14 +224,17 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.READ) - @UseGuards(PermissionsGuard) @Get(':noteIdOrAlias/revisions') - @ApiOkResponse({ - description: 'Revisions of the note', - isArray: true, - type: RevisionMetadataDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'Revisions of the note', + isArray: true, + dto: RevisionMetadataDto, + }, + 403, + 404, + ) async getNoteRevisions( @RequestUser() user: User, @RequestNote() note: Note, @@ -261,13 +249,16 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.READ) - @UseGuards(PermissionsGuard) @Get(':noteIdOrAlias/revisions/:revisionId') - @ApiOkResponse({ - description: 'Revision of the note for the given id or alias', - type: RevisionDto, - }) - @FullApi + @OpenApi( + { + code: 200, + description: 'Revision of the note for the given id or alias', + dto: RevisionDto, + }, + 403, + 404, + ) async getNoteRevision( @RequestUser() user: User, @RequestNote() note: Note, @@ -280,14 +271,13 @@ export class NotesController { @UseInterceptors(GetNoteInterceptor) @Permissions(Permission.READ) - @UseGuards(PermissionsGuard) @Get(':noteIdOrAlias/media') - @ApiOkResponse({ + @OpenApi({ + code: 200, description: 'All media uploads of the note', isArray: true, - type: MediaUploadDto, + dto: MediaUploadDto, }) - @ApiUnauthorizedResponse({ description: unauthorizedDescription }) async getNotesMedia( @RequestUser() user: User, @RequestNote() note: Note, diff --git a/src/api/utils/fullapi-decorator.ts b/src/api/utils/fullapi-decorator.ts deleted file mode 100644 index 39b776754..000000000 --- a/src/api/utils/fullapi-decorator.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { applyDecorators } from '@nestjs/common'; -import { - ApiForbiddenResponse, - ApiNotFoundResponse, - ApiUnauthorizedResponse, -} from '@nestjs/swagger'; - -import { forbiddenDescription, notFoundDescription } from './descriptions'; - -// eslint-disable-next-line @typescript-eslint/naming-convention -export const FullApi = applyDecorators( - ApiForbiddenResponse({ description: forbiddenDescription }), - ApiNotFoundResponse({ description: notFoundDescription }), - ApiUnauthorizedResponse({ description: forbiddenDescription }), -);