Merged PR 320: API実装(ライセンス割り当てAPI)_履歴以外
## 概要 [Task2362: API実装(ライセンス割り当てAPI)_履歴以外](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2362) ライセンス割り当てのAPIを作成しました。 ※ライセンス割り当て履歴テーブルが絡む処理は別タスクでの対応となるので、ここでは未実装です。 ## レビューポイント なし ## UIの変更 なし ## 動作確認状況 ローカルでUT、動作確認実施済み ## 補足 なし
This commit is contained in:
parent
3b785e97aa
commit
356f5fe346
@ -44,4 +44,6 @@ export const ErrorCodes = [
|
||||
'E010802', // ライセンス取り込み済みエラー
|
||||
'E010803', // ライセンス発行済みエラー
|
||||
'E010804', // ライセンス不足エラー
|
||||
'E010805', // ライセンス有効期限切れエラー
|
||||
'E010806', // ライセンス割り当て不可エラー
|
||||
] as const;
|
||||
|
||||
@ -33,4 +33,6 @@ export const errors: Errors = {
|
||||
E010802: 'License already activated Error',
|
||||
E010803: 'License already issued Error',
|
||||
E010804: 'License shortage Error',
|
||||
E010805: 'License is expired Error',
|
||||
E010806: 'License is unavailable Error',
|
||||
};
|
||||
|
||||
@ -126,6 +126,12 @@ export const LICENSE_ALLOCATED_STATUS = {
|
||||
*/
|
||||
export const LICENSE_EXPIRATION_THRESHOLD_DAYS = 14;
|
||||
|
||||
/**
|
||||
* ライセンスの有効期間
|
||||
* @const {number}
|
||||
*/
|
||||
export const LICENSE_EXPIRATION_DAYS = 365;
|
||||
|
||||
/**
|
||||
* カードライセンスの桁数
|
||||
* @const {number}
|
||||
|
||||
@ -580,7 +580,6 @@ describe('getPartnerAccount', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('getOrderHistories', () => {
|
||||
let source: DataSource = null;
|
||||
beforeEach(async () => {
|
||||
|
||||
@ -126,4 +126,4 @@ export const createUser = async (
|
||||
});
|
||||
const user = identifiers.pop() as User;
|
||||
return { userId: user.id, externalId: external_id, authorId: author_id };
|
||||
};
|
||||
};
|
||||
|
||||
@ -191,15 +191,15 @@ export class GetPartnerLicensesResponse {
|
||||
|
||||
// 第五階層のshortage算出用
|
||||
export class PartnerLicenseInfoForShortage {
|
||||
expiringSoonLicense?:number;
|
||||
allocatableLicenseWithMargin?:number;
|
||||
expiringSoonLicense?: number;
|
||||
allocatableLicenseWithMargin?: number;
|
||||
}
|
||||
|
||||
// RepositoryからPartnerLicenseInfoに関する情報を取得する際の型
|
||||
export type PartnerLicenseInfoForRepository = Omit<
|
||||
PartnerLicenseInfo & PartnerLicenseInfoForShortage,
|
||||
'shortage'
|
||||
>;
|
||||
>;
|
||||
|
||||
export class GetOrderHistoriesRequest {
|
||||
@ApiProperty({ description: '取得件数' })
|
||||
|
||||
@ -202,7 +202,6 @@ export class LicensesController {
|
||||
@Req() req: Request,
|
||||
@Body() body: GetAllocatableLicensesRequest,
|
||||
): Promise<GetAllocatableLicensesResponse> {
|
||||
|
||||
// TODO 仮の戻り値
|
||||
return {
|
||||
allocatableLicenses: [
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
IssueCardLicensesRequest,
|
||||
IssueCardLicensesResponse,
|
||||
ActivateCardLicensesRequest,
|
||||
NewAllocatedLicenseExpirationDate,
|
||||
} from './types/types';
|
||||
import {
|
||||
makeDefaultAccountsRepositoryMockValue,
|
||||
@ -31,6 +32,8 @@ import {
|
||||
selectCardLicense,
|
||||
selectLicense,
|
||||
} from './test/utility';
|
||||
import { UsersService } from '../users/users.service';
|
||||
import { makeContext } from '../../common/log';
|
||||
|
||||
describe('LicensesService', () => {
|
||||
it('ライセンス注文が完了する', async () => {
|
||||
@ -327,7 +330,14 @@ describe('DBテスト', () => {
|
||||
const licenseId = 50;
|
||||
const issueId = 100;
|
||||
|
||||
await createLicense(source, licenseId, defaultAccountId);
|
||||
await createLicense(
|
||||
source,
|
||||
licenseId,
|
||||
null,
|
||||
defaultAccountId,
|
||||
'Unallocated',
|
||||
null,
|
||||
);
|
||||
await createCardLicense(source, licenseId, issueId, cardLicenseKey);
|
||||
await createCardLicenseIssue(source, issueId);
|
||||
|
||||
@ -345,3 +355,133 @@ describe('DBテスト', () => {
|
||||
expect(dbSelectResultFromLicense.license.account_id).toEqual(accountId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ライセンス割り当て', () => {
|
||||
let source: DataSource = 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 () => {
|
||||
await source.destroy();
|
||||
source = null;
|
||||
});
|
||||
|
||||
it('未割当のライセンスに対して、ライセンス割り当てが完了する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId } = await createUser(source, accountId, 'userId', 'admin');
|
||||
await createLicense(source, 1, null, accountId, 'Unallocated', null);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
const expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 1);
|
||||
const result = await selectLicense(source, 1);
|
||||
expect(result.license.allocated_user_id).toBe(userId);
|
||||
expect(result.license.status).toBe('Allocated');
|
||||
expect(result.license.expiry_date.setMilliseconds(0)).toEqual(
|
||||
expiry_date.setMilliseconds(0),
|
||||
);
|
||||
});
|
||||
|
||||
it('再割り当て可能なライセンスに対して、ライセンス割り当てが完了する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId } = await createUser(source, accountId, 'userId', 'admin');
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 30);
|
||||
await createLicense(source, 1, date, accountId, 'Reusable', null);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 1);
|
||||
const result = await selectLicense(source, 1);
|
||||
expect(result.license.allocated_user_id).toBe(userId);
|
||||
expect(result.license.status).toBe('Allocated');
|
||||
expect(result.license.expiry_date).toEqual(date);
|
||||
});
|
||||
|
||||
it('未割当のライセンスに対して、別のライセンスが割り当てられているユーザーの割り当てが完了する', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId } = await createUser(source, accountId, 'userId', 'admin');
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 30);
|
||||
await createLicense(source, 1, date, accountId, 'Allocated', userId);
|
||||
await createLicense(source, 2, null, accountId, 'Unallocated', null);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
const expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
|
||||
await service.allocateLicense(makeContext('trackingId'), userId, 2);
|
||||
|
||||
// もともと割り当てられていたライセンスの状態確認
|
||||
const result1 = await selectLicense(source, 1);
|
||||
expect(result1.license.allocated_user_id).toBe(null);
|
||||
expect(result1.license.status).toBe('Reusable');
|
||||
expect(result1.license.expiry_date).toEqual(date);
|
||||
|
||||
// 新たに割り当てたライセンスの状態確認
|
||||
const result2 = await selectLicense(source, 2);
|
||||
expect(result2.license.allocated_user_id).toBe(userId);
|
||||
expect(result2.license.status).toBe('Allocated');
|
||||
expect(result2.license.expiry_date.setMilliseconds(0)).toEqual(
|
||||
expiry_date.setMilliseconds(0),
|
||||
);
|
||||
});
|
||||
|
||||
it('有効期限が切れているライセンスを割り当てようとした場合、エラーになる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId } = await createUser(source, accountId, 'userId', 'admin');
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() - 30);
|
||||
await createLicense(source, 1, date, accountId, 'Reusable', null);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
await expect(
|
||||
service.allocateLicense(makeContext('trackingId'), userId, 1),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010805'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
|
||||
it('割り当て不可なライセンスを割り当てようとした場合、エラーになる', async () => {
|
||||
const module = await makeTestingModule(source);
|
||||
|
||||
const { accountId } = await createAccount(source);
|
||||
const { userId } = await createUser(source, accountId, 'userId', 'admin');
|
||||
const date = new Date();
|
||||
date.setDate(date.getDate() + 30);
|
||||
await createLicense(source, 1, null, accountId, 'Allocated', null);
|
||||
await createLicense(source, 2, null, accountId, 'Deleted', null);
|
||||
|
||||
const service = module.get<UsersService>(UsersService);
|
||||
|
||||
await expect(
|
||||
service.allocateLicense(makeContext('trackingId'), userId, 1),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
await expect(
|
||||
service.allocateLicense(makeContext('trackingId'), userId, 2),
|
||||
).rejects.toEqual(
|
||||
new HttpException(makeErrorResponse('E010806'), HttpStatus.BAD_REQUEST),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@ -58,15 +58,18 @@ export const createUser = async (
|
||||
export const createLicense = async (
|
||||
datasource: DataSource,
|
||||
licenseId: number,
|
||||
expiry_date: Date,
|
||||
accountId: number,
|
||||
status: string,
|
||||
allocated_user_id: number,
|
||||
): Promise<void> => {
|
||||
const { identifiers } = await datasource.getRepository(License).insert({
|
||||
id: licenseId,
|
||||
expiry_date: null,
|
||||
expiry_date: expiry_date,
|
||||
account_id: accountId,
|
||||
type: 'card',
|
||||
status: 'Unallocated',
|
||||
allocated_user_id: null,
|
||||
status: status,
|
||||
allocated_user_id: allocated_user_id,
|
||||
order_id: null,
|
||||
deleted_at: null,
|
||||
delete_order_id: null,
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import { ApiProperty } from '@nestjs/swagger';
|
||||
import { IsInt, Matches, Max, Min, Length } from 'class-validator';
|
||||
import { LICENSE_EXPIRATION_THRESHOLD_DAYS } from '../../../constants';
|
||||
import {
|
||||
LICENSE_EXPIRATION_DAYS,
|
||||
LICENSE_EXPIRATION_THRESHOLD_DAYS,
|
||||
} from '../../../constants';
|
||||
|
||||
export class CreateOrdersRequest {
|
||||
@ApiProperty()
|
||||
@ -74,4 +77,18 @@ export class ExpirationThresholdDate extends Date {
|
||||
this.setDate(this.getDate() + LICENSE_EXPIRATION_THRESHOLD_DAYS);
|
||||
this.setHours(23, 59, 59, 999); // 時分秒を"23:59:59.999"に固定
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 新規ライセンス割り当て時の有効期限算出用に、365日後の日付を取得する
|
||||
export class NewAllocatedLicenseExpirationDate extends Date {
|
||||
constructor(...args: any[]) {
|
||||
if (args.length === 0) {
|
||||
super(); // 引数がない場合、現在の日付で初期化
|
||||
} else {
|
||||
super(...(args as [string])); // 引数がある場合、引数をそのままDateクラスのコンストラクタに渡す
|
||||
}
|
||||
this.setDate(this.getDate() + LICENSE_EXPIRATION_DAYS);
|
||||
this.setHours(23, 59, 59); // 時分秒を"23:59:59"に固定
|
||||
this.setMilliseconds(0);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
|
||||
import { User } from '../../../repositories/users/entity/user.entity';
|
||||
import { UsersRepositoryService } from '../../../repositories/users/users.repository.service';
|
||||
import { LicensesRepositoryService } from '../../../repositories/licenses/licenses.repository.service';
|
||||
import { UsersService } from '../users.service';
|
||||
import { SortCriteria } from '../../../repositories/sort_criteria/entity/sort_criteria.entity';
|
||||
import { SortCriteriaRepositoryService } from '../../../repositories/sort_criteria/sort_criteria.repository.service';
|
||||
@ -31,6 +32,10 @@ export type UsersRepositoryMockValue = {
|
||||
existsAuthorId: boolean | Error;
|
||||
};
|
||||
|
||||
export type LicensesRepositoryMockValue = {
|
||||
// empty
|
||||
};
|
||||
|
||||
export type AdB2cMockValue = {
|
||||
getMetaData: B2cMetadata | Error;
|
||||
getSignKeySets: JwkSignKey[] | Error;
|
||||
@ -58,6 +63,7 @@ export type ConfigMockValue = {
|
||||
|
||||
export const makeUsersServiceMock = async (
|
||||
usersRepositoryMockValue: UsersRepositoryMockValue,
|
||||
licensesRepositoryMockValue: LicensesRepositoryMockValue,
|
||||
adB2cMockValue: AdB2cMockValue,
|
||||
sendGridMockValue: SendGridMockValue,
|
||||
configMockValue: ConfigMockValue,
|
||||
@ -75,6 +81,8 @@ export const makeUsersServiceMock = async (
|
||||
switch (token) {
|
||||
case UsersRepositoryService:
|
||||
return makeUsersRepositoryMock(usersRepositoryMockValue);
|
||||
case LicensesRepositoryService:
|
||||
return makeLicensesRepositoryMock();
|
||||
case AdB2cService:
|
||||
return makeAdB2cServiceMock(adB2cMockValue);
|
||||
case SendGridService:
|
||||
@ -239,6 +247,12 @@ export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
|
||||
};
|
||||
};
|
||||
|
||||
export const makeLicensesRepositoryMock = (): LicensesRepositoryMockValue => {
|
||||
return {
|
||||
// empty
|
||||
};
|
||||
};
|
||||
|
||||
export const makeSendGridMock = (value: SendGridMockValue) => {
|
||||
const { sendMail, createMailContentFromEmailConfirmForNormalUser } = value;
|
||||
|
||||
|
||||
@ -247,4 +247,4 @@ export class AllocateLicenseRequest {
|
||||
newLicenseId: number;
|
||||
}
|
||||
|
||||
export class AllocateLicenseResponse{}
|
||||
export class AllocateLicenseResponse {}
|
||||
|
||||
@ -409,7 +409,15 @@ export class UsersController {
|
||||
@Body() body: AllocateLicenseRequest,
|
||||
@Req() req: Request,
|
||||
): Promise<AllocateLicenseResponse> {
|
||||
const accessToken = retrieveAuthorizationToken(req);
|
||||
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
|
||||
|
||||
const context = makeContext(userId);
|
||||
await this.usersService.allocateLicense(
|
||||
context,
|
||||
body.userId,
|
||||
body.newLicenseId,
|
||||
);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -4,12 +4,14 @@ import { AdB2cModule } from '../../gateways/adb2c/adb2c.module';
|
||||
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
|
||||
import { SortCriteriaRepositoryModule } from '../../repositories/sort_criteria/sort_criteria.repository.module';
|
||||
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
|
||||
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
|
||||
import { UsersController } from './users.controller';
|
||||
import { UsersService } from './users.service';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
UsersRepositoryModule,
|
||||
LicensesRepositoryModule,
|
||||
SortCriteriaRepositoryModule,
|
||||
AdB2cModule,
|
||||
SendGridModule,
|
||||
|
||||
@ -30,6 +30,7 @@ import { makeTestingModule } from '../../common/test/modules';
|
||||
describe('UsersService.confirmUser', () => {
|
||||
it('ユーザの仮登録時に払い出されるトークンにより、未認証のユーザが認証済みになる', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -37,6 +38,7 @@ describe('UsersService.confirmUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
@ -66,6 +68,7 @@ describe('UsersService.confirmUser', () => {
|
||||
encryption: false,
|
||||
prompt: false,
|
||||
};
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
const sortCriteriaRepositoryMockValue =
|
||||
@ -73,6 +76,7 @@ describe('UsersService.confirmUser', () => {
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
@ -86,6 +90,7 @@ describe('UsersService.confirmUser', () => {
|
||||
|
||||
it('トークンの形式が不正な場合、形式不正エラーとなる。', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -93,6 +98,7 @@ describe('UsersService.confirmUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -123,6 +129,7 @@ describe('UsersService.confirmUser', () => {
|
||||
encryption: false,
|
||||
prompt: false,
|
||||
};
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -130,6 +137,7 @@ describe('UsersService.confirmUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
@ -142,6 +150,7 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
it('ユーザが既に認証済みだった場合、認証済みユーザエラーとなる。', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -153,6 +162,7 @@ describe('UsersService.confirmUser', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -183,6 +193,7 @@ describe('UsersService.confirmUser', () => {
|
||||
encryption: false,
|
||||
prompt: false,
|
||||
};
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -193,6 +204,7 @@ describe('UsersService.confirmUser', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
@ -206,6 +218,7 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
it('DBネットワークエラーとなる場合、エラーとなる。', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -215,6 +228,7 @@ describe('UsersService.confirmUser', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -248,6 +262,7 @@ describe('UsersService.confirmUser', () => {
|
||||
encryption: false,
|
||||
prompt: false,
|
||||
};
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendGridMockValue = makeDefaultSendGridlValue();
|
||||
usersRepositoryMockValue.updateUserVerified = new Error('DB error');
|
||||
@ -256,6 +271,7 @@ describe('UsersService.confirmUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendGridMockValue,
|
||||
configMockValue,
|
||||
@ -272,6 +288,7 @@ describe('UsersService.confirmUser', () => {
|
||||
});
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:None)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -279,6 +296,7 @@ describe('UsersService.confirmUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -308,6 +326,7 @@ describe('UsersService.confirmUser', () => {
|
||||
describe('UsersService.createUser', () => {
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化あり)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -315,6 +334,7 @@ describe('UsersService.createUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -350,6 +370,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Author; 暗号化無し)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -357,6 +378,7 @@ describe('UsersService.createUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -392,6 +414,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
it('管理者権限のあるアクセストークンを使用して、新規ユーザが追加される(role:Transcriptioninst)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -399,6 +422,7 @@ describe('UsersService.createUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -426,6 +450,7 @@ describe('UsersService.createUser', () => {
|
||||
});
|
||||
it('DBネットワークエラーとなる場合、エラーとなる。', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -434,6 +459,7 @@ describe('UsersService.createUser', () => {
|
||||
usersRepositoryMockValue.createNormalUser = new Error('DB error');
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -465,6 +491,7 @@ describe('UsersService.createUser', () => {
|
||||
});
|
||||
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
adb2cParam.createUser = new Error();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
@ -473,6 +500,7 @@ describe('UsersService.createUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -504,6 +532,7 @@ describe('UsersService.createUser', () => {
|
||||
});
|
||||
it('メールアドレスが重複している場合、エラーとなる。', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
adb2cParam.createUser = { reason: 'email', message: 'ObjectConflict' };
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
@ -512,6 +541,7 @@ describe('UsersService.createUser', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -540,6 +570,7 @@ describe('UsersService.createUser', () => {
|
||||
});
|
||||
it('AuthorIDが重複している場合、エラーとなる。(AuthorID重複チェックでエラー)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -550,6 +581,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -580,6 +612,7 @@ describe('UsersService.createUser', () => {
|
||||
});
|
||||
it('AuthorIDが重複している場合、エラーとなる。(insert失敗)', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -590,6 +623,7 @@ describe('UsersService.createUser', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -882,6 +916,7 @@ describe('UsersService.getUsers', () => {
|
||||
describe('UsersService.updateSortCriteria', () => {
|
||||
it('ソート条件を変更できる', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -889,6 +924,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -906,6 +942,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
|
||||
it('ユーザー情報が存在せず、ソート条件を変更できない', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -916,6 +953,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -938,6 +976,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
|
||||
it('ソート条件が存在せず、ソート条件を変更できない', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -949,6 +988,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -973,6 +1013,7 @@ describe('UsersService.updateSortCriteria', () => {
|
||||
describe('UsersService.getSortCriteria', () => {
|
||||
it('ソート条件を取得できる', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -980,6 +1021,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
makeDefaultSortCriteriaRepositoryMockValue();
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -997,6 +1039,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
|
||||
it('ソート条件が存在せず、ソート条件を取得できない', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -1009,6 +1052,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
@ -1031,6 +1075,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
|
||||
it('DBから取得した値が不正だった場合、エラーとなる', async () => {
|
||||
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
|
||||
const licensesRepositoryMockValue = null;
|
||||
const adb2cParam = makeDefaultAdB2cMockValue();
|
||||
const sendgridMockValue = makeDefaultSendGridlValue();
|
||||
const configMockValue = makeDefaultConfigValue();
|
||||
@ -1045,6 +1090,7 @@ describe('UsersService.getSortCriteria', () => {
|
||||
|
||||
const service = await makeUsersServiceMock(
|
||||
usersRepositoryMockValue,
|
||||
licensesRepositoryMockValue,
|
||||
adb2cParam,
|
||||
sendgridMockValue,
|
||||
configMockValue,
|
||||
|
||||
@ -23,6 +23,7 @@ import {
|
||||
newUser,
|
||||
} from '../../repositories/users/entity/user.entity';
|
||||
import { UsersRepositoryService } from '../../repositories/users/users.repository.service';
|
||||
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
|
||||
import { GetRelationsResponse, User } from './types/types';
|
||||
import {
|
||||
AuthorIdAlreadyExistsError,
|
||||
@ -39,11 +40,16 @@ import {
|
||||
import { DateWithZeroTime } from '../licenses/types/types';
|
||||
import { Context } from '../../common/log';
|
||||
import { UserRoles } from '../../common/types/role';
|
||||
import {
|
||||
LicenseExpiredError,
|
||||
LicenseUnavailableError,
|
||||
} from '../../repositories/licenses/errors/types';
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(
|
||||
private readonly usersRepository: UsersRepositoryService,
|
||||
private readonly licensesRepository: LicensesRepositoryService,
|
||||
private readonly sortCriteriaRepository: SortCriteriaRepositoryService,
|
||||
private readonly adB2cService: AdB2cService,
|
||||
private readonly configService: ConfigService,
|
||||
@ -805,4 +811,51 @@ export class UsersService {
|
||||
this.logger.log(`[OUT] [${context.trackingId}] ${this.updateUser.name}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ライセンスをユーザーに割り当てます
|
||||
* @param context
|
||||
* @param userId
|
||||
* @param newLicenseId
|
||||
*/
|
||||
async allocateLicense(
|
||||
context: Context,
|
||||
userId: number,
|
||||
newLicenseId: number,
|
||||
): Promise<void> {
|
||||
this.logger.log(
|
||||
`[IN] [${context.trackingId}] ${this.allocateLicense.name} | params: { ` +
|
||||
`userId: ${userId}, ` +
|
||||
`newLicenseId: ${newLicenseId}, `,
|
||||
);
|
||||
|
||||
try {
|
||||
await this.licensesRepository.allocateLicense(userId, newLicenseId);
|
||||
} catch (e) {
|
||||
this.logger.error(`error=${e}`);
|
||||
if (e instanceof Error) {
|
||||
switch (e.constructor) {
|
||||
case LicenseExpiredError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010805'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
case LicenseUnavailableError:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E010806'),
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
default:
|
||||
throw new HttpException(
|
||||
makeErrorResponse('E009999'),
|
||||
HttpStatus.INTERNAL_SERVER_ERROR,
|
||||
);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.logger.log(
|
||||
`[OUT] [${context.trackingId}] ${this.allocateLicense.name}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -507,16 +507,16 @@ export class AccountsRepositoryService {
|
||||
|
||||
// 戻り値用の値を設定
|
||||
const childPartnerLicenseFromRepository: PartnerLicenseInfoForRepository =
|
||||
{
|
||||
accountId: childAccount.id,
|
||||
tier: childAccount.tier,
|
||||
companyName: childAccount.company_name,
|
||||
stockLicense: childLicenseOrderStatus.stockLicense,
|
||||
issuedRequested: childLicenseOrderStatus.issuedRequested,
|
||||
issueRequesting: childLicenseOrderStatus.issueRequesting,
|
||||
expiringSoonLicense: expiringSoonLicense,
|
||||
allocatableLicenseWithMargin: allocatableLicenseWithMargin,
|
||||
};
|
||||
{
|
||||
accountId: childAccount.id,
|
||||
tier: childAccount.tier,
|
||||
companyName: childAccount.company_name,
|
||||
stockLicense: childLicenseOrderStatus.stockLicense,
|
||||
issuedRequested: childLicenseOrderStatus.issuedRequested,
|
||||
issueRequesting: childLicenseOrderStatus.issueRequesting,
|
||||
expiringSoonLicense: expiringSoonLicense,
|
||||
allocatableLicenseWithMargin: allocatableLicenseWithMargin,
|
||||
};
|
||||
|
||||
childPartnerLicensesFromRepository.push(
|
||||
childPartnerLicenseFromRepository,
|
||||
|
||||
@ -13,3 +13,8 @@ export class OrderNotFoundError extends Error {}
|
||||
export class AlreadyIssuedError extends Error {}
|
||||
// ライセンス不足エラー
|
||||
export class LicensesShortageError extends Error {}
|
||||
|
||||
// ライセンス有効期限切れエラー
|
||||
export class LicenseExpiredError extends Error {}
|
||||
// ライセンス割り当て不可エラー
|
||||
export class LicenseUnavailableError extends Error {}
|
||||
|
||||
@ -21,7 +21,10 @@ import {
|
||||
LicensesShortageError,
|
||||
AlreadyIssuedError,
|
||||
OrderNotFoundError,
|
||||
LicenseExpiredError,
|
||||
LicenseUnavailableError,
|
||||
} from './errors/types';
|
||||
import { NewAllocatedLicenseExpirationDate } from '../../features/licenses/types/types';
|
||||
|
||||
@Injectable()
|
||||
export class LicensesRepositoryService {
|
||||
@ -387,4 +390,64 @@ export class LicensesRepositoryService {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* ライセンスをユーザーに割り当てる
|
||||
* @param userId
|
||||
* @param newLicenseId
|
||||
*/
|
||||
async allocateLicense(userId: number, newLicenseId: number): Promise<void> {
|
||||
await this.dataSource.transaction(async (entityManager) => {
|
||||
// 割り当て対象のライセンス情報を取得
|
||||
const licenseRepo = entityManager.getRepository(License);
|
||||
const targetLicense = await licenseRepo.findOne({
|
||||
where: {
|
||||
id: newLicenseId,
|
||||
},
|
||||
});
|
||||
|
||||
// 期限切れの場合はエラー
|
||||
if (targetLicense.expiry_date) {
|
||||
const currentDay = new Date();
|
||||
currentDay.setHours(23, 59, 59, 999);
|
||||
if (targetLicense.expiry_date < currentDay) {
|
||||
throw new LicenseExpiredError(
|
||||
`License is expired. expiration date: ${targetLicense.expiry_date} current Date: ${currentDay}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
// ライセンス状態が「未割当」「再利用可能」以外の場合はエラー
|
||||
if (
|
||||
targetLicense.status === LICENSE_ALLOCATED_STATUS.ALLOCATED ||
|
||||
targetLicense.status === LICENSE_ALLOCATED_STATUS.DELETED
|
||||
) {
|
||||
throw new LicenseUnavailableError(
|
||||
`License is unavailable. License status: ${targetLicense.status}`,
|
||||
);
|
||||
}
|
||||
|
||||
// 対象ユーザーのライセンス割り当て状態を取得
|
||||
const allocatedLicense = await licenseRepo.findOne({
|
||||
where: {
|
||||
allocated_user_id: userId,
|
||||
},
|
||||
});
|
||||
|
||||
// 既にライセンスが割り当てられているなら、割り当てを解除
|
||||
if (allocatedLicense) {
|
||||
allocatedLicense.status = LICENSE_ALLOCATED_STATUS.REUSABLE;
|
||||
allocatedLicense.allocated_user_id = null;
|
||||
await licenseRepo.save(allocatedLicense);
|
||||
}
|
||||
|
||||
// ライセンス割り当てを実施
|
||||
targetLicense.status = LICENSE_ALLOCATED_STATUS.ALLOCATED;
|
||||
targetLicense.allocated_user_id = userId;
|
||||
// 有効期限が未設定なら365日後に設定
|
||||
if (!targetLicense.expiry_date) {
|
||||
targetLicense.expiry_date = new NewAllocatedLicenseExpirationDate();
|
||||
}
|
||||
await licenseRepo.save(targetLicense);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user