From 8dfbcea0dad7f6db9d061f520a95100783f546a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B9=AF=E6=9C=AC=20=E9=96=8B?= Date: Thu, 25 Jan 2024 04:00:54 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20702:=20API=20IF=E5=AE=9F=E8=A3=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task3520: API IF実装](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3520) - ユーザー削除APIのI/Fを実装 ## レビューポイント - バリデーターは適切に設定されているか - 不要な処理が混入していないか - 代行操作による実行を許可しているが、認識は間違っていないか - マージ先ブランチは間違っていないか ## 動作確認状況 - openapi.jsonの生成成功を確認 --- dictation_server/src/api/odms/openapi.json | 62 +++++++++++++++ .../src/features/users/types/types.ts | 9 +++ .../src/features/users/users.controller.ts | 75 +++++++++++++++++++ 3 files changed, 146 insertions(+) diff --git a/dictation_server/src/api/odms/openapi.json b/dictation_server/src/api/odms/openapi.json index 9b20a43..4c8b812 100644 --- a/dictation_server/src/api/odms/openapi.json +++ b/dictation_server/src/api/odms/openapi.json @@ -2050,6 +2050,60 @@ "security": [{ "bearer": [] }] } }, + "/users/delete": { + "post": { + "operationId": "updeateUser", + "summary": "", + "description": "ユーザーを削除します", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/PostDeleteUserRequest" } + } + } + }, + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/PostDeleteUserResponse" + } + } + } + }, + "400": { + "description": "不正なパラメータ", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + }, + "401": { + "description": "認証エラー", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + } + }, + "tags": ["users"], + "security": [{ "bearer": [] }] + } + }, "/files/audio/upload-finished": { "post": { "operationId": "uploadFinished", @@ -4363,6 +4417,14 @@ }, "required": ["userName"] }, + "PostDeleteUserRequest": { + "type": "object", + "properties": { + "userId": { "type": "number", "description": "削除対象のユーザーID" } + }, + "required": ["userId"] + }, + "PostDeleteUserResponse": { "type": "object", "properties": {} }, "AudioOptionItem": { "type": "object", "properties": { diff --git a/dictation_server/src/features/users/types/types.ts b/dictation_server/src/features/users/types/types.ts index 80b1a13..2e957a6 100644 --- a/dictation_server/src/features/users/types/types.ts +++ b/dictation_server/src/features/users/types/types.ts @@ -252,6 +252,15 @@ export class PostUpdateUserRequest { export class PostUpdateUserResponse {} +export class PostDeleteUserRequest { + @ApiProperty({ description: '削除対象のユーザーID' }) + @Type(() => Number) + @IsInt() + userId: number; +} + +export class PostDeleteUserResponse {} + export class AllocateLicenseRequest { @ApiProperty({ description: 'ユーザーID' }) @Type(() => Number) diff --git a/dictation_server/src/features/users/users.controller.ts b/dictation_server/src/features/users/users.controller.ts index d607e50..6a1e74e 100644 --- a/dictation_server/src/features/users/users.controller.ts +++ b/dictation_server/src/features/users/users.controller.ts @@ -42,6 +42,8 @@ import { UpdateAcceptedVersionRequest, UpdateAcceptedVersionResponse, GetMyUserResponse, + PostDeleteUserRequest, + PostDeleteUserResponse, } from './types/types'; import { UsersService } from './users.service'; import { AuthService } from '../auth/auth.service'; @@ -916,4 +918,77 @@ export class UsersController { const userName = await this.usersService.getUserName(context, userId); return { userName }; } + + @ApiResponse({ + status: HttpStatus.OK, + type: PostDeleteUserResponse, + description: '成功時のレスポンス', + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: '不正なパラメータ', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: '認証エラー', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: '想定外のサーバーエラー', + type: ErrorResponse, + }) + @ApiOperation({ + operationId: 'updeateUser', + description: 'ユーザーを削除します', + }) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @UseGuards( + RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }), + ) + @Post('delete') + async deleteUser( + @Body() body: PostDeleteUserRequest, + @Req() req: Request, + ): Promise { + const accessToken = retrieveAuthorizationToken(req); + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + + const ip = retrieveIp(req); + if (!ip) { + throw new HttpException( + makeErrorResponse('E000401'), + HttpStatus.UNAUTHORIZED, + ); + } + + const requestId = retrieveRequestId(req); + if (!requestId) { + throw new HttpException( + makeErrorResponse('E000501'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + + const decodedAccessToken = jwt.decode(accessToken, { json: true }); + if (!decodedAccessToken) { + throw new HttpException( + makeErrorResponse('E000101'), + HttpStatus.UNAUTHORIZED, + ); + } + const { userId } = decodedAccessToken as AccessToken; + + const context = makeContext(userId, requestId); + this.logger.log(`[${context.getTrackingId()}] ip : ${ip}`); + + return {}; + } }