Merged PR 76: API実装(I/F)
## 概要 [Task1576: API実装(I/F)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1576) - sas発行APIのIFを実装 - アップロード用のSAS発行API - GET /blob/uploadSas - ダウンロード用のSAS発行API - GET /blob/downloadSas - notification関連のフォーマット修正 - notification関連でlintエラーが出ていた箇所を修正 - openapi.jsonを生成するコマンドを使用できるように修正 ## レビューポイント - レスポンスとして返却する内容に不足は無いか - URIはこれで良さそうか ## UIの変更 - Before/Afterのスクショなど - スクショ置き場 ## 動作確認状況 - ローカルで確認 ## 補足 - 相談、参考資料などがあれば
This commit is contained in:
parent
6a5926ab3f
commit
0a970e814f
@ -8,8 +8,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"prebuild": "rimraf dist",
|
"prebuild": "rimraf dist",
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"apigen": "ts-node src/api/generate.ts",
|
"apigen": "ts-node src/api/generate.ts && prettier --write \"src/api/odms/*.json\"",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"src/api/odms/*.json\"",
|
||||||
"start": "nest start",
|
"start": "nest start",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
|
|||||||
@ -1,24 +1,12 @@
|
|||||||
// XXX 現状うまく動作しないため運用できない
|
|
||||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||||
import { Test } from '@nestjs/testing';
|
|
||||||
import { AppModule } from '../app.module';
|
import { AppModule } from '../app.module';
|
||||||
import { promises as fs } from 'fs';
|
import { promises as fs } from 'fs';
|
||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
async function bootstrap(): Promise<void> {
|
async function bootstrap(): Promise<void> {
|
||||||
const controllers = Reflect.getMetadata('controllers', AppModule);
|
const app = await NestFactory.create(AppModule, {
|
||||||
const providers = Reflect.getMetadata('providers', AppModule);
|
preview: true,
|
||||||
const mockedProviders = providers.map((provider) => {
|
|
||||||
return {
|
|
||||||
provide: provider.name,
|
|
||||||
useValue: {},
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const testingModule = await Test.createTestingModule({
|
|
||||||
controllers: controllers,
|
|
||||||
providers: mockedProviders,
|
|
||||||
}).compile();
|
|
||||||
const app = testingModule.createNestApplication();
|
|
||||||
|
|
||||||
const options = new DocumentBuilder()
|
const options = new DocumentBuilder()
|
||||||
.setTitle('ODMSOpenAPI')
|
.setTitle('ODMSOpenAPI')
|
||||||
.setVersion('1.0.0')
|
.setVersion('1.0.0')
|
||||||
@ -32,7 +20,7 @@ async function bootstrap(): Promise<void> {
|
|||||||
const document = SwaggerModule.createDocument(app, options);
|
const document = SwaggerModule.createDocument(app, options);
|
||||||
await fs.writeFile(
|
await fs.writeFile(
|
||||||
'src/api/odms/openapi.json',
|
'src/api/odms/openapi.json',
|
||||||
JSON.stringify(document, null, 2),
|
JSON.stringify(document, null, 0),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@ -350,6 +350,88 @@
|
|||||||
"tags": ["notification"],
|
"tags": ["notification"],
|
||||||
"security": [{ "bearer": [] }]
|
"security": [{ "bearer": [] }]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"/files/audio/upload-location": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "uploadLocation",
|
||||||
|
"summary": "",
|
||||||
|
"parameters": [],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功時のレスポンス",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AudioUploadLocationResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "認証エラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "想定外のサーバーエラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": ["files"],
|
||||||
|
"security": [{ "bearer": [] }]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/files/audio/download-location": {
|
||||||
|
"get": {
|
||||||
|
"operationId": "downloadLocation",
|
||||||
|
"summary": "",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"required": true,
|
||||||
|
"in": "query",
|
||||||
|
"description": "音声ファイル情報をDBから取得するためのID",
|
||||||
|
"schema": { "type": "string" }
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "成功時のレスポンス",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/AudioDownloadLocationResponse"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"401": {
|
||||||
|
"description": "認証エラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"500": {
|
||||||
|
"description": "想定外のサーバーエラー",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tags": ["files"],
|
||||||
|
"security": [{ "bearer": [] }]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"info": {
|
"info": {
|
||||||
@ -508,7 +590,17 @@
|
|||||||
},
|
},
|
||||||
"required": ["pns", "handler"]
|
"required": ["pns", "handler"]
|
||||||
},
|
},
|
||||||
"RegisterResponse": { "type": "object", "properties": {} }
|
"RegisterResponse": { "type": "object", "properties": {} },
|
||||||
|
"AudioUploadLocationResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": { "url": { "type": "string" } },
|
||||||
|
"required": ["url"]
|
||||||
|
},
|
||||||
|
"AudioDownloadLocationResponse": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": { "url": { "type": "string" } },
|
||||||
|
"required": ["url"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,6 +22,7 @@ import { UsersRepositoryModule } from './repositories/users/users.repository.mod
|
|||||||
import { NotificationhubModule } from './gateways/notificationhub/notificationhub.module';
|
import { NotificationhubModule } from './gateways/notificationhub/notificationhub.module';
|
||||||
import { NotificationhubService } from './gateways/notificationhub/notificationhub.service';
|
import { NotificationhubService } from './gateways/notificationhub/notificationhub.service';
|
||||||
import { NotificationModule } from './features/notification/notification.module';
|
import { NotificationModule } from './features/notification/notification.module';
|
||||||
|
import { BlobModule } from './features/blob/blob.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@ -56,6 +57,7 @@ import { NotificationModule } from './features/notification/notification.module'
|
|||||||
}),
|
}),
|
||||||
NotificationModule,
|
NotificationModule,
|
||||||
NotificationhubModule,
|
NotificationhubModule,
|
||||||
|
BlobModule,
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
HealthController,
|
HealthController,
|
||||||
|
|||||||
23
dictation_server/src/features/blob/blob.controller.spec.ts
Normal file
23
dictation_server/src/features/blob/blob.controller.spec.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { BlobController } from './blob.controller';
|
||||||
|
import { BlobService } from './blob.service';
|
||||||
|
|
||||||
|
describe('BlobController', () => {
|
||||||
|
let controller: BlobController;
|
||||||
|
const mockBlobService = {};
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [BlobController],
|
||||||
|
providers: [BlobService],
|
||||||
|
})
|
||||||
|
.overrideProvider(BlobService)
|
||||||
|
.useValue(mockBlobService)
|
||||||
|
.compile();
|
||||||
|
|
||||||
|
controller = module.get<BlobController>(BlobController);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(controller).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
79
dictation_server/src/features/blob/blob.controller.ts
Normal file
79
dictation_server/src/features/blob/blob.controller.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { Controller, Get, Headers, HttpStatus, Query } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiResponse,
|
||||||
|
ApiOperation,
|
||||||
|
ApiBearerAuth,
|
||||||
|
ApiTags,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { ErrorResponse } from '../../common/error/types/types';
|
||||||
|
import { BlobService } from './blob.service';
|
||||||
|
import {
|
||||||
|
AudioUploadLocationResponse,
|
||||||
|
AudioUploadLocationRequest,
|
||||||
|
AudioDownloadLocationResponse,
|
||||||
|
AudioDownloadLocationRequest,
|
||||||
|
} from './types/types';
|
||||||
|
|
||||||
|
@ApiTags('files')
|
||||||
|
@Controller('files')
|
||||||
|
export class BlobController {
|
||||||
|
constructor(private readonly blobService: BlobService) {}
|
||||||
|
@Get('audio/upload-location')
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.OK,
|
||||||
|
type: AudioUploadLocationResponse,
|
||||||
|
description: '成功時のレスポンス',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.UNAUTHORIZED,
|
||||||
|
description: '認証エラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
description: '想定外のサーバーエラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiOperation({ operationId: 'uploadLocation' })
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async uploadLocation(
|
||||||
|
@Headers() headers,
|
||||||
|
@Query() query: AudioUploadLocationRequest,
|
||||||
|
): Promise<AudioUploadLocationResponse> {
|
||||||
|
const {} = query;
|
||||||
|
// コンテナ作成処理の前にアクセストークンの認証を行う
|
||||||
|
// アップロード先を決定する国情報はトークンから取得する想定
|
||||||
|
|
||||||
|
return { url: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('audio/download-location')
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.OK,
|
||||||
|
type: AudioDownloadLocationResponse,
|
||||||
|
description: '成功時のレスポンス',
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.UNAUTHORIZED,
|
||||||
|
description: '認証エラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiResponse({
|
||||||
|
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
description: '想定外のサーバーエラー',
|
||||||
|
type: ErrorResponse,
|
||||||
|
})
|
||||||
|
@ApiOperation({ operationId: 'downloadLocation' })
|
||||||
|
@ApiBearerAuth()
|
||||||
|
async downloadLocation(
|
||||||
|
@Headers() headers,
|
||||||
|
@Query() body: AudioDownloadLocationRequest,
|
||||||
|
): Promise<AudioDownloadLocationResponse> {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
const { id } = body;
|
||||||
|
// コンテナ作成処理の前にアクセストークンの認証を行う
|
||||||
|
//
|
||||||
|
|
||||||
|
return { url: '' };
|
||||||
|
}
|
||||||
|
}
|
||||||
9
dictation_server/src/features/blob/blob.module.ts
Normal file
9
dictation_server/src/features/blob/blob.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { BlobService } from './blob.service';
|
||||||
|
import { BlobController } from './blob.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [BlobService],
|
||||||
|
controllers: [BlobController],
|
||||||
|
})
|
||||||
|
export class BlobModule {}
|
||||||
18
dictation_server/src/features/blob/blob.service.spec.ts
Normal file
18
dictation_server/src/features/blob/blob.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { BlobService } from './blob.service';
|
||||||
|
|
||||||
|
describe('BlobService', () => {
|
||||||
|
let service: BlobService;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [BlobService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<BlobService>(BlobService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(service).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
4
dictation_server/src/features/blob/blob.service.ts
Normal file
4
dictation_server/src/features/blob/blob.service.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BlobService {}
|
||||||
18
dictation_server/src/features/blob/types/types.ts
Normal file
18
dictation_server/src/features/blob/types/types.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class AudioUploadLocationRequest {}
|
||||||
|
|
||||||
|
export class AudioUploadLocationResponse {
|
||||||
|
@ApiProperty()
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AudioDownloadLocationRequest {
|
||||||
|
@ApiProperty({ description: '音声ファイル情報をDBから取得するためのID' })
|
||||||
|
id: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AudioDownloadLocationResponse {
|
||||||
|
@ApiProperty()
|
||||||
|
url: string;
|
||||||
|
}
|
||||||
@ -4,8 +4,8 @@ import { NotificationController } from './notification.controller';
|
|||||||
import { NotificationhubModule } from '../../gateways/notificationhub/notificationhub.module';
|
import { NotificationhubModule } from '../../gateways/notificationhub/notificationhub.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports:[NotificationhubModule],
|
imports: [NotificationhubModule],
|
||||||
providers: [NotificationService],
|
providers: [NotificationService],
|
||||||
controllers: [NotificationController]
|
controllers: [NotificationController],
|
||||||
})
|
})
|
||||||
export class NotificationModule {}
|
export class NotificationModule {}
|
||||||
|
|||||||
@ -14,7 +14,7 @@ export class NotificationService {
|
|||||||
* @param pnsHandler
|
* @param pnsHandler
|
||||||
* @returns register
|
* @returns register
|
||||||
*/
|
*/
|
||||||
async register(pns: string, pnsHandler: string): Promise<{}> {
|
async register(pns: string, pnsHandler: string): Promise<void> {
|
||||||
this.logger.log(`[IN] ${this.register.name}`);
|
this.logger.log(`[IN] ${this.register.name}`);
|
||||||
try {
|
try {
|
||||||
await this.notificationhubService.register(pns, pnsHandler);
|
await this.notificationhubService.register(pns, pnsHandler);
|
||||||
@ -27,6 +27,5 @@ export class NotificationService {
|
|||||||
} finally {
|
} finally {
|
||||||
this.logger.log(`[OUT] ${this.register.name}`);
|
this.logger.log(`[OUT] ${this.register.name}`);
|
||||||
}
|
}
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,7 +3,7 @@ import { NotificationhubService } from './notificationhub.service';
|
|||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports:[ConfigModule],
|
imports: [ConfigModule],
|
||||||
exports: [NotificationhubService],
|
exports: [NotificationhubService],
|
||||||
providers: [NotificationhubService],
|
providers: [NotificationhubService],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -23,7 +23,7 @@ export class NotificationhubService {
|
|||||||
* @param pnsHandler
|
* @param pnsHandler
|
||||||
* @returns register
|
* @returns register
|
||||||
*/
|
*/
|
||||||
async register(pns: string, pnsHandler: string): Promise<{}> {
|
async register(pns: string, pnsHandler: string): Promise<void> {
|
||||||
this.logger.log(`[IN] ${this.register.name}`);
|
this.logger.log(`[IN] ${this.register.name}`);
|
||||||
let reg: RegistrationDescription;
|
let reg: RegistrationDescription;
|
||||||
//登録情報作成
|
//登録情報作成
|
||||||
@ -59,7 +59,5 @@ export class NotificationhubService {
|
|||||||
} finally {
|
} finally {
|
||||||
this.logger.log(`[OUT] ${this.register.name}`);
|
this.logger.log(`[OUT] ${this.register.name}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user