Merged PR 6: タスク 1362: API実装(I/F)
## 概要 [Task: 1362](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/OMDSDictation/_sprints/taskboard/OMDSDictation%20%E3%83%81%E3%83%BC%E3%83%A0/OMDSDictation/%E3%82%B9%E3%83%97%E3%83%AA%E3%83%B3%E3%83%88%202_2?workitem=1362) - 以下のIFを実装 - AzureADのidトークンを受け取り、アクセストークン・リフレッシュトークンを返却するAPI - リフレッシュトークンを受け取り、アクセストークンを返却するAPI - エラー時のレスポンスを作成 ## レビューポイント - 各APIのリクエスト・レスポンスの型 ## UIの変更 - なし ## 動作確認状況 - SwaggerUIでAPIを確認 ## 補足
This commit is contained in:
parent
3b4b3c59e7
commit
c82d0363ac
2
dictation_client/codegen.sh
Normal file
2
dictation_client/codegen.sh
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
npx openapi-generator-cli version-manager set latest
|
||||||
|
npx openapi-generator-cli generate -g typescript-axios -i /app/dictation_server/src/api/odms/openapi.json -o /app/dictation_client/src/api/
|
||||||
@ -2,6 +2,6 @@
|
|||||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||||
"spaces": 2,
|
"spaces": 2,
|
||||||
"generator-cli": {
|
"generator-cli": {
|
||||||
"version": "6.2.1"
|
"version": "6.4.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,7 +1,11 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
const SamplePage: React.FC = () => {
|
const SamplePage: React.FC = () => (
|
||||||
return (<div>hello whorld!<br />Dictation App Service Site</div>)
|
<div>
|
||||||
};
|
hello whorld!
|
||||||
|
<br />
|
||||||
|
Dictation App Service Site
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
export default SamplePage;
|
export default SamplePage;
|
||||||
|
|||||||
@ -21,6 +21,12 @@ async function bootstrap(): Promise<void> {
|
|||||||
const options = new DocumentBuilder()
|
const options = new DocumentBuilder()
|
||||||
.setTitle('ODMSOpenAPI')
|
.setTitle('ODMSOpenAPI')
|
||||||
.setVersion('1.0.0')
|
.setVersion('1.0.0')
|
||||||
|
.addBearerAuth({
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
bearerFormat: 'JWT',
|
||||||
|
in: 'header',
|
||||||
|
})
|
||||||
.build();
|
.build();
|
||||||
const document = SwaggerModule.createDocument(app, options);
|
const document = SwaggerModule.createDocument(app, options);
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
|
|||||||
@ -12,10 +12,109 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/auth/token": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "token",
|
||||||
|
"summary": "",
|
||||||
|
"parameters": [],
|
||||||
|
"requestBody": {
|
||||||
|
"required": true,
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TokenRequest"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功時のレスポンス",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/TokenResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "認証エラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "想定外のサーバーエラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"auth"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/auth/accessToken": {
|
||||||
|
"post": {
|
||||||
|
"operationId": "accessToken",
|
||||||
|
"summary": "",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功時のレスポンス",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AccessTokenResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "認証エラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "想定外のサーバーエラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/ErrorResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": [
|
||||||
|
"auth"
|
||||||
|
],
|
||||||
|
"security": [
|
||||||
|
{
|
||||||
|
"bearer": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
"title": "OMDSOpenAPI",
|
"title": "ODMSOpenAPI",
|
||||||
"description": "",
|
"description": "",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"contact": {}
|
"contact": {}
|
||||||
@ -23,6 +122,72 @@
|
|||||||
"tags": [],
|
"tags": [],
|
||||||
"servers": [],
|
"servers": [],
|
||||||
"components": {
|
"components": {
|
||||||
"schemas": {}
|
"securitySchemes": {
|
||||||
|
"bearer": {
|
||||||
|
"scheme": "bearer",
|
||||||
|
"bearerFormat": "JWT",
|
||||||
|
"type": "http",
|
||||||
|
"in": "header"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"schemas": {
|
||||||
|
"TokenRequest": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"idToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "web or mobile or desktop"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"idToken",
|
||||||
|
"type"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"TokenResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"refreshToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"accessToken": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"refreshToken",
|
||||||
|
"accessToken"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"ErrorResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"message": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"message",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"AccessTokenResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"accessToken": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"accessToken"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,11 +1,11 @@
|
|||||||
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
import { MiddlewareConsumer, Module } from '@nestjs/common';
|
||||||
import { HealthController } from './health.controller';
|
import { HealthController } from './health.controller';
|
||||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
||||||
import { LoggerMiddleware } from './common/loggerMiddleware';
|
import { LoggerMiddleware } from './common/loggerMiddleware';
|
||||||
import { AuthModule } from './features/auth/auth.module';
|
import { AuthModule } from './features/auth/auth.module';
|
||||||
|
import { AuthController } from './features/auth/auth.controller';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -32,7 +32,7 @@ import { AuthModule } from './features/auth/auth.module';
|
|||||||
// inject: [ConfigService],
|
// inject: [ConfigService],
|
||||||
// }),
|
// }),
|
||||||
],
|
],
|
||||||
controllers: [HealthController],
|
controllers: [HealthController, AuthController],
|
||||||
providers: [],
|
providers: [],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
|
|||||||
4
dictation_server/src/common/error/code.ts
Normal file
4
dictation_server/src/common/error/code.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
//TODO 仮のエラーコード作成
|
||||||
|
export const ErrorCodes = [
|
||||||
|
'E009999', // 汎用エラー
|
||||||
|
] as const;
|
||||||
10
dictation_server/src/common/error/makeErrorResponse.ts
Normal file
10
dictation_server/src/common/error/makeErrorResponse.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { errors } from './message';
|
||||||
|
import { ErrorCodeType, ErrorResponse } from './types/types';
|
||||||
|
|
||||||
|
export const makeErrorResponse = (errorcode: ErrorCodeType): ErrorResponse => {
|
||||||
|
const msg = errors[errorcode];
|
||||||
|
return {
|
||||||
|
code: errorcode,
|
||||||
|
message: msg,
|
||||||
|
};
|
||||||
|
};
|
||||||
6
dictation_server/src/common/error/message.ts
Normal file
6
dictation_server/src/common/error/message.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { Errors } from './types/types';
|
||||||
|
|
||||||
|
// エラーコードとメッセージ対応表
|
||||||
|
export const errors: Errors = {
|
||||||
|
E009999: 'Internal Server Error',
|
||||||
|
};
|
||||||
15
dictation_server/src/common/error/types/types.ts
Normal file
15
dictation_server/src/common/error/types/types.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { ErrorCodes } from '../code';
|
||||||
|
|
||||||
|
export class ErrorResponse {
|
||||||
|
@ApiProperty()
|
||||||
|
message: string;
|
||||||
|
@ApiProperty()
|
||||||
|
code: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ErrorCodeType = (typeof ErrorCodes)[number];
|
||||||
|
|
||||||
|
export type Errors = {
|
||||||
|
[P in ErrorCodeType]: string;
|
||||||
|
};
|
||||||
@ -1,4 +1,66 @@
|
|||||||
import { Controller } from '@nestjs/common';
|
import { Body, Controller, Headers, HttpStatus, Post } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { ErrorResponse } from '../../common/error/types/types';
|
||||||
|
import {
|
||||||
|
AccessTokenResponse,
|
||||||
|
TokenRequest,
|
||||||
|
TokenResponse,
|
||||||
|
} from './types/types';
|
||||||
|
@ApiTags('auth')
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
export class AuthController {}
|
export class AuthController {
|
||||||
|
@Post('token')
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.OK,
|
||||||
|
type: TokenResponse,
|
||||||
|
description: '成功時のレスポンス',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.UNAUTHORIZED,
|
||||||
|
description: '認証エラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
description: '想定外のサーバーエラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiOperation({ operationId: 'token' })
|
||||||
|
async token(@Body() body: TokenRequest): Promise<TokenResponse> {
|
||||||
|
console.log(body);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: '',
|
||||||
|
refreshToken: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Post('accessToken')
|
||||||
|
@ApiBearerAuth()
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.OK,
|
||||||
|
type: AccessTokenResponse,
|
||||||
|
description: '成功時のレスポンス',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.UNAUTHORIZED,
|
||||||
|
description: '認証エラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
description: '想定外のサーバーエラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiOperation({ operationId: 'accessToken' })
|
||||||
|
async accessToken(@Headers() headers): Promise<AccessTokenResponse> {
|
||||||
|
console.log(headers['authorization']);
|
||||||
|
|
||||||
|
return { accessToken: '' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
19
dictation_server/src/features/auth/types/types.ts
Normal file
19
dictation_server/src/features/auth/types/types.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class TokenRequest {
|
||||||
|
@ApiProperty()
|
||||||
|
idToken: string;
|
||||||
|
@ApiProperty({ description: 'web or mobile or desktop' })
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
export class TokenResponse {
|
||||||
|
@ApiProperty()
|
||||||
|
refreshToken: string;
|
||||||
|
@ApiProperty()
|
||||||
|
accessToken: string;
|
||||||
|
}
|
||||||
|
export class AccessTokenResponse {
|
||||||
|
@ApiProperty()
|
||||||
|
accessToken: string;
|
||||||
|
}
|
||||||
|
export class AccessTokenRequest {}
|
||||||
@ -18,6 +18,12 @@ async function bootstrap() {
|
|||||||
const options = new DocumentBuilder()
|
const options = new DocumentBuilder()
|
||||||
.setTitle('ODMSOpenAPI')
|
.setTitle('ODMSOpenAPI')
|
||||||
.setVersion('1.0.0')
|
.setVersion('1.0.0')
|
||||||
|
.addBearerAuth({
|
||||||
|
type: 'http',
|
||||||
|
scheme: 'bearer',
|
||||||
|
bearerFormat: 'JWT',
|
||||||
|
in: 'header',
|
||||||
|
})
|
||||||
.build();
|
.build();
|
||||||
const document = SwaggerModule.createDocument(app, options);
|
const document = SwaggerModule.createDocument(app, options);
|
||||||
SwaggerModule.setup('api', app, document);
|
SwaggerModule.setup('api', app, document);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user