feat: workflow asset tag trigger/filter/action (#29043)

* feat: workflow asset tag trigger and filter

* feat(web): show tag names in workflow editor

* fix(web): tag picker in schema config editor

* fix: invalid plugin manifest

* chore: update workflow method wrapper type

* chore: update tag filter method declaration

* feat: workflow action to add tags to assets
This commit is contained in:
Ben Beckford 2026-08-12 16:26:03 -07:00 committed by GitHub
parent b82d480552
commit a939561e70
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 175 additions and 8 deletions

View file

@ -308,6 +308,56 @@
},
"uiHints": ["Filter"]
},
{
"name": "assetAddTags",
"title": "Add Tags",
"description": "Add tags to an asset",
"types": ["AssetV1"],
"hostFunctions": true,
"schema": {
"type": "object",
"properties": {
"tags": {
"title": "Tags",
"description": "Tags to add to the asset",
"type": "string",
"array": true,
"uiHint": {
"type": "TagId"
}
}
},
"required": ["tags"]
}
},
{
"name": "assetTagFilter",
"title": "Filter by tags",
"description": "Filter assets by which tags they have",
"types": ["AssetV1"],
"schema": {
"type": "object",
"properties": {
"tags": {
"title": "Tags",
"description": "Which tags the asset must have",
"type": "string",
"array": true,
"uiHint": {
"type": "TagId"
}
},
"matching": {
"title": "Matching",
"description": "Whether assets must have all, any, or none of the listed tags",
"type": "string",
"enum": ["all", "any", "none"]
}
},
"required": ["tags", "matching"]
},
"uiHints": ["Filter"]
},
{
"name": "assetExifFilter",
"title": "Filter by EXIF metadata",

View file

@ -39,6 +39,11 @@ const matchValueResult = (value: string, config: MatchValueConfig) => {
};
const methods = wrapper<Manifest>({
assetAddTags: ({ config, data, functions }) => {
functions.bulkTagAssets({ assetIds: [data.asset.id], tagIds: config.tags });
return {};
},
assetAddToAlbums: ({ config, data, functions }) => {
const assetId = data.asset.id;
@ -176,6 +181,24 @@ const methods = wrapper<Manifest>({
return { workflow: { continue: hasTimeZone === needsTimeZone } };
},
assetTagFilter: ({ config, data }) => {
const assetTags = data.asset.tags.map((tag) => tag.id);
for (const tag of config.tags) {
if (assetTags.includes(tag)) {
if (config.matching === 'any') {
break;
} else if (config.matching === 'none') {
return { workflow: { continue: false } };
}
} else if (config.matching === 'all') {
return { workflow: { continue: false } };
}
}
return { workflow: { continue: true } };
},
assetTypeFilter: ({ config, data }) => {
return { workflow: { continue: config.allowedTypes.includes(data.asset.type) } };
},
@ -208,6 +231,7 @@ const methods = wrapper<Manifest>({
});
const {
assetAddTags,
assetAddToAlbums,
assetArchive,
assetFavorite,
@ -217,6 +241,7 @@ const {
assetDateFilter,
assetLock,
assetMissingTimeZoneFilter,
assetTagFilter,
assetTypeFilter,
assetVisibility,
webhook,
@ -226,6 +251,7 @@ const {
} = methods;
export {
assetAddTags,
assetAddToAlbums,
assetArchive,
assetFavorite,
@ -235,6 +261,7 @@ export {
assetDateFilter,
assetLock,
assetMissingTimeZoneFilter,
assetTagFilter,
assetTypeFilter,
assetVisibility,
webhook,