import { ApiExtraModels, ApiProperty, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { ArrayMinSize, IsEnum, IsInt, Min, ValidateNested } from 'class-validator'; import { IsAxisAlignedRotation, IsUniqueEditActions, ValidateEnum, ValidateUUID } from 'src/validation'; export enum AssetEditAction { Crop = 'crop', Rotate = 'rotate', Mirror = 'mirror', } export enum MirrorAxis { Horizontal = 'horizontal', Vertical = 'vertical', } export class CropParameters { @IsInt() @Min(0) @ApiProperty({ description: 'Top-Left X coordinate of crop' }) x!: number; @IsInt() @Min(0) @ApiProperty({ description: 'Top-Left Y coordinate of crop' }) y!: number; @IsInt() @Min(1) @ApiProperty({ description: 'Width of the crop' }) width!: number; @IsInt() @Min(1) @ApiProperty({ description: 'Height of the crop' }) height!: number; } export class RotateParameters { @IsAxisAlignedRotation() @ApiProperty({ description: 'Rotation angle in degrees' }) angle!: number; } export class MirrorParameters { @IsEnum(MirrorAxis) @ApiProperty({ enum: MirrorAxis, enumName: 'MirrorAxis', description: 'Axis to mirror along' }) axis!: MirrorAxis; } export type AssetEditParameters = CropParameters | RotateParameters | MirrorParameters; export type AssetEditActionItem = | { action: AssetEditAction.Crop; parameters: CropParameters; } | { action: AssetEditAction.Rotate; parameters: RotateParameters; } | { action: AssetEditAction.Mirror; parameters: MirrorParameters; }; @ApiExtraModels(CropParameters, RotateParameters, MirrorParameters) export class AssetEditActionItemDto { @ValidateEnum({ name: 'AssetEditAction', enum: AssetEditAction, description: 'Type of edit action to perform' }) action!: AssetEditAction; @ApiProperty({ description: 'List of edit actions to apply (crop, rotate, or mirror)', anyOf: [CropParameters, RotateParameters, MirrorParameters].map((type) => ({ $ref: getSchemaPath(type), })), }) @ValidateNested() @Type((options) => actionParameterMap[options?.object.action as keyof AssetEditActionParameter]) parameters!: AssetEditActionItem['parameters']; } export class AssetEditActionItemResponseDto extends AssetEditActionItemDto { @ValidateUUID() id!: string; } export type AssetEditActionParameter = typeof actionParameterMap; const actionParameterMap = { [AssetEditAction.Crop]: CropParameters, [AssetEditAction.Rotate]: RotateParameters, [AssetEditAction.Mirror]: MirrorParameters, }; export class AssetEditsCreateDto { @ArrayMinSize(1) @IsUniqueEditActions() @ValidateNested({ each: true }) @Type(() => AssetEditActionItemDto) @ApiProperty({ description: 'List of edit actions to apply (crop, rotate, or mirror)' }) edits!: AssetEditActionItemDto[]; } export class AssetEditsResponseDto { @ValidateUUID({ description: 'Asset ID these edits belong to' }) assetId!: string; @ApiProperty({ description: 'List of edit actions applied to the asset', }) edits!: AssetEditActionItemResponseDto[]; }