mirror of
https://github.com/immich-app/immich.git
synced 2025-12-14 22:19:18 +03:00
fix: updating lockable properties
This commit is contained in:
@@ -1,5 +1,14 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
|
-- AssetRepository.upsertExif
|
||||||
|
insert into
|
||||||
|
"asset_exif" ("dateTimeOriginal", "lockedProperties")
|
||||||
|
values
|
||||||
|
($1, $2)
|
||||||
|
on conflict ("assetId") do update
|
||||||
|
set
|
||||||
|
"dateTimeOriginal" = "excluded"."dateTimeOriginal"
|
||||||
|
|
||||||
-- AssetRepository.updateAllExif
|
-- AssetRepository.updateAllExif
|
||||||
update "asset_exif"
|
update "asset_exif"
|
||||||
set
|
set
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import { Chunked, ChunkedArray, DummyValue, GenerateSql } from 'src/decorators';
|
|||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { AssetFileType, AssetMetadataKey, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
import { AssetFileType, AssetMetadataKey, AssetOrder, AssetStatus, AssetType, AssetVisibility } from 'src/enum';
|
||||||
import { DB } from 'src/schema';
|
import { DB } from 'src/schema';
|
||||||
import { AssetExifTable, LockableProperty } from 'src/schema/tables/asset-exif.table';
|
import { AssetExifTable, lockableProperties, LockableProperty } from 'src/schema/tables/asset-exif.table';
|
||||||
import { AssetFileTable } from 'src/schema/tables/asset-file.table';
|
import { AssetFileTable } from 'src/schema/tables/asset-file.table';
|
||||||
import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table';
|
import { AssetJobStatusTable } from 'src/schema/tables/asset-job-status.table';
|
||||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||||
@@ -120,13 +120,26 @@ const distinctLocked = <T extends LockableProperty[] | null>(eb: ExpressionBuild
|
|||||||
export class AssetRepository {
|
export class AssetRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [{ dateTimeOriginal: DummyValue.DATE }, { lockedPropertiesBehavior: 'update' }] })
|
||||||
async upsertExif(
|
async upsertExif(
|
||||||
exif: Insertable<AssetExifTable>,
|
exif: Insertable<AssetExifTable>,
|
||||||
{ lockedPropertiesBehavior }: { lockedPropertiesBehavior: 'none' | 'update' | 'skip' },
|
{ lockedPropertiesBehavior }: { lockedPropertiesBehavior: 'none' | 'update' | 'skip' },
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
|
const values = {
|
||||||
|
...exif,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (lockedPropertiesBehavior !== 'none') {
|
||||||
|
delete values.lockedProperties;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lockedPropertiesBehavior === 'update') {
|
||||||
|
values.lockedProperties = lockableProperties.filter((property) => Object.keys(exif).includes(property));
|
||||||
|
}
|
||||||
|
|
||||||
await this.db
|
await this.db
|
||||||
.insertInto('asset_exif')
|
.insertInto('asset_exif')
|
||||||
.values(exif)
|
.values(values)
|
||||||
.onConflict((oc) =>
|
.onConflict((oc) =>
|
||||||
oc.column('assetId').doUpdateSet((eb) => {
|
oc.column('assetId').doUpdateSet((eb) => {
|
||||||
const updateLocked = <T extends keyof AssetExifTable>(col: T) => eb.ref(`excluded.${col}`);
|
const updateLocked = <T extends keyof AssetExifTable>(col: T) => eb.ref(`excluded.${col}`);
|
||||||
@@ -138,43 +151,45 @@ export class AssetRepository {
|
|||||||
.else(eb.ref(`excluded.${col}`))
|
.else(eb.ref(`excluded.${col}`))
|
||||||
.end();
|
.end();
|
||||||
const ref = lockedPropertiesBehavior === 'update' ? updateLocked : skipLocked;
|
const ref = lockedPropertiesBehavior === 'update' ? updateLocked : skipLocked;
|
||||||
return removeUndefinedKeys(
|
return {
|
||||||
{
|
...removeUndefinedKeys(
|
||||||
description: ref('description'),
|
{
|
||||||
exifImageWidth: ref('exifImageWidth'),
|
description: ref('description'),
|
||||||
exifImageHeight: ref('exifImageHeight'),
|
exifImageWidth: ref('exifImageWidth'),
|
||||||
fileSizeInByte: ref('fileSizeInByte'),
|
exifImageHeight: ref('exifImageHeight'),
|
||||||
orientation: ref('orientation'),
|
fileSizeInByte: ref('fileSizeInByte'),
|
||||||
dateTimeOriginal: ref('dateTimeOriginal'),
|
orientation: ref('orientation'),
|
||||||
modifyDate: ref('modifyDate'),
|
dateTimeOriginal: ref('dateTimeOriginal'),
|
||||||
timeZone: ref('timeZone'),
|
modifyDate: ref('modifyDate'),
|
||||||
latitude: ref('latitude'),
|
timeZone: ref('timeZone'),
|
||||||
longitude: ref('longitude'),
|
latitude: ref('latitude'),
|
||||||
projectionType: ref('projectionType'),
|
longitude: ref('longitude'),
|
||||||
city: ref('city'),
|
projectionType: ref('projectionType'),
|
||||||
livePhotoCID: ref('livePhotoCID'),
|
city: ref('city'),
|
||||||
autoStackId: ref('autoStackId'),
|
livePhotoCID: ref('livePhotoCID'),
|
||||||
state: ref('state'),
|
autoStackId: ref('autoStackId'),
|
||||||
country: ref('country'),
|
state: ref('state'),
|
||||||
make: ref('make'),
|
country: ref('country'),
|
||||||
model: ref('model'),
|
make: ref('make'),
|
||||||
lensModel: ref('lensModel'),
|
model: ref('model'),
|
||||||
fNumber: ref('fNumber'),
|
lensModel: ref('lensModel'),
|
||||||
focalLength: ref('focalLength'),
|
fNumber: ref('fNumber'),
|
||||||
iso: ref('iso'),
|
focalLength: ref('focalLength'),
|
||||||
exposureTime: ref('exposureTime'),
|
iso: ref('iso'),
|
||||||
profileDescription: ref('profileDescription'),
|
exposureTime: ref('exposureTime'),
|
||||||
colorspace: ref('colorspace'),
|
profileDescription: ref('profileDescription'),
|
||||||
bitsPerSample: ref('bitsPerSample'),
|
colorspace: ref('colorspace'),
|
||||||
rating: ref('rating'),
|
bitsPerSample: ref('bitsPerSample'),
|
||||||
fps: ref('fps'),
|
rating: ref('rating'),
|
||||||
lockedProperties:
|
fps: ref('fps'),
|
||||||
exif.lockedProperties !== undefined && lockedPropertiesBehavior !== 'none'
|
lockedProperties:
|
||||||
? distinctLocked(eb, exif.lockedProperties)
|
lockedPropertiesBehavior === 'update'
|
||||||
: exif.lockedProperties,
|
? distinctLocked(eb, values.lockedProperties ?? [])
|
||||||
},
|
: ref('lockedProperties'),
|
||||||
exif,
|
},
|
||||||
);
|
values,
|
||||||
|
),
|
||||||
|
};
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.execute();
|
.execute();
|
||||||
|
|||||||
@@ -268,4 +268,65 @@ describe(AssetService.name, () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('update', () => {
|
||||||
|
it('should update dateTimeOriginal', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
ctx.getMock(JobRepository).queue.mockResolvedValue();
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const auth = factory.auth({ user });
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
await ctx.newExif({ assetId: asset.id, description: 'test' });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: null });
|
||||||
|
await sut.update(auth, asset.id, { dateTimeOriginal: '2023-11-19T18:11:00.000-07:00' });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: ['dateTimeOriginal'] });
|
||||||
|
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
exifInfo: expect.objectContaining({ dateTimeOriginal: '2023-11-20T01:11:00+00:00' }),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('updateAll', () => {
|
||||||
|
it('should relatively update assets', async () => {
|
||||||
|
const { sut, ctx } = setup();
|
||||||
|
ctx.getMock(JobRepository).queueAll.mockResolvedValue();
|
||||||
|
const { user } = await ctx.newUser();
|
||||||
|
const auth = factory.auth({ user });
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
await ctx.newExif({ assetId: asset.id, dateTimeOriginal: '2023-11-19T18:11:00' });
|
||||||
|
|
||||||
|
await sut.updateAll(auth, { ids: [asset.id], dateTimeRelative: -11 });
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
ctx.database
|
||||||
|
.selectFrom('asset_exif')
|
||||||
|
.select('lockedProperties')
|
||||||
|
.where('assetId', '=', asset.id)
|
||||||
|
.executeTakeFirstOrThrow(),
|
||||||
|
).resolves.toEqual({ lockedProperties: ['timeZone', 'dateTimeOriginal'] });
|
||||||
|
await expect(ctx.get(AssetRepository).getById(asset.id, { exifInfo: true })).resolves.toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
exifInfo: expect.objectContaining({
|
||||||
|
dateTimeOriginal: '2023-11-19T18:00:00+00:00',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user