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 <alex.tran1502@gmail.com>
This commit is contained in:
Mert 2024-02-20 22:59:26 -05:00 committed by GitHub
parent bb5236ae65
commit eb73f6605b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 68 additions and 51 deletions

View File

@ -1153,7 +1153,7 @@ Name | Type | Description | Notes
**updatedAfter** | **DateTime**| | [optional] **updatedAfter** | **DateTime**| | [optional]
**updatedBefore** | **DateTime**| | [optional] **updatedBefore** | **DateTime**| | [optional]
**webpPath** | **String**| | [optional] **webpPath** | **String**| | [optional]
**withArchived** | **bool**| | [optional] **withArchived** | **bool**| | [optional] [default to false]
**withDeleted** | **bool**| | [optional] **withDeleted** | **bool**| | [optional]
**withExif** | **bool**| | [optional] **withExif** | **bool**| | [optional]
**withPeople** | **bool**| | [optional] **withPeople** | **bool**| | [optional]

View File

@ -46,7 +46,7 @@ Name | Type | Description | Notes
**updatedAfter** | [**DateTime**](DateTime.md) | | [optional] **updatedAfter** | [**DateTime**](DateTime.md) | | [optional]
**updatedBefore** | [**DateTime**](DateTime.md) | | [optional] **updatedBefore** | [**DateTime**](DateTime.md) | | [optional]
**webpPath** | **String** | | [optional] **webpPath** | **String** | | [optional]
**withArchived** | **bool** | | [optional] **withArchived** | **bool** | | [optional] [default to false]
**withDeleted** | **bool** | | [optional] **withDeleted** | **bool** | | [optional]
**withExif** | **bool** | | [optional] **withExif** | **bool** | | [optional]
**withPeople** | **bool** | | [optional] **withPeople** | **bool** | | [optional]

View File

@ -37,7 +37,7 @@ Name | Type | Description | Notes
**type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional] **type** | [**AssetTypeEnum**](AssetTypeEnum.md) | | [optional]
**updatedAfter** | [**DateTime**](DateTime.md) | | [optional] **updatedAfter** | [**DateTime**](DateTime.md) | | [optional]
**updatedBefore** | [**DateTime**](DateTime.md) | | [optional] **updatedBefore** | [**DateTime**](DateTime.md) | | [optional]
**withArchived** | **bool** | | [optional] **withArchived** | **bool** | | [optional] [default to false]
**withDeleted** | **bool** | | [optional] **withDeleted** | **bool** | | [optional]
**withExif** | **bool** | | [optional] **withExif** | **bool** | | [optional]

View File

