Merged PR 751: [FB対応]WorkTypeIDを大文字固定にしたい

## 概要
[Task3733: 対応する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/3733)

- 大文字のみの入力に強制する
  - WorkTypeID
  - OptionItem

- APIの大文字固定のパラメータにバリデーションを付与する
  - AuthorID
  - WorkTypeID
  - OptionItem

## レビューポイント
- 大文字に変換する処理をreducerに寄せたが問題ないか
- バリデータを付けるパラメータに漏れはないか

## UIの変更
- https://ndstokyo.sharepoint.com/:f:/r/sites/Piranha/Shared%20Documents/General/OMDS/%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%BC%E3%83%B3%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88/Task3733?csf=1&web=1&e=zNB1Hi

## 動作確認状況
- ローカルで確認

## 補足
- 相談、参考資料などがあれば
This commit is contained in:
saito.k 2024-02-19 01:09:39 +00:00
parent cd58916e05
commit b4df229de4
8 changed files with 74 additions and 19 deletions

View File

@ -45,7 +45,7 @@ export const worktypeSlice = createSlice({
action: PayloadAction<{ worktypeId: string }>
) => {
const { worktypeId } = action.payload;
state.apps.worktypeId = worktypeId;
state.apps.worktypeId = worktypeId.toUpperCase();
},
changeDescription: (
state,
@ -65,7 +65,12 @@ export const worktypeSlice = createSlice({
optionItem.defaultValueType !== OPTION_ITEMS_DEFAULT_VALUE_TYPE.DEFAULT
) {
optionItem.initialValue = "";
} else {
// defaultValueTypeがDefaultの場合はinitialValueを大文字にする
optionItem.initialValue = optionItem.initialValue.toUpperCase();
}
// itemLabelを大文字にする
optionItem.itemLabel = optionItem.itemLabel.toUpperCase();
// idが一致するoptionItemを削除して、新しいoptionItemを追加する。一致するidがない場合は何もしない
const optionItems = state.apps.optionItems?.filter(

View File

@ -0,0 +1,36 @@
import {
ValidatorConstraint,
ValidatorConstraintInterface,
ValidationArguments,
ValidationOptions,
registerDecorator,
} from 'class-validator';
// 大文字英数字とアンダースコアのみを許可するバリデータ
@ValidatorConstraint({ name: 'IsAuthorId', async: false })
export class IsAuthorId implements ValidatorConstraintInterface {
validate(value: any, args: ValidationArguments) {
return /^[A-Z0-9_]*$/.test(value);
}
defaultMessage(args: ValidationArguments) {
return `${args.property} should be uppercase alphanumeric and underscore only`;
}
}
/**
*
* @param [validationOptions]
* @returns
*/
export function IsAuthorIdValid(validationOptions?: ValidationOptions) {
return function (object: object, propertyName: string) {
registerDecorator({
name: 'IsAuthorId',
target: object.constructor,
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: IsAuthorId,
});
};
}

View File

@ -19,7 +19,7 @@ export class IsRecorderAllowedCharacters
}
// 正規表現でWorktypeIDのチェックを行う
// 以下の禁則文字を除く半角英数記号
// 以下の禁則文字を除く大文字英数記号
// \ (backslash)
// / (forward slash)
// : (colon)
@ -31,7 +31,7 @@ export class IsRecorderAllowedCharacters
// | (vertical bar)
// . (period)
const regex =
/^(?!.*\\)(?!.*\/)(?!.*:)(?!.*\*)(?!.*\?)(?!.*")(?!.*<)(?!.*>)(?!.*\|)(?!.*\.)[ -~]+$/;
/^(?!.*\\)(?!.*\/)(?!.*:)(?!.*\*)(?!.*\?)(?!.*")(?!.*<)(?!.*>)(?!.*\|)(?!.*\.)[A-Z0-9 !#$%&'()+,\-;=@\[\]^_`{}~]*$/;
return regex.test(value);
}

View File

@ -134,8 +134,8 @@ describe('createAccount', () => {
},
});
let _subject: string = "";
let _url: string | undefined = "";
let _subject: string = '';
let _url: string | undefined = '';
overrideSendgridService(service, {
sendMail: async (
context: Context,
@ -197,7 +197,9 @@ describe('createAccount', () => {
// 想定通りのメールが送られているか確認
expect(_subject).toBe('User Registration Notification [U-102]');
expect(_url?.startsWith('http://localhost:8081/mail-confirm?verify=')).toBeTruthy();
expect(
_url?.startsWith('http://localhost:8081/mail-confirm?verify='),
).toBeTruthy();
});
it('アカウントを作成がAzure AD B2Cへの通信失敗によって失敗すると500エラーが発生する', async () => {
@ -5727,8 +5729,8 @@ describe('アカウント情報更新', () => {
const module = await makeTestingModule(source);
if (!module) fail();
const service = module.get<AccountsService>(AccountsService);
let _subject: string = "";
let _url: string | undefined = "";
let _subject: string = '';
let _url: string | undefined = '';
overrideSendgridService(service, {
sendMail: async (
context: Context,

View File

@ -18,14 +18,21 @@ import {
} from './test/utility';
import { UsersService } from '../users/users.service';
import { Context, makeContext } from '../../common/log';
import { ADB2C_SIGN_IN_TYPE, LICENSE_ALLOCATED_STATUS, LICENSE_TYPE } from '../../constants';
import {
ADB2C_SIGN_IN_TYPE,
LICENSE_ALLOCATED_STATUS,
LICENSE_TYPE,
} from '../../constants';
import {
makeHierarchicalAccounts,
makeTestSimpleAccount,
makeTestUser,
} from '../../common/test/utility';
import { LicensesRepositoryService } from '../../repositories/licenses/licenses.repository.service';
import { overrideAdB2cService, overrideSendgridService } from '../../common/test/overrides';
import {
overrideAdB2cService,
overrideSendgridService,
} from '../../common/test/overrides';
import { truncateAllTable } from '../../common/test/init';
describe('ライセンス注文', () => {
@ -672,7 +679,10 @@ describe('ライセンス割り当て', () => {
const module = await makeTestingModule(source);
if (!module) fail();
const { id: dealerId } = await makeTestSimpleAccount(source, { company_name: "DEALER_COMPANY", tier: 4 });
const { id: dealerId } = await makeTestSimpleAccount(source, {
company_name: 'DEALER_COMPANY',
tier: 4,
});
const { id: dealerAdminId } = await makeTestUser(source, {
account_id: dealerId,
external_id: 'userId_admin',
@ -682,7 +692,7 @@ describe('ライセンス割り当て', () => {
const { id: accountId } = await makeTestSimpleAccount(source, {
parent_account_id: dealerId,
tier: 5
tier: 5,
});
const { id: userId } = await makeTestUser(source, {
account_id: accountId,
@ -740,7 +750,7 @@ describe('ライセンス割り当て', () => {
},
],
}));
}
},
});
overrideSendgridService(service, {

View File

@ -138,8 +138,7 @@ export const createTask = async (
label: `label${i}:audio_file_id${audioFileIdentifiers[0].id}`,
value: `value${i}:audio_file_id${audioFileIdentifiers[0].id}`,
};
}
);
});
await datasource.getRepository(AudioOptionItem).insert(audioOptionItems);
@ -161,7 +160,7 @@ export const createTask = async (
return { taskId: task.id, audioFileId: audioFile.id };
};
export const createAudioFile = async(
export const createAudioFile = async (
datasource: DataSource,
account_id: number,
owner_user_id: number,
@ -189,7 +188,7 @@ export const createAudioFile = async(
});
const audioFile = audioFileIdentifiers.pop() as AudioFile;
return { audioFileId: audioFile.id };
}
};
/**
*

View File

@ -18,6 +18,7 @@ import {
} from '../../../common/validators/encryptionPassword.validator';
import { IsRoleAuthorDataValid } from '../../../common/validators/roleAuthor.validator';
import { Type } from 'class-transformer';
import { IsAuthorIdValid } from '../../../common/validators/authorId.validator';
export class ConfirmRequest {
@ApiProperty()
@ -90,6 +91,7 @@ export class SignupRequest {
@ApiProperty({ required: false })
@IsRoleAuthorDataValid()
@IsAuthorIdValid()
authorId?: string;
@ApiProperty()
@ -225,6 +227,7 @@ export class PostUpdateUserRequest {
@ApiProperty({ required: false })
@IsRoleAuthorDataValid()
@IsAuthorIdValid()
authorId?: string;
@ApiProperty()