From 8a0815821e9bbde39b6c6e8f6560c0bf8faee957 Mon Sep 17 00:00:00 2001 From: "makabe.t" Date: Mon, 24 Apr 2023 09:58:25 +0000 Subject: [PATCH] =?UTF-8?q?Merged=20PR=2085:=20IF=E5=AE=9F=E8=A3=85(?= =?UTF-8?q?=E3=83=A6=E3=83=BC=E3=82=B6=E3=83=BC=E6=83=85=E5=A0=B1=E5=8F=96?= =?UTF-8?q?=E5=BE=97API/=E9=9F=B3=E5=A3=B0=E3=83=95=E3=82=A1=E3=82=A4?= =?UTF-8?q?=E3=83=AB=E3=82=A2=E3=83=83=E3=83=97=E3=83=AD=E3=83=BC=E3=83=89?= =?UTF-8?q?=E5=AE=8C=E4=BA=86API)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 概要 [Task1644: IF実装(ユーザー情報取得API/音声ファイルアップロード完了API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1644) - 以下の外部連携APIのIFを実装しました。 - ユーザー情報取得API - 音声ファイルアップロード完了API ## レビューポイント - 音声ファイルアップロード完了APIについてTaskフォルダを掘って新規に追加したがフォルダ構成に問題はないか。 - IFの応答ステータスは適切か。 - リクエスト/レスポンスは想定通りか - ユーザー情報取得API応答のWorkTypeID周りが不安なのでご確認をお願いします。 ## UIの変更 - なし ## 動作確認状況 - Swaggerをローカルで確認 --- dictation_server/src/api/odms/openapi.json | 636 +++++++++++++++--- dictation_server/src/app.module.ts | 6 + .../src/features/task/task.controller.spec.ts | 24 + .../src/features/task/task.controller.ts | 38 ++ .../src/features/task/task.module.ts | 10 + .../src/features/task/task.service.spec.ts | 21 + .../src/features/task/task.service.ts | 4 + .../src/features/task/types/types.ts | 48 ++ .../src/features/users/types/types.ts | 56 ++ .../src/features/users/users.controller.ts | 44 ++ 10 files changed, 806 insertions(+), 81 deletions(-) create mode 100644 dictation_server/src/features/task/task.controller.spec.ts create mode 100644 dictation_server/src/features/task/task.controller.ts create mode 100644 dictation_server/src/features/task/task.module.ts create mode 100644 dictation_server/src/features/task/task.service.spec.ts create mode 100644 dictation_server/src/features/task/task.service.ts create mode 100644 dictation_server/src/features/task/types/types.ts diff --git a/dictation_server/src/api/odms/openapi.json b/dictation_server/src/api/odms/openapi.json index a5a1a42..68ba4dd 100644 --- a/dictation_server/src/api/odms/openapi.json +++ b/dictation_server/src/api/odms/openapi.json @@ -6,7 +6,11 @@ "operationId": "checkHealth", "summary": "", "parameters": [], - "responses": { "200": { "description": "" } } + "responses": { + "200": { + "description": "" + } + } } }, "/auth/token": { @@ -18,7 +22,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/TokenRequest" } + "schema": { + "$ref": "#/components/schemas/TokenRequest" + } } } }, @@ -27,7 +33,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/TokenResponse" } + "schema": { + "$ref": "#/components/schemas/TokenResponse" + } } } }, @@ -35,7 +43,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -43,7 +53,9 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -61,7 +73,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/AccessTokenResponse" } + "schema": { + "$ref": "#/components/schemas/AccessTokenResponse" + } } } }, @@ -69,7 +83,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -77,13 +93,19 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } }, "tags": ["auth"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/accounts": { @@ -95,7 +117,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/CreateAccountRequest" } + "schema": { + "$ref": "#/components/schemas/CreateAccountRequest" + } } } }, @@ -114,7 +138,9 @@ "description": "登録済みユーザーからの登録など", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -122,7 +148,9 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -130,6 +158,56 @@ "tags": ["accounts"] } }, + "/files/audio/upload-finished": { + "post": { + "operationId": "createTask", + "summary": "", + "parameters": [], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AudioUploadFinishedRequest" + } + } + } + }, + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/AudioUploadFinishedResponse" + } + } + } + }, + "400": { + "description": "不正なパラメータ", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "tags": ["files"] + } + }, "/users/confirm": { "post": { "operationId": "confirmUser", @@ -139,7 +217,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ConfirmRequest" } + "schema": { + "$ref": "#/components/schemas/ConfirmRequest" + } } } }, @@ -148,7 +228,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ConfirmResponse" } + "schema": { + "$ref": "#/components/schemas/ConfirmResponse" + } } } }, @@ -156,7 +238,9 @@ "description": "不正なトークン", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -164,7 +248,9 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -181,7 +267,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ConfirmRequest" } + "schema": { + "$ref": "#/components/schemas/ConfirmRequest" + } } } }, @@ -190,7 +278,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ConfirmResponse" } + "schema": { + "$ref": "#/components/schemas/ConfirmResponse" + } } } }, @@ -198,7 +288,9 @@ "description": "不正なトークン", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -206,7 +298,9 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } @@ -224,7 +318,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/GetUsersResponse" } + "schema": { + "$ref": "#/components/schemas/GetUsersResponse" + } } } }, @@ -232,7 +328,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -240,13 +338,19 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/users/signup": { @@ -258,7 +362,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/SignupRequest" } + "schema": { + "$ref": "#/components/schemas/SignupRequest" + } } } }, @@ -267,7 +373,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/SignupResponse" } + "schema": { + "$ref": "#/components/schemas/SignupResponse" + } } } }, @@ -275,7 +383,9 @@ "description": "登録済みメールによる再登録、AuthorIDの重複など", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -283,7 +393,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -291,13 +403,64 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } }, "tags": ["users"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] + } + }, + "/users/user-info": { + "get": { + "operationId": "getUserInfo", + "summary": "", + "parameters": [], + "responses": { + "200": { + "description": "成功時のレスポンス", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/GetUserInfoResponse" + } + } + } + }, + "401": { + "description": "認証エラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + }, + "500": { + "description": "想定外のサーバーエラー", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } + } + } + } + }, + "tags": ["users"], + "security": [ + { + "bearer": [] + } + ] } }, "/notification/register": { @@ -309,7 +472,9 @@ "required": true, "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RegisterRequest" } + "schema": { + "$ref": "#/components/schemas/RegisterRequest" + } } } }, @@ -318,7 +483,9 @@ "description": "成功時のレスポンス", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/RegisterResponse" } + "schema": { + "$ref": "#/components/schemas/RegisterResponse" + } } } }, @@ -326,7 +493,9 @@ "description": "不正なパラメータ", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -334,7 +503,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -342,13 +513,19 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } }, "tags": ["notification"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/files/audio/upload-location": { @@ -371,7 +548,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -379,13 +558,19 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } }, "tags": ["files"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } }, "/files/audio/download-location": { @@ -398,7 +583,9 @@ "required": true, "in": "query", "description": "音声ファイル情報をDBから取得するためのID", - "schema": { "type": "string" } + "schema": { + "type": "string" + } } ], "responses": { @@ -416,7 +603,9 @@ "description": "認証エラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } }, @@ -424,13 +613,19 @@ "description": "想定外のサーバーエラー", "content": { "application/json": { - "schema": { "$ref": "#/components/schemas/ErrorResponse" } + "schema": { + "$ref": "#/components/schemas/ErrorResponse" + } } } } }, "tags": ["files"], - "security": [{ "bearer": [] }] + "security": [ + { + "bearer": [] + } + ] } } }, @@ -455,7 +650,9 @@ "TokenRequest": { "type": "object", "properties": { - "idToken": { "type": "string" }, + "idToken": { + "type": "string" + }, "type": { "type": "string", "description": "web or mobile or desktop" @@ -466,43 +663,69 @@ "TokenResponse": { "type": "object", "properties": { - "refreshToken": { "type": "string" }, - "accessToken": { "type": "string" } + "refreshToken": { + "type": "string" + }, + "accessToken": { + "type": "string" + } }, "required": ["refreshToken", "accessToken"] }, "ErrorResponse": { "type": "object", "properties": { - "message": { "type": "string" }, - "code": { "type": "string" } + "message": { + "type": "string" + }, + "code": { + "type": "string" + } }, "required": ["message", "code"] }, "AccessTokenResponse": { "type": "object", - "properties": { "accessToken": { "type": "string" } }, + "properties": { + "accessToken": { + "type": "string" + } + }, "required": ["accessToken"] }, "CreateAccountRequest": { "type": "object", "properties": { - "companyName": { "type": "string" }, + "companyName": { + "type": "string" + }, "country": { "type": "string", "description": "国名(ISO 3166-1 alpha-2)", "minLength": 2, "maxLength": 2 }, - "dealerAccountId": { "type": "number", "nullable": true }, - "adminName": { "type": "string" }, - "adminMail": { "type": "string" }, - "adminPassword": { "type": "string" }, + "dealerAccountId": { + "type": "number", + "nullable": true + }, + "adminName": { + "type": "string" + }, + "adminMail": { + "type": "string" + }, + "adminPassword": { + "type": "string" + }, "acceptedTermsVersion": { "type": "string", "description": "同意済み利用規約のバージョン" }, - "token": { "type": "string", "description": "reCAPTCHA Token" } + "token": { + "type": "string", + "description": "reCAPTCHA Token" + } }, "required": [ "companyName", @@ -515,25 +738,146 @@ "token" ] }, - "CreateAccountResponse": { "type": "object", "properties": {} }, + "CreateAccountResponse": { + "type": "object", + "properties": {} + }, + "AudioUploadFinishedRequest": { + "type": "object", + "properties": { + "url": { + "type": "string", + "description": "アップロード先Blob Storage(ファイル名含む)" + }, + "authorId": { + "type": "string", + "description": "自分自身(ログイン認証)したAuthorID" + }, + "fileName": { + "type": "string", + "description": "音声ファイル名" + }, + "duration": { + "type": "string", + "description": "音声ファイルの録音時間(yyyy-mm-ddThh:mm:ss.sss)" + }, + "createdDate": { + "type": "string", + "description": "音声ファイルの録音作成日時(開始日時)(yyyy-mm-ddThh:mm:ss.sss)" + }, + "completedDate": { + "type": "string", + "description": "音声ファイルの録音作成終了日時(yyyy-mm-ddThh:mm:ss.sss)" + }, + "uploadedDate": { + "type": "string", + "description": "音声ファイルのアップロード日時(yyyy-mm-ddThh:mm:ss.sss)" + }, + "fileSize": { + "type": "string" + }, + "priority": { + "type": "string", + "description": "優先度 \"00\":Normal / \"01\":High" + }, + "audioFormat": { + "type": "string", + "description": "録音形式: DSS/DS2(SP)/DS2(QP)" + }, + "comment": { + "type": "string" + }, + "workType": { + "type": "string" + }, + "optionItemLabel": { + "type": "string", + "minLength": 1, + "maxLength": 16 + }, + "optionItemValue": { + "type": "string", + "minLength": 1, + "maxLength": 20 + }, + "isEncrypted": { + "type": "boolean" + } + }, + "required": [ + "url", + "authorId", + "fileName", + "duration", + "createdDate", + "completedDate", + "uploadedDate", + "fileSize", + "priority", + "audioFormat", + "comment", + "workType", + "optionItemLabel", + "optionItemValue", + "isEncrypted" + ] + }, + "AudioUploadFinishedResponse": { + "type": "object", + "properties": { + "jobNumber": { + "type": "string", + "description": "8桁固定の数字" + } + }, + "required": ["jobNumber"] + }, "ConfirmRequest": { "type": "object", - "properties": { "token": { "type": "string" } }, + "properties": { + "token": { + "type": "string" + } + }, "required": ["token"] }, - "ConfirmResponse": { "type": "object", "properties": {} }, + "ConfirmResponse": { + "type": "object", + "properties": {} + }, "User": { "type": "object", "properties": { - "name": { "type": "string" }, - "role": { "type": "string", "description": "none/author/typist" }, - "authorId": { "type": "string", "nullable": true }, - "typistGroupName": { "type": "string", "nullable": true }, - "email": { "type": "string" }, - "emailVerified": { "type": "boolean" }, - "autoRenew": { "type": "boolean" }, - "licenseAlert": { "type": "boolean" }, - "notification": { "type": "boolean" } + "name": { + "type": "string" + }, + "role": { + "type": "string", + "description": "none/author/typist" + }, + "authorId": { + "type": "string", + "nullable": true + }, + "typistGroupName": { + "type": "string", + "nullable": true + }, + "email": { + "type": "string" + }, + "emailVerified": { + "type": "boolean" + }, + "autoRenew": { + "type": "boolean" + }, + "licenseAlert": { + "type": "boolean" + }, + "notification": { + "type": "boolean" + } }, "required": [ "name", @@ -552,7 +896,9 @@ "properties": { "users": { "type": "array", - "items": { "$ref": "#/components/schemas/User" } + "items": { + "$ref": "#/components/schemas/User" + } } }, "required": ["users"] @@ -560,14 +906,31 @@ "SignupRequest": { "type": "object", "properties": { - "name": { "type": "string" }, - "role": { "type": "string", "description": "none/author/typist" }, - "authorId": { "type": "string" }, - "typistGroupId": { "type": "number" }, - "email": { "type": "string" }, - "autoRenew": { "type": "boolean" }, - "licenseAlert": { "type": "boolean" }, - "notification": { "type": "boolean" } + "name": { + "type": "string" + }, + "role": { + "type": "string", + "description": "none/author/typist" + }, + "authorId": { + "type": "string" + }, + "typistGroupId": { + "type": "number" + }, + "email": { + "type": "string" + }, + "autoRenew": { + "type": "boolean" + }, + "licenseAlert": { + "type": "boolean" + }, + "notification": { + "type": "boolean" + } }, "required": [ "name", @@ -578,11 +941,111 @@ "notification" ] }, - "SignupResponse": { "type": "object", "properties": {} }, + "SignupResponse": { + "type": "object", + "properties": {} + }, + "OptionItem": { + "type": "object", + "properties": { + "label": { + "type": "string", + "minLength": 1, + "maxLength": 16, + "description": "Option Itemのラベル" + }, + "initialValueType": { + "type": "number", + "description": "項目タイプ 1:Blank/2:Default/3:前の値" + }, + "defaultValue": { + "type": "string", + "minLength": 1, + "maxLength": 20, + "description": "typeでDefaultを選択した場合のデフォルト値" + } + }, + "required": ["label", "initialValueType", "defaultValue"] + }, + "OptionItemList": { + "type": "object", + "properties": { + "workTypeId": { + "type": "string" + }, + "optionItemList": { + "maxItems": 10, + "description": "1WorkTypeIDにつき、10個まで登録可能", + "type": "array", + "items": { + "$ref": "#/components/schemas/OptionItem" + } + } + }, + "required": ["workTypeId", "optionItemList"] + }, + "GetUserInfoResponse": { + "type": "object", + "properties": { + "authorId": { + "type": "string", + "description": "自分自身(ログイン認証)したAuthorID" + }, + "authorIdList": { + "description": "属しているアカウントのAuthorID List(全て)", + "type": "array", + "items": { + "type": "string" + } + }, + "workTypeList": { + "maxItems": 20, + "description": "Authorに設定されているWorktypeIDのリスト(最大20個)", + "type": "array", + "items": { + "$ref": "#/components/schemas/OptionItemList" + } + }, + "isEncrypted": { + "type": "boolean", + "description": "音声ファイルを暗号化するかどうか" + }, + "encryptionPassword": { + "type": "string", + "description": "Encryptionで暗号化を掛ける場合のパスワード", + "nullable": true + }, + "activeWorktype": { + "type": "string", + "description": "デフォルトで利用するWorkTypeID(アカウントに紐づくWorkTypeIDから一つ指定)" + }, + "audioFormat": { + "type": "string", + "description": "録音形式: DSS/DS2(SP)/DS2(QP): DS2固定" + }, + "prompt": { + "type": "boolean", + "description": "デバイス上で、Option Itemを表示するかどうか" + } + }, + "required": [ + "authorId", + "authorIdList", + "workTypeList", + "isEncrypted", + "encryptionPassword", + "activeWorktype", + "audioFormat", + "prompt" + ] + }, "RegisterRequest": { "type": "object", "properties": { - "pns": { "type": "string", "description": "wns or apns" }, + "pns": { + "type": "string", + "description": "wns or apns" + }, "handler": { "type": "string", "description": "wnsのチャネルURI or apnsのデバイストークン" @@ -590,15 +1053,26 @@ }, "required": ["pns", "handler"] }, - "RegisterResponse": { "type": "object", "properties": {} }, + "RegisterResponse": { + "type": "object", + "properties": {} + }, "AudioUploadLocationResponse": { "type": "object", - "properties": { "url": { "type": "string" } }, + "properties": { + "url": { + "type": "string" + } + }, "required": ["url"] }, "AudioDownloadLocationResponse": { "type": "object", - "properties": { "url": { "type": "string" } }, + "properties": { + "url": { + "type": "string" + } + }, "required": ["url"] } } diff --git a/dictation_server/src/app.module.ts b/dictation_server/src/app.module.ts index d01af52..671893c 100644 --- a/dictation_server/src/app.module.ts +++ b/dictation_server/src/app.module.ts @@ -23,6 +23,9 @@ import { NotificationhubModule } from './gateways/notificationhub/notificationhu import { NotificationhubService } from './gateways/notificationhub/notificationhub.service'; import { NotificationModule } from './features/notification/notification.module'; import { BlobModule } from './features/blob/blob.module'; +import { TaskController } from './features/task/task.controller'; +import { TaskService } from './features/task/task.service'; +import { TaskModule } from './features/task/task.module'; @Module({ imports: [ @@ -37,6 +40,7 @@ import { BlobModule } from './features/blob/blob.module'; CryptoModule, AdB2cModule, AccountsModule, + TaskModule, UsersModule, SendGridModule, AccountsRepositoryModule, @@ -63,11 +67,13 @@ import { BlobModule } from './features/blob/blob.module'; HealthController, AuthController, AccountsController, + TaskController, UsersController, ], providers: [ AuthService, AccountsService, + TaskService, UsersService, NotificationhubService, ], diff --git a/dictation_server/src/features/task/task.controller.spec.ts b/dictation_server/src/features/task/task.controller.spec.ts new file mode 100644 index 0000000..160ee76 --- /dev/null +++ b/dictation_server/src/features/task/task.controller.spec.ts @@ -0,0 +1,24 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { TaskController } from './task.controller'; +import { TaskService } from './task.service'; + +describe('TaskController', () => { + let controller: TaskController; + const mockTaskervice = {}; + + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + controllers: [TaskController], + providers: [TaskService], + }) + .overrideProvider(TaskService) + .useValue(mockTaskervice) + .compile(); + + controller = module.get(TaskController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/dictation_server/src/features/task/task.controller.ts b/dictation_server/src/features/task/task.controller.ts new file mode 100644 index 0000000..479ee40 --- /dev/null +++ b/dictation_server/src/features/task/task.controller.ts @@ -0,0 +1,38 @@ +import { Body, Controller, HttpStatus, Post } from '@nestjs/common'; +import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { ErrorResponse } from '../../common/error/types/types'; +import { TaskService } from './task.service'; +import { + AudioUploadFinishedRequest, + AudioUploadFinishedResponse, +} from './types/types'; + +@ApiTags('files') +@Controller('files') +export class TaskController { + constructor(private readonly taskService: TaskService) {} + + @ApiResponse({ + status: HttpStatus.OK, + type: AudioUploadFinishedResponse, + description: '成功時のレスポンス', + }) + @ApiResponse({ + status: HttpStatus.BAD_REQUEST, + description: '不正なパラメータ', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: '想定外のサーバーエラー', + type: ErrorResponse, + }) + @ApiOperation({ operationId: 'createTask' }) + @Post('audio/upload-finished') + async uploadFinished( + @Body() body: AudioUploadFinishedRequest, + ): Promise { + console.log(body); + return { jobNumber: '00000001' }; + } +} diff --git a/dictation_server/src/features/task/task.module.ts b/dictation_server/src/features/task/task.module.ts new file mode 100644 index 0000000..8e88d64 --- /dev/null +++ b/dictation_server/src/features/task/task.module.ts @@ -0,0 +1,10 @@ +import { Module } from '@nestjs/common'; +import { TaskController } from './task.controller'; +import { TaskService } from './task.service'; + +@Module({ + imports: [], + controllers: [TaskController], + providers: [TaskService], +}) +export class TaskModule {} diff --git a/dictation_server/src/features/task/task.service.spec.ts b/dictation_server/src/features/task/task.service.spec.ts new file mode 100644 index 0000000..106fdb4 --- /dev/null +++ b/dictation_server/src/features/task/task.service.spec.ts @@ -0,0 +1,21 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { TaskService } from './task.service'; + +describe('TaskService', () => { + let service: TaskService; + const mockTaskService = {}; + beforeEach(async () => { + const module: TestingModule = await Test.createTestingModule({ + providers: [TaskService], + }) + .overrideProvider(TaskService) + .useValue(mockTaskService) + .compile(); + + service = module.get(TaskService); + }); + + it('should be defined', () => { + expect(service).toBeDefined(); + }); +}); diff --git a/dictation_server/src/features/task/task.service.ts b/dictation_server/src/features/task/task.service.ts new file mode 100644 index 0000000..0703d00 --- /dev/null +++ b/dictation_server/src/features/task/task.service.ts @@ -0,0 +1,4 @@ +import { Injectable } from '@nestjs/common'; + +@Injectable() +export class TaskService {} diff --git a/dictation_server/src/features/task/types/types.ts b/dictation_server/src/features/task/types/types.ts new file mode 100644 index 0000000..79ec3af --- /dev/null +++ b/dictation_server/src/features/task/types/types.ts @@ -0,0 +1,48 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class AudioUploadFinishedRequest { + @ApiProperty({ description: 'アップロード先Blob Storage(ファイル名含む)' }) + url: string; + @ApiProperty({ description: '自分自身(ログイン認証)したAuthorID' }) + authorId: string; + @ApiProperty({ description: '音声ファイル名' }) + fileName: string; + @ApiProperty({ + description: '音声ファイルの録音時間(yyyy-mm-ddThh:mm:ss.sss)', + }) + duration: string; + @ApiProperty({ + description: + '音声ファイルの録音作成日時(開始日時)(yyyy-mm-ddThh:mm:ss.sss)', + }) + createdDate: string; + @ApiProperty({ + description: '音声ファイルの録音作成終了日時(yyyy-mm-ddThh:mm:ss.sss)', + }) + completedDate: string; + @ApiProperty({ + description: '音声ファイルのアップロード日時(yyyy-mm-ddThh:mm:ss.sss)', + }) + uploadedDate: string; + @ApiProperty() + fileSize: string; + @ApiProperty({ description: '優先度 "00":Normal / "01":High' }) + priority: string; + @ApiProperty({ description: '録音形式: DSS/DS2(SP)/DS2(QP)' }) + audioFormat: string; + @ApiProperty() + comment: string; + @ApiProperty() + workType: string; + @ApiProperty({ minLength: 1, maxLength: 16 }) + optionItemLabel: string; + @ApiProperty({ minLength: 1, maxLength: 20 }) + optionItemValue: string; + @ApiProperty() + isEncrypted: boolean; +} + +export class AudioUploadFinishedResponse { + @ApiProperty({ description: '8桁固定の数字' }) + jobNumber: string; +} diff --git a/dictation_server/src/features/users/types/types.ts b/dictation_server/src/features/users/types/types.ts index dbc0227..dd55487 100644 --- a/dictation_server/src/features/users/types/types.ts +++ b/dictation_server/src/features/users/types/types.ts @@ -68,3 +68,59 @@ export class SignupRequest { } export class SignupResponse {} + +export class OptionItem { + @ApiProperty({ + minLength: 1, + maxLength: 16, + description: 'Option Itemのラベル', + }) + label: string; + @ApiProperty({ description: '項目タイプ 1:Blank/2:Default/3:前の値' }) + initialValueType: number; + @ApiProperty({ + minLength: 1, + maxLength: 20, + description: 'typeでDefaultを選択した場合のデフォルト値', + }) + defaultValue: string; +} +export class OptionItemList { + @ApiProperty() + workTypeId: string; + @ApiProperty({ + type: [OptionItem], + maxItems: 10, + description: '1WorkTypeIDにつき、10個まで登録可能', + }) + optionItemList: OptionItem[]; +} + +export class GetUserInfoResponse { + @ApiProperty({ description: '自分自身(ログイン認証)したAuthorID' }) + authorId: string; + @ApiProperty({ description: '属しているアカウントのAuthorID List(全て)' }) + authorIdList: string[]; + @ApiProperty({ + type: [OptionItemList], + maxItems: 20, + description: 'Authorに設定されているWorktypeIDのリスト(最大20個)', + }) + workTypeList: OptionItemList[]; + @ApiProperty({ description: '音声ファイルを暗号化するかどうか' }) + isEncrypted: boolean; + @ApiProperty({ + description: 'Encryptionで暗号化を掛ける場合のパスワード', + nullable: true, + }) + encryptionPassword?: string | undefined; + @ApiProperty({ + description: + 'デフォルトで利用するWorkTypeID(アカウントに紐づくWorkTypeIDから一つ指定)', + }) + activeWorktype: string; + @ApiProperty({ description: '録音形式: DSS/DS2(SP)/DS2(QP): DS2固定' }) + audioFormat: string; + @ApiProperty({ description: 'デバイス上で、Option Itemを表示するかどうか' }) + prompt: boolean; +} diff --git a/dictation_server/src/features/users/users.controller.ts b/dictation_server/src/features/users/users.controller.ts index b696aa1..8b78df8 100644 --- a/dictation_server/src/features/users/users.controller.ts +++ b/dictation_server/src/features/users/users.controller.ts @@ -9,6 +9,7 @@ import { ErrorResponse } from '../../common/error/types/types'; import { ConfirmRequest, ConfirmResponse, + GetUserInfoResponse, GetUsersResponse, SignupRequest, SignupResponse, @@ -121,4 +122,47 @@ export class UsersController { console.log(body); return {}; } + + @ApiResponse({ + status: HttpStatus.OK, + type: GetUserInfoResponse, + description: '成功時のレスポンス', + }) + @ApiResponse({ + status: HttpStatus.UNAUTHORIZED, + description: '認証エラー', + type: ErrorResponse, + }) + @ApiResponse({ + status: HttpStatus.INTERNAL_SERVER_ERROR, + description: '想定外のサーバーエラー', + type: ErrorResponse, + }) + @ApiOperation({ operationId: 'getUserInfo' }) + @ApiBearerAuth() + @Get('user-info') + async getUserInfo(@Req() req: Request): Promise { + console.log(req.header('Authorization')); + return { + authorId: 'AUTHOR', + authorIdList: ['AUTHOR', 'AUTHOR1'], + workTypeList: [ + { + workTypeId: '1', + optionItemList: [ + { + label: '1', + initialValueType: 0, + defaultValue: 'default', + }, + ], + }, + ], + isEncrypted: true, + encryptionPassword: '', + activeWorktype: '', + audioFormat: 'DS2', + prompt: true, + }; + } }