@ -51,7 +51,7 @@ class MetadataSearchDto {
this.updatedAfter, this.updatedAfter,
this.updatedBefore, this.updatedBefore,
this.webpPath, this.webpPath,
this.withArchived, this.withArchived = false,
this.withDeleted, this.withDeleted,
this.withExif, this.withExif,
this.withPeople, this.withPeople,
@ -356,13 +356,7 @@ class MetadataSearchDto {
/// ///
String? webpPath; String? webpPath;
/// bool withArchived;
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? withArchived;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
@ -483,7 +477,7 @@ class MetadataSearchDto {
(updatedAfter == null ? 0 : updatedAfter!.hashCode) + (updatedAfter == null ? 0 : updatedAfter!.hashCode) +
(updatedBefore == null ? 0 : updatedBefore!.hashCode) + (updatedBefore == null ? 0 : updatedBefore!.hashCode) +
(webpPath == null ? 0 : webpPath!.hashCode) + (webpPath == null ? 0 : webpPath!.hashCode) +
(withArchived == null ? 0 : withArchived!.hashCode) + (withArchived.hashCode) +
(withDeleted == null ? 0 : withDeleted!.hashCode) + (withDeleted == null ? 0 : withDeleted!.hashCode) +
(withExif == null ? 0 : withExif!.hashCode) + (withExif == null ? 0 : withExif!.hashCode) +
(withPeople == null ? 0 : withPeople!.hashCode) + (withPeople == null ? 0 : withPeople!.hashCode) +
@ -680,11 +674,7 @@ class MetadataSearchDto {
} else { } else {
// json[r'webpPath'] = null; // json[r'webpPath'] = null;
} }
if (this.withArchived != null) {
json[r'withArchived'] = this.withArchived; json[r'withArchived'] = this.withArchived;
} else {
// json[r'withArchived'] = null;
}
if (this.withDeleted != null) { if (this.withDeleted != null) {
json[r'withDeleted'] = this.withDeleted; json[r'withDeleted'] = this.withDeleted;
} else { } else {
@ -756,7 +746,7 @@ class MetadataSearchDto {
updatedAfter: mapDateTime(json, r'updatedAfter', r''), updatedAfter: mapDateTime(json, r'updatedAfter', r''),
updatedBefore: mapDateTime(json, r'updatedBefore', r''), updatedBefore: mapDateTime(json, r'updatedBefore', r''),
webpPath: mapValueOfType<String>(json, r'webpPath'), webpPath: mapValueOfType<String>(json, r'webpPath'),
withArchived: mapValueOfType<bool>(json, r'withArchived'), withArchived: mapValueOfType<bool>(json, r'withArchived') ?? false,
withDeleted: mapValueOfType<bool>(json, r'withDeleted'), withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
withExif: mapValueOfType<bool>(json, r'withExif'), withExif: mapValueOfType<bool>(json, r'withExif'),
withPeople: mapValueOfType<bool>(json, r'withPeople'), withPeople: mapValueOfType<bool>(json, r'withPeople'),

View File

@ -42,7 +42,7 @@ class SmartSearchDto {
this.type, this.type,
this.updatedAfter, this.updatedAfter,
this.updatedBefore, this.updatedBefore,
this.withArchived, this.withArchived = false,
this.withDeleted, this.withDeleted,
this.withExif, this.withExif,
}); });
@ -273,13 +273,7 @@ class SmartSearchDto {
/// ///
DateTime? updatedBefore; DateTime? updatedBefore;
/// bool withArchived;
/// Please note: This property should have been non-nullable! Since the specification file
/// does not include a default value (using the "default:" property), however, the generated
/// source code must fall back to having a nullable type.
/// Consider adding a "default:" property in the specification file to hide this note.
///
bool? withArchived;
/// ///
/// Please note: This property should have been non-nullable! Since the specification file /// Please note: This property should have been non-nullable! Since the specification file
@ -364,7 +358,7 @@ class SmartSearchDto {
(type == null ? 0 : type!.hashCode) + (type == null ? 0 : type!.hashCode) +
(updatedAfter == null ? 0 : updatedAfter!.hashCode) + (updatedAfter == null ? 0 : updatedAfter!.hashCode) +
(updatedBefore == null ? 0 : updatedBefore!.hashCode) + (updatedBefore == null ? 0 : updatedBefore!.hashCode) +
(withArchived == null ? 0 : withArchived!.hashCode) + (withArchived.hashCode) +
(withDeleted == null ? 0 : withDeleted!.hashCode) + (withDeleted == null ? 0 : withDeleted!.hashCode) +
(withExif == null ? 0 : withExif!.hashCode); (withExif == null ? 0 : withExif!.hashCode);
@ -514,11 +508,7 @@ class SmartSearchDto {
} else { } else {
// json[r'updatedBefore'] = null; // json[r'updatedBefore'] = null;
} }
if (this.withArchived != null) {
json[r'withArchived'] = this.withArchived; json[r'withArchived'] = this.withArchived;
} else {
// json[r'withArchived'] = null;
}
if (this.withDeleted != null) { if (this.withDeleted != null) {
json[r'withDeleted'] = this.withDeleted; json[r'withDeleted'] = this.withDeleted;
} else { } else {
@ -569,7 +559,7 @@ class SmartSearchDto {
type: AssetTypeEnum.fromJson(json[r'type']), type: AssetTypeEnum.fromJson(json[r'type']),
updatedAfter: mapDateTime(json, r'updatedAfter', r''), updatedAfter: mapDateTime(json, r'updatedAfter', r''),
updatedBefore: mapDateTime(json, r'updatedBefore', r''), updatedBefore: mapDateTime(json, r'updatedBefore', r''),
withArchived: mapValueOfType<bool>(json, r'withArchived'), withArchived: mapValueOfType<bool>(json, r'withArchived') ?? false,
withDeleted: mapValueOfType<bool>(json, r'withDeleted'), withDeleted: mapValueOfType<bool>(json, r'withDeleted'),
withExif: mapValueOfType<bool>(json, r'withExif'), withExif: mapValueOfType<bool>(json, r'withExif'),
); );

View File

@ -206,7 +206,7 @@ void main() {
// TODO // TODO
}); });
// bool withArchived // bool withArchived (default value: false)
test('to test the property `withArchived`', () async { test('to test the property `withArchived`', () async {
// TODO // TODO
}); });

View File

@ -161,7 +161,7 @@ void main() {
// TODO // TODO
}); });
// bool withArchived // bool withArchived (default value: false)
test('to test the property `withArchived`', () async { test('to test the property `withArchived`', () async {
// TODO // TODO
}); });

