Merged PR 376: [Sp17完了MISO]バリデータをクラスを使用した記述に統一する

## 概要
[Task2502: [Sp17完了MISO]バリデータをクラスを使用した記述に統一する](https://paruru.nds-tyo.co.jp:8443/tfs/ReciproCollection/fa4924a4-d079-4fab-9fb5-a9a11eb205f0/_workitems/edit/2502)

バリデータの記述方法をクラスで外だしする形に統一しました。
また、未使用の引数を削除しました。

## レビューポイント
期待通りの修正内容であるか。
未使用の引数を削除してしまったが、問題ないか。

## UIの変更
なし

## 動作確認状況
ローカルで該当バリデーションを使用しているAPIを実行し、動作を確認済み

## 補足
なし
This commit is contained in:
oura.a 2023-09-01 09:12:52 +00:00
parent 7a453c80f8
commit f56f95123b
6 changed files with 119 additions and 97 deletions

View File

@ -8,8 +8,7 @@ import {
@ValidatorConstraint()
export class IsUniqueArray implements ValidatorConstraintInterface {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validate(arr: any[], args: ValidationArguments) {
validate(arr: any[]) {
return arr.length === new Set(arr).size;
}

View File

@ -1,6 +1,30 @@
import { registerDecorator, ValidationOptions } from 'class-validator';
import {
registerDecorator,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
@ValidatorConstraint()
export class IsAdminPassword implements ValidatorConstraintInterface {
validate(value: string): boolean {
// 8文字64文字でなければ早期に不合格
const minLength = 8;
const maxLength = 64;
if (value.length < minLength || value.length > maxLength) {
return false;
}
// 英字の大文字、英字の小文字、アラビア数字、記号(@#$%^&*\-_+=[]{}|\:',.?/`~"();!から2種類以上組み合わせ
const charaTypePattern =
/^((?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[\d])|(?=.*[a-z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[A-Z])(?=.*[\d])|(?=.*[A-Z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[\d])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]))[a-zA-Z\d@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]/;
return new RegExp(charaTypePattern).test(value);
}
defaultMessage(): string {
return 'Admin password rule not satisfied';
}
}
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
export const IsAdminPasswordvalid = (validationOptions?: ValidationOptions) => {
return (object: any, propertyName: string) => {
registerDecorator({
@ -9,24 +33,7 @@ export const IsAdminPasswordvalid = (validationOptions?: ValidationOptions) => {
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: {
validate: (value: string) => {
// 8文字64文字でなければ早期に不合格
const minLength = 8;
const maxLength = 64;
if (value.length < minLength || value.length > maxLength) {
return false;
}
// 英字の大文字、英字の小文字、アラビア数字、記号(@#$%^&*\-_+=[]{}|\:',.?/`~"();!から2種類以上組み合わせ
const charaTypePattern =
/^((?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[\d])|(?=.*[a-z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[A-Z])(?=.*[\d])|(?=.*[A-Z])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!])|(?=.*[\d])(?=.*[@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]))[a-zA-Z\d@#$%^&*\\\-_+=\[\]{}|:',.?\/`~"();!]/;
return new RegExp(charaTypePattern).test(value);
},
defaultMessage: () => {
return 'Admin password rule not satisfied';
},
},
validator: IsAdminPassword,
});
};
};

View File

@ -1,10 +1,33 @@
import {
registerDecorator,
ValidationOptions,
ValidationArguments,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { Assignee } from '../../features/tasks/types/types';
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
@ValidatorConstraint()
export class IsTypist implements ValidatorConstraintInterface {
validate(values: Assignee[]): boolean {
return values.every((value) => {
const { typistUserId, typistGroupId, typistName } = value;
if (typistUserId === undefined && typistGroupId === undefined) {
return false;
}
if (typistUserId !== undefined && typistGroupId !== undefined) {
return false;
}
if (!typistName) {
return false;
}
return true;
});
}
defaultMessage(): string {
return 'Request body is invalid format';
}
}
/**
* Validations options
* @param [validationOptions]
@ -17,28 +40,7 @@ export const IsAssignees = (validationOptions?: ValidationOptions) => {
target: object.constructor,
propertyName: propertyName,
options: validationOptions,
validator: {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validate: (values: Assignee[], args: ValidationArguments) => {
return values.every((value) => {
const { typistUserId, typistGroupId, typistName } = value;
if (typistUserId === undefined && typistGroupId === undefined) {
return false;
}
if (typistUserId !== undefined && typistGroupId !== undefined) {
return false;
}
if (!typistName) {
return false;
}
return true;
});
},
// eslint-disable-next-line @typescript-eslint/no-unused-vars
defaultMessage: (args?: ValidationArguments): string => {
return 'Request body is invalid format';
},
},
validator: IsTypist,
});
};
};

View File

@ -2,9 +2,46 @@ import {
registerDecorator,
ValidationArguments,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import { SignupRequest } from '../../features/users/types/types';
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
@ValidatorConstraint()
export class IsPassword implements ValidatorConstraintInterface {
validate(value: string | undefined): boolean {
// passwordが設定されていない場合はチェックしない
if (value === undefined) {
return true;
}
// 正規表現でパスワードのチェックを行う
// 416文字の半角英数字と記号のみ
const regex = /^[!-~]{4,16}$/;
if (!regex.test(value)) {
return false;
}
return true;
}
defaultMessage(): string {
return 'EncryptionPassword rule not satisfied';
}
}
@ValidatorConstraint()
export class IsEncryptionPassword implements ValidatorConstraintInterface {
validate(value: string | undefined, args: ValidationArguments): boolean {
const { encryption } = args.object as SignupRequest;
if (encryption === true && !value) {
return false;
}
return true;
}
defaultMessage(): string {
return 'Encryption password is required when encryption is enabled';
}
}
export const IsPasswordvalid = (validationOptions?: ValidationOptions) => {
return (object: any, propertyName: string) => {
registerDecorator({
@ -13,29 +50,10 @@ export const IsPasswordvalid = (validationOptions?: ValidationOptions) => {
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: {
validate: (value: string | undefined) => {
// passwordが設定されていない場合はチェックしない
if (value === undefined) {
return true;
}
// 正規表現でパスワードのチェックを行う
// 416文字の半角英数字と記号のみ
const regex = /^[!-~]{4,16}$/;
if (!regex.test(value)) {
return false;
}
return true;
},
defaultMessage: () => {
return 'EncryptionPassword rule not satisfied';
},
},
validator: IsPassword,
});
};
};
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
export const IsEncryptionPasswordPresent = (
validationOptions?: ValidationOptions,
) => {
@ -46,18 +64,7 @@ export const IsEncryptionPasswordPresent = (
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: {
validate: (value: string | undefined, args: ValidationArguments) => {
const { encryption } = args.object as SignupRequest;
if (encryption === true && !value) {
return false;
}
return true;
},
defaultMessage: () => {
return 'Encryption password is required when encryption is enabled';
},
},
validator: IsEncryptionPassword,
});
};
};

View File

@ -2,6 +2,8 @@ import {
registerDecorator,
ValidationArguments,
ValidationOptions,
ValidatorConstraint,
ValidatorConstraintInterface,
} from 'class-validator';
import {
PostUpdateUserRequest,
@ -9,7 +11,26 @@ import {
} from '../../features/users/types/types';
import { USER_ROLES } from '../../constants';
// TODO タスク 2502: バリデータをクラスを使用した記述に統一するで修正する
@ValidatorConstraint()
export class IsRoleAuthorData implements ValidatorConstraintInterface {
propertyName: string;
constructor(propertyName: string) {
this.propertyName = propertyName;
}
validate(value: any, args: ValidationArguments): boolean {
const request = args.object as SignupRequest | PostUpdateUserRequest;
const { role } = request;
if (role === USER_ROLES.AUTHOR && value === undefined) {
return false;
}
return true;
}
defaultMessage(): string {
return `When role is author, ${this.propertyName} cannot be undefined`;
}
}
export const IsRoleAuthorDataValid = <
T extends SignupRequest | PostUpdateUserRequest,
>(
@ -22,19 +43,7 @@ export const IsRoleAuthorDataValid = <
propertyName: propertyName,
constraints: [],
options: validationOptions,
validator: {
validate: (value: any, args: ValidationArguments) => {
const request = args.object as T;
const { role } = request;
if (role === USER_ROLES.AUTHOR && value === undefined) {
return false;
}
return true;
},
defaultMessage: () => {
return `When role is author, ${propertyName} cannot be undefined`;
},
},
validator: new IsRoleAuthorData(propertyName),
});
};
};

View File

@ -10,8 +10,7 @@ import { TASK_STATUS } from '../../constants';
@ValidatorConstraint()
export class IsStatusConstraint implements ValidatorConstraintInterface {
private readonly STATUS: string[] = Object.values(TASK_STATUS);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
validate(value: string, args: ValidationArguments): boolean {
validate(value: string): boolean {
if (value) {
// ,で分割した文字列のすべてがTASK_STATUSのプロパティに存在する値であった場合のみtrue
return value.split(',').every((state) => this.STATUS.includes(state));
@ -20,8 +19,7 @@ export class IsStatusConstraint implements ValidatorConstraintInterface {
}
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
defaultMessage(validationArguments?: ValidationArguments): string {
defaultMessage(): string {
return `invalid status string`;
}
}