Merged PR 277: API実装(注文履歴取得API)

## 概要
[Task2261: API実装(注文履歴取得API)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2261)

- 何をどう変更したか、追加したライブラリなど
- このPull Requestでの対象/対象外
- 影響範囲(他の機能にも影響があるか)

## レビューポイント
なし

## UIの変更

## 動作確認状況
-ローカルとポストマンにて確認
モック動作確認は1つのAPIから値を返す単純なもののため行わず。

## 補足
This commit is contained in:
水本 祐希 2023-08-01 10:10:58 +00:00
parent 6fc9a2db64
commit 3ffa45e179
7 changed files with 310 additions and 28 deletions

View File

@ -38,7 +38,6 @@ import { RoleGuard } from '../../common/guards/role/roleguards';
import { retrieveAuthorizationToken } from '../../common/http/helper';
import { AccessToken } from '../../common/token';
import jwt from 'jsonwebtoken';
import { LicenseHistory } from '../../repositories/licenses/entity/license.entity';
@ApiTags('accounts')
@Controller('accounts')
@ -360,26 +359,9 @@ export class AccountsController {
): Promise<GetOrderHistoriesResponce> {
const { limit, offset, accountId } = body;
// XXX Task2261で本実装する
const orderHistories: LicenseOrder[] = [];
const orderHistory: LicenseOrder = {
orderDate: '2023/01/01',
issueDate: '2023/01/01',
numberOfOrder: 50,
poNumber: 'PO001',
status: 'Issue Requesting',
};
orderHistories.push(orderHistory);
const getOrderHistoriesResponce = new GetOrderHistoriesResponce();
getOrderHistoriesResponce.total = 1;
getOrderHistoriesResponce.orderHistories = orderHistories;
/* =
await this.accountService.getOrderHistoriesResponce(
limit,
offset,
accountId,
);
*/
const getOrderHistoriesResponce =
await this.accountService.getOrderHistories(limit, offset, accountId);
return getOrderHistoriesResponce;
}
}

View File

