From eb73f6605bc6260c358bf1a988538fc96ebbd73e Mon Sep 17 00:00:00 2001 From: Mert <101130780+mertalev@users.noreply.github.com> Date: Tue, 20 Feb 2024 22:59:26 -0500 Subject: [PATCH] fix(server): don't return archived assets by default (#7278) * don't show archived results by default * fix e2e * generate sql * set default in dto --------- Co-authored-by: Alex Tran --- mobile/openapi/doc/AssetApi.md | Bin 57883 -> 57901 bytes mobile/openapi/doc/MetadataSearchDto.md | Bin 2462 -> 2480 bytes mobile/openapi/doc/SmartSearchDto.md | Bin 1904 -> 1922 bytes .../lib/model/metadata_search_dto.dart | Bin 32394 -> 31939 bytes .../openapi/lib/model/smart_search_dto.dart | Bin 24379 -> 23924 bytes .../test/metadata_search_dto_test.dart | Bin 4948 -> 4971 bytes .../openapi/test/smart_search_dto_test.dart | Bin 3742 -> 3765 bytes open-api/immich-openapi-specs.json | 3 ++ server/e2e/api/specs/asset.e2e-spec.ts | 51 +++++++++++++----- server/src/domain/search/dto/search.dto.ts | 1 + server/src/infra/infra.utils.ts | 2 +- server/src/infra/sql/asset.repository.sql | 2 +- server/src/infra/sql/search.repository.sql | 14 +++-- 13 files changed, 55 insertions(+), 18 deletions(-) diff --git a/mobile/openapi/doc/AssetApi.md b/mobile/openapi/doc/AssetApi.md index 5691965bc71c730a54aaa2bbee3afcdc0823d2d2..93b758a5959f2fff4014e76f2091ec58b91ad5e9 100644 GIT binary patch delta 25 hcmbPzgn8`|<_&fmC;wZ=!2-q0|1??3*G<# delta 18 acmZ2`gn9N6<_&fmCl|DEY>wD?)c^ogdkDq= diff --git a/mobile/openapi/doc/MetadataSearchDto.md b/mobile/openapi/doc/MetadataSearchDto.md index bfbf81749e94d0b23eb636ed0d188e8efd4c6217..d1d098fb0ec8242498b09b03ef635ee3e8801320 100644 GIT binary patch delta 31 mcmbOyyg_(_4~I~6N@`kSX-}DU1AVvVNW(u$X delta 16 XcmdlWJWqIo565JA4))Ed9NvroE#m~H diff --git a/mobile/openapi/doc/SmartSearchDto.md b/mobile/openapi/doc/SmartSearchDto.md index fd9bc35490479c0d7f18aeb9e12c0e073b75e081..d4ec1a70f6937efda925be4fbf7d398c786235c7 100644 GIT binary patch delta 30 lcmeys*Tlb}lual)B{eOvG^a$NBwrydF{d~+c5*4(Zvd^n3!wl2 delta 11 ScmZqT|G>AQlx=c5+iw6Hs|3~n diff --git a/mobile/openapi/lib/model/metadata_search_dto.dart b/mobile/openapi/lib/model/metadata_search_dto.dart index 47756cd527849b174fcf594ee137391f95fb48cf..86a2856e667625f47a1e4d0db8db5d1afa3bd2a5 100644 GIT binary patch delta 65 zcmeDB%Xs)F%*YmbWPqnd}%Ky!k+YwB)2L!_8S) Ry1bkU_V!Sf)5|}z0RSDW7rp=h delta 85 zcmX^7ldh>g-n*oWf>}yC88uCg6ksHHZRUl;oW?p^fMa(xzi!P diff --git a/mobile/openapi/lib/model/smart_search_dto.dart b/mobile/openapi/lib/model/smart_search_dto.dart index 269a07102020fff93b3973be0b94ebd19b1f7963..664850db82f8554f10136f780b9e3ddd61596ac3 100644 GIT binary patch delta 72 zcmdnJkMYYc#toO4ITUOa(h_ruQ#W5?zNEpKl%JoYP@Y+mv3V^|0psRs{cN7iOg2x1 YCUg56ZZ7pt;p9}Xw})!D8Ii;W01JZ~3IG5A delta 102 zcmeyei*ffp#toO4H{W8uq_O!PXDs973H)rEH|pHyoqW}lpQ}8xB*U>NIU}=dvVxiD z=0#SQgeDvK@p3@KQd1^7`pHgi^i`Sc=O{5*z>kFus`#U$!sd0p(VUxw!jsqlbSfu8 diff --git a/mobile/openapi/test/metadata_search_dto_test.dart b/mobile/openapi/test/metadata_search_dto_test.dart index f1635de4e09ac18ec6c162ae392ee3b7e834d759..f817b7da74facd56b578049a7ff300e31256e916 100644 GIT binary patch delta 40 vcmcbj_F8R2w4k_xMoMa0Vrfo^LRn%?X{wb%T4GLds^(-tX2s1lf;SieEu#(? delta 20 ccmaE@c13MNwBY2=tcsHjgjqIE7rep<09#H6-T(jq diff --git a/mobile/openapi/test/smart_search_dto_test.dart b/mobile/openapi/test/smart_search_dto_test.dart index 84a85cf208c927facbd9a93b56372961e7b757a9..4db3ac08089df856fe9e0a9a321dde5f89cf226d 100644 GIT binary patch delta 43 zcmbOyyH$2WDX+MKMoMa0Vrfo^LRn%?X{wb%T4GLds^;W}tcsKPST@h%WnlyWO&|_z delta 16 YcmdlgJ5P2)DevS*%!-@W@^UZ&062OD&j0`b diff --git a/open-api/immich-openapi-specs.json b/open-api/immich-openapi-specs.json index 790fe8e8e..d32d5b820 100644 --- a/open-api/immich-openapi-specs.json +++ b/open-api/immich-openapi-specs.json @@ -2463,6 +2463,7 @@ "required": false, "in": "query", "schema": { + "default": false, "type": "boolean" } }, @@ -8429,6 +8430,7 @@ "type": "string" }, "withArchived": { + "default": false, "type": "boolean" }, "withDeleted": { @@ -9500,6 +9502,7 @@ "type": "string" }, "withArchived": { + "default": false, "type": "boolean" }, "withDeleted": { diff --git a/server/e2e/api/specs/asset.e2e-spec.ts b/server/e2e/api/specs/asset.e2e-spec.ts index 5993a7040..778988120 100644 --- a/server/e2e/api/specs/asset.e2e-spec.ts +++ b/server/e2e/api/specs/asset.e2e-spec.ts @@ -50,6 +50,7 @@ describe(`${AssetController.name} (e2e)`, () => { let asset3: AssetResponseDto; let asset4: AssetResponseDto; let asset5: AssetResponseDto; + let asset6: AssetResponseDto; const createAsset = async ( loginResponse: LoginResponseDto, @@ -96,12 +97,11 @@ describe(`${AssetController.name} (e2e)`, () => { beforeEach(async () => { await testApp.reset({ entities: [AssetEntity, AssetStackEntity] }); - [asset1, asset2, asset3, asset4, asset5] = await Promise.all([ + [asset1, asset2, asset3, asset4, asset5, asset6] = await Promise.all([ createAsset(user1, new Date('1970-01-01')), createAsset(user1, new Date('1970-02-10')), createAsset(user1, new Date('1970-02-11'), { isFavorite: true, - isArchived: true, isExternal: true, isReadOnly: true, type: AssetType.VIDEO, @@ -118,6 +118,9 @@ describe(`${AssetController.name} (e2e)`, () => { createAsset(user1, new Date('1970-01-01'), { deletedAt: yesterday.toJSDate(), }), + createAsset(user1, new Date('1970-02-11'), { + isArchived: true, + }), ]); await assetRepository.upsertExif({ @@ -275,14 +278,14 @@ describe(`${AssetController.name} (e2e)`, () => { should: 'should search by isArchived (true)', deferred: () => ({ query: { isArchived: true }, - assets: [asset3], + assets: [asset6], }), }, { should: 'should search by isArchived (false)', deferred: () => ({ query: { isArchived: false }, - assets: [asset2, asset1], + assets: [asset3, asset2, asset1], }), }, { @@ -313,6 +316,20 @@ describe(`${AssetController.name} (e2e)`, () => { assets: [asset3], }), }, + { + should: 'should search by withArchived (true)', + deferred: () => ({ + query: { withArchived: true }, + assets: [asset3, asset6, asset2, asset1], + }), + }, + { + should: 'should search by withArchived (false)', + deferred: () => ({ + query: { withArchived: false }, + assets: [asset3, asset2, asset1], + }), + }, { should: 'should search by createdBefore', deferred: () => ({ @@ -902,7 +919,7 @@ describe(`${AssetController.name} (e2e)`, () => { .get('/asset/statistics') .set('Authorization', `Bearer ${user1.accessToken}`); - expect(body).toEqual({ images: 5, videos: 1, total: 6 }); + expect(body).toEqual({ images: 6, videos: 1, total: 7 }); expect(status).toBe(200); }); @@ -923,7 +940,7 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isArchived: true }); expect(status).toBe(200); - expect(body).toEqual({ images: 2, videos: 1, total: 3 }); + expect(body).toEqual({ images: 3, videos: 0, total: 3 }); }); it('should return stats of all favored and archived assets', async () => { @@ -933,7 +950,7 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isFavorite: true, isArchived: true }); expect(status).toBe(200); - expect(body).toEqual({ images: 1, videos: 1, total: 2 }); + expect(body).toEqual({ images: 1, videos: 0, total: 1 }); }); it('should return stats of all assets neither favored nor archived', async () => { @@ -1041,7 +1058,7 @@ describe(`${AssetController.name} (e2e)`, () => { expect.arrayContaining([ { count: 1, timeBucket: '2023-11-01T00:00:00.000Z' }, { count: 1, timeBucket: '1970-01-01T00:00:00.000Z' }, - { count: 1, timeBucket: '1970-02-01T00:00:00.000Z' }, + { count: 2, timeBucket: '1970-02-01T00:00:00.000Z' }, ]), ); }); @@ -1198,8 +1215,13 @@ describe(`${AssetController.name} (e2e)`, () => { .set('Authorization', `Bearer ${user1.accessToken}`); expect(status).toBe(200); - expect(body).toHaveLength(1); - expect(body).toEqual(expect.arrayContaining([expect.objectContaining({ id: asset2.id })])); + expect(body).toHaveLength(2); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: asset2.id }), + expect.objectContaining({ id: asset3.id }), + ]), + ); }); it('should get all map markers', async () => { @@ -1209,8 +1231,13 @@ describe(`${AssetController.name} (e2e)`, () => { .query({ isArchived: false }); expect(status).toBe(200); - expect(body).toHaveLength(1); - expect(body).toEqual([expect.objectContaining({ id: asset2.id })]); + expect(body).toHaveLength(2); + expect(body).toEqual( + expect.arrayContaining([ + expect.objectContaining({ id: asset2.id }), + expect.objectContaining({ id: asset3.id }), + ]), + ); }); }); diff --git a/server/src/domain/search/dto/search.dto.ts b/server/src/domain/search/dto/search.dto.ts index 519e39fd2..4f2aa1819 100644 --- a/server/src/domain/search/dto/search.dto.ts +++ b/server/src/domain/search/dto/search.dto.ts @@ -23,6 +23,7 @@ class BaseSearchDto { isArchived?: boolean; @QueryBoolean({ optional: true }) + @ApiProperty({ default: false }) withArchived?: boolean; @QueryBoolean({ optional: true }) diff --git a/server/src/infra/infra.utils.ts b/server/src/infra/infra.utils.ts index ab7d74431..1538958e0 100644 --- a/server/src/infra/infra.utils.ts +++ b/server/src/infra/infra.utils.ts @@ -183,7 +183,7 @@ export function searchAssetBuilder( _.omitBy( { ...status, - isArchived: isArchived ?? withArchived, + isArchived: isArchived ?? (withArchived ? undefined : false), encodedVideoPath: isEncoded ? Not(IsNull()) : undefined, livePhotoVideoId: isMotion ? Not(IsNull()) : undefined, }, diff --git a/server/src/infra/sql/asset.repository.sql b/server/src/infra/sql/asset.repository.sql index d971129e7..e5cf6771f 100644 --- a/server/src/infra/sql/asset.repository.sql +++ b/server/src/infra/sql/asset.repository.sql @@ -434,7 +434,7 @@ WHERE AND 1 = 1 AND "asset"."ownerId" IN ($2) AND 1 = 1 - AND 1 = 1 + AND "asset"."isArchived" = $3 ) AND ("asset"."deletedAt" IS NULL) ORDER BY diff --git a/server/src/infra/sql/search.repository.sql b/server/src/infra/sql/search.repository.sql index ebae46f65..a21697c26 100644 --- a/server/src/infra/sql/search.repository.sql +++ b/server/src/infra/sql/search.repository.sql @@ -79,7 +79,10 @@ FROM AND "exifInfo"."lensModel" = $2 AND 1 = 1 AND 1 = 1 - AND "asset"."isFavorite" = $3 + AND ( + "asset"."isFavorite" = $3 + AND "asset"."isArchived" = $4 + ) AND ( "stack"."primaryAssetId" = "asset"."id" OR "asset"."stackId" IS NULL @@ -177,16 +180,19 @@ WHERE AND "exifInfo"."lensModel" = $2 AND 1 = 1 AND 1 = 1 - AND "asset"."isFavorite" = $3 + AND ( + "asset"."isFavorite" = $3 + AND "asset"."isArchived" = $4 + ) AND ( "stack"."primaryAssetId" = "asset"."id" OR "asset"."stackId" IS NULL ) - AND "asset"."ownerId" IN ($4) + AND "asset"."ownerId" IN ($5) ) AND ("asset"."deletedAt" IS NULL) ORDER BY - "search"."embedding" <= > $5 ASC + "search"."embedding" <= > $6 ASC LIMIT 101 COMMIT