Merged PR 501: strictNullChecks修正①(accounts,auth,Repositoiesのaccounts,common)

## 概要
[Task2835: 修正①(accounts,auth,Repositoiesのaccounts,common)](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2835)

- features
  - accounts
  - auth
- common
- repositories
  - accounts
- 各entity
  - Nullableの項目の`@Column`デコレータに`type`を追加しないとTypeORMがエラーになりテストが通らないので追加
    - https://qiita.com/maruware/items/08c9ad594e14e4ea1497#%E5%95%8F%E9%A1%8C

## レビューポイント
- コメントとして記載

## UIの変更
- Before/Afterのスクショなど
- スクショ置き場

## 動作確認状況
- ローカルで確認、develop環境で確認など

## 補足
- レビュー完了後、TODOコメント(strictNullChecks対応)は削除します
This commit is contained in:
saito.k 2023-10-19 07:13:56 +00:00
parent 9323cd02e4
commit f553bfc95b
46 changed files with 1291 additions and 571 deletions

View File

@ -92,13 +92,15 @@ export class RoleGuard implements CanActivate {
* @returns true/false
*/
checkRole(role: string): boolean {
const { roles } = this.settings;
const settings = this.settings;
if (!settings || !settings.roles) {
return true;
}
const userRoles = role.split(' ');
// Role毎にAccessTokenの権限チェックを行う
for (let i = 0; i < roles.length; i++) {
const role = roles[i];
for (let i = 0; i < settings.roles.length; i++) {
const role = settings.roles[i];
let isValid = false;
if (Array.isArray(role)) {
isValid = role.every((x) => userRoles.includes(x));
@ -172,9 +174,12 @@ export class RoleGuard implements CanActivate {
* @returns true/false
*/
checkTier(tier: number): boolean {
const { tiers } = this.settings;
const settings = this.settings;
if (!settings || !settings.tiers) {
return true;
}
// 宣言された階層中にパラメータの内容が含まれていればtrue
return tiers.includes(tier as (typeof TIERS)[keyof typeof TIERS]);
return settings.tiers.includes(tier as (typeof TIERS)[keyof typeof TIERS]);
}
}

View File

@ -15,10 +15,9 @@ export const makePassword = (): string => {
// autoGeneratedPasswordが以上の条件を満たせばvalidがtrueになる
let valid = false;
let autoGeneratedPassword: string;
let autoGeneratedPassword: string = '';
while (!valid) {
autoGeneratedPassword = '';
// パスワードをランダムに決定
while (autoGeneratedPassword.length < passLength) {
// 上で決定したcharsの中からランダムに1文字ずつ追加

View File

@ -58,11 +58,11 @@ export const makeHierarchicalAccounts = async (
}
// 第2階層を作成
{
const { account: tier1 } = state.tier1Accounts.slice().shift();
const tier1 = state.tier1Accounts.slice().shift();
{
const { account, admin } = await makeTestAccount(datasource, {
tier: 2,
parent_account_id: tier1.id,
parent_account_id: tier1?.account.id,
company_name: 'OMDS_US',
});
state.tier2Accounts.push({
@ -73,7 +73,7 @@ export const makeHierarchicalAccounts = async (
{
const { account, admin } = await makeTestAccount(datasource, {
tier: 2,
parent_account_id: tier1.id,
parent_account_id: tier1?.account.id,
company_name: 'OMDS_EU',
});
state.tier2Accounts.push({
@ -202,7 +202,7 @@ export const makeTestAccount = async (
}
// Accountの管理者を設定する
let secondaryAdminUserId = null;
let secondaryAdminUserId: number | null = null;
if (isPrimaryAdminNotExist && !isSecondaryAdminNotExist) {
secondaryAdminUserId = userId;
}
@ -225,6 +225,9 @@ export const makeTestAccount = async (
id: userId,
},
});
if (!account || !admin) {
throw new Error('Unexpected null');
}
return {
account: account,
@ -264,7 +267,9 @@ export const makeTestSimpleAccount = async (
id: result.id,
},
});
if (!account) {
throw new Error('Unexpected null');
}
return account;
};
@ -300,11 +305,15 @@ export const makeTestUser = async (
});
const result = identifiers.pop() as User;
return await datasource.getRepository(User).findOne({
const user = await datasource.getRepository(User).findOne({
where: {
id: result.id,
},
});
if (!user) {
throw new Error('Unexpected null');
}
return user;
};
/**
@ -313,7 +322,10 @@ export const makeTestUser = async (
* @param id ID
* @returns
*/
export const getAccount = async (dataSource: DataSource, id: number) => {
export const getAccount = async (
dataSource: DataSource,
id: number,
): Promise<Account | null> => {
return await dataSource.getRepository(Account).findOne({
where: { id: id },
});
@ -354,7 +366,7 @@ export const getUserFromExternalId = async (
export const getUser = async (
datasource: DataSource,
id: number,
): Promise<User> => {
): Promise<User | null> => {
const user = await datasource.getRepository(User).findOne({
where: {
id: id,

View File

@ -244,7 +244,7 @@ export const OPTION_ITEM_VALUE_TYPE = {
* @const {string[]}
*/
export const ADB2C_SIGN_IN_TYPE = {
EAMILADDRESS: 'emailAddress',
EMAILADDRESS: 'emailAddress',
} as const;
/**

View File

@ -200,14 +200,26 @@ export class AccountsController {
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get('me')
async getMyAccount(@Req() req: Request): Promise<GetMyAccountResponse> {
// アクセストークン取得
const accessToken = retrieveAuthorizationToken(req);
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
const context = makeContext(payload.userId);
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
//アカウントID取得処理
const accountInfo = await this.accountService.getAccountInfo(
context,
payload.userId,
userId,
);
return accountInfo;
}
@ -237,8 +249,21 @@ export class AccountsController {
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get('authors')
async getAuthors(@Req() req: Request): Promise<GetAuthorsResponse> {
const accessToken = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
const authors = await this.accountService.getAuthors(context, userId);
@ -270,10 +295,23 @@ export class AccountsController {
@UseGuards(AuthGuard)
@Get('typists')
async getTypists(@Req() req: Request): Promise<GetTypistsResponse> {
const accessToken = retrieveAuthorizationToken(req);
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const typists = await this.accountService.getTypists(payload.userId);
const typists = await this.accountService.getTypists(userId);
return { typists };
}
@ -302,12 +340,23 @@ export class AccountsController {
@UseGuards(AuthGuard)
@Get('typist-groups')
async getTypistGroups(@Req() req: Request): Promise<GetTypistGroupsResponse> {
const accessToken = retrieveAuthorizationToken(req);
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const typistGroups = await this.accountService.getTypistGroups(
payload.userId,
);
const typistGroups = await this.accountService.getTypistGroups(userId);
return { typistGroups };
}
@ -348,8 +397,22 @@ export class AccountsController {
const { typistGroupId } = param;
// アクセストークン取得
const accessToken = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -397,8 +460,22 @@ export class AccountsController {
): Promise<CreateTypistGroupResponse> {
const { typistGroupName, typistIds } = body;
// アクセストークン取得
const accessToken = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
await this.accountService.createTypistGroup(
context,
@ -447,8 +524,22 @@ export class AccountsController {
const { typistGroupId } = param;
// アクセストークン取得
const accessToken = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(accessToken, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -498,10 +589,24 @@ export class AccountsController {
@Body() body: CreatePartnerAccountRequest,
): Promise<CreatePartnerAccountResponse> {
const { companyName, country, email, adminName } = body;
const accessToken = retrieveAuthorizationToken(req);
const payload = jwt.decode(accessToken, { json: true }) as AccessToken;
const context = makeContext(payload.userId);
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId, tier } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
await this.accountService.createPartnerAccount(
context,
@ -509,8 +614,8 @@ export class AccountsController {
country,
email,
adminName,
payload.userId,
payload.tier,
userId,
tier,
);
return {};
@ -626,15 +731,28 @@ export class AccountsController {
): Promise<IssueLicenseResponse> {
const { orderedAccountId, poNumber } = body;
const token = retrieveAuthorizationToken(req);
const accessToken = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId, tier } = decodedAccessToken as AccessToken;
const context = makeContext(accessToken.userId);
const context = makeContext(userId);
await this.accountService.issueLicense(
context,
orderedAccountId,
accessToken.userId,
accessToken.tier,
userId,
tier,
poNumber,
);
return {};
@ -694,14 +812,27 @@ export class AccountsController {
@Req() req: Request,
@Body() body: CancelIssueRequest,
): Promise<CancelIssueResponse> {
const token = retrieveAuthorizationToken(req);
const payload = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(payload.userId);
const context = makeContext(userId);
await this.accountService.cancelIssue(
context,
payload.userId,
userId,
body.poNumber,
body.orderedAccountId,
);
@ -729,8 +860,21 @@ export class AccountsController {
@UseGuards(AuthGuard)
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
async getWorktypes(@Req() req: Request): Promise<GetWorktypesResponse> {
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
const worktypes = await this.accountService.getWorktypes(context, userId);
@ -768,8 +912,22 @@ export class AccountsController {
@Body() body: CreateWorktypesRequest,
): Promise<CreateWorktypeResponse> {
const { worktypeId, description } = body;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
await this.accountService.createWorktype(
@ -814,8 +972,22 @@ export class AccountsController {
): Promise<UpdateWorktypeResponse> {
const { worktypeId, description } = body;
const { id } = param;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -860,8 +1032,22 @@ export class AccountsController {
@Param() param: DeleteWorktypeRequestParam,
): Promise<DeleteWorktypeResponse> {
const { id } = param;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -899,8 +1085,22 @@ export class AccountsController {
@Param() param: GetOptionItemsRequestParam,
): Promise<GetOptionItemsResponse> {
const { id } = param;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -945,8 +1145,22 @@ export class AccountsController {
): Promise<UpdateOptionItemsResponse> {
const { optionItems } = body;
const { id } = param;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -990,8 +1204,22 @@ export class AccountsController {
@Body() body: PostActiveWorktypeRequest,
): Promise<PostActiveWorktypeResponse> {
const { id } = body;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
@ -1034,8 +1262,22 @@ export class AccountsController {
@Query() query: GetPartnersRequest,
): Promise<GetPartnersResponse> {
const { limit, offset } = query;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
const response = await this.accountService.getPartners(
@ -1087,8 +1329,22 @@ export class AccountsController {
primaryAdminUserId,
secondryAdminUserId,
} = body;
const token = retrieveAuthorizationToken(req);
const { userId, tier } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId, tier } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
await this.accountService.updateAccountInfo(
@ -1101,7 +1357,7 @@ export class AccountsController {
secondryAdminUserId,
);
return;
return {};
}
@Post('/delete')
@ -1133,12 +1389,26 @@ export class AccountsController {
@Body() body: DeleteAccountRequest,
): Promise<DeleteAccountResponse> {
const { accountId } = body;
const token = retrieveAuthorizationToken(req);
const { userId } = jwt.decode(token, { json: true }) as AccessToken;
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
makeErrorResponse('E000107'),
HttpStatus.UNAUTHORIZED,
);
}
const decodedAccessToken = jwt.decode(accessToken, { json: true });
if (!decodedAccessToken) {
throw new HttpException(
makeErrorResponse('E000101'),
HttpStatus.UNAUTHORIZED,
);
}
const { userId } = decodedAccessToken as AccessToken;
const context = makeContext(userId);
await this.accountService.deleteAccountAndData(context, userId, accountId);
return;
return {};
}
@Post('/minimal-access')

View File

@ -33,6 +33,7 @@ import {
GetPartnersResponse,
PostWorktypeOptionItem,
Author,
Partner,
} from './types/types';
import {
DateWithZeroTime,
@ -72,6 +73,8 @@ import {
@Injectable()
export class AccountsService {
private readonly mailFrom =
this.configService.getOrThrow<string>('MAIL_FROM');
constructor(
private readonly accountRepository: AccountsRepositoryService,
private readonly licensesRepository: LicensesRepositoryService,
@ -257,9 +260,6 @@ export class AccountsService {
}
try {
// メールの送信元を取得
const from = this.configService.get<string>('MAIL_FROM') ?? '';
// メールの内容を構成
const { subject, text, html } =
await this.sendgridService.createMailContentFromEmailConfirm(
@ -273,7 +273,7 @@ export class AccountsService {
await this.sendgridService.sendMail(
context,
email,
from,
this.mailFrom,
subject,
text,
html,
@ -394,7 +394,7 @@ export class AccountsService {
userInfo.account_id,
);
let parentInfo: Account;
let parentInfo: Account | undefined;
if (accountInfo.parent_account_id) {
parentInfo = await this.accountRepository.findAccountById(
accountInfo.parent_account_id,
@ -481,14 +481,20 @@ export class AccountsService {
const { account_id } = await this.usersRepository.findUserByExternalId(
externalId,
);
const userGroup = await this.userGroupsRepository.getTypistGroup(
account_id,
typistGroupId,
);
const { name, userGroupMembers } =
await this.userGroupsRepository.getTypistGroup(
account_id,
typistGroupId,
);
if (!userGroupMembers) {
throw new TypistGroupNotExistError(
`Typist Group is not exist. typistGroupId: ${typistGroupId}`,
);
}
return {
typistGroupName: userGroup.name,
typistIds: userGroup.userGroupMembers.map((x) => x.user_id),
typistGroupName: name,
typistIds: userGroupMembers.map((x) => x.user_id),
};
} catch (e) {
this.logger.error(`error=${e}`);
@ -541,6 +547,11 @@ export class AccountsService {
const typists = typistUsers.map((x) => {
const user = adb2cUsers.find((adb2c) => adb2c.id === x.external_id);
if (!user) {
throw new Error(
`user not found. externalId: ${x.external_id}, userId: ${x.id}`,
);
}
return {
id: x.id,
name: user.displayName,
@ -586,6 +597,11 @@ export class AccountsService {
);
const authors = authorUsers.map((x) => {
if (!x.author_id) {
throw new Error(
`author_id is Not Found. externalId: ${x.external_id}, userId: ${x.id}`,
);
}
return {
id: x.id,
authorId: x.author_id,
@ -701,8 +717,8 @@ export class AccountsService {
creatorAccountTier + 1,
externalUser.sub,
USER_ROLES.NONE,
null,
null,
undefined,
undefined,
);
account = newAccount;
user = adminUser;
@ -743,7 +759,6 @@ export class AccountsService {
}
try {
const from = this.configService.get<string>('MAIL_FROM') || '';
const { subject, text, html } =
await this.sendgridService.createMailContentFromEmailConfirmForNormalUser(
account.id,
@ -753,7 +768,7 @@ export class AccountsService {
await this.sendgridService.sendMail(
context,
email,
from,
this.mailFrom,
subject,
text,
html,
@ -836,11 +851,19 @@ export class AccountsService {
// 各子アカウントのShortageを算出してreturn用の変数にマージする
const childrenPartnerLicenses: PartnerLicenseInfo[] = [];
for (const childPartnerLicenseFromRepository of getPartnerLicenseResult.childPartnerLicensesFromRepository) {
let childShortage;
const { allocatableLicenseWithMargin, expiringSoonLicense } =
childPartnerLicenseFromRepository;
let childShortage: number = 0;
if (childPartnerLicenseFromRepository.tier === TIERS.TIER5) {
childShortage =
childPartnerLicenseFromRepository.allocatableLicenseWithMargin -
childPartnerLicenseFromRepository.expiringSoonLicense;
if (
allocatableLicenseWithMargin === undefined ||
expiringSoonLicense === undefined
) {
throw new Error(
`Tier5 account has no allocatableLicenseWithMargin or expiringSoonLicense. accountId: ${accountId}`,
);
}
childShortage = allocatableLicenseWithMargin - expiringSoonLicense;
} else {
childShortage =
childPartnerLicenseFromRepository.stockLicense -
@ -907,13 +930,13 @@ export class AccountsService {
licenseOrder.issued_at !== null
? new Date(licenseOrder.issued_at)
.toISOString()
.substr(0, 10)
.substring(0, 10)
.replace(/-/g, '/')
: null,
: undefined,
numberOfOrder: licenseOrder.quantity,
orderDate: new Date(licenseOrder.ordered_at)
.toISOString()
.substr(0, 10)
.substring(0, 10)
.replace(/-/g, '/'),
poNumber: licenseOrder.po_number,
status: licenseOrder.status,
@ -1634,7 +1657,7 @@ export class AccountsService {
async updateActiveWorktype(
context: Context,
externalId: string,
id: number,
id: number | undefined,
): Promise<void> {
this.logger.log(
`[IN] [${context.trackingId}] ${this.updateActiveWorktype.name} | params: { ` +
@ -1701,34 +1724,40 @@ export class AccountsService {
const { account_id: accountId } =
await this.usersRepository.findUserByExternalId(externalId);
const partners = await this.accountRepository.getPartners(
const partnersRecords = await this.accountRepository.getPartners(
accountId,
limit,
offset,
);
// DBから取得したユーザーの外部IDをもとにADB2Cからユーザーを取得する
let externalIds = partners.partnersInfo.map(
let externalIds = partnersRecords.partnersInfo.map(
(x) => x.primaryAccountExternalId,
);
externalIds = externalIds.filter((item) => item !== undefined);
const adb2cUsers = await this.adB2cService.getUsers(context, externalIds);
// DBから取得した情報とADB2Cから取得した情報をマージ
const response = partners.partnersInfo.map((db) => {
const partners = partnersRecords.partnersInfo.map((db): Partner => {
const adb2cUser = adb2cUsers.find(
(adb2c) => db.primaryAccountExternalId === adb2c.id,
);
let primaryAdmin = undefined;
let mail = undefined;
if (adb2cUser) {
primaryAdmin = adb2cUser.displayName;
mail = adb2cUser.identities.find(
(identity) =>
identity.signInType === ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
).issuerAssignedId;
if (!adb2cUser) {
throw new Error(
`adb2c user not found. externalId: ${db.primaryAccountExternalId}`,
);
}
const primaryAdmin = adb2cUser.displayName;
const mail = adb2cUser.identities?.find(
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
)?.issuerAssignedId;
if (!mail) {
throw new Error(
`adb2c user mail not found. externalId: ${db.primaryAccountExternalId}`,
);
}
return {
name: db.name,
tier: db.tier,
@ -1741,17 +1770,15 @@ export class AccountsService {
});
return {
total: partners.total,
partners: response,
total: partnersRecords.total,
partners: partners,
};
} catch (e) {
this.logger.error(`error=${e}`);
if (e instanceof Error) {
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
}
throw new HttpException(
makeErrorResponse('E009999'),
HttpStatus.INTERNAL_SERVER_ERROR,
);
} finally {
this.logger.log(`[OUT] [${context.trackingId}] ${this.getPartners.name}`);
}

View File

@ -30,7 +30,7 @@ export type LicensesRepositoryMockValue = {
orderHistories: LicenseOrder[];
}
| Error;
issueLicense: undefined | Error;
issueLicense: void | Error;
};
export type UsersRepositoryMockValue = {
findUserById: User | Error;
@ -61,10 +61,12 @@ export type ConfigMockValue = {
get: string | Error;
};
export type AccountsRepositoryMockValue = {
getLicenseSummaryInfo: {
licenseSummary: LicenseSummaryInfo;
isStorageAvailable: boolean;
};
getLicenseSummaryInfo:
| {
licenseSummary: LicenseSummaryInfo;
isStorageAvailable: boolean;
}
| Error;
createAccount: { newAccount: Account; adminUser: User } | Error;
};
@ -181,18 +183,7 @@ export const makeLicensesRepositoryMock = (
issueLicense:
issueLicense instanceof Error
? jest.fn<Promise<void>, []>().mockRejectedValue(issueLicense)
: jest
.fn<
Promise<{
context: Context;
orderedAccountId: number;
myAccountId: number;
tier: number;
poNumber: string;
}>,
[]
>()
.mockResolvedValue(issueLicense),
: jest.fn<Promise<void>, []>().mockResolvedValue(issueLicense),
};
};
export const makeUsersRepositoryMock = (value: UsersRepositoryMockValue) => {
@ -355,7 +346,7 @@ export const makeDefaultAccountsRepositoryMockValue =
user.created_by = 'test';
user.created_at = new Date();
user.updated_by = null;
user.updated_at = null;
user.updated_at = new Date();
return {
getLicenseSummaryInfo: {
licenseSummary: licenseSummaryInfo,
@ -385,7 +376,7 @@ export const makeDefaultUsersRepositoryMockValue =
user.created_by = 'test';
user.created_at = new Date();
user.updated_by = null;
user.updated_at = null;
user.updated_at = new Date();
const typists: User[] = [];
typists.push(
@ -434,7 +425,7 @@ export const makeDefaultUserGroupsRepositoryMockValue =
user.created_by = 'test';
user.created_at = new Date();
user.updated_by = null;
user.updated_at = null;
user.updated_at = new Date();
return {
getUserGroups: [

View File

@ -23,14 +23,14 @@ export const getSortCriteria = async (dataSource: DataSource) => {
export const createLicense = async (
datasource: DataSource,
licenseId: number,
expiry_date: Date,
expiry_date: Date | null,
accountId: number,
type: string,
status: string,
allocated_user_id: number,
order_id: number,
deleted_at: Date,
delete_order_id: number,
allocated_user_id: number | null,
order_id: number | null,
deleted_at: Date | null,
delete_order_id: number | null,
): Promise<void> => {
const { identifiers } = await datasource.getRepository(License).insert({
id: licenseId,
@ -54,7 +54,7 @@ export const createLicense = async (
export const createLicenseSetExpiryDateAndStatus = async (
datasource: DataSource,
accountId: number,
expiryDate: Date,
expiryDate: Date | null,
status: string,
): Promise<void> => {
const { identifiers } = await datasource.getRepository(License).insert({
@ -171,19 +171,21 @@ export const createOptionItems = async (
datasource: DataSource,
worktypeId: number,
): Promise<OptionItem[]> => {
const optionItems = [];
const optionItems: OptionItem[] = [];
for (let i = 0; i < 10; i++) {
optionItems.push({
worktype_id: worktypeId,
item_label: '',
default_value_type: OPTION_ITEM_VALUE_TYPE.DEFAULT,
initial_value: '',
created_by: 'test_runner',
created_at: new Date(),
updated_by: 'updater',
updated_at: new Date(),
});
const optionItem = new OptionItem();
{
optionItem.worktype_id = worktypeId;
optionItem.item_label = '';
optionItem.default_value_type = OPTION_ITEM_VALUE_TYPE.DEFAULT;
optionItem.initial_value = '';
optionItem.created_by = 'test_runner';
optionItem.created_at = new Date();
optionItem.updated_by = 'updater';
optionItem.updated_at = new Date();
}
optionItems.push(optionItem);
}
await datasource.getRepository(OptionItem).insert(optionItems);

View File

@ -327,8 +327,8 @@ export class GetOrderHistoriesRequest {
export class LicenseOrder {
@ApiProperty({ description: '注文日付' })
orderDate: string;
@ApiProperty({ description: '発行日付' })
issueDate: string;
@ApiProperty({ description: '発行日付', required: false })
issueDate?: string;
@ApiProperty({ description: '注文数' })
numberOfOrder: number;
@ApiProperty({ description: 'POナンバー' })

View File

@ -5,12 +5,19 @@ import {
makeAdB2cServiceMock,
makeDefaultAdB2cMockValue,
} from './test/auth.service.mock';
import { ConfigModule } from '@nestjs/config';
describe('AuthController', () => {
let controller: AuthController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
envFilePath: ['.env.local', '.env'],
isGlobal: true,
}),
],
controllers: [AuthController],
providers: [AuthService],
})

View File

@ -3,6 +3,7 @@ import { makeErrorResponse } from '../../common/error/makeErrorResponse';
import {
makeAuthServiceMock,
makeDefaultAdB2cMockValue,
makeDefaultConfigValue,
makeDefaultGetPublicKeyFromJwk,
} from './test/auth.service.mock';
import { DataSource } from 'typeorm';
@ -16,7 +17,8 @@ import { v4 as uuidv4 } from 'uuid';
describe('AuthService', () => {
it('IDトークンの検証とペイロードの取得に成功する', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const service = await makeAuthServiceMock(adb2cParam);
const configMockValue = makeDefaultConfigValue();
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
const token =
@ -27,7 +29,8 @@ describe('AuthService', () => {
it('IDトークンの形式が不正な場合、形式不正エラーとなる。', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const service = await makeAuthServiceMock(adb2cParam);
const configMockValue = makeDefaultConfigValue();
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
const token = 'invalid.id.token';
await expect(service.getVerifiedIdToken(token)).rejects.toEqual(
@ -37,7 +40,8 @@ describe('AuthService', () => {
it('IDトークンの有効期限が切れている場合、有効期限切れエラーとなる。', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const service = await makeAuthServiceMock(adb2cParam);
const configMockValue = makeDefaultConfigValue();
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
const token =
@ -50,7 +54,8 @@ describe('AuthService', () => {
it('IDトークンが開始日より前の場合、開始前エラーとなる。', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const service = await makeAuthServiceMock(adb2cParam);
const configMockValue = makeDefaultConfigValue();
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
const token =
@ -63,7 +68,8 @@ describe('AuthService', () => {
it('IDトークンの署名が不正な場合、署名不正エラーとなる。', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const service = await makeAuthServiceMock(adb2cParam);
const configMockValue = makeDefaultConfigValue();
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdXNlciIsInN1YiI6InN1YiIsImF1ZCI6ImF1ZCIsIm5vbmNlIjoiZGVmYXVsdE5vbmNlIiwiaWF0IjoxMDAwMDAwMDAwLCJhdXRoX3RpbWUiOjEwMDAwMDAwMDAsImVtYWlscyI6WyJ4eHhAeHguY29tIl0sInRmcCI6InNpZ25pbl91c2VyZmxvdyJ9.sign';
@ -74,7 +80,8 @@ describe('AuthService', () => {
it('IDトークンの発行元が想定と異なる場合、発行元不正エラーとなる。', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const service = await makeAuthServiceMock(adb2cParam);
const configMockValue = makeDefaultConfigValue();
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
//JWKの生成→PEM変換を自力で表現することが厳しいためMockで代替
service.getPublicKeyFromJwk = makeDefaultGetPublicKeyFromJwk;
const token =
@ -87,8 +94,9 @@ describe('AuthService', () => {
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。メタデータ', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const configMockValue = makeDefaultConfigValue();
adb2cParam.getMetaData = new Error('failed get metadata');
const service = await makeAuthServiceMock(adb2cParam);
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
@ -101,8 +109,9 @@ describe('AuthService', () => {
});
it('Azure ADB2Cでネットワークエラーとなる場合、エラーとなる。キーセット', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const configMockValue = makeDefaultConfigValue();
adb2cParam.getSignKeySets = new Error('failed get keyset');
const service = await makeAuthServiceMock(adb2cParam);
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
@ -116,10 +125,11 @@ describe('AuthService', () => {
it('Azure ADB2Cから取得した鍵が一致しない場合、エラーとなる。', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
const configMockValue = makeDefaultConfigValue();
adb2cParam.getSignKeySets = [
{ kid: 'invalid', kty: 'RSA', nbf: 0, use: 'sig', e: '', n: '' },
];
const service = await makeAuthServiceMock(adb2cParam);
const service = await makeAuthServiceMock(adb2cParam, configMockValue);
const token =
'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtpZCJ9.eyJleHAiOjkwMDAwMDAwMDAsIm5iZiI6MTAwMDAwMDAwMCwidmVyIjoiMS4wIiwiaXNzIjoiaXNzdWVyIiwic3ViIjoic3ViIiwiYXVkIjoiYXVkIiwibm9uY2UiOiJkZWZhdWx0Tm9uY2UiLCJpYXQiOjEwMDAwMDAwMDAsImF1dGhfdGltZSI6MTAwMDAwMDAwMCwiZW1haWxzIjpbInh4eEB4eC5jb20iXSwidGZwIjoic2lnbmluX3VzZXJmbG93In0.RyieW-VHsHPQOjXbbhRc307AYJOc1sq2hrcu4SW1-K0pvLlkplepxvx02a3vCwQrnBYrIP5w6HExG-S_JgW5nYyWr6DeY11mA484n9KA8GeAcAXV37StH1gfWUJvfGb4C8BaMbMM9Ix4Z9NGwKA9vjNwevfmBZnz9lQUePgv6BJNmyvCt8ElJ01O-1WODbZuojJ4xXymA1OqluzfbphPOsqWTSNmTn0emkLjjnlMQf1iwM4C_kvvr8dUCFg0_UGDfQVJnzPEKB38UqnhLnC5WacrddDwQ0kBuGKZgZ_63Q_7fOvqAZivqLK7BPmbPxi6mx3R1S9Eq2ugzpY1LfJOjA';
@ -133,7 +143,7 @@ describe('AuthService', () => {
});
describe('checkIsAcceptedLatestVersion', () => {
let source: DataSource = null;
let source: DataSource | null = null;
beforeEach(async () => {
source = new DataSource({
type: 'sqlite',
@ -146,11 +156,14 @@ describe('checkIsAcceptedLatestVersion', () => {
});
afterEach(async () => {
if (!source) return;
await source.destroy();
source = null;
});
it('同意済み利用規約バージョンが最新のときにチェックが通ること(第五)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AuthService>(AuthService);
const { admin } = await makeTestAccount(source, {
tier: 5,
@ -171,7 +184,9 @@ describe('checkIsAcceptedLatestVersion', () => {
});
it('同意済み利用規約バージョンが最新のときにチェックが通ること(第一~第四)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AuthService>(AuthService);
const { admin } = await makeTestAccount(source, {
tier: 4,
@ -192,7 +207,9 @@ describe('checkIsAcceptedLatestVersion', () => {
});
it('同意済み利用規約バージョンが最新でないときにチェックが通らないこと(第五)', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AuthService>(AuthService);
const { admin } = await makeTestAccount(source, {
tier: 5,
@ -213,7 +230,9 @@ describe('checkIsAcceptedLatestVersion', () => {
});
it('同意済み利用規約EULAバージョンが最新でないときにチェックが通らないこと第一第四', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AuthService>(AuthService);
const { admin } = await makeTestAccount(source, {
tier: 4,
@ -234,7 +253,9 @@ describe('checkIsAcceptedLatestVersion', () => {
});
it('同意済み利用規約バージョンDPAが最新でないときにチェックが通らないこと第一第四', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AuthService>(AuthService);
const { admin } = await makeTestAccount(source, {
tier: 4,

View File

@ -25,6 +25,13 @@ import { Context } from '../../common/log';
@Injectable()
export class AuthService {
private readonly refreshTokenLifetimeWeb =
this.configService.getOrThrow<number>('REFRESH_TOKEN_LIFETIME_WEB');
private readonly refreshTokenLifetimeDefault =
this.configService.getOrThrow<number>('REFRESH_TOKEN_LIFETIME_DEFAULT');
private readonly accessTokenlifetime = this.configService.getOrThrow<number>(
'ACCESS_TOKEN_LIFETIME_WEB',
);
constructor(
private readonly adB2cService: AdB2cService,
private readonly configService: ConfigService,
@ -68,10 +75,7 @@ export class AuthService {
this.logger.log(
`[IN] [${context.trackingId}] ${this.generateRefreshToken.name}`,
);
const lifetimeWeb = this.configService.get('REFRESH_TOKEN_LIFETIME_WEB');
const lifetimeDefault = this.configService.get(
'REFRESH_TOKEN_LIFETIME_DEFAULT',
);
let user: User;
// ユーザー情報とユーザーが属しているアカウント情報を取得
try {
@ -106,7 +110,10 @@ export class AuthService {
);
}
// 要求された環境用トークンの寿命を決定
const refreshTokenLifetime = type === 'web' ? lifetimeWeb : lifetimeDefault;
const refreshTokenLifetime =
type === 'web'
? this.refreshTokenLifetimeWeb
: this.refreshTokenLifetimeDefault;
const privateKey = getPrivateKey(this.configService);
// ユーザーのロールを設定
@ -165,7 +172,6 @@ export class AuthService {
this.logger.log(
`[IN] [${context.trackingId}] ${this.generateAccessToken.name}`,
);
const lifetime = this.configService.get('ACCESS_TOKEN_LIFETIME_WEB');
const privateKey = getPrivateKey(this.configService);
const pubkey = getPublicKey(this.configService);
@ -188,7 +194,7 @@ export class AuthService {
tier: token.tier,
userId: token.userId,
},
lifetime,
this.accessTokenlifetime,
privateKey,
);
@ -205,11 +211,14 @@ export class AuthService {
async getVerifiedIdToken(token: string): Promise<IDToken> {
this.logger.log(`[IN] ${this.getVerifiedIdToken.name}`);
let kid = '';
let kid: string | undefined = '';
try {
// JWTトークンのヘッダを見るため一度デコードする
const decodedToken = jwt.decode(token, { complete: true });
kid = decodedToken.header.kid;
kid = decodedToken?.header.kid;
if (!kid) {
throw new Error('kid not found');
}
} catch (e) {
this.logger.error(`error=${e}`);
throw new HttpException(

View File

@ -9,9 +9,13 @@ export type AdB2cMockValue = {
getMetaData: B2cMetadata | Error;
getSignKeySets: JwkSignKey[] | Error;
};
export type ConfigMockValue = {
getOrThrow: number;
};
export const makeAuthServiceMock = async (
adB2cMockValue: AdB2cMockValue,
configMockValue: ConfigMockValue,
): Promise<AuthService> => {
const module: TestingModule = await Test.createTestingModule({
providers: [AuthService],
@ -21,7 +25,7 @@ export const makeAuthServiceMock = async (
case AdB2cService:
return makeAdB2cServiceMock(adB2cMockValue);
case ConfigService:
return {};
return makeConfigMock(configMockValue);
case UsersRepositoryService:
return {};
}
@ -80,3 +84,16 @@ export const makeDefaultGetPublicKeyFromJwk = (jwkKey: JwkSignKey): string => {
'-----END PUBLIC KEY-----',
].join('\n');
};
export const makeConfigMock = (value: ConfigMockValue) => {
const { getOrThrow } = value;
return {
getOrThrow: jest.fn<Promise<number>, []>().mockResolvedValue(getOrThrow),
};
};
export const makeDefaultConfigValue = (): ConfigMockValue => {
return {
getOrThrow: 80000,
};
};

View File

@ -158,6 +158,9 @@ export const makeDefaultUsersRepositoryMockValue =
created_at: new Date(),
updated_by: '',
updated_at: new Date(),
active_worktype_id: null,
secondary_admin_user_id: null,
user: null,
},
},
};

View File

@ -75,7 +75,6 @@ export class LicensesController {
@Req() req: Request,
@Body() body: CreateOrdersRequest,
): Promise<CreateOrdersResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -127,7 +126,6 @@ export class LicensesController {
@Req() req: Request,
@Body() body: IssueCardLicensesRequest,
): Promise<IssueCardLicensesResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -184,7 +182,6 @@ export class LicensesController {
@Req() req: Request,
@Body() body: ActivateCardLicensesRequest,
): Promise<ActivateCardLicensesResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -238,7 +235,6 @@ export class LicensesController {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@Req() req: Request,
): Promise<GetAllocatableLicensesResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -300,7 +296,6 @@ export class LicensesController {
@Req() req: Request,
@Body() body: CancelOrderRequest,
): Promise<CancelOrderResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(

View File

@ -77,14 +77,14 @@ export const makeDefaultUsersRepositoryMockValue =
user.external_id = 'external_id';
user.account_id = 123;
user.role = 'none';
user.author_id = undefined;
user.author_id = null;
user.accepted_eula_version = '1.0';
user.accepted_dpa_version = '1.0';
user.email_verified = true;
user.auto_renew = false;
user.license_alert = false;
user.notification = false;
user.deleted_at = undefined;
user.deleted_at = null;
user.created_by = 'test';
user.created_at = new Date();
user.updated_by = 'test';

View File

@ -84,7 +84,6 @@ export class TasksController {
@Req() req,
@Query() body: TasksRequest,
): Promise<TasksResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -204,7 +203,7 @@ export class TasksController {
@Param() param: ChangeStatusRequest,
): Promise<ChangeStatusResponse> {
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -274,7 +273,7 @@ export class TasksController {
): Promise<ChangeStatusResponse> {
const { audioFileId } = params;
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -341,7 +340,7 @@ export class TasksController {
): Promise<ChangeStatusResponse> {
const { audioFileId } = params;
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -410,7 +409,7 @@ export class TasksController {
): Promise<ChangeStatusResponse> {
const { audioFileId } = params;
// AuthGuardでチェック済みなのでここでのアクセストークンチェックはしない
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -560,7 +559,7 @@ export class TasksController {
@Body() body: PostCheckoutPermissionRequest,
): Promise<PostCheckoutPermissionResponse> {
const { assignees } = body;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(

View File

@ -66,7 +66,7 @@ describe('getTemplates', () => {
expect(templates[1].id).toBe(template2.id);
expect(templates[1].name).toBe(template2.file_name);
}
}, 6000000);
});
it('テンプレートファイル一覧を取得できる0件', async () => {
if (!source) fail();

View File

@ -23,6 +23,9 @@ export const createTemplateFile = async (
id: template.id,
},
});
if (!templateFile) {
fail();
}
return templateFile;
};

View File

@ -8,7 +8,7 @@ import { HttpException, HttpStatus } from '@nestjs/common';
import { makeErrorResponse } from '../../common/error/makeErrorResponse';
describe('利用規約取得', () => {
let source: DataSource = null;
let source: DataSource | null = null;
beforeEach(async () => {
source = new DataSource({
type: 'sqlite',
@ -21,12 +21,15 @@ describe('利用規約取得', () => {
});
afterEach(async () => {
if (!source) return;
await source.destroy();
source = null;
});
it('最新の利用規約情報が取得できる', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<TermsService>(TermsService);
await createTermInfo(source, 'EULA', 'v1.0');
@ -44,7 +47,9 @@ describe('利用規約取得', () => {
});
it('利用規約情報(EULA、DPA両方)が存在しない場合エラーとなる', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<TermsService>(TermsService);
const context = makeContext(uuidv4());
await expect(service.getTermsInfo(context)).rejects.toEqual(
@ -56,7 +61,9 @@ describe('利用規約取得', () => {
});
it('利用規約情報(EULAのみ)が存在しない場合エラーとなる', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<TermsService>(TermsService);
await createTermInfo(source, 'DPA', 'v1.0');
const context = makeContext(uuidv4());
@ -69,7 +76,9 @@ describe('利用規約取得', () => {
});
it('利用規約情報(DPAのみ)が存在しない場合エラーとなる', async () => {
if (!source) fail();
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<TermsService>(TermsService);
await createTermInfo(source, 'EULA', 'v1.0');
const context = makeContext(uuidv4());

View File

@ -406,7 +406,7 @@ const AdB2cMockUsers: AdB2cUser[] = [
displayName: 'test1',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'test1@mail.com',
},
@ -417,7 +417,7 @@ const AdB2cMockUsers: AdB2cUser[] = [
displayName: 'test2',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'test2@mail.com',
},
@ -428,7 +428,7 @@ const AdB2cMockUsers: AdB2cUser[] = [
displayName: 'test3',
identities: [
{
signInType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
signInType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: 'issuer',
issuerAssignedId: 'test3@mail.com',
},

View File

@ -130,7 +130,6 @@ export class UsersController {
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get()
async getUsers(@Req() req: Request): Promise<GetUsersResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -193,7 +192,6 @@ export class UsersController {
prompt,
} = body;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -253,7 +251,6 @@ export class UsersController {
@UseGuards(AuthGuard)
@Get('relations')
async getRelations(@Req() req: Request): Promise<GetRelationsResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -307,7 +304,7 @@ export class UsersController {
@Req() req: Request,
): Promise<PostSortCriteriaResponse> {
const { direction, paramName } = body;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -365,7 +362,7 @@ export class UsersController {
@Req() req: Request,
): Promise<GetSortCriteriaResponse> {
const {} = query;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -432,7 +429,6 @@ export class UsersController {
prompt,
} = body;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req);
if (!accessToken) {
throw new HttpException(
@ -501,7 +497,6 @@ export class UsersController {
@Body() body: AllocateLicenseRequest,
@Req() req: Request,
): Promise<AllocateLicenseResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(
@ -561,7 +556,6 @@ export class UsersController {
@Body() body: DeallocateLicenseRequest,
@Req() req: Request,
): Promise<DeallocateLicenseResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req) as string;
if (!accessToken) {
throw new HttpException(

View File

@ -116,7 +116,7 @@ describe('UsersService.confirmUser', () => {
user: resultLicenses[0].user ?? null,
};
expect(resultUser.email_verified).toBe(true);
expect(resultUser?.email_verified).toBe(true);
expect(resultLicenses.length).toBe(100);
expect(resultLicensesCheckParam).toEqual({
id: 0,
@ -1764,7 +1764,7 @@ describe('UsersService.getUsers', () => {
await expect(service.getUsers('externalId_failed')).rejects.toEqual(
new HttpException(makeErrorResponse('E009999'), HttpStatus.NOT_FOUND),
);
}, 60000000);
});
it('ADB2Cからのユーザーの取得に失敗した場合、エラーとなる', async () => {
const adb2cParam = makeDefaultAdB2cMockValue();
@ -2030,15 +2030,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.NONE);
expect(createdUser.author_id).toBeNull();
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(false);
expect(createdUser.encryption_password).toBeNull();
expect(createdUser.prompt).toBe(false);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.NONE);
expect(createdUser?.author_id).toBeNull();
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(false);
expect(createdUser?.encryption_password).toBeNull();
expect(createdUser?.prompt).toBe(false);
});
it('ユーザー情報を更新できるTypist', async () => {
@ -2088,15 +2088,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.TYPIST);
expect(createdUser.author_id).toBeNull();
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(false);
expect(createdUser.encryption_password).toBeNull();
expect(createdUser.prompt).toBe(false);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.TYPIST);
expect(createdUser?.author_id).toBeNull();
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(false);
expect(createdUser?.encryption_password).toBeNull();
expect(createdUser?.prompt).toBe(false);
});
it('ユーザー情報を更新できるAuthor', async () => {
@ -2146,15 +2146,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser.author_id).toBe('AUTHOR_ID');
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(true);
expect(createdUser.encryption_password).toBe('new_password');
expect(createdUser.prompt).toBe(true);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser?.author_id).toBe('AUTHOR_ID');
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(true);
expect(createdUser?.encryption_password).toBe('new_password');
expect(createdUser?.prompt).toBe(true);
});
it('ユーザーのRoleを更新できるNone⇒Typist', async () => {
@ -2204,15 +2204,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.TYPIST);
expect(createdUser.author_id).toBeNull();
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(false);
expect(createdUser.encryption_password).toBeNull();
expect(createdUser.prompt).toBe(false);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.TYPIST);
expect(createdUser?.author_id).toBeNull();
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(false);
expect(createdUser?.encryption_password).toBeNull();
expect(createdUser?.prompt).toBe(false);
});
it('ユーザーのRoleを更新できるNone⇒Author', async () => {
@ -2262,15 +2262,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser.author_id).toBe('AUTHOR_ID');
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(false);
expect(createdUser.encryption_password).toBeNull();
expect(createdUser.prompt).toBe(false);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser?.author_id).toBe('AUTHOR_ID');
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(false);
expect(createdUser?.encryption_password).toBeNull();
expect(createdUser?.prompt).toBe(false);
});
it('None以外からRoleを変更した場合、エラーとなるTypist⇒None', async () => {
@ -2368,15 +2368,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser.author_id).toBe('AUTHOR_ID');
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(true);
expect(createdUser.encryption_password).toBe('password');
expect(createdUser.prompt).toBe(true);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser?.author_id).toBe('AUTHOR_ID');
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(true);
expect(createdUser?.encryption_password).toBe('password');
expect(createdUser?.prompt).toBe(true);
});
it('Authorが暗号化なしで更新した場合、パスワードをNULLにするEncryptionがfalse', async () => {
@ -2426,15 +2426,15 @@ describe('UsersService.updateUser', () => {
const createdUser = await getUser(source, user1);
expect(createdUser.id).toBe(user1);
expect(createdUser.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser.author_id).toBe('AUTHOR_ID');
expect(createdUser.auto_renew).toBe(false);
expect(createdUser.license_alert).toBe(false);
expect(createdUser.notification).toBe(false);
expect(createdUser.encryption).toBe(false);
expect(createdUser.encryption_password).toBeNull();
expect(createdUser.prompt).toBe(true);
expect(createdUser?.id).toBe(user1);
expect(createdUser?.role).toBe(USER_ROLES.AUTHOR);
expect(createdUser?.author_id).toBe('AUTHOR_ID');
expect(createdUser?.auto_renew).toBe(false);
expect(createdUser?.license_alert).toBe(false);
expect(createdUser?.notification).toBe(false);
expect(createdUser?.encryption).toBe(false);
expect(createdUser?.encryption_password).toBeNull();
expect(createdUser?.prompt).toBe(true);
});
it('AuthorのDBにパスワードが設定されていない場合、パスワードundefinedでわたすとエラーとなるEncryptionがtrue', async () => {
@ -2577,7 +2577,7 @@ describe('UsersService.updateAcceptedVersion', () => {
await service.updateAcceptedVersion(context, admin.external_id, 'v2.0');
const user = await getUser(source, admin.id);
expect(user.accepted_eula_version).toBe('v2.0');
expect(user?.accepted_eula_version).toBe('v2.0');
});
it('同意済み利用規約バージョンを更新できる(第一~第四)', async () => {
@ -2598,8 +2598,8 @@ describe('UsersService.updateAcceptedVersion', () => {
);
const user = await getUser(source, admin.id);
expect(user.accepted_eula_version).toBe('v2.0');
expect(user.accepted_dpa_version).toBe('v3.0');
expect(user?.accepted_eula_version).toBe('v2.0');
expect(user?.accepted_dpa_version).toBe('v3.0');
});
it('パラメータが不在のときエラーとなる(第一~第四)', async () => {

View File

@ -486,7 +486,7 @@ export class UsersService {
// メールアドレスを取得する
const mail = adb2cUser?.identities?.find(
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
(identity) => identity.signInType === ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
)?.issuerAssignedId;
//メールアドレスが取得できない場合はエラー

View File

@ -49,7 +49,7 @@ export const getWorkflow = async (
datasource: DataSource,
accountId: number,
id: number,
): Promise<Workflow> => {
): Promise<Workflow | null> => {
return await datasource.getRepository(Workflow).findOne({
where: {
account_id: accountId,

View File

@ -66,7 +66,6 @@ export class WorkflowsController {
@UseGuards(RoleGuard.requireds({ roles: [ADMIN_ROLES.ADMIN] }))
@Get()
async getWorkflows(@Req() req: Request): Promise<GetWorkflowsResponse> {
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req);
if (!accessToken) {
throw new HttpException(
@ -123,7 +122,7 @@ export class WorkflowsController {
@Body() body: CreateWorkflowsRequest,
): Promise<CreateWorkflowsResponse> {
const { authorId, worktypeId, templateId, typists } = body;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req);
if (!accessToken) {
throw new HttpException(
@ -188,7 +187,7 @@ export class WorkflowsController {
): Promise<UpdateWorkflowResponse> {
const { authorId, worktypeId, templateId, typists } = body;
const { workflowId } = param;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req);
if (!accessToken) {
throw new HttpException(
@ -252,7 +251,7 @@ export class WorkflowsController {
@Param() param: DeleteWorkflowRequestParam,
): Promise<DeleteWorkflowResponse> {
const { workflowId } = param;
// TODO strictNullChecks対応
const accessToken = retrieveAuthorizationToken(req);
if (!accessToken) {
throw new HttpException(

View File

@ -40,8 +40,8 @@ export class AdB2cService {
// ADB2Cへの認証情報
const credential = new ClientSecretCredential(
this.configService.getOrThrow<string>('ADB2C_TENANT_ID'),
this.configService.getOrThrow('ADB2C_CLIENT_ID'),
this.configService.getOrThrow('ADB2C_CLIENT_SECRET'),
this.configService.getOrThrow<string>('ADB2C_CLIENT_ID'),
this.configService.getOrThrow<string>('ADB2C_CLIENT_SECRET'),
);
const authProvider = new TokenCredentialAuthenticationProvider(credential, {
scopes: ['https://graph.microsoft.com/.default'],
@ -76,7 +76,7 @@ export class AdB2cService {
},
identities: [
{
signinType: ADB2C_SIGN_IN_TYPE.EAMILADDRESS,
signinType: ADB2C_SIGN_IN_TYPE.EMAILADDRESS,
issuer: `${this.tenantName}.onmicrosoft.com`,
issuerAssignedId: email,
},

View File

@ -9,7 +9,7 @@ import { Context } from '../../common/log';
export class SendGridService {
private readonly logger = new Logger(SendGridService.name);
private readonly emailConfirmLifetime: number;
readonly appDomain: string;
private readonly appDomain: string;
constructor(private readonly configService: ConfigService) {
this.appDomain = this.configService.getOrThrow<string>('APP_DOMAIN');
this.emailConfirmLifetime = this.configService.getOrThrow<number>(

View File

@ -126,13 +126,13 @@ export class AccountsRepositoryService {
tier: number,
adminExternalUserId: string,
adminUserRole: string,
adminUserAcceptedEulaVersion: string,
adminUserAcceptedDpaVersion: string,
adminUserAcceptedEulaVersion?: string,
adminUserAcceptedDpaVersion?: string,
): Promise<{ newAccount: Account; adminUser: User }> {
return await this.dataSource.transaction(async (entityManager) => {
const account = new Account();
{
account.parent_account_id = dealerAccountId;
account.parent_account_id = dealerAccountId ?? null;
account.company_name = companyName;
account.country = country;
account.tier = tier;
@ -147,8 +147,8 @@ export class AccountsRepositoryService {
user.account_id = persistedAccount.id;
user.external_id = adminExternalUserId;
user.role = adminUserRole;
user.accepted_eula_version = adminUserAcceptedEulaVersion;
user.accepted_dpa_version = adminUserAcceptedDpaVersion;
user.accepted_eula_version = adminUserAcceptedEulaVersion ?? null;
user.accepted_dpa_version = adminUserAcceptedDpaVersion ?? null;
}
const usersRepo = entityManager.getRepository(User);
const newUser = usersRepo.create(user);
@ -499,6 +499,9 @@ export class AccountsRepositoryService {
id: id,
},
});
if (!ownAccount) {
throw new AccountNotFoundError();
}
// 自アカウントのライセンス注文状況を取得する
const ownLicenseOrderStatus = await this.getAccountLicenseOrderStatus(
@ -541,8 +544,8 @@ export class AccountsRepositoryService {
);
// 第五の不足数を算出するためのライセンス数情報を取得する
let expiringSoonLicense: number;
let allocatableLicenseWithMargin: number;
let expiringSoonLicense: number = 0;
let allocatableLicenseWithMargin: number = 0;
if (childAccount.tier === TIERS.TIER5) {
expiringSoonLicense = await this.getExpiringSoonLicense(
entityManager,
@ -615,7 +618,7 @@ export class AccountsRepositoryService {
return await this.dataSource.transaction(async (entityManager) => {
const accountRepository = entityManager.getRepository(Account);
const maxTierDifference = TIERS.TIER5 - TIERS.TIER1;
const parentAccountIds = [];
const parentAccountIds: number[] = [];
let currentAccountId = targetAccountId;
// システム的な最大の階層差異分、親を参照する
@ -628,6 +631,9 @@ export class AccountsRepositoryService {
if (!account) {
break;
}
if (!account.parent_account_id) {
throw new Error("Parent account doesn't exist.");
}
parentAccountIds.push(account.parent_account_id);
currentAccountId = account.parent_account_id;
@ -749,11 +755,13 @@ export class AccountsRepositoryService {
});
// ADB2Cから情報を取得するための外部ユーザIDを取得する念のためプライマリ管理者IDが存在しない場合を考慮
const primaryUserIds = partnerAccounts.map((x) => {
const primaryUserIds = partnerAccounts.flatMap((x) => {
if (x.primary_admin_user_id) {
return x.primary_admin_user_id;
return [x.primary_admin_user_id];
} else if (x.secondary_admin_user_id) {
return x.secondary_admin_user_id;
return [x.secondary_admin_user_id];
} else {
return [];
}
});
const userRepo = entityManager.getRepository(User);
@ -770,15 +778,18 @@ export class AccountsRepositoryService {
user.id === account.primary_admin_user_id ||
user.id === account.secondary_admin_user_id,
);
const primaryAccountExternalId = primaryUser
? primaryUser.external_id
: undefined;
if (!primaryUser) {
throw new AdminUserNotFoundError(
`Primary admin user is not found. id: ${account.primary_admin_user_id}, account_id: ${account.id}`,
);
}
return {
name: account.company_name,
tier: account.tier,
accountId: account.id,
country: account.country,
primaryAccountExternalId: primaryAccountExternalId,
primaryAccountExternalId: primaryUser.external_id,
dealerManagement: account.delegation_permission,
};
});
@ -799,7 +810,7 @@ export class AccountsRepositoryService {
async getOneUpperTierAccount(
accountId: number,
tier: number,
): Promise<Account | undefined> {
): Promise<Account | null> {
return await this.dataSource.transaction(async (entityManager) => {
const accountRepo = entityManager.getRepository(Account);
return await accountRepo.findOne({
@ -878,10 +889,10 @@ export class AccountsRepositoryService {
await accountRepo.update(
{ id: myAccountId },
{
parent_account_id: parentAccountId || null,
parent_account_id: parentAccountId ?? null,
delegation_permission: delegationPermission,
primary_admin_user_id: primaryAdminUserId,
secondary_admin_user_id: secondryAdminUserId || null,
secondary_admin_user_id: secondryAdminUserId ?? null,
},
);
});

View File

@ -13,7 +13,7 @@ export class Account {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
parent_account_id: number | null;
@Column()
@ -34,30 +34,36 @@ export class Account {
@Column({ default: false })
verified: boolean;
@Column({ nullable: true })
primary_admin_user_id?: number;
@Column({ nullable: true, type: 'unsigned big int' })
primary_admin_user_id: number | null;
@Column({ nullable: true })
secondary_admin_user_id?: number;
@Column({ nullable: true, type: 'unsigned big int' })
secondary_admin_user_id: number | null;
@Column({ nullable: true })
active_worktype_id?: number;
@Column({ nullable: true, type: 'unsigned big int' })
active_worktype_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
created_by?: string;
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true })
updated_by?: string;
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
@OneToMany(() => User, (user) => user.id)
user?: User[];
user: User[] | null;
}

View File

@ -32,9 +32,9 @@ export class AudioFile {
priority: string;
@Column()
audio_format: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
comment: string | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column()
is_encrypted: boolean;

View File

@ -18,10 +18,10 @@ export class CheckoutPermission {
@Column({})
task_id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
user_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
user_group_id: number | null;
@OneToOne(() => User, (user) => user.id)

View File

@ -25,10 +25,13 @@ export class LicenseOrder {
@Column()
to_account_id: number;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
ordered_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
issued_at: Date | null;
@Column()
@ -37,19 +40,25 @@ export class LicenseOrder {
@Column()
status: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
canceled_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" })
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
updated_at: Date;
}
@ -58,7 +67,7 @@ export class License {
@PrimaryGeneratedColumn()
id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
expiry_date: Date | null;
@Column()
@ -70,28 +79,34 @@ export class License {
@Column()
status: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
allocated_user_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
order_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
delete_order_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" })
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
updated_at: Date;
@OneToOne(() => User, (user) => user.license, {
@ -109,16 +124,22 @@ export class CardLicenseIssue {
@Column()
issued_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" })
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
updated_at: Date;
}
@ -133,19 +154,25 @@ export class CardLicense {
@Column()
card_license_key: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
activated_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" })
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
updated_at: Date;
}
@ -172,19 +199,25 @@ export class LicenseAllocationHistory {
@Column()
switch_from_type: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" })
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
updated_at: Date;
@ManyToOne(() => License, (licenses) => licenses.id, {
@ -199,7 +232,7 @@ export class LicenseArchive {
@PrimaryColumn()
id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
expiry_date: Date | null;
@Column()
@ -211,31 +244,34 @@ export class LicenseArchive {
@Column()
status: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
allocated_user_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
order_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
delete_order_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@Column()
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@Column()
updated_at: Date;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
archived_at: Date;
}
@ -262,21 +298,24 @@ export class LicenseAllocationHistoryArchive {
@Column()
switch_from_type: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@Column()
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@Column()
updated_at: Date;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" })
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
})
archived_at: Date;
}

View File

@ -20,21 +20,21 @@ export class Task {
job_number: string;
@Column()
account_id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'tinyint' })
is_job_number_enabled: boolean | null;
@Column()
audio_file_id: number;
@Column()
status: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
typist_user_id: number | null;
@Column()
priority: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
template_file_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
started_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
finished_at: Date | null;
@Column({})
created_at: Date;

View File

@ -18,11 +18,11 @@ export class TemplateFile {
url: string;
@Column()
file_name: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn()
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn()
updated_at: Date;

View File

@ -17,15 +17,21 @@ export class Term {
@Column()
version: string;
@Column({ nullable: true })
created_by: string;
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true })
updated_by?: string;
@Column({ nullable: true, type: 'varchar' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
}

View File

@ -19,19 +19,25 @@ export class UserGroup {
@Column()
name: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date | null;
@OneToMany(

View File

@ -21,19 +21,25 @@ export class UserGroupMember {
@Column()
user_id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date | null;
@ManyToOne(() => User, (user) => user.id)

View File

@ -28,13 +28,13 @@ export class User {
@Column()
role: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
author_id: string | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
accepted_eula_version: string | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
accepted_dpa_version: string | null;
@Column({ default: false })
@ -52,25 +52,31 @@ export class User {
@Column({ default: false })
encryption: boolean;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
encryption_password: string | null;
@Column({ default: false })
prompt: boolean;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
created_by: string;
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
@ManyToOne(() => Account, (account) => account.user, {
@ -100,13 +106,13 @@ export class UserArchive {
@Column()
role: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
author_id: string | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
accepted_eula_version: string | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
accepted_dpa_version: string | null;
@Column()
@ -127,22 +133,25 @@ export class UserArchive {
@Column()
prompt: boolean;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
created_by: string;
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@Column()
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@Column()
updated_at: Date;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
archived_at: Date;
}

View File

@ -471,10 +471,8 @@ export class UsersRepositoryService {
},
});
if (!latestEulaInfo.version || !latestDpaInfo.version) {
throw new TermInfoNotFoundError(
`Terms info is not found. latestEulaInfo: ${latestEulaInfo.version}, latestDpaInfo: ${latestDpaInfo.version}`,
);
if (!latestEulaInfo || !latestDpaInfo) {
throw new TermInfoNotFoundError(`Terms info is not found.`);
}
return {

View File

@ -24,22 +24,28 @@ export class Workflow {
@Column()
author_id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
worktype_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
template_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
@ManyToOne(() => User, (user) => user.id)

View File

@ -19,22 +19,28 @@ export class WorkflowTypist {
@Column()
workflow_id: number;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
typist_id: number | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'unsigned big int' })
typist_group_id: number | null;
@Column({ nullable: true })
created_by: string;
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
@ManyToOne(() => Workflow, (workflow) => workflow.id)

View File

@ -18,12 +18,18 @@ export class OptionItem {
default_value_type: string;
@Column()
initial_value: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date | null;
}

View File

@ -18,21 +18,27 @@ export class Worktype {
@Column()
custom_worktype_id: string;
@Column({ nullable: true })
@Column({ nullable: true, type: 'varchar' })
description: string | null;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
deleted_at: Date | null;
@Column({ nullable: true })
created_by: string;
@Column({ nullable: true, type: 'datetime' })
created_by: string | null;
@CreateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@CreateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
created_at: Date;
@Column({ nullable: true })
@Column({ nullable: true, type: 'datetime' })
updated_by: string | null;
@UpdateDateColumn({ default: () => "datetime('now', 'localtime')" }) // defaultはSQLite用設定値.本番用は別途migrationで設定
@UpdateDateColumn({
default: () => "datetime('now', 'localtime')",
type: 'datetime',
}) // defaultはSQLite用設定値.本番用は別途migrationで設定
updated_at: Date;
}

View File

@ -12,7 +12,7 @@
"baseUrl": "./",
"incremental": true,
"skipLibCheck": true,
"strictNullChecks": false,
"strictNullChecks": true,
"noImplicitAny": false,
"strictBindCallApply": false,
"forceConsistentCasingInFileNames": false,