From 302f30247330a7990d2d4444f92fc9c5c23ffee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B0=B4=E6=9C=AC=20=E7=A5=90=E5=B8=8C?= Date: Tue, 5 Dec 2023 07:39:13 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=20597:=20=E3=83=A9=E3=82=A4?= =?UTF-8?q?=E3=82=BB=E3=83=B3=E3=82=B9=E7=A2=BA=E8=AA=8D=E7=94=BB=E9=9D=A2?= =?UTF-8?q?=EF=BC=88=E7=AC=AC=E4=BA=94=E9=9A=8E=E5=B1=A4=EF=BC=89=E3=81=AB?= =?UTF-8?q?=E4=BC=9A=E7=A4=BE=E5=90=8D=E3=82=92=E8=A1=A8=E7=A4=BA=E3=81=99?= =?UTF-8?q?=E3=82=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task2762: ライセンス確認画面(第五階層)に会社名を表示する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2762) - 何をどう変更したか、追加したライブラリなど ライセンス確認画面(第5階層)の会社名を取得するAPIを実装 - このPull Requestでの対象/対象外 画面側の実装は別ブランチで対応するためここでは対象外 ## レビューポイント 特になし ## 動作確認状況 - ポストマン、ユニットテスト --- dictation_server/src/api/odms/openapi.json | 56 ++++++++++++++++++ .../features/accounts/accounts.controller.ts | 54 ++++++++++++++++++ .../accounts/accounts.service.spec.ts | 57 +++++++++++++++++++ .../src/features/accounts/accounts.service.ts | 48 ++++++++++++++++ .../src/features/accounts/types/types.ts | 10 ++++ 5 files changed, 225 insertions(+) diff --git a/dictation_server/src/api/odms/openapi.json b/dictation_server/src/api/odms/openapi.json index e112ae2..7667921 100644 --- a/dictation_server/src/api/odms/openapi.json +++ b/dictation_server/src/api/odms/openapi.json @@ -1455,6 +1455,52 @@ "tags": ["accounts"] } }, + "/accounts/company-name": { + "post": { + "operationId": "getCompanyName", + "summary": "", + "description": "指定したアカウントの会社名を取得します", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/GetCompanyNameRequest" } + } + } + }, + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetCompanyNameResponse" + } + } + } + }, + "401": { + "description": "認証エラー", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/ErrorResponse" } + } + } + } + }, + "tags": ["accounts"], + "security": [{ "bearer": [] }] + } + }, "/users/confirm": { "post": { "operationId": "confirmUser", @@ -4009,6 +4055,16 @@ "properties": { "tier": { "type": "number", "description": "階層" } }, "required": ["tier"] }, + "GetCompanyNameRequest": { + "type": "object", + "properties": { "accountId": { "type": "number" } }, + "required": ["accountId"] + }, + "GetCompanyNameResponse": { + "type": "object", + "properties": { "companyName": { "type": "string" } }, + "required": ["companyName"] + }, "ConfirmRequest": { "type": "object", "properties": { "token": { "type": "string" } }, diff --git a/dictation_server/src/features/accounts/accounts.controller.ts b/dictation_server/src/features/accounts/accounts.controller.ts index 571f4ae..04c8e26 100644 --- a/dictation_server/src/features/accounts/accounts.controller.ts +++ b/dictation_server/src/features/accounts/accounts.controller.ts @@ -68,6 +68,8 @@ import { GetAccountInfoMinimalAccessResponse, DeleteWorktypeRequestParam, DeleteWorktypeResponse, + GetCompanyNameRequest, + GetCompanyNameResponse, } from './types/types'; import { USER_ROLES, ADMIN_ROLES, TIERS } from '../../constants'; import { AuthGuard } from '../../common/guards/auth/authguards'; @@ -1550,4 +1552,56 @@ export class AccountsController { ); return { tier }; } + + @ApiResponse({ + status: HttpStatus.OK, + type: GetCompanyNameResponse, + description: '成功時のレスポンス', + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: '認証エラー', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: '想定外のサーバーエラー', + type: ErrorResponse, + }) + @ApiOperation({ + operationId: 'getCompanyName', + description: '指定したアカウントの会社名を取得します', + }) + @ApiBearerAuth() + @UseGuards(AuthGuard) + @UseGuards( + RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN], delegation: true }), + ) + @Post('company-name') + async getCompanyName( + @Req() req: Request, + @Body() body: GetCompanyNameRequest, + ): Promise { + const accessToken = retrieveAuthorizationToken(req); + if (!accessToken) { + throw new HttpException( + makeErrorResponse('E000107'), + HttpStatus.UNAUTHORIZED, + ); + } + 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); + const companyName = await this.accountService.getCompanyName( + context, + body.accountId, + ); + return companyName; + } } diff --git a/dictation_server/src/features/accounts/accounts.service.spec.ts b/dictation_server/src/features/accounts/accounts.service.spec.ts index b0c2b47..2656aed 100644 --- a/dictation_server/src/features/accounts/accounts.service.spec.ts +++ b/dictation_server/src/features/accounts/accounts.service.spec.ts @@ -6694,3 +6694,60 @@ describe('getAccountInfoMinimalAccess', () => { } }); }); +describe('getCompanyName', () => { + let source: DataSource | null = null; + beforeEach(async () => { + source = new DataSource({ + type: 'sqlite', + database: ':memory:', + logging: false, + entities: [__dirname + '/../../**/*.entity{.ts,.js}'], + synchronize: true, // trueにすると自動的にmigrationが行われるため注意 + }); + return source.initialize(); + }); + + afterEach(async () => { + if (!source) return; + await source.destroy(); + source = null; + }); + + it('アカウントIDから会社名が取得できること', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(AccountsService); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 5, + company_name: 'testCompany', + }); + const context = makeContext(admin.external_id); + const response = await service.getCompanyName(context, account.id); + expect({ companyName: 'testCompany' }).toEqual(response); + }); + + it('アカウントが存在しない場合、400エラーとなること', async () => { + if (!source) fail(); + const module = await makeTestingModule(source); + if (!module) fail(); + const service = module.get(AccountsService); + // 第五階層のアカウント作成 + const { account, admin } = await makeTestAccount(source, { + tier: 5, + company_name: 'testCompany', + }); + const context = makeContext(admin.external_id); + try { + await service.getCompanyName(context, 123); + } catch (e) { + if (e instanceof HttpException) { + expect(e.getStatus()).toEqual(HttpStatus.BAD_REQUEST); + expect(e.getResponse()).toEqual(makeErrorResponse('E010501')); + } else { + fail(); + } + } + }); +}); diff --git a/dictation_server/src/features/accounts/accounts.service.ts b/dictation_server/src/features/accounts/accounts.service.ts index fda08e0..49f61f2 100644 --- a/dictation_server/src/features/accounts/accounts.service.ts +++ b/dictation_server/src/features/accounts/accounts.service.ts @@ -34,6 +34,7 @@ import { PostWorktypeOptionItem, Author, Partner, + GetCompanyNameResponse, } from './types/types'; import { DateWithZeroTime, @@ -2151,4 +2152,51 @@ export class AccountsService { ); } } + /** + * 自アカウントの会社名を取得する + * @param accountId + * @returns CompanyName + */ + async getCompanyName( + context: Context, + accountId: number, + ): Promise { + this.logger.log( + `[IN] [${context.getTrackingId()}] ${ + this.getCompanyName.name + } | params: { accountId: ${accountId}, };`, + ); + + try { + const { company_name } = await this.accountRepository.findAccountById( + accountId, + ); + + return { companyName: company_name }; + } catch (e) { + this.logger.error(`[${context.getTrackingId()}] error=${e}`); + if (e instanceof Error) { + switch (e.constructor) { + case AccountNotFoundError: + throw new HttpException( + makeErrorResponse('E010501'), + HttpStatus.BAD_REQUEST, + ); + default: + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } + } + throw new HttpException( + makeErrorResponse('E009999'), + HttpStatus.INTERNAL_SERVER_ERROR, + ); + } finally { + this.logger.log( + `[OUT] [${context.getTrackingId()}] ${this.getCompanyName.name}`, + ); + } + } } diff --git a/dictation_server/src/features/accounts/types/types.ts b/dictation_server/src/features/accounts/types/types.ts index 921bd10..8a555d6 100644 --- a/dictation_server/src/features/accounts/types/types.ts +++ b/dictation_server/src/features/accounts/types/types.ts @@ -599,3 +599,13 @@ export class GetAccountInfoMinimalAccessResponse { @ApiProperty({ description: '階層' }) tier: number; } +export class GetCompanyNameRequest { + @ApiProperty() + @IsInt() + @Type(() => Number) + accountId: number; +} +export class GetCompanyNameResponse { + @ApiProperty() + companyName: string; +}