mirror of
https://github.com/immich-app/immich.git
synced 2026-06-04 05:05:22 -04:00
feat(plugin-core): add filterByAlbum workflow step
Adds a filterByAlbum core plugin method so workflows (e.g. the AlbumAssetAdded trigger) can be scoped to specific albums. The step checks the asset's album membership via the searchAlbums host function and halts the workflow when the asset is not in any of the configured albums (or, with inverse, when it is).
This commit is contained in:
@@ -152,6 +152,33 @@
|
||||
},
|
||||
"uiHints": ["Filter"]
|
||||
},
|
||||
{
|
||||
"name": "filterByAlbum",
|
||||
"title": "Filter by album",
|
||||
"description": "Continue only when the asset belongs to one of the selected albums",
|
||||
"types": ["AssetV1"],
|
||||
"hostFunctions": true,
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"albumIds": {
|
||||
"type": "string",
|
||||
"array": true,
|
||||
"title": "Album IDs",
|
||||
"description": "Albums to match against",
|
||||
"uiHint": "AlbumId"
|
||||
},
|
||||
"inverse": {
|
||||
"type": "boolean",
|
||||
"title": "Inverse",
|
||||
"description": "Continue only when the asset is NOT in the selected albums",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["albumIds"]
|
||||
},
|
||||
"uiHints": ["Filter"]
|
||||
},
|
||||
{
|
||||
"name": "assetArchive",
|
||||
"title": "Archive asset",
|
||||
|
||||
Vendored
+1
@@ -13,6 +13,7 @@ declare module 'main' {
|
||||
// filters
|
||||
export function assetFileFilter(): I32;
|
||||
export function assetMissingTimeZoneFilter(): I32;
|
||||
export function filterByAlbum(): I32;
|
||||
|
||||
// updates
|
||||
export function assetFavorite(): I32;
|
||||
|
||||
@@ -50,6 +50,20 @@ export const assetMissingTimeZoneFilter = () => {
|
||||
});
|
||||
};
|
||||
|
||||
export const filterByAlbum = () => {
|
||||
return wrapper<WorkflowType.AssetV1, { albumIds: string[]; inverse?: boolean }>(({ config, data, functions }) => {
|
||||
const { albumIds = [], inverse = false } = config;
|
||||
if (albumIds.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const albums = functions.searchAlbums({ assetId: data.asset.id });
|
||||
const isMember = albums.some((album) => albumIds.includes(album.id));
|
||||
|
||||
return { workflow: { continue: isMember !== inverse } };
|
||||
});
|
||||
};
|
||||
|
||||
export const assetFavorite = () => {
|
||||
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
|
||||
const target = config.inverse ? false : true;
|
||||
|
||||
@@ -332,4 +332,65 @@ describe('core plugin', () => {
|
||||
await expect(ctx.get(AlbumRepository).getAssetIds(album.id, [asset.id])).resolves.not.toContain(asset.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('filterByAlbum', () => {
|
||||
it('should continue when the asset is in a selected album', async () => {
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const { album } = await ctx.newAlbum({ ownerId: user.id }, [asset.id]);
|
||||
|
||||
const workflow = await createWorkflow({
|
||||
ownerId: user.id,
|
||||
trigger: WorkflowTrigger.AlbumAssetAdded,
|
||||
steps: [
|
||||
{ method: 'immich-plugin-core#filterByAlbum', config: { albumIds: [album.id] } },
|
||||
{ method: 'immich-plugin-core#assetFavorite' },
|
||||
],
|
||||
});
|
||||
|
||||
await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined();
|
||||
|
||||
await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: true });
|
||||
});
|
||||
|
||||
it('should stop when the asset is not in a selected album', async () => {
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
const [{ album }, { album: other }] = await Promise.all([
|
||||
ctx.newAlbum({ ownerId: user.id }, [asset.id]),
|
||||
ctx.newAlbum({ ownerId: user.id }),
|
||||
]);
|
||||
|
||||
const workflow = await createWorkflow({
|
||||
ownerId: user.id,
|
||||
trigger: WorkflowTrigger.AlbumAssetAdded,
|
||||
steps: [
|
||||
{ method: 'immich-plugin-core#filterByAlbum', config: { albumIds: [other.id] } },
|
||||
{ method: 'immich-plugin-core#assetFavorite' },
|
||||
],
|
||||
});
|
||||
|
||||
await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined();
|
||||
|
||||
await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: false });
|
||||
});
|
||||
|
||||
it('should continue when no albums are configured', async () => {
|
||||
const { user } = await ctx.newUser();
|
||||
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||
|
||||
const workflow = await createWorkflow({
|
||||
ownerId: user.id,
|
||||
trigger: WorkflowTrigger.AlbumAssetAdded,
|
||||
steps: [
|
||||
{ method: 'immich-plugin-core#filterByAlbum', config: { albumIds: [] } },
|
||||
{ method: 'immich-plugin-core#assetFavorite' },
|
||||
],
|
||||
});
|
||||
|
||||
await expect(ctx.sut.handleAssetTrigger({ workflowId: workflow.id, assetId: asset.id })).resolves.toBeUndefined();
|
||||
|
||||
await expect(ctx.get(AssetRepository).getById(asset.id)).resolves.toMatchObject({ isFavorite: true });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user