mirror of
https://github.com/immich-app/immich.git
synced 2026-06-04 05:05:22 -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:
-3
@@ -25,13 +25,11 @@ class WorkflowType {
|
||||
|
||||
static const assetV1 = WorkflowType._(r'AssetV1');
|
||||
static const assetPersonV1 = WorkflowType._(r'AssetPersonV1');
|
||||
static const assetAlbumV1 = WorkflowType._(r'AssetAlbumV1');
|
||||
|
||||
/// List of all possible values in this [enum][WorkflowType].
|
||||
static const values = <WorkflowType>[
|
||||
assetV1,
|
||||
assetPersonV1,
|
||||
assetAlbumV1,
|
||||
];
|
||||
|
||||
static WorkflowType? fromJson(dynamic value) => WorkflowTypeTypeTransformer().decode(value);
|
||||
@@ -72,7 +70,6 @@ class WorkflowTypeTypeTransformer {
|
||||
switch (data) {
|
||||
case r'AssetV1': return WorkflowType.assetV1;
|
||||
case r'AssetPersonV1': return WorkflowType.assetPersonV1;
|
||||
case r'AssetAlbumV1': return WorkflowType.assetAlbumV1;
|
||||
default:
|
||||
if (!allowNull) {
|
||||
throw ArgumentError('Unknown enum value to decode: $data');
|
||||
|
||||
@@ -26840,8 +26840,7 @@
|
||||
"description": "Workflow type",
|
||||
"enum": [
|
||||
"AssetV1",
|
||||
"AssetPersonV1",
|
||||
"AssetAlbumV1"
|
||||
"AssetPersonV1"
|
||||
],
|
||||
"type": "string"
|
||||
},
|
||||
|
||||
@@ -11,7 +11,6 @@ type DeepPartial<T> = T extends Date
|
||||
export type WorkflowEventMap = {
|
||||
[WorkflowType.AssetV1]: AssetV1;
|
||||
[WorkflowType.AssetPersonV1]: AssetPersonV1;
|
||||
[WorkflowType.AssetAlbumV1]: AssetAlbumV1;
|
||||
};
|
||||
|
||||
export type WorkflowEventData<T extends WorkflowType> = WorkflowEventMap[T];
|
||||
@@ -130,12 +129,3 @@ export type AssetPersonV1 = AssetV1 & {
|
||||
name: string;
|
||||
};
|
||||
};
|
||||
|
||||
export type AssetAlbumV1 = AssetV1 & {
|
||||
album: {
|
||||
id: string;
|
||||
ownerId: string;
|
||||
albumName: string;
|
||||
description: string;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -7179,8 +7179,7 @@ export enum PartnerDirection {
|
||||
}
|
||||
export enum WorkflowType {
|
||||
AssetV1 = "AssetV1",
|
||||
AssetPersonV1 = "AssetPersonV1",
|
||||
AssetAlbumV1 = "AssetAlbumV1"
|
||||
AssetPersonV1 = "AssetPersonV1"
|
||||
}
|
||||
export enum WorkflowTrigger {
|
||||
AssetCreate = "AssetCreate",
|
||||
|
||||
@@ -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