diff --git a/src/ws/dto/app-metadata.dto.ts b/src/ws/dto/app-metadata.dto.ts new file mode 100644 index 0000000..1b1dbef --- /dev/null +++ b/src/ws/dto/app-metadata.dto.ts @@ -0,0 +1,51 @@ +import { + IsOptional, + IsString, + MaxLength, + ValidateBy, + ValidationOptions, +} from 'class-validator'; + +/** + * Custom validator to ensure at least one of localized or unlocalized is a non-empty string + */ +function IsAtLeastOneNameProvided(validationOptions?: ValidationOptions) { + return ValidateBy( + { + name: 'isAtLeastOneNameProvided', + validator: { + validate(value, args) { + const object = args?.object as AppMetadataDto; + const hasLocalized = + typeof object.localized === 'string' && + object.localized.trim().length > 0; + const hasUnlocalized = + typeof object.unlocalized === 'string' && + object.unlocalized.trim().length > 0; + return hasLocalized || hasUnlocalized; + }, + defaultMessage() { + return 'At least one of localized or unlocalized must be a non-empty string'; + }, + }, + }, + validationOptions, + ); +} + +export class AppMetadataDto { + @IsOptional() + @IsString() + @MaxLength(200) + @IsAtLeastOneNameProvided() + localized: string | null; + + @IsOptional() + @IsString() + @MaxLength(200) + unlocalized: string | null; + + @IsOptional() + @IsString() + appIconB64: string | null; +} diff --git a/src/ws/dto/user-status.dto.ts b/src/ws/dto/user-status.dto.ts index 469e08b..a68c9f3 100644 --- a/src/ws/dto/user-status.dto.ts +++ b/src/ws/dto/user-status.dto.ts @@ -1,4 +1,6 @@ -import { IsEnum, IsNotEmpty, IsString, MaxLength } from 'class-validator'; +import { IsEnum, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { AppMetadataDto } from './app-metadata.dto'; export enum UserState { IDLE = 'idle', @@ -6,10 +8,9 @@ export enum UserState { } export class UserStatusDto { - @IsString() - @IsNotEmpty() - @MaxLength(100) - activeApp: string; + @ValidateNested() + @Type(() => AppMetadataDto) + appMetadata: AppMetadataDto; @IsEnum(UserState) state: UserState; diff --git a/src/ws/state/state.gateway.spec.ts b/src/ws/state/state.gateway.spec.ts index d773b8a..78138a3 100644 --- a/src/ws/state/state.gateway.spec.ts +++ b/src/ws/state/state.gateway.spec.ts @@ -495,7 +495,11 @@ describe('StateGateway', () => { ]); const data: UserStatusDto = { - activeApp: 'VS Code', + appMetadata: { + localized: null, + unlocalized: 'VS Code', + appIconB64: null, + }, state: UserState.IDLE, }; @@ -527,7 +531,11 @@ describe('StateGateway', () => { }; const data: UserStatusDto = { - activeApp: 'VS Code', + appMetadata: { + localized: null, + unlocalized: 'VS Code', + appIconB64: null, + }, state: UserState.IDLE, }; @@ -550,7 +558,11 @@ describe('StateGateway', () => { }; const data: UserStatusDto = { - activeApp: 'VS Code', + appMetadata: { + localized: null, + unlocalized: 'VS Code', + appIconB64: null, + }, state: UserState.IDLE, }; @@ -569,7 +581,11 @@ describe('StateGateway', () => { data: {}, }; const data: UserStatusDto = { - activeApp: 'VS Code', + appMetadata: { + localized: null, + unlocalized: 'VS Code', + appIconB64: null, + }, state: UserState.IDLE, }; @@ -598,7 +614,11 @@ describe('StateGateway', () => { ]); const data: UserStatusDto = { - activeApp: 'VS Code', + appMetadata: { + localized: null, + unlocalized: 'VS Code', + appIconB64: null, + }, state: UserState.IDLE, };