diff --git a/dictation_server/src/common/validators/authorId.validator.ts b/dictation_server/src/common/validators/authorId.validator.ts index 23afafb..de721b9 100644 --- a/dictation_server/src/common/validators/authorId.validator.ts +++ b/dictation_server/src/common/validators/authorId.validator.ts @@ -12,10 +12,10 @@ import { import { USER_ROLES } from '../../constants'; // 大文字英数字とアンダースコアのみを許可するバリデータ -@ValidatorConstraint({ name: 'IsAuthorId', async: false }) -export class IsAuthorId implements ValidatorConstraintInterface { +@ValidatorConstraint({ name: 'IsAuthorIdValidConstraint', async: false }) +export class IsAuthorIdValidConstraint implements ValidatorConstraintInterface { validate(value: any, args: ValidationArguments) { - const request = args.object as SignupRequest | PostUpdateUserRequest; + const request = args.object as SignupRequest | PostUpdateUserRequest; // requestの存在チェック if (!request) { return false; @@ -40,12 +40,44 @@ export class IsAuthorId implements ValidatorConstraintInterface { export function IsAuthorIdValid(validationOptions?: ValidationOptions) { return function (object: object, propertyName: string) { registerDecorator({ - name: 'IsAuthorId', + name: 'IsAuthorIdValidConstraint', target: object.constructor, propertyName: propertyName, constraints: [], options: validationOptions, - validator: IsAuthorId, + validator: IsAuthorIdValidConstraint, + }); + }; +} + +@ValidatorConstraint({ async: false }) +class IsAuhtorIDConstraint implements ValidatorConstraintInterface { + validate(value: any, args: ValidationArguments) { + // null or undefinedであれば不合格 + if (value == null) { + return false; + } + // 文字列型でなければ不合格 + if (typeof value !== 'string') { + return false; + } + + return /^[A-Z0-9_]*$/.test(value); + } + + defaultMessage(args: ValidationArguments) { + return `${args.property} should be uppercase alphanumeric and underscore only`; + } +} + +export function IsAuthorID(validationOptions?: ValidationOptions) { + return function (object: Object, propertyName: string) { + registerDecorator({ + target: object.constructor, + propertyName: propertyName, + options: validationOptions, + constraints: [], + validator: IsAuhtorIDConstraint, }); }; } diff --git a/dictation_server/src/features/users/types/types.ts b/dictation_server/src/features/users/types/types.ts index 64896eb..822a45b 100644 --- a/dictation_server/src/features/users/types/types.ts +++ b/dictation_server/src/features/users/types/types.ts @@ -23,7 +23,10 @@ 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'; +import { + IsAuthorID, + IsAuthorIdValid, +} from '../../../common/validators/authorId.validator'; export class ConfirmRequest { @ApiProperty() @@ -288,8 +291,9 @@ export class MultipleImportUser { role: number; @ApiProperty({ required: false }) - @IsAuthorIdValid() @ValidateIf((o) => o.role === 1) // roleがauthorの場合のみバリデーションを実施 + @IsAuthorID() + @IsNotEmpty() authorId?: string; @ApiProperty({ description: '0(false)/1(true)' }) diff --git a/dictation_server/src/features/users/users.controller.spec.ts b/dictation_server/src/features/users/users.controller.spec.ts index fa0bcd3..2f93755 100644 --- a/dictation_server/src/features/users/users.controller.spec.ts +++ b/dictation_server/src/features/users/users.controller.spec.ts @@ -115,6 +115,59 @@ describe('UsersController', () => { const errors = await validate(valdationObject); expect(errors.length).toBeGreaterThan(0); }); + it('AuthorIDがルールに違反していた場合、バリデーションエラーが発生する', async () => { + // ルールに合致したAuthorIDではエラーが発生しない + const validAuthorIDs = ['A', '_', 'AB', 'A1', '1A', '_1', 'A_B']; + for await (const authorId of validAuthorIDs) { + const request = new PostMultipleImportsRequest(); + request.users = [ + { + name: 'namae', + email: 'hogehoge@example.com', + role: 1, + authorId: authorId, + autoRenew: 0, + notification: 0, + encryption: 0, + prompt: 0, + }, + ]; + + const valdationObject = plainToClass( + PostMultipleImportsRequest, + request, + ); + + const errors = await validate(valdationObject); + expect(errors.length).toBe(0); + } + + // ルールに違反したAuthorIDではエラーが発生する + const invalidAuthorIDs = ['a', '+', 'AB.', 'Ab', '1a', '_.', 'A/B', '']; + for await (const authorId of invalidAuthorIDs) { + const request = new PostMultipleImportsRequest(); + request.users = [ + { + name: 'namae', + email: 'hogehoge@example.com', + role: 1, + authorId: authorId, + autoRenew: 0, + notification: 0, + encryption: 0, + prompt: 0, + }, + ]; + + const valdationObject = plainToClass( + PostMultipleImportsRequest, + request, + ); + + const errors = await validate(valdationObject); + expect(errors.length).toBeGreaterThan(0); + } + }); it('Authorなのにencryptionがない場合、バリデーションエラーが発生する', async () => { const request = new PostMultipleImportsRequest(); request.users = [