From a50f125dd18711e41f582db8ab36ed44b727f1e4 Mon Sep 17 00:00:00 2001 From: Jason Rasmussen Date: Thu, 7 Mar 2024 22:59:02 -0500 Subject: [PATCH] refactor: api validators (boolean and date) (#7709) * refactor: api validators (boolean and date) * chore: open api * revert: time bucket change --- cli/src/commands/upload.command.ts | 4 +- e2e/src/api/specs/person.e2e-spec.ts | 23 ++--- mobile/openapi/doc/PersonApi.md | Bin 21092 -> 21074 bytes mobile/openapi/doc/ScanLibraryDto.md | Bin 499 -> 481 bytes mobile/openapi/doc/SharedLinkCreateDto.md | Bin 891 -> 873 bytes .../openapi/lib/model/scan_library_dto.dart | Bin 3686 -> 4150 bytes .../lib/model/shared_link_create_dto.dart | Bin 6452 -> 6904 bytes .../openapi/test/scan_library_dto_test.dart | Bin 724 -> 701 bytes .../test/shared_link_create_dto_test.dart | Bin 1514 -> 1491 bytes open-api/immich-openapi-specs.json | 3 - .../src/domain/album/dto/album-update.dto.ts | 7 +- server/src/domain/album/dto/album.dto.ts | 8 +- server/src/domain/album/dto/get-albums.dto.ts | 10 +- .../domain/asset/dto/asset-statistics.dto.ts | 16 +-- server/src/domain/asset/dto/asset.dto.ts | 53 +++------- server/src/domain/asset/dto/map-marker.dto.ts | 28 ++---- .../src/domain/asset/dto/time-bucket.dto.ts | 26 ++--- server/src/domain/audit/audit.dto.ts | 7 +- server/src/domain/domain.util.ts | 95 +++++++++++------- server/src/domain/job/job.dto.ts | 7 +- server/src/domain/library/library.dto.ts | 21 ++-- server/src/domain/person/person.dto.ts | 19 ++-- server/src/domain/search/dto/search.dto.ts | 74 ++++++-------- .../src/domain/shared-link/shared-link.dto.ts | 27 ++--- .../domain/smart-info/dto/model-config.dto.ts | 6 +- .../dto/system-config-ffmpeg.dto.ts | 7 +- .../dto/system-config-library.dto.ts | 7 +- .../dto/system-config-logging.dto.ts | 5 +- .../dto/system-config-machine-learning.dto.ts | 7 +- .../dto/system-config-map.dto.ts | 5 +- .../system-config-new-version-check.dto.ts | 4 +- .../dto/system-config-oauth.dto.ts | 11 +- .../dto/system-config-password-login.dto.ts | 4 +- .../system-config-reverse-geocoding.dto.ts | 4 +- .../dto/system-config-storage-template.dto.ts | 9 +- .../dto/system-config-trash.dto.ts | 5 +- server/src/domain/user/dto/create-user.dto.ts | 10 +- server/src/domain/user/dto/update-user.dto.ts | 13 +-- .../api-v1/asset/dto/asset-search.dto.ts | 24 ++--- .../api-v1/asset/dto/create-asset.dto.ts | 33 ++---- .../immich/api-v1/asset/dto/serve-file.dto.ts | 16 +-- 41 files changed, 243 insertions(+), 355 deletions(-) diff --git a/cli/src/commands/upload.command.ts b/cli/src/commands/upload.command.ts index 8029b1313f..250fd79c62 100644 --- a/cli/src/commands/upload.command.ts +++ b/cli/src/commands/upload.command.ts @@ -66,8 +66,8 @@ class Asset { assetData: new File([await fs.openAsBlob(this.path)], basename(this.path)), deviceAssetId: this.deviceAssetId, deviceId: 'CLI', - fileCreatedAt: this.fileCreatedAt, - fileModifiedAt: this.fileModifiedAt, + fileCreatedAt: this.fileCreatedAt.toISOString(), + fileModifiedAt: this.fileModifiedAt.toISOString(), isFavorite: String(false), }; const formData = new FormData(); diff --git a/e2e/src/api/specs/person.e2e-spec.ts b/e2e/src/api/specs/person.e2e-spec.ts index 8015580430..54fbfa9be5 100644 --- a/e2e/src/api/specs/person.e2e-spec.ts +++ b/e2e/src/api/specs/person.e2e-spec.ts @@ -6,10 +6,9 @@ import request from 'supertest'; import { beforeAll, beforeEach, describe, expect, it } from 'vitest'; const invalidBirthday = [ - // TODO: enable after replacing `@Type(() => Date)` - // { birthDate: 'false', response: 'Invalid date' }, - // { birthDate: '123567', response: 'Invalid date }, - // { birthDate: 123_567, response: ['Birth date cannot be in the future'] }, + { birthDate: 'false', response: 'birthDate must be a date string' }, + { birthDate: '123567', response: 'birthDate must be a date string' }, + { birthDate: 123_567, response: 'birthDate must be a date string' }, { birthDate: new Date(9999, 0, 0).toISOString(), response: ['Birth date cannot be in the future'] }, ]; @@ -152,16 +151,16 @@ describe('/person', () => { expect(body).toEqual(errorDto.unauthorized); }); - it('should not accept invalid birth dates', async () => { - for (const { birthDate, response } of invalidBirthday) { + for (const { birthDate, response } of invalidBirthday) { + it(`should not accept an invalid birth date [${birthDate}]`, async () => { const { status, body } = await request(app) .post(`/person`) .set('Authorization', `Bearer ${admin.accessToken}`) .send({ birthDate }); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest(response)); - } - }); + }); + } it('should create a person', async () => { const { status, body } = await request(app) @@ -202,16 +201,16 @@ describe('/person', () => { }); } - it('should not accept invalid birth dates', async () => { - for (const { birthDate, response } of invalidBirthday) { + for (const { birthDate, response } of invalidBirthday) { + it(`should not accept an invalid birth date [${birthDate}]`, async () => { const { status, body } = await request(app) .put(`/person/${visiblePerson.id}`) .set('Authorization', `Bearer ${admin.accessToken}`) .send({ birthDate }); expect(status).toBe(400); expect(body).toEqual(errorDto.badRequest(response)); - } - }); + }); + } it('should update a date of birth', async () => { const { status, body } = await request(app) diff --git a/mobile/openapi/doc/PersonApi.md b/mobile/openapi/doc/PersonApi.md index 2ade49aec7411a07be63ee39d7baf42fa21f9565..48c1c3cc4a839e665e4ff64f015c2392c7be2f40 100644 GIT binary patch delta 18 acmaE|gz?f6#toH%lj|MCHZK%B>;M2!;Rp)= delta 37 tcmcb#gz?D|#toH%LeVLyX^EvdB?=|^3TcTs#i_BA1ueuj_X{3&0015e4qyNP diff --git a/mobile/openapi/doc/ScanLibraryDto.md b/mobile/openapi/doc/ScanLibraryDto.md index 39f55290dcca63f11656f437ac471db6f30e2b64..e2c489d852b2392a76fb3d8dd67e637f40e5a96b 100644 GIT binary patch delta 11 Scmey&{E&GA8{=dN#@hfJ1q2xY delta 30 lcmaFJ{F!+J8>3KkN@`kSX-|{2^+W@H#3giF) diff --git a/mobile/openapi/doc/SharedLinkCreateDto.md b/mobile/openapi/doc/SharedLinkCreateDto.md index 8f845dfa4985a96138068f9b5e3665c5ce1510a6..78e208912177214d88d6e3cbb879f850c05d9837 100644 GIT binary patch delta 12 Tcmey(_L6Nw0psQ-#-EG;C3OWt delta 23 ecmaFK_M2@(0psLKCLXr5#GK;P*v$ouKN$gRmk783 diff --git a/mobile/openapi/lib/model/scan_library_dto.dart b/mobile/openapi/lib/model/scan_library_dto.dart index 2b34e2bbeb07b8a8ad8e0509b818527e7132ad50..0f5dedf64a5878e60e023a755648d9020c49342d 100644 GIT binary patch delta 116 zcmaDRvrS>cB*w|J7_~OvWz1sSe1UNjle~hhtwLUDPL6`Tf`NjSLQ!g3QEG99V@{4+ zW=?9c;^rn6bw=ULGzE>4jLc#^g!JSetg4fDvZ_z^W0MEcyxbr|thp3`pmy_Z)-a~c IRvbEP06sA$r2qf` delta 76 zcmdm{@JwdIBt||3TZOd5oZ?g+E(IW%yq8^)AH=NHwB`a*N%{FXo8_2yF>U_Ns?Ips Xjop0nPWC`1P6c~=h_=mKTsmw3NXZpV diff --git a/mobile/openapi/lib/model/shared_link_create_dto.dart b/mobile/openapi/lib/model/shared_link_create_dto.dart index 14dc109a93a4c08fec4f8c239631e8a1daeca8c8..920e62e52e667109f039d8b1d2ddfd439fbc836b 100644 GIT binary patch delta 123 zcmdmD^uu(+ImXS`7*&`yZ)1&RWKGJ?&#~Y9kNGgGw1TazLSAW3j)J{{fr6DnVopwe zd1ygSeqxH^=7XF+m_#zu6f{aQGK=+)q$aQ9m7Ltq!#epmkK*JMUI{LQ+~fith0ULM MyjV873chCt0OYtVc>n+a delta 60 zcmexiy2WV2IYtfzTZOd5oZ{5Y=NMI(Sd#Mdb2f*t9%kKa#QlS5vLB!R=B<1oESw7V L_E2S%d diff --git a/mobile/openapi/test/shared_link_create_dto_test.dart b/mobile/openapi/test/shared_link_create_dto_test.dart index df57e089f54b561a05ea92f28c8cdd140a06f112..982d72a1408fa80d230be912a9236494bea7d61e 100644 GIT binary patch delta 16 YcmaFGeVKd1Wv0om8I?ElGT&ta06(V&od5s; delta 38 ucmcc2{fc|TWhPMtjg-{1#L}D+g|ft)(o`#jw8Wg^$^5L!o4+vKX955jqz)_q diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index bd99b24765..8819825b91 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -4012,7 +4012,6 @@ "required": false, "in": "query", "schema": { - "default": false, "type": "boolean" } } @@ -8937,7 +8936,6 @@ "ScanLibraryDto": { "properties": { "refreshAllFiles": { - "default": false, "type": "boolean" }, "refreshModifiedFiles": { @@ -9346,7 +9344,6 @@ "type": "boolean" }, "allowUpload": { - "default": false, "type": "boolean" }, "assetIds": { diff --git a/server/src/domain/album/dto/album-update.dto.ts b/server/src/domain/album/dto/album-update.dto.ts index 3b1858ba10..1b6c754f02 100644 --- a/server/src/domain/album/dto/album-update.dto.ts +++ b/server/src/domain/album/dto/album-update.dto.ts @@ -1,5 +1,5 @@ -import { IsBoolean, IsString } from 'class-validator'; -import { Optional, ValidateUUID } from '../../domain.util'; +import { IsString } from 'class-validator'; +import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util'; export class UpdateAlbumDto { @Optional() @@ -13,7 +13,6 @@ export class UpdateAlbumDto { @ValidateUUID({ optional: true }) albumThumbnailAssetId?: string; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isActivityEnabled?: boolean; } diff --git a/server/src/domain/album/dto/album.dto.ts b/server/src/domain/album/dto/album.dto.ts index d1fc701a0e..b7aad98b5c 100644 --- a/server/src/domain/album/dto/album.dto.ts +++ b/server/src/domain/album/dto/album.dto.ts @@ -1,10 +1,6 @@ -import { Transform } from 'class-transformer'; -import { IsBoolean } from 'class-validator'; -import { Optional, toBoolean } from '../../domain.util'; +import { ValidateBoolean } from '../../domain.util'; export class AlbumInfoDto { - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) withoutAssets?: boolean; } diff --git a/server/src/domain/album/dto/get-albums.dto.ts b/server/src/domain/album/dto/get-albums.dto.ts index ce037e1899..2628a3fc72 100644 --- a/server/src/domain/album/dto/get-albums.dto.ts +++ b/server/src/domain/album/dto/get-albums.dto.ts @@ -1,13 +1,7 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean } from 'class-validator'; -import { Optional, toBoolean, ValidateUUID } from '../../domain.util'; +import { ValidateBoolean, ValidateUUID } from '../../domain.util'; export class GetAlbumsDto { - @Optional() - @IsBoolean() - @Transform(toBoolean) - @ApiProperty() + @ValidateBoolean({ optional: true }) /** * true: only shared albums * false: only non-shared own albums diff --git a/server/src/domain/asset/dto/asset-statistics.dto.ts b/server/src/domain/asset/dto/asset-statistics.dto.ts index a53e774f44..c313ccdf47 100644 --- a/server/src/domain/asset/dto/asset-statistics.dto.ts +++ b/server/src/domain/asset/dto/asset-statistics.dto.ts @@ -1,24 +1,16 @@ import { AssetType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean } from 'class-validator'; -import { Optional, toBoolean } from '../../domain.util'; +import { ValidateBoolean } from '../../domain.util'; import { AssetStats } from '../../repositories'; export class AssetStatsDto { - @IsBoolean() - @Transform(toBoolean) - @Optional() + @ValidateBoolean({ optional: true }) isArchived?: boolean; - @IsBoolean() - @Transform(toBoolean) - @Optional() + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @IsBoolean() - @Transform(toBoolean) - @Optional() + @ValidateBoolean({ optional: true }) isTrashed?: boolean; } diff --git a/server/src/domain/asset/dto/asset.dto.ts b/server/src/domain/asset/dto/asset.dto.ts index 0244ecd90e..8b5c675d89 100644 --- a/server/src/domain/asset/dto/asset.dto.ts +++ b/server/src/domain/asset/dto/asset.dto.ts @@ -1,6 +1,5 @@ import { Type } from 'class-transformer'; import { - IsBoolean, IsDateString, IsInt, IsLatitude, @@ -10,7 +9,7 @@ import { IsString, ValidateIf, } from 'class-validator'; -import { Optional, ValidateUUID } from '../../domain.util'; +import { Optional, ValidateBoolean, ValidateUUID } from '../../domain.util'; import { BulkIdsDto } from '../response-dto'; export class DeviceIdDto { @@ -28,23 +27,13 @@ const hasGPS = (o: { latitude: undefined; longitude: undefined }) => o.latitude !== undefined || o.longitude !== undefined; const ValidateGPS = () => ValidateIf(hasGPS); -export class AssetBulkUpdateDto extends BulkIdsDto { - @Optional() - @IsBoolean() +export class UpdateAssetBase { + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isArchived?: boolean; - @Optional() - @ValidateUUID() - stackParentId?: string; - - @Optional() - @IsBoolean() - removeParent?: boolean; - @Optional() @IsDateString() dateTimeOriginal?: string; @@ -60,32 +49,21 @@ export class AssetBulkUpdateDto extends BulkIdsDto { longitude?: number; } -export class UpdateAssetDto { - @Optional() - @IsBoolean() - isFavorite?: boolean; +export class AssetBulkUpdateDto extends UpdateAssetBase { + @ValidateUUID({ each: true }) + ids!: string[]; - @Optional() - @IsBoolean() - isArchived?: boolean; + @ValidateUUID({ optional: true }) + stackParentId?: string; + @ValidateBoolean({ optional: true }) + removeParent?: boolean; +} + +export class UpdateAssetDto extends UpdateAssetBase { @Optional() @IsString() description?: string; - - @Optional() - @IsDateString() - dateTimeOriginal?: string; - - @ValidateGPS() - @IsLatitude() - @IsNotEmpty() - latitude?: number; - - @ValidateGPS() - @IsLongitude() - @IsNotEmpty() - longitude?: number; } export class RandomAssetsDto { @@ -97,7 +75,6 @@ export class RandomAssetsDto { } export class AssetBulkDeleteDto extends BulkIdsDto { - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) force?: boolean; } diff --git a/server/src/domain/asset/dto/map-marker.dto.ts b/server/src/domain/asset/dto/map-marker.dto.ts index b703d6e73e..4fe6c16b84 100644 --- a/server/src/domain/asset/dto/map-marker.dto.ts +++ b/server/src/domain/asset/dto/map-marker.dto.ts @@ -1,34 +1,18 @@ -import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsBoolean, IsDate } from 'class-validator'; -import { Optional, toBoolean } from '../../domain.util'; +import { ValidateBoolean, ValidateDate } from '../../domain.util'; export class MapMarkerDto { - @ApiProperty() - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isArchived?: boolean; - @ApiProperty() - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @Optional() - @IsDate() - @Type(() => Date) + @ValidateDate({ optional: true }) fileCreatedAfter?: Date; - @Optional() - @IsDate() - @Type(() => Date) + @ValidateDate({ optional: true }) fileCreatedBefore?: Date; - @ApiProperty() - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) withPartners?: boolean; } diff --git a/server/src/domain/asset/dto/time-bucket.dto.ts b/server/src/domain/asset/dto/time-bucket.dto.ts index 849b8713f0..597a5de356 100644 --- a/server/src/domain/asset/dto/time-bucket.dto.ts +++ b/server/src/domain/asset/dto/time-bucket.dto.ts @@ -1,7 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean, IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { Optional, ValidateUUID, toBoolean } from '../../domain.util'; +import { IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { ValidateBoolean, ValidateUUID } from '../../domain.util'; import { TimeBucketSize } from '../../repositories'; export class TimeBucketDto { @@ -19,34 +18,23 @@ export class TimeBucketDto { @ValidateUUID({ optional: true }) personId?: string; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isArchived?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isTrashed?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) withStacked?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) withPartners?: boolean; } export class TimeBucketAssetDto extends TimeBucketDto { @IsString() - @IsNotEmpty() timeBucket!: string; } diff --git a/server/src/domain/audit/audit.dto.ts b/server/src/domain/audit/audit.dto.ts index d941f9a1df..0f3f04dab2 100644 --- a/server/src/domain/audit/audit.dto.ts +++ b/server/src/domain/audit/audit.dto.ts @@ -1,14 +1,13 @@ import { AssetPathType, EntityType, PathType, PersonPathType, UserPathType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsArray, IsDate, IsEnum, IsString, IsUUID, ValidateNested } from 'class-validator'; -import { Optional, ValidateUUID } from '../domain.util'; +import { IsArray, IsEnum, IsString, IsUUID, ValidateNested } from 'class-validator'; +import { Optional, ValidateDate, ValidateUUID } from '../domain.util'; const PathEnum = Object.values({ ...AssetPathType, ...PersonPathType, ...UserPathType }); export class AuditDeletesDto { - @IsDate() - @Type(() => Date) + @ValidateDate() after!: Date; @ApiProperty({ enum: EntityType, enumName: 'EntityType' }) diff --git a/server/src/domain/domain.util.ts b/server/src/domain/domain.util.ts index 1dadf03aed..a079ff6bf6 100644 --- a/server/src/domain/domain.util.ts +++ b/server/src/domain/domain.util.ts @@ -1,7 +1,7 @@ import { ImmichLogger } from '@app/infra/logger'; -import { applyDecorators } from '@nestjs/common'; +import { BadRequestException, applyDecorators } from '@nestjs/common'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; +import { Transform } from 'class-transformer'; import { IsArray, IsBoolean, @@ -12,6 +12,7 @@ import { IsUUID, ValidateIf, ValidationOptions, + isDateString, } from 'class-validator'; import { CronJob } from 'cron'; import _ from 'lodash'; @@ -40,14 +41,10 @@ export interface OpenGraphTags { imageUrl?: string; } -export type Options = { - optional?: boolean; - each?: boolean; -}; - export const isConnectionAborted = (error: Error | any) => error.code === 'ECONNABORTED'; -export function ValidateUUID(options?: Options) { +type UUIDOptions = { optional?: boolean; each?: boolean }; +export const ValidateUUID = (options?: UUIDOptions) => { const { optional, each } = { optional: false, each: false, ...options }; return applyDecorators( IsUUID('4', { each }), @@ -55,7 +52,58 @@ export function ValidateUUID(options?: Options) { optional ? Optional() : IsNotEmpty(), each ? IsArray() : IsString(), ); -} +}; + +type DateOptions = { optional?: boolean; nullable?: boolean; format?: 'date' | 'date-time' }; +export const ValidateDate = (options?: DateOptions) => { + const { optional, nullable, format } = { optional: false, nullable: false, format: 'date-time', ...options }; + + const decorators = [ + ApiProperty({ format }), + IsDate(), + optional ? Optional({ nullable: true }) : IsNotEmpty(), + Transform(({ key, value }) => { + if (value === null || value === undefined) { + return value; + } + + if (!isDateString(value)) { + throw new BadRequestException(`${key} must be a date string`); + } + + return new Date(value as string); + }), + ]; + + if (optional) { + decorators.push(Optional({ nullable })); + } + + return applyDecorators(...decorators); +}; + +type BooleanOptions = { optional?: boolean }; +export const ValidateBoolean = (options?: BooleanOptions) => { + const { optional } = { optional: false, ...options }; + const decorators = [ + // ApiProperty(), + IsBoolean(), + Transform(({ value }) => { + if (value == 'true') { + return true; + } else if (value == 'false') { + return false; + } + return value; + }), + ]; + + if (optional) { + decorators.push(Optional()); + } + + return applyDecorators(...decorators); +}; export function validateCronExpression(expression: string) { try { @@ -67,34 +115,7 @@ export function validateCronExpression(expression: string) { return true; } -interface IValue { - value?: string; -} - -export const QueryBoolean = ({ optional }: { optional?: boolean }) => { - const decorators = [IsBoolean(), Transform(toBoolean)]; - if (optional) { - decorators.push(Optional()); - } - return applyDecorators(...decorators); -}; - -export const QueryDate = ({ optional }: { optional?: boolean }) => { - const decorators = [IsDate(), Type(() => Date)]; - if (optional) { - decorators.push(Optional()); - } - return applyDecorators(...decorators); -}; - -export const toBoolean = ({ value }: IValue) => { - if (value == 'true') { - return true; - } else if (value == 'false') { - return false; - } - return value; -}; +type IValue = { value: string }; export const toEmail = ({ value }: IValue) => value?.toLowerCase(); diff --git a/server/src/domain/job/job.dto.ts b/server/src/domain/job/job.dto.ts index db0bd8dc43..87be1332f7 100644 --- a/server/src/domain/job/job.dto.ts +++ b/server/src/domain/job/job.dto.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IsBoolean, IsEnum, IsNotEmpty } from 'class-validator'; -import { Optional } from '../domain.util'; +import { IsEnum, IsNotEmpty } from 'class-validator'; +import { ValidateBoolean } from '../domain.util'; import { JobCommand, QueueName } from './job.constants'; export class JobIdParamDto { @@ -16,8 +16,7 @@ export class JobCommandDto { @ApiProperty({ type: 'string', enum: JobCommand, enumName: 'JobCommand' }) command!: JobCommand; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) force!: boolean; } diff --git a/server/src/domain/library/library.dto.ts b/server/src/domain/library/library.dto.ts index b57d56e7b2..b11bc99987 100644 --- a/server/src/domain/library/library.dto.ts +++ b/server/src/domain/library/library.dto.ts @@ -1,7 +1,7 @@ import { LibraryEntity, LibraryType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { ArrayMaxSize, ArrayUnique, IsBoolean, IsEnum, IsNotEmpty, IsString } from 'class-validator'; -import { Optional, ValidateUUID } from '../domain.util'; +import { ArrayMaxSize, ArrayUnique, IsEnum, IsNotEmpty, IsString } from 'class-validator'; +import { Optional, ValidateBoolean, ValidateUUID } from '../domain.util'; export class CreateLibraryDto { @IsEnum(LibraryType) @@ -16,8 +16,7 @@ export class CreateLibraryDto { @IsNotEmpty() name?: string; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isVisible?: boolean; @Optional() @@ -34,8 +33,7 @@ export class CreateLibraryDto { @ArrayMaxSize(128) exclusionPatterns?: string[]; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isWatched?: boolean; } @@ -45,8 +43,7 @@ export class UpdateLibraryDto { @IsNotEmpty() name?: string; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isVisible?: boolean; @Optional() @@ -102,13 +99,11 @@ export class LibrarySearchDto { } export class ScanLibraryDto { - @IsBoolean() - @Optional() + @ValidateBoolean({ optional: true }) refreshModifiedFiles?: boolean; - @IsBoolean() - @Optional() - refreshAllFiles?: boolean = false; + @ValidateBoolean({ optional: true }) + refreshAllFiles?: boolean; } export class SearchLibraryDto { diff --git a/server/src/domain/person/person.dto.ts b/server/src/domain/person/person.dto.ts index e76ce3308e..a00971c6be 100644 --- a/server/src/domain/person/person.dto.ts +++ b/server/src/domain/person/person.dto.ts @@ -1,9 +1,9 @@ import { AssetFaceEntity, PersonEntity } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsArray, IsBoolean, IsDate, IsNotEmpty, IsString, MaxDate, ValidateNested } from 'class-validator'; +import { Type } from 'class-transformer'; +import { IsArray, IsNotEmpty, IsString, MaxDate, ValidateNested } from 'class-validator'; import { AuthDto } from '../auth'; -import { Optional, ValidateUUID, toBoolean } from '../domain.util'; +import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from '../domain.util'; export class PersonCreateDto { /** @@ -17,18 +17,14 @@ export class PersonCreateDto { * Person date of birth. * Note: the mobile app cannot currently set the birth date to null. */ - @Optional({ nullable: true }) - @IsDate() - @Type(() => Date) @MaxDate(() => new Date(), { message: 'Birth date cannot be in the future' }) - @ApiProperty({ format: 'date' }) + @ValidateDate({ optional: true, nullable: true, format: 'date' }) birthDate?: Date | null; /** * Person visibility */ - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isHidden?: boolean; } @@ -63,9 +59,8 @@ export class MergePersonDto { } export class PersonSearchDto { - @IsBoolean() - @Transform(toBoolean) - withHidden?: boolean = false; + @ValidateBoolean({ optional: true }) + withHidden?: boolean; } export class PersonResponseDto { diff --git a/server/src/domain/search/dto/search.dto.ts b/server/src/domain/search/dto/search.dto.ts index c529f6887b..70d8ee2884 100644 --- a/server/src/domain/search/dto/search.dto.ts +++ b/server/src/domain/search/dto/search.dto.ts @@ -1,9 +1,9 @@ import { AssetOrder } from '@app/domain/asset/dto/asset.dto'; import { AssetType, GeodataPlacesEntity } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsBoolean, IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator'; -import { Optional, QueryBoolean, QueryDate, ValidateUUID, toBoolean } from '../../domain.util'; +import { Type } from 'class-transformer'; +import { IsEnum, IsInt, IsNotEmpty, IsString, Max, Min } from 'class-validator'; +import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from '../../domain.util'; class BaseSearchDto { @ValidateUUID({ optional: true }) @@ -19,62 +19,62 @@ class BaseSearchDto { @ApiProperty({ enumName: 'AssetTypeEnum', enum: AssetType }) type?: AssetType; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isArchived?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) @ApiProperty({ default: false }) withArchived?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isEncoded?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isExternal?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isMotion?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isOffline?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isReadOnly?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isVisible?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) withDeleted?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) withExif?: boolean; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) createdBefore?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) createdAfter?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) updatedBefore?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) updatedAfter?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) trashedBefore?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) trashedAfter?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) takenBefore?: Date; - @QueryDate({ optional: true }) + @ValidateDate({ optional: true }) takenAfter?: Date; @IsString() @@ -120,7 +120,7 @@ class BaseSearchDto { @Optional() size?: number; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) isNotInAlbum?: boolean; @Optional() @@ -141,10 +141,10 @@ export class MetadataSearchDto extends BaseSearchDto { @Optional() checksum?: string; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) withStacked?: boolean; - @QueryBoolean({ optional: true }) + @ValidateBoolean({ optional: true }) withPeople?: boolean; @IsString() @@ -197,34 +197,24 @@ export class SearchDto { @Optional() query?: string; - @IsBoolean() - @Optional() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) smart?: boolean; /** @deprecated */ - @IsBoolean() - @Optional() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) clip?: boolean; @IsEnum(AssetType) @Optional() type?: AssetType; - @IsBoolean() - @Optional() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) recent?: boolean; - @IsBoolean() - @Optional() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) motion?: boolean; - @IsBoolean() - @Optional() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) withArchived?: boolean; @IsInt() @@ -252,9 +242,7 @@ export class SearchPeopleDto { @IsNotEmpty() name!: string; - @IsBoolean() - @Transform(toBoolean) - @Optional() + @ValidateBoolean({ optional: true }) withHidden?: boolean; } diff --git a/server/src/domain/shared-link/shared-link.dto.ts b/server/src/domain/shared-link/shared-link.dto.ts index bb5b61820a..550ed70ea1 100644 --- a/server/src/domain/shared-link/shared-link.dto.ts +++ b/server/src/domain/shared-link/shared-link.dto.ts @@ -1,8 +1,7 @@ import { SharedLinkType } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { Type } from 'class-transformer'; -import { IsBoolean, IsDate, IsEnum, IsString } from 'class-validator'; -import { Optional, ValidateUUID } from '../domain.util'; +import { IsEnum, IsString } from 'class-validator'; +import { Optional, ValidateBoolean, ValidateDate, ValidateUUID } from '../domain.util'; export class SharedLinkCreateDto { @IsEnum(SharedLinkType) @@ -23,21 +22,16 @@ export class SharedLinkCreateDto { @Optional() password?: string; - @IsDate() - @Type(() => Date) - @Optional({ nullable: true }) + @ValidateDate({ optional: true, nullable: true }) expiresAt?: Date | null = null; - @Optional() - @IsBoolean() - allowUpload?: boolean = false; + @ValidateBoolean({ optional: true }) + allowUpload?: boolean; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) allowDownload?: boolean = true; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) showMetadata?: boolean = true; } @@ -54,10 +48,10 @@ export class SharedLinkEditDto { @Optional() allowUpload?: boolean; - @Optional() + @ValidateBoolean({ optional: true }) allowDownload?: boolean; - @Optional() + @ValidateBoolean({ optional: true }) showMetadata?: boolean; /** @@ -65,8 +59,7 @@ export class SharedLinkEditDto { * Setting this flag and not sending expiryAt is considered as null instead. * Clients that can send null values can ignore this. */ - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) changeExpiryTime?: boolean; } diff --git a/server/src/domain/smart-info/dto/model-config.dto.ts b/server/src/domain/smart-info/dto/model-config.dto.ts index 64f8c1d009..b9e27669f3 100644 --- a/server/src/domain/smart-info/dto/model-config.dto.ts +++ b/server/src/domain/smart-info/dto/model-config.dto.ts @@ -1,11 +1,11 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsBoolean, IsEnum, IsNotEmpty, IsNumber, IsString, Max, Min } from 'class-validator'; -import { Optional } from '../../domain.util'; +import { IsEnum, IsNotEmpty, IsNumber, IsString, Max, Min } from 'class-validator'; +import { Optional, ValidateBoolean } from '../../domain.util'; import { CLIPMode, ModelType } from '../../repositories'; export class ModelConfig { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @IsString() diff --git a/server/src/domain/system-config/dto/system-config-ffmpeg.dto.ts b/server/src/domain/system-config/dto/system-config-ffmpeg.dto.ts index 2783e35e67..3a219888fb 100644 --- a/server/src/domain/system-config/dto/system-config-ffmpeg.dto.ts +++ b/server/src/domain/system-config/dto/system-config-ffmpeg.dto.ts @@ -1,7 +1,8 @@ import { AudioCodec, CQMode, ToneMapping, TranscodeHWAccel, TranscodePolicy, VideoCodec } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsBoolean, IsEnum, IsInt, IsString, Max, Min } from 'class-validator'; +import { IsEnum, IsInt, IsString, Max, Min } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigFFmpegDto { @IsInt() @@ -68,14 +69,14 @@ export class SystemConfigFFmpegDto { @ApiProperty({ type: 'integer' }) npl!: number; - @IsBoolean() + @ValidateBoolean() temporalAQ!: boolean; @IsEnum(CQMode) @ApiProperty({ enumName: 'CQMode', enum: CQMode }) cqMode!: CQMode; - @IsBoolean() + @ValidateBoolean() twoPass!: boolean; @IsString() diff --git a/server/src/domain/system-config/dto/system-config-library.dto.ts b/server/src/domain/system-config/dto/system-config-library.dto.ts index fdbae600f6..85ab626345 100644 --- a/server/src/domain/system-config/dto/system-config-library.dto.ts +++ b/server/src/domain/system-config/dto/system-config-library.dto.ts @@ -1,7 +1,5 @@ -import { validateCronExpression } from '@app/domain'; import { Type } from 'class-transformer'; import { - IsBoolean, IsNotEmpty, IsObject, IsString, @@ -11,6 +9,7 @@ import { ValidatorConstraint, ValidatorConstraintInterface, } from 'class-validator'; +import { ValidateBoolean, validateCronExpression } from '../../domain.util'; const isEnabled = (config: SystemConfigLibraryScanDto) => config.enabled; @@ -22,7 +21,7 @@ class CronValidator implements ValidatorConstraintInterface { } export class SystemConfigLibraryScanDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @ValidateIf(isEnabled) @@ -33,7 +32,7 @@ export class SystemConfigLibraryScanDto { } export class SystemConfigLibraryWatchDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; } diff --git a/server/src/domain/system-config/dto/system-config-logging.dto.ts b/server/src/domain/system-config/dto/system-config-logging.dto.ts index d280df5356..09f78fc862 100644 --- a/server/src/domain/system-config/dto/system-config-logging.dto.ts +++ b/server/src/domain/system-config/dto/system-config-logging.dto.ts @@ -1,9 +1,10 @@ import { LogLevel } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; -import { IsBoolean, IsEnum } from 'class-validator'; +import { IsEnum } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigLoggingDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @ApiProperty({ enum: LogLevel, enumName: 'LogLevel' }) diff --git a/server/src/domain/system-config/dto/system-config-machine-learning.dto.ts b/server/src/domain/system-config/dto/system-config-machine-learning.dto.ts index ed331eb6a1..435e688268 100644 --- a/server/src/domain/system-config/dto/system-config-machine-learning.dto.ts +++ b/server/src/domain/system-config/dto/system-config-machine-learning.dto.ts @@ -1,9 +1,10 @@ -import { CLIPConfig, RecognitionConfig } from '@app/domain'; import { Type } from 'class-transformer'; -import { IsBoolean, IsObject, IsUrl, ValidateIf, ValidateNested } from 'class-validator'; +import { IsObject, IsUrl, ValidateIf, ValidateNested } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; +import { CLIPConfig, RecognitionConfig } from '../../smart-info/dto/model-config.dto'; export class SystemConfigMachineLearningDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @IsUrl({ require_tld: false, allow_underscores: true }) diff --git a/server/src/domain/system-config/dto/system-config-map.dto.ts b/server/src/domain/system-config/dto/system-config-map.dto.ts index 07700d98cd..9e21e2d5de 100644 --- a/server/src/domain/system-config/dto/system-config-map.dto.ts +++ b/server/src/domain/system-config/dto/system-config-map.dto.ts @@ -1,7 +1,8 @@ -import { IsBoolean, IsString } from 'class-validator'; +import { IsString } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigMapDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @IsString() diff --git a/server/src/domain/system-config/dto/system-config-new-version-check.dto.ts b/server/src/domain/system-config/dto/system-config-new-version-check.dto.ts index c276739243..379f5643de 100644 --- a/server/src/domain/system-config/dto/system-config-new-version-check.dto.ts +++ b/server/src/domain/system-config/dto/system-config-new-version-check.dto.ts @@ -1,6 +1,6 @@ -import { IsBoolean } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigNewVersionCheckDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; } diff --git a/server/src/domain/system-config/dto/system-config-oauth.dto.ts b/server/src/domain/system-config/dto/system-config-oauth.dto.ts index 04159b8d34..99779bdfe4 100644 --- a/server/src/domain/system-config/dto/system-config-oauth.dto.ts +++ b/server/src/domain/system-config/dto/system-config-oauth.dto.ts @@ -1,13 +1,14 @@ -import { IsBoolean, IsNotEmpty, IsNumber, IsString, IsUrl, Min, ValidateIf } from 'class-validator'; +import { IsNotEmpty, IsNumber, IsString, IsUrl, Min, ValidateIf } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; const isEnabled = (config: SystemConfigOAuthDto) => config.enabled; const isOverrideEnabled = (config: SystemConfigOAuthDto) => config.mobileOverrideEnabled; export class SystemConfigOAuthDto { - @IsBoolean() + @ValidateBoolean() autoLaunch!: boolean; - @IsBoolean() + @ValidateBoolean() autoRegister!: boolean; @IsString() @@ -27,7 +28,7 @@ export class SystemConfigOAuthDto { @Min(0) defaultStorageQuota!: number; - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @ValidateIf(isEnabled) @@ -35,7 +36,7 @@ export class SystemConfigOAuthDto { @IsString() issuerUrl!: string; - @IsBoolean() + @ValidateBoolean() mobileOverrideEnabled!: boolean; @ValidateIf(isOverrideEnabled) diff --git a/server/src/domain/system-config/dto/system-config-password-login.dto.ts b/server/src/domain/system-config/dto/system-config-password-login.dto.ts index 119de65f61..279bcc5a61 100644 --- a/server/src/domain/system-config/dto/system-config-password-login.dto.ts +++ b/server/src/domain/system-config/dto/system-config-password-login.dto.ts @@ -1,6 +1,6 @@ -import { IsBoolean } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigPasswordLoginDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; } diff --git a/server/src/domain/system-config/dto/system-config-reverse-geocoding.dto.ts b/server/src/domain/system-config/dto/system-config-reverse-geocoding.dto.ts index aa224ccc6c..11e0ae289d 100644 --- a/server/src/domain/system-config/dto/system-config-reverse-geocoding.dto.ts +++ b/server/src/domain/system-config/dto/system-config-reverse-geocoding.dto.ts @@ -1,6 +1,6 @@ -import { IsBoolean } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigReverseGeocodingDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; } diff --git a/server/src/domain/system-config/dto/system-config-storage-template.dto.ts b/server/src/domain/system-config/dto/system-config-storage-template.dto.ts index c09b5564aa..615fd8521c 100644 --- a/server/src/domain/system-config/dto/system-config-storage-template.dto.ts +++ b/server/src/domain/system-config/dto/system-config-storage-template.dto.ts @@ -1,10 +1,13 @@ -import { IsBoolean, IsNotEmpty, IsString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigStorageTemplateDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; - @IsBoolean() + + @ValidateBoolean() hashVerificationEnabled!: boolean; + @IsNotEmpty() @IsString() template!: string; diff --git a/server/src/domain/system-config/dto/system-config-trash.dto.ts b/server/src/domain/system-config/dto/system-config-trash.dto.ts index bfbdb39415..4824107032 100644 --- a/server/src/domain/system-config/dto/system-config-trash.dto.ts +++ b/server/src/domain/system-config/dto/system-config-trash.dto.ts @@ -1,9 +1,10 @@ import { ApiProperty } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsBoolean, IsInt, Min } from 'class-validator'; +import { IsInt, Min } from 'class-validator'; +import { ValidateBoolean } from '../../domain.util'; export class SystemConfigTrashDto { - @IsBoolean() + @ValidateBoolean() enabled!: boolean; @IsInt() diff --git a/server/src/domain/user/dto/create-user.dto.ts b/server/src/domain/user/dto/create-user.dto.ts index 737b68c036..f0cc7938c6 100644 --- a/server/src/domain/user/dto/create-user.dto.ts +++ b/server/src/domain/user/dto/create-user.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsEmail, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator'; -import { Optional, toEmail, toSanitized } from '../../domain.util'; +import { IsEmail, IsNotEmpty, IsNumber, IsPositive, IsString } from 'class-validator'; +import { Optional, ValidateBoolean, toEmail, toSanitized } from '../../domain.util'; export class CreateUserDto { @IsEmail({ require_tld: false }) @@ -21,8 +21,7 @@ export class CreateUserDto { @Transform(toSanitized) storageLabel?: string | null; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) memoriesEnabled?: boolean; @Optional({ nullable: true }) @@ -31,8 +30,7 @@ export class CreateUserDto { @ApiProperty({ type: 'integer', format: 'int64' }) quotaSizeInBytes?: number | null; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) shouldChangePassword?: boolean; } diff --git a/server/src/domain/user/dto/update-user.dto.ts b/server/src/domain/user/dto/update-user.dto.ts index 1cab11627b..e8cce22141 100644 --- a/server/src/domain/user/dto/update-user.dto.ts +++ b/server/src/domain/user/dto/update-user.dto.ts @@ -1,8 +1,8 @@ import { UserAvatarColor } from '@app/infra/entities'; import { ApiProperty } from '@nestjs/swagger'; import { Transform } from 'class-transformer'; -import { IsBoolean, IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString, IsUUID } from 'class-validator'; -import { Optional, toEmail, toSanitized } from '../../domain.util'; +import { IsEmail, IsEnum, IsNotEmpty, IsNumber, IsPositive, IsString, IsUUID } from 'class-validator'; +import { Optional, ValidateBoolean, toEmail, toSanitized } from '../../domain.util'; export class UpdateUserDto { @Optional() @@ -30,16 +30,13 @@ export class UpdateUserDto { @ApiProperty({ format: 'uuid' }) id!: string; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) isAdmin?: boolean; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) shouldChangePassword?: boolean; - @Optional() - @IsBoolean() + @ValidateBoolean({ optional: true }) memoriesEnabled?: boolean; @Optional() diff --git a/server/src/immich/api-v1/asset/dto/asset-search.dto.ts b/server/src/immich/api-v1/asset/dto/asset-search.dto.ts index d73856ab9a..7190184881 100644 --- a/server/src/immich/api-v1/asset/dto/asset-search.dto.ts +++ b/server/src/immich/api-v1/asset/dto/asset-search.dto.ts @@ -1,19 +1,13 @@ -import { Optional, toBoolean } from '@app/domain'; +import { Optional, ValidateBoolean, ValidateDate } from '@app/domain'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsBoolean, IsDate, IsInt, IsNotEmpty, IsUUID } from 'class-validator'; +import { Type } from 'class-transformer'; +import { IsInt, IsUUID } from 'class-validator'; export class AssetSearchDto { - @Optional() - @IsNotEmpty() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @Optional() - @IsNotEmpty() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isArchived?: boolean; @Optional() @@ -33,13 +27,9 @@ export class AssetSearchDto { @ApiProperty({ format: 'uuid' }) userId?: string; - @Optional() - @IsDate() - @Type(() => Date) + @ValidateDate({ optional: true }) updatedAfter?: Date; - @Optional() - @IsDate() - @Type(() => Date) + @ValidateDate({ optional: true }) updatedBefore?: Date; } diff --git a/server/src/immich/api-v1/asset/dto/create-asset.dto.ts b/server/src/immich/api-v1/asset/dto/create-asset.dto.ts index 9850384d96..1b140d69f8 100644 --- a/server/src/immich/api-v1/asset/dto/create-asset.dto.ts +++ b/server/src/immich/api-v1/asset/dto/create-asset.dto.ts @@ -1,7 +1,6 @@ -import { Optional, toBoolean, UploadFieldName, ValidateUUID } from '@app/domain'; +import { Optional, UploadFieldName, ValidateBoolean, ValidateDate, ValidateUUID } from '@app/domain'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform, Type } from 'class-transformer'; -import { IsBoolean, IsDate, IsNotEmpty, IsString } from 'class-validator'; +import { IsNotEmpty, IsString } from 'class-validator'; export class CreateAssetDto { @ValidateUUID({ optional: true }) @@ -15,43 +14,29 @@ export class CreateAssetDto { @IsString() deviceId!: string; - @IsNotEmpty() - @IsDate() - @Type(() => Date) + @ValidateDate() fileCreatedAt!: Date; - @IsNotEmpty() - @IsDate() - @Type(() => Date) + @ValidateDate() fileModifiedAt!: Date; @Optional() @IsString() duration?: string; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isFavorite?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isArchived?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isVisible?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isOffline?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) + @ValidateBoolean({ optional: true }) isReadOnly?: boolean; // The properties below are added to correctly generate the API docs diff --git a/server/src/immich/api-v1/asset/dto/serve-file.dto.ts b/server/src/immich/api-v1/asset/dto/serve-file.dto.ts index dd13c6dfbc..72e2286014 100644 --- a/server/src/immich/api-v1/asset/dto/serve-file.dto.ts +++ b/server/src/immich/api-v1/asset/dto/serve-file.dto.ts @@ -1,18 +1,12 @@ -import { Optional, toBoolean } from '@app/domain'; +import { ValidateBoolean } from '@app/domain'; import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { IsBoolean } from 'class-validator'; export class ServeFileDto { - @Optional() - @IsBoolean() - @Transform(toBoolean) - @ApiProperty({ type: Boolean, title: 'Is serve thumbnail (resize) file' }) + @ValidateBoolean({ optional: true }) + @ApiProperty({ title: 'Is serve thumbnail (resize) file' }) isThumb?: boolean; - @Optional() - @IsBoolean() - @Transform(toBoolean) - @ApiProperty({ type: Boolean, title: 'Is request made from web' }) + @ValidateBoolean({ optional: true }) + @ApiProperty({ title: 'Is request made from web' }) isWeb?: boolean; }