mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
fix: unlock properties after successful sidecar write (#25168)
This commit is contained in:
@@ -49,6 +49,23 @@ returning
|
|||||||
"dateTimeOriginal",
|
"dateTimeOriginal",
|
||||||
"timeZone"
|
"timeZone"
|
||||||
|
|
||||||
|
-- AssetRepository.unlockProperties
|
||||||
|
update "asset_exif"
|
||||||
|
set
|
||||||
|
"lockedProperties" = nullif(
|
||||||
|
array(
|
||||||
|
select distinct
|
||||||
|
property
|
||||||
|
from
|
||||||
|
unnest("asset_exif"."lockedProperties") property
|
||||||
|
where
|
||||||
|
not property = any ($1)
|
||||||
|
),
|
||||||
|
'{}'
|
||||||
|
)
|
||||||
|
where
|
||||||
|
"assetId" = $2
|
||||||
|
|
||||||
-- AssetRepository.getMetadata
|
-- AssetRepository.getMetadata
|
||||||
select
|
select
|
||||||
"key",
|
"key",
|
||||||
|
|||||||
@@ -223,6 +223,17 @@ export class AssetRepository {
|
|||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, ['description']] })
|
||||||
|
unlockProperties(assetId: string, properties: LockableProperty[]) {
|
||||||
|
return this.db
|
||||||
|
.updateTable('asset_exif')
|
||||||
|
.where('assetId', '=', assetId)
|
||||||
|
.set((eb) => ({
|
||||||
|
lockedProperties: sql`nullif(array(select distinct property from unnest(${eb.ref('asset_exif.lockedProperties')}) property where not property = any(${properties})), '{}')`,
|
||||||
|
}))
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
async upsertJobStatus(...jobStatus: Insertable<AssetJobStatusTable>[]): Promise<void> {
|
async upsertJobStatus(...jobStatus: Insertable<AssetJobStatusTable>[]): Promise<void> {
|
||||||
if (jobStatus.length === 0) {
|
if (jobStatus.length === 0) {
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1758,6 +1758,12 @@ describe(MetadataService.name, () => {
|
|||||||
GPSLatitude: gps,
|
GPSLatitude: gps,
|
||||||
GPSLongitude: gps,
|
GPSLongitude: gps,
|
||||||
});
|
});
|
||||||
|
expect(mocks.asset.unlockProperties).toHaveBeenCalledWith(asset.id, [
|
||||||
|
'description',
|
||||||
|
'latitude',
|
||||||
|
'longitude',
|
||||||
|
'dateTimeOriginal',
|
||||||
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -461,6 +461,8 @@ export class MetadataService extends BaseService {
|
|||||||
await this.assetRepository.upsertFile({ assetId: id, type: AssetFileType.Sidecar, path: sidecarPath });
|
await this.assetRepository.upsertFile({ assetId: id, type: AssetFileType.Sidecar, path: sidecarPath });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.assetRepository.unlockProperties(asset.id, lockedProperties);
|
||||||
|
|
||||||
return JobStatus.Success;
|
return JobStatus.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -87,4 +87,64 @@ describe(AssetRepository.name, () => {
|
|||||||
).resolves.toEqual({ lockedProperties: ['description', 'dateTimeOriginal'] });
|
).resolves.toEqual({ lockedProperties: ['description', 'dateTimeOriginal'] });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('unlockProperties', () => {
|
||||||
|
it('should unlock one property', async () => {
|
||||||
|
const { ctx, sut } = setup();
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
await ctx.newExif({
|
||||||
|
assetId: asset.id,
|
||||||
|
dateTimeOriginal: '2023-11-19T18:11:00',
|
||||||
|
lockedProperties: ['dateTimeOriginal', 'description'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: ['dateTimeOriginal', 'description'] });
|
||||||
|
|
||||||
|
await sut.unlockProperties(asset.id, ['dateTimeOriginal']);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: ['description'] });
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should unlock all properties', async () => {
|
||||||
|
const { ctx, sut } = setup();
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
await ctx.newExif({
|
||||||
|
assetId: asset.id,
|
||||||
|
dateTimeOriginal: '2023-11-19T18:11:00',
|
||||||
|
lockedProperties: ['dateTimeOriginal', 'description'],
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: ['dateTimeOriginal', 'description'] });
|
||||||
|
|
||||||
|
await sut.unlockProperties(asset.id, ['description', 'dateTimeOriginal']);
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: null });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const newAssetRepositoryMock = (): Mocked<RepositoryInterface<AssetReposi
|
|||||||
upsertExif: vitest.fn(),
|
upsertExif: vitest.fn(),
|
||||||
updateAllExif: vitest.fn(),
|
updateAllExif: vitest.fn(),
|
||||||
updateDateTimeOriginal: vitest.fn().mockResolvedValue([]),
|
updateDateTimeOriginal: vitest.fn().mockResolvedValue([]),
|
||||||
|
unlockProperties: vitest.fn().mockResolvedValue([]),
|
||||||
upsertJobStatus: vitest.fn(),
|
upsertJobStatus: vitest.fn(),
|
||||||
getForCopy: vitest.fn(),
|
getForCopy: vitest.fn(),
|
||||||
getByDayOfYear: vitest.fn(),
|
getByDayOfYear: vitest.fn(),
|
||||||
|
|||||||
Reference in New Issue
Block a user