View File

@ -2463,6 +2463,7 @@
"required": false, "required": false,
"in": "query", "in": "query",
"schema": { "schema": {
"default": false,
"type": "boolean" "type": "boolean"
} }
}, },
@ -8429,6 +8430,7 @@
"type": "string" "type": "string"
}, },
"withArchived": { "withArchived": {
"default": false,
"type": "boolean" "type": "boolean"
}, },
"withDeleted": { "withDeleted": {
@ -9500,6 +9502,7 @@
"type": "string" "type": "string"
}, },
"withArchived": { "withArchived": {
"default": false,
"type": "boolean" "type": "boolean"
}, },
"withDeleted": { "withDeleted": {

View File

@ -50,6 +50,7 @@ describe(`${AssetController.name} (e2e)`, () => {
let asset3: AssetResponseDto; let asset3: AssetResponseDto;
let asset4: AssetResponseDto; let asset4: AssetResponseDto;
let asset5: AssetResponseDto; let asset5: AssetResponseDto;
let asset6: AssetResponseDto;
const createAsset = async ( const createAsset = async (
loginResponse: LoginResponseDto, loginResponse: LoginResponseDto,
@ -96,12 +97,11 @@ describe(`${AssetController.name} (e2e)`, () => {
beforeEach(async () => { beforeEach(async () => {
await testApp.reset({ entities: [AssetEntity, AssetStackEntity] }); 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-01-01')),
createAsset(user1, new Date('1970-02-10')), createAsset(user1, new Date('1970-02-10')),
createAsset(user1, new Date('1970-02-11'), { createAsset(user1, new Date('1970-02-11'), {
isFavorite: true, isFavorite: true,
isArchived: true,
isExternal: true, isExternal: true,
isReadOnly: true, isReadOnly: true,
type: AssetType.VIDEO, type: AssetType.VIDEO,
@ -118,6 +118,9 @@ describe(`${AssetController.name} (e2e)`, () => {
createAsset(user1, new Date('1970-01-01'), { createAsset(user1, new Date('1970-01-01'), {
deletedAt: yesterday.toJSDate(), deletedAt: yesterday.toJSDate(),
}), }),
createAsset(user1, new Date('1970-02-11'), {
isArchived: true,
}),
]); ]);
await assetRepository.upsertExif({ await assetRepository.upsertExif({
@ -275,14 +278,14 @@ describe(`${AssetController.name} (e2e)`, () => {
should: 'should search by isArchived (true)', should: 'should search by isArchived (true)',
deferred: () => ({ deferred: () => ({
query: { isArchived: true }, query: { isArchived: true },
assets: [asset3], assets: [asset6],
}), }),
}, },
{ {
should: 'should search by isArchived (false)', should: 'should search by isArchived (false)',
deferred: () => ({ deferred: () => ({
query: { isArchived: false }, query: { isArchived: false },
assets: [asset2, asset1], assets: [asset3, asset2, asset1],
}), }),
}, },
{ {
@ -313,6 +316,20 @@ describe(`${AssetController.name} (e2e)`, () => {
assets: [asset3], 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', should: 'should search by createdBefore',
deferred: () => ({ deferred: () => ({
@ -902,7 +919,7 @@ describe(`${AssetController.name} (e2e)`, () => {
.get('/asset/statistics') .get('/asset/statistics')
.set('Authorization', `Bearer ${user1.accessToken}`); .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); expect(status).toBe(200);
}); });
@ -923,7 +940,7 @@ describe(`${AssetController.name} (e2e)`, () => {
.query({ isArchived: true }); .query({ isArchived: true });
expect(status).toBe(200); 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 () => { it('should return stats of all favored and archived assets', async () => {
@ -933,7 +950,7 @@ describe(`${AssetController.name} (e2e)`, () => {
.query({ isFavorite: true, isArchived: true }); .query({ isFavorite: true, isArchived: true });
expect(status).toBe(200); 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 () => { it('should return stats of all assets neither favored nor archived', async () => {
@ -1041,7 +1058,7 @@ describe(`${AssetController.name} (e2e)`, () => {
expect.arrayContaining([ expect.arrayContaining([
{ count: 1, timeBucket: '2023-11-01T00:00:00.000Z' }, { count: 1, timeBucket: '2023-11-01T00:00:00.000Z' },
{ count: 1, timeBucket: '1970-01-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}`); .set('Authorization', `Bearer ${user1.accessToken}`);
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(1); expect(body).toHaveLength(2);
expect(body).toEqual(expect.arrayContaining([expect.objectContaining({ id: asset2.id })])); expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: asset2.id }),
expect.objectContaining({ id: asset3.id }),
]),
);
}); });
it('should get all map markers', async () => { it('should get all map markers', async () => {
@ -1209,8 +1231,13 @@ describe(`${AssetController.name} (e2e)`, () => {
.query({ isArchived: false }); .query({ isArchived: false });
expect(status).toBe(200); expect(status).toBe(200);
expect(body).toHaveLength(1); expect(body).toHaveLength(2);
expect(body).toEqual([expect.objectContaining({ id: asset2.id })]); expect(body).toEqual(
expect.arrayContaining([
expect.objectContaining({ id: asset2.id }),
expect.objectContaining({ id: asset3.id }),
]),
);
}); });
}); });

View File

@ -23,6 +23,7 @@ class BaseSearchDto {
isArchived?: boolean; isArchived?: boolean;
@QueryBoolean({ optional: true }) @QueryBoolean({ optional: true })
@ApiProperty({ default: false })
withArchived?: boolean; withArchived?: boolean;
@QueryBoolean({ optional: true }) @QueryBoolean({ optional: true })

View File

@ -183,7 +183,7 @@ export function searchAssetBuilder(
_.omitBy( _.omitBy(
{ {
...status, ...status,
isArchived: isArchived ?? withArchived, isArchived: isArchived ?? (withArchived ? undefined : false),
encodedVideoPath: isEncoded ? Not(IsNull()) : undefined, encodedVideoPath: isEncoded ? Not(IsNull()) : undefined,
livePhotoVideoId: isMotion ? Not(IsNull()) : undefined, livePhotoVideoId: isMotion ? Not(IsNull()) : undefined,
}, },

View File

@ -434,7 +434,7 @@ WHERE
AND 1 = 1 AND 1 = 1
AND "asset"."ownerId" IN ($2) AND "asset"."ownerId" IN ($2)
AND 1 = 1 AND 1 = 1
AND 1 = 1 AND "asset"."isArchived" = $3
) )
AND ("asset"."deletedAt" IS NULL) AND ("asset"."deletedAt" IS NULL)
ORDER BY ORDER BY

View File

@ -79,7 +79,10 @@ FROM
AND "exifInfo"."lensModel" = $2 AND "exifInfo"."lensModel" = $2
AND 1 = 1 AND 1 = 1
AND 1 = 1 AND 1 = 1
AND "asset"."isFavorite" = $3 AND (
"asset"."isFavorite" = $3
AND "asset"."isArchived" = $4
)
AND ( AND (
"stack"."primaryAssetId" = "asset"."id" "stack"."primaryAssetId" = "asset"."id"
OR "asset"."stackId" IS NULL OR "asset"."stackId" IS NULL
@ -177,16 +180,19 @@ WHERE
AND "exifInfo"."lensModel" = $2 AND "exifInfo"."lensModel" = $2
AND 1 = 1 AND 1 = 1
AND 1 = 1 AND 1 = 1
AND "asset"."isFavorite" = $3 AND (
"asset"."isFavorite" = $3
AND "asset"."isArchived" = $4
)
AND ( AND (
"stack"."primaryAssetId" = "asset"."id" "stack"."primaryAssetId" = "asset"."id"
OR "asset"."stackId" IS NULL OR "asset"."stackId" IS NULL
) )
AND "asset"."ownerId" IN ($4) AND "asset"."ownerId" IN ($5)
) )
AND ("asset"."deletedAt" IS NULL) AND ("asset"."deletedAt" IS NULL)
ORDER BY ORDER BY
"search"."embedding" <= > $5 ASC "search"."embedding" <= > $6 ASC
LIMIT LIMIT
101 101
COMMIT COMMIT