@ -1,5 +1,6 @@
import { Module } from '@nestjs/common';
import { UsersRepositoryModule } from '../../repositories/users/users.repository.module';
import { LicensesRepositoryModule } from '../../repositories/licenses/licenses.repository.module';
import { SendGridModule } from '../../gateways/sendgrid/sendgrid.module';
import { AccountsRepositoryModule } from '../../repositories/accounts/accounts.repository.module';
import { AccountsController } from './accounts.controller';
@ -11,6 +12,7 @@ import { UserGroupsRepositoryModule } from '../../repositories/user_groups/user_
imports: [
AccountsRepositoryModule,
UsersRepositoryModule,
LicensesRepositoryModule,
UserGroupsRepositoryModule,
SendGridModule,
AdB2cModule,

View File

@ -4,6 +4,7 @@ import {
makeAccountsServiceMock,
makeDefaultAccountsRepositoryMockValue,
makeDefaultAdB2cMockValue,
makeDefaultLicensesRepositoryMockValue,
makeDefaultSendGridlValue,
makeDefaultUserGroupsRepositoryMockValue,
makeDefaultUsersRepositoryMockValue,
@ -29,6 +30,8 @@ describe('AccountsService', () => {
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -36,6 +39,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
expect(await service.getLicenseSummary(accountId)).toEqual(
expectedAccountLisenceCounts,
@ -53,6 +57,8 @@ describe('AccountsService', () => {
accountsRepositoryMockValue.getLicenseSummaryInfo = null;
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -60,6 +66,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(service.getLicenseSummary(accountId)).rejects.toEqual(
new HttpException(
@ -79,6 +86,8 @@ describe('AccountsService', () => {
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -86,6 +95,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
expect(await service.getTypists(externalId)).toEqual([
{ id: 1, name: 'Typist1' },
@ -104,6 +114,8 @@ describe('AccountsService', () => {
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -111,6 +123,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(service.getTypists(externalId)).rejects.toEqual(
new HttpException(
@ -130,6 +143,8 @@ describe('AccountsService', () => {
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -137,6 +152,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(service.getTypists(externalId)).rejects.toEqual(
new HttpException(
@ -156,6 +172,8 @@ describe('AccountsService', () => {
makeDefaultUserGroupsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -163,6 +181,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
expect(await service.getTypistGroups(externalId)).toEqual([
@ -181,6 +200,8 @@ describe('AccountsService', () => {
makeDefaultUserGroupsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -188,6 +209,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(service.getTypistGroups(externalId)).rejects.toEqual(
@ -208,6 +230,8 @@ describe('AccountsService', () => {
userGroupsRepositoryMockValue.getUserGroups = new Error('DB failed');
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -215,6 +239,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(service.getTypistGroups(externalId)).rejects.toEqual(
@ -239,7 +264,8 @@ describe('AccountsService', () => {
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -247,6 +273,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
expect(
await service.createPartnerAccount(
@ -275,7 +302,8 @@ describe('AccountsService', () => {
accountsRepositoryMockValue.createAccount = new Error('DB failed');
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
@ -283,6 +311,7 @@ describe('AccountsService', () => {
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(
service.createPartnerAccount(
@ -368,7 +397,13 @@ describe('createPartnerAccount', () => {
await createLicense(source, childAccountId2);
// ライセンス注文を追加子1→親10ライセンス、子2→親5ライセンス
await createLicenseOrder(source, childAccountId1, parentAccountId, 10);
await createLicenseOrder(
source,
childAccountId1,
parentAccountId,
10,
'TEST222',
);
await createLicenseOrder(source, childAccountId2, parentAccountId, 5);
const service = module.get<AccountsService>(AccountsService);
@ -397,3 +432,120 @@ describe('createPartnerAccount', () => {
expect(responce.childrenPartnerLicenses[1].issueRequesting).toBe(5);
});
});
describe('getOrderHistories', () => {
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('指定したアカウントIDの注文履歴情報を取得できる', async () => {
const module = await makeTestingModule(source);
const targetAccountId = 10;
const targetParentAccountId = 14;
const now = new Date(); // 現在の時刻を取得
// 注文履歴情報の作成
await createLicenseOrder(
source,
targetAccountId,
targetParentAccountId,
10,
'TEST001',
new Date(now.getTime() + 60 * 60 * 1000),
);
await createLicenseOrder(
source,
targetAccountId,
targetParentAccountId,
10,
'TEST002',
new Date(now.getTime() + 60 * 60 * 1000 * 2),
);
await createLicenseOrder(
source,
targetAccountId,
targetParentAccountId,
10,
'TEST003',
new Date(now.getTime() + 60 * 60 * 1000 * 3),
);
await createLicenseOrder(
source,
targetAccountId,
targetParentAccountId,
10,
'TEST004',
new Date(now.getTime() + 60 * 60 * 1000 * 4),
);
await createLicenseOrder(
source,
targetAccountId,
targetParentAccountId,
10,
'TEST005',
new Date(now.getTime() + 60 * 60 * 1000 * 5),
);
const service = module.get<AccountsService>(AccountsService);
const accountId = targetAccountId;
const offset = 1;
const limit = 2;
const responce = await service.getOrderHistories(limit, offset, accountId);
expect(responce.total).toBe(5);
expect(responce.orderHistories[0].poNumber).toBe('TEST004');
expect(responce.orderHistories[1].poNumber).toBe('TEST003');
});
it('注文履歴情報の取得に失敗した場合、エラーとなる', async () => {
const limit = 0;
const offset = 0;
const accountId = 0;
const usersRepositoryMockValue = makeDefaultUsersRepositoryMockValue();
usersRepositoryMockValue.findTypistUsers = new Error();
const userGroupsRepositoryMockValue =
makeDefaultUserGroupsRepositoryMockValue();
const adb2cParam = makeDefaultAdB2cMockValue();
const accountsRepositoryMockValue =
makeDefaultAccountsRepositoryMockValue();
const configMockValue = makeDefaultConfigValue();
const sendGridMockValue = makeDefaultSendGridlValue();
const licensesRepositoryMockValue =
makeDefaultLicensesRepositoryMockValue();
licensesRepositoryMockValue.getLicenseOrderHistoryInfo = new Error(
'DB failed',
);
const service = await makeAccountsServiceMock(
accountsRepositoryMockValue,
usersRepositoryMockValue,
userGroupsRepositoryMockValue,
adb2cParam,
configMockValue,
sendGridMockValue,
licensesRepositoryMockValue,
);
await expect(
service.getOrderHistories(limit, offset, accountId),
).rejects.toEqual(
new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
),
);
});
});

View File

@ -20,6 +20,8 @@ import {
TypistGroup,
GetPartnerLicensesResponse,
PartnerLicenseInfo,
GetOrderHistoriesResponce,
LicenseOrder,
} from './types/types';
import { DateWithZeroTime } from '../licenses/types/types';
import { GetLicenseSummaryResponse, Typist } from './types/types';
@ -27,11 +29,12 @@ import { AccessToken } from '../../common/token';
import { UserNotFoundError } from '../../repositories/users/errors/types';
import { UserGroupsRepositoryService } from '../../repositories/user_groups/user_groups.repository.service';
import { makePassword } from '../../common/password';
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
@Injectable()
export class AccountsService {
constructor(
private readonly accountRepository: AccountsRepositoryService,
private readonly licensesRepository: LicensesRepositoryService,
private readonly usersRepository: UsersRepositoryService,
private readonly userGroupsRepository: UserGroupsRepositoryService,
private readonly adB2cService: AdB2cService,
@ -492,4 +495,59 @@ export class AccountsService {
this.logger.log(`[OUT] ${this.getPartnerLicenses.name}`);
}
}
/**
*
* @param limit
* @param offset
* @param accountId
* @returns getOrderHistoriesResponce
*/
async getOrderHistories(
limit: number,
offset: number,
accountId: number,
): Promise<GetOrderHistoriesResponce> {
this.logger.log(`[IN] ${this.getOrderHistories.name}`);
try {
const licenseHistoryInfo =
await this.licensesRepository.getLicenseOrderHistoryInfo(
accountId,
offset,
limit,
);
// 戻り値用に配列の詰めなおしを行う
const orderHistories: LicenseOrder[] = [];
for (const licenseOrder of licenseHistoryInfo.licenseOrders) {
const returnLicenseOrder: LicenseOrder = {
issueDate: new Date(licenseOrder.issued_at)
.toISOString()
.substr(0, 10)
.replace(/-/g, '/'),
numberOfOrder: licenseOrder.quantity,
orderDate: new Date(licenseOrder.ordered_at)
.toISOString()
.substr(0, 10)
.replace(/-/g, '/'),
poNumber: licenseOrder.po_number,
status: licenseOrder.status,
};
orderHistories.push(returnLicenseOrder);
}
const getOrderHistoriesResponse: GetOrderHistoriesResponce = {
total: licenseHistoryInfo.total,
orderHistories: orderHistories,
};
return getOrderHistoriesResponse;
} catch (e) {
this.logger.error(e);
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(`[OUT] ${this.getOrderHistories.name}`);
}
}
}

View File

@ -9,10 +9,18 @@ import {
ConflictError,
} from '../../../gateways/adb2c/adb2c.service';
import { SendGridService } from '../../../gateways/sendgrid/sendgrid.service';
import { Account, LicenseSummaryInfo } from '../types/types';
import { Account, LicenseOrder, LicenseSummaryInfo } from '../types/types';
import { UserGroup } from '../../../repositories/user_groups/entity/user_group.entity';
import { UserGroupsRepositoryService } from '../../../repositories/user_groups/user_groups.repository.service';
import { AdB2cUser } from '../../../gateways/adb2c/types/types';
import { LicensesRepositoryService } from '../../../repositories/licenses/licenses.repository.service';
export type LicensesRepositoryMockValue = {
getLicenseOrderHistoryInfo: {
total: number;
orderHistories: LicenseOrder[];
} | Error;
};
export type UsersRepositoryMockValue = {
findUserById: User | Error;
findUserByExternalId: User | Error;
@ -55,6 +63,7 @@ export const makeAccountsServiceMock = async (
adB2cMockValue: AdB2cMockValue,
configMockValue: ConfigMockValue,
sendGridMockValue: SendGridMockValue,
licensesRepositoryMockValue: LicensesRepositoryMockValue
): Promise<AccountsService> => {
const module: TestingModule = await Test.createTestingModule({
providers: [AccountsService],
@ -79,6 +88,8 @@ export const makeAccountsServiceMock = async (
return makeConfigMock(configMockValue);
case SendGridService:
return makeSendGridServiceMock(sendGridMockValue);
case LicensesRepositoryService:
return makeLicensesRepositoryMock(licensesRepositoryMockValue);
}
})
.compile();
@ -111,6 +122,24 @@ export const makeAccountsRepositoryMock = (
.mockResolvedValue(createAccount),
};
};
export const makeLicensesRepositoryMock = (value: LicensesRepositoryMockValue) => {
const { getLicenseOrderHistoryInfo } =
value;
return {
findUserById:
getLicenseOrderHistoryInfo instanceof Error
? jest
.fn<Promise<void>, []>()
.mockRejectedValue(getLicenseOrderHistoryInfo)
: jest
.fn<
Promise<{ total: number; orderHistories: LicenseOrder[] }>,
[]
>()
.mockResolvedValue(getLicenseOrderHistoryInfo),
};
};
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
const { findUserById, findUserByExternalId, findTypistUsers } = value;
@ -355,3 +384,19 @@ export const makeDefaultSendGridlValue = (): SendGridMockValue => {
},
};
};
export const makeDefaultLicensesRepositoryMockValue = (): LicensesRepositoryMockValue => {
return {
getLicenseOrderHistoryInfo: {
total: 100,
orderHistories: [
{
orderDate: '2023/04/01',
issueDate: '2023/04/01',
numberOfOrder: 10,
poNumber: 'PO001',
status:'Issued'
},
],
},
};
};

View File

@ -55,12 +55,14 @@ export const createLicenseOrder = async (
fromAccountId: number,
toAccountId: number,
quantity: number,
po_number = 'TEST123',
ordered_at = new Date(),
): Promise<void> => {
const { identifiers } = await datasource.getRepository(LicenseOrder).insert({
po_number: 'TEST123',
po_number: po_number,
from_account_id: fromAccountId,
to_account_id: toAccountId,
ordered_at: new Date(),
ordered_at: ordered_at,
issued_at: null,
quantity: quantity,
status: 'Issue Requesting',

View File

@ -255,4 +255,45 @@ export class LicensesRepositoryService {
});
return;
}
/**
* IDに紐づく注文履歴情報を取得する
* @param accountId
* @param offset
* @param limit
* @returns total
* @returns licenseOrders
*/
async getLicenseOrderHistoryInfo(
accountId: number,
offset: number,
limit: number,
): Promise<{
total: number;
licenseOrders: LicenseOrder[];
}> {
return await this.dataSource.transaction(async (entityManager) => {
const licenseOrder = entityManager.getRepository(LicenseOrder);
// limit/offsetによらない総件数を取得する
const total = await licenseOrder.count({
where: {
from_account_id: accountId,
},
});
const licenseOrders = await licenseOrder.find({
where: {
from_account_id: accountId,
},
order: {
ordered_at: 'DESC',
},
take: limit,
skip: offset,
});
return {
total: total,
licenseOrders: licenseOrders,
};
});
}
}