Merged PR 54: API実装(I/F)
## 概要 [Task1494: API実装(I/F)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/1494) - POST /accounts のAPIを実装 - POST /users/confirm のAPIを実装 - 上記APIからopenapiを実装 ## レビューポイント - ラフスケッチ時から変更になった箇所があるが問題ないか - ディーラーIDは省略可能かつIDを指定するべきなのでnumber?に型を変更 - 管理者ユーザー用に同意済み利用規約バージョンを受け付けるようにした - reCAPTCHAを想定して事前にreCAPTCHA用トークンを受け付けるようにした ## 動作確認状況 - openapiが生成されることを確認
This commit is contained in:
parent
6fe1cc4d6d
commit
dfd9abc1c3
@ -6,11 +6,7 @@
|
||||
"operationId": "checkHealth",
|
||||
"summary": "",
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
"responses": { "200": { "description": "" } }
|
||||
}
|
||||
},
|
||||
"/auth/token": {
|
||||
@ -22,9 +18,7 @@
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TokenRequest"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/TokenRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -33,9 +27,7 @@
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/TokenResponse"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/TokenResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -43,9 +35,7 @@
|
||||
"description": "認証エラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -53,16 +43,12 @@
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"auth"
|
||||
]
|
||||
"tags": ["auth"]
|
||||
}
|
||||
},
|
||||
"/auth/accessToken": {
|
||||
@ -75,9 +61,7 @@
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/AccessTokenResponse"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/AccessTokenResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -85,9 +69,7 @@
|
||||
"description": "認証エラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -95,21 +77,99 @@
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ErrorResponse"
|
||||
}
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"auth"
|
||||
],
|
||||
"security": [
|
||||
{
|
||||
"bearer": []
|
||||
"tags": ["auth"],
|
||||
"security": [{ "bearer": [] }]
|
||||
}
|
||||
},
|
||||
"/accounts": {
|
||||
"post": {
|
||||
"operationId": "createAccount",
|
||||
"summary": "",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/CreateAccountRequest" }
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CreateAccountResponse"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "登録済みユーザーからの登録など",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["accounts"]
|
||||
}
|
||||
},
|
||||
"/users/confirm": {
|
||||
"post": {
|
||||
"operationId": "confirmUser",
|
||||
"summary": "",
|
||||
"parameters": [],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ConfirmRequest" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "成功時のレスポンス",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ConfirmResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "不正なトークン",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "想定外のサーバーエラー",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/ErrorResponse" }
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": ["users"]
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -134,60 +194,73 @@
|
||||
"TokenRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"idToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"idToken": { "type": "string" },
|
||||
"type": {
|
||||
"type": "string",
|
||||
"description": "web or mobile or desktop"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"idToken",
|
||||
"type"
|
||||
]
|
||||
"required": ["idToken", "type"]
|
||||
},
|
||||
"TokenResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"refreshToken": {
|
||||
"type": "string"
|
||||
},
|
||||
"accessToken": {
|
||||
"type": "string"
|
||||
}
|
||||
"refreshToken": { "type": "string" },
|
||||
"accessToken": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"refreshToken",
|
||||
"accessToken"
|
||||
]
|
||||
"required": ["refreshToken", "accessToken"]
|
||||
},
|
||||
"ErrorResponse": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"message": {
|
||||
"type": "string"
|
||||
},
|
||||
"code": {
|
||||
"type": "string"
|
||||
}
|
||||
"message": { "type": "string" },
|
||||
"code": { "type": "string" }
|
||||
},
|
||||
"required": [
|
||||
"message",
|
||||
"code"
|
||||
]
|
||||
"required": ["message", "code"]
|
||||
},
|
||||
"AccessTokenResponse": {
|
||||
"type": "object",
|
||||
"properties": { "accessToken": { "type": "string" } },
|
||||
"required": ["accessToken"]
|
||||
},
|
||||
"CreateAccountRequest": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"accessToken": {
|
||||
"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" },
|
||||
"acceptedTermsVersion": {
|
||||
"type": "string",
|
||||
"description": "同意済み利用規約のバージョン"
|
||||
},
|
||||
"token": { "type": "string", "description": "reCAPTCHA Token" }
|
||||
},
|
||||
"required": [
|
||||
"accessToken"
|
||||
"companyName",
|
||||
"country",
|
||||
"dealerAccountId",
|
||||
"adminName",
|
||||
"adminMail",
|
||||
"adminPassword",
|
||||
"acceptedTermsVersion",
|
||||
"token"
|
||||
]
|
||||
}
|
||||
},
|
||||
"CreateAccountResponse": { "type": "object", "properties": {} },
|
||||
"ConfirmRequest": {
|
||||
"type": "object",
|
||||
"properties": { "token": { "type": "string" } },
|
||||
"required": ["token"]
|
||||
},
|
||||
"ConfirmResponse": { "type": "object", "properties": {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9,6 +9,12 @@ import { AuthController } from './features/auth/auth.controller';
|
||||
import { AuthService } from './features/auth/auth.service';
|
||||
import { CryptoModule } from './gateways/crypto/crypto.module';
|
||||
import { AdB2cModule } from './gateways/adb2c/adb2c.module';
|
||||
import { AccountsController } from './features/accounts/accounts.controller';
|
||||
import { AccountsService } from './features/accounts/accounts.service';
|
||||
import { AccountsModule } from './features/accounts/accounts.module';
|
||||
import { UsersController } from './features/users/users.controller';
|
||||
import { UsersService } from './features/users/users.service';
|
||||
import { UsersModule } from './features/users/users.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@ -22,6 +28,8 @@ import { AdB2cModule } from './gateways/adb2c/adb2c.module';
|
||||
AuthModule,
|
||||
CryptoModule,
|
||||
AdB2cModule,
|
||||
AccountsModule,
|
||||
UsersModule,
|
||||
// TypeOrmModule.forRootAsync({
|
||||
// imports: [ConfigModule],
|
||||
// useFactory: async (configService: ConfigService) => ({
|
||||
@ -37,8 +45,13 @@ import { AdB2cModule } from './gateways/adb2c/adb2c.module';
|
||||
// inject: [ConfigService],
|
||||
// }),
|
||||
],
|
||||
controllers: [HealthController, AuthController],
|
||||
providers: [AuthService],
|
||||
controllers: [
|
||||
HealthController,
|
||||
AuthController,
|
||||
AccountsController,
|
||||
UsersController,
|
||||
],
|
||||
providers: [AuthService, AccountsService, UsersService],
|
||||
})
|
||||
export class AppModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
|
||||
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AccountsController } from './accounts.controller';
|
||||
|
||||
describe('AccountsController', () => {
|
||||
let controller: AccountsController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [AccountsController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<AccountsController>(AccountsController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,36 @@
|
||||
import { Body, Controller, HttpStatus, Post } from '@nestjs/common';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { AccountsService } from './accounts.service';
|
||||
import { CreateAccountRequest, CreateAccountResponse } from './types/types';
|
||||
|
||||
@ApiTags('accounts')
|
||||
@Controller('accounts')
|
||||
export class AccountsController {
|
||||
constructor(private readonly accountService: AccountsService) {}
|
||||
|
||||
@Post()
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
type: CreateAccountResponse,
|
||||
description: '成功時のレスポンス',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
description: '登録済みユーザーからの登録など',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
description: '想定外のサーバーエラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'createAccount' })
|
||||
async createAccount(
|
||||
@Body() body: CreateAccountRequest,
|
||||
): Promise<CreateAccountResponse> {
|
||||
console.log(JSON.stringify(body));
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AccountsController } from './accounts.controller';
|
||||
import { AccountsService } from './accounts.service';
|
||||
|
||||
@Module({
|
||||
controllers: [AccountsController],
|
||||
providers: [AccountsService],
|
||||
})
|
||||
export class AccountsModule {}
|
||||
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { AccountsService } from './accounts.service';
|
||||
|
||||
describe('AccountsService', () => {
|
||||
let service: AccountsService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [AccountsService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<AccountsService>(AccountsService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class AccountsService {}
|
||||
29
dictation_server/src/features/accounts/types/types.ts
Normal file
29
dictation_server/src/features/accounts/types/types.ts
Normal file
@ -0,0 +1,29 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsEmail, IsInt } from 'class-validator';
|
||||
|
||||
export class CreateAccountRequest {
|
||||
@ApiProperty()
|
||||
companyName: string;
|
||||
@ApiProperty({
|
||||
description: '国名(ISO 3166-1 alpha-2)',
|
||||
minLength: 2,
|
||||
maxLength: 2,
|
||||
})
|
||||
country: string;
|
||||
@ApiProperty({ nullable: true })
|
||||
@IsInt()
|
||||
dealerAccountId?: number;
|
||||
@ApiProperty()
|
||||
adminName: string;
|
||||
@ApiProperty()
|
||||
@IsEmail()
|
||||
adminMail: string;
|
||||
@ApiProperty()
|
||||
adminPassword: string;
|
||||
@ApiProperty({ description: '同意済み利用規約のバージョン' })
|
||||
acceptedTermsVersion: string;
|
||||
@ApiProperty({ description: 'reCAPTCHA Token' })
|
||||
token: string;
|
||||
}
|
||||
|
||||
export class CreateAccountResponse {}
|
||||
@ -1,5 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { AdB2cModule } from 'src/gateways/adb2c/adb2c.module';
|
||||
import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
|
||||
import { CryptoModule } from '../../gateways/crypto/crypto.module';
|
||||
import { AuthController } from './auth.controller';
|
||||
import { AuthService } from './auth.service';
|
||||
|
||||
8
dictation_server/src/features/users/types/types.ts
Normal file
8
dictation_server/src/features/users/types/types.ts
Normal file
@ -0,0 +1,8 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
|
||||
export class ConfirmRequest {
|
||||
@ApiProperty()
|
||||
token: string;
|
||||
}
|
||||
|
||||
export class ConfirmResponse {}
|
||||
18
dictation_server/src/features/users/users.controller.spec.ts
Normal file
18
dictation_server/src/features/users/users.controller.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersController } from './users.controller';
|
||||
|
||||
describe('UsersController', () => {
|
||||
let controller: UsersController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [UsersController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<UsersController>(UsersController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
34
dictation_server/src/features/users/users.controller.ts
Normal file
34
dictation_server/src/features/users/users.controller.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { Body, Controller, HttpStatus, Post } from '@nestjs/common';
|
||||
import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { ErrorResponse } from '../../common/error/types/types';
|
||||
import { ConfirmRequest, ConfirmResponse } from './types/types';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@ApiTags('users')
|
||||
@Controller('users')
|
||||
export class UsersController {
|
||||
constructor(private readonly usersService: UsersService) {}
|
||||
|
||||
@Post('confirm')
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
type: ConfirmResponse,
|
||||
description: '成功時のレスポンス',
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.BAD_REQUEST,
|
||||
description: '不正なトークン',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
description: '想定外のサーバーエラー',
|
||||
type: ErrorResponse,
|
||||
})
|
||||
@ApiOperation({ operationId: 'confirmUser' })
|
||||
async confirmUser(@Body() body: ConfirmRequest): Promise<ConfirmResponse> {
|
||||
console.log(JSON.stringify(body));
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
||||
9
dictation_server/src/features/users/users.module.ts
Normal file
9
dictation_server/src/features/users/users.module.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Module({
|
||||
controllers: [UsersController],
|
||||
providers: [UsersService],
|
||||
})
|
||||
export class UsersModule {}
|
||||
18
dictation_server/src/features/users/users.service.spec.ts
Normal file
18
dictation_server/src/features/users/users.service.spec.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
describe('UsersService', () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
4
dictation_server/src/features/users/users.service.ts
Normal file
4
dictation_server/src/features/users/users.service.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {}
|
||||
@ -3,9 +3,7 @@ import cookieParser from 'cookie-parser';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { AppModule } from './app.module';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import { LoggerMiddleware } from './common/loggerMiddleware';
|
||||
import helmet from 'helmet';
|
||||
import crypto from 'crypto';
|
||||
const helmetDirectives = helmet.contentSecurityPolicy.getDefaultDirectives();
|
||||
helmetDirectives['connect-src'] = [
|
||||
"'self'",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user