From 94d070560767bd0d48e382531053dd5bd6bb0876 Mon Sep 17 00:00:00 2001 From: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com> Date: Mon, 29 May 2023 16:05:14 +0200 Subject: [PATCH] refactor(server): change asset entity to date type (#2599) * refactor(server): change asset entity to date type * lower coverage threshold --- mobile/lib/shared/models/asset.dart | 6 +- mobile/openapi/doc/AssetApi.md | Bin 58115 -> 58130 bytes mobile/openapi/doc/AssetResponseDto.md | Bin 1368 -> 1419 bytes mobile/openapi/lib/api/asset_api.dart | Bin 52327 -> 52343 bytes .../openapi/lib/model/asset_response_dto.dart | Bin 9726 -> 9789 bytes mobile/openapi/test/asset_api_test.dart | Bin 5603 -> 5607 bytes .../openapi/test/asset_response_dto_test.dart | Bin 2779 -> 2785 bytes .../src/api-v1/asset/asset.service.spec.ts | 16 ++--- .../src/api-v1/asset/dto/create-asset.dto.ts | 4 +- .../metadata-extraction.processor.ts | 6 +- server/immich-openapi-specs.json | 5 ++ .../libs/domain/src/asset/asset.repository.ts | 4 +- .../domain/src/asset/dto/map-marker.dto.ts | 16 ++--- .../asset/response-dto/asset-response.dto.ts | 6 +- .../storage-template/storage-template.core.ts | 4 +- server/libs/domain/test/fixtures.ts | 62 +++++++++--------- .../libs/infra/src/entities/asset.entity.ts | 8 +-- server/package.json | 2 +- 18 files changed, 72 insertions(+), 67 deletions(-) diff --git a/mobile/lib/shared/models/asset.dart b/mobile/lib/shared/models/asset.dart index 0bee110f8a..840653554e 100644 --- a/mobile/lib/shared/models/asset.dart +++ b/mobile/lib/shared/models/asset.dart @@ -15,9 +15,9 @@ class Asset { Asset.remote(AssetResponseDto remote) : remoteId = remote.id, isLocal = false, - fileCreatedAt = DateTime.parse(remote.fileCreatedAt), - fileModifiedAt = DateTime.parse(remote.fileModifiedAt), - updatedAt = DateTime.parse(remote.updatedAt), + fileCreatedAt = remote.fileCreatedAt, + fileModifiedAt = remote.fileModifiedAt, + updatedAt = remote.updatedAt, durationInSeconds = remote.duration.toDuration()?.inSeconds ?? 0, type = remote.type.toAssetType(), fileName = p.basename(remote.originalPath), diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 9bee8759acd91d967ae8442302808117aa042139..9a1c07340653f1e42ac0c7f78eb0c2fb88ddd8e3 100644 GIT binary patch delta 57 zcmZoZ#ysg5^M=6NlUJ|Mnp}EY9852X)Y^Re_C96~m&B6Pkj&iF$@A|kz&V?b-nU~0 E03hcYbpQYW delta 109 zcmbPqjJf$3^M=6NylI&^sm?{Ii6yBijwO@r1H~sh++mXkiTdWJWTs_;gcWQRPz7M> oCa;QA+5GJGKIX|kvUw+eIK?t~!hM#>yYCA^`D~jX-Zx|h0Eyu*hyVZp diff --git a/mobile/openapi/doc/AssetResponseDto.md b/mobile/openapi/doc/AssetResponseDto.md index e4cef33ec3c4be58fce5bd1633f4e728ff38ab1b..f64e7144f62b38a2f2c578faffc094e702e7b877 100644 GIT binary patch delta 146 zcmcb?)y=(uo2fopOUos(BsC;6H&sh3Rs+V?%T3W#s8IkSE-kIJ%$!u;{FKbJ%+wUe c5-lyDC^6bf3sQh4CSks`%$!u;{FKbJ%+wUelF1L4BqpaZ@qwf^&tUq@1OQW>53B$H diff --git a/mobile/openapi/lib/api/asset_api.dart b/mobile/openapi/lib/api/asset_api.dart index 609a6536cdd95648bafffb38df082fb4f1d43e17..e27d4e455773475f63c656d4f0f0fb6b9adaefeb 100644 GIT binary patch delta 131 zcmaDpgZcXm<_+aLCi8WPPVU+v2c-EnZ`pB{QNbm#BsC;6H&r1mGbh!#C>6*}aV*h+ mi%?l#~;(;mz?Iq`TSTq!OiHvc=G$pQdjR5rZ; delta 127 zcmex9gZcRk<_+aLCJTgeOn!V+6GU$oI~LA3IYyFevgob?nc$M5%)E4kw9K4T=c3fa nlGGH(5*-DI=w!YVM<-ukvfsRNw*eQL5jPm|YTe9tB9jFG(8Mwh diff --git a/mobile/openapi/lib/model/asset_response_dto.dart b/mobile/openapi/lib/model/asset_response_dto.dart index 664a5a76f7aefc9147b841e02e471ed4265a4d9a..22f6f5f5d13470d1ac8ce6671abf01d9c5de653c 100644 GIT binary patch delta 274 zcmez8z1L?$1B<;&Vo7R9W^SrNT4qkFb5Sago8nkv&BdjlfFk9apOTrDi6OUnCyO$l zlwL`GXi2h$CXn_l&bKf#Fbpm!%FIjG(42fn$P}Nnps+R`>CHC6EgVWPcW7i4=jZ7t z6se;)Rb59xeez3b1rC_9$ucq$YCu7CO+_7`FCd__AO+$xD}~&|0=Uvd8DVCi(#^#( G-JAf}lvhFk delta 136 zcmdn%^Ur%j1B+#FNl|8AxLOJVCgHWAZU68D12FCY#A9fJ7&!%kWRGl92+jIZF#tAl7W&B-70a03s$Z AZvX%Q diff --git a/mobile/openapi/test/asset_api_test.dart b/mobile/openapi/test/asset_api_test.dart index e9615e2d50a140b999dad9536ab59f8b3d14b9b3..e5d50815ca37a3ad974cda4376e5f9aa41e87786 100644 GIT binary patch delta 27 jcmaE?{aky)Ns-CQ0;1d{Itng{C8;5qxv7(%iu?lrm2e8{ delta 40 rcmaE^{aAa$NfDXglA_GKbcM9coK)wc)Wnk16vq-B1&HY6mm>cFRYefL diff --git a/mobile/openapi/test/asset_response_dto_test.dart b/mobile/openapi/test/asset_response_dto_test.dart index fb446bd21a98bb50a7782cb5a1a9558207e14469..34b31d510e339f87cb111795673695a17f8f1b47 100644 GIT binary patch delta 47 ncmcaD`cQNOH!FurVo7R9W^U?aLDozJr-3ya&e{Bt^%V;Mi3Sl8 delta 53 zcmaDTdRue@H|r#3cFwfSoK)wc)Wnj>{j8dk-B`_lf|FZWvw#$9X+a8DTy?V&+kO@R D*oYAe diff --git a/server/apps/immich/src/api-v1/asset/asset.service.spec.ts b/server/apps/immich/src/api-v1/asset/asset.service.spec.ts index 98838de378..abe63eac7e 100644 --- a/server/apps/immich/src/api-v1/asset/asset.service.spec.ts +++ b/server/apps/immich/src/api-v1/asset/asset.service.spec.ts @@ -37,8 +37,8 @@ const _getCreateAssetDto = (): CreateAssetDto => { createAssetDto.deviceAssetId = 'deviceAssetId'; createAssetDto.deviceId = 'deviceId'; createAssetDto.assetType = AssetType.OTHER; - createAssetDto.fileCreatedAt = '2022-06-19T23:41:36.910Z'; - createAssetDto.fileModifiedAt = '2022-06-19T23:41:36.910Z'; + createAssetDto.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z'); + createAssetDto.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z'); createAssetDto.isFavorite = false; createAssetDto.isArchived = false; createAssetDto.duration = '0:00:00.000000'; @@ -56,9 +56,9 @@ const _getAsset_1 = () => { asset_1.type = AssetType.VIDEO; asset_1.originalPath = 'fake_path/asset_1.jpeg'; asset_1.resizePath = ''; - asset_1.fileModifiedAt = '2022-06-19T23:41:36.910Z'; - asset_1.fileCreatedAt = '2022-06-19T23:41:36.910Z'; - asset_1.updatedAt = '2022-06-19T23:41:36.910Z'; + asset_1.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_1.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_1.updatedAt = new Date('2022-06-19T23:41:36.910Z'); asset_1.isFavorite = false; asset_1.isArchived = false; asset_1.mimeType = 'image/jpeg'; @@ -81,9 +81,9 @@ const _getAsset_2 = () => { asset_2.type = AssetType.VIDEO; asset_2.originalPath = 'fake_path/asset_2.jpeg'; asset_2.resizePath = ''; - asset_2.fileModifiedAt = '2022-06-19T23:41:36.910Z'; - asset_2.fileCreatedAt = '2022-06-19T23:41:36.910Z'; - asset_2.updatedAt = '2022-06-19T23:41:36.910Z'; + asset_2.fileModifiedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_2.fileCreatedAt = new Date('2022-06-19T23:41:36.910Z'); + asset_2.updatedAt = new Date('2022-06-19T23:41:36.910Z'); asset_2.isFavorite = false; asset_2.isArchived = false; asset_2.mimeType = 'image/jpeg'; diff --git a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts index 582b1249aa..1c4880fc80 100644 --- a/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts +++ b/server/apps/immich/src/api-v1/asset/dto/create-asset.dto.ts @@ -16,10 +16,10 @@ export class CreateAssetDto { assetType!: AssetType; @IsNotEmpty() - fileCreatedAt!: string; + fileCreatedAt!: Date; @IsNotEmpty() - fileModifiedAt!: string; + fileModifiedAt!: Date; @IsNotEmpty() isFavorite!: boolean; diff --git a/server/apps/microservices/src/processors/metadata-extraction.processor.ts b/server/apps/microservices/src/processors/metadata-extraction.processor.ts index af8767a35b..8395b9fdad 100644 --- a/server/apps/microservices/src/processors/metadata-extraction.processor.ts +++ b/server/apps/microservices/src/processors/metadata-extraction.processor.ts @@ -214,7 +214,7 @@ export class MetadataExtractionProcessor { } await this.exifRepository.upsert(newExif, { conflictPaths: ['assetId'] }); - await this.assetRepository.save({ id: asset.id, fileCreatedAt: fileCreatedAt?.toISOString() }); + await this.assetRepository.save({ id: asset.id, fileCreatedAt: fileCreatedAt || undefined }); return true; } @@ -227,9 +227,9 @@ export class MetadataExtractionProcessor { const videoTags = data.format.tags; if (videoTags) { if (videoTags['com.apple.quicktime.creationdate']) { - fileCreatedAt = String(videoTags['com.apple.quicktime.creationdate']); + fileCreatedAt = new Date(videoTags['com.apple.quicktime.creationdate']); } else if (videoTags['creation_time']) { - fileCreatedAt = String(videoTags['creation_time']); + fileCreatedAt = new Date(videoTags['creation_time']); } } diff --git a/server/immich-openapi-specs.json b/server/immich-openapi-specs.json index c795ef0f78..2fbf1a22a4 100644 --- a/server/immich-openapi-specs.json +++ b/server/immich-openapi-specs.json @@ -4450,12 +4450,15 @@ "type": "boolean" }, "fileCreatedAt": { + "format": "date-time", "type": "string" }, "fileModifiedAt": { + "format": "date-time", "type": "string" }, "updatedAt": { + "format": "date-time", "type": "string" }, "isFavorite": { @@ -5783,9 +5786,11 @@ "type": "string" }, "fileCreatedAt": { + "format": "date-time", "type": "string" }, "fileModifiedAt": { + "format": "date-time", "type": "string" }, "isFavorite": { diff --git a/server/libs/domain/src/asset/asset.repository.ts b/server/libs/domain/src/asset/asset.repository.ts index efdfe2eca0..21f88a0c8e 100644 --- a/server/libs/domain/src/asset/asset.repository.ts +++ b/server/libs/domain/src/asset/asset.repository.ts @@ -15,8 +15,8 @@ export interface LivePhotoSearchOptions { export interface MapMarkerSearchOptions { isFavorite?: boolean; - fileCreatedBefore?: string; - fileCreatedAfter?: string; + fileCreatedBefore?: Date; + fileCreatedAfter?: Date; } export interface MapMarker { diff --git a/server/libs/domain/src/asset/dto/map-marker.dto.ts b/server/libs/domain/src/asset/dto/map-marker.dto.ts index 1fef60c16e..5398b95472 100644 --- a/server/libs/domain/src/asset/dto/map-marker.dto.ts +++ b/server/libs/domain/src/asset/dto/map-marker.dto.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { toBoolean } from 'apps/immich/src/utils/transform.util'; -import { Transform } from 'class-transformer'; -import { IsBoolean, IsISO8601, IsOptional } from 'class-validator'; +import { Transform, Type } from 'class-transformer'; +import { IsBoolean, IsDate, IsOptional } from 'class-validator'; export class MapMarkerDto { @ApiProperty() @@ -10,13 +10,13 @@ export class MapMarkerDto { @Transform(toBoolean) isFavorite?: boolean; - @ApiProperty({ format: 'date-time' }) @IsOptional() - @IsISO8601({ strict: true, strictSeparator: true }) - fileCreatedAfter?: string; + @IsDate() + @Type(() => Date) + fileCreatedAfter?: Date; - @ApiProperty({ format: 'date-time' }) @IsOptional() - @IsISO8601({ strict: true, strictSeparator: true }) - fileCreatedBefore?: string; + @IsDate() + @Type(() => Date) + fileCreatedBefore?: Date; } diff --git a/server/libs/domain/src/asset/response-dto/asset-response.dto.ts b/server/libs/domain/src/asset/response-dto/asset-response.dto.ts index 8a17466132..4ece34bcd6 100644 --- a/server/libs/domain/src/asset/response-dto/asset-response.dto.ts +++ b/server/libs/domain/src/asset/response-dto/asset-response.dto.ts @@ -16,9 +16,9 @@ export class AssetResponseDto { originalPath!: string; originalFileName!: string; resized!: boolean; - fileCreatedAt!: string; - fileModifiedAt!: string; - updatedAt!: string; + fileCreatedAt!: Date; + fileModifiedAt!: Date; + updatedAt!: Date; isFavorite!: boolean; isArchived!: boolean; mimeType!: string | null; diff --git a/server/libs/domain/src/storage-template/storage-template.core.ts b/server/libs/domain/src/storage-template/storage-template.core.ts index 85c3d12b2b..da97ab6294 100644 --- a/server/libs/domain/src/storage-template/storage-template.core.ts +++ b/server/libs/domain/src/storage-template/storage-template.core.ts @@ -107,7 +107,7 @@ export class StorageTemplateCore { this.render( template, { - fileCreatedAt: new Date().toISOString(), + fileCreatedAt: new Date(), originalPath: '/upload/test/IMG_123.jpg', type: AssetType.IMAGE, } as AssetEntity, @@ -140,7 +140,7 @@ export class StorageTemplateCore { filetypefull: asset.type == AssetType.IMAGE ? 'IMAGE' : 'VIDEO', }; - const dt = luxon.DateTime.fromISO(new Date(asset.fileCreatedAt).toISOString()); + const dt = luxon.DateTime.fromJSDate(asset.fileCreatedAt); const dateTokens = [ ...supportedYearTokens, diff --git a/server/libs/domain/test/fixtures.ts b/server/libs/domain/test/fixtures.ts index 1a1753352c..da420346fe 100644 --- a/server/libs/domain/test/fixtures.ts +++ b/server/libs/domain/test/fixtures.ts @@ -140,8 +140,8 @@ export const assetEntityStub = { id: 'asset-id', originalFileName: 'asset_1.jpeg', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -151,8 +151,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -168,8 +168,8 @@ export const assetEntityStub = { image: Object.freeze({ id: 'asset-id', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -179,8 +179,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -198,8 +198,8 @@ export const assetEntityStub = { id: 'asset-id', originalFileName: 'asset-id.ext', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -209,8 +209,8 @@ export const assetEntityStub = { type: AssetType.VIDEO, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -229,8 +229,8 @@ export const assetEntityStub = { ownerId: authStub.user1.id, type: AssetType.VIDEO, isVisible: false, - fileModifiedAt: '2022-06-19T23:41:36.910Z', - fileCreatedAt: '2022-06-19T23:41:36.910Z', + fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), + fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), } as AssetEntity), livePhotoStillAsset: Object.freeze({ @@ -240,15 +240,15 @@ export const assetEntityStub = { type: AssetType.IMAGE, livePhotoVideoId: 'live-photo-motion-asset', isVisible: true, - fileModifiedAt: '2022-06-19T23:41:36.910Z', - fileCreatedAt: '2022-06-19T23:41:36.910Z', + fileModifiedAt: new Date('2022-06-19T23:41:36.910Z'), + fileCreatedAt: new Date('2022-06-19T23:41:36.910Z'), } as AssetEntity), withLocation: Object.freeze({ id: 'asset-with-favorite-id', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -259,8 +259,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: false, isArchived: false, @@ -280,8 +280,8 @@ export const assetEntityStub = { sidecar: Object.freeze({ id: 'asset-id', deviceAssetId: 'device-asset-id', - fileModifiedAt: '2023-02-23T05:06:29.716Z', - fileCreatedAt: '2023-02-23T05:06:29.716Z', + fileModifiedAt: new Date('2023-02-23T05:06:29.716Z'), + fileCreatedAt: new Date('2023-02-23T05:06:29.716Z'), owner: userEntityStub.user1, ownerId: 'user-id', deviceId: 'device-id', @@ -291,8 +291,8 @@ export const assetEntityStub = { type: AssetType.IMAGE, webpPath: null, encodedVideoPath: null, - createdAt: '2023-02-23T05:06:29.716Z', - updatedAt: '2023-02-23T05:06:29.716Z', + createdAt: new Date('2023-02-23T05:06:29.716Z'), + updatedAt: new Date('2023-02-23T05:06:29.716Z'), mimeType: null, isFavorite: true, isArchived: false, @@ -447,9 +447,9 @@ const assetResponse: AssetResponseDto = { originalPath: 'fake_path/jpeg', originalFileName: 'asset_1.jpeg', resized: false, - fileModifiedAt: today.toISOString(), - fileCreatedAt: today.toISOString(), - updatedAt: today.toISOString(), + fileModifiedAt: today, + fileCreatedAt: today, + updatedAt: today, isFavorite: false, isArchived: false, mimeType: 'image/jpeg', @@ -700,10 +700,10 @@ export const sharedLinkStub = { originalPath: 'fake_path/jpeg', resizePath: '', checksum: Buffer.from('file hash', 'utf8'), - fileModifiedAt: today.toISOString(), - fileCreatedAt: today.toISOString(), - createdAt: today.toISOString(), - updatedAt: today.toISOString(), + fileModifiedAt: today, + fileCreatedAt: today, + createdAt: today, + updatedAt: today, isFavorite: false, isArchived: false, mimeType: 'image/jpeg', diff --git a/server/libs/infra/src/entities/asset.entity.ts b/server/libs/infra/src/entities/asset.entity.ts index 58ea69565e..202e393d22 100644 --- a/server/libs/infra/src/entities/asset.entity.ts +++ b/server/libs/infra/src/entities/asset.entity.ts @@ -55,16 +55,16 @@ export class AssetEntity { encodedVideoPath!: string | null; @CreateDateColumn({ type: 'timestamptz' }) - createdAt!: string; + createdAt!: Date; @UpdateDateColumn({ type: 'timestamptz' }) - updatedAt!: string; + updatedAt!: Date; @Column({ type: 'timestamptz' }) - fileCreatedAt!: string; + fileCreatedAt!: Date; @Column({ type: 'timestamptz' }) - fileModifiedAt!: string; + fileModifiedAt!: Date; @Column({ type: 'boolean', default: false }) isFavorite!: boolean; diff --git a/server/package.json b/server/package.json index f3f7722720..5a4f1bf391 100644 --- a/server/package.json +++ b/server/package.json @@ -141,7 +141,7 @@ "./libs/domain/": { "branches": 80, "functions": 87, - "lines": 93.8, + "lines": 93.7, "statements": 93 } },