mirror of
https://github.com/immich-app/immich.git
synced 2026-06-05 13:45:20 -04:00
refactor(workflow): map AlbumAssetAdded trigger to AssetV1
Drop the AssetAlbumV1 workflow type and have the AlbumAssetAdded trigger reuse the existing asset-aware AssetV1 type, so all current AssetV1 plugin methods work with it out of the box.
This commit is contained in:
@@ -1172,7 +1172,6 @@ export const WorkflowTriggerSchema = z
|
||||
export enum WorkflowType {
|
||||
AssetV1 = 'AssetV1',
|
||||
AssetPersonV1 = 'AssetPersonV1',
|
||||
AssetAlbumV1 = 'AssetAlbumV1',
|
||||
}
|
||||
|
||||
export const WorkflowTypeSchema = z.enum(WorkflowType).describe('Workflow type').meta({ id: 'WorkflowType' });
|
||||
|
||||
@@ -5,7 +5,6 @@ import { InjectKysely } from 'nestjs-kysely';
|
||||
import { columns } from 'src/database';
|
||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||
import { WorkflowSearchDto } from 'src/dtos/workflow.dto';
|
||||
import { AlbumUserRole } from 'src/enum';
|
||||
import { DB } from 'src/schema';
|
||||
import { WorkflowStepTable } from 'src/schema/tables/workflow-step.table';
|
||||
import { WorkflowTable } from 'src/schema/tables/workflow.table';
|
||||
@@ -183,15 +182,4 @@ export class WorkflowRepository {
|
||||
.where('id', '=', assetId)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
|
||||
getAlbumForWorkflow(albumId: string) {
|
||||
return this.db
|
||||
.selectFrom('album')
|
||||
.innerJoin('album_user', 'album_user.albumId', 'album.id')
|
||||
.where('album_user.role', '=', AlbumUserRole.Owner)
|
||||
.select(['album.id', 'album_user.userId as ownerId', 'album.albumName', 'album.description'])
|
||||
.where('album.id', '=', albumId)
|
||||
.where('album.deletedAt', 'is', null)
|
||||
.executeTakeFirstOrThrow();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ type ExecuteOptions<T extends WorkflowType> = {
|
||||
write: (auth: AuthDto, changes: WorkflowChanges<T>) => Promise<void>;
|
||||
};
|
||||
|
||||
type AssetTrigger = { userId: string; assetId: string; albumId?: string; trigger: WorkflowTrigger };
|
||||
type AssetTrigger = { userId: string; assetId: string; trigger: WorkflowTrigger };
|
||||
|
||||
export class WorkflowExecutionService extends BaseService {
|
||||
private jwtSecret!: string;
|
||||
@@ -275,56 +275,25 @@ export class WorkflowExecutionService extends BaseService {
|
||||
}
|
||||
|
||||
@OnEvent({ name: 'AlbumAssetAdd' })
|
||||
onAlbumAssetAdd({ userId, assetId, albumId }: ArgOf<'AlbumAssetAdd'>) {
|
||||
return this.onAssetTrigger({ userId, assetId, albumId, trigger: WorkflowTrigger.AlbumAssetAdded });
|
||||
onAlbumAssetAdd({ userId, assetId }: ArgOf<'AlbumAssetAdd'>) {
|
||||
return this.onAssetTrigger({ userId, assetId, trigger: WorkflowTrigger.AlbumAssetAdded });
|
||||
}
|
||||
|
||||
private async onAssetTrigger({ userId, assetId, albumId, trigger }: AssetTrigger) {
|
||||
private async onAssetTrigger({ userId, assetId, trigger }: AssetTrigger) {
|
||||
const items = await this.workflowRepository.search({ userId, trigger });
|
||||
await this.jobRepository.queueAll(
|
||||
items.map((workflow) => ({
|
||||
name: JobName.WorkflowAssetTrigger,
|
||||
data: { workflowId: workflow.id, assetId, albumId, trigger },
|
||||
data: { workflowId: workflow.id, assetId, trigger },
|
||||
})),
|
||||
);
|
||||
}
|
||||
|
||||
@OnJob({ name: JobName.WorkflowAssetTrigger, queue: QueueName.Workflow })
|
||||
handleAssetTrigger({ workflowId, assetId, albumId }: JobOf<JobName.WorkflowAssetTrigger>) {
|
||||
handleAssetTrigger({ workflowId, assetId }: JobOf<JobName.WorkflowAssetTrigger>) {
|
||||
return this.execute(workflowId, (type) => {
|
||||
const assetService = BaseService.create(AssetService, this);
|
||||
|
||||
const writeAsset: ExecuteOptions<WorkflowType.AssetV1>['write'] = async (auth, changes) => {
|
||||
const asset = changes.asset;
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
|
||||
await assetService.update(auth, assetId, {
|
||||
isFavorite: asset.isFavorite,
|
||||
visibility: asset.visibility,
|
||||
dateTimeOriginal: asset.exifInfo?.dateTimeOriginal ?? undefined,
|
||||
// TODO allow setting to null
|
||||
longitude: asset.exifInfo?.longitude ?? undefined,
|
||||
// TODO allow setting to null
|
||||
latitude: asset.exifInfo?.latitude ?? undefined,
|
||||
// TODO allow setting to null
|
||||
description: asset.exifInfo?.description ?? undefined,
|
||||
rating: asset.exifInfo?.rating,
|
||||
|
||||
// TODO add to update dto
|
||||
// make: asset.exifInfo?.make,
|
||||
// model: asset.exifInfo?.model,
|
||||
// city: asset.exifInfo?.city,
|
||||
// state: asset.exifInfo?.state,
|
||||
// country: asset.exifInfo?.country,
|
||||
// lensModel: asset.exifInfo?.lensModel,
|
||||
// fNumber: asset.exifInfo?.fNumber,
|
||||
// fps: asset.exifInfo?.fps,
|
||||
// iso: asset.exifInfo?.iso,
|
||||
});
|
||||
};
|
||||
|
||||
switch (type) {
|
||||
case WorkflowType.AssetV1: {
|
||||
return {
|
||||
@@ -335,28 +304,36 @@ export class WorkflowExecutionService extends BaseService {
|
||||
authUserId: asset.ownerId,
|
||||
};
|
||||
},
|
||||
write: writeAsset,
|
||||
} satisfies ExecuteOptions<typeof type>;
|
||||
}
|
||||
write: async (auth, changes) => {
|
||||
const asset = changes.asset;
|
||||
if (!asset) {
|
||||
return;
|
||||
}
|
||||
|
||||
case WorkflowType.AssetAlbumV1: {
|
||||
if (!albumId) {
|
||||
this.logger.error(`Misconfigured workflow ${workflowId}: missing albumId for type ${type}`);
|
||||
return;
|
||||
}
|
||||
await assetService.update(auth, assetId, {
|
||||
isFavorite: asset.isFavorite,
|
||||
visibility: asset.visibility,
|
||||
dateTimeOriginal: asset.exifInfo?.dateTimeOriginal ?? undefined,
|
||||
// TODO allow setting to null
|
||||
longitude: asset.exifInfo?.longitude ?? undefined,
|
||||
// TODO allow setting to null
|
||||
latitude: asset.exifInfo?.latitude ?? undefined,
|
||||
// TODO allow setting to null
|
||||
description: asset.exifInfo?.description ?? undefined,
|
||||
rating: asset.exifInfo?.rating,
|
||||
|
||||
return {
|
||||
read: async () => {
|
||||
const [asset, album] = await Promise.all([
|
||||
this.workflowRepository.getForAssetV1(assetId),
|
||||
this.workflowRepository.getAlbumForWorkflow(albumId),
|
||||
]);
|
||||
return {
|
||||
data: { asset, album } as any,
|
||||
authUserId: asset.ownerId,
|
||||
};
|
||||
// TODO add to update dto
|
||||
// make: asset.exifInfo?.make,
|
||||
// model: asset.exifInfo?.model,
|
||||
// city: asset.exifInfo?.city,
|
||||
// state: asset.exifInfo?.state,
|
||||
// country: asset.exifInfo?.country,
|
||||
// lensModel: asset.exifInfo?.lensModel,
|
||||
// fNumber: asset.exifInfo?.fNumber,
|
||||
// fps: asset.exifInfo?.fps,
|
||||
// iso: asset.exifInfo?.iso,
|
||||
});
|
||||
},
|
||||
write: writeAsset,
|
||||
} satisfies ExecuteOptions<typeof type>;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -420,7 +420,7 @@ export type JobItem =
|
||||
| { name: JobName.Ocr; data: IEntityJob }
|
||||
|
||||
// Workflow
|
||||
| { name: JobName.WorkflowAssetTrigger; data: { workflowId: string; assetId: string; albumId?: string } }
|
||||
| { name: JobName.WorkflowAssetTrigger; data: { workflowId: string; assetId: string } }
|
||||
|
||||
// Editor
|
||||
| { name: JobName.AssetEditThumbnailGeneration; data: IEntityJob };
|
||||
|
||||
@@ -30,17 +30,12 @@ const tests: Array<{ trigger: WorkflowTrigger; types: WorkflowType[]; expected:
|
||||
},
|
||||
{
|
||||
trigger: WorkflowTrigger.AlbumAssetAdded,
|
||||
types: [WorkflowType.AssetAlbumV1],
|
||||
types: [WorkflowType.AssetV1],
|
||||
expected: true,
|
||||
},
|
||||
{
|
||||
trigger: WorkflowTrigger.AlbumAssetAdded,
|
||||
types: [WorkflowType.AssetV1],
|
||||
expected: false,
|
||||
},
|
||||
{
|
||||
trigger: WorkflowTrigger.AssetCreate,
|
||||
types: [WorkflowType.AssetAlbumV1],
|
||||
types: [WorkflowType.AssetPersonV1],
|
||||
expected: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -6,7 +6,7 @@ export const triggerMap: Record<WorkflowTrigger, WorkflowType[]> = {
|
||||
[WorkflowTrigger.AssetCreate]: [WorkflowType.AssetV1],
|
||||
[WorkflowTrigger.PersonRecognized]: [WorkflowType.AssetPersonV1],
|
||||
[WorkflowTrigger.AssetMetadataExtraction]: [WorkflowType.AssetV1],
|
||||
[WorkflowTrigger.AlbumAssetAdded]: [WorkflowType.AssetAlbumV1],
|
||||
[WorkflowTrigger.AlbumAssetAdded]: [WorkflowType.AssetV1],
|
||||
};
|
||||
|
||||
export const getWorkflowTriggers = () =>
|
||||
@@ -16,7 +16,6 @@ export const getWorkflowTriggers = () =>
|
||||
const inferredMap: Record<WorkflowType, WorkflowType[]> = {
|
||||
[WorkflowType.AssetV1]: [],
|
||||
[WorkflowType.AssetPersonV1]: [WorkflowType.AssetV1],
|
||||
[WorkflowType.AssetAlbumV1]: [WorkflowType.AssetV1],
|
||||
};
|
||||
|
||||
const withImpliedItems = (type: WorkflowType): WorkflowType[] => {
|
||||
|
||||
Reference in New Issue
Block a user