mirror of
https://github.com/immich-app/immich.git
synced 2026-05-18 03:10:24 +03:00
feat: plugins
chore: better types feat: plugins
This commit is contained in:
@@ -16,7 +16,7 @@ services:
|
|||||||
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
- ${UPLOAD_LOCATION:-upload-devcontainer-volume}${UPLOAD_LOCATION:+/photos}:/data
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- pnpm_store_server:/buildcache/pnpm-store
|
- pnpm_store_server:/buildcache/pnpm-store
|
||||||
- ../packages/plugins:/build/corePlugin
|
- ../packages/plugin-core:/build/plugins/immich-plugin-core
|
||||||
immich-web:
|
immich-web:
|
||||||
env_file: !reset []
|
env_file: !reset []
|
||||||
immich-machine-learning:
|
immich-machine-learning:
|
||||||
|
|||||||
+10
-14
@@ -62,9 +62,6 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./server
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||||
@@ -84,7 +81,7 @@ jobs:
|
|||||||
github_token: ${{ steps.token.outputs.token }}
|
github_token: ${{ steps.token.outputs.token }}
|
||||||
|
|
||||||
- name: Run ci-unit
|
- name: Run ci-unit
|
||||||
run: mise run ci-unit
|
run: mise run //server:ci-unit
|
||||||
|
|
||||||
cli-unit-tests:
|
cli-unit-tests:
|
||||||
name: Unit Test CLI
|
name: Unit Test CLI
|
||||||
@@ -380,7 +377,7 @@ jobs:
|
|||||||
cache-dependency-path: '**/pnpm-lock.yaml'
|
cache-dependency-path: '**/pnpm-lock.yaml'
|
||||||
|
|
||||||
- name: Setup packages
|
- name: Setup packages
|
||||||
run: pnpm --filter "@immich/*" install --frozen-lockfile && pnpm --filter "@immich/*" build
|
run: pnpm --filter @immich/sdk --filter @immich/cli install --frozen-lockfile && pnpm --filter @immich/sdk --filter @immich/cli build
|
||||||
|
|
||||||
- name: Run setup web
|
- name: Run setup web
|
||||||
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
run: pnpm install --frozen-lockfile && pnpm exec svelte-kit sync
|
||||||
@@ -678,7 +675,6 @@ jobs:
|
|||||||
|
|
||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich install --frozen-lockfile
|
||||||
|
|
||||||
- name: Run API generation
|
- name: Run API generation
|
||||||
run: mise //:open-api
|
run: mise //:open-api
|
||||||
working-directory: open-api
|
working-directory: open-api
|
||||||
@@ -717,9 +713,6 @@ jobs:
|
|||||||
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
--health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||||
ports:
|
ports:
|
||||||
- 5432:5432
|
- 5432:5432
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./server
|
|
||||||
steps:
|
steps:
|
||||||
- id: token
|
- id: token
|
||||||
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
uses: immich-app/devtools/actions/create-workflow-token@caa599d954228439ea3e8ce1c3328f41ab120ee6 # create-workflow-token-action-v2.0.0
|
||||||
@@ -741,18 +734,21 @@ jobs:
|
|||||||
- name: Install server dependencies
|
- name: Install server dependencies
|
||||||
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
run: SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Build plugins
|
||||||
|
run: mise //:plugins
|
||||||
|
|
||||||
- name: Build the app
|
- name: Build the app
|
||||||
run: pnpm build
|
run: mise //server:build
|
||||||
|
|
||||||
- name: Run existing migrations
|
- name: Run existing migrations
|
||||||
run: pnpm migrations:run
|
run: pnpm --filter immich migrations:run
|
||||||
|
|
||||||
- name: Test npm run schema:reset command works
|
- name: Test npm run schema:reset command works
|
||||||
run: pnpm schema:reset
|
run: pnpm --filter immich schema:reset
|
||||||
|
|
||||||
- name: Generate new migrations
|
- name: Generate new migrations
|
||||||
continue-on-error: true
|
continue-on-error: true
|
||||||
run: pnpm migrations:generate src/TestMigration
|
run: pnpm --filter migrations:generate src/TestMigration
|
||||||
|
|
||||||
- name: Find file changes
|
- name: Find file changes
|
||||||
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
uses: tj-actions/verify-changed-files@a1c6acee9df209257a246f2cc6ae8cb6581c1edf # v20.0.4
|
||||||
@@ -768,7 +764,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "ERROR: Generated migration files not up to date!"
|
echo "ERROR: Generated migration files not up to date!"
|
||||||
echo "Changed files: ${CHANGED_FILES}"
|
echo "Changed files: ${CHANGED_FILES}"
|
||||||
cat ./src/*-TestMigration.ts
|
cat ./server/src/*-TestMigration.ts
|
||||||
exit 1
|
exit 1
|
||||||
|
|
||||||
- name: Run SQL generation
|
- name: Run SQL generation
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ services:
|
|||||||
- ${UPLOAD_LOCATION}/photos:/data
|
- ${UPLOAD_LOCATION}/photos:/data
|
||||||
- /etc/localtime:/etc/localtime:ro
|
- /etc/localtime:/etc/localtime:ro
|
||||||
- pnpm_store_server:/buildcache/pnpm-store
|
- pnpm_store_server:/buildcache/pnpm-store
|
||||||
- ../packages/plugins:/build/corePlugin
|
- ../packages/plugin-core:/build/plugins/immich-plugin-core
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .env
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ make e2e
|
|||||||
Before you can run the tests, you need to run the following commands _once_:
|
Before you can run the tests, you need to run the following commands _once_:
|
||||||
|
|
||||||
- `pnpm install`
|
- `pnpm install`
|
||||||
- `pnpm --filter "@immich/*" build`
|
- `pnpm --filter @immich/sdk --filter @immich/cli build`
|
||||||
- `mise //:open-api`
|
- `mise //:open-api`
|
||||||
|
|
||||||
Once the test environment is running, the e2e tests can be run via:
|
Once the test environment is running, the e2e tests can be run via:
|
||||||
|
|||||||
+13
-7
@@ -22,13 +22,12 @@
|
|||||||
"add_birthday": "Add a birthday",
|
"add_birthday": "Add a birthday",
|
||||||
"add_endpoint": "Add endpoint",
|
"add_endpoint": "Add endpoint",
|
||||||
"add_exclusion_pattern": "Add exclusion pattern",
|
"add_exclusion_pattern": "Add exclusion pattern",
|
||||||
"add_filter": "Add filter",
|
|
||||||
"add_filter_description": "Click to add a filter condition",
|
|
||||||
"add_location": "Add location",
|
"add_location": "Add location",
|
||||||
"add_more_users": "Add more users",
|
"add_more_users": "Add more users",
|
||||||
"add_partner": "Add partner",
|
"add_partner": "Add partner",
|
||||||
"add_path": "Add path",
|
"add_path": "Add path",
|
||||||
"add_photos": "Add photos",
|
"add_photos": "Add photos",
|
||||||
|
"add_step": "Add step",
|
||||||
"add_tag": "Add tag",
|
"add_tag": "Add tag",
|
||||||
"add_to": "Add to…",
|
"add_to": "Add to…",
|
||||||
"add_to_album": "Add to album",
|
"add_to_album": "Add to album",
|
||||||
@@ -42,7 +41,6 @@
|
|||||||
"add_to_shared_album": "Add to shared album",
|
"add_to_shared_album": "Add to shared album",
|
||||||
"add_upload_to_stack": "Add upload to stack",
|
"add_upload_to_stack": "Add upload to stack",
|
||||||
"add_url": "Add URL",
|
"add_url": "Add URL",
|
||||||
"add_workflow_step": "Add workflow step",
|
|
||||||
"added_to_archive": "Added to archive",
|
"added_to_archive": "Added to archive",
|
||||||
"added_to_favorites": "Added to favorites",
|
"added_to_favorites": "Added to favorites",
|
||||||
"added_to_favorites_count": "Added {count, number} to favorites",
|
"added_to_favorites_count": "Added {count, number} to favorites",
|
||||||
@@ -733,6 +731,7 @@
|
|||||||
"cannot_update_the_description": "Cannot update the description",
|
"cannot_update_the_description": "Cannot update the description",
|
||||||
"cast": "Cast",
|
"cast": "Cast",
|
||||||
"cast_description": "Configure available cast destinations",
|
"cast_description": "Configure available cast destinations",
|
||||||
|
"change": "Change",
|
||||||
"change_date": "Change date",
|
"change_date": "Change date",
|
||||||
"change_description": "Change description",
|
"change_description": "Change description",
|
||||||
"change_display_order": "Change display order",
|
"change_display_order": "Change display order",
|
||||||
@@ -761,6 +760,7 @@
|
|||||||
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
"check_corrupt_asset_backup_description": "Run this check only over Wi-Fi and once all assets have been backed-up. The procedure might take a few minutes.",
|
||||||
"check_logs": "Check Logs",
|
"check_logs": "Check Logs",
|
||||||
"checksum": "Checksum",
|
"checksum": "Checksum",
|
||||||
|
"choose": "Choose",
|
||||||
"choose_matching_people_to_merge": "Choose matching people to merge",
|
"choose_matching_people_to_merge": "Choose matching people to merge",
|
||||||
"city": "City",
|
"city": "City",
|
||||||
"cleanup_confirm_description": "Immich found {count} assets (created before {date}) safely backed up to the server. Remove the local copies from this device?",
|
"cleanup_confirm_description": "Immich found {count} assets (created before {date}) safely backed up to the server. Remove the local copies from this device?",
|
||||||
@@ -809,6 +809,7 @@
|
|||||||
"comments_are_disabled": "Comments are disabled",
|
"comments_are_disabled": "Comments are disabled",
|
||||||
"common_create_new_album": "Create new album",
|
"common_create_new_album": "Create new album",
|
||||||
"completed": "Completed",
|
"completed": "Completed",
|
||||||
|
"configuration": "Configuration",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"confirm_admin_password": "Confirm Admin Password",
|
"confirm_admin_password": "Confirm Admin Password",
|
||||||
"confirm_delete_face": "Are you sure you want to delete {name} face from the asset?",
|
"confirm_delete_face": "Are you sure you want to delete {name} face from the asset?",
|
||||||
@@ -1628,7 +1629,6 @@
|
|||||||
"next": "Next",
|
"next": "Next",
|
||||||
"next_memory": "Next memory",
|
"next_memory": "Next memory",
|
||||||
"no": "No",
|
"no": "No",
|
||||||
"no_actions_added": "No actions added yet",
|
|
||||||
"no_albums_found": "No albums found",
|
"no_albums_found": "No albums found",
|
||||||
"no_albums_message": "Create an album to organize your photos and videos",
|
"no_albums_message": "Create an album to organize your photos and videos",
|
||||||
"no_albums_with_name_yet": "It looks like you do not have any albums with this name yet.",
|
"no_albums_with_name_yet": "It looks like you do not have any albums with this name yet.",
|
||||||
@@ -1645,7 +1645,6 @@
|
|||||||
"no_exif_info_available": "No exif info available",
|
"no_exif_info_available": "No exif info available",
|
||||||
"no_explore_results_message": "Upload more photos to explore your collection.",
|
"no_explore_results_message": "Upload more photos to explore your collection.",
|
||||||
"no_favorites_message": "Add favorites to quickly find your best pictures and videos",
|
"no_favorites_message": "Add favorites to quickly find your best pictures and videos",
|
||||||
"no_filters_added": "No filters added yet",
|
|
||||||
"no_libraries_message": "Create an external library to view your photos and videos",
|
"no_libraries_message": "Create an external library to view your photos and videos",
|
||||||
"no_local_assets_found": "No local assets found with this checksum",
|
"no_local_assets_found": "No local assets found with this checksum",
|
||||||
"no_location_set": "No location set",
|
"no_location_set": "No location set",
|
||||||
@@ -1658,6 +1657,7 @@
|
|||||||
"no_results": "No results",
|
"no_results": "No results",
|
||||||
"no_results_description": "Try a synonym or more general keyword",
|
"no_results_description": "Try a synonym or more general keyword",
|
||||||
"no_shared_albums_message": "Create an album to share photos and videos with people in your network",
|
"no_shared_albums_message": "Create an album to share photos and videos with people in your network",
|
||||||
|
"no_steps": "No steps added yet",
|
||||||
"no_uploads_in_progress": "No uploads in progress",
|
"no_uploads_in_progress": "No uploads in progress",
|
||||||
"none": "None",
|
"none": "None",
|
||||||
"not_allowed": "Not allowed",
|
"not_allowed": "Not allowed",
|
||||||
@@ -1794,6 +1794,8 @@
|
|||||||
"play_original_video_setting_description": "Prefer playback of original videos rather than transcoded videos. If original asset is not compatible it may not playback correctly.",
|
"play_original_video_setting_description": "Prefer playback of original videos rather than transcoded videos. If original asset is not compatible it may not playback correctly.",
|
||||||
"play_transcoded_video": "Play transcoded video",
|
"play_transcoded_video": "Play transcoded video",
|
||||||
"please_auth_to_access": "Please authenticate to access",
|
"please_auth_to_access": "Please authenticate to access",
|
||||||
|
"plugin_method_filter_type": "Filter",
|
||||||
|
"plugin_method_filter_type_description": "This method can filter events and conditionally prevent subsequent steps from running",
|
||||||
"port": "Port",
|
"port": "Port",
|
||||||
"preferences_settings_subtitle": "Manage the app's preferences",
|
"preferences_settings_subtitle": "Manage the app's preferences",
|
||||||
"preferences_settings_title": "Preferences",
|
"preferences_settings_title": "Preferences",
|
||||||
@@ -2236,6 +2238,10 @@
|
|||||||
"start_date_before_end_date": "Start date must be before end date",
|
"start_date_before_end_date": "Start date must be before end date",
|
||||||
"state": "State",
|
"state": "State",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
|
"step_delete": "Delete step",
|
||||||
|
"step_delete_confirm": "Are you sure you want to delete this step?",
|
||||||
|
"step_details": "Step details",
|
||||||
|
"steps": "Steps",
|
||||||
"stop_casting": "Stop casting",
|
"stop_casting": "Stop casting",
|
||||||
"stop_motion_photo": "Stop Motion Photo",
|
"stop_motion_photo": "Stop Motion Photo",
|
||||||
"stop_photo_sharing": "Stop sharing your photos?",
|
"stop_photo_sharing": "Stop sharing your photos?",
|
||||||
@@ -2329,7 +2335,7 @@
|
|||||||
"trash_page_title": "Trash ({count})",
|
"trash_page_title": "Trash ({count})",
|
||||||
"trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.",
|
"trashed_items_will_be_permanently_deleted_after": "Trashed items will be permanently deleted after {days, plural, one {# day} other {# days}}.",
|
||||||
"trigger": "Trigger",
|
"trigger": "Trigger",
|
||||||
"trigger_asset_uploaded": "Asset Uploaded",
|
"trigger_asset_uploaded": "Asset Upload",
|
||||||
"trigger_asset_uploaded_description": "Triggered when a new asset is uploaded",
|
"trigger_asset_uploaded_description": "Triggered when a new asset is uploaded",
|
||||||
"trigger_description": "An event that kicks off the workflow",
|
"trigger_description": "An event that kicks off the workflow",
|
||||||
"trigger_person_recognized": "Person Recognized",
|
"trigger_person_recognized": "Person Recognized",
|
||||||
@@ -2369,7 +2375,6 @@
|
|||||||
"unsupported_field_type": "Unsupported field type",
|
"unsupported_field_type": "Unsupported field type",
|
||||||
"unsupported_file_type": "File {file} can't be uploaded because its file type {type} is not supported.",
|
"unsupported_file_type": "File {file} can't be uploaded because its file type {type} is not supported.",
|
||||||
"untagged": "Untagged",
|
"untagged": "Untagged",
|
||||||
"untitled_workflow": "Untitled workflow",
|
|
||||||
"up_next": "Up next",
|
"up_next": "Up next",
|
||||||
"update_location_action_prompt": "Update the location of {count} selected assets with:",
|
"update_location_action_prompt": "Update the location of {count} selected assets with:",
|
||||||
"updated_at": "Updated",
|
"updated_at": "Updated",
|
||||||
@@ -2461,6 +2466,7 @@
|
|||||||
"welcome_to_immich": "Welcome to Immich",
|
"welcome_to_immich": "Welcome to Immich",
|
||||||
"width": "Width",
|
"width": "Width",
|
||||||
"wifi_name": "Wi-Fi Name",
|
"wifi_name": "Wi-Fi Name",
|
||||||
|
"workflow": "Workflow",
|
||||||
"workflow_delete_prompt": "Are you sure you want to delete this workflow?",
|
"workflow_delete_prompt": "Are you sure you want to delete this workflow?",
|
||||||
"workflow_deleted": "Workflow deleted",
|
"workflow_deleted": "Workflow deleted",
|
||||||
"workflow_description": "Workflow description",
|
"workflow_description": "Workflow description",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ experimental_monorepo_root = true
|
|||||||
|
|
||||||
[monorepo]
|
[monorepo]
|
||||||
config_roots = [
|
config_roots = [
|
||||||
"packages/plugins",
|
"packages/plugin-core",
|
||||||
"server",
|
"server",
|
||||||
"packages/cli",
|
"packages/cli",
|
||||||
"deployment",
|
"deployment",
|
||||||
@@ -22,6 +22,9 @@ terragrunt = "1.0.3"
|
|||||||
opentofu = "1.11.6"
|
opentofu = "1.11.6"
|
||||||
java = "21.0.2"
|
java = "21.0.2"
|
||||||
"npm:oazapfts" = "7.5.0"
|
"npm:oazapfts" = "7.5.0"
|
||||||
|
"github:extism/cli" = "1.6.3"
|
||||||
|
"github:webassembly/binaryen" = "version_124"
|
||||||
|
"github:extism/js-pdk" = "1.6.0"
|
||||||
|
|
||||||
[tools."github:CQLabs/homebrew-dcm"]
|
[tools."github:CQLabs/homebrew-dcm"]
|
||||||
version = "1.37.0"
|
version = "1.37.0"
|
||||||
@@ -41,6 +44,12 @@ macos-arm64 = { asset_pattern = "jellyfin-ffmpeg_*_portable_macarm64-gpl.tar.xz"
|
|||||||
experimental = true
|
experimental = true
|
||||||
pin = true
|
pin = true
|
||||||
|
|
||||||
|
[tasks.plugins]
|
||||||
|
run = [
|
||||||
|
"pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core install --frozen-lockfile",
|
||||||
|
"pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core build"
|
||||||
|
]
|
||||||
|
|
||||||
[tasks.open-api-typescript]
|
[tasks.open-api-typescript]
|
||||||
run = [
|
run = [
|
||||||
"oazapfts --optimistic --argumentStyle=object --useEnumType --allSchemas open-api/immich-openapi-specs.json packages/sdk/src/fetch-client.ts",
|
"oazapfts --optimistic --argumentStyle=object --useEnumType --allSchemas open-api/immich-openapi-specs.json packages/sdk/src/fetch-client.ts",
|
||||||
@@ -55,6 +64,8 @@ run = "bash ./bin/generate-dart-sdk.sh"
|
|||||||
[tasks.open-api]
|
[tasks.open-api]
|
||||||
env = { SHARP_IGNORE_GLOBAL_LIBVIPS = true }
|
env = { SHARP_IGNORE_GLOBAL_LIBVIPS = true }
|
||||||
run = [
|
run = [
|
||||||
|
{ task = "//:plugins" },
|
||||||
|
{ task = "//server:build" },
|
||||||
{ task = "//server:install" },
|
{ task = "//server:install" },
|
||||||
{ task = "//server:build" },
|
{ task = "//server:build" },
|
||||||
{ task = "//server:sync-open-api" },
|
{ task = "//server:sync-open-api" },
|
||||||
|
|||||||
Generated
+12
-16
@@ -205,8 +205,8 @@ Class | Method | HTTP request | Description
|
|||||||
*PeopleApi* | [**updatePeople**](doc//PeopleApi.md#updatepeople) | **PUT** /people | Update people
|
*PeopleApi* | [**updatePeople**](doc//PeopleApi.md#updatepeople) | **PUT** /people | Update people
|
||||||
*PeopleApi* | [**updatePerson**](doc//PeopleApi.md#updateperson) | **PUT** /people/{id} | Update person
|
*PeopleApi* | [**updatePerson**](doc//PeopleApi.md#updateperson) | **PUT** /people/{id} | Update person
|
||||||
*PluginsApi* | [**getPlugin**](doc//PluginsApi.md#getplugin) | **GET** /plugins/{id} | Retrieve a plugin
|
*PluginsApi* | [**getPlugin**](doc//PluginsApi.md#getplugin) | **GET** /plugins/{id} | Retrieve a plugin
|
||||||
*PluginsApi* | [**getPluginTriggers**](doc//PluginsApi.md#getplugintriggers) | **GET** /plugins/triggers | List all plugin triggers
|
*PluginsApi* | [**searchPluginMethods**](doc//PluginsApi.md#searchpluginmethods) | **GET** /plugins/methods | Retrieve plugin methods
|
||||||
*PluginsApi* | [**getPlugins**](doc//PluginsApi.md#getplugins) | **GET** /plugins | List all plugins
|
*PluginsApi* | [**searchPlugins**](doc//PluginsApi.md#searchplugins) | **GET** /plugins | List all plugins
|
||||||
*QueuesApi* | [**emptyQueue**](doc//QueuesApi.md#emptyqueue) | **DELETE** /queues/{name}/jobs | Empty a queue
|
*QueuesApi* | [**emptyQueue**](doc//QueuesApi.md#emptyqueue) | **DELETE** /queues/{name}/jobs | Empty a queue
|
||||||
*QueuesApi* | [**getQueue**](doc//QueuesApi.md#getqueue) | **GET** /queues/{name} | Retrieve a queue
|
*QueuesApi* | [**getQueue**](doc//QueuesApi.md#getqueue) | **GET** /queues/{name} | Retrieve a queue
|
||||||
*QueuesApi* | [**getQueueJobs**](doc//QueuesApi.md#getqueuejobs) | **GET** /queues/{name}/jobs | Retrieve queue jobs
|
*QueuesApi* | [**getQueueJobs**](doc//QueuesApi.md#getqueuejobs) | **GET** /queues/{name}/jobs | Retrieve queue jobs
|
||||||
@@ -314,7 +314,9 @@ Class | Method | HTTP request | Description
|
|||||||
*WorkflowsApi* | [**createWorkflow**](doc//WorkflowsApi.md#createworkflow) | **POST** /workflows | Create a workflow
|
*WorkflowsApi* | [**createWorkflow**](doc//WorkflowsApi.md#createworkflow) | **POST** /workflows | Create a workflow
|
||||||
*WorkflowsApi* | [**deleteWorkflow**](doc//WorkflowsApi.md#deleteworkflow) | **DELETE** /workflows/{id} | Delete a workflow
|
*WorkflowsApi* | [**deleteWorkflow**](doc//WorkflowsApi.md#deleteworkflow) | **DELETE** /workflows/{id} | Delete a workflow
|
||||||
*WorkflowsApi* | [**getWorkflow**](doc//WorkflowsApi.md#getworkflow) | **GET** /workflows/{id} | Retrieve a workflow
|
*WorkflowsApi* | [**getWorkflow**](doc//WorkflowsApi.md#getworkflow) | **GET** /workflows/{id} | Retrieve a workflow
|
||||||
*WorkflowsApi* | [**getWorkflows**](doc//WorkflowsApi.md#getworkflows) | **GET** /workflows | List all workflows
|
*WorkflowsApi* | [**getWorkflowForShare**](doc//WorkflowsApi.md#getworkflowforshare) | **GET** /workflows/{id}/share | Retrieve a workflow
|
||||||
|
*WorkflowsApi* | [**getWorkflowTriggers**](doc//WorkflowsApi.md#getworkflowtriggers) | **GET** /workflows/triggers | List all workflow triggers
|
||||||
|
*WorkflowsApi* | [**searchWorkflows**](doc//WorkflowsApi.md#searchworkflows) | **GET** /workflows | List all workflows
|
||||||
*WorkflowsApi* | [**updateWorkflow**](doc//WorkflowsApi.md#updateworkflow) | **PUT** /workflows/{id} | Update a workflow
|
*WorkflowsApi* | [**updateWorkflow**](doc//WorkflowsApi.md#updateworkflow) | **PUT** /workflows/{id} | Update a workflow
|
||||||
|
|
||||||
|
|
||||||
@@ -487,16 +489,8 @@ Class | Method | HTTP request | Description
|
|||||||
- [PinCodeResetDto](doc//PinCodeResetDto.md)
|
- [PinCodeResetDto](doc//PinCodeResetDto.md)
|
||||||
- [PinCodeSetupDto](doc//PinCodeSetupDto.md)
|
- [PinCodeSetupDto](doc//PinCodeSetupDto.md)
|
||||||
- [PlacesResponseDto](doc//PlacesResponseDto.md)
|
- [PlacesResponseDto](doc//PlacesResponseDto.md)
|
||||||
- [PluginActionResponseDto](doc//PluginActionResponseDto.md)
|
- [PluginMethodResponseDto](doc//PluginMethodResponseDto.md)
|
||||||
- [PluginContextType](doc//PluginContextType.md)
|
|
||||||
- [PluginFilterResponseDto](doc//PluginFilterResponseDto.md)
|
|
||||||
- [PluginJsonSchema](doc//PluginJsonSchema.md)
|
|
||||||
- [PluginJsonSchemaProperty](doc//PluginJsonSchemaProperty.md)
|
|
||||||
- [PluginJsonSchemaPropertyAdditionalProperties](doc//PluginJsonSchemaPropertyAdditionalProperties.md)
|
|
||||||
- [PluginJsonSchemaType](doc//PluginJsonSchemaType.md)
|
|
||||||
- [PluginResponseDto](doc//PluginResponseDto.md)
|
- [PluginResponseDto](doc//PluginResponseDto.md)
|
||||||
- [PluginTriggerResponseDto](doc//PluginTriggerResponseDto.md)
|
|
||||||
- [PluginTriggerType](doc//PluginTriggerType.md)
|
|
||||||
- [PurchaseResponse](doc//PurchaseResponse.md)
|
- [PurchaseResponse](doc//PurchaseResponse.md)
|
||||||
- [PurchaseUpdate](doc//PurchaseUpdate.md)
|
- [PurchaseUpdate](doc//PurchaseUpdate.md)
|
||||||
- [QueueCommand](doc//QueueCommand.md)
|
- [QueueCommand](doc//QueueCommand.md)
|
||||||
@@ -669,12 +663,14 @@ Class | Method | HTTP request | Description
|
|||||||
- [VersionCheckStateResponseDto](doc//VersionCheckStateResponseDto.md)
|
- [VersionCheckStateResponseDto](doc//VersionCheckStateResponseDto.md)
|
||||||
- [VideoCodec](doc//VideoCodec.md)
|
- [VideoCodec](doc//VideoCodec.md)
|
||||||
- [VideoContainer](doc//VideoContainer.md)
|
- [VideoContainer](doc//VideoContainer.md)
|
||||||
- [WorkflowActionItemDto](doc//WorkflowActionItemDto.md)
|
|
||||||
- [WorkflowActionResponseDto](doc//WorkflowActionResponseDto.md)
|
|
||||||
- [WorkflowCreateDto](doc//WorkflowCreateDto.md)
|
- [WorkflowCreateDto](doc//WorkflowCreateDto.md)
|
||||||
- [WorkflowFilterItemDto](doc//WorkflowFilterItemDto.md)
|
|
||||||
- [WorkflowFilterResponseDto](doc//WorkflowFilterResponseDto.md)
|
|
||||||
- [WorkflowResponseDto](doc//WorkflowResponseDto.md)
|
- [WorkflowResponseDto](doc//WorkflowResponseDto.md)
|
||||||
|
- [WorkflowShareResponseDto](doc//WorkflowShareResponseDto.md)
|
||||||
|
- [WorkflowShareStepDto](doc//WorkflowShareStepDto.md)
|
||||||
|
- [WorkflowStepDto](doc//WorkflowStepDto.md)
|
||||||
|
- [WorkflowTrigger](doc//WorkflowTrigger.md)
|
||||||
|
- [WorkflowTriggerResponseDto](doc//WorkflowTriggerResponseDto.md)
|
||||||
|
- [WorkflowType](doc//WorkflowType.md)
|
||||||
- [WorkflowUpdateDto](doc//WorkflowUpdateDto.md)
|
- [WorkflowUpdateDto](doc//WorkflowUpdateDto.md)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+7
-13
@@ -235,16 +235,8 @@ part 'model/pin_code_change_dto.dart';
|
|||||||
part 'model/pin_code_reset_dto.dart';
|
part 'model/pin_code_reset_dto.dart';
|
||||||
part 'model/pin_code_setup_dto.dart';
|
part 'model/pin_code_setup_dto.dart';
|
||||||
part 'model/places_response_dto.dart';
|
part 'model/places_response_dto.dart';
|
||||||
part 'model/plugin_action_response_dto.dart';
|
part 'model/plugin_method_response_dto.dart';
|
||||||
part 'model/plugin_context_type.dart';
|
|
||||||
part 'model/plugin_filter_response_dto.dart';
|
|
||||||
part 'model/plugin_json_schema.dart';
|
|
||||||
part 'model/plugin_json_schema_property.dart';
|
|
||||||
part 'model/plugin_json_schema_property_additional_properties.dart';
|
|
||||||
part 'model/plugin_json_schema_type.dart';
|
|
||||||
part 'model/plugin_response_dto.dart';
|
part 'model/plugin_response_dto.dart';
|
||||||
part 'model/plugin_trigger_response_dto.dart';
|
|
||||||
part 'model/plugin_trigger_type.dart';
|
|
||||||
part 'model/purchase_response.dart';
|
part 'model/purchase_response.dart';
|
||||||
part 'model/purchase_update.dart';
|
part 'model/purchase_update.dart';
|
||||||
part 'model/queue_command.dart';
|
part 'model/queue_command.dart';
|
||||||
@@ -417,12 +409,14 @@ part 'model/validate_library_response_dto.dart';
|
|||||||
part 'model/version_check_state_response_dto.dart';
|
part 'model/version_check_state_response_dto.dart';
|
||||||
part 'model/video_codec.dart';
|
part 'model/video_codec.dart';
|
||||||
part 'model/video_container.dart';
|
part 'model/video_container.dart';
|
||||||
part 'model/workflow_action_item_dto.dart';
|
|
||||||
part 'model/workflow_action_response_dto.dart';
|
|
||||||
part 'model/workflow_create_dto.dart';
|
part 'model/workflow_create_dto.dart';
|
||||||
part 'model/workflow_filter_item_dto.dart';
|
|
||||||
part 'model/workflow_filter_response_dto.dart';
|
|
||||||
part 'model/workflow_response_dto.dart';
|
part 'model/workflow_response_dto.dart';
|
||||||
|
part 'model/workflow_share_response_dto.dart';
|
||||||
|
part 'model/workflow_share_step_dto.dart';
|
||||||
|
part 'model/workflow_step_dto.dart';
|
||||||
|
part 'model/workflow_trigger.dart';
|
||||||
|
part 'model/workflow_trigger_response_dto.dart';
|
||||||
|
part 'model/workflow_type.dart';
|
||||||
part 'model/workflow_update_dto.dart';
|
part 'model/workflow_update_dto.dart';
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Generated
+144
-13
@@ -73,14 +73,40 @@ class PluginsApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List all plugin triggers
|
/// Retrieve plugin methods
|
||||||
///
|
///
|
||||||
/// Retrieve a list of all available plugin triggers.
|
/// Retrieve a list of plugin methods
|
||||||
///
|
///
|
||||||
/// Note: This method returns the HTTP [Response].
|
/// Note: This method returns the HTTP [Response].
|
||||||
Future<Response> getPluginTriggersWithHttpInfo() async {
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] description:
|
||||||
|
///
|
||||||
|
/// * [bool] enabled:
|
||||||
|
/// Whether the plugin method is enabled
|
||||||
|
///
|
||||||
|
/// * [String] id:
|
||||||
|
/// Plugin method ID
|
||||||
|
///
|
||||||
|
/// * [String] name:
|
||||||
|
///
|
||||||
|
/// * [String] pluginName:
|
||||||
|
/// Plugin name
|
||||||
|
///
|
||||||
|
/// * [String] pluginVersion:
|
||||||
|
/// Plugin version
|
||||||
|
///
|
||||||
|
/// * [String] title:
|
||||||
|
///
|
||||||
|
/// * [WorkflowTrigger] trigger:
|
||||||
|
/// Workflow trigger
|
||||||
|
///
|
||||||
|
/// * [WorkflowType] type:
|
||||||
|
/// Workflow types
|
||||||
|
Future<Response> searchPluginMethodsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, String? pluginName, String? pluginVersion, String? title, WorkflowTrigger? trigger, WorkflowType? type, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/plugins/triggers';
|
final apiPath = r'/plugins/methods';
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
Object? postBody;
|
Object? postBody;
|
||||||
@@ -89,6 +115,34 @@ class PluginsApi {
|
|||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (description != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'description', description));
|
||||||
|
}
|
||||||
|
if (enabled != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'enabled', enabled));
|
||||||
|
}
|
||||||
|
if (id != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'id', id));
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'name', name));
|
||||||
|
}
|
||||||
|
if (pluginName != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'pluginName', pluginName));
|
||||||
|
}
|
||||||
|
if (pluginVersion != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'pluginVersion', pluginVersion));
|
||||||
|
}
|
||||||
|
if (title != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'title', title));
|
||||||
|
}
|
||||||
|
if (trigger != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'trigger', trigger));
|
||||||
|
}
|
||||||
|
if (type != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'type', type));
|
||||||
|
}
|
||||||
|
|
||||||
const contentTypes = <String>[];
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
@@ -103,11 +157,37 @@ class PluginsApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List all plugin triggers
|
/// Retrieve plugin methods
|
||||||
///
|
///
|
||||||
/// Retrieve a list of all available plugin triggers.
|
/// Retrieve a list of plugin methods
|
||||||
Future<List<PluginTriggerResponseDto>?> getPluginTriggers() async {
|
///
|
||||||
final response = await getPluginTriggersWithHttpInfo();
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] description:
|
||||||
|
///
|
||||||
|
/// * [bool] enabled:
|
||||||
|
/// Whether the plugin method is enabled
|
||||||
|
///
|
||||||
|
/// * [String] id:
|
||||||
|
/// Plugin method ID
|
||||||
|
///
|
||||||
|
/// * [String] name:
|
||||||
|
///
|
||||||
|
/// * [String] pluginName:
|
||||||
|
/// Plugin name
|
||||||
|
///
|
||||||
|
/// * [String] pluginVersion:
|
||||||
|
/// Plugin version
|
||||||
|
///
|
||||||
|
/// * [String] title:
|
||||||
|
///
|
||||||
|
/// * [WorkflowTrigger] trigger:
|
||||||
|
/// Workflow trigger
|
||||||
|
///
|
||||||
|
/// * [WorkflowType] type:
|
||||||
|
/// Workflow types
|
||||||
|
Future<List<PluginMethodResponseDto>?> searchPluginMethods({ String? description, bool? enabled, String? id, String? name, String? pluginName, String? pluginVersion, String? title, WorkflowTrigger? trigger, WorkflowType? type, }) async {
|
||||||
|
final response = await searchPluginMethodsWithHttpInfo( description: description, enabled: enabled, id: id, name: name, pluginName: pluginName, pluginVersion: pluginVersion, title: title, trigger: trigger, type: type, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
@@ -116,8 +196,8 @@ class PluginsApi {
|
|||||||
// FormatException when trying to decode an empty string.
|
// FormatException when trying to decode an empty string.
|
||||||
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
final responseBody = await _decodeBodyBytes(response);
|
final responseBody = await _decodeBodyBytes(response);
|
||||||
return (await apiClient.deserializeAsync(responseBody, 'List<PluginTriggerResponseDto>') as List)
|
return (await apiClient.deserializeAsync(responseBody, 'List<PluginMethodResponseDto>') as List)
|
||||||
.cast<PluginTriggerResponseDto>()
|
.cast<PluginMethodResponseDto>()
|
||||||
.toList(growable: false);
|
.toList(growable: false);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -129,7 +209,23 @@ class PluginsApi {
|
|||||||
/// Retrieve a list of plugins available to the authenticated user.
|
/// Retrieve a list of plugins available to the authenticated user.
|
||||||
///
|
///
|
||||||
/// Note: This method returns the HTTP [Response].
|
/// Note: This method returns the HTTP [Response].
|
||||||
Future<Response> getPluginsWithHttpInfo() async {
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] description:
|
||||||
|
///
|
||||||
|
/// * [bool] enabled:
|
||||||
|
/// Whether the plugin is enabled
|
||||||
|
///
|
||||||
|
/// * [String] id:
|
||||||
|
/// Plugin ID
|
||||||
|
///
|
||||||
|
/// * [String] name:
|
||||||
|
///
|
||||||
|
/// * [String] title:
|
||||||
|
///
|
||||||
|
/// * [String] version:
|
||||||
|
Future<Response> searchPluginsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, String? title, String? version, }) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/plugins';
|
final apiPath = r'/plugins';
|
||||||
|
|
||||||
@@ -140,6 +236,25 @@ class PluginsApi {
|
|||||||
final headerParams = <String, String>{};
|
final headerParams = <String, String>{};
|
||||||
final formParams = <String, String>{};
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (description != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'description', description));
|
||||||
|
}
|
||||||
|
if (enabled != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'enabled', enabled));
|
||||||
|
}
|
||||||
|
if (id != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'id', id));
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'name', name));
|
||||||
|
}
|
||||||
|
if (title != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'title', title));
|
||||||
|
}
|
||||||
|
if (version != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'version', version));
|
||||||
|
}
|
||||||
|
|
||||||
const contentTypes = <String>[];
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
@@ -157,8 +272,24 @@ class PluginsApi {
|
|||||||
/// List all plugins
|
/// List all plugins
|
||||||
///
|
///
|
||||||
/// Retrieve a list of plugins available to the authenticated user.
|
/// Retrieve a list of plugins available to the authenticated user.
|
||||||
Future<List<PluginResponseDto>?> getPlugins() async {
|
///
|
||||||
final response = await getPluginsWithHttpInfo();
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] description:
|
||||||
|
///
|
||||||
|
/// * [bool] enabled:
|
||||||
|
/// Whether the plugin is enabled
|
||||||
|
///
|
||||||
|
/// * [String] id:
|
||||||
|
/// Plugin ID
|
||||||
|
///
|
||||||
|
/// * [String] name:
|
||||||
|
///
|
||||||
|
/// * [String] title:
|
||||||
|
///
|
||||||
|
/// * [String] version:
|
||||||
|
Future<List<PluginResponseDto>?> searchPlugins({ String? description, bool? enabled, String? id, String? name, String? title, String? version, }) async {
|
||||||
|
final response = await searchPluginsWithHttpInfo( description: description, enabled: enabled, id: id, name: name, title: title, version: version, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
|||||||
+164
-6
@@ -178,14 +178,19 @@ class WorkflowsApi {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// List all workflows
|
/// Retrieve a workflow
|
||||||
///
|
///
|
||||||
/// Retrieve a list of workflows available to the authenticated user.
|
/// Retrieve a workflow details without ids, default values, etc.
|
||||||
///
|
///
|
||||||
/// Note: This method returns the HTTP [Response].
|
/// Note: This method returns the HTTP [Response].
|
||||||
Future<Response> getWorkflowsWithHttpInfo() async {
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] id (required):
|
||||||
|
Future<Response> getWorkflowForShareWithHttpInfo(String id,) async {
|
||||||
// ignore: prefer_const_declarations
|
// ignore: prefer_const_declarations
|
||||||
final apiPath = r'/workflows';
|
final apiPath = r'/workflows/{id}/share'
|
||||||
|
.replaceAll('{id}', id);
|
||||||
|
|
||||||
// ignore: prefer_final_locals
|
// ignore: prefer_final_locals
|
||||||
Object? postBody;
|
Object? postBody;
|
||||||
@@ -208,11 +213,164 @@ class WorkflowsApi {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieve a workflow
|
||||||
|
///
|
||||||
|
/// Retrieve a workflow details without ids, default values, etc.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] id (required):
|
||||||
|
Future<WorkflowShareResponseDto?> getWorkflowForShare(String id,) async {
|
||||||
|
final response = await getWorkflowForShareWithHttpInfo(id,);
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
return await apiClient.deserializeAsync(await _decodeBodyBytes(response), 'WorkflowShareResponseDto',) as WorkflowShareResponseDto;
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all workflow triggers
|
||||||
|
///
|
||||||
|
/// Retrieve a list of all available workflow triggers.
|
||||||
|
///
|
||||||
|
/// Note: This method returns the HTTP [Response].
|
||||||
|
Future<Response> getWorkflowTriggersWithHttpInfo() async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final apiPath = r'/workflows/triggers';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
apiPath,
|
||||||
|
'GET',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all workflow triggers
|
||||||
|
///
|
||||||
|
/// Retrieve a list of all available workflow triggers.
|
||||||
|
Future<List<WorkflowTriggerResponseDto>?> getWorkflowTriggers() async {
|
||||||
|
final response = await getWorkflowTriggersWithHttpInfo();
|
||||||
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
|
}
|
||||||
|
// When a remote server returns no body with a status of 204, we shall not decode it.
|
||||||
|
// At the time of writing this, `dart:convert` will throw an "Unexpected end of input"
|
||||||
|
// FormatException when trying to decode an empty string.
|
||||||
|
if (response.body.isNotEmpty && response.statusCode != HttpStatus.noContent) {
|
||||||
|
final responseBody = await _decodeBodyBytes(response);
|
||||||
|
return (await apiClient.deserializeAsync(responseBody, 'List<WorkflowTriggerResponseDto>') as List)
|
||||||
|
.cast<WorkflowTriggerResponseDto>()
|
||||||
|
.toList(growable: false);
|
||||||
|
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/// List all workflows
|
/// List all workflows
|
||||||
///
|
///
|
||||||
/// Retrieve a list of workflows available to the authenticated user.
|
/// Retrieve a list of workflows available to the authenticated user.
|
||||||
Future<List<WorkflowResponseDto>?> getWorkflows() async {
|
///
|
||||||
final response = await getWorkflowsWithHttpInfo();
|
/// Note: This method returns the HTTP [Response].
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] description:
|
||||||
|
/// Workflow description
|
||||||
|
///
|
||||||
|
/// * [bool] enabled:
|
||||||
|
/// Workflow enabled
|
||||||
|
///
|
||||||
|
/// * [String] id:
|
||||||
|
/// Workflow ID
|
||||||
|
///
|
||||||
|
/// * [String] name:
|
||||||
|
/// Workflow name
|
||||||
|
///
|
||||||
|
/// * [WorkflowTrigger] trigger:
|
||||||
|
/// Workflow trigger type
|
||||||
|
Future<Response> searchWorkflowsWithHttpInfo({ String? description, bool? enabled, String? id, String? name, WorkflowTrigger? trigger, }) async {
|
||||||
|
// ignore: prefer_const_declarations
|
||||||
|
final apiPath = r'/workflows';
|
||||||
|
|
||||||
|
// ignore: prefer_final_locals
|
||||||
|
Object? postBody;
|
||||||
|
|
||||||
|
final queryParams = <QueryParam>[];
|
||||||
|
final headerParams = <String, String>{};
|
||||||
|
final formParams = <String, String>{};
|
||||||
|
|
||||||
|
if (description != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'description', description));
|
||||||
|
}
|
||||||
|
if (enabled != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'enabled', enabled));
|
||||||
|
}
|
||||||
|
if (id != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'id', id));
|
||||||
|
}
|
||||||
|
if (name != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'name', name));
|
||||||
|
}
|
||||||
|
if (trigger != null) {
|
||||||
|
queryParams.addAll(_queryParams('', 'trigger', trigger));
|
||||||
|
}
|
||||||
|
|
||||||
|
const contentTypes = <String>[];
|
||||||
|
|
||||||
|
|
||||||
|
return apiClient.invokeAPI(
|
||||||
|
apiPath,
|
||||||
|
'GET',
|
||||||
|
queryParams,
|
||||||
|
postBody,
|
||||||
|
headerParams,
|
||||||
|
formParams,
|
||||||
|
contentTypes.isEmpty ? null : contentTypes.first,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List all workflows
|
||||||
|
///
|
||||||
|
/// Retrieve a list of workflows available to the authenticated user.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
///
|
||||||
|
/// * [String] description:
|
||||||
|
/// Workflow description
|
||||||
|
///
|
||||||
|
/// * [bool] enabled:
|
||||||
|
/// Workflow enabled
|
||||||
|
///
|
||||||
|
/// * [String] id:
|
||||||
|
/// Workflow ID
|
||||||
|
///
|
||||||
|
/// * [String] name:
|
||||||
|
/// Workflow name
|
||||||
|
///
|
||||||
|
/// * [WorkflowTrigger] trigger:
|
||||||
|
/// Workflow trigger type
|
||||||
|
Future<List<WorkflowResponseDto>?> searchWorkflows({ String? description, bool? enabled, String? id, String? name, WorkflowTrigger? trigger, }) async {
|
||||||
|
final response = await searchWorkflowsWithHttpInfo( description: description, enabled: enabled, id: id, name: name, trigger: trigger, );
|
||||||
if (response.statusCode >= HttpStatus.badRequest) {
|
if (response.statusCode >= HttpStatus.badRequest) {
|
||||||
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
throw ApiException(response.statusCode, await _decodeBodyBytes(response));
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+14
-26
@@ -516,26 +516,10 @@ class ApiClient {
|
|||||||
return PinCodeSetupDto.fromJson(value);
|
return PinCodeSetupDto.fromJson(value);
|
||||||
case 'PlacesResponseDto':
|
case 'PlacesResponseDto':
|
||||||
return PlacesResponseDto.fromJson(value);
|
return PlacesResponseDto.fromJson(value);
|
||||||
case 'PluginActionResponseDto':
|
case 'PluginMethodResponseDto':
|
||||||
return PluginActionResponseDto.fromJson(value);
|
return PluginMethodResponseDto.fromJson(value);
|
||||||
case 'PluginContextType':
|
|
||||||
return PluginContextTypeTypeTransformer().decode(value);
|
|
||||||
case 'PluginFilterResponseDto':
|
|
||||||
return PluginFilterResponseDto.fromJson(value);
|
|
||||||
case 'PluginJsonSchema':
|
|
||||||
return PluginJsonSchema.fromJson(value);
|
|
||||||
case 'PluginJsonSchemaProperty':
|
|
||||||
return PluginJsonSchemaProperty.fromJson(value);
|
|
||||||
case 'PluginJsonSchemaPropertyAdditionalProperties':
|
|
||||||
return PluginJsonSchemaPropertyAdditionalProperties.fromJson(value);
|
|
||||||
case 'PluginJsonSchemaType':
|
|
||||||
return PluginJsonSchemaTypeTypeTransformer().decode(value);
|
|
||||||
case 'PluginResponseDto':
|
case 'PluginResponseDto':
|
||||||
return PluginResponseDto.fromJson(value);
|
return PluginResponseDto.fromJson(value);
|
||||||
case 'PluginTriggerResponseDto':
|
|
||||||
return PluginTriggerResponseDto.fromJson(value);
|
|
||||||
case 'PluginTriggerType':
|
|
||||||
return PluginTriggerTypeTypeTransformer().decode(value);
|
|
||||||
case 'PurchaseResponse':
|
case 'PurchaseResponse':
|
||||||
return PurchaseResponse.fromJson(value);
|
return PurchaseResponse.fromJson(value);
|
||||||
case 'PurchaseUpdate':
|
case 'PurchaseUpdate':
|
||||||
@@ -880,18 +864,22 @@ class ApiClient {
|
|||||||
return VideoCodecTypeTransformer().decode(value);
|
return VideoCodecTypeTransformer().decode(value);
|
||||||
case 'VideoContainer':
|
case 'VideoContainer':
|
||||||
return VideoContainerTypeTransformer().decode(value);
|
return VideoContainerTypeTransformer().decode(value);
|
||||||
case 'WorkflowActionItemDto':
|
|
||||||
return WorkflowActionItemDto.fromJson(value);
|
|
||||||
case 'WorkflowActionResponseDto':
|
|
||||||
return WorkflowActionResponseDto.fromJson(value);
|
|
||||||
case 'WorkflowCreateDto':
|
case 'WorkflowCreateDto':
|
||||||
return WorkflowCreateDto.fromJson(value);
|
return WorkflowCreateDto.fromJson(value);
|
||||||
case 'WorkflowFilterItemDto':
|
|
||||||
return WorkflowFilterItemDto.fromJson(value);
|
|
||||||
case 'WorkflowFilterResponseDto':
|
|
||||||
return WorkflowFilterResponseDto.fromJson(value);
|
|
||||||
case 'WorkflowResponseDto':
|
case 'WorkflowResponseDto':
|
||||||
return WorkflowResponseDto.fromJson(value);
|
return WorkflowResponseDto.fromJson(value);
|
||||||
|
case 'WorkflowShareResponseDto':
|
||||||
|
return WorkflowShareResponseDto.fromJson(value);
|
||||||
|
case 'WorkflowShareStepDto':
|
||||||
|
return WorkflowShareStepDto.fromJson(value);
|
||||||
|
case 'WorkflowStepDto':
|
||||||
|
return WorkflowStepDto.fromJson(value);
|
||||||
|
case 'WorkflowTrigger':
|
||||||
|
return WorkflowTriggerTypeTransformer().decode(value);
|
||||||
|
case 'WorkflowTriggerResponseDto':
|
||||||
|
return WorkflowTriggerResponseDto.fromJson(value);
|
||||||
|
case 'WorkflowType':
|
||||||
|
return WorkflowTypeTypeTransformer().decode(value);
|
||||||
case 'WorkflowUpdateDto':
|
case 'WorkflowUpdateDto':
|
||||||
return WorkflowUpdateDto.fromJson(value);
|
return WorkflowUpdateDto.fromJson(value);
|
||||||
default:
|
default:
|
||||||
|
|||||||
Generated
+6
-9
@@ -142,15 +142,6 @@ String parameterToString(dynamic value) {
|
|||||||
if (value is Permission) {
|
if (value is Permission) {
|
||||||
return PermissionTypeTransformer().encode(value).toString();
|
return PermissionTypeTransformer().encode(value).toString();
|
||||||
}
|
}
|
||||||
if (value is PluginContextType) {
|
|
||||||
return PluginContextTypeTypeTransformer().encode(value).toString();
|
|
||||||
}
|
|
||||||
if (value is PluginJsonSchemaType) {
|
|
||||||
return PluginJsonSchemaTypeTypeTransformer().encode(value).toString();
|
|
||||||
}
|
|
||||||
if (value is PluginTriggerType) {
|
|
||||||
return PluginTriggerTypeTypeTransformer().encode(value).toString();
|
|
||||||
}
|
|
||||||
if (value is QueueCommand) {
|
if (value is QueueCommand) {
|
||||||
return QueueCommandTypeTransformer().encode(value).toString();
|
return QueueCommandTypeTransformer().encode(value).toString();
|
||||||
}
|
}
|
||||||
@@ -208,6 +199,12 @@ String parameterToString(dynamic value) {
|
|||||||
if (value is VideoContainer) {
|
if (value is VideoContainer) {
|
||||||
return VideoContainerTypeTransformer().encode(value).toString();
|
return VideoContainerTypeTransformer().encode(value).toString();
|
||||||
}
|
}
|
||||||
|
if (value is WorkflowTrigger) {
|
||||||
|
return WorkflowTriggerTypeTransformer().encode(value).toString();
|
||||||
|
}
|
||||||
|
if (value is WorkflowType) {
|
||||||
|
return WorkflowTypeTypeTransformer().encode(value).toString();
|
||||||
|
}
|
||||||
return value.toString();
|
return value.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Generated
+3
-3
@@ -77,7 +77,7 @@ class JobName {
|
|||||||
static const versionCheck = JobName._(r'VersionCheck');
|
static const versionCheck = JobName._(r'VersionCheck');
|
||||||
static const ocrQueueAll = JobName._(r'OcrQueueAll');
|
static const ocrQueueAll = JobName._(r'OcrQueueAll');
|
||||||
static const ocr = JobName._(r'Ocr');
|
static const ocr = JobName._(r'Ocr');
|
||||||
static const workflowRun = JobName._(r'WorkflowRun');
|
static const workflowAssetCreate = JobName._(r'WorkflowAssetCreate');
|
||||||
|
|
||||||
/// List of all possible values in this [enum][JobName].
|
/// List of all possible values in this [enum][JobName].
|
||||||
static const values = <JobName>[
|
static const values = <JobName>[
|
||||||
@@ -135,7 +135,7 @@ class JobName {
|
|||||||
versionCheck,
|
versionCheck,
|
||||||
ocrQueueAll,
|
ocrQueueAll,
|
||||||
ocr,
|
ocr,
|
||||||
workflowRun,
|
workflowAssetCreate,
|
||||||
];
|
];
|
||||||
|
|
||||||
static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value);
|
static JobName? fromJson(dynamic value) => JobNameTypeTransformer().decode(value);
|
||||||
@@ -228,7 +228,7 @@ class JobNameTypeTransformer {
|
|||||||
case r'VersionCheck': return JobName.versionCheck;
|
case r'VersionCheck': return JobName.versionCheck;
|
||||||
case r'OcrQueueAll': return JobName.ocrQueueAll;
|
case r'OcrQueueAll': return JobName.ocrQueueAll;
|
||||||
case r'Ocr': return JobName.ocr;
|
case r'Ocr': return JobName.ocr;
|
||||||
case r'WorkflowRun': return JobName.workflowRun;
|
case r'WorkflowAssetCreate': return JobName.workflowAssetCreate;
|
||||||
default:
|
default:
|
||||||
if (!allowNull) {
|
if (!allowNull) {
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
throw ArgumentError('Unknown enum value to decode: $data');
|
||||||
|
|||||||
@@ -1,158 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class PluginActionResponseDto {
|
|
||||||
/// Returns a new [PluginActionResponseDto] instance.
|
|
||||||
PluginActionResponseDto({
|
|
||||||
required this.description,
|
|
||||||
required this.id,
|
|
||||||
required this.methodName,
|
|
||||||
required this.pluginId,
|
|
||||||
required this.schema,
|
|
||||||
this.supportedContexts = const [],
|
|
||||||
required this.title,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Action description
|
|
||||||
String description;
|
|
||||||
|
|
||||||
/// Action ID
|
|
||||||
String id;
|
|
||||||
|
|
||||||
/// Method name
|
|
||||||
String methodName;
|
|
||||||
|
|
||||||
/// Plugin ID
|
|
||||||
String pluginId;
|
|
||||||
|
|
||||||
/// Action schema
|
|
||||||
PluginJsonSchema? schema;
|
|
||||||
|
|
||||||
/// Supported contexts
|
|
||||||
List<PluginContextType> supportedContexts;
|
|
||||||
|
|
||||||
/// Action title
|
|
||||||
String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginActionResponseDto &&
|
|
||||||
other.description == description &&
|
|
||||||
other.id == id &&
|
|
||||||
other.methodName == methodName &&
|
|
||||||
other.pluginId == pluginId &&
|
|
||||||
other.schema == schema &&
|
|
||||||
_deepEquality.equals(other.supportedContexts, supportedContexts) &&
|
|
||||||
other.title == title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(description.hashCode) +
|
|
||||||
(id.hashCode) +
|
|
||||||
(methodName.hashCode) +
|
|
||||||
(pluginId.hashCode) +
|
|
||||||
(schema == null ? 0 : schema!.hashCode) +
|
|
||||||
(supportedContexts.hashCode) +
|
|
||||||
(title.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'PluginActionResponseDto[description=$description, id=$id, methodName=$methodName, pluginId=$pluginId, schema=$schema, supportedContexts=$supportedContexts, title=$title]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
json[r'description'] = this.description;
|
|
||||||
json[r'id'] = this.id;
|
|
||||||
json[r'methodName'] = this.methodName;
|
|
||||||
json[r'pluginId'] = this.pluginId;
|
|
||||||
if (this.schema != null) {
|
|
||||||
json[r'schema'] = this.schema;
|
|
||||||
} else {
|
|
||||||
// json[r'schema'] = null;
|
|
||||||
}
|
|
||||||
json[r'supportedContexts'] = this.supportedContexts;
|
|
||||||
json[r'title'] = this.title;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [PluginActionResponseDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static PluginActionResponseDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "PluginActionResponseDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return PluginActionResponseDto(
|
|
||||||
description: mapValueOfType<String>(json, r'description')!,
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
|
||||||
methodName: mapValueOfType<String>(json, r'methodName')!,
|
|
||||||
pluginId: mapValueOfType<String>(json, r'pluginId')!,
|
|
||||||
schema: PluginJsonSchema.fromJson(json[r'schema']),
|
|
||||||
supportedContexts: PluginContextType.listFromJson(json[r'supportedContexts']),
|
|
||||||
title: mapValueOfType<String>(json, r'title')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<PluginActionResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginActionResponseDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginActionResponseDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, PluginActionResponseDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, PluginActionResponseDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = PluginActionResponseDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of PluginActionResponseDto-objects as value to a dart map
|
|
||||||
static Map<String, List<PluginActionResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<PluginActionResponseDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = PluginActionResponseDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'description',
|
|
||||||
'id',
|
|
||||||
'methodName',
|
|
||||||
'pluginId',
|
|
||||||
'schema',
|
|
||||||
'supportedContexts',
|
|
||||||
'title',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
-88
@@ -1,88 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
/// Plugin context
|
|
||||||
class PluginContextType {
|
|
||||||
/// Instantiate a new enum with the provided [value].
|
|
||||||
const PluginContextType._(this.value);
|
|
||||||
|
|
||||||
/// The underlying value of this enum member.
|
|
||||||
final String value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => value;
|
|
||||||
|
|
||||||
String toJson() => value;
|
|
||||||
|
|
||||||
static const asset = PluginContextType._(r'asset');
|
|
||||||
static const album = PluginContextType._(r'album');
|
|
||||||
static const person = PluginContextType._(r'person');
|
|
||||||
|
|
||||||
/// List of all possible values in this [enum][PluginContextType].
|
|
||||||
static const values = <PluginContextType>[
|
|
||||||
asset,
|
|
||||||
album,
|
|
||||||
person,
|
|
||||||
];
|
|
||||||
|
|
||||||
static PluginContextType? fromJson(dynamic value) => PluginContextTypeTypeTransformer().decode(value);
|
|
||||||
|
|
||||||
static List<PluginContextType> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginContextType>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginContextType.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transformation class that can [encode] an instance of [PluginContextType] to String,
|
|
||||||
/// and [decode] dynamic data back to [PluginContextType].
|
|
||||||
class PluginContextTypeTypeTransformer {
|
|
||||||
factory PluginContextTypeTypeTransformer() => _instance ??= const PluginContextTypeTypeTransformer._();
|
|
||||||
|
|
||||||
const PluginContextTypeTypeTransformer._();
|
|
||||||
|
|
||||||
String encode(PluginContextType data) => data.value;
|
|
||||||
|
|
||||||
/// Decodes a [dynamic value][data] to a PluginContextType.
|
|
||||||
///
|
|
||||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
|
||||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
|
||||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
|
||||||
///
|
|
||||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
|
||||||
/// and users are still using an old app with the old code.
|
|
||||||
PluginContextType? decode(dynamic data, {bool allowNull = true}) {
|
|
||||||
if (data != null) {
|
|
||||||
switch (data) {
|
|
||||||
case r'asset': return PluginContextType.asset;
|
|
||||||
case r'album': return PluginContextType.album;
|
|
||||||
case r'person': return PluginContextType.person;
|
|
||||||
default:
|
|
||||||
if (!allowNull) {
|
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Singleton [PluginContextTypeTypeTransformer] instance.
|
|
||||||
static PluginContextTypeTypeTransformer? _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class PluginFilterResponseDto {
|
|
||||||
/// Returns a new [PluginFilterResponseDto] instance.
|
|
||||||
PluginFilterResponseDto({
|
|
||||||
required this.description,
|
|
||||||
required this.id,
|
|
||||||
required this.methodName,
|
|
||||||
required this.pluginId,
|
|
||||||
required this.schema,
|
|
||||||
this.supportedContexts = const [],
|
|
||||||
required this.title,
|
|
||||||
});
|
|
||||||
|
|
||||||
/// Filter description
|
|
||||||
String description;
|
|
||||||
|
|
||||||
/// Filter ID
|
|
||||||
String id;
|
|
||||||
|
|
||||||
/// Method name
|
|
||||||
String methodName;
|
|
||||||
|
|
||||||
/// Plugin ID
|
|
||||||
String pluginId;
|
|
||||||
|
|
||||||
/// Filter schema
|
|
||||||
PluginJsonSchema? schema;
|
|
||||||
|
|
||||||
/// Supported contexts
|
|
||||||
List<PluginContextType> supportedContexts;
|
|
||||||
|
|
||||||
/// Filter title
|
|
||||||
String title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginFilterResponseDto &&
|
|
||||||
other.description == description &&
|
|
||||||
other.id == id &&
|
|
||||||
other.methodName == methodName &&
|
|
||||||
other.pluginId == pluginId &&
|
|
||||||
other.schema == schema &&
|
|
||||||
_deepEquality.equals(other.supportedContexts, supportedContexts) &&
|
|
||||||
other.title == title;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(description.hashCode) +
|
|
||||||
(id.hashCode) +
|
|
||||||
(methodName.hashCode) +
|
|
||||||
(pluginId.hashCode) +
|
|
||||||
(schema == null ? 0 : schema!.hashCode) +
|
|
||||||
(supportedContexts.hashCode) +
|
|
||||||
(title.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'PluginFilterResponseDto[description=$description, id=$id, methodName=$methodName, pluginId=$pluginId, schema=$schema, supportedContexts=$supportedContexts, title=$title]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
json[r'description'] = this.description;
|
|
||||||
json[r'id'] = this.id;
|
|
||||||
json[r'methodName'] = this.methodName;
|
|
||||||
json[r'pluginId'] = this.pluginId;
|
|
||||||
if (this.schema != null) {
|
|
||||||
json[r'schema'] = this.schema;
|
|
||||||
} else {
|
|
||||||
// json[r'schema'] = null;
|
|
||||||
}
|
|
||||||
json[r'supportedContexts'] = this.supportedContexts;
|
|
||||||
json[r'title'] = this.title;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [PluginFilterResponseDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static PluginFilterResponseDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "PluginFilterResponseDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return PluginFilterResponseDto(
|
|
||||||
description: mapValueOfType<String>(json, r'description')!,
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
|
||||||
methodName: mapValueOfType<String>(json, r'methodName')!,
|
|
||||||
pluginId: mapValueOfType<String>(json, r'pluginId')!,
|
|
||||||
schema: PluginJsonSchema.fromJson(json[r'schema']),
|
|
||||||
supportedContexts: PluginContextType.listFromJson(json[r'supportedContexts']),
|
|
||||||
title: mapValueOfType<String>(json, r'title')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<PluginFilterResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginFilterResponseDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginFilterResponseDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, PluginFilterResponseDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, PluginFilterResponseDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = PluginFilterResponseDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of PluginFilterResponseDto-objects as value to a dart map
|
|
||||||
static Map<String, List<PluginFilterResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<PluginFilterResponseDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = PluginFilterResponseDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'description',
|
|
||||||
'id',
|
|
||||||
'methodName',
|
|
||||||
'pluginId',
|
|
||||||
'schema',
|
|
||||||
'supportedContexts',
|
|
||||||
'title',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
-158
@@ -1,158 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class PluginJsonSchema {
|
|
||||||
/// Returns a new [PluginJsonSchema] instance.
|
|
||||||
PluginJsonSchema({
|
|
||||||
this.additionalProperties,
|
|
||||||
this.description,
|
|
||||||
this.properties = const {},
|
|
||||||
this.required_ = const [],
|
|
||||||
this.type,
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
bool? additionalProperties;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
String? description;
|
|
||||||
|
|
||||||
Map<String, PluginJsonSchemaProperty> properties;
|
|
||||||
|
|
||||||
List<String> required_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaType? type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginJsonSchema &&
|
|
||||||
other.additionalProperties == additionalProperties &&
|
|
||||||
other.description == description &&
|
|
||||||
_deepEquality.equals(other.properties, properties) &&
|
|
||||||
_deepEquality.equals(other.required_, required_) &&
|
|
||||||
other.type == type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(additionalProperties == null ? 0 : additionalProperties!.hashCode) +
|
|
||||||
(description == null ? 0 : description!.hashCode) +
|
|
||||||
(properties.hashCode) +
|
|
||||||
(required_.hashCode) +
|
|
||||||
(type == null ? 0 : type!.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'PluginJsonSchema[additionalProperties=$additionalProperties, description=$description, properties=$properties, required_=$required_, type=$type]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
if (this.additionalProperties != null) {
|
|
||||||
json[r'additionalProperties'] = this.additionalProperties;
|
|
||||||
} else {
|
|
||||||
// json[r'additionalProperties'] = null;
|
|
||||||
}
|
|
||||||
if (this.description != null) {
|
|
||||||
json[r'description'] = this.description;
|
|
||||||
} else {
|
|
||||||
// json[r'description'] = null;
|
|
||||||
}
|
|
||||||
json[r'properties'] = this.properties;
|
|
||||||
json[r'required'] = this.required_;
|
|
||||||
if (this.type != null) {
|
|
||||||
json[r'type'] = this.type;
|
|
||||||
} else {
|
|
||||||
// json[r'type'] = null;
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [PluginJsonSchema] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static PluginJsonSchema? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "PluginJsonSchema");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return PluginJsonSchema(
|
|
||||||
additionalProperties: mapValueOfType<bool>(json, r'additionalProperties'),
|
|
||||||
description: mapValueOfType<String>(json, r'description'),
|
|
||||||
properties: PluginJsonSchemaProperty.mapFromJson(json[r'properties']),
|
|
||||||
required_: json[r'required'] is Iterable
|
|
||||||
? (json[r'required'] as Iterable).cast<String>().toList(growable: false)
|
|
||||||
: const [],
|
|
||||||
type: PluginJsonSchemaType.fromJson(json[r'type']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<PluginJsonSchema> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginJsonSchema>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginJsonSchema.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, PluginJsonSchema> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, PluginJsonSchema>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = PluginJsonSchema.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of PluginJsonSchema-objects as value to a dart map
|
|
||||||
static Map<String, List<PluginJsonSchema>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<PluginJsonSchema>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = PluginJsonSchema.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class PluginJsonSchemaProperty {
|
|
||||||
/// Returns a new [PluginJsonSchemaProperty] instance.
|
|
||||||
PluginJsonSchemaProperty({
|
|
||||||
this.additionalProperties,
|
|
||||||
this.default_,
|
|
||||||
this.description,
|
|
||||||
this.enum_ = const [],
|
|
||||||
this.items,
|
|
||||||
this.properties = const {},
|
|
||||||
this.required_ = const [],
|
|
||||||
this.type,
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaPropertyAdditionalProperties? additionalProperties;
|
|
||||||
|
|
||||||
Object? default_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
String? description;
|
|
||||||
|
|
||||||
List<String> enum_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaProperty? items;
|
|
||||||
|
|
||||||
Map<String, PluginJsonSchemaProperty> properties;
|
|
||||||
|
|
||||||
List<String> required_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaType? type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginJsonSchemaProperty &&
|
|
||||||
other.additionalProperties == additionalProperties &&
|
|
||||||
other.default_ == default_ &&
|
|
||||||
other.description == description &&
|
|
||||||
_deepEquality.equals(other.enum_, enum_) &&
|
|
||||||
other.items == items &&
|
|
||||||
_deepEquality.equals(other.properties, properties) &&
|
|
||||||
_deepEquality.equals(other.required_, required_) &&
|
|
||||||
other.type == type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(additionalProperties == null ? 0 : additionalProperties!.hashCode) +
|
|
||||||
(default_ == null ? 0 : default_!.hashCode) +
|
|
||||||
(description == null ? 0 : description!.hashCode) +
|
|
||||||
(enum_.hashCode) +
|
|
||||||
(items == null ? 0 : items!.hashCode) +
|
|
||||||
(properties.hashCode) +
|
|
||||||
(required_.hashCode) +
|
|
||||||
(type == null ? 0 : type!.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'PluginJsonSchemaProperty[additionalProperties=$additionalProperties, default_=$default_, description=$description, enum_=$enum_, items=$items, properties=$properties, required_=$required_, type=$type]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
if (this.additionalProperties != null) {
|
|
||||||
json[r'additionalProperties'] = this.additionalProperties;
|
|
||||||
} else {
|
|
||||||
// json[r'additionalProperties'] = null;
|
|
||||||
}
|
|
||||||
if (this.default_ != null) {
|
|
||||||
json[r'default'] = this.default_;
|
|
||||||
} else {
|
|
||||||
// json[r'default'] = null;
|
|
||||||
}
|
|
||||||
if (this.description != null) {
|
|
||||||
json[r'description'] = this.description;
|
|
||||||
} else {
|
|
||||||
// json[r'description'] = null;
|
|
||||||
}
|
|
||||||
json[r'enum'] = this.enum_;
|
|
||||||
if (this.items != null) {
|
|
||||||
json[r'items'] = this.items;
|
|
||||||
} else {
|
|
||||||
// json[r'items'] = null;
|
|
||||||
}
|
|
||||||
json[r'properties'] = this.properties;
|
|
||||||
json[r'required'] = this.required_;
|
|
||||||
if (this.type != null) {
|
|
||||||
json[r'type'] = this.type;
|
|
||||||
} else {
|
|
||||||
// json[r'type'] = null;
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [PluginJsonSchemaProperty] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static PluginJsonSchemaProperty? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "PluginJsonSchemaProperty");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return PluginJsonSchemaProperty(
|
|
||||||
additionalProperties: PluginJsonSchemaPropertyAdditionalProperties.fromJson(json[r'additionalProperties']),
|
|
||||||
default_: mapValueOfType<Object>(json, r'default'),
|
|
||||||
description: mapValueOfType<String>(json, r'description'),
|
|
||||||
enum_: json[r'enum'] is Iterable
|
|
||||||
? (json[r'enum'] as Iterable).cast<String>().toList(growable: false)
|
|
||||||
: const [],
|
|
||||||
items: PluginJsonSchemaProperty.fromJson(json[r'items']),
|
|
||||||
properties: PluginJsonSchemaProperty.mapFromJson(json[r'properties']),
|
|
||||||
required_: json[r'required'] is Iterable
|
|
||||||
? (json[r'required'] as Iterable).cast<String>().toList(growable: false)
|
|
||||||
: const [],
|
|
||||||
type: PluginJsonSchemaType.fromJson(json[r'type']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<PluginJsonSchemaProperty> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginJsonSchemaProperty>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginJsonSchemaProperty.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, PluginJsonSchemaProperty> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, PluginJsonSchemaProperty>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = PluginJsonSchemaProperty.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of PluginJsonSchemaProperty-objects as value to a dart map
|
|
||||||
static Map<String, List<PluginJsonSchemaProperty>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<PluginJsonSchemaProperty>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = PluginJsonSchemaProperty.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
-195
@@ -1,195 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class PluginJsonSchemaPropertyAdditionalProperties {
|
|
||||||
/// Returns a new [PluginJsonSchemaPropertyAdditionalProperties] instance.
|
|
||||||
PluginJsonSchemaPropertyAdditionalProperties({
|
|
||||||
this.additionalProperties,
|
|
||||||
this.default_,
|
|
||||||
this.description,
|
|
||||||
this.enum_ = const [],
|
|
||||||
this.items,
|
|
||||||
this.properties = const {},
|
|
||||||
this.required_ = const [],
|
|
||||||
this.type,
|
|
||||||
});
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaPropertyAdditionalProperties? additionalProperties;
|
|
||||||
|
|
||||||
Object? default_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
String? description;
|
|
||||||
|
|
||||||
List<String> enum_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaProperty? items;
|
|
||||||
|
|
||||||
Map<String, PluginJsonSchemaProperty> properties;
|
|
||||||
|
|
||||||
List<String> required_;
|
|
||||||
|
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
PluginJsonSchemaType? type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginJsonSchemaPropertyAdditionalProperties &&
|
|
||||||
other.additionalProperties == additionalProperties &&
|
|
||||||
other.default_ == default_ &&
|
|
||||||
other.description == description &&
|
|
||||||
_deepEquality.equals(other.enum_, enum_) &&
|
|
||||||
other.items == items &&
|
|
||||||
_deepEquality.equals(other.properties, properties) &&
|
|
||||||
_deepEquality.equals(other.required_, required_) &&
|
|
||||||
other.type == type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(additionalProperties == null ? 0 : additionalProperties!.hashCode) +
|
|
||||||
(default_ == null ? 0 : default_!.hashCode) +
|
|
||||||
(description == null ? 0 : description!.hashCode) +
|
|
||||||
(enum_.hashCode) +
|
|
||||||
(items == null ? 0 : items!.hashCode) +
|
|
||||||
(properties.hashCode) +
|
|
||||||
(required_.hashCode) +
|
|
||||||
(type == null ? 0 : type!.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'PluginJsonSchemaPropertyAdditionalProperties[additionalProperties=$additionalProperties, default_=$default_, description=$description, enum_=$enum_, items=$items, properties=$properties, required_=$required_, type=$type]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
if (this.additionalProperties != null) {
|
|
||||||
json[r'additionalProperties'] = this.additionalProperties;
|
|
||||||
} else {
|
|
||||||
// json[r'additionalProperties'] = null;
|
|
||||||
}
|
|
||||||
if (this.default_ != null) {
|
|
||||||
json[r'default'] = this.default_;
|
|
||||||
} else {
|
|
||||||
// json[r'default'] = null;
|
|
||||||
}
|
|
||||||
if (this.description != null) {
|
|
||||||
json[r'description'] = this.description;
|
|
||||||
} else {
|
|
||||||
// json[r'description'] = null;
|
|
||||||
}
|
|
||||||
json[r'enum'] = this.enum_;
|
|
||||||
if (this.items != null) {
|
|
||||||
json[r'items'] = this.items;
|
|
||||||
} else {
|
|
||||||
// json[r'items'] = null;
|
|
||||||
}
|
|
||||||
json[r'properties'] = this.properties;
|
|
||||||
json[r'required'] = this.required_;
|
|
||||||
if (this.type != null) {
|
|
||||||
json[r'type'] = this.type;
|
|
||||||
} else {
|
|
||||||
// json[r'type'] = null;
|
|
||||||
}
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [PluginJsonSchemaPropertyAdditionalProperties] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static PluginJsonSchemaPropertyAdditionalProperties? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "PluginJsonSchemaPropertyAdditionalProperties");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return PluginJsonSchemaPropertyAdditionalProperties(
|
|
||||||
additionalProperties: PluginJsonSchemaPropertyAdditionalProperties.fromJson(json[r'additionalProperties']),
|
|
||||||
default_: mapValueOfType<Object>(json, r'default'),
|
|
||||||
description: mapValueOfType<String>(json, r'description'),
|
|
||||||
enum_: json[r'enum'] is Iterable
|
|
||||||
? (json[r'enum'] as Iterable).cast<String>().toList(growable: false)
|
|
||||||
: const [],
|
|
||||||
items: PluginJsonSchemaProperty.fromJson(json[r'items']),
|
|
||||||
properties: PluginJsonSchemaProperty.mapFromJson(json[r'properties']),
|
|
||||||
required_: json[r'required'] is Iterable
|
|
||||||
? (json[r'required'] as Iterable).cast<String>().toList(growable: false)
|
|
||||||
: const [],
|
|
||||||
type: PluginJsonSchemaType.fromJson(json[r'type']),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<PluginJsonSchemaPropertyAdditionalProperties> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginJsonSchemaPropertyAdditionalProperties>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginJsonSchemaPropertyAdditionalProperties.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, PluginJsonSchemaPropertyAdditionalProperties> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, PluginJsonSchemaPropertyAdditionalProperties>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = PluginJsonSchemaPropertyAdditionalProperties.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of PluginJsonSchemaPropertyAdditionalProperties-objects as value to a dart map
|
|
||||||
static Map<String, List<PluginJsonSchemaPropertyAdditionalProperties>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<PluginJsonSchemaPropertyAdditionalProperties>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = PluginJsonSchemaPropertyAdditionalProperties.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
-100
@@ -1,100 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
|
|
||||||
class PluginJsonSchemaType {
|
|
||||||
/// Instantiate a new enum with the provided [value].
|
|
||||||
const PluginJsonSchemaType._(this.value);
|
|
||||||
|
|
||||||
/// The underlying value of this enum member.
|
|
||||||
final String value;
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => value;
|
|
||||||
|
|
||||||
String toJson() => value;
|
|
||||||
|
|
||||||
static const string = PluginJsonSchemaType._(r'string');
|
|
||||||
static const number = PluginJsonSchemaType._(r'number');
|
|
||||||
static const integer = PluginJsonSchemaType._(r'integer');
|
|
||||||
static const boolean = PluginJsonSchemaType._(r'boolean');
|
|
||||||
static const object = PluginJsonSchemaType._(r'object');
|
|
||||||
static const array = PluginJsonSchemaType._(r'array');
|
|
||||||
static const null_ = PluginJsonSchemaType._(r'null');
|
|
||||||
|
|
||||||
/// List of all possible values in this [enum][PluginJsonSchemaType].
|
|
||||||
static const values = <PluginJsonSchemaType>[
|
|
||||||
string,
|
|
||||||
number,
|
|
||||||
integer,
|
|
||||||
boolean,
|
|
||||||
object,
|
|
||||||
array,
|
|
||||||
null_,
|
|
||||||
];
|
|
||||||
|
|
||||||
static PluginJsonSchemaType? fromJson(dynamic value) => PluginJsonSchemaTypeTypeTransformer().decode(value);
|
|
||||||
|
|
||||||
static List<PluginJsonSchemaType> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginJsonSchemaType>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginJsonSchemaType.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Transformation class that can [encode] an instance of [PluginJsonSchemaType] to String,
|
|
||||||
/// and [decode] dynamic data back to [PluginJsonSchemaType].
|
|
||||||
class PluginJsonSchemaTypeTypeTransformer {
|
|
||||||
factory PluginJsonSchemaTypeTypeTransformer() => _instance ??= const PluginJsonSchemaTypeTypeTransformer._();
|
|
||||||
|
|
||||||
const PluginJsonSchemaTypeTypeTransformer._();
|
|
||||||
|
|
||||||
String encode(PluginJsonSchemaType data) => data.value;
|
|
||||||
|
|
||||||
/// Decodes a [dynamic value][data] to a PluginJsonSchemaType.
|
|
||||||
///
|
|
||||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
|
||||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
|
||||||
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
|
||||||
///
|
|
||||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
|
||||||
/// and users are still using an old app with the old code.
|
|
||||||
PluginJsonSchemaType? decode(dynamic data, {bool allowNull = true}) {
|
|
||||||
if (data != null) {
|
|
||||||
switch (data) {
|
|
||||||
case r'string': return PluginJsonSchemaType.string;
|
|
||||||
case r'number': return PluginJsonSchemaType.number;
|
|
||||||
case r'integer': return PluginJsonSchemaType.integer;
|
|
||||||
case r'boolean': return PluginJsonSchemaType.boolean;
|
|
||||||
case r'object': return PluginJsonSchemaType.object;
|
|
||||||
case r'array': return PluginJsonSchemaType.array;
|
|
||||||
case r'null': return PluginJsonSchemaType.null_;
|
|
||||||
default:
|
|
||||||
if (!allowNull) {
|
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Singleton [PluginJsonSchemaTypeTypeTransformer] instance.
|
|
||||||
static PluginJsonSchemaTypeTypeTransformer? _instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class PluginMethodResponseDto {
|
||||||
|
/// Returns a new [PluginMethodResponseDto] instance.
|
||||||
|
PluginMethodResponseDto({
|
||||||
|
required this.description,
|
||||||
|
required this.hostFunctions,
|
||||||
|
required this.key,
|
||||||
|
required this.name,
|
||||||
|
this.schema,
|
||||||
|
required this.title,
|
||||||
|
this.types = const [],
|
||||||
|
this.uiHints = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Description
|
||||||
|
String description;
|
||||||
|
|
||||||
|
bool hostFunctions;
|
||||||
|
|
||||||
|
/// Key
|
||||||
|
String key;
|
||||||
|
|
||||||
|
/// Name
|
||||||
|
String name;
|
||||||
|
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
Object? schema;
|
||||||
|
|
||||||
|
/// Title
|
||||||
|
String title;
|
||||||
|
|
||||||
|
/// Workflow types
|
||||||
|
List<WorkflowType> types;
|
||||||
|
|
||||||
|
/// Ui hints
|
||||||
|
List<String> uiHints;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is PluginMethodResponseDto &&
|
||||||
|
other.description == description &&
|
||||||
|
other.hostFunctions == hostFunctions &&
|
||||||
|
other.key == key &&
|
||||||
|
other.name == name &&
|
||||||
|
other.schema == schema &&
|
||||||
|
other.title == title &&
|
||||||
|
_deepEquality.equals(other.types, types) &&
|
||||||
|
_deepEquality.equals(other.uiHints, uiHints);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(description.hashCode) +
|
||||||
|
(hostFunctions.hashCode) +
|
||||||
|
(key.hashCode) +
|
||||||
|
(name.hashCode) +
|
||||||
|
(schema == null ? 0 : schema!.hashCode) +
|
||||||
|
(title.hashCode) +
|
||||||
|
(types.hashCode) +
|
||||||
|
(uiHints.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'PluginMethodResponseDto[description=$description, hostFunctions=$hostFunctions, key=$key, name=$name, schema=$schema, title=$title, types=$types, uiHints=$uiHints]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
json[r'description'] = this.description;
|
||||||
|
json[r'hostFunctions'] = this.hostFunctions;
|
||||||
|
json[r'key'] = this.key;
|
||||||
|
json[r'name'] = this.name;
|
||||||
|
if (this.schema != null) {
|
||||||
|
json[r'schema'] = this.schema;
|
||||||
|
} else {
|
||||||
|
// json[r'schema'] = null;
|
||||||
|
}
|
||||||
|
json[r'title'] = this.title;
|
||||||
|
json[r'types'] = this.types;
|
||||||
|
json[r'uiHints'] = this.uiHints;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [PluginMethodResponseDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static PluginMethodResponseDto? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "PluginMethodResponseDto");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return PluginMethodResponseDto(
|
||||||
|
description: mapValueOfType<String>(json, r'description')!,
|
||||||
|
hostFunctions: mapValueOfType<bool>(json, r'hostFunctions')!,
|
||||||
|
key: mapValueOfType<String>(json, r'key')!,
|
||||||
|
name: mapValueOfType<String>(json, r'name')!,
|
||||||
|
schema: mapValueOfType<Object>(json, r'schema'),
|
||||||
|
title: mapValueOfType<String>(json, r'title')!,
|
||||||
|
types: WorkflowType.listFromJson(json[r'types']),
|
||||||
|
uiHints: json[r'uiHints'] is Iterable
|
||||||
|
? (json[r'uiHints'] as Iterable).cast<String>().toList(growable: false)
|
||||||
|
: const [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<PluginMethodResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <PluginMethodResponseDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = PluginMethodResponseDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, PluginMethodResponseDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, PluginMethodResponseDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = PluginMethodResponseDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of PluginMethodResponseDto-objects as value to a dart map
|
||||||
|
static Map<String, List<PluginMethodResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<PluginMethodResponseDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = PluginMethodResponseDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'description',
|
||||||
|
'hostFunctions',
|
||||||
|
'key',
|
||||||
|
'name',
|
||||||
|
'title',
|
||||||
|
'types',
|
||||||
|
'uiHints',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
+10
-19
@@ -13,21 +13,17 @@ part of openapi.api;
|
|||||||
class PluginResponseDto {
|
class PluginResponseDto {
|
||||||
/// Returns a new [PluginResponseDto] instance.
|
/// Returns a new [PluginResponseDto] instance.
|
||||||
PluginResponseDto({
|
PluginResponseDto({
|
||||||
this.actions = const [],
|
|
||||||
required this.author,
|
required this.author,
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.description,
|
required this.description,
|
||||||
this.filters = const [],
|
|
||||||
required this.id,
|
required this.id,
|
||||||
|
this.methods = const [],
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.title,
|
required this.title,
|
||||||
required this.updatedAt,
|
required this.updatedAt,
|
||||||
required this.version,
|
required this.version,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Plugin actions
|
|
||||||
List<PluginActionResponseDto> actions;
|
|
||||||
|
|
||||||
/// Plugin author
|
/// Plugin author
|
||||||
String author;
|
String author;
|
||||||
|
|
||||||
@@ -37,12 +33,12 @@ class PluginResponseDto {
|
|||||||
/// Plugin description
|
/// Plugin description
|
||||||
String description;
|
String description;
|
||||||
|
|
||||||
/// Plugin filters
|
|
||||||
List<PluginFilterResponseDto> filters;
|
|
||||||
|
|
||||||
/// Plugin ID
|
/// Plugin ID
|
||||||
String id;
|
String id;
|
||||||
|
|
||||||
|
/// Plugin methods
|
||||||
|
List<PluginMethodResponseDto> methods;
|
||||||
|
|
||||||
/// Plugin name
|
/// Plugin name
|
||||||
String name;
|
String name;
|
||||||
|
|
||||||
@@ -57,12 +53,11 @@ class PluginResponseDto {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is PluginResponseDto &&
|
||||||
_deepEquality.equals(other.actions, actions) &&
|
|
||||||
other.author == author &&
|
other.author == author &&
|
||||||
other.createdAt == createdAt &&
|
other.createdAt == createdAt &&
|
||||||
other.description == description &&
|
other.description == description &&
|
||||||
_deepEquality.equals(other.filters, filters) &&
|
|
||||||
other.id == id &&
|
other.id == id &&
|
||||||
|
_deepEquality.equals(other.methods, methods) &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.title == title &&
|
other.title == title &&
|
||||||
other.updatedAt == updatedAt &&
|
other.updatedAt == updatedAt &&
|
||||||
@@ -71,28 +66,26 @@ class PluginResponseDto {
|
|||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(actions.hashCode) +
|
|
||||||
(author.hashCode) +
|
(author.hashCode) +
|
||||||
(createdAt.hashCode) +
|
(createdAt.hashCode) +
|
||||||
(description.hashCode) +
|
(description.hashCode) +
|
||||||
(filters.hashCode) +
|
|
||||||
(id.hashCode) +
|
(id.hashCode) +
|
||||||
|
(methods.hashCode) +
|
||||||
(name.hashCode) +
|
(name.hashCode) +
|
||||||
(title.hashCode) +
|
(title.hashCode) +
|
||||||
(updatedAt.hashCode) +
|
(updatedAt.hashCode) +
|
||||||
(version.hashCode);
|
(version.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'PluginResponseDto[actions=$actions, author=$author, createdAt=$createdAt, description=$description, filters=$filters, id=$id, name=$name, title=$title, updatedAt=$updatedAt, version=$version]';
|
String toString() => 'PluginResponseDto[author=$author, createdAt=$createdAt, description=$description, id=$id, methods=$methods, name=$name, title=$title, updatedAt=$updatedAt, version=$version]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json[r'actions'] = this.actions;
|
|
||||||
json[r'author'] = this.author;
|
json[r'author'] = this.author;
|
||||||
json[r'createdAt'] = this.createdAt;
|
json[r'createdAt'] = this.createdAt;
|
||||||
json[r'description'] = this.description;
|
json[r'description'] = this.description;
|
||||||
json[r'filters'] = this.filters;
|
|
||||||
json[r'id'] = this.id;
|
json[r'id'] = this.id;
|
||||||
|
json[r'methods'] = this.methods;
|
||||||
json[r'name'] = this.name;
|
json[r'name'] = this.name;
|
||||||
json[r'title'] = this.title;
|
json[r'title'] = this.title;
|
||||||
json[r'updatedAt'] = this.updatedAt;
|
json[r'updatedAt'] = this.updatedAt;
|
||||||
@@ -109,12 +102,11 @@ class PluginResponseDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return PluginResponseDto(
|
return PluginResponseDto(
|
||||||
actions: PluginActionResponseDto.listFromJson(json[r'actions']),
|
|
||||||
author: mapValueOfType<String>(json, r'author')!,
|
author: mapValueOfType<String>(json, r'author')!,
|
||||||
createdAt: mapValueOfType<String>(json, r'createdAt')!,
|
createdAt: mapValueOfType<String>(json, r'createdAt')!,
|
||||||
description: mapValueOfType<String>(json, r'description')!,
|
description: mapValueOfType<String>(json, r'description')!,
|
||||||
filters: PluginFilterResponseDto.listFromJson(json[r'filters']),
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
id: mapValueOfType<String>(json, r'id')!,
|
||||||
|
methods: PluginMethodResponseDto.listFromJson(json[r'methods']),
|
||||||
name: mapValueOfType<String>(json, r'name')!,
|
name: mapValueOfType<String>(json, r'name')!,
|
||||||
title: mapValueOfType<String>(json, r'title')!,
|
title: mapValueOfType<String>(json, r'title')!,
|
||||||
updatedAt: mapValueOfType<String>(json, r'updatedAt')!,
|
updatedAt: mapValueOfType<String>(json, r'updatedAt')!,
|
||||||
@@ -166,12 +158,11 @@ class PluginResponseDto {
|
|||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
'actions',
|
|
||||||
'author',
|
'author',
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'description',
|
'description',
|
||||||
'filters',
|
|
||||||
'id',
|
'id',
|
||||||
|
'methods',
|
||||||
'name',
|
'name',
|
||||||
'title',
|
'title',
|
||||||
'updatedAt',
|
'updatedAt',
|
||||||
|
|||||||
@@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class PluginTriggerResponseDto {
|
|
||||||
/// Returns a new [PluginTriggerResponseDto] instance.
|
|
||||||
PluginTriggerResponseDto({
|
|
||||||
required this.contextType,
|
|
||||||
required this.type,
|
|
||||||
});
|
|
||||||
|
|
||||||
PluginContextType contextType;
|
|
||||||
|
|
||||||
PluginTriggerType type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is PluginTriggerResponseDto &&
|
|
||||||
other.contextType == contextType &&
|
|
||||||
other.type == type;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(contextType.hashCode) +
|
|
||||||
(type.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'PluginTriggerResponseDto[contextType=$contextType, type=$type]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
json[r'contextType'] = this.contextType;
|
|
||||||
json[r'type'] = this.type;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [PluginTriggerResponseDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static PluginTriggerResponseDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "PluginTriggerResponseDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return PluginTriggerResponseDto(
|
|
||||||
contextType: PluginContextType.fromJson(json[r'contextType'])!,
|
|
||||||
type: PluginTriggerType.fromJson(json[r'type'])!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<PluginTriggerResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <PluginTriggerResponseDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = PluginTriggerResponseDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, PluginTriggerResponseDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, PluginTriggerResponseDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = PluginTriggerResponseDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of PluginTriggerResponseDto-objects as value to a dart map
|
|
||||||
static Map<String, List<PluginTriggerResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<PluginTriggerResponseDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = PluginTriggerResponseDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'contextType',
|
|
||||||
'type',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
-107
@@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class WorkflowActionItemDto {
|
|
||||||
/// Returns a new [WorkflowActionItemDto] instance.
|
|
||||||
WorkflowActionItemDto({
|
|
||||||
this.actionConfig = const {},
|
|
||||||
required this.pluginActionId,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, Object> actionConfig;
|
|
||||||
|
|
||||||
/// Plugin action ID
|
|
||||||
String pluginActionId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowActionItemDto &&
|
|
||||||
_deepEquality.equals(other.actionConfig, actionConfig) &&
|
|
||||||
other.pluginActionId == pluginActionId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(actionConfig.hashCode) +
|
|
||||||
(pluginActionId.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'WorkflowActionItemDto[actionConfig=$actionConfig, pluginActionId=$pluginActionId]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
json[r'actionConfig'] = this.actionConfig;
|
|
||||||
json[r'pluginActionId'] = this.pluginActionId;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [WorkflowActionItemDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static WorkflowActionItemDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "WorkflowActionItemDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return WorkflowActionItemDto(
|
|
||||||
actionConfig: mapCastOfType<String, Object>(json, r'actionConfig') ?? const {},
|
|
||||||
pluginActionId: mapValueOfType<String>(json, r'pluginActionId')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<WorkflowActionItemDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <WorkflowActionItemDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = WorkflowActionItemDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, WorkflowActionItemDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, WorkflowActionItemDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = WorkflowActionItemDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of WorkflowActionItemDto-objects as value to a dart map
|
|
||||||
static Map<String, List<WorkflowActionItemDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<WorkflowActionItemDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = WorkflowActionItemDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'pluginActionId',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class WorkflowActionResponseDto {
|
|
||||||
/// Returns a new [WorkflowActionResponseDto] instance.
|
|
||||||
WorkflowActionResponseDto({
|
|
||||||
required this.actionConfig,
|
|
||||||
required this.id,
|
|
||||||
required this.order,
|
|
||||||
required this.pluginActionId,
|
|
||||||
required this.workflowId,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, Object>? actionConfig;
|
|
||||||
|
|
||||||
/// Action ID
|
|
||||||
String id;
|
|
||||||
|
|
||||||
/// Action order
|
|
||||||
///
|
|
||||||
/// Minimum value: -9007199254740991
|
|
||||||
/// Maximum value: 9007199254740991
|
|
||||||
int order;
|
|
||||||
|
|
||||||
/// Plugin action ID
|
|
||||||
String pluginActionId;
|
|
||||||
|
|
||||||
/// Workflow ID
|
|
||||||
String workflowId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowActionResponseDto &&
|
|
||||||
_deepEquality.equals(other.actionConfig, actionConfig) &&
|
|
||||||
other.id == id &&
|
|
||||||
other.order == order &&
|
|
||||||
other.pluginActionId == pluginActionId &&
|
|
||||||
other.workflowId == workflowId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(actionConfig == null ? 0 : actionConfig!.hashCode) +
|
|
||||||
(id.hashCode) +
|
|
||||||
(order.hashCode) +
|
|
||||||
(pluginActionId.hashCode) +
|
|
||||||
(workflowId.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'WorkflowActionResponseDto[actionConfig=$actionConfig, id=$id, order=$order, pluginActionId=$pluginActionId, workflowId=$workflowId]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
if (this.actionConfig != null) {
|
|
||||||
json[r'actionConfig'] = this.actionConfig;
|
|
||||||
} else {
|
|
||||||
// json[r'actionConfig'] = null;
|
|
||||||
}
|
|
||||||
json[r'id'] = this.id;
|
|
||||||
json[r'order'] = this.order;
|
|
||||||
json[r'pluginActionId'] = this.pluginActionId;
|
|
||||||
json[r'workflowId'] = this.workflowId;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [WorkflowActionResponseDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static WorkflowActionResponseDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "WorkflowActionResponseDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return WorkflowActionResponseDto(
|
|
||||||
actionConfig: mapCastOfType<String, Object>(json, r'actionConfig'),
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
|
||||||
order: mapValueOfType<int>(json, r'order')!,
|
|
||||||
pluginActionId: mapValueOfType<String>(json, r'pluginActionId')!,
|
|
||||||
workflowId: mapValueOfType<String>(json, r'workflowId')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<WorkflowActionResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <WorkflowActionResponseDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = WorkflowActionResponseDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, WorkflowActionResponseDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, WorkflowActionResponseDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = WorkflowActionResponseDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of WorkflowActionResponseDto-objects as value to a dart map
|
|
||||||
static Map<String, List<WorkflowActionResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<WorkflowActionResponseDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = WorkflowActionResponseDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'actionConfig',
|
|
||||||
'id',
|
|
||||||
'order',
|
|
||||||
'pluginActionId',
|
|
||||||
'workflowId',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
+23
-37
@@ -13,24 +13,14 @@ part of openapi.api;
|
|||||||
class WorkflowCreateDto {
|
class WorkflowCreateDto {
|
||||||
/// Returns a new [WorkflowCreateDto] instance.
|
/// Returns a new [WorkflowCreateDto] instance.
|
||||||
WorkflowCreateDto({
|
WorkflowCreateDto({
|
||||||
this.actions = const [],
|
|
||||||
this.description,
|
this.description,
|
||||||
this.enabled,
|
this.enabled,
|
||||||
this.filters = const [],
|
this.name,
|
||||||
required this.name,
|
this.steps = const [],
|
||||||
required this.triggerType,
|
required this.trigger,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Workflow actions
|
|
||||||
List<WorkflowActionItemDto> actions;
|
|
||||||
|
|
||||||
/// Workflow description
|
/// Workflow description
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
String? description;
|
String? description;
|
||||||
|
|
||||||
/// Workflow enabled
|
/// Workflow enabled
|
||||||
@@ -42,39 +32,35 @@ class WorkflowCreateDto {
|
|||||||
///
|
///
|
||||||
bool? enabled;
|
bool? enabled;
|
||||||
|
|
||||||
/// Workflow filters
|
|
||||||
List<WorkflowFilterItemDto> filters;
|
|
||||||
|
|
||||||
/// Workflow name
|
/// Workflow name
|
||||||
String name;
|
String? name;
|
||||||
|
|
||||||
PluginTriggerType triggerType;
|
List<WorkflowStepDto> steps;
|
||||||
|
|
||||||
|
WorkflowTrigger trigger;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowCreateDto &&
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowCreateDto &&
|
||||||
_deepEquality.equals(other.actions, actions) &&
|
|
||||||
other.description == description &&
|
other.description == description &&
|
||||||
other.enabled == enabled &&
|
other.enabled == enabled &&
|
||||||
_deepEquality.equals(other.filters, filters) &&
|
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.triggerType == triggerType;
|
_deepEquality.equals(other.steps, steps) &&
|
||||||
|
other.trigger == trigger;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(actions.hashCode) +
|
|
||||||
(description == null ? 0 : description!.hashCode) +
|
(description == null ? 0 : description!.hashCode) +
|
||||||
(enabled == null ? 0 : enabled!.hashCode) +
|
(enabled == null ? 0 : enabled!.hashCode) +
|
||||||
(filters.hashCode) +
|
(name == null ? 0 : name!.hashCode) +
|
||||||
(name.hashCode) +
|
(steps.hashCode) +
|
||||||
(triggerType.hashCode);
|
(trigger.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'WorkflowCreateDto[actions=$actions, description=$description, enabled=$enabled, filters=$filters, name=$name, triggerType=$triggerType]';
|
String toString() => 'WorkflowCreateDto[description=$description, enabled=$enabled, name=$name, steps=$steps, trigger=$trigger]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json[r'actions'] = this.actions;
|
|
||||||
if (this.description != null) {
|
if (this.description != null) {
|
||||||
json[r'description'] = this.description;
|
json[r'description'] = this.description;
|
||||||
} else {
|
} else {
|
||||||
@@ -85,9 +71,13 @@ class WorkflowCreateDto {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'enabled'] = null;
|
// json[r'enabled'] = null;
|
||||||
}
|
}
|
||||||
json[r'filters'] = this.filters;
|
if (this.name != null) {
|
||||||
json[r'name'] = this.name;
|
json[r'name'] = this.name;
|
||||||
json[r'triggerType'] = this.triggerType;
|
} else {
|
||||||
|
// json[r'name'] = null;
|
||||||
|
}
|
||||||
|
json[r'steps'] = this.steps;
|
||||||
|
json[r'trigger'] = this.trigger;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -100,12 +90,11 @@ class WorkflowCreateDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return WorkflowCreateDto(
|
return WorkflowCreateDto(
|
||||||
actions: WorkflowActionItemDto.listFromJson(json[r'actions']),
|
|
||||||
description: mapValueOfType<String>(json, r'description'),
|
description: mapValueOfType<String>(json, r'description'),
|
||||||
enabled: mapValueOfType<bool>(json, r'enabled'),
|
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||||
filters: WorkflowFilterItemDto.listFromJson(json[r'filters']),
|
name: mapValueOfType<String>(json, r'name'),
|
||||||
name: mapValueOfType<String>(json, r'name')!,
|
steps: WorkflowStepDto.listFromJson(json[r'steps']),
|
||||||
triggerType: PluginTriggerType.fromJson(json[r'triggerType'])!,
|
trigger: WorkflowTrigger.fromJson(json[r'trigger'])!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -153,10 +142,7 @@ class WorkflowCreateDto {
|
|||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
'actions',
|
'trigger',
|
||||||
'filters',
|
|
||||||
'name',
|
|
||||||
'triggerType',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
-107
@@ -1,107 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class WorkflowFilterItemDto {
|
|
||||||
/// Returns a new [WorkflowFilterItemDto] instance.
|
|
||||||
WorkflowFilterItemDto({
|
|
||||||
this.filterConfig = const {},
|
|
||||||
required this.pluginFilterId,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, Object> filterConfig;
|
|
||||||
|
|
||||||
/// Plugin filter ID
|
|
||||||
String pluginFilterId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowFilterItemDto &&
|
|
||||||
_deepEquality.equals(other.filterConfig, filterConfig) &&
|
|
||||||
other.pluginFilterId == pluginFilterId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(filterConfig.hashCode) +
|
|
||||||
(pluginFilterId.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'WorkflowFilterItemDto[filterConfig=$filterConfig, pluginFilterId=$pluginFilterId]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
json[r'filterConfig'] = this.filterConfig;
|
|
||||||
json[r'pluginFilterId'] = this.pluginFilterId;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [WorkflowFilterItemDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static WorkflowFilterItemDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "WorkflowFilterItemDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return WorkflowFilterItemDto(
|
|
||||||
filterConfig: mapCastOfType<String, Object>(json, r'filterConfig') ?? const {},
|
|
||||||
pluginFilterId: mapValueOfType<String>(json, r'pluginFilterId')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<WorkflowFilterItemDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <WorkflowFilterItemDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = WorkflowFilterItemDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, WorkflowFilterItemDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, WorkflowFilterItemDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = WorkflowFilterItemDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of WorkflowFilterItemDto-objects as value to a dart map
|
|
||||||
static Map<String, List<WorkflowFilterItemDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<WorkflowFilterItemDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = WorkflowFilterItemDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'pluginFilterId',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
//
|
|
||||||
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
|
||||||
//
|
|
||||||
// @dart=2.18
|
|
||||||
|
|
||||||
// ignore_for_file: unused_element, unused_import
|
|
||||||
// ignore_for_file: always_put_required_named_parameters_first
|
|
||||||
// ignore_for_file: constant_identifier_names
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars
|
|
||||||
|
|
||||||
part of openapi.api;
|
|
||||||
|
|
||||||
class WorkflowFilterResponseDto {
|
|
||||||
/// Returns a new [WorkflowFilterResponseDto] instance.
|
|
||||||
WorkflowFilterResponseDto({
|
|
||||||
required this.filterConfig,
|
|
||||||
required this.id,
|
|
||||||
required this.order,
|
|
||||||
required this.pluginFilterId,
|
|
||||||
required this.workflowId,
|
|
||||||
});
|
|
||||||
|
|
||||||
Map<String, Object>? filterConfig;
|
|
||||||
|
|
||||||
/// Filter ID
|
|
||||||
String id;
|
|
||||||
|
|
||||||
/// Filter order
|
|
||||||
///
|
|
||||||
/// Minimum value: -9007199254740991
|
|
||||||
/// Maximum value: 9007199254740991
|
|
||||||
int order;
|
|
||||||
|
|
||||||
/// Plugin filter ID
|
|
||||||
String pluginFilterId;
|
|
||||||
|
|
||||||
/// Workflow ID
|
|
||||||
String workflowId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowFilterResponseDto &&
|
|
||||||
_deepEquality.equals(other.filterConfig, filterConfig) &&
|
|
||||||
other.id == id &&
|
|
||||||
other.order == order &&
|
|
||||||
other.pluginFilterId == pluginFilterId &&
|
|
||||||
other.workflowId == workflowId;
|
|
||||||
|
|
||||||
@override
|
|
||||||
int get hashCode =>
|
|
||||||
// ignore: unnecessary_parenthesis
|
|
||||||
(filterConfig == null ? 0 : filterConfig!.hashCode) +
|
|
||||||
(id.hashCode) +
|
|
||||||
(order.hashCode) +
|
|
||||||
(pluginFilterId.hashCode) +
|
|
||||||
(workflowId.hashCode);
|
|
||||||
|
|
||||||
@override
|
|
||||||
String toString() => 'WorkflowFilterResponseDto[filterConfig=$filterConfig, id=$id, order=$order, pluginFilterId=$pluginFilterId, workflowId=$workflowId]';
|
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
|
||||||
final json = <String, dynamic>{};
|
|
||||||
if (this.filterConfig != null) {
|
|
||||||
json[r'filterConfig'] = this.filterConfig;
|
|
||||||
} else {
|
|
||||||
// json[r'filterConfig'] = null;
|
|
||||||
}
|
|
||||||
json[r'id'] = this.id;
|
|
||||||
json[r'order'] = this.order;
|
|
||||||
json[r'pluginFilterId'] = this.pluginFilterId;
|
|
||||||
json[r'workflowId'] = this.workflowId;
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns a new [WorkflowFilterResponseDto] instance and imports its values from
|
|
||||||
/// [value] if it's a [Map], null otherwise.
|
|
||||||
// ignore: prefer_constructors_over_static_methods
|
|
||||||
static WorkflowFilterResponseDto? fromJson(dynamic value) {
|
|
||||||
upgradeDto(value, "WorkflowFilterResponseDto");
|
|
||||||
if (value is Map) {
|
|
||||||
final json = value.cast<String, dynamic>();
|
|
||||||
|
|
||||||
return WorkflowFilterResponseDto(
|
|
||||||
filterConfig: mapCastOfType<String, Object>(json, r'filterConfig'),
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
|
||||||
order: mapValueOfType<int>(json, r'order')!,
|
|
||||||
pluginFilterId: mapValueOfType<String>(json, r'pluginFilterId')!,
|
|
||||||
workflowId: mapValueOfType<String>(json, r'workflowId')!,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
static List<WorkflowFilterResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final result = <WorkflowFilterResponseDto>[];
|
|
||||||
if (json is List && json.isNotEmpty) {
|
|
||||||
for (final row in json) {
|
|
||||||
final value = WorkflowFilterResponseDto.fromJson(row);
|
|
||||||
if (value != null) {
|
|
||||||
result.add(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result.toList(growable: growable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static Map<String, WorkflowFilterResponseDto> mapFromJson(dynamic json) {
|
|
||||||
final map = <String, WorkflowFilterResponseDto>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
final value = WorkflowFilterResponseDto.fromJson(entry.value);
|
|
||||||
if (value != null) {
|
|
||||||
map[entry.key] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// maps a json object with a list of WorkflowFilterResponseDto-objects as value to a dart map
|
|
||||||
static Map<String, List<WorkflowFilterResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
|
||||||
final map = <String, List<WorkflowFilterResponseDto>>{};
|
|
||||||
if (json is Map && json.isNotEmpty) {
|
|
||||||
// ignore: parameter_assignments
|
|
||||||
json = json.cast<String, dynamic>();
|
|
||||||
for (final entry in json.entries) {
|
|
||||||
map[entry.key] = WorkflowFilterResponseDto.listFromJson(entry.value, growable: growable,);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return map;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
|
||||||
static const requiredKeys = <String>{
|
|
||||||
'filterConfig',
|
|
||||||
'id',
|
|
||||||
'order',
|
|
||||||
'pluginFilterId',
|
|
||||||
'workflowId',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
+32
-37
@@ -13,86 +13,83 @@ part of openapi.api;
|
|||||||
class WorkflowResponseDto {
|
class WorkflowResponseDto {
|
||||||
/// Returns a new [WorkflowResponseDto] instance.
|
/// Returns a new [WorkflowResponseDto] instance.
|
||||||
WorkflowResponseDto({
|
WorkflowResponseDto({
|
||||||
this.actions = const [],
|
|
||||||
required this.createdAt,
|
required this.createdAt,
|
||||||
required this.description,
|
required this.description,
|
||||||
required this.enabled,
|
required this.enabled,
|
||||||
this.filters = const [],
|
|
||||||
required this.id,
|
required this.id,
|
||||||
required this.name,
|
required this.name,
|
||||||
required this.ownerId,
|
this.steps = const [],
|
||||||
required this.triggerType,
|
required this.trigger,
|
||||||
|
required this.updatedAt,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Workflow actions
|
|
||||||
List<WorkflowActionResponseDto> actions;
|
|
||||||
|
|
||||||
/// Creation date
|
/// Creation date
|
||||||
String createdAt;
|
String createdAt;
|
||||||
|
|
||||||
/// Workflow description
|
/// Workflow description
|
||||||
String description;
|
String? description;
|
||||||
|
|
||||||
/// Workflow enabled
|
/// Workflow enabled
|
||||||
bool enabled;
|
bool enabled;
|
||||||
|
|
||||||
/// Workflow filters
|
|
||||||
List<WorkflowFilterResponseDto> filters;
|
|
||||||
|
|
||||||
/// Workflow ID
|
/// Workflow ID
|
||||||
String id;
|
String id;
|
||||||
|
|
||||||
/// Workflow name
|
/// Workflow name
|
||||||
String? name;
|
String? name;
|
||||||
|
|
||||||
/// Owner user ID
|
/// Workflow steps
|
||||||
String ownerId;
|
List<WorkflowStepDto> steps;
|
||||||
|
|
||||||
PluginTriggerType triggerType;
|
WorkflowTrigger trigger;
|
||||||
|
|
||||||
|
/// Update date
|
||||||
|
String updatedAt;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowResponseDto &&
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowResponseDto &&
|
||||||
_deepEquality.equals(other.actions, actions) &&
|
|
||||||
other.createdAt == createdAt &&
|
other.createdAt == createdAt &&
|
||||||
other.description == description &&
|
other.description == description &&
|
||||||
other.enabled == enabled &&
|
other.enabled == enabled &&
|
||||||
_deepEquality.equals(other.filters, filters) &&
|
|
||||||
other.id == id &&
|
other.id == id &&
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.ownerId == ownerId &&
|
_deepEquality.equals(other.steps, steps) &&
|
||||||
other.triggerType == triggerType;
|
other.trigger == trigger &&
|
||||||
|
other.updatedAt == updatedAt;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(actions.hashCode) +
|
|
||||||
(createdAt.hashCode) +
|
(createdAt.hashCode) +
|
||||||
(description.hashCode) +
|
(description == null ? 0 : description!.hashCode) +
|
||||||
(enabled.hashCode) +
|
(enabled.hashCode) +
|
||||||
(filters.hashCode) +
|
|
||||||
(id.hashCode) +
|
(id.hashCode) +
|
||||||
(name == null ? 0 : name!.hashCode) +
|
(name == null ? 0 : name!.hashCode) +
|
||||||
(ownerId.hashCode) +
|
(steps.hashCode) +
|
||||||
(triggerType.hashCode);
|
(trigger.hashCode) +
|
||||||
|
(updatedAt.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'WorkflowResponseDto[actions=$actions, createdAt=$createdAt, description=$description, enabled=$enabled, filters=$filters, id=$id, name=$name, ownerId=$ownerId, triggerType=$triggerType]';
|
String toString() => 'WorkflowResponseDto[createdAt=$createdAt, description=$description, enabled=$enabled, id=$id, name=$name, steps=$steps, trigger=$trigger, updatedAt=$updatedAt]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json[r'actions'] = this.actions;
|
|
||||||
json[r'createdAt'] = this.createdAt;
|
json[r'createdAt'] = this.createdAt;
|
||||||
|
if (this.description != null) {
|
||||||
json[r'description'] = this.description;
|
json[r'description'] = this.description;
|
||||||
|
} else {
|
||||||
|
// json[r'description'] = null;
|
||||||
|
}
|
||||||
json[r'enabled'] = this.enabled;
|
json[r'enabled'] = this.enabled;
|
||||||
json[r'filters'] = this.filters;
|
|
||||||
json[r'id'] = this.id;
|
json[r'id'] = this.id;
|
||||||
if (this.name != null) {
|
if (this.name != null) {
|
||||||
json[r'name'] = this.name;
|
json[r'name'] = this.name;
|
||||||
} else {
|
} else {
|
||||||
// json[r'name'] = null;
|
// json[r'name'] = null;
|
||||||
}
|
}
|
||||||
json[r'ownerId'] = this.ownerId;
|
json[r'steps'] = this.steps;
|
||||||
json[r'triggerType'] = this.triggerType;
|
json[r'trigger'] = this.trigger;
|
||||||
|
json[r'updatedAt'] = this.updatedAt;
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,15 +102,14 @@ class WorkflowResponseDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return WorkflowResponseDto(
|
return WorkflowResponseDto(
|
||||||
actions: WorkflowActionResponseDto.listFromJson(json[r'actions']),
|
|
||||||
createdAt: mapValueOfType<String>(json, r'createdAt')!,
|
createdAt: mapValueOfType<String>(json, r'createdAt')!,
|
||||||
description: mapValueOfType<String>(json, r'description')!,
|
description: mapValueOfType<String>(json, r'description'),
|
||||||
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
enabled: mapValueOfType<bool>(json, r'enabled')!,
|
||||||
filters: WorkflowFilterResponseDto.listFromJson(json[r'filters']),
|
|
||||||
id: mapValueOfType<String>(json, r'id')!,
|
id: mapValueOfType<String>(json, r'id')!,
|
||||||
name: mapValueOfType<String>(json, r'name'),
|
name: mapValueOfType<String>(json, r'name'),
|
||||||
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
steps: WorkflowStepDto.listFromJson(json[r'steps']),
|
||||||
triggerType: PluginTriggerType.fromJson(json[r'triggerType'])!,
|
trigger: WorkflowTrigger.fromJson(json[r'trigger'])!,
|
||||||
|
updatedAt: mapValueOfType<String>(json, r'updatedAt')!,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
@@ -161,15 +157,14 @@ class WorkflowResponseDto {
|
|||||||
|
|
||||||
/// The list of required keys that must be present in a JSON.
|
/// The list of required keys that must be present in a JSON.
|
||||||
static const requiredKeys = <String>{
|
static const requiredKeys = <String>{
|
||||||
'actions',
|
|
||||||
'createdAt',
|
'createdAt',
|
||||||
'description',
|
'description',
|
||||||
'enabled',
|
'enabled',
|
||||||
'filters',
|
|
||||||
'id',
|
'id',
|
||||||
'name',
|
'name',
|
||||||
'ownerId',
|
'steps',
|
||||||
'triggerType',
|
'trigger',
|
||||||
|
'updatedAt',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,134 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class WorkflowShareResponseDto {
|
||||||
|
/// Returns a new [WorkflowShareResponseDto] instance.
|
||||||
|
WorkflowShareResponseDto({
|
||||||
|
required this.description,
|
||||||
|
required this.name,
|
||||||
|
this.steps = const [],
|
||||||
|
required this.trigger,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Workflow description
|
||||||
|
String? description;
|
||||||
|
|
||||||
|
/// Workflow name
|
||||||
|
String? name;
|
||||||
|
|
||||||
|
/// Workflow steps
|
||||||
|
List<WorkflowShareStepDto> steps;
|
||||||
|
|
||||||
|
WorkflowTrigger trigger;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowShareResponseDto &&
|
||||||
|
other.description == description &&
|
||||||
|
other.name == name &&
|
||||||
|
_deepEquality.equals(other.steps, steps) &&
|
||||||
|
other.trigger == trigger;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(description == null ? 0 : description!.hashCode) +
|
||||||
|
(name == null ? 0 : name!.hashCode) +
|
||||||
|
(steps.hashCode) +
|
||||||
|
(trigger.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'WorkflowShareResponseDto[description=$description, name=$name, steps=$steps, trigger=$trigger]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
if (this.description != null) {
|
||||||
|
json[r'description'] = this.description;
|
||||||
|
} else {
|
||||||
|
// json[r'description'] = null;
|
||||||
|
}
|
||||||
|
if (this.name != null) {
|
||||||
|
json[r'name'] = this.name;
|
||||||
|
} else {
|
||||||
|
// json[r'name'] = null;
|
||||||
|
}
|
||||||
|
json[r'steps'] = this.steps;
|
||||||
|
json[r'trigger'] = this.trigger;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [WorkflowShareResponseDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static WorkflowShareResponseDto? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "WorkflowShareResponseDto");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return WorkflowShareResponseDto(
|
||||||
|
description: mapValueOfType<String>(json, r'description'),
|
||||||
|
name: mapValueOfType<String>(json, r'name'),
|
||||||
|
steps: WorkflowShareStepDto.listFromJson(json[r'steps']),
|
||||||
|
trigger: WorkflowTrigger.fromJson(json[r'trigger'])!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WorkflowShareResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <WorkflowShareResponseDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = WorkflowShareResponseDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, WorkflowShareResponseDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, WorkflowShareResponseDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = WorkflowShareResponseDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of WorkflowShareResponseDto-objects as value to a dart map
|
||||||
|
static Map<String, List<WorkflowShareResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<WorkflowShareResponseDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = WorkflowShareResponseDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'description',
|
||||||
|
'name',
|
||||||
|
'steps',
|
||||||
|
'trigger',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class WorkflowShareStepDto {
|
||||||
|
/// Returns a new [WorkflowShareStepDto] instance.
|
||||||
|
WorkflowShareStepDto({
|
||||||
|
this.config = const {},
|
||||||
|
this.enabled,
|
||||||
|
required this.method,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Step configuration
|
||||||
|
Map<String, Object>? config;
|
||||||
|
|
||||||
|
/// Step is enabled
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
bool? enabled;
|
||||||
|
|
||||||
|
/// Step plugin method
|
||||||
|
String method;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowShareStepDto &&
|
||||||
|
_deepEquality.equals(other.config, config) &&
|
||||||
|
other.enabled == enabled &&
|
||||||
|
other.method == method;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(config == null ? 0 : config!.hashCode) +
|
||||||
|
(enabled == null ? 0 : enabled!.hashCode) +
|
||||||
|
(method.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'WorkflowShareStepDto[config=$config, enabled=$enabled, method=$method]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
if (this.config != null) {
|
||||||
|
json[r'config'] = this.config;
|
||||||
|
} else {
|
||||||
|
// json[r'config'] = null;
|
||||||
|
}
|
||||||
|
if (this.enabled != null) {
|
||||||
|
json[r'enabled'] = this.enabled;
|
||||||
|
} else {
|
||||||
|
// json[r'enabled'] = null;
|
||||||
|
}
|
||||||
|
json[r'method'] = this.method;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [WorkflowShareStepDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static WorkflowShareStepDto? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "WorkflowShareStepDto");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return WorkflowShareStepDto(
|
||||||
|
config: mapCastOfType<String, Object>(json, r'config'),
|
||||||
|
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||||
|
method: mapValueOfType<String>(json, r'method')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WorkflowShareStepDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <WorkflowShareStepDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = WorkflowShareStepDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, WorkflowShareStepDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, WorkflowShareStepDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = WorkflowShareStepDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of WorkflowShareStepDto-objects as value to a dart map
|
||||||
|
static Map<String, List<WorkflowShareStepDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<WorkflowShareStepDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = WorkflowShareStepDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'config',
|
||||||
|
'method',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
+131
@@ -0,0 +1,131 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class WorkflowStepDto {
|
||||||
|
/// Returns a new [WorkflowStepDto] instance.
|
||||||
|
WorkflowStepDto({
|
||||||
|
this.config = const {},
|
||||||
|
this.enabled,
|
||||||
|
required this.method,
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Step configuration
|
||||||
|
Map<String, Object>? config;
|
||||||
|
|
||||||
|
/// Step is enabled
|
||||||
|
///
|
||||||
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
|
/// source code must fall back to having a nullable type.
|
||||||
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
|
///
|
||||||
|
bool? enabled;
|
||||||
|
|
||||||
|
/// Step plugin method
|
||||||
|
String method;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowStepDto &&
|
||||||
|
_deepEquality.equals(other.config, config) &&
|
||||||
|
other.enabled == enabled &&
|
||||||
|
other.method == method;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(config == null ? 0 : config!.hashCode) +
|
||||||
|
(enabled == null ? 0 : enabled!.hashCode) +
|
||||||
|
(method.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'WorkflowStepDto[config=$config, enabled=$enabled, method=$method]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
if (this.config != null) {
|
||||||
|
json[r'config'] = this.config;
|
||||||
|
} else {
|
||||||
|
// json[r'config'] = null;
|
||||||
|
}
|
||||||
|
if (this.enabled != null) {
|
||||||
|
json[r'enabled'] = this.enabled;
|
||||||
|
} else {
|
||||||
|
// json[r'enabled'] = null;
|
||||||
|
}
|
||||||
|
json[r'method'] = this.method;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [WorkflowStepDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static WorkflowStepDto? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "WorkflowStepDto");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return WorkflowStepDto(
|
||||||
|
config: mapCastOfType<String, Object>(json, r'config'),
|
||||||
|
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||||
|
method: mapValueOfType<String>(json, r'method')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WorkflowStepDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <WorkflowStepDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = WorkflowStepDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, WorkflowStepDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, WorkflowStepDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = WorkflowStepDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of WorkflowStepDto-objects as value to a dart map
|
||||||
|
static Map<String, List<WorkflowStepDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<WorkflowStepDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = WorkflowStepDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'config',
|
||||||
|
'method',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Generated
+22
-22
@@ -11,9 +11,9 @@
|
|||||||
part of openapi.api;
|
part of openapi.api;
|
||||||
|
|
||||||
/// Plugin trigger type
|
/// Plugin trigger type
|
||||||
class PluginTriggerType {
|
class WorkflowTrigger {
|
||||||
/// Instantiate a new enum with the provided [value].
|
/// Instantiate a new enum with the provided [value].
|
||||||
const PluginTriggerType._(this.value);
|
const WorkflowTrigger._(this.value);
|
||||||
|
|
||||||
/// The underlying value of this enum member.
|
/// The underlying value of this enum member.
|
||||||
final String value;
|
final String value;
|
||||||
@@ -23,22 +23,22 @@ class PluginTriggerType {
|
|||||||
|
|
||||||
String toJson() => value;
|
String toJson() => value;
|
||||||
|
|
||||||
static const assetCreate = PluginTriggerType._(r'AssetCreate');
|
static const assetCreate = WorkflowTrigger._(r'AssetCreate');
|
||||||
static const personRecognized = PluginTriggerType._(r'PersonRecognized');
|
static const personRecognized = WorkflowTrigger._(r'PersonRecognized');
|
||||||
|
|
||||||
/// List of all possible values in this [enum][PluginTriggerType].
|
/// List of all possible values in this [enum][WorkflowTrigger].
|
||||||
static const values = <PluginTriggerType>[
|
static const values = <WorkflowTrigger>[
|
||||||
assetCreate,
|
assetCreate,
|
||||||
personRecognized,
|
personRecognized,
|
||||||
];
|
];
|
||||||
|
|
||||||
static PluginTriggerType? fromJson(dynamic value) => PluginTriggerTypeTypeTransformer().decode(value);
|
static WorkflowTrigger? fromJson(dynamic value) => WorkflowTriggerTypeTransformer().decode(value);
|
||||||
|
|
||||||
static List<PluginTriggerType> listFromJson(dynamic json, {bool growable = false,}) {
|
static List<WorkflowTrigger> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
final result = <PluginTriggerType>[];
|
final result = <WorkflowTrigger>[];
|
||||||
if (json is List && json.isNotEmpty) {
|
if (json is List && json.isNotEmpty) {
|
||||||
for (final row in json) {
|
for (final row in json) {
|
||||||
final value = PluginTriggerType.fromJson(row);
|
final value = WorkflowTrigger.fromJson(row);
|
||||||
if (value != null) {
|
if (value != null) {
|
||||||
result.add(value);
|
result.add(value);
|
||||||
}
|
}
|
||||||
@@ -48,16 +48,16 @@ class PluginTriggerType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Transformation class that can [encode] an instance of [PluginTriggerType] to String,
|
/// Transformation class that can [encode] an instance of [WorkflowTrigger] to String,
|
||||||
/// and [decode] dynamic data back to [PluginTriggerType].
|
/// and [decode] dynamic data back to [WorkflowTrigger].
|
||||||
class PluginTriggerTypeTypeTransformer {
|
class WorkflowTriggerTypeTransformer {
|
||||||
factory PluginTriggerTypeTypeTransformer() => _instance ??= const PluginTriggerTypeTypeTransformer._();
|
factory WorkflowTriggerTypeTransformer() => _instance ??= const WorkflowTriggerTypeTransformer._();
|
||||||
|
|
||||||
const PluginTriggerTypeTypeTransformer._();
|
const WorkflowTriggerTypeTransformer._();
|
||||||
|
|
||||||
String encode(PluginTriggerType data) => data.value;
|
String encode(WorkflowTrigger data) => data.value;
|
||||||
|
|
||||||
/// Decodes a [dynamic value][data] to a PluginTriggerType.
|
/// Decodes a [dynamic value][data] to a WorkflowTrigger.
|
||||||
///
|
///
|
||||||
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||||
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||||
@@ -65,11 +65,11 @@ class PluginTriggerTypeTypeTransformer {
|
|||||||
///
|
///
|
||||||
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||||
/// and users are still using an old app with the old code.
|
/// and users are still using an old app with the old code.
|
||||||
PluginTriggerType? decode(dynamic data, {bool allowNull = true}) {
|
WorkflowTrigger? decode(dynamic data, {bool allowNull = true}) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
switch (data) {
|
switch (data) {
|
||||||
case r'AssetCreate': return PluginTriggerType.assetCreate;
|
case r'AssetCreate': return WorkflowTrigger.assetCreate;
|
||||||
case r'PersonRecognized': return PluginTriggerType.personRecognized;
|
case r'PersonRecognized': return WorkflowTrigger.personRecognized;
|
||||||
default:
|
default:
|
||||||
if (!allowNull) {
|
if (!allowNull) {
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
throw ArgumentError('Unknown enum value to decode: $data');
|
||||||
@@ -79,7 +79,7 @@ class PluginTriggerTypeTypeTransformer {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Singleton [PluginTriggerTypeTypeTransformer] instance.
|
/// Singleton [WorkflowTriggerTypeTransformer] instance.
|
||||||
static PluginTriggerTypeTypeTransformer? _instance;
|
static WorkflowTriggerTypeTransformer? _instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class WorkflowTriggerResponseDto {
|
||||||
|
/// Returns a new [WorkflowTriggerResponseDto] instance.
|
||||||
|
WorkflowTriggerResponseDto({
|
||||||
|
required this.trigger,
|
||||||
|
this.types = const [],
|
||||||
|
});
|
||||||
|
|
||||||
|
WorkflowTrigger trigger;
|
||||||
|
|
||||||
|
/// Workflow types
|
||||||
|
List<WorkflowType> types;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowTriggerResponseDto &&
|
||||||
|
other.trigger == trigger &&
|
||||||
|
_deepEquality.equals(other.types, types);
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(trigger.hashCode) +
|
||||||
|
(types.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'WorkflowTriggerResponseDto[trigger=$trigger, types=$types]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
json[r'trigger'] = this.trigger;
|
||||||
|
json[r'types'] = this.types;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [WorkflowTriggerResponseDto] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static WorkflowTriggerResponseDto? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "WorkflowTriggerResponseDto");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return WorkflowTriggerResponseDto(
|
||||||
|
trigger: WorkflowTrigger.fromJson(json[r'trigger'])!,
|
||||||
|
types: WorkflowType.listFromJson(json[r'types']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<WorkflowTriggerResponseDto> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <WorkflowTriggerResponseDto>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = WorkflowTriggerResponseDto.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, WorkflowTriggerResponseDto> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, WorkflowTriggerResponseDto>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = WorkflowTriggerResponseDto.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of WorkflowTriggerResponseDto-objects as value to a dart map
|
||||||
|
static Map<String, List<WorkflowTriggerResponseDto>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<WorkflowTriggerResponseDto>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = WorkflowTriggerResponseDto.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'trigger',
|
||||||
|
'types',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
+85
@@ -0,0 +1,85 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
/// Workflow type
|
||||||
|
class WorkflowType {
|
||||||
|
/// Instantiate a new enum with the provided [value].
|
||||||
|
const WorkflowType._(this.value);
|
||||||
|
|
||||||
|
/// The underlying value of this enum member.
|
||||||
|
final String value;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => value;
|
||||||
|
|
||||||
|
String toJson() => value;
|
||||||
|
|
||||||
|
static const assetV1 = WorkflowType._(r'AssetV1');
|
||||||
|
static const assetPersonV1 = WorkflowType._(r'AssetPersonV1');
|
||||||
|
|
||||||
|
/// List of all possible values in this [enum][WorkflowType].
|
||||||
|
static const values = <WorkflowType>[
|
||||||
|
assetV1,
|
||||||
|
assetPersonV1,
|
||||||
|
];
|
||||||
|
|
||||||
|
static WorkflowType? fromJson(dynamic value) => WorkflowTypeTypeTransformer().decode(value);
|
||||||
|
|
||||||
|
static List<WorkflowType> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <WorkflowType>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = WorkflowType.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Transformation class that can [encode] an instance of [WorkflowType] to String,
|
||||||
|
/// and [decode] dynamic data back to [WorkflowType].
|
||||||
|
class WorkflowTypeTypeTransformer {
|
||||||
|
factory WorkflowTypeTypeTransformer() => _instance ??= const WorkflowTypeTypeTransformer._();
|
||||||
|
|
||||||
|
const WorkflowTypeTypeTransformer._();
|
||||||
|
|
||||||
|
String encode(WorkflowType data) => data.value;
|
||||||
|
|
||||||
|
/// Decodes a [dynamic value][data] to a WorkflowType.
|
||||||
|
///
|
||||||
|
/// If [allowNull] is true and the [dynamic value][data] cannot be decoded successfully,
|
||||||
|
/// then null is returned. However, if [allowNull] is false and the [dynamic value][data]
|
||||||
|
/// cannot be decoded successfully, then an [UnimplementedError] is thrown.
|
||||||
|
///
|
||||||
|
/// The [allowNull] is very handy when an API changes and a new enum value is added or removed,
|
||||||
|
/// and users are still using an old app with the old code.
|
||||||
|
WorkflowType? decode(dynamic data, {bool allowNull = true}) {
|
||||||
|
if (data != null) {
|
||||||
|
switch (data) {
|
||||||
|
case r'AssetV1': return WorkflowType.assetV1;
|
||||||
|
case r'AssetPersonV1': return WorkflowType.assetPersonV1;
|
||||||
|
default:
|
||||||
|
if (!allowNull) {
|
||||||
|
throw ArgumentError('Unknown enum value to decode: $data');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Singleton [WorkflowTypeTypeTransformer] instance.
|
||||||
|
static WorkflowTypeTypeTransformer? _instance;
|
||||||
|
}
|
||||||
|
|
||||||
+16
-37
@@ -13,24 +13,14 @@ part of openapi.api;
|
|||||||
class WorkflowUpdateDto {
|
class WorkflowUpdateDto {
|
||||||
/// Returns a new [WorkflowUpdateDto] instance.
|
/// Returns a new [WorkflowUpdateDto] instance.
|
||||||
WorkflowUpdateDto({
|
WorkflowUpdateDto({
|
||||||
this.actions = const [],
|
|
||||||
this.description,
|
this.description,
|
||||||
this.enabled,
|
this.enabled,
|
||||||
this.filters = const [],
|
|
||||||
this.name,
|
this.name,
|
||||||
this.triggerType,
|
this.steps = const [],
|
||||||
|
this.trigger,
|
||||||
});
|
});
|
||||||
|
|
||||||
/// Workflow actions
|
|
||||||
List<WorkflowActionItemDto> actions;
|
|
||||||
|
|
||||||
/// Workflow description
|
/// Workflow description
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
String? description;
|
String? description;
|
||||||
|
|
||||||
/// Workflow enabled
|
/// Workflow enabled
|
||||||
@@ -42,51 +32,41 @@ class WorkflowUpdateDto {
|
|||||||
///
|
///
|
||||||
bool? enabled;
|
bool? enabled;
|
||||||
|
|
||||||
/// Workflow filters
|
|
||||||
List<WorkflowFilterItemDto> filters;
|
|
||||||
|
|
||||||
/// Workflow name
|
/// Workflow name
|
||||||
///
|
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
|
||||||
/// source code must fall back to having a nullable type.
|
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
|
||||||
///
|
|
||||||
String? name;
|
String? name;
|
||||||
|
|
||||||
|
List<WorkflowStepDto> steps;
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Please note: This property should have been non-nullable! Since the specification file
|
/// Please note: This property should have been non-nullable! Since the specification file
|
||||||
/// does not include a default value (using the "default:" property), however, the generated
|
/// does not include a default value (using the "default:" property), however, the generated
|
||||||
/// source code must fall back to having a nullable type.
|
/// source code must fall back to having a nullable type.
|
||||||
/// Consider adding a "default:" property in the specification file to hide this note.
|
/// Consider adding a "default:" property in the specification file to hide this note.
|
||||||
///
|
///
|
||||||
PluginTriggerType? triggerType;
|
WorkflowTrigger? trigger;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) => identical(this, other) || other is WorkflowUpdateDto &&
|
bool operator ==(Object other) => identical(this, other) || other is WorkflowUpdateDto &&
|
||||||
_deepEquality.equals(other.actions, actions) &&
|
|
||||||
other.description == description &&
|
other.description == description &&
|
||||||
other.enabled == enabled &&
|
other.enabled == enabled &&
|
||||||
_deepEquality.equals(other.filters, filters) &&
|
|
||||||
other.name == name &&
|
other.name == name &&
|
||||||
other.triggerType == triggerType;
|
_deepEquality.equals(other.steps, steps) &&
|
||||||
|
other.trigger == trigger;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
int get hashCode =>
|
int get hashCode =>
|
||||||
// ignore: unnecessary_parenthesis
|
// ignore: unnecessary_parenthesis
|
||||||
(actions.hashCode) +
|
|
||||||
(description == null ? 0 : description!.hashCode) +
|
(description == null ? 0 : description!.hashCode) +
|
||||||
(enabled == null ? 0 : enabled!.hashCode) +
|
(enabled == null ? 0 : enabled!.hashCode) +
|
||||||
(filters.hashCode) +
|
|
||||||
(name == null ? 0 : name!.hashCode) +
|
(name == null ? 0 : name!.hashCode) +
|
||||||
(triggerType == null ? 0 : triggerType!.hashCode);
|
(steps.hashCode) +
|
||||||
|
(trigger == null ? 0 : trigger!.hashCode);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() => 'WorkflowUpdateDto[actions=$actions, description=$description, enabled=$enabled, filters=$filters, name=$name, triggerType=$triggerType]';
|
String toString() => 'WorkflowUpdateDto[description=$description, enabled=$enabled, name=$name, steps=$steps, trigger=$trigger]';
|
||||||
|
|
||||||
Map<String, dynamic> toJson() {
|
Map<String, dynamic> toJson() {
|
||||||
final json = <String, dynamic>{};
|
final json = <String, dynamic>{};
|
||||||
json[r'actions'] = this.actions;
|
|
||||||
if (this.description != null) {
|
if (this.description != null) {
|
||||||
json[r'description'] = this.description;
|
json[r'description'] = this.description;
|
||||||
} else {
|
} else {
|
||||||
@@ -97,16 +77,16 @@ class WorkflowUpdateDto {
|
|||||||
} else {
|
} else {
|
||||||
// json[r'enabled'] = null;
|
// json[r'enabled'] = null;
|
||||||
}
|
}
|
||||||
json[r'filters'] = this.filters;
|
|
||||||
if (this.name != null) {
|
if (this.name != null) {
|
||||||
json[r'name'] = this.name;
|
json[r'name'] = this.name;
|
||||||
} else {
|
} else {
|
||||||
// json[r'name'] = null;
|
// json[r'name'] = null;
|
||||||
}
|
}
|
||||||
if (this.triggerType != null) {
|
json[r'steps'] = this.steps;
|
||||||
json[r'triggerType'] = this.triggerType;
|
if (this.trigger != null) {
|
||||||
|
json[r'trigger'] = this.trigger;
|
||||||
} else {
|
} else {
|
||||||
// json[r'triggerType'] = null;
|
// json[r'trigger'] = null;
|
||||||
}
|
}
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
@@ -120,12 +100,11 @@ class WorkflowUpdateDto {
|
|||||||
final json = value.cast<String, dynamic>();
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
return WorkflowUpdateDto(
|
return WorkflowUpdateDto(
|
||||||
actions: WorkflowActionItemDto.listFromJson(json[r'actions']),
|
|
||||||
description: mapValueOfType<String>(json, r'description'),
|
description: mapValueOfType<String>(json, r'description'),
|
||||||
enabled: mapValueOfType<bool>(json, r'enabled'),
|
enabled: mapValueOfType<bool>(json, r'enabled'),
|
||||||
filters: WorkflowFilterItemDto.listFromJson(json[r'filters']),
|
|
||||||
name: mapValueOfType<String>(json, r'name'),
|
name: mapValueOfType<String>(json, r'name'),
|
||||||
triggerType: PluginTriggerType.fromJson(json[r'triggerType']),
|
steps: WorkflowStepDto.listFromJson(json[r'steps']),
|
||||||
|
trigger: WorkflowTrigger.fromJson(json[r'trigger']),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|||||||
+507
-468
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
|||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all",
|
||||||
|
"printWidth": 120,
|
||||||
|
"semi": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
const esbuild = require('esbuild');
|
||||||
|
|
||||||
|
esbuild.build({
|
||||||
|
entryPoints: ['src/index.ts'],
|
||||||
|
outdir: 'dist',
|
||||||
|
bundle: true,
|
||||||
|
sourcemap: false,
|
||||||
|
minify: false, // might want to use true for production build
|
||||||
|
format: 'cjs', // needs to be CJS for now
|
||||||
|
target: ['es2020'], // don't go over es2020 because quickjs doesn't support it
|
||||||
|
});
|
||||||
@@ -0,0 +1,258 @@
|
|||||||
|
{
|
||||||
|
"name": "immich-plugin-core",
|
||||||
|
"version": "2.0.1",
|
||||||
|
"title": "Immich Core Plugin",
|
||||||
|
"description": "Core workflow capabilities for Immich",
|
||||||
|
"author": "Immich Team",
|
||||||
|
"wasmPath": "dist/plugin.wasm",
|
||||||
|
"methods": [
|
||||||
|
{
|
||||||
|
"name": "assetFileFilter",
|
||||||
|
"title": "Filter by filename",
|
||||||
|
"description": "Filter assets by filename pattern using text matching or regular expressions",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"pattern": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Filename pattern",
|
||||||
|
"description": "Text or regex pattern to match against filename"
|
||||||
|
},
|
||||||
|
"matchType": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Match type",
|
||||||
|
"enum": ["contains", "startsWith", "exact", "regex"],
|
||||||
|
"default": "contains",
|
||||||
|
"description": "Type of pattern matching to perform"
|
||||||
|
},
|
||||||
|
"caseSensitive": {
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false,
|
||||||
|
"title": "Case sensitive",
|
||||||
|
"description": "Whether matching should be case-sensitive"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["pattern"]
|
||||||
|
},
|
||||||
|
"uiHints": ["filter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "filterFileType",
|
||||||
|
"title": "Filter by file type",
|
||||||
|
"description": "Filter assets by file type",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"fileTypes": {
|
||||||
|
"title": "File types",
|
||||||
|
"description": "Allowed file types",
|
||||||
|
"type": "string",
|
||||||
|
"array": true,
|
||||||
|
"enum": ["image", "video"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["fileTypes"]
|
||||||
|
},
|
||||||
|
"uiHints": ["filter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "filterPerson",
|
||||||
|
"title": "Filter by person",
|
||||||
|
"description": "Filter by detected person",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"personIds": {
|
||||||
|
"type": "string",
|
||||||
|
"array": true,
|
||||||
|
"title": "Person IDs",
|
||||||
|
"description": "List of person to match",
|
||||||
|
"uiHint": "personI"
|
||||||
|
},
|
||||||
|
"matchAny": {
|
||||||
|
"type": "boolean",
|
||||||
|
"title": "Match any",
|
||||||
|
"default": true,
|
||||||
|
"description": "Match any name (true) or require all names (false)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["personIds"]
|
||||||
|
},
|
||||||
|
"uiHints": ["filter"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assetArchive",
|
||||||
|
"title": "Archive asset",
|
||||||
|
"description": "Change asset visibility to archive",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"inverse": {
|
||||||
|
"title": "Inverse",
|
||||||
|
"description": "When true will unarchive any archived assets",
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assetLock",
|
||||||
|
"title": "Move to locked folder",
|
||||||
|
"description": "Change visibility to locked",
|
||||||
|
"types": ["AssetV1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assetTimeline",
|
||||||
|
"title": "Move to timeline",
|
||||||
|
"description": "Change visibility to timeline",
|
||||||
|
"types": ["AssetV1"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assetVisibility",
|
||||||
|
"title": "Update visibility",
|
||||||
|
"description": "Change visibility to selected option",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"visibility": {
|
||||||
|
"title": "Visibility",
|
||||||
|
"description": "Asset visibility",
|
||||||
|
"type": "string",
|
||||||
|
"enum": ["archive", "timeline", "locked"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["visibility"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assetFavorite",
|
||||||
|
"title": "Favorite",
|
||||||
|
"description": "Favorite an asset",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inverse": {
|
||||||
|
"type": "boolean",
|
||||||
|
"title": "Inverse",
|
||||||
|
"description": "Unfavorite by default, set to true to favorite instead",
|
||||||
|
"default": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "assetAddToAlbums",
|
||||||
|
"title": "Add to Album(s)",
|
||||||
|
"description": "Add asset to selected albums",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"hostFunctions": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"albumIds": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Album IDs",
|
||||||
|
"array": true,
|
||||||
|
"description": "Target album IDs",
|
||||||
|
"uiHint": "albumId"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["albumIds"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "noop1",
|
||||||
|
"title": "DEV: Nested properties",
|
||||||
|
"description": "Example configuration with nested properties",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"number1": {
|
||||||
|
"type": "number",
|
||||||
|
"title": "Number 1",
|
||||||
|
"description": "Basic number"
|
||||||
|
},
|
||||||
|
"number2": {
|
||||||
|
"type": "number",
|
||||||
|
"title": "Number 2",
|
||||||
|
"array": true,
|
||||||
|
"description": "List of numbers"
|
||||||
|
},
|
||||||
|
"string1": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "String 1",
|
||||||
|
"description": "Basic string"
|
||||||
|
},
|
||||||
|
"string2": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "String 2",
|
||||||
|
"array": true,
|
||||||
|
"description": "List of strings"
|
||||||
|
},
|
||||||
|
"string3": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "String 3",
|
||||||
|
"enum": ["choice-1", "choice-2"],
|
||||||
|
"description": "Select from a list"
|
||||||
|
},
|
||||||
|
"nested": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Nested",
|
||||||
|
"description": "Nested properties for nesting",
|
||||||
|
"properties": {
|
||||||
|
"nested1": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Nested 1",
|
||||||
|
"description": "Nested string"
|
||||||
|
},
|
||||||
|
"nested2": {
|
||||||
|
"type": "number",
|
||||||
|
"title": "Nested 2",
|
||||||
|
"description": "Nested number"
|
||||||
|
},
|
||||||
|
"nested3": {
|
||||||
|
"type": "object",
|
||||||
|
"title": "Nested 3",
|
||||||
|
"description": "Nested again",
|
||||||
|
"properties": {
|
||||||
|
"nested4": {
|
||||||
|
"type": "boolean",
|
||||||
|
"title": "Nested 4",
|
||||||
|
"description": "Nested, nested boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "noop2",
|
||||||
|
"title": "DEV: Album pickers",
|
||||||
|
"description": "Example configuration with album pickers",
|
||||||
|
"types": ["AssetV1"],
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"albumId": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Album ID",
|
||||||
|
"description": "Target album ID",
|
||||||
|
"uiHint": "albumId"
|
||||||
|
},
|
||||||
|
"albumIds": {
|
||||||
|
"type": "string",
|
||||||
|
"title": "Album IDs",
|
||||||
|
"description": "Target album IDs",
|
||||||
|
"array": true,
|
||||||
|
"uiHint": "albumId"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
[tasks.install]
|
||||||
|
run = "pnpm install --frozen-lockfile"
|
||||||
|
|
||||||
|
[tasks.build]
|
||||||
|
depends = ["install"]
|
||||||
|
run = "pnpm --filter @immich/plugin-sdk --filter @immich/plugin-core build"
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"name": "plugins",
|
"name": "@immich/plugin-core",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"description": "",
|
"description": "",
|
||||||
"main": "src/index.ts",
|
"main": "src/index.ts",
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
"license": "AGPL-3.0",
|
"license": "AGPL-3.0",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@extism/js-pdk": "^1.0.1",
|
"@extism/js-pdk": "^1.0.1",
|
||||||
|
"@immich/plugin-sdk": "workspace:*",
|
||||||
"esbuild": "^0.28.0",
|
"esbuild": "^0.28.0",
|
||||||
"typescript": "^6.0.0"
|
"typescript": "^6.0.0"
|
||||||
}
|
}
|
||||||
Vendored
+19
@@ -0,0 +1,19 @@
|
|||||||
|
// copy from
|
||||||
|
// import '@immich/plugin-sdk/host-functions';
|
||||||
|
declare module 'extism:host' {
|
||||||
|
interface user {
|
||||||
|
albumAddAssets(ptr: PTR): I64;
|
||||||
|
addAssetsToAlbums(ptr: PTR): I64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module 'main' {
|
||||||
|
export function assetFileFilter(): I32;
|
||||||
|
export function assetFavorite(): I32;
|
||||||
|
export function assetVisibility(): I32;
|
||||||
|
export function assetArchive(): I32;
|
||||||
|
export function assetLock(): I32;
|
||||||
|
export function assetTimeline(): I32;
|
||||||
|
export function assetTrash(): I32;
|
||||||
|
export function assetAddToAlbums(): I32;
|
||||||
|
}
|
||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import { AssetStatus, AssetVisibility, WorkflowType, wrapper } from '@immich/plugin-sdk';
|
||||||
|
|
||||||
|
type AssetFileFilterConfig = {
|
||||||
|
pattern: string;
|
||||||
|
matchType?: 'contains' | 'exact' | 'regex' | 'startsWith';
|
||||||
|
caseSensitive?: boolean;
|
||||||
|
};
|
||||||
|
export const assetFileFilter = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, AssetFileFilterConfig>(({ data, config }) => {
|
||||||
|
const { pattern, matchType = 'contains', caseSensitive = false } = config;
|
||||||
|
|
||||||
|
const { asset } = data;
|
||||||
|
|
||||||
|
const fileName = asset.originalFileName || '';
|
||||||
|
const searchName = caseSensitive ? fileName : fileName.toLowerCase();
|
||||||
|
const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
|
||||||
|
|
||||||
|
switch (matchType) {
|
||||||
|
case 'contains': {
|
||||||
|
return { workflow: { continue: searchName.includes(searchPattern) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'exact': {
|
||||||
|
return { workflow: { continue: searchName === searchPattern } };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'startsWith': {
|
||||||
|
return { workflow: { continue: searchName.startsWith(searchPattern) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'regex': {
|
||||||
|
const flags = caseSensitive ? '' : 'i';
|
||||||
|
const regex = new RegExp(searchPattern, flags);
|
||||||
|
return { workflow: { continue: regex.test(fileName) } };
|
||||||
|
}
|
||||||
|
|
||||||
|
default: {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const assetFavorite = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
|
||||||
|
const target = config.inverse ? false : true;
|
||||||
|
if (target !== data.asset.isFavorite) {
|
||||||
|
return {
|
||||||
|
changes: {
|
||||||
|
asset: { isFavorite: target },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const assetVisibility = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, { visibility: AssetVisibility }>(({ config }) => ({
|
||||||
|
changes: { asset: { visibility: config.visibility } },
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const assetArchive = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
|
||||||
|
if (!config.inverse && data.asset.visibility !== AssetVisibility.Archive) {
|
||||||
|
return { changes: { asset: { visibility: AssetVisibility.Archive } } };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.inverse && data.asset.visibility === AssetVisibility.Archive) {
|
||||||
|
return { changes: { asset: { visibility: AssetVisibility.Timeline } } };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const assetLock = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => {
|
||||||
|
if (!config.inverse && data.asset.visibility !== AssetVisibility.Locked) {
|
||||||
|
return { changes: { asset: { visibility: AssetVisibility.Locked } } };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.inverse && data.asset.visibility === AssetVisibility.Locked) {
|
||||||
|
return { changes: { asset: { visibility: AssetVisibility.Timeline } } };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const assetTrash = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, { inverse?: boolean }>(({ config, data }) => ({
|
||||||
|
changes: {
|
||||||
|
asset: config.inverse
|
||||||
|
? { deletedAt: null, status: AssetStatus.Active }
|
||||||
|
: { deletedAt: new Date(), status: AssetStatus.Trashed },
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const assetAddToAlbums = () => {
|
||||||
|
return wrapper<WorkflowType.AssetV1, { albumIds: string[] }>(({ config, data, functions }) => {
|
||||||
|
if (config.albumIds.length === 1) {
|
||||||
|
functions.albumAddAssets(config.albumIds[0], [data.asset.id]);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
functions.addAssetsToAlbums({ albumIds: config.albumIds, assetIds: [data.asset.id] });
|
||||||
|
return {};
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -1,20 +1,24 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2020", // Specify ECMAScript target version
|
|
||||||
"module": "commonjs", // Specify module code generation
|
|
||||||
"lib": ["es2020"], // Specify a list of library files to be included in the compilation
|
|
||||||
"types": ["./src/index.d.ts", "./node_modules/@extism/js-pdk"], // Specify a list of type definition files to be included in the compilation
|
|
||||||
"strict": true, // Enable all strict type-checking options
|
|
||||||
"esModuleInterop": true, // Enables compatibility with Babel-style module imports
|
|
||||||
"skipLibCheck": true, // Skip type checking of declaration files
|
|
||||||
"allowJs": true, // Allow JavaScript files to be compiled
|
"allowJs": true, // Allow JavaScript files to be compiled
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"esModuleInterop": true, // Enables compatibility with Babel-style module imports
|
||||||
|
"lib": ["es2020"], // Specify a list of library files to be included in the compilation
|
||||||
|
"module": "nodenext", // Specify module code generation
|
||||||
|
"moduleResolution": "nodenext",
|
||||||
|
"noEmit": true, // Do not emit outputs (no .js or .d.ts files)
|
||||||
|
"outDir": "./dist",
|
||||||
"rootDir": "./src",
|
"rootDir": "./src",
|
||||||
"noEmit": true // Do not emit outputs (no .js or .d.ts files)
|
"skipLibCheck": true, // Skip type checking of declaration files
|
||||||
|
"strict": true, // Enable all strict type-checking options
|
||||||
|
"target": "es2020", // Specify ECMAScript target version
|
||||||
|
"types": ["./src/index.d.ts", "./node_modules/@extism/js-pdk"] // Specify a list of type definition files to be included in the compilation
|
||||||
},
|
},
|
||||||
"include": [
|
|
||||||
"src/**/*.ts" // Include all TypeScript files in src directory
|
|
||||||
],
|
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"node_modules" // Exclude the node_modules directory
|
"node_modules" // Exclude the node_modules directory
|
||||||
|
],
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts" // Include all TypeScript files in src directory
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import esbuild from 'esbuild';
|
||||||
|
|
||||||
|
esbuild.build({
|
||||||
|
entryPoints: ['src/index.ts'],
|
||||||
|
outdir: 'dist',
|
||||||
|
bundle: true,
|
||||||
|
sourcemap: false,
|
||||||
|
minify: false,
|
||||||
|
format: 'esm',
|
||||||
|
target: ['es2020'],
|
||||||
|
});
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"name": "@immich/plugin-sdk",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"type": "module",
|
||||||
|
"exports": {
|
||||||
|
"./host-functions": {
|
||||||
|
"import": "./dist/host-functions.js",
|
||||||
|
"types": "./dist/host-functions.d.ts"
|
||||||
|
},
|
||||||
|
".": {
|
||||||
|
"import": "./dist/index.js",
|
||||||
|
"types": "./dist/index.d.ts"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"build": "node esbuild.js && tsc --emitDeclarationOnly && tsc-alias"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
|
"keywords": [],
|
||||||
|
"author": "",
|
||||||
|
"license": "GNU Affero General Public License version 3",
|
||||||
|
"packageManager": "pnpm@10.30.3",
|
||||||
|
"devDependencies": {
|
||||||
|
"@extism/js-pdk": "^1.1.1",
|
||||||
|
"@types/node": "^24.11.0",
|
||||||
|
"esbuild": "^0.27.3",
|
||||||
|
"tsc-alias": "^1.8.16",
|
||||||
|
"typescript": "^5.9.3"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@extism/js-pdk": "^1.1.1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
export enum WorkflowTrigger {
|
||||||
|
AssetCreate = 'AssetCreate',
|
||||||
|
PersonRecognized = 'PersonRecognized',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum WorkflowType {
|
||||||
|
AssetV1 = 'AssetV1',
|
||||||
|
AssetPersonV1 = 'AssetPersonV1',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AssetType {
|
||||||
|
Image = 'IMAGE',
|
||||||
|
Video = 'VIDEO',
|
||||||
|
Audio = 'AUDIO',
|
||||||
|
Other = 'OTHER',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AssetStatus {
|
||||||
|
Active = 'active',
|
||||||
|
Trashed = 'trashed',
|
||||||
|
Deleted = 'deleted',
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum AssetVisibility {
|
||||||
|
Archive = 'archive',
|
||||||
|
Timeline = 'timeline',
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Video part of the LivePhotos and MotionPhotos
|
||||||
|
*/
|
||||||
|
Hidden = 'hidden',
|
||||||
|
Locked = 'locked',
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
declare module 'extism:host' {
|
||||||
|
interface user {
|
||||||
|
albumAddAssets(ptr: PTR): I64;
|
||||||
|
addAssetsToAlbums(ptr: PTR): I64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const host = Host.getFunctions();
|
||||||
|
type HostFunctionName = keyof typeof host;
|
||||||
|
type HostFunctionSuccessResult<T> = { success: true; response: T };
|
||||||
|
type HostFunctionErrorResult = {
|
||||||
|
success: false;
|
||||||
|
status: number;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
type HostFunctionResult<T> =
|
||||||
|
| HostFunctionSuccessResult<T>
|
||||||
|
| HostFunctionErrorResult;
|
||||||
|
|
||||||
|
const call = <T, R>(name: HostFunctionName, authToken: string, args: T) => {
|
||||||
|
const pointer1 = Memory.fromString(JSON.stringify({ authToken, args }));
|
||||||
|
const fn = host[name];
|
||||||
|
const handler = Memory.find(fn(pointer1.offset));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = JSON.parse(handler.readString()) as HostFunctionResult<R>;
|
||||||
|
|
||||||
|
if (result.success) {
|
||||||
|
return result.response;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`Failed to call host function "${String(name)}", received ${result.status} - ${JSON.stringify(result.message)}`,
|
||||||
|
);
|
||||||
|
} finally {
|
||||||
|
handler.free();
|
||||||
|
pointer1.free();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
type AlbumsToAssets = {
|
||||||
|
assetIds: string[];
|
||||||
|
albumIds: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export const hostFunctions = (authToken: string) => ({
|
||||||
|
albumAddAssets: (albumId: string, assetIds: string[]) =>
|
||||||
|
call('albumAddAssets', authToken, [albumId, { ids: assetIds }]),
|
||||||
|
addAssetsToAlbums: ({ assetIds, albumIds }: AlbumsToAssets) =>
|
||||||
|
call('addAssetsToAlbums', authToken, [{ albumIds, assetIds }]),
|
||||||
|
});
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export * from 'src/enum.js';
|
||||||
|
export * from 'src/host-functions.js';
|
||||||
|
export * from 'src/sdk.js';
|
||||||
|
export * from 'src/types.js';
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import type { WorkflowType } from 'src/enum.js';
|
||||||
|
import { hostFunctions } from 'src/host-functions.js';
|
||||||
|
import type {
|
||||||
|
ConfigValue,
|
||||||
|
WorkflowEventPayload,
|
||||||
|
WorkflowResponse,
|
||||||
|
} from 'src/types.js';
|
||||||
|
|
||||||
|
export const wrapper = <
|
||||||
|
T extends WorkflowType = WorkflowType,
|
||||||
|
TConfig extends ConfigValue = ConfigValue,
|
||||||
|
>(
|
||||||
|
fn: (
|
||||||
|
payload: WorkflowEventPayload<T, TConfig> & {
|
||||||
|
functions: ReturnType<typeof hostFunctions>;
|
||||||
|
},
|
||||||
|
) => WorkflowResponse<T> | undefined,
|
||||||
|
) => {
|
||||||
|
const input = Host.inputString();
|
||||||
|
|
||||||
|
try {
|
||||||
|
const event = JSON.parse(input) as WorkflowEventPayload<T, TConfig>;
|
||||||
|
// const debug = event.workflow.debug ?? false;
|
||||||
|
|
||||||
|
console.debug(
|
||||||
|
`Inputs: trigger=${event.trigger}, event=${event.type}, config=${JSON.stringify(event.config)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const response =
|
||||||
|
fn({ ...event, functions: hostFunctions(event.workflow.authToken) }) ??
|
||||||
|
{};
|
||||||
|
|
||||||
|
console.debug(
|
||||||
|
`Outputs: workflow=${JSON.stringify(response.workflow)}, changes=${JSON.stringify(response.changes)}, data=${JSON.stringify(response.data)}`,
|
||||||
|
);
|
||||||
|
|
||||||
|
const output = JSON.stringify(response);
|
||||||
|
Host.outputString(output);
|
||||||
|
} catch (error: Error | any) {
|
||||||
|
console.error(`Unhandled plugin exception: ${error.message || error}`);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,129 @@
|
|||||||
|
import type {
|
||||||
|
AssetStatus,
|
||||||
|
AssetType,
|
||||||
|
AssetVisibility,
|
||||||
|
WorkflowTrigger,
|
||||||
|
WorkflowType,
|
||||||
|
} from 'src/enum.js';
|
||||||
|
|
||||||
|
type DeepPartial<T> = T extends Date
|
||||||
|
? T
|
||||||
|
: T extends Record<string, unknown>
|
||||||
|
? { [K in keyof T]?: DeepPartial<T[K]> }
|
||||||
|
: T extends Array<infer R>
|
||||||
|
? DeepPartial<R>[]
|
||||||
|
: T;
|
||||||
|
|
||||||
|
export type WorkflowEventMap = {
|
||||||
|
[WorkflowType.AssetV1]: AssetV1;
|
||||||
|
[WorkflowType.AssetPersonV1]: AssetPersonV1;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowEventData<T extends WorkflowType> = WorkflowEventMap[T];
|
||||||
|
|
||||||
|
export type WorkflowEventPayload<
|
||||||
|
T extends WorkflowType = WorkflowType,
|
||||||
|
TConfig = WorkflowStepConfig,
|
||||||
|
> = {
|
||||||
|
trigger: WorkflowTrigger;
|
||||||
|
type: T;
|
||||||
|
data: WorkflowEventData<T>;
|
||||||
|
config: TConfig;
|
||||||
|
workflow: {
|
||||||
|
id: string;
|
||||||
|
authToken: string;
|
||||||
|
stepId: string;
|
||||||
|
debug?: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowChanges<T extends WorkflowType = WorkflowType> =
|
||||||
|
DeepPartial<WorkflowEventData<T>>;
|
||||||
|
|
||||||
|
export type WorkflowResponse<T extends WorkflowType = WorkflowType> = {
|
||||||
|
workflow?: {
|
||||||
|
/** stop the workflow */
|
||||||
|
continue?: boolean;
|
||||||
|
};
|
||||||
|
changes?: WorkflowChanges<T>;
|
||||||
|
/** data to be passed to the next workflow step */
|
||||||
|
data?: Record<string, unknown>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type WorkflowStepConfig = {
|
||||||
|
[key: string]: ConfigValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ConfigValue =
|
||||||
|
| string
|
||||||
|
| number
|
||||||
|
| boolean
|
||||||
|
| null
|
||||||
|
| ConfigValue[]
|
||||||
|
| { [key: string]: ConfigValue };
|
||||||
|
|
||||||
|
export type AssetV1 = {
|
||||||
|
asset: {
|
||||||
|
id: string;
|
||||||
|
ownerId: string;
|
||||||
|
type: AssetType;
|
||||||
|
originalPath: string;
|
||||||
|
fileCreatedAt: Date;
|
||||||
|
fileModifiedAt: Date;
|
||||||
|
isFavorite: boolean;
|
||||||
|
checksum: Buffer; // sha1 checksum
|
||||||
|
livePhotoVideoId: string | null;
|
||||||
|
updatedAt: Date;
|
||||||
|
createdAt: Date;
|
||||||
|
originalFileName: string;
|
||||||
|
isOffline: boolean;
|
||||||
|
libraryId: string | null;
|
||||||
|
isExternal: boolean;
|
||||||
|
deletedAt: Date | null;
|
||||||
|
localDateTime: Date;
|
||||||
|
stackId: string | null;
|
||||||
|
duplicateId: string | null;
|
||||||
|
status: AssetStatus;
|
||||||
|
visibility: AssetVisibility;
|
||||||
|
isEdited: boolean;
|
||||||
|
exifInfo: {
|
||||||
|
make: string | null;
|
||||||
|
model: string | null;
|
||||||
|
exifImageWidth: number | null;
|
||||||
|
exifImageHeight: number | null;
|
||||||
|
fileSizeInByte: number | null;
|
||||||
|
orientation: string | null;
|
||||||
|
dateTimeOriginal: Date | null;
|
||||||
|
modifyDate: Date | null;
|
||||||
|
lensModel: string | null;
|
||||||
|
fNumber: number | null;
|
||||||
|
focalLength: number | null;
|
||||||
|
iso: number | null;
|
||||||
|
latitude: number | null;
|
||||||
|
longitude: number | null;
|
||||||
|
city: string | null;
|
||||||
|
state: string | null;
|
||||||
|
country: string | null;
|
||||||
|
description: string | null;
|
||||||
|
fps: number | null;
|
||||||
|
exposureTime: string | null;
|
||||||
|
livePhotoCID: string | null;
|
||||||
|
timeZone: string | null;
|
||||||
|
projectionType: string | null;
|
||||||
|
profileDescription: string | null;
|
||||||
|
colorspace: string | null;
|
||||||
|
bitsPerSample: number | null;
|
||||||
|
autoStackId: string | null;
|
||||||
|
rating: number | null;
|
||||||
|
tags: string[] | null;
|
||||||
|
updatedAt: Date | null;
|
||||||
|
} | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AssetPersonV1 = AssetV1 & {
|
||||||
|
person: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"declaration": true,
|
||||||
|
"emitDeclarationOnly": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"exactOptionalPropertyTypes": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"lib": ["esnext"],
|
||||||
|
"module": "nodenext",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noUncheckedSideEffectImports": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"paths": {
|
||||||
|
"src/*": ["./src/*"]
|
||||||
|
},
|
||||||
|
"removeComments": true,
|
||||||
|
"rootDir": "./src",
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": false,
|
||||||
|
"strict": true,
|
||||||
|
"target": "esnext",
|
||||||
|
"types": ["node", "@extism/js-pdk"],
|
||||||
|
"verbatimModuleSyntax": true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
Copyright 2024, The Extism Authors.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification,
|
|
||||||
are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
1. Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
3. Neither the name of the copyright holder nor the names of its contributors
|
|
||||||
may be used to endorse or promote products derived from this software without
|
|
||||||
specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
|
||||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
||||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
|
||||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
||||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
||||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
|
||||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
||||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
const esbuild = require('esbuild');
|
|
||||||
|
|
||||||
esbuild
|
|
||||||
.build({
|
|
||||||
entryPoints: ['src/index.ts'],
|
|
||||||
outdir: 'dist',
|
|
||||||
bundle: true,
|
|
||||||
sourcemap: true,
|
|
||||||
minify: false, // might want to use true for production build
|
|
||||||
format: 'cjs', // needs to be CJS for now
|
|
||||||
target: ['es2020'] // don't go over es2020 because quickjs doesn't support it
|
|
||||||
})
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "immich-core",
|
|
||||||
"version": "2.0.1",
|
|
||||||
"title": "Immich Core",
|
|
||||||
"description": "Core workflow capabilities for Immich",
|
|
||||||
"author": "Immich Team",
|
|
||||||
"wasm": {
|
|
||||||
"path": "dist/plugin.wasm"
|
|
||||||
},
|
|
||||||
"filters": [
|
|
||||||
{
|
|
||||||
"methodName": "filterFileName",
|
|
||||||
"title": "Filter by filename",
|
|
||||||
"description": "Filter assets by filename pattern using text matching or regular expressions",
|
|
||||||
"supportedContexts": [
|
|
||||||
"asset"
|
|
||||||
],
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"pattern": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Filename pattern",
|
|
||||||
"description": "Text or regex pattern to match against filename"
|
|
||||||
},
|
|
||||||
"matchType": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Match type",
|
|
||||||
"enum": [
|
|
||||||
"contains",
|
|
||||||
"regex",
|
|
||||||
"exact"
|
|
||||||
],
|
|
||||||
"default": "contains",
|
|
||||||
"description": "Type of pattern matching to perform"
|
|
||||||
},
|
|
||||||
"caseSensitive": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": false,
|
|
||||||
"description": "Whether matching should be case-sensitive"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"pattern"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"methodName": "filterFileType",
|
|
||||||
"title": "Filter by file type",
|
|
||||||
"description": "Filter assets by file type",
|
|
||||||
"supportedContexts": [
|
|
||||||
"asset"
|
|
||||||
],
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"fileTypes": {
|
|
||||||
"type": "array",
|
|
||||||
"title": "File types",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"image",
|
|
||||||
"video"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"description": "Allowed file types"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"fileTypes"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"methodName": "filterPerson",
|
|
||||||
"title": "Filter by person",
|
|
||||||
"description": "Filter by detected person",
|
|
||||||
"supportedContexts": [
|
|
||||||
"person"
|
|
||||||
],
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"personIds": {
|
|
||||||
"type": "array",
|
|
||||||
"title": "Person IDs",
|
|
||||||
"items": {
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"description": "List of person to match",
|
|
||||||
"subType": "people-picker"
|
|
||||||
},
|
|
||||||
"matchAny": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true,
|
|
||||||
"description": "Match any name (true) or require all names (false)"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"personIds"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"actions": [
|
|
||||||
{
|
|
||||||
"methodName": "actionArchive",
|
|
||||||
"title": "Archive",
|
|
||||||
"description": "Move the asset to archive",
|
|
||||||
"supportedContexts": [
|
|
||||||
"asset"
|
|
||||||
],
|
|
||||||
"schema": {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"methodName": "actionFavorite",
|
|
||||||
"title": "Favorite",
|
|
||||||
"description": "Mark the asset as favorite or unfavorite",
|
|
||||||
"supportedContexts": [
|
|
||||||
"asset"
|
|
||||||
],
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"favorite": {
|
|
||||||
"type": "boolean",
|
|
||||||
"default": true,
|
|
||||||
"description": "Set favorite (true) or unfavorite (false)"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"methodName": "actionAddToAlbum",
|
|
||||||
"title": "Add to Album",
|
|
||||||
"description": "Add the item to a specified album",
|
|
||||||
"supportedContexts": [
|
|
||||||
"asset",
|
|
||||||
"person"
|
|
||||||
],
|
|
||||||
"schema": {
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"albumId": {
|
|
||||||
"type": "string",
|
|
||||||
"title": "Album ID",
|
|
||||||
"description": "Target album ID",
|
|
||||||
"subType": "album-picker"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"required": [
|
|
||||||
"albumId"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
[tools]
|
|
||||||
"github:extism/cli" = "1.6.3"
|
|
||||||
"github:webassembly/binaryen" = "version_124"
|
|
||||||
"github:extism/js-pdk" = "1.6.0"
|
|
||||||
|
|
||||||
[tasks.install]
|
|
||||||
run = "pnpm install --frozen-lockfile"
|
|
||||||
|
|
||||||
[tasks.build]
|
|
||||||
depends = ["install"]
|
|
||||||
run = "pnpm run build"
|
|
||||||
Generated
-533
@@ -1,533 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "plugins",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "plugins",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "AGPL-3.0",
|
|
||||||
"devDependencies": {
|
|
||||||
"@extism/js-pdk": "^1.0.1",
|
|
||||||
"esbuild": "^0.28.0",
|
|
||||||
"typescript": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/aix-ppc64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"aix"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/android-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"android"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/darwin-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"darwin"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/freebsd-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"freebsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ia32": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-loong64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==",
|
|
||||||
"cpu": [
|
|
||||||
"loong64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-mips64el": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==",
|
|
||||||
"cpu": [
|
|
||||||
"mips64el"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-ppc64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==",
|
|
||||||
"cpu": [
|
|
||||||
"ppc64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-riscv64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==",
|
|
||||||
"cpu": [
|
|
||||||
"riscv64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-s390x": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==",
|
|
||||||
"cpu": [
|
|
||||||
"s390x"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/linux-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"linux"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/netbsd-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"netbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openbsd-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openbsd"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/openharmony-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"openharmony"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/sunos-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"sunos"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-arm64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==",
|
|
||||||
"cpu": [
|
|
||||||
"arm64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-ia32": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==",
|
|
||||||
"cpu": [
|
|
||||||
"ia32"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@esbuild/win32-x64": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==",
|
|
||||||
"cpu": [
|
|
||||||
"x64"
|
|
||||||
],
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"optional": true,
|
|
||||||
"os": [
|
|
||||||
"win32"
|
|
||||||
],
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@extism/js-pdk": {
|
|
||||||
"version": "1.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/@extism/js-pdk/-/js-pdk-1.1.1.tgz",
|
|
||||||
"integrity": "sha512-VZLn/dX0ttA1uKk2PZeR/FL3N+nA1S5Vc7E5gdjkR60LuUIwCZT9cYON245V4HowHlBA7YOegh0TLjkx+wNbrA==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "BSD-Clause-3",
|
|
||||||
"dependencies": {
|
|
||||||
"urlpattern-polyfill": "^8.0.2"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/esbuild": {
|
|
||||||
"version": "0.28.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz",
|
|
||||||
"integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==",
|
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
|
||||||
"license": "MIT",
|
|
||||||
"bin": {
|
|
||||||
"esbuild": "bin/esbuild"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@esbuild/aix-ppc64": "0.28.0",
|
|
||||||
"@esbuild/android-arm": "0.28.0",
|
|
||||||
"@esbuild/android-arm64": "0.28.0",
|
|
||||||
"@esbuild/android-x64": "0.28.0",
|
|
||||||
"@esbuild/darwin-arm64": "0.28.0",
|
|
||||||
"@esbuild/darwin-x64": "0.28.0",
|
|
||||||
"@esbuild/freebsd-arm64": "0.28.0",
|
|
||||||
"@esbuild/freebsd-x64": "0.28.0",
|
|
||||||
"@esbuild/linux-arm": "0.28.0",
|
|
||||||
"@esbuild/linux-arm64": "0.28.0",
|
|
||||||
"@esbuild/linux-ia32": "0.28.0",
|
|
||||||
"@esbuild/linux-loong64": "0.28.0",
|
|
||||||
"@esbuild/linux-mips64el": "0.28.0",
|
|
||||||
"@esbuild/linux-ppc64": "0.28.0",
|
|
||||||
"@esbuild/linux-riscv64": "0.28.0",
|
|
||||||
"@esbuild/linux-s390x": "0.28.0",
|
|
||||||
"@esbuild/linux-x64": "0.28.0",
|
|
||||||
"@esbuild/netbsd-arm64": "0.28.0",
|
|
||||||
"@esbuild/netbsd-x64": "0.28.0",
|
|
||||||
"@esbuild/openbsd-arm64": "0.28.0",
|
|
||||||
"@esbuild/openbsd-x64": "0.28.0",
|
|
||||||
"@esbuild/openharmony-arm64": "0.28.0",
|
|
||||||
"@esbuild/sunos-x64": "0.28.0",
|
|
||||||
"@esbuild/win32-arm64": "0.28.0",
|
|
||||||
"@esbuild/win32-ia32": "0.28.0",
|
|
||||||
"@esbuild/win32-x64": "0.28.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/typescript": {
|
|
||||||
"version": "6.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz",
|
|
||||||
"integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "Apache-2.0",
|
|
||||||
"bin": {
|
|
||||||
"tsc": "bin/tsc",
|
|
||||||
"tsserver": "bin/tsserver"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/urlpattern-polyfill": {
|
|
||||||
"version": "8.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/urlpattern-polyfill/-/urlpattern-polyfill-8.0.2.tgz",
|
|
||||||
"integrity": "sha512-Qp95D4TPJl1kC9SKigDcqgyM2VDVO4RiJc2d4qe5GrYm+zbIQCWWKAFaJNQ4BhdFeDGwBmAxqJBwWSJDb9T3BQ==",
|
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Vendored
-12
@@ -1,12 +0,0 @@
|
|||||||
declare module 'main' {
|
|
||||||
export function filterFileName(): I32;
|
|
||||||
export function actionAddToAlbum(): I32;
|
|
||||||
export function actionArchive(): I32;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module 'extism:host' {
|
|
||||||
interface user {
|
|
||||||
updateAsset(ptr: PTR): I32;
|
|
||||||
addAssetToAlbum(ptr: PTR): I32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
const { updateAsset, addAssetToAlbum } = Host.getFunctions();
|
|
||||||
|
|
||||||
function parseInput() {
|
|
||||||
return JSON.parse(Host.inputString());
|
|
||||||
}
|
|
||||||
|
|
||||||
function returnOutput(output: any) {
|
|
||||||
Host.outputString(JSON.stringify(output));
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function filterFileName() {
|
|
||||||
const input = parseInput();
|
|
||||||
const { data, config } = input;
|
|
||||||
const { pattern, matchType = 'contains', caseSensitive = false } = config;
|
|
||||||
|
|
||||||
const fileName = data.asset.originalFileName || data.asset.fileName || '';
|
|
||||||
const searchName = caseSensitive ? fileName : fileName.toLowerCase();
|
|
||||||
const searchPattern = caseSensitive ? pattern : pattern.toLowerCase();
|
|
||||||
|
|
||||||
let passed = false;
|
|
||||||
|
|
||||||
if (matchType === 'exact') {
|
|
||||||
passed = searchName === searchPattern;
|
|
||||||
} else if (matchType === 'regex') {
|
|
||||||
const flags = caseSensitive ? '' : 'i';
|
|
||||||
const regex = new RegExp(searchPattern, flags);
|
|
||||||
passed = regex.test(fileName);
|
|
||||||
} else {
|
|
||||||
// contains
|
|
||||||
passed = searchName.includes(searchPattern);
|
|
||||||
}
|
|
||||||
|
|
||||||
return returnOutput({ passed });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function actionAddToAlbum() {
|
|
||||||
const input = parseInput();
|
|
||||||
const { authToken, config, data } = input;
|
|
||||||
const { albumId } = config;
|
|
||||||
|
|
||||||
const ptr = Memory.fromString(
|
|
||||||
JSON.stringify({
|
|
||||||
authToken,
|
|
||||||
assetId: data.asset.id,
|
|
||||||
albumId: albumId,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
addAssetToAlbum(ptr.offset);
|
|
||||||
ptr.free();
|
|
||||||
|
|
||||||
return returnOutput({ success: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
export function actionArchive() {
|
|
||||||
const input = parseInput();
|
|
||||||
const { authToken, data } = input;
|
|
||||||
const ptr = Memory.fromString(
|
|
||||||
JSON.stringify({
|
|
||||||
authToken,
|
|
||||||
id: data.asset.id,
|
|
||||||
visibility: 'archive',
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
|
|
||||||
updateAsset(ptr.offset);
|
|
||||||
ptr.free();
|
|
||||||
|
|
||||||
return returnOutput({ success: true });
|
|
||||||
}
|
|
||||||
+150
-141
@@ -1478,72 +1478,33 @@ export type PersonStatisticsResponseDto = {
|
|||||||
/** Number of assets */
|
/** Number of assets */
|
||||||
assets: number;
|
assets: number;
|
||||||
};
|
};
|
||||||
export type PluginJsonSchemaProperty = {
|
export type PluginMethodResponseDto = {
|
||||||
additionalProperties?: boolean | PluginJsonSchemaProperty;
|
/** Description */
|
||||||
"default"?: any;
|
|
||||||
description?: string;
|
|
||||||
"enum"?: string[];
|
|
||||||
items?: PluginJsonSchemaProperty;
|
|
||||||
properties?: {
|
|
||||||
[key: string]: PluginJsonSchemaProperty;
|
|
||||||
};
|
|
||||||
required?: string[];
|
|
||||||
"type"?: PluginJsonSchemaType;
|
|
||||||
};
|
|
||||||
export type PluginJsonSchema = {
|
|
||||||
additionalProperties?: boolean;
|
|
||||||
description?: string;
|
|
||||||
properties?: {
|
|
||||||
[key: string]: PluginJsonSchemaProperty;
|
|
||||||
};
|
|
||||||
required?: string[];
|
|
||||||
"type"?: PluginJsonSchemaType;
|
|
||||||
};
|
|
||||||
export type PluginActionResponseDto = {
|
|
||||||
/** Action description */
|
|
||||||
description: string;
|
description: string;
|
||||||
/** Action ID */
|
hostFunctions: boolean;
|
||||||
id: string;
|
/** Key */
|
||||||
/** Method name */
|
key: string;
|
||||||
methodName: string;
|
/** Name */
|
||||||
/** Plugin ID */
|
name: string;
|
||||||
pluginId: string;
|
schema?: {};
|
||||||
/** Action schema */
|
/** Title */
|
||||||
schema: (PluginJsonSchema) | null;
|
|
||||||
/** Supported contexts */
|
|
||||||
supportedContexts: PluginContextType[];
|
|
||||||
/** Action title */
|
|
||||||
title: string;
|
|
||||||
};
|
|
||||||
export type PluginFilterResponseDto = {
|
|
||||||
/** Filter description */
|
|
||||||
description: string;
|
|
||||||
/** Filter ID */
|
|
||||||
id: string;
|
|
||||||
/** Method name */
|
|
||||||
methodName: string;
|
|
||||||
/** Plugin ID */
|
|
||||||
pluginId: string;
|
|
||||||
/** Filter schema */
|
|
||||||
schema: (PluginJsonSchema) | null;
|
|
||||||
/** Supported contexts */
|
|
||||||
supportedContexts: PluginContextType[];
|
|
||||||
/** Filter title */
|
|
||||||
title: string;
|
title: string;
|
||||||
|
/** Workflow types */
|
||||||
|
types: WorkflowType[];
|
||||||
|
/** Ui hints */
|
||||||
|
uiHints: string[];
|
||||||
};
|
};
|
||||||
export type PluginResponseDto = {
|
export type PluginResponseDto = {
|
||||||
/** Plugin actions */
|
|
||||||
actions: PluginActionResponseDto[];
|
|
||||||
/** Plugin author */
|
/** Plugin author */
|
||||||
author: string;
|
author: string;
|
||||||
/** Creation date */
|
/** Creation date */
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
/** Plugin description */
|
/** Plugin description */
|
||||||
description: string;
|
description: string;
|
||||||
/** Plugin filters */
|
|
||||||
filters: PluginFilterResponseDto[];
|
|
||||||
/** Plugin ID */
|
/** Plugin ID */
|
||||||
id: string;
|
id: string;
|
||||||
|
/** Plugin methods */
|
||||||
|
methods: PluginMethodResponseDto[];
|
||||||
/** Plugin name */
|
/** Plugin name */
|
||||||
name: string;
|
name: string;
|
||||||
/** Plugin title */
|
/** Plugin title */
|
||||||
@@ -1553,10 +1514,6 @@ export type PluginResponseDto = {
|
|||||||
/** Plugin version */
|
/** Plugin version */
|
||||||
version: string;
|
version: string;
|
||||||
};
|
};
|
||||||
export type PluginTriggerResponseDto = {
|
|
||||||
contextType: PluginContextType;
|
|
||||||
"type": PluginTriggerType;
|
|
||||||
};
|
|
||||||
export type QueueResponseDto = {
|
export type QueueResponseDto = {
|
||||||
/** Whether the queue is paused */
|
/** Whether the queue is paused */
|
||||||
isPaused: boolean;
|
isPaused: boolean;
|
||||||
@@ -2710,89 +2667,81 @@ export type CreateProfileImageResponseDto = {
|
|||||||
/** User ID */
|
/** User ID */
|
||||||
userId: string;
|
userId: string;
|
||||||
};
|
};
|
||||||
export type PluginConfigValue = any;
|
export type WorkflowStepDto = {
|
||||||
export type WorkflowActionConfig = {
|
/** Step configuration */
|
||||||
[key: string]: PluginConfigValue;
|
config: {
|
||||||
};
|
[key: string]: any;
|
||||||
export type WorkflowActionResponseDto = {
|
} | null;
|
||||||
actionConfig: (WorkflowActionConfig) | null;
|
/** Step is enabled */
|
||||||
/** Action ID */
|
enabled?: boolean;
|
||||||
id: string;
|
/** Step plugin method */
|
||||||
/** Action order */
|
method: string;
|
||||||
order: number;
|
|
||||||
/** Plugin action ID */
|
|
||||||
pluginActionId: string;
|
|
||||||
/** Workflow ID */
|
|
||||||
workflowId: string;
|
|
||||||
};
|
|
||||||
export type WorkflowFilterConfig = {
|
|
||||||
[key: string]: PluginConfigValue;
|
|
||||||
};
|
|
||||||
export type WorkflowFilterResponseDto = {
|
|
||||||
filterConfig: (WorkflowFilterConfig) | null;
|
|
||||||
/** Filter ID */
|
|
||||||
id: string;
|
|
||||||
/** Filter order */
|
|
||||||
order: number;
|
|
||||||
/** Plugin filter ID */
|
|
||||||
pluginFilterId: string;
|
|
||||||
/** Workflow ID */
|
|
||||||
workflowId: string;
|
|
||||||
};
|
};
|
||||||
export type WorkflowResponseDto = {
|
export type WorkflowResponseDto = {
|
||||||
/** Workflow actions */
|
|
||||||
actions: WorkflowActionResponseDto[];
|
|
||||||
/** Creation date */
|
/** Creation date */
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
/** Workflow description */
|
/** Workflow description */
|
||||||
description: string;
|
description: string | null;
|
||||||
/** Workflow enabled */
|
/** Workflow enabled */
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
/** Workflow filters */
|
|
||||||
filters: WorkflowFilterResponseDto[];
|
|
||||||
/** Workflow ID */
|
/** Workflow ID */
|
||||||
id: string;
|
id: string;
|
||||||
/** Workflow name */
|
/** Workflow name */
|
||||||
name: string | null;
|
name: string | null;
|
||||||
/** Owner user ID */
|
/** Workflow steps */
|
||||||
ownerId: string;
|
steps: WorkflowStepDto[];
|
||||||
triggerType: PluginTriggerType;
|
/** Workflow trigger type */
|
||||||
};
|
trigger: WorkflowTrigger;
|
||||||
export type WorkflowActionItemDto = {
|
/** Update date */
|
||||||
actionConfig?: WorkflowActionConfig;
|
updatedAt: string;
|
||||||
/** Plugin action ID */
|
|
||||||
pluginActionId: string;
|
|
||||||
};
|
|
||||||
export type WorkflowFilterItemDto = {
|
|
||||||
filterConfig?: WorkflowFilterConfig;
|
|
||||||
/** Plugin filter ID */
|
|
||||||
pluginFilterId: string;
|
|
||||||
};
|
};
|
||||||
export type WorkflowCreateDto = {
|
export type WorkflowCreateDto = {
|
||||||
/** Workflow actions */
|
|
||||||
actions: WorkflowActionItemDto[];
|
|
||||||
/** Workflow description */
|
/** Workflow description */
|
||||||
description?: string;
|
description?: string | null;
|
||||||
/** Workflow enabled */
|
/** Workflow enabled */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
/** Workflow filters */
|
|
||||||
filters: WorkflowFilterItemDto[];
|
|
||||||
/** Workflow name */
|
/** Workflow name */
|
||||||
name: string;
|
name?: string | null;
|
||||||
triggerType: PluginTriggerType;
|
steps?: WorkflowStepDto[];
|
||||||
|
/** Workflow trigger type */
|
||||||
|
trigger: WorkflowTrigger;
|
||||||
|
};
|
||||||
|
export type WorkflowTriggerResponseDto = {
|
||||||
|
/** Trigger type */
|
||||||
|
trigger: WorkflowTrigger;
|
||||||
|
/** Workflow types */
|
||||||
|
types: WorkflowType[];
|
||||||
};
|
};
|
||||||
export type WorkflowUpdateDto = {
|
export type WorkflowUpdateDto = {
|
||||||
/** Workflow actions */
|
|
||||||
actions?: WorkflowActionItemDto[];
|
|
||||||
/** Workflow description */
|
/** Workflow description */
|
||||||
description?: string;
|
description?: string | null;
|
||||||
/** Workflow enabled */
|
/** Workflow enabled */
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
/** Workflow filters */
|
|
||||||
filters?: WorkflowFilterItemDto[];
|
|
||||||
/** Workflow name */
|
/** Workflow name */
|
||||||
name?: string;
|
name?: string | null;
|
||||||
triggerType?: PluginTriggerType;
|
steps?: WorkflowStepDto[];
|
||||||
|
/** Workflow trigger type */
|
||||||
|
trigger?: WorkflowTrigger;
|
||||||
|
};
|
||||||
|
export type WorkflowShareStepDto = {
|
||||||
|
/** Step configuration */
|
||||||
|
config: {
|
||||||
|
[key: string]: any;
|
||||||
|
} | null;
|
||||||
|
/** Step is enabled */
|
||||||
|
enabled?: boolean;
|
||||||
|
/** Step plugin method */
|
||||||
|
method: string;
|
||||||
|
};
|
||||||
|
export type WorkflowShareResponseDto = {
|
||||||
|
/** Workflow description */
|
||||||
|
description: string | null;
|
||||||
|
/** Workflow name */
|
||||||
|
name: string | null;
|
||||||
|
/** Workflow steps */
|
||||||
|
steps: WorkflowShareStepDto[];
|
||||||
|
/** Workflow trigger type */
|
||||||
|
trigger: WorkflowTrigger;
|
||||||
};
|
};
|
||||||
export type LicenseResponseDto = UserLicense;
|
export type LicenseResponseDto = UserLicense;
|
||||||
export type SyncAckV1 = {};
|
export type SyncAckV1 = {};
|
||||||
@@ -5240,22 +5189,56 @@ export function getPersonThumbnail({ id }: {
|
|||||||
/**
|
/**
|
||||||
* List all plugins
|
* List all plugins
|
||||||
*/
|
*/
|
||||||
export function getPlugins(opts?: Oazapfts.RequestOpts) {
|
export function searchPlugins({ description, enabled, id, name, title, version }: {
|
||||||
|
description?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
title?: string;
|
||||||
|
version?: string;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: PluginResponseDto[];
|
data: PluginResponseDto[];
|
||||||
}>("/plugins", {
|
}>(`/plugins${QS.query(QS.explode({
|
||||||
|
description,
|
||||||
|
enabled,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
title,
|
||||||
|
version
|
||||||
|
}))}`, {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* List all plugin triggers
|
* Retrieve plugin methods
|
||||||
*/
|
*/
|
||||||
export function getPluginTriggers(opts?: Oazapfts.RequestOpts) {
|
export function searchPluginMethods({ description, enabled, id, name, pluginName, pluginVersion, title, trigger, $type }: {
|
||||||
|
description?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
pluginName?: string;
|
||||||
|
pluginVersion?: string;
|
||||||
|
title?: string;
|
||||||
|
trigger?: WorkflowTrigger;
|
||||||
|
$type?: WorkflowType;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: PluginTriggerResponseDto[];
|
data: PluginMethodResponseDto[];
|
||||||
}>("/plugins/triggers", {
|
}>(`/plugins/methods${QS.query(QS.explode({
|
||||||
|
description,
|
||||||
|
enabled,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
pluginName,
|
||||||
|
pluginVersion,
|
||||||
|
title,
|
||||||
|
trigger,
|
||||||
|
"type": $type
|
||||||
|
}))}`, {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -6631,11 +6614,23 @@ export function getUniqueOriginalPaths(opts?: Oazapfts.RequestOpts) {
|
|||||||
/**
|
/**
|
||||||
* List all workflows
|
* List all workflows
|
||||||
*/
|
*/
|
||||||
export function getWorkflows(opts?: Oazapfts.RequestOpts) {
|
export function searchWorkflows({ description, enabled, id, name, trigger }: {
|
||||||
|
description?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
id?: string;
|
||||||
|
name?: string;
|
||||||
|
trigger?: WorkflowTrigger;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
return oazapfts.ok(oazapfts.fetchJson<{
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
status: 200;
|
status: 200;
|
||||||
data: WorkflowResponseDto[];
|
data: WorkflowResponseDto[];
|
||||||
}>("/workflows", {
|
}>(`/workflows${QS.query(QS.explode({
|
||||||
|
description,
|
||||||
|
enabled,
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
trigger
|
||||||
|
}))}`, {
|
||||||
...opts
|
...opts
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -6654,6 +6649,17 @@ export function createWorkflow({ workflowCreateDto }: {
|
|||||||
body: workflowCreateDto
|
body: workflowCreateDto
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* List all workflow triggers
|
||||||
|
*/
|
||||||
|
export function getWorkflowTriggers(opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: WorkflowTriggerResponseDto[];
|
||||||
|
}>("/workflows/triggers", {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Delete a workflow
|
* Delete a workflow
|
||||||
*/
|
*/
|
||||||
@@ -6694,6 +6700,19 @@ export function updateWorkflow({ id, workflowUpdateDto }: {
|
|||||||
body: workflowUpdateDto
|
body: workflowUpdateDto
|
||||||
})));
|
})));
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve a workflow
|
||||||
|
*/
|
||||||
|
export function getWorkflowForShare({ id }: {
|
||||||
|
id: string;
|
||||||
|
}, opts?: Oazapfts.RequestOpts) {
|
||||||
|
return oazapfts.ok(oazapfts.fetchJson<{
|
||||||
|
status: 200;
|
||||||
|
data: WorkflowShareResponseDto;
|
||||||
|
}>(`/workflows/${encodeURIComponent(id)}/share`, {
|
||||||
|
...opts
|
||||||
|
}));
|
||||||
|
}
|
||||||
export enum ReactionLevel {
|
export enum ReactionLevel {
|
||||||
Album = "album",
|
Album = "album",
|
||||||
Asset = "asset"
|
Asset = "asset"
|
||||||
@@ -7017,21 +7036,11 @@ export enum PartnerDirection {
|
|||||||
SharedBy = "shared-by",
|
SharedBy = "shared-by",
|
||||||
SharedWith = "shared-with"
|
SharedWith = "shared-with"
|
||||||
}
|
}
|
||||||
export enum PluginJsonSchemaType {
|
export enum WorkflowType {
|
||||||
String = "string",
|
AssetV1 = "AssetV1",
|
||||||
Number = "number",
|
AssetPersonV1 = "AssetPersonV1"
|
||||||
Integer = "integer",
|
|
||||||
Boolean = "boolean",
|
|
||||||
Object = "object",
|
|
||||||
Array = "array",
|
|
||||||
Null = "null"
|
|
||||||
}
|
}
|
||||||
export enum PluginContextType {
|
export enum WorkflowTrigger {
|
||||||
Asset = "asset",
|
|
||||||
Album = "album",
|
|
||||||
Person = "person"
|
|
||||||
}
|
|
||||||
export enum PluginTriggerType {
|
|
||||||
AssetCreate = "AssetCreate",
|
AssetCreate = "AssetCreate",
|
||||||
PersonRecognized = "PersonRecognized"
|
PersonRecognized = "PersonRecognized"
|
||||||
}
|
}
|
||||||
@@ -7098,7 +7107,7 @@ export enum JobName {
|
|||||||
VersionCheck = "VersionCheck",
|
VersionCheck = "VersionCheck",
|
||||||
OcrQueueAll = "OcrQueueAll",
|
OcrQueueAll = "OcrQueueAll",
|
||||||
Ocr = "Ocr",
|
Ocr = "Ocr",
|
||||||
WorkflowRun = "WorkflowRun"
|
WorkflowAssetCreate = "WorkflowAssetCreate"
|
||||||
}
|
}
|
||||||
export enum SearchSuggestionType {
|
export enum SearchSuggestionType {
|
||||||
Country = "country",
|
Country = "country",
|
||||||
|
|||||||
Generated
+75
-1
@@ -312,11 +312,14 @@ importers:
|
|||||||
specifier: ^4.20.6
|
specifier: ^4.20.6
|
||||||
version: 4.21.0
|
version: 4.21.0
|
||||||
|
|
||||||
packages/plugins:
|
packages/plugin-core:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@extism/js-pdk':
|
'@extism/js-pdk':
|
||||||
specifier: ^1.0.1
|
specifier: ^1.0.1
|
||||||
version: 1.1.1
|
version: 1.1.1
|
||||||
|
'@immich/plugin-sdk':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../plugin-sdk
|
||||||
esbuild:
|
esbuild:
|
||||||
specifier: ^0.28.0
|
specifier: ^0.28.0
|
||||||
version: 0.28.0
|
version: 0.28.0
|
||||||
@@ -324,6 +327,24 @@ importers:
|
|||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.3
|
version: 6.0.3
|
||||||
|
|
||||||
|
packages/plugin-sdk:
|
||||||
|
devDependencies:
|
||||||
|
'@extism/js-pdk':
|
||||||
|
specifier: ^1.1.1
|
||||||
|
version: 1.1.1
|
||||||
|
'@types/node':
|
||||||
|
specifier: ^24.11.0
|
||||||
|
version: 24.12.2
|
||||||
|
esbuild:
|
||||||
|
specifier: ^0.27.3
|
||||||
|
version: 0.27.4
|
||||||
|
tsc-alias:
|
||||||
|
specifier: ^1.8.16
|
||||||
|
version: 1.8.16
|
||||||
|
typescript:
|
||||||
|
specifier: ^5.9.3
|
||||||
|
version: 5.9.3
|
||||||
|
|
||||||
packages/sdk:
|
packages/sdk:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@oazapfts/runtime':
|
'@oazapfts/runtime':
|
||||||
@@ -342,6 +363,9 @@ importers:
|
|||||||
'@extism/extism':
|
'@extism/extism':
|
||||||
specifier: 2.0.0-rc13
|
specifier: 2.0.0-rc13
|
||||||
version: 2.0.0-rc13
|
version: 2.0.0-rc13
|
||||||
|
'@immich/plugin-sdk':
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../packages/plugin-sdk
|
||||||
'@immich/sql-tools':
|
'@immich/sql-tools':
|
||||||
specifier: ^0.5.1
|
specifier: ^0.5.1
|
||||||
version: 0.5.2
|
version: 0.5.2
|
||||||
@@ -453,6 +477,9 @@ importers:
|
|||||||
fluent-ffmpeg:
|
fluent-ffmpeg:
|
||||||
specifier: ^2.1.2
|
specifier: ^2.1.2
|
||||||
version: 2.1.3
|
version: 2.1.3
|
||||||
|
generic-pool:
|
||||||
|
specifier: ^3.9.0
|
||||||
|
version: 3.9.0
|
||||||
geo-tz:
|
geo-tz:
|
||||||
specifier: ^8.0.0
|
specifier: ^8.0.0
|
||||||
version: 8.1.6
|
version: 8.1.6
|
||||||
@@ -6446,6 +6473,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
|
||||||
engines: {node: '>= 12'}
|
engines: {node: '>= 12'}
|
||||||
|
|
||||||
|
commander@9.5.0:
|
||||||
|
resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
|
||||||
|
engines: {node: ^12.20.0 || >=14}
|
||||||
|
|
||||||
comment-json@5.0.0:
|
comment-json@5.0.0:
|
||||||
resolution: {integrity: sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==}
|
resolution: {integrity: sha512-uiqLcOiVDJtBP8WGkZHEP+FZIhTzP1dxvn59EfoYUi9gqupjrBWVQkO2atDrbnKPwLeotFYDsuNb26uBMqB+hw==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@@ -7817,6 +7848,10 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
deprecated: This package is no longer supported.
|
deprecated: This package is no longer supported.
|
||||||
|
|
||||||
|
generic-pool@3.9.0:
|
||||||
|
resolution: {integrity: sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==}
|
||||||
|
engines: {node: '>= 4'}
|
||||||
|
|
||||||
gensync@1.0.0-beta.2:
|
gensync@1.0.0-beta.2:
|
||||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -9462,6 +9497,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
|
resolution: {integrity: sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA==}
|
||||||
engines: {node: ^18.17.0 || >=20.5.0}
|
engines: {node: ^18.17.0 || >=20.5.0}
|
||||||
|
|
||||||
|
mylas@2.1.14:
|
||||||
|
resolution: {integrity: sha512-BzQguy9W9NJgoVn2mRWzbFrFWWztGCcng2QI9+41frfk+Athwgx3qhqhvStz7ExeUUu7Kzw427sNzHpEZNINog==}
|
||||||
|
engines: {node: '>=16.0.0'}
|
||||||
|
|
||||||
mz@2.7.0:
|
mz@2.7.0:
|
||||||
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
|
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
|
||||||
|
|
||||||
@@ -10000,6 +10039,10 @@ packages:
|
|||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
plimit-lit@1.6.1:
|
||||||
|
resolution: {integrity: sha512-B7+VDyb8Tl6oMJT9oSO2CW8XC/T4UcJGrwOVoNGwOQsQYhlpfajmrMj5xeejqaASq3V/EqThyOeATEOMuSEXiA==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
pluralize@8.0.0:
|
pluralize@8.0.0:
|
||||||
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -10642,6 +10685,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
|
resolution: {integrity: sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==}
|
||||||
engines: {node: '>=0.6'}
|
engines: {node: '>=0.6'}
|
||||||
|
|
||||||
|
queue-lit@1.5.2:
|
||||||
|
resolution: {integrity: sha512-tLc36IOPeMAubu8BkW8YDBV+WyIgKlYU7zUNs0J5Vk9skSZ4JfGlPOqplP0aHdfv7HL0B2Pg6nwiq60Qc6M2Hw==}
|
||||||
|
engines: {node: '>=12'}
|
||||||
|
|
||||||
queue-microtask@1.2.3:
|
queue-microtask@1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
@@ -11868,6 +11915,11 @@ packages:
|
|||||||
ts-interface-checker@0.1.13:
|
ts-interface-checker@0.1.13:
|
||||||
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
|
||||||
|
|
||||||
|
tsc-alias@1.8.16:
|
||||||
|
resolution: {integrity: sha512-QjCyu55NFyRSBAl6+MTFwplpFcnm2Pq01rR/uxfqJoLMm6X3O14KEGtaSDZpJYaE1bJBGDjD0eSuiIWPe2T58g==}
|
||||||
|
engines: {node: '>=16.20.2'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
tsconfck@3.1.6:
|
tsconfck@3.1.6:
|
||||||
resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
|
resolution: {integrity: sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==}
|
||||||
engines: {node: ^18 || >=20}
|
engines: {node: ^18 || >=20}
|
||||||
@@ -18998,6 +19050,8 @@ snapshots:
|
|||||||
|
|
||||||
commander@8.3.0: {}
|
commander@8.3.0: {}
|
||||||
|
|
||||||
|
commander@9.5.0: {}
|
||||||
|
|
||||||
comment-json@5.0.0:
|
comment-json@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
array-timsort: 1.0.3
|
array-timsort: 1.0.3
|
||||||
@@ -20643,6 +20697,8 @@ snapshots:
|
|||||||
strip-ansi: 6.0.1
|
strip-ansi: 6.0.1
|
||||||
wide-align: 1.1.5
|
wide-align: 1.1.5
|
||||||
|
|
||||||
|
generic-pool@3.9.0: {}
|
||||||
|
|
||||||
gensync@1.0.0-beta.2: {}
|
gensync@1.0.0-beta.2: {}
|
||||||
|
|
||||||
geo-coordinates-parser@1.7.4: {}
|
geo-coordinates-parser@1.7.4: {}
|
||||||
@@ -22684,6 +22740,8 @@ snapshots:
|
|||||||
|
|
||||||
mute-stream@2.0.0: {}
|
mute-stream@2.0.0: {}
|
||||||
|
|
||||||
|
mylas@2.1.14: {}
|
||||||
|
|
||||||
mz@2.7.0:
|
mz@2.7.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
@@ -23241,6 +23299,10 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents: 2.3.2
|
fsevents: 2.3.2
|
||||||
|
|
||||||
|
plimit-lit@1.6.1:
|
||||||
|
dependencies:
|
||||||
|
queue-lit: 1.5.2
|
||||||
|
|
||||||
pluralize@8.0.0: {}
|
pluralize@8.0.0: {}
|
||||||
|
|
||||||
pmtiles@3.2.1:
|
pmtiles@3.2.1:
|
||||||
@@ -23925,6 +23987,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
side-channel: 1.1.0
|
side-channel: 1.1.0
|
||||||
|
|
||||||
|
queue-lit@1.5.2: {}
|
||||||
|
|
||||||
queue-microtask@1.2.3: {}
|
queue-microtask@1.2.3: {}
|
||||||
|
|
||||||
quick-lru@5.1.1: {}
|
quick-lru@5.1.1: {}
|
||||||
@@ -25518,6 +25582,16 @@ snapshots:
|
|||||||
|
|
||||||
ts-interface-checker@0.1.13: {}
|
ts-interface-checker@0.1.13: {}
|
||||||
|
|
||||||
|
tsc-alias@1.8.16:
|
||||||
|
dependencies:
|
||||||
|
chokidar: 3.6.0
|
||||||
|
commander: 9.5.0
|
||||||
|
get-tsconfig: 4.13.0
|
||||||
|
globby: 11.1.0
|
||||||
|
mylas: 2.1.14
|
||||||
|
normalize-path: 3.0.0
|
||||||
|
plimit-lit: 1.6.1
|
||||||
|
|
||||||
tsconfck@3.1.6(typescript@6.0.3):
|
tsconfck@3.1.6(typescript@6.0.3):
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
typescript: 6.0.3
|
typescript: 6.0.3
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ packages:
|
|||||||
- plugins
|
- plugins
|
||||||
- web
|
- web
|
||||||
- .github
|
- .github
|
||||||
|
- packages/*
|
||||||
ignoredBuiltDependencies:
|
ignoredBuiltDependencies:
|
||||||
- '@nestjs/core'
|
- '@nestjs/core'
|
||||||
- '@parcel/watcher'
|
- '@parcel/watcher'
|
||||||
|
|||||||
+15
-10
@@ -13,12 +13,13 @@ FROM builder AS server
|
|||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /usr/src/app
|
||||||
COPY ./server ./server/
|
COPY ./server ./server/
|
||||||
|
COPY ./packages/plugin-sdk ./packages/plugin-sdk/
|
||||||
RUN --mount=type=cache,id=pnpm-server,target=/buildcache/pnpm-store \
|
RUN --mount=type=cache,id=pnpm-server,target=/buildcache/pnpm-store \
|
||||||
--mount=type=bind,source=package.json,target=package.json \
|
--mount=type=bind,source=package.json,target=package.json \
|
||||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
||||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
||||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
||||||
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile build && \
|
SHARP_IGNORE_GLOBAL_LIBVIPS=true pnpm --filter immich --filter @immich/plugin-sdk --frozen-lockfile build && \
|
||||||
SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned
|
SHARP_FORCE_GLOBAL_LIBVIPS=true pnpm --filter immich --frozen-lockfile --prod --no-optional deploy /output/server-pruned
|
||||||
|
|
||||||
FROM builder AS web
|
FROM builder AS web
|
||||||
@@ -56,22 +57,26 @@ ARG TARGETPLATFORM
|
|||||||
|
|
||||||
COPY --from=ghcr.io/jdx/mise:2026.3.12@sha256:0210678cbf58413806531a27adb2c7daf1c37238e56e8f7ea381d73521571775 /usr/local/bin/mise /usr/local/bin/mise
|
COPY --from=ghcr.io/jdx/mise:2026.3.12@sha256:0210678cbf58413806531a27adb2c7daf1c37238e56e8f7ea381d73521571775 /usr/local/bin/mise /usr/local/bin/mise
|
||||||
|
|
||||||
WORKDIR /usr/src/app
|
WORKDIR /app
|
||||||
COPY ./packages/plugins/mise.toml ./packages/plugins/
|
COPY ./mise.toml ./mise.toml
|
||||||
ENV MISE_TRUSTED_CONFIG_PATHS=/usr/src/app/packages/plugins/mise.toml
|
COPY ./packages/plugin-core/mise.toml ./packages/plugin-core/
|
||||||
|
ENV MISE_TRUSTED_CONFIG_PATHS=/app/mise.toml
|
||||||
ENV MISE_DATA_DIR=/buildcache/mise
|
ENV MISE_DATA_DIR=/buildcache/mise
|
||||||
|
ENV MISE_DISABLE_TOOLS=flutter
|
||||||
RUN --mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
RUN --mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
||||||
mise install --cd packages/plugins
|
mise install
|
||||||
|
|
||||||
|
COPY ./packages/plugin-core ./packages/plugin-core/
|
||||||
|
COPY ./packages/plugin-sdk ./packages/plugin-sdk/
|
||||||
|
|
||||||
COPY ./packages/plugins ./packages/plugins/
|
|
||||||
# Build plugins
|
# Build plugins
|
||||||
RUN --mount=type=cache,id=pnpm-plugins,target=/buildcache/pnpm-store \
|
RUN --mount=type=cache,id=pnpm-packages,target=/buildcache/pnpm-store \
|
||||||
--mount=type=bind,source=package.json,target=package.json \
|
--mount=type=bind,source=package.json,target=package.json \
|
||||||
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
--mount=type=bind,source=.pnpmfile.cjs,target=.pnpmfile.cjs \
|
||||||
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
--mount=type=bind,source=pnpm-lock.yaml,target=pnpm-lock.yaml \
|
||||||
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
--mount=type=bind,source=pnpm-workspace.yaml,target=pnpm-workspace.yaml \
|
||||||
--mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
--mount=type=cache,id=mise-tools-${TARGETPLATFORM},target=/buildcache/mise \
|
||||||
cd packages/plugins && mise run build
|
mise //:plugins
|
||||||
|
|
||||||
FROM ghcr.io/immich-app/base-server-prod:202605051129@sha256:50f7ffe4ed31e360c90c4905bd5f6658f2a121297544e3fe9368e338b3f76bcd
|
FROM ghcr.io/immich-app/base-server-prod:202605051129@sha256:50f7ffe4ed31e360c90c4905bd5f6658f2a121297544e3fe9368e338b3f76bcd
|
||||||
|
|
||||||
@@ -83,8 +88,8 @@ ENV NODE_ENV=production \
|
|||||||
COPY --from=server /output/server-pruned ./server
|
COPY --from=server /output/server-pruned ./server
|
||||||
COPY --from=web /usr/src/app/web/build /build/www
|
COPY --from=web /usr/src/app/web/build /build/www
|
||||||
COPY --from=cli /output/cli-pruned ./cli
|
COPY --from=cli /output/cli-pruned ./cli
|
||||||
COPY --from=plugins /usr/src/app/packages/plugins/dist /build/corePlugin/dist
|
COPY --from=plugins /app/packages/plugin-core/dist /build/plugins/immich-core-plugin/dist
|
||||||
COPY --from=plugins /usr/src/app/packages/plugins/manifest.json /build/corePlugin/manifest.json
|
COPY --from=plugins /app/packages/plugin-core/manifest.json /build/plugins/immich-core-plugin/manifest.json
|
||||||
RUN ln -s ../../cli/bin/immich server/bin/immich
|
RUN ln -s ../../cli/bin/immich server/bin/immich
|
||||||
COPY LICENSE /licenses/LICENSE.txt
|
COPY LICENSE /licenses/LICENSE.txt
|
||||||
COPY LICENSE /LICENSE
|
COPY LICENSE /LICENSE
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ run = "email dev -p 3050 --dir src/emails"
|
|||||||
[tasks.ci-unit]
|
[tasks.ci-unit]
|
||||||
run = [
|
run = [
|
||||||
{ task = ":install" },
|
{ task = ":install" },
|
||||||
|
{ task = "//:plugins" },
|
||||||
{ task = ":format" },
|
{ task = ":format" },
|
||||||
{ task = ":lint" },
|
{ task = ":lint" },
|
||||||
{ task = ":check" },
|
{ task = ":check" },
|
||||||
@@ -67,6 +68,7 @@ run = [
|
|||||||
[tasks.ci-medium]
|
[tasks.ci-medium]
|
||||||
run = [
|
run = [
|
||||||
{ task = ":install" },
|
{ task = ":install" },
|
||||||
|
{ task = "//packages/plugin-core:build" },
|
||||||
{ task = ":test-medium --run" },
|
{ task = ":test-medium --run" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@extism/extism": "2.0.0-rc13",
|
"@extism/extism": "2.0.0-rc13",
|
||||||
|
"@immich/plugin-sdk": "workspace:*",
|
||||||
"@immich/sql-tools": "^0.5.1",
|
"@immich/sql-tools": "^0.5.1",
|
||||||
"@nestjs/bullmq": "^11.0.1",
|
"@nestjs/bullmq": "^11.0.1",
|
||||||
"@nestjs/common": "^11.0.4",
|
"@nestjs/common": "^11.0.4",
|
||||||
@@ -74,6 +75,7 @@
|
|||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"fast-glob": "^3.3.2",
|
"fast-glob": "^3.3.2",
|
||||||
"fluent-ffmpeg": "^2.1.2",
|
"fluent-ffmpeg": "^2.1.2",
|
||||||
|
"generic-pool": "^3.9.0",
|
||||||
"geo-tz": "^8.0.0",
|
"geo-tz": "^8.0.0",
|
||||||
"handlebars": "^4.7.8",
|
"handlebars": "^4.7.8",
|
||||||
"helmet": "^8.1.0",
|
"helmet": "^8.1.0",
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { PluginController } from 'src/controllers/plugin.controller';
|
||||||
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { PluginService } from 'src/services/plugin.service';
|
||||||
|
import request from 'supertest';
|
||||||
|
import { errorDto } from 'test/medium/responses';
|
||||||
|
import { factory } from 'test/small.factory';
|
||||||
|
import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils';
|
||||||
|
|
||||||
|
describe(PluginController.name, () => {
|
||||||
|
let ctx: ControllerContext;
|
||||||
|
const service = mockBaseService(PluginService);
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
ctx = await controllerSetup(PluginController, [
|
||||||
|
{ provide: PluginService, useValue: service },
|
||||||
|
{ provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) },
|
||||||
|
]);
|
||||||
|
return () => ctx.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service.resetAllMocks();
|
||||||
|
ctx.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /plugins', () => {
|
||||||
|
it('should be an authenticated route', async () => {
|
||||||
|
await request(ctx.getHttpServer()).get('/plugins');
|
||||||
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require id to be a uuid`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.get(`/plugins`)
|
||||||
|
.query({ id: 'invalid' })
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /plugins/:id', () => {
|
||||||
|
it('should be an authenticated route', async () => {
|
||||||
|
await request(ctx.getHttpServer()).get(`/plugins/${factory.uuid()}`);
|
||||||
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require id to be a uuid`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.get(`/plugins/invalid`)
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,7 +1,12 @@
|
|||||||
import { Controller, Get, Param } from '@nestjs/common';
|
import { Controller, Get, Param, Query } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||||
import { PluginResponseDto, PluginTriggerResponseDto } from 'src/dtos/plugin.dto';
|
import {
|
||||||
|
PluginMethodResponseDto,
|
||||||
|
PluginMethodSearchDto,
|
||||||
|
PluginResponseDto,
|
||||||
|
PluginSearchDto,
|
||||||
|
} from 'src/dtos/plugin.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { Authenticated } from 'src/middleware/auth.guard';
|
import { Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { PluginService } from 'src/services/plugin.service';
|
import { PluginService } from 'src/services/plugin.service';
|
||||||
@@ -12,26 +17,26 @@ import { UUIDParamDto } from 'src/validation';
|
|||||||
export class PluginController {
|
export class PluginController {
|
||||||
constructor(private service: PluginService) {}
|
constructor(private service: PluginService) {}
|
||||||
|
|
||||||
@Get('triggers')
|
|
||||||
@Authenticated({ permission: Permission.PluginRead })
|
|
||||||
@Endpoint({
|
|
||||||
summary: 'List all plugin triggers',
|
|
||||||
description: 'Retrieve a list of all available plugin triggers.',
|
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
|
||||||
})
|
|
||||||
getPluginTriggers(): PluginTriggerResponseDto[] {
|
|
||||||
return this.service.getTriggers();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
@Authenticated({ permission: Permission.PluginRead })
|
@Authenticated({ permission: Permission.PluginRead })
|
||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'List all plugins',
|
summary: 'List all plugins',
|
||||||
description: 'Retrieve a list of plugins available to the authenticated user.',
|
description: 'Retrieve a list of plugins available to the authenticated user.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
getPlugins(): Promise<PluginResponseDto[]> {
|
searchPlugins(@Query() dto: PluginSearchDto): Promise<PluginResponseDto[]> {
|
||||||
return this.service.getAll();
|
return this.service.search(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('methods')
|
||||||
|
@Authenticated({ permission: Permission.PluginRead })
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Retrieve plugin methods',
|
||||||
|
description: 'Retrieve a list of plugin methods',
|
||||||
|
history: HistoryBuilder.v3(),
|
||||||
|
})
|
||||||
|
searchPluginMethods(@Query() dto: PluginMethodSearchDto): Promise<PluginMethodResponseDto[]> {
|
||||||
|
return this.service.searchMethods(dto);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@@ -39,7 +44,7 @@ export class PluginController {
|
|||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'Retrieve a plugin',
|
summary: 'Retrieve a plugin',
|
||||||
description: 'Retrieve information about a specific plugin by its ID.',
|
description: 'Retrieve information about a specific plugin by its ID.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
getPlugin(@Param() { id }: UUIDParamDto): Promise<PluginResponseDto> {
|
getPlugin(@Param() { id }: UUIDParamDto): Promise<PluginResponseDto> {
|
||||||
return this.service.get(id);
|
return this.service.get(id);
|
||||||
|
|||||||
@@ -0,0 +1,113 @@
|
|||||||
|
import { WorkflowController } from 'src/controllers/workflow.controller';
|
||||||
|
import { WorkflowTrigger } from 'src/enum';
|
||||||
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
|
import { WorkflowService } from 'src/services/workflow.service';
|
||||||
|
import request from 'supertest';
|
||||||
|
import { errorDto } from 'test/medium/responses';
|
||||||
|
import { factory } from 'test/small.factory';
|
||||||
|
import { automock, ControllerContext, controllerSetup, mockBaseService } from 'test/utils';
|
||||||
|
|
||||||
|
describe(WorkflowController.name, () => {
|
||||||
|
let ctx: ControllerContext;
|
||||||
|
const service = mockBaseService(WorkflowService);
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
ctx = await controllerSetup(WorkflowController, [
|
||||||
|
{ provide: WorkflowService, useValue: service },
|
||||||
|
{ provide: LoggingRepository, useValue: automock(LoggingRepository, { strict: false }) },
|
||||||
|
]);
|
||||||
|
return () => ctx.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
service.resetAllMocks();
|
||||||
|
ctx.reset();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('POST /workflows', () => {
|
||||||
|
it('should be an authenticated route', async () => {
|
||||||
|
await request(ctx.getHttpServer()).post('/workflows').send({});
|
||||||
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require a valid trigger`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.post(`/workflows`)
|
||||||
|
.send({ trigger: 'invalid' })
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(
|
||||||
|
errorDto.validationError([
|
||||||
|
{ path: ['trigger'], message: expect.stringContaining('Invalid option: expected one of') },
|
||||||
|
]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require a valid enabled value`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.post(`/workflows`)
|
||||||
|
.send({ enabled: 'invalid' })
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(
|
||||||
|
errorDto.validationError([{ path: ['enabled'], message: 'Invalid input: expected boolean, received string' }]),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should not require a name`, async () => {
|
||||||
|
const { status } = await request(ctx.getHttpServer())
|
||||||
|
.post(`/workflows`)
|
||||||
|
.send({ trigger: WorkflowTrigger.AssetCreate })
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(201);
|
||||||
|
expect(service.create).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /workflows', () => {
|
||||||
|
it('should be an authenticated route', async () => {
|
||||||
|
await request(ctx.getHttpServer()).get('/workflows');
|
||||||
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require id to be a uuid`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.get(`/workflows`)
|
||||||
|
.query({ id: 'invalid' })
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('GET /workflows/:id', () => {
|
||||||
|
it('should be an authenticated route', async () => {
|
||||||
|
await request(ctx.getHttpServer()).get(`/workflows/${factory.uuid()}`);
|
||||||
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require id to be a uuid`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.get(`/workflows/invalid`)
|
||||||
|
.set('Authorization', `Bearer token`);
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PUT /workflows/:id', () => {
|
||||||
|
it('should be an authenticated route', async () => {
|
||||||
|
await request(ctx.getHttpServer()).put(`/workflows/${factory.uuid()}`).send({});
|
||||||
|
expect(ctx.authenticate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it(`should require id to be a uuid`, async () => {
|
||||||
|
const { status, body } = await request(ctx.getHttpServer())
|
||||||
|
.put(`/workflows/invalid`)
|
||||||
|
.set('Authorization', `Bearer token`)
|
||||||
|
.send({});
|
||||||
|
expect(status).toBe(400);
|
||||||
|
expect(body).toEqual(errorDto.validationError([{ path: ['id'], message: 'Invalid UUID' }]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,8 +1,15 @@
|
|||||||
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put, Query } from '@nestjs/common';
|
||||||
import { ApiTags } from '@nestjs/swagger';
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
import { Endpoint, HistoryBuilder } from 'src/decorators';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { WorkflowCreateDto, WorkflowResponseDto, WorkflowUpdateDto } from 'src/dtos/workflow.dto';
|
import {
|
||||||
|
WorkflowCreateDto,
|
||||||
|
WorkflowResponseDto,
|
||||||
|
WorkflowSearchDto,
|
||||||
|
WorkflowShareResponseDto,
|
||||||
|
WorkflowTriggerResponseDto,
|
||||||
|
WorkflowUpdateDto,
|
||||||
|
} from 'src/dtos/workflow.dto';
|
||||||
import { Permission } from 'src/enum';
|
import { Permission } from 'src/enum';
|
||||||
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
import { Auth, Authenticated } from 'src/middleware/auth.guard';
|
||||||
import { WorkflowService } from 'src/services/workflow.service';
|
import { WorkflowService } from 'src/services/workflow.service';
|
||||||
@@ -18,7 +25,7 @@ export class WorkflowController {
|
|||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'Create a workflow',
|
summary: 'Create a workflow',
|
||||||
description: 'Create a new workflow, the workflow can also be created with empty filters and actions.',
|
description: 'Create a new workflow, the workflow can also be created with empty filters and actions.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
createWorkflow(@Auth() auth: AuthDto, @Body() dto: WorkflowCreateDto): Promise<WorkflowResponseDto> {
|
createWorkflow(@Auth() auth: AuthDto, @Body() dto: WorkflowCreateDto): Promise<WorkflowResponseDto> {
|
||||||
return this.service.create(auth, dto);
|
return this.service.create(auth, dto);
|
||||||
@@ -29,10 +36,21 @@ export class WorkflowController {
|
|||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'List all workflows',
|
summary: 'List all workflows',
|
||||||
description: 'Retrieve a list of workflows available to the authenticated user.',
|
description: 'Retrieve a list of workflows available to the authenticated user.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
getWorkflows(@Auth() auth: AuthDto): Promise<WorkflowResponseDto[]> {
|
searchWorkflows(@Auth() auth: AuthDto, @Query() dto: WorkflowSearchDto): Promise<WorkflowResponseDto[]> {
|
||||||
return this.service.getAll(auth);
|
return this.service.search(auth, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('triggers')
|
||||||
|
@Authenticated({ permission: false })
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'List all workflow triggers',
|
||||||
|
description: 'Retrieve a list of all available workflow triggers.',
|
||||||
|
history: HistoryBuilder.v3(),
|
||||||
|
})
|
||||||
|
getWorkflowTriggers(): WorkflowTriggerResponseDto[] {
|
||||||
|
return this.service.getTriggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
@@ -40,19 +58,30 @@ export class WorkflowController {
|
|||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'Retrieve a workflow',
|
summary: 'Retrieve a workflow',
|
||||||
description: 'Retrieve information about a specific workflow by its ID.',
|
description: 'Retrieve information about a specific workflow by its ID.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
getWorkflow(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<WorkflowResponseDto> {
|
getWorkflow(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<WorkflowResponseDto> {
|
||||||
return this.service.get(auth, id);
|
return this.service.get(auth, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get(':id/share')
|
||||||
|
@Authenticated({ permission: Permission.WorkflowRead })
|
||||||
|
@Endpoint({
|
||||||
|
summary: 'Retrieve a workflow',
|
||||||
|
description: 'Retrieve a workflow details without ids, default values, etc.',
|
||||||
|
history: HistoryBuilder.v3(),
|
||||||
|
})
|
||||||
|
getWorkflowForShare(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<WorkflowShareResponseDto> {
|
||||||
|
return this.service.share(auth, id);
|
||||||
|
}
|
||||||
|
|
||||||
@Put(':id')
|
@Put(':id')
|
||||||
@Authenticated({ permission: Permission.WorkflowUpdate })
|
@Authenticated({ permission: Permission.WorkflowUpdate })
|
||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'Update a workflow',
|
summary: 'Update a workflow',
|
||||||
description:
|
description:
|
||||||
'Update the information of a specific workflow by its ID. This endpoint can be used to update the workflow name, description, trigger type, filters and actions order, etc.',
|
'Update the information of a specific workflow by its ID. This endpoint can be used to update the workflow name, description, trigger type, filters and actions order, etc.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
updateWorkflow(
|
updateWorkflow(
|
||||||
@Auth() auth: AuthDto,
|
@Auth() auth: AuthDto,
|
||||||
@@ -68,7 +97,7 @@ export class WorkflowController {
|
|||||||
@Endpoint({
|
@Endpoint({
|
||||||
summary: 'Delete a workflow',
|
summary: 'Delete a workflow',
|
||||||
description: 'Delete a workflow by its ID.',
|
description: 'Delete a workflow by its ID.',
|
||||||
history: new HistoryBuilder().added('v2.3.0').alpha('v2.3.0'),
|
history: HistoryBuilder.v3(),
|
||||||
})
|
})
|
||||||
deleteWorkflow(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
deleteWorkflow(@Auth() auth: AuthDto, @Param() { id }: UUIDParamDto): Promise<void> {
|
||||||
return this.service.delete(auth, id);
|
return this.service.delete(auth, id);
|
||||||
|
|||||||
+37
-52
@@ -8,8 +8,6 @@ import {
|
|||||||
ChecksumAlgorithm,
|
ChecksumAlgorithm,
|
||||||
MemoryType,
|
MemoryType,
|
||||||
Permission,
|
Permission,
|
||||||
PluginContext,
|
|
||||||
PluginTriggerType,
|
|
||||||
SharedLinkType,
|
SharedLinkType,
|
||||||
SourceType,
|
SourceType,
|
||||||
UserAvatarColor,
|
UserAvatarColor,
|
||||||
@@ -18,10 +16,8 @@ import {
|
|||||||
import { AlbumTable } from 'src/schema/tables/album.table';
|
import { AlbumTable } from 'src/schema/tables/album.table';
|
||||||
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
import { AssetExifTable } from 'src/schema/tables/asset-exif.table';
|
||||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||||
import { PluginActionTable, PluginFilterTable } from 'src/schema/tables/plugin.table';
|
import { PluginTable } from 'src/schema/tables/plugin.table';
|
||||||
import { WorkflowActionTable, WorkflowFilterTable, WorkflowTable } from 'src/schema/tables/workflow.table';
|
|
||||||
import { UserMetadataItem } from 'src/types';
|
import { UserMetadataItem } from 'src/types';
|
||||||
import type { ActionConfig, FilterConfig, JSONSchema } from 'src/types/plugin-schema.types';
|
|
||||||
|
|
||||||
export type AuthUser = {
|
export type AuthUser = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -276,42 +272,7 @@ export type AssetFace = {
|
|||||||
isVisible: boolean;
|
isVisible: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PluginFilter = Selectable<PluginFilterTable> & {
|
export type Plugin = Selectable<PluginTable>;
|
||||||
methodName: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
supportedContexts: PluginContext[];
|
|
||||||
schema: JSONSchema | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PluginAction = Selectable<PluginActionTable> & {
|
|
||||||
methodName: string;
|
|
||||||
title: string;
|
|
||||||
description: string;
|
|
||||||
supportedContexts: PluginContext[];
|
|
||||||
schema: JSONSchema | null;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type Workflow = Selectable<WorkflowTable> & {
|
|
||||||
triggerType: PluginTriggerType;
|
|
||||||
name: string | null;
|
|
||||||
description: string;
|
|
||||||
enabled: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WorkflowFilter = Selectable<WorkflowFilterTable> & {
|
|
||||||
workflowId: string;
|
|
||||||
pluginFilterId: string;
|
|
||||||
filterConfig: FilterConfig | null;
|
|
||||||
order: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type WorkflowAction = Selectable<WorkflowActionTable> & {
|
|
||||||
workflowId: string;
|
|
||||||
pluginActionId: string;
|
|
||||||
actionConfig: ActionConfig | null;
|
|
||||||
order: number;
|
|
||||||
};
|
|
||||||
|
|
||||||
const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const;
|
const userColumns = ['id', 'name', 'email', 'avatarColor', 'profileImagePath', 'profileChangedAt'] as const;
|
||||||
const userWithPrefixColumns = [
|
const userWithPrefixColumns = [
|
||||||
@@ -343,6 +304,32 @@ export const columns = {
|
|||||||
'asset.height',
|
'asset.height',
|
||||||
'asset.isEdited',
|
'asset.isEdited',
|
||||||
],
|
],
|
||||||
|
workflowAssetV1: [
|
||||||
|
'asset.id',
|
||||||
|
'asset.ownerId',
|
||||||
|
'asset.stackId',
|
||||||
|
'asset.livePhotoVideoId',
|
||||||
|
'asset.libraryId',
|
||||||
|
'asset.duplicateId',
|
||||||
|
'asset.createdAt',
|
||||||
|
'asset.updatedAt',
|
||||||
|
'asset.deletedAt',
|
||||||
|
'asset.fileCreatedAt',
|
||||||
|
'asset.fileModifiedAt',
|
||||||
|
'asset.localDateTime',
|
||||||
|
'asset.type',
|
||||||
|
'asset.status',
|
||||||
|
'asset.visibility',
|
||||||
|
'asset.duration',
|
||||||
|
'asset.checksum',
|
||||||
|
'asset.originalPath',
|
||||||
|
'asset.originalFileName',
|
||||||
|
'asset.isOffline',
|
||||||
|
'asset.isFavorite',
|
||||||
|
'asset.isExternal',
|
||||||
|
'asset.isEdited',
|
||||||
|
'asset.isFavorite',
|
||||||
|
],
|
||||||
assetFiles: ['asset_file.id', 'asset_file.path', 'asset_file.type', 'asset_file.isEdited'],
|
assetFiles: ['asset_file.id', 'asset_file.path', 'asset_file.type', 'asset_file.isEdited'],
|
||||||
assetFilesForThumbnail: [
|
assetFilesForThumbnail: [
|
||||||
'asset_file.id',
|
'asset_file.id',
|
||||||
@@ -374,6 +361,15 @@ export const columns = {
|
|||||||
tag: ['tag.id', 'tag.value', 'tag.createdAt', 'tag.updatedAt', 'tag.color', 'tag.parentId'],
|
tag: ['tag.id', 'tag.value', 'tag.createdAt', 'tag.updatedAt', 'tag.color', 'tag.parentId'],
|
||||||
apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'],
|
apiKey: ['id', 'name', 'userId', 'createdAt', 'updatedAt', 'permissions'],
|
||||||
notification: ['id', 'createdAt', 'level', 'type', 'title', 'description', 'data', 'readAt'],
|
notification: ['id', 'createdAt', 'level', 'type', 'title', 'description', 'data', 'readAt'],
|
||||||
|
pluginMethod: [
|
||||||
|
'plugin_method.name',
|
||||||
|
'plugin_method.title',
|
||||||
|
'plugin_method.description',
|
||||||
|
'plugin_method.types',
|
||||||
|
'plugin_method.schema',
|
||||||
|
'plugin_method.hostFunctions',
|
||||||
|
'plugin_method.uiHints',
|
||||||
|
],
|
||||||
syncAsset: [
|
syncAsset: [
|
||||||
'asset.id',
|
'asset.id',
|
||||||
'asset.ownerId',
|
'asset.ownerId',
|
||||||
@@ -487,17 +483,6 @@ export const columns = {
|
|||||||
'asset_exif.tags',
|
'asset_exif.tags',
|
||||||
'asset_exif.timeZone',
|
'asset_exif.timeZone',
|
||||||
],
|
],
|
||||||
plugin: [
|
|
||||||
'plugin.id as id',
|
|
||||||
'plugin.name as name',
|
|
||||||
'plugin.title as title',
|
|
||||||
'plugin.description as description',
|
|
||||||
'plugin.author as author',
|
|
||||||
'plugin.version as version',
|
|
||||||
'plugin.wasmPath as wasmPath',
|
|
||||||
'plugin.createdAt as createdAt',
|
|
||||||
'plugin.updatedAt as updatedAt',
|
|
||||||
],
|
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
export type LockableProperty = (typeof lockableProperties)[number];
|
export type LockableProperty = (typeof lockableProperties)[number];
|
||||||
|
|||||||
@@ -200,6 +200,10 @@ export class HistoryBuilder {
|
|||||||
private hasDeprecated = false;
|
private hasDeprecated = false;
|
||||||
private items: HistoryEntry[] = [];
|
private items: HistoryEntry[] = [];
|
||||||
|
|
||||||
|
static v3() {
|
||||||
|
return new HistoryBuilder().added('v3.0.0');
|
||||||
|
}
|
||||||
|
|
||||||
added(version: string, description?: string) {
|
added(version: string, description?: string) {
|
||||||
return this.push({ version, state: 'Added', description });
|
return this.push({ version, state: 'Added', description });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { createZodDto } from 'nestjs-zod';
|
||||||
|
import z from 'zod';
|
||||||
|
|
||||||
|
export const JsonSchemaTypeSchema = z
|
||||||
|
.enum(['string', 'number', 'integer', 'boolean', 'object'])
|
||||||
|
.meta({ id: 'JsonSchemaType' });
|
||||||
|
|
||||||
|
const JsonSchemaPropertySchema = z
|
||||||
|
.object({
|
||||||
|
type: JsonSchemaTypeSchema.optional().default('object').describe('Type'),
|
||||||
|
title: z.string().describe('Title'),
|
||||||
|
description: z.string().describe('Description'),
|
||||||
|
default: z.any().optional().describe('Default value'),
|
||||||
|
enum: z.array(z.string()).optional().describe('Valid choices for enum types'),
|
||||||
|
array: z.boolean().optional().describe('Type is an array type'),
|
||||||
|
required: z.array(z.string()).optional().describe('A list of required properties'),
|
||||||
|
uiHint: z.string().optional(),
|
||||||
|
get properties() {
|
||||||
|
return z.record(z.string(), JsonSchemaPropertySchema).optional();
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.meta({ id: 'JsonSchemaPropertyDto' });
|
||||||
|
|
||||||
|
export const JsonSchemaSchema = z
|
||||||
|
.object({
|
||||||
|
...JsonSchemaPropertySchema.shape,
|
||||||
|
title: z.string().optional().describe('Title'),
|
||||||
|
description: z.string().optional().describe('Description'),
|
||||||
|
})
|
||||||
|
.meta({ id: 'JsonSchemaDto' });
|
||||||
|
|
||||||
|
export class JsonSchemaDto extends createZodDto(JsonSchemaSchema) {}
|
||||||
@@ -1,39 +1,29 @@
|
|||||||
import { createZodDto } from 'nestjs-zod';
|
import { createZodDto } from 'nestjs-zod';
|
||||||
import { PluginContextSchema } from 'src/enum';
|
import { JsonSchemaSchema } from 'src/dtos/json-schema.dto';
|
||||||
import { JSONSchemaSchema } from 'src/types/plugin-schema.types';
|
import { WorkflowTypeSchema } from 'src/enum';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
const pluginNameRegex = /^[a-z0-9-]+[a-z0-9]$/;
|
const pluginNameRegex = /^[a-z0-9-]+[a-z0-9]$/;
|
||||||
const semverRegex =
|
const semverRegex =
|
||||||
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
||||||
|
|
||||||
const PluginManifestWasmSchema = z
|
export const PluginManifestMethodSchemaSchema = JsonSchemaSchema.nullable()
|
||||||
.object({
|
.optional()
|
||||||
path: z.string().describe('WASM file path'),
|
.transform((value) => (value && Object.keys(value).length === 0 ? null : value));
|
||||||
})
|
|
||||||
.meta({ id: 'PluginManifestWasmDto' });
|
|
||||||
|
|
||||||
const PluginManifestFilterSchema = z
|
const PluginManifestMethodSchema = z
|
||||||
.object({
|
.object({
|
||||||
methodName: z.string().describe('Filter method name'),
|
name: z.string().min(1).describe('Method name'),
|
||||||
title: z.string().describe('Filter title'),
|
title: z.string().min(1).describe('Method title'),
|
||||||
description: z.string().describe('Filter description'),
|
description: z.string().min(1).describe('Method description'),
|
||||||
supportedContexts: z.array(PluginContextSchema).min(1).describe('Supported contexts'),
|
types: z.array(WorkflowTypeSchema).min(1).describe('Workflow type'),
|
||||||
schema: JSONSchemaSchema.optional(),
|
hostFunctions: z.boolean().optional().default(false).describe('Method uses host functions'),
|
||||||
|
schema: PluginManifestMethodSchemaSchema.describe('Schema'),
|
||||||
|
uiHints: z.array(z.string()).optional().describe('Ui hints, for example "filter"'),
|
||||||
})
|
})
|
||||||
.meta({ id: 'PluginManifestFilterDto' });
|
.meta({ id: 'PluginManifestMethodDto' });
|
||||||
|
|
||||||
const PluginManifestActionSchema = z
|
const PluginManifestSchema = z
|
||||||
.object({
|
|
||||||
methodName: z.string().describe('Action method name'),
|
|
||||||
title: z.string().describe('Action title'),
|
|
||||||
description: z.string().describe('Action description'),
|
|
||||||
supportedContexts: z.array(PluginContextSchema).min(1).describe('Supported contexts'),
|
|
||||||
schema: JSONSchemaSchema.optional(),
|
|
||||||
})
|
|
||||||
.meta({ id: 'PluginManifestActionDto' });
|
|
||||||
|
|
||||||
export const PluginManifestSchema = z
|
|
||||||
.object({
|
.object({
|
||||||
name: z
|
name: z
|
||||||
.string()
|
.string()
|
||||||
@@ -44,12 +34,11 @@ export const PluginManifestSchema = z
|
|||||||
)
|
)
|
||||||
.describe('Plugin name (lowercase, numbers, hyphens only)'),
|
.describe('Plugin name (lowercase, numbers, hyphens only)'),
|
||||||
version: z.string().regex(semverRegex).describe('Plugin version (semver)'),
|
version: z.string().regex(semverRegex).describe('Plugin version (semver)'),
|
||||||
title: z.string().describe('Plugin title'),
|
title: z.string().min(1).describe('Plugin title'),
|
||||||
description: z.string().describe('Plugin description'),
|
description: z.string().min(1).describe('Plugin description'),
|
||||||
author: z.string().describe('Plugin author'),
|
wasmPath: z.string().min(1).describe('WASM file path'),
|
||||||
wasm: PluginManifestWasmSchema,
|
author: z.string().min(1).describe('Plugin author'),
|
||||||
filters: z.array(PluginManifestFilterSchema).optional().describe('Plugin filters'),
|
methods: z.array(PluginManifestMethodSchema).optional().default([]).describe('Plugin methods'),
|
||||||
actions: z.array(PluginManifestActionSchema).optional().describe('Plugin actions'),
|
|
||||||
})
|
})
|
||||||
.meta({ id: 'PluginManifestDto' });
|
.meta({ id: 'PluginManifestDto' });
|
||||||
|
|
||||||
|
|||||||
@@ -1,39 +1,33 @@
|
|||||||
import { createZodDto } from 'nestjs-zod';
|
import { createZodDto } from 'nestjs-zod';
|
||||||
import { PluginAction, PluginFilter } from 'src/database';
|
import { JsonSchemaDto } from 'src/dtos/json-schema.dto';
|
||||||
import { PluginContextSchema, PluginTriggerTypeSchema } from 'src/enum';
|
import { WorkflowTriggerSchema, WorkflowType, WorkflowTypeSchema } from 'src/enum';
|
||||||
import { JSONSchemaSchema } from 'src/types/plugin-schema.types';
|
import { asMethodString } from 'src/utils/workflow';
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
const PluginTriggerResponseSchema = z
|
const PluginSearchSchema = z
|
||||||
.object({
|
.object({
|
||||||
type: PluginTriggerTypeSchema,
|
id: z.uuidv4().optional().describe('Plugin ID'),
|
||||||
contextType: PluginContextSchema,
|
enabled: z.boolean().optional().describe('Whether the plugin is enabled'),
|
||||||
|
name: z.string().optional(),
|
||||||
|
version: z.string().optional(),
|
||||||
|
title: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
})
|
})
|
||||||
.meta({ id: 'PluginTriggerResponseDto' });
|
.meta({ id: 'PluginSearchDto' });
|
||||||
|
|
||||||
const PluginFilterResponseSchema = z
|
const PluginMethodResponseSchema = z
|
||||||
.object({
|
.object({
|
||||||
id: z.string().describe('Filter ID'),
|
key: z.string().describe('Key'),
|
||||||
pluginId: z.string().describe('Plugin ID'),
|
name: z.string().describe('Name'),
|
||||||
methodName: z.string().describe('Method name'),
|
title: z.string().describe('Title'),
|
||||||
title: z.string().describe('Filter title'),
|
description: z.string().describe('Description'),
|
||||||
description: z.string().describe('Filter description'),
|
types: z.array(WorkflowTypeSchema).describe('Workflow types'),
|
||||||
supportedContexts: z.array(PluginContextSchema).describe('Supported contexts'),
|
uiHints: z.array(z.string()).describe('Ui hints'),
|
||||||
schema: JSONSchemaSchema.nullable().describe('Filter schema'),
|
// TODO fix this
|
||||||
|
schema: z.object().optional(),
|
||||||
|
hostFunctions: z.boolean(),
|
||||||
})
|
})
|
||||||
.meta({ id: 'PluginFilterResponseDto' });
|
.meta({ id: 'PluginMethodResponseDto' });
|
||||||
|
|
||||||
const PluginActionResponseSchema = z
|
|
||||||
.object({
|
|
||||||
id: z.string().describe('Action ID'),
|
|
||||||
pluginId: z.string().describe('Plugin ID'),
|
|
||||||
methodName: z.string().describe('Method name'),
|
|
||||||
title: z.string().describe('Action title'),
|
|
||||||
description: z.string().describe('Action description'),
|
|
||||||
supportedContexts: z.array(PluginContextSchema).describe('Supported contexts'),
|
|
||||||
schema: JSONSchemaSchema.nullable().describe('Action schema'),
|
|
||||||
})
|
|
||||||
.meta({ id: 'PluginActionResponseDto' });
|
|
||||||
|
|
||||||
const PluginResponseSchema = z
|
const PluginResponseSchema = z
|
||||||
.object({
|
.object({
|
||||||
@@ -45,29 +39,53 @@ const PluginResponseSchema = z
|
|||||||
version: z.string().describe('Plugin version'),
|
version: z.string().describe('Plugin version'),
|
||||||
createdAt: z.string().describe('Creation date'),
|
createdAt: z.string().describe('Creation date'),
|
||||||
updatedAt: z.string().describe('Last update date'),
|
updatedAt: z.string().describe('Last update date'),
|
||||||
filters: z.array(PluginFilterResponseSchema).describe('Plugin filters'),
|
methods: z.array(PluginMethodResponseSchema).describe('Plugin methods'),
|
||||||
actions: z.array(PluginActionResponseSchema).describe('Plugin actions'),
|
|
||||||
})
|
})
|
||||||
.meta({ id: 'PluginResponseDto' });
|
.meta({ id: 'PluginResponseDto' });
|
||||||
|
|
||||||
export class PluginTriggerResponseDto extends createZodDto(PluginTriggerResponseSchema) {}
|
const PluginMethodSearchSchema = z
|
||||||
export class PluginResponseDto extends createZodDto(PluginResponseSchema) {}
|
.object({
|
||||||
|
id: z.uuidv4().optional().describe('Plugin method ID'),
|
||||||
|
enabled: z.boolean().optional().describe('Whether the plugin method is enabled'),
|
||||||
|
name: z.string().optional(),
|
||||||
|
title: z.string().optional(),
|
||||||
|
description: z.string().optional(),
|
||||||
|
type: WorkflowTypeSchema.optional().describe('Workflow types'),
|
||||||
|
trigger: WorkflowTriggerSchema.optional().describe('Workflow trigger'),
|
||||||
|
pluginName: z.string().optional().describe('Plugin name'),
|
||||||
|
pluginVersion: z.string().optional().describe('Plugin version'),
|
||||||
|
})
|
||||||
|
.meta({ id: 'PluginMethodSearchDto' });
|
||||||
|
|
||||||
type MapPlugin = {
|
export class PluginSearchDto extends createZodDto(PluginSearchSchema) {}
|
||||||
|
export class PluginResponseDto extends createZodDto(PluginResponseSchema) {}
|
||||||
|
export class PluginMethodSearchDto extends createZodDto(PluginMethodSearchSchema) {}
|
||||||
|
export class PluginMethodResponseDto extends createZodDto(PluginMethodResponseSchema) {}
|
||||||
|
|
||||||
|
type Plugin = {
|
||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
title: string;
|
title: string;
|
||||||
description: string;
|
description: string;
|
||||||
author: string;
|
author: string;
|
||||||
version: string;
|
version: string;
|
||||||
wasmPath: string;
|
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
updatedAt: Date;
|
updatedAt: Date;
|
||||||
filters: PluginFilter[];
|
methods: PluginMethod[];
|
||||||
actions: PluginAction[];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function mapPlugin(plugin: MapPlugin): PluginResponseDto {
|
type PluginMethod = {
|
||||||
|
pluginName: string;
|
||||||
|
name: string;
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
types: WorkflowType[];
|
||||||
|
schema: JsonSchemaDto | null;
|
||||||
|
hostFunctions: boolean;
|
||||||
|
uiHints: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export function mapPlugin(plugin: Plugin): PluginResponseDto {
|
||||||
return {
|
return {
|
||||||
id: plugin.id,
|
id: plugin.id,
|
||||||
name: plugin.name,
|
name: plugin.name,
|
||||||
@@ -77,7 +95,19 @@ export function mapPlugin(plugin: MapPlugin): PluginResponseDto {
|
|||||||
version: plugin.version,
|
version: plugin.version,
|
||||||
createdAt: plugin.createdAt.toISOString(),
|
createdAt: plugin.createdAt.toISOString(),
|
||||||
updatedAt: plugin.updatedAt.toISOString(),
|
updatedAt: plugin.updatedAt.toISOString(),
|
||||||
filters: plugin.filters,
|
methods: plugin.methods.map((method) => mapMethod(method)),
|
||||||
actions: plugin.actions,
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const mapMethod = (method: PluginMethod): PluginMethodResponseDto => {
|
||||||
|
return {
|
||||||
|
key: asMethodString({ pluginName: method.pluginName, methodName: method.name }),
|
||||||
|
name: method.name,
|
||||||
|
title: method.title,
|
||||||
|
hostFunctions: method.hostFunctions,
|
||||||
|
uiHints: method.uiHints,
|
||||||
|
description: method.description,
|
||||||
|
types: method.types,
|
||||||
|
schema: method.schema as any,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,101 +1,135 @@
|
|||||||
|
import type { WorkflowStepConfig } from '@immich/plugin-sdk';
|
||||||
import { createZodDto } from 'nestjs-zod';
|
import { createZodDto } from 'nestjs-zod';
|
||||||
import type { WorkflowAction, WorkflowFilter } from 'src/database';
|
import { WorkflowTrigger, WorkflowTriggerSchema, WorkflowTypeSchema } from 'src/enum';
|
||||||
import { PluginTriggerTypeSchema } from 'src/enum';
|
|
||||||
import { ActionConfigSchema, FilterConfigSchema } from 'src/types/plugin-schema.types';
|
|
||||||
import z from 'zod';
|
import z from 'zod';
|
||||||
|
|
||||||
const WorkflowFilterItemSchema = z
|
const WorkflowTriggerResponseSchema = z
|
||||||
.object({
|
.object({
|
||||||
pluginFilterId: z.uuidv4().describe('Plugin filter ID'),
|
trigger: WorkflowTriggerSchema.describe('Trigger type'),
|
||||||
filterConfig: FilterConfigSchema.optional(),
|
types: z.array(WorkflowTypeSchema).describe('Workflow types'),
|
||||||
})
|
})
|
||||||
.meta({ id: 'WorkflowFilterItemDto' });
|
.meta({ id: 'WorkflowTriggerResponseDto' });
|
||||||
|
|
||||||
const WorkflowActionItemSchema = z
|
const WorkflowSearchSchema = z
|
||||||
.object({
|
.object({
|
||||||
pluginActionId: z.uuidv4().describe('Plugin action ID'),
|
id: z.uuidv4().optional().describe('Workflow ID'),
|
||||||
actionConfig: ActionConfigSchema.optional(),
|
trigger: WorkflowTriggerSchema.optional().describe('Workflow trigger type'),
|
||||||
|
name: z.string().optional().describe('Workflow name'),
|
||||||
|
description: z.string().optional().describe('Workflow description'),
|
||||||
|
enabled: z.boolean().optional().describe('Workflow enabled'),
|
||||||
})
|
})
|
||||||
.meta({ id: 'WorkflowActionItemDto' });
|
.meta({ id: 'WorkflowSearchDto' });
|
||||||
|
|
||||||
|
const WorkflowStepSchema = z
|
||||||
|
.object({
|
||||||
|
method: z.string().describe('Step plugin method'),
|
||||||
|
config: z.record(z.string(), z.unknown()).nullable().describe('Step configuration'),
|
||||||
|
enabled: z.boolean().optional().describe('Step is enabled'),
|
||||||
|
})
|
||||||
|
.meta({ id: 'WorkflowStepDto' });
|
||||||
|
|
||||||
|
const WorkflowShareStepSchema = z
|
||||||
|
.object({
|
||||||
|
method: z.string().describe('Step plugin method'),
|
||||||
|
config: z.record(z.string(), z.unknown()).nullable().describe('Step configuration'),
|
||||||
|
enabled: z.boolean().optional().describe('Step is enabled'),
|
||||||
|
})
|
||||||
|
.meta({ id: 'WorkflowShareStepDto' });
|
||||||
|
|
||||||
const WorkflowCreateSchema = z
|
const WorkflowCreateSchema = z
|
||||||
.object({
|
.object({
|
||||||
triggerType: PluginTriggerTypeSchema,
|
trigger: WorkflowTriggerSchema.describe('Workflow trigger type'),
|
||||||
name: z.string().describe('Workflow name'),
|
name: z.string().nullable().optional().describe('Workflow name'),
|
||||||
description: z.string().optional().describe('Workflow description'),
|
description: z.string().nullable().optional().describe('Workflow description'),
|
||||||
enabled: z.boolean().optional().describe('Workflow enabled'),
|
enabled: z.boolean().optional().describe('Workflow enabled'),
|
||||||
filters: z.array(WorkflowFilterItemSchema).describe('Workflow filters'),
|
steps: z.array(WorkflowStepSchema).optional(),
|
||||||
actions: z.array(WorkflowActionItemSchema).describe('Workflow actions'),
|
|
||||||
})
|
})
|
||||||
.meta({ id: 'WorkflowCreateDto' });
|
.meta({ id: 'WorkflowCreateDto' });
|
||||||
|
|
||||||
const WorkflowUpdateSchema = z
|
const WorkflowUpdateSchema = z
|
||||||
.object({
|
.object({
|
||||||
triggerType: PluginTriggerTypeSchema.optional(),
|
trigger: WorkflowTriggerSchema.optional().describe('Workflow trigger type'),
|
||||||
name: z.string().optional().describe('Workflow name'),
|
name: z.string().nullable().optional().describe('Workflow name'),
|
||||||
description: z.string().optional().describe('Workflow description'),
|
description: z.string().nullable().optional().describe('Workflow description'),
|
||||||
enabled: z.boolean().optional().describe('Workflow enabled'),
|
enabled: z.boolean().optional().describe('Workflow enabled'),
|
||||||
filters: z.array(WorkflowFilterItemSchema).optional().describe('Workflow filters'),
|
steps: z.array(WorkflowStepSchema).optional(),
|
||||||
actions: z.array(WorkflowActionItemSchema).optional().describe('Workflow actions'),
|
|
||||||
})
|
})
|
||||||
.meta({ id: 'WorkflowUpdateDto' });
|
.meta({ id: 'WorkflowUpdateDto' });
|
||||||
|
|
||||||
const WorkflowFilterResponseSchema = z
|
|
||||||
.object({
|
|
||||||
id: z.string().describe('Filter ID'),
|
|
||||||
workflowId: z.string().describe('Workflow ID'),
|
|
||||||
pluginFilterId: z.string().describe('Plugin filter ID'),
|
|
||||||
filterConfig: FilterConfigSchema.nullable(),
|
|
||||||
order: z.int().describe('Filter order'),
|
|
||||||
})
|
|
||||||
.meta({ id: 'WorkflowFilterResponseDto' });
|
|
||||||
|
|
||||||
const WorkflowActionResponseSchema = z
|
|
||||||
.object({
|
|
||||||
id: z.string().describe('Action ID'),
|
|
||||||
workflowId: z.string().describe('Workflow ID'),
|
|
||||||
pluginActionId: z.string().describe('Plugin action ID'),
|
|
||||||
actionConfig: ActionConfigSchema.nullable(),
|
|
||||||
order: z.int().describe('Action order'),
|
|
||||||
})
|
|
||||||
.meta({ id: 'WorkflowActionResponseDto' });
|
|
||||||
|
|
||||||
const WorkflowResponseSchema = z
|
const WorkflowResponseSchema = z
|
||||||
.object({
|
.object({
|
||||||
id: z.string().describe('Workflow ID'),
|
id: z.string().describe('Workflow ID'),
|
||||||
ownerId: z.string().describe('Owner user ID'),
|
trigger: WorkflowTriggerSchema.describe('Workflow trigger type'),
|
||||||
triggerType: PluginTriggerTypeSchema,
|
|
||||||
name: z.string().nullable().describe('Workflow name'),
|
name: z.string().nullable().describe('Workflow name'),
|
||||||
description: z.string().describe('Workflow description'),
|
description: z.string().nullable().describe('Workflow description'),
|
||||||
createdAt: z.string().describe('Creation date'),
|
createdAt: z.string().describe('Creation date'),
|
||||||
|
updatedAt: z.string().describe('Update date'),
|
||||||
enabled: z.boolean().describe('Workflow enabled'),
|
enabled: z.boolean().describe('Workflow enabled'),
|
||||||
filters: z.array(WorkflowFilterResponseSchema).describe('Workflow filters'),
|
steps: z.array(WorkflowStepSchema).describe('Workflow steps'),
|
||||||
actions: z.array(WorkflowActionResponseSchema).describe('Workflow actions'),
|
|
||||||
})
|
})
|
||||||
.meta({ id: 'WorkflowResponseDto' });
|
.meta({ id: 'WorkflowResponseDto' });
|
||||||
|
|
||||||
|
const WorkflowShareResponseSchema = z
|
||||||
|
.object({
|
||||||
|
trigger: WorkflowTriggerSchema.describe('Workflow trigger type'),
|
||||||
|
name: z.string().nullable().describe('Workflow name'),
|
||||||
|
description: z.string().nullable().describe('Workflow description'),
|
||||||
|
steps: z.array(WorkflowShareStepSchema).describe('Workflow steps'),
|
||||||
|
})
|
||||||
|
.meta({ id: 'WorkflowShareResponseDto' });
|
||||||
|
|
||||||
|
export class WorkflowTriggerResponseDto extends createZodDto(WorkflowTriggerResponseSchema) {}
|
||||||
|
export class WorkflowSearchDto extends createZodDto(WorkflowSearchSchema) {}
|
||||||
export class WorkflowCreateDto extends createZodDto(WorkflowCreateSchema) {}
|
export class WorkflowCreateDto extends createZodDto(WorkflowCreateSchema) {}
|
||||||
export class WorkflowUpdateDto extends createZodDto(WorkflowUpdateSchema) {}
|
export class WorkflowUpdateDto extends createZodDto(WorkflowUpdateSchema) {}
|
||||||
export class WorkflowResponseDto extends createZodDto(WorkflowResponseSchema) {}
|
export class WorkflowResponseDto extends createZodDto(WorkflowResponseSchema) {}
|
||||||
class WorkflowFilterResponseDto extends createZodDto(WorkflowFilterResponseSchema) {}
|
export class WorkflowShareResponseDto extends createZodDto(WorkflowShareResponseSchema) {}
|
||||||
class WorkflowActionResponseDto extends createZodDto(WorkflowActionResponseSchema) {}
|
|
||||||
|
|
||||||
export function mapWorkflowFilter(filter: WorkflowFilter): WorkflowFilterResponseDto {
|
type Workflow = {
|
||||||
return {
|
id: string;
|
||||||
id: filter.id,
|
createdAt: Date;
|
||||||
workflowId: filter.workflowId,
|
updatedAt: Date;
|
||||||
pluginFilterId: filter.pluginFilterId,
|
trigger: WorkflowTrigger;
|
||||||
filterConfig: filter.filterConfig,
|
name: string | null;
|
||||||
order: filter.order,
|
description: string | null;
|
||||||
};
|
enabled: boolean;
|
||||||
}
|
};
|
||||||
|
|
||||||
export function mapWorkflowAction(action: WorkflowAction): WorkflowActionResponseDto {
|
type WorkflowStep = {
|
||||||
|
enabled: boolean;
|
||||||
|
methodName: string;
|
||||||
|
config: WorkflowStepConfig | null;
|
||||||
|
pluginName: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const mapWorkflow = (workflow: Workflow & { steps: WorkflowStep[] }): WorkflowResponseDto => {
|
||||||
return {
|
return {
|
||||||
id: action.id,
|
id: workflow.id,
|
||||||
workflowId: action.workflowId,
|
enabled: workflow.enabled,
|
||||||
pluginActionId: action.pluginActionId,
|
trigger: workflow.trigger,
|
||||||
actionConfig: action.actionConfig,
|
name: workflow.name,
|
||||||
order: action.order,
|
description: workflow.description,
|
||||||
|
createdAt: workflow.createdAt.toISOString(),
|
||||||
|
updatedAt: workflow.updatedAt.toISOString(),
|
||||||
|
steps: workflow.steps.map((step) => ({
|
||||||
|
method: `${step.pluginName}#${step.methodName}`,
|
||||||
|
// TODO fix this
|
||||||
|
config: step.config as any,
|
||||||
|
enabled: step.enabled,
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
};
|
||||||
|
|
||||||
|
export const mapWorkflowShare = (workflow: Workflow & { steps: WorkflowStep[] }): WorkflowShareResponseDto => {
|
||||||
|
return {
|
||||||
|
trigger: workflow.trigger,
|
||||||
|
name: workflow.name,
|
||||||
|
description: workflow.description,
|
||||||
|
steps: workflow.steps.map((step) => ({
|
||||||
|
method: `${step.pluginName}#${step.methodName}`,
|
||||||
|
// TODO fix this
|
||||||
|
config: step.config as any,
|
||||||
|
enabled: step.enabled ? undefined : false,
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|||||||
+17
-6
@@ -749,8 +749,11 @@ export enum BootstrapEventPriority {
|
|||||||
StorageService = -195,
|
StorageService = -195,
|
||||||
// Other services may need to queue jobs on bootstrap.
|
// Other services may need to queue jobs on bootstrap.
|
||||||
JobService = -190,
|
JobService = -190,
|
||||||
// Initialise config after other bootstrap services, stop other services from using config on bootstrap
|
// Initialize config after other bootstrap services, stop other services from using config on bootstrap
|
||||||
SystemConfig = 100,
|
SystemConfig = 100,
|
||||||
|
PluginSync = 190,
|
||||||
|
// Load plugins into memory after sync
|
||||||
|
PluginLoad = 200,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum QueueName {
|
export enum QueueName {
|
||||||
@@ -863,7 +866,7 @@ export enum JobName {
|
|||||||
Ocr = 'Ocr',
|
Ocr = 'Ocr',
|
||||||
|
|
||||||
// Workflow
|
// Workflow
|
||||||
WorkflowRun = 'WorkflowRun',
|
WorkflowAssetCreate = 'WorkflowAssetCreate',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const JobNameSchema = z.enum(JobName).describe('Job name').meta({ id: 'JobName' });
|
export const JobNameSchema = z.enum(JobName).describe('Job name').meta({ id: 'JobName' });
|
||||||
@@ -909,6 +912,7 @@ export enum DatabaseLock {
|
|||||||
CLIPDimSize = 512,
|
CLIPDimSize = 512,
|
||||||
Library = 1337,
|
Library = 1337,
|
||||||
NightlyJobs = 600,
|
NightlyJobs = 600,
|
||||||
|
PluginImport = 666,
|
||||||
MediaLocation = 700,
|
MediaLocation = 700,
|
||||||
GetSystemConfig = 69,
|
GetSystemConfig = 69,
|
||||||
BackupDatabase = 42,
|
BackupDatabase = 42,
|
||||||
@@ -1160,12 +1164,19 @@ export enum PluginContext {
|
|||||||
|
|
||||||
export const PluginContextSchema = z.enum(PluginContext).describe('Plugin context').meta({ id: 'PluginContextType' });
|
export const PluginContextSchema = z.enum(PluginContext).describe('Plugin context').meta({ id: 'PluginContextType' });
|
||||||
|
|
||||||
export enum PluginTriggerType {
|
export enum WorkflowTrigger {
|
||||||
AssetCreate = 'AssetCreate',
|
AssetCreate = 'AssetCreate',
|
||||||
PersonRecognized = 'PersonRecognized',
|
PersonRecognized = 'PersonRecognized',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const PluginTriggerTypeSchema = z
|
export const WorkflowTriggerSchema = z
|
||||||
.enum(PluginTriggerType)
|
.enum(WorkflowTrigger)
|
||||||
.describe('Plugin trigger type')
|
.describe('Plugin trigger type')
|
||||||
.meta({ id: 'PluginTriggerType' });
|
.meta({ id: 'WorkflowTrigger' });
|
||||||
|
|
||||||
|
export enum WorkflowType {
|
||||||
|
AssetV1 = 'AssetV1',
|
||||||
|
AssetPersonV1 = 'AssetPersonV1',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WorkflowTypeSchema = z.enum(WorkflowType).describe('Workflow type').meta({ id: 'WorkflowType' });
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
import { PluginContext, PluginTriggerType } from 'src/enum';
|
|
||||||
|
|
||||||
export type PluginTrigger = {
|
|
||||||
type: PluginTriggerType;
|
|
||||||
contextType: PluginContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const pluginTriggers: PluginTrigger[] = [
|
|
||||||
{
|
|
||||||
type: PluginTriggerType.AssetCreate,
|
|
||||||
contextType: PluginContext.Asset,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: PluginTriggerType.PersonRecognized,
|
|
||||||
contextType: PluginContext.Person,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
@@ -1,159 +1,159 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
-- PluginRepository.getPlugin
|
-- PluginRepository.getForLoad
|
||||||
select
|
select
|
||||||
"plugin"."id" as "id",
|
"plugin"."id",
|
||||||
"plugin"."name" as "name",
|
"plugin"."name",
|
||||||
"plugin"."title" as "title",
|
"plugin"."version",
|
||||||
"plugin"."description" as "description",
|
"plugin"."wasmBytes",
|
||||||
"plugin"."author" as "author",
|
|
||||||
"plugin"."version" as "version",
|
|
||||||
"plugin"."wasmPath" as "wasmPath",
|
|
||||||
"plugin"."createdAt" as "createdAt",
|
|
||||||
"plugin"."updatedAt" as "updatedAt",
|
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
coalesce(json_agg(agg), '[]')
|
coalesce(json_agg(agg), '[]')
|
||||||
from
|
from
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
*
|
"plugin_method"."name",
|
||||||
|
"plugin_method"."hostFunctions"
|
||||||
from
|
from
|
||||||
"plugin_filter"
|
"plugin_method"
|
||||||
where
|
where
|
||||||
"plugin_filter"."pluginId" = "plugin"."id"
|
"plugin_method"."pluginId" = "plugin"."id"
|
||||||
) as agg
|
) as agg
|
||||||
) as "filters",
|
) as "methods"
|
||||||
(
|
|
||||||
select
|
|
||||||
coalesce(json_agg(agg), '[]')
|
|
||||||
from
|
|
||||||
(
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"plugin_action"
|
|
||||||
where
|
|
||||||
"plugin_action"."pluginId" = "plugin"."id"
|
|
||||||
) as agg
|
|
||||||
) as "actions"
|
|
||||||
from
|
from
|
||||||
"plugin"
|
"plugin"
|
||||||
where
|
where
|
||||||
"plugin"."id" = $1
|
"enabled" = $1
|
||||||
|
|
||||||
-- PluginRepository.getPluginByName
|
-- PluginRepository.search
|
||||||
select
|
select
|
||||||
"plugin"."id" as "id",
|
"plugin"."id",
|
||||||
"plugin"."name" as "name",
|
"plugin"."name",
|
||||||
"plugin"."title" as "title",
|
"plugin"."title",
|
||||||
"plugin"."description" as "description",
|
"plugin"."description",
|
||||||
"plugin"."author" as "author",
|
"plugin"."author",
|
||||||
"plugin"."version" as "version",
|
"plugin"."version",
|
||||||
"plugin"."wasmPath" as "wasmPath",
|
"plugin"."createdAt",
|
||||||
"plugin"."createdAt" as "createdAt",
|
"plugin"."updatedAt",
|
||||||
"plugin"."updatedAt" as "updatedAt",
|
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
coalesce(json_agg(agg), '[]')
|
coalesce(json_agg(agg), '[]')
|
||||||
from
|
from
|
||||||
(
|
(
|
||||||
select
|
select
|
||||||
*
|
"plugin_method"."name",
|
||||||
|
"plugin_method"."title",
|
||||||
|
"plugin_method"."description",
|
||||||
|
"plugin_method"."types",
|
||||||
|
"plugin_method"."schema",
|
||||||
|
"plugin_method"."hostFunctions",
|
||||||
|
"plugin_method"."uiHints",
|
||||||
|
"plugin"."name" as "pluginName"
|
||||||
from
|
from
|
||||||
"plugin_filter"
|
"plugin_method"
|
||||||
where
|
where
|
||||||
"plugin_filter"."pluginId" = "plugin"."id"
|
"plugin_method"."pluginId" = "plugin"."id"
|
||||||
) as agg
|
) as agg
|
||||||
) as "filters",
|
) as "methods"
|
||||||
(
|
|
||||||
select
|
|
||||||
coalesce(json_agg(agg), '[]')
|
|
||||||
from
|
|
||||||
(
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"plugin_action"
|
|
||||||
where
|
|
||||||
"plugin_action"."pluginId" = "plugin"."id"
|
|
||||||
) as agg
|
|
||||||
) as "actions"
|
|
||||||
from
|
|
||||||
"plugin"
|
|
||||||
where
|
|
||||||
"plugin"."name" = $1
|
|
||||||
|
|
||||||
-- PluginRepository.getAllPlugins
|
|
||||||
select
|
|
||||||
"plugin"."id" as "id",
|
|
||||||
"plugin"."name" as "name",
|
|
||||||
"plugin"."title" as "title",
|
|
||||||
"plugin"."description" as "description",
|
|
||||||
"plugin"."author" as "author",
|
|
||||||
"plugin"."version" as "version",
|
|
||||||
"plugin"."wasmPath" as "wasmPath",
|
|
||||||
"plugin"."createdAt" as "createdAt",
|
|
||||||
"plugin"."updatedAt" as "updatedAt",
|
|
||||||
(
|
|
||||||
select
|
|
||||||
coalesce(json_agg(agg), '[]')
|
|
||||||
from
|
|
||||||
(
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"plugin_filter"
|
|
||||||
where
|
|
||||||
"plugin_filter"."pluginId" = "plugin"."id"
|
|
||||||
) as agg
|
|
||||||
) as "filters",
|
|
||||||
(
|
|
||||||
select
|
|
||||||
coalesce(json_agg(agg), '[]')
|
|
||||||
from
|
|
||||||
(
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"plugin_action"
|
|
||||||
where
|
|
||||||
"plugin_action"."pluginId" = "plugin"."id"
|
|
||||||
) as agg
|
|
||||||
) as "actions"
|
|
||||||
from
|
from
|
||||||
"plugin"
|
"plugin"
|
||||||
order by
|
order by
|
||||||
"plugin"."name"
|
"plugin"."name"
|
||||||
|
|
||||||
-- PluginRepository.getFilter
|
-- PluginRepository.getByName
|
||||||
select
|
select
|
||||||
*
|
"plugin"."id",
|
||||||
|
"plugin"."name",
|
||||||
|
"plugin"."title",
|
||||||
|
"plugin"."description",
|
||||||
|
"plugin"."author",
|
||||||
|
"plugin"."version",
|
||||||
|
"plugin"."createdAt",
|
||||||
|
"plugin"."updatedAt",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"plugin_method"."name",
|
||||||
|
"plugin_method"."title",
|
||||||
|
"plugin_method"."description",
|
||||||
|
"plugin_method"."types",
|
||||||
|
"plugin_method"."schema",
|
||||||
|
"plugin_method"."hostFunctions",
|
||||||
|
"plugin_method"."uiHints",
|
||||||
|
"plugin"."name" as "pluginName"
|
||||||
|
from
|
||||||
|
"plugin_method"
|
||||||
|
where
|
||||||
|
"plugin_method"."pluginId" = "plugin"."id"
|
||||||
|
) as agg
|
||||||
|
) as "methods"
|
||||||
from
|
from
|
||||||
"plugin_filter"
|
"plugin"
|
||||||
where
|
where
|
||||||
"id" = $1
|
"plugin"."name" = $1
|
||||||
|
|
||||||
-- PluginRepository.getFiltersByPlugin
|
-- PluginRepository.get
|
||||||
select
|
select
|
||||||
*
|
"plugin"."id",
|
||||||
|
"plugin"."name",
|
||||||
|
"plugin"."title",
|
||||||
|
"plugin"."description",
|
||||||
|
"plugin"."author",
|
||||||
|
"plugin"."version",
|
||||||
|
"plugin"."createdAt",
|
||||||
|
"plugin"."updatedAt",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"plugin_method"."name",
|
||||||
|
"plugin_method"."title",
|
||||||
|
"plugin_method"."description",
|
||||||
|
"plugin_method"."types",
|
||||||
|
"plugin_method"."schema",
|
||||||
|
"plugin_method"."hostFunctions",
|
||||||
|
"plugin_method"."uiHints",
|
||||||
|
"plugin"."name" as "pluginName"
|
||||||
|
from
|
||||||
|
"plugin_method"
|
||||||
|
where
|
||||||
|
"plugin_method"."pluginId" = "plugin"."id"
|
||||||
|
) as agg
|
||||||
|
) as "methods"
|
||||||
from
|
from
|
||||||
"plugin_filter"
|
"plugin"
|
||||||
where
|
where
|
||||||
"pluginId" = $1
|
"plugin"."id" = $1
|
||||||
|
|
||||||
-- PluginRepository.getAction
|
-- PluginRepository.getForValidation
|
||||||
select
|
select
|
||||||
*
|
"plugin_method"."id",
|
||||||
|
"plugin_method"."name",
|
||||||
|
"plugin"."name" as "pluginName",
|
||||||
|
"plugin_method"."types"
|
||||||
from
|
from
|
||||||
"plugin_action"
|
"plugin_method"
|
||||||
where
|
inner join "plugin" on "plugin_method"."pluginId" = "plugin"."id"
|
||||||
"id" = $1
|
|
||||||
|
|
||||||
-- PluginRepository.getActionsByPlugin
|
-- PluginRepository.searchMethods
|
||||||
select
|
select
|
||||||
*
|
"plugin"."name" as "pluginName",
|
||||||
|
"plugin_method"."pluginId",
|
||||||
|
"plugin_method"."id",
|
||||||
|
"plugin_method"."name",
|
||||||
|
"plugin_method"."title",
|
||||||
|
"plugin_method"."description",
|
||||||
|
"plugin_method"."types",
|
||||||
|
"plugin_method"."schema",
|
||||||
|
"plugin_method"."hostFunctions",
|
||||||
|
"plugin_method"."uiHints"
|
||||||
from
|
from
|
||||||
"plugin_action"
|
"plugin_method"
|
||||||
where
|
inner join "plugin" on "plugin"."id" = "plugin_method"."pluginId"
|
||||||
"pluginId" = $1
|
order by
|
||||||
|
"plugin_method"."name"
|
||||||
|
|||||||
@@ -1,70 +1,101 @@
|
|||||||
-- NOTE: This file is auto generated by ./sql-generator
|
-- NOTE: This file is auto generated by ./sql-generator
|
||||||
|
|
||||||
-- WorkflowRepository.getWorkflow
|
-- WorkflowRepository.search
|
||||||
select
|
select
|
||||||
*
|
"workflow"."id",
|
||||||
|
"workflow"."name",
|
||||||
|
"workflow"."description",
|
||||||
|
"workflow"."trigger",
|
||||||
|
"workflow"."enabled",
|
||||||
|
"workflow"."createdAt",
|
||||||
|
"workflow"."updatedAt",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"plugin"."name" as "pluginName",
|
||||||
|
"plugin_method"."name" as "methodName",
|
||||||
|
"workflow_step"."config",
|
||||||
|
"workflow_step"."enabled"
|
||||||
|
from
|
||||||
|
"workflow_step"
|
||||||
|
inner join "plugin_method" on "plugin_method"."id" = "workflow_step"."pluginMethodId"
|
||||||
|
inner join "plugin" on "plugin"."id" = "plugin_method"."pluginId"
|
||||||
|
where
|
||||||
|
"workflow"."id" = "workflow_step"."workflowId"
|
||||||
|
) as agg
|
||||||
|
) as "steps"
|
||||||
|
from
|
||||||
|
"workflow"
|
||||||
|
order by
|
||||||
|
"createdAt" desc
|
||||||
|
|
||||||
|
-- WorkflowRepository.get
|
||||||
|
select
|
||||||
|
"workflow"."id",
|
||||||
|
"workflow"."name",
|
||||||
|
"workflow"."description",
|
||||||
|
"workflow"."trigger",
|
||||||
|
"workflow"."enabled",
|
||||||
|
"workflow"."createdAt",
|
||||||
|
"workflow"."updatedAt",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"plugin"."name" as "pluginName",
|
||||||
|
"plugin_method"."name" as "methodName",
|
||||||
|
"workflow_step"."config",
|
||||||
|
"workflow_step"."enabled"
|
||||||
|
from
|
||||||
|
"workflow_step"
|
||||||
|
inner join "plugin_method" on "plugin_method"."id" = "workflow_step"."pluginMethodId"
|
||||||
|
inner join "plugin" on "plugin"."id" = "plugin_method"."pluginId"
|
||||||
|
where
|
||||||
|
"workflow"."id" = "workflow_step"."workflowId"
|
||||||
|
) as agg
|
||||||
|
) as "steps"
|
||||||
from
|
from
|
||||||
"workflow"
|
"workflow"
|
||||||
where
|
where
|
||||||
"id" = $1
|
"id" = $1
|
||||||
order by
|
|
||||||
"createdAt" desc
|
|
||||||
|
|
||||||
-- WorkflowRepository.getWorkflowsByOwner
|
-- WorkflowRepository.getForWorkflowRun
|
||||||
select
|
select
|
||||||
*
|
"workflow"."id",
|
||||||
|
"workflow"."name",
|
||||||
|
"workflow"."trigger",
|
||||||
|
(
|
||||||
|
select
|
||||||
|
coalesce(json_agg(agg), '[]')
|
||||||
|
from
|
||||||
|
(
|
||||||
|
select
|
||||||
|
"workflow_step"."id",
|
||||||
|
"workflow_step"."config",
|
||||||
|
"plugin_method"."pluginId" as "pluginId",
|
||||||
|
"plugin_method"."name" as "methodName",
|
||||||
|
"plugin_method"."types" as "types",
|
||||||
|
"plugin_method"."hostFunctions"
|
||||||
|
from
|
||||||
|
"workflow_step"
|
||||||
|
inner join "plugin_method" on "plugin_method"."id" = "workflow_step"."pluginMethodId"
|
||||||
|
where
|
||||||
|
"workflow_step"."workflowId" = "workflow"."id"
|
||||||
|
and "workflow_step"."enabled" = $1
|
||||||
|
) as agg
|
||||||
|
) as "steps"
|
||||||
from
|
from
|
||||||
"workflow"
|
"workflow"
|
||||||
where
|
where
|
||||||
"ownerId" = $1
|
"id" = $2
|
||||||
order by
|
|
||||||
"createdAt" desc
|
|
||||||
|
|
||||||
-- WorkflowRepository.getWorkflowsByTrigger
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"workflow"
|
|
||||||
where
|
|
||||||
"triggerType" = $1
|
|
||||||
and "enabled" = $2
|
|
||||||
|
|
||||||
-- WorkflowRepository.getWorkflowByOwnerAndTrigger
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"workflow"
|
|
||||||
where
|
|
||||||
"ownerId" = $1
|
|
||||||
and "triggerType" = $2
|
|
||||||
and "enabled" = $3
|
and "enabled" = $3
|
||||||
|
|
||||||
-- WorkflowRepository.deleteWorkflow
|
-- WorkflowRepository.delete
|
||||||
delete from "workflow"
|
delete from "workflow"
|
||||||
where
|
where
|
||||||
"id" = $1
|
"id" = $1
|
||||||
|
|
||||||
-- WorkflowRepository.getFilters
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"workflow_filter"
|
|
||||||
where
|
|
||||||
"workflowId" = $1
|
|
||||||
order by
|
|
||||||
"order" asc
|
|
||||||
|
|
||||||
-- WorkflowRepository.deleteFiltersByWorkflow
|
|
||||||
delete from "workflow_filter"
|
|
||||||
where
|
|
||||||
"workflowId" = $1
|
|
||||||
|
|
||||||
-- WorkflowRepository.getActions
|
|
||||||
select
|
|
||||||
*
|
|
||||||
from
|
|
||||||
"workflow_action"
|
|
||||||
where
|
|
||||||
"workflowId" = $1
|
|
||||||
order by
|
|
||||||
"order" asc
|
|
||||||
|
|||||||
@@ -346,7 +346,7 @@ const getEnv = (): EnvData => {
|
|||||||
root: folders.web,
|
root: folders.web,
|
||||||
indexHtml: join(folders.web, 'index.html'),
|
indexHtml: join(folders.web, 'index.html'),
|
||||||
},
|
},
|
||||||
corePlugin: join(buildFolder, 'corePlugin'),
|
corePlugin: join(buildFolder, 'plugins', 'immich-plugin-core'),
|
||||||
},
|
},
|
||||||
|
|
||||||
setup: {
|
setup: {
|
||||||
|
|||||||
@@ -119,6 +119,10 @@ export class LoggingRepository {
|
|||||||
logLevels = level ? LOG_LEVELS.slice(LOG_LEVELS.indexOf(level)) : [];
|
logLevels = level ? LOG_LEVELS.slice(LOG_LEVELS.indexOf(level)) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getLogLevel(): LogLevel {
|
||||||
|
return logLevels[0] || LogLevel.Fatal;
|
||||||
|
}
|
||||||
|
|
||||||
verbose(message: string, ...details: LogDetails) {
|
verbose(message: string, ...details: LogDetails) {
|
||||||
this.handleMessage(LogLevel.Verbose, message, details);
|
this.handleMessage(LogLevel.Verbose, message, details);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,176 +1,254 @@
|
|||||||
|
import { CallContext, Plugin as ExtismPlugin, newPlugin } from '@extism/extism';
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Kysely } from 'kysely';
|
import { createPool, Pool } from 'generic-pool';
|
||||||
|
import { Insertable, Kysely } from 'kysely';
|
||||||
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
import { jsonArrayFrom } from 'kysely/helpers/postgres';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
import { readdir } from 'node:fs/promises';
|
|
||||||
import { columns } from 'src/database';
|
import { columns } from 'src/database';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { PluginManifestDto } from 'src/dtos/plugin-manifest.dto';
|
import { PluginMethodSearchDto, PluginSearchDto } from 'src/dtos/plugin.dto';
|
||||||
|
import { LogLevel, WorkflowType } from 'src/enum';
|
||||||
|
import { LoggingRepository } from 'src/repositories/logging.repository';
|
||||||
import { DB } from 'src/schema';
|
import { DB } from 'src/schema';
|
||||||
|
import { PluginMethodTable } from 'src/schema/tables/plugin-method.table';
|
||||||
|
import { PluginTable } from 'src/schema/tables/plugin.table';
|
||||||
|
|
||||||
|
type PluginMethod = { pluginKey: string; methodName: string };
|
||||||
|
type PluginLoad = { key: string; label: string; wasmBytes: Buffer };
|
||||||
|
|
||||||
|
export type PluginHostFunction = (callContext: CallContext, input: bigint) => Promise<bigint> | bigint;
|
||||||
|
export type PluginLoadOptions = {
|
||||||
|
runInWorker?: boolean;
|
||||||
|
functions?: Record<string, PluginHostFunction>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type PluginMethodSearchResponse = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
pluginName: string;
|
||||||
|
types: WorkflowType[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const levels = {
|
||||||
|
[LogLevel.Verbose]: 'trace',
|
||||||
|
[LogLevel.Debug]: 'debug',
|
||||||
|
[LogLevel.Log]: 'info',
|
||||||
|
[LogLevel.Warn]: 'warn',
|
||||||
|
[LogLevel.Error]: 'error',
|
||||||
|
[LogLevel.Fatal]: 'error',
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
const asExtismLogLevel = (logLevel: LogLevel) => levels[logLevel] || 'info';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PluginRepository {
|
export class PluginRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
private pluginMap: Map<string, { label: string; pool: Pool<ExtismPlugin> }> = new Map();
|
||||||
|
|
||||||
/**
|
constructor(
|
||||||
* Loads a plugin from a validated manifest file in a transaction.
|
@InjectKysely() private db: Kysely<DB>,
|
||||||
* This ensures all plugin, filter, and action operations are atomic.
|
private logger: LoggingRepository,
|
||||||
* @param manifest The validated plugin manifest
|
) {
|
||||||
* @param basePath The base directory path where the plugin is located
|
this.logger.setContext(PluginRepository.name);
|
||||||
*/
|
|
||||||
async loadPlugin(manifest: PluginManifestDto, basePath: string) {
|
|
||||||
return this.db.transaction().execute(async (tx) => {
|
|
||||||
// Upsert the plugin
|
|
||||||
const plugin = await tx
|
|
||||||
.insertInto('plugin')
|
|
||||||
.values({
|
|
||||||
name: manifest.name,
|
|
||||||
title: manifest.title,
|
|
||||||
description: manifest.description,
|
|
||||||
author: manifest.author,
|
|
||||||
version: manifest.version,
|
|
||||||
wasmPath: `${basePath}/${manifest.wasm.path}`,
|
|
||||||
})
|
|
||||||
.onConflict((oc) =>
|
|
||||||
oc.column('name').doUpdateSet({
|
|
||||||
title: manifest.title,
|
|
||||||
description: manifest.description,
|
|
||||||
author: manifest.author,
|
|
||||||
version: manifest.version,
|
|
||||||
wasmPath: `${basePath}/${manifest.wasm.path}`,
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
.returningAll()
|
|
||||||
.executeTakeFirstOrThrow();
|
|
||||||
|
|
||||||
const filters = manifest.filters
|
|
||||||
? await tx
|
|
||||||
.insertInto('plugin_filter')
|
|
||||||
.values(
|
|
||||||
manifest.filters.map((filter) => ({
|
|
||||||
pluginId: plugin.id,
|
|
||||||
methodName: filter.methodName,
|
|
||||||
title: filter.title,
|
|
||||||
description: filter.description,
|
|
||||||
supportedContexts: filter.supportedContexts,
|
|
||||||
schema: filter.schema,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.onConflict((oc) =>
|
|
||||||
oc.column('methodName').doUpdateSet((eb) => ({
|
|
||||||
pluginId: eb.ref('excluded.pluginId'),
|
|
||||||
title: eb.ref('excluded.title'),
|
|
||||||
description: eb.ref('excluded.description'),
|
|
||||||
supportedContexts: eb.ref('excluded.supportedContexts'),
|
|
||||||
schema: eb.ref('excluded.schema'),
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.returningAll()
|
|
||||||
.execute()
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const actions = manifest.actions
|
|
||||||
? await tx
|
|
||||||
.insertInto('plugin_action')
|
|
||||||
.values(
|
|
||||||
manifest.actions.map((action) => ({
|
|
||||||
pluginId: plugin.id,
|
|
||||||
methodName: action.methodName,
|
|
||||||
title: action.title,
|
|
||||||
description: action.description,
|
|
||||||
supportedContexts: action.supportedContexts,
|
|
||||||
schema: action.schema,
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.onConflict((oc) =>
|
|
||||||
oc.column('methodName').doUpdateSet((eb) => ({
|
|
||||||
pluginId: eb.ref('excluded.pluginId'),
|
|
||||||
title: eb.ref('excluded.title'),
|
|
||||||
description: eb.ref('excluded.description'),
|
|
||||||
supportedContexts: eb.ref('excluded.supportedContexts'),
|
|
||||||
schema: eb.ref('excluded.schema'),
|
|
||||||
})),
|
|
||||||
)
|
|
||||||
.returningAll()
|
|
||||||
.execute()
|
|
||||||
: [];
|
|
||||||
|
|
||||||
return { plugin, filters, actions };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async readDirectory(path: string) {
|
|
||||||
return readdir(path, { withFileTypes: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
|
||||||
getPlugin(id: string) {
|
|
||||||
return this.db
|
|
||||||
.selectFrom('plugin')
|
|
||||||
.select((eb) => [
|
|
||||||
...columns.plugin,
|
|
||||||
jsonArrayFrom(
|
|
||||||
eb.selectFrom('plugin_filter').selectAll().whereRef('plugin_filter.pluginId', '=', 'plugin.id'),
|
|
||||||
).as('filters'),
|
|
||||||
jsonArrayFrom(
|
|
||||||
eb.selectFrom('plugin_action').selectAll().whereRef('plugin_action.pluginId', '=', 'plugin.id'),
|
|
||||||
).as('actions'),
|
|
||||||
])
|
|
||||||
.where('plugin.id', '=', id)
|
|
||||||
.executeTakeFirst();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.STRING] })
|
|
||||||
getPluginByName(name: string) {
|
|
||||||
return this.db
|
|
||||||
.selectFrom('plugin')
|
|
||||||
.select((eb) => [
|
|
||||||
...columns.plugin,
|
|
||||||
jsonArrayFrom(
|
|
||||||
eb.selectFrom('plugin_filter').selectAll().whereRef('plugin_filter.pluginId', '=', 'plugin.id'),
|
|
||||||
).as('filters'),
|
|
||||||
jsonArrayFrom(
|
|
||||||
eb.selectFrom('plugin_action').selectAll().whereRef('plugin_action.pluginId', '=', 'plugin.id'),
|
|
||||||
).as('actions'),
|
|
||||||
])
|
|
||||||
.where('plugin.name', '=', name)
|
|
||||||
.executeTakeFirst();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql()
|
@GenerateSql()
|
||||||
getAllPlugins() {
|
getForLoad() {
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('plugin')
|
.selectFrom('plugin')
|
||||||
.select((eb) => [
|
.select((eb) => [
|
||||||
...columns.plugin,
|
'plugin.id',
|
||||||
|
'plugin.name',
|
||||||
|
'plugin.version',
|
||||||
|
'plugin.wasmBytes',
|
||||||
jsonArrayFrom(
|
jsonArrayFrom(
|
||||||
eb.selectFrom('plugin_filter').selectAll().whereRef('plugin_filter.pluginId', '=', 'plugin.id'),
|
eb
|
||||||
).as('filters'),
|
.selectFrom('plugin_method')
|
||||||
jsonArrayFrom(
|
.whereRef('plugin_method.pluginId', '=', 'plugin.id')
|
||||||
eb.selectFrom('plugin_action').selectAll().whereRef('plugin_action.pluginId', '=', 'plugin.id'),
|
.select(['plugin_method.name', 'plugin_method.hostFunctions']),
|
||||||
).as('actions'),
|
).as('methods'),
|
||||||
])
|
])
|
||||||
|
.where('enabled', '=', true)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private queryBuilder() {
|
||||||
|
return this.db.selectFrom('plugin').select((eb) => [
|
||||||
|
'plugin.id',
|
||||||
|
'plugin.name',
|
||||||
|
'plugin.title',
|
||||||
|
'plugin.description',
|
||||||
|
'plugin.author',
|
||||||
|
'plugin.version',
|
||||||
|
'plugin.createdAt',
|
||||||
|
'plugin.updatedAt',
|
||||||
|
jsonArrayFrom(
|
||||||
|
eb
|
||||||
|
.selectFrom('plugin_method')
|
||||||
|
.select([...columns.pluginMethod, 'plugin.name as pluginName'])
|
||||||
|
.whereRef('plugin_method.pluginId', '=', 'plugin.id'),
|
||||||
|
).as('methods'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql()
|
||||||
|
search(dto: PluginSearchDto = {}) {
|
||||||
|
return this.queryBuilder()
|
||||||
|
.$if(!!dto.id, (qb) => qb.where('plugin.id', '=', dto.id!))
|
||||||
|
.$if(!!dto.name, (qb) => qb.where('plugin.name', '=', dto.name!))
|
||||||
|
.$if(!!dto.title, (qb) => qb.where('plugin.title', '=', dto.title!))
|
||||||
|
.$if(!!dto.description, (qb) => qb.where('plugin.description', '=', dto.description!))
|
||||||
|
.$if(!!dto.version, (qb) => qb.where('plugin.version', '=', dto.version!))
|
||||||
.orderBy('plugin.name')
|
.orderBy('plugin.name')
|
||||||
.execute();
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.STRING] })
|
||||||
getFilter(id: string) {
|
getByName(name: string) {
|
||||||
return this.db.selectFrom('plugin_filter').selectAll().where('id', '=', id).executeTakeFirst();
|
return this.queryBuilder().where('plugin.name', '=', name).executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getFiltersByPlugin(pluginId: string) {
|
get(id: string) {
|
||||||
return this.db.selectFrom('plugin_filter').selectAll().where('pluginId', '=', pluginId).execute();
|
return this.queryBuilder().where('plugin.id', '=', id).executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql()
|
||||||
getAction(id: string) {
|
getForValidation(): Promise<PluginMethodSearchResponse[]> {
|
||||||
return this.db.selectFrom('plugin_action').selectAll().where('id', '=', id).executeTakeFirst();
|
return this.db
|
||||||
|
.selectFrom('plugin_method')
|
||||||
|
.innerJoin('plugin', 'plugin_method.pluginId', 'plugin.id')
|
||||||
|
.select(['plugin_method.id', 'plugin_method.name', 'plugin.name as pluginName', 'plugin_method.types'])
|
||||||
|
.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql()
|
||||||
getActionsByPlugin(pluginId: string) {
|
searchMethods(dto: PluginMethodSearchDto = {}) {
|
||||||
return this.db.selectFrom('plugin_action').selectAll().where('pluginId', '=', pluginId).execute();
|
return this.db
|
||||||
|
.selectFrom('plugin_method')
|
||||||
|
.innerJoin('plugin', 'plugin.id', 'plugin_method.pluginId')
|
||||||
|
.select(['plugin.name as pluginName', 'plugin_method.pluginId', 'plugin_method.id', ...columns.pluginMethod])
|
||||||
|
.$if(!!dto.id, (qb) => qb.where('plugin_method.id', '=', dto.id!))
|
||||||
|
.$if(!!dto.name, (qb) => qb.where('plugin_method.name', '=', dto.name!))
|
||||||
|
.$if(!!dto.title, (qb) => qb.where('plugin_method.title', '=', dto.title!))
|
||||||
|
.$if(!!dto.type, (qb) => qb.where('plugin_method.types', '@>', [dto.type!]))
|
||||||
|
.$if(!!dto.description, (qb) => qb.where('plugin_method.description', '=', dto.description!))
|
||||||
|
.$if(!!dto.pluginVersion, (qb) => qb.where('plugin.version', '=', dto.pluginVersion!))
|
||||||
|
.$if(!!dto.pluginName, (qb) => qb.where('plugin.name', '=', dto.pluginName!))
|
||||||
|
.orderBy('plugin_method.name')
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
async upsert(dto: Insertable<PluginTable>, initialMethods: Omit<Insertable<PluginMethodTable>, 'pluginId'>[]) {
|
||||||
|
return this.db.transaction().execute(async (tx) => {
|
||||||
|
// Upsert the plugin
|
||||||
|
const plugin = await tx
|
||||||
|
.insertInto('plugin')
|
||||||
|
.values(dto)
|
||||||
|
.onConflict((oc) =>
|
||||||
|
oc.columns(['name', 'version']).doUpdateSet((eb) => ({
|
||||||
|
title: eb.ref('excluded.title'),
|
||||||
|
description: eb.ref('excluded.description'),
|
||||||
|
author: eb.ref('excluded.author'),
|
||||||
|
version: eb.ref('excluded.version'),
|
||||||
|
wasmBytes: eb.ref('excluded.wasmBytes'),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.returning(['id', 'name'])
|
||||||
|
.executeTakeFirstOrThrow();
|
||||||
|
|
||||||
|
// prune methods that no longer exist
|
||||||
|
if (initialMethods.length > 0) {
|
||||||
|
await tx
|
||||||
|
.deleteFrom('plugin_method')
|
||||||
|
.where('plugin_method.pluginId', '=', plugin.id)
|
||||||
|
.where(
|
||||||
|
'name',
|
||||||
|
'not in',
|
||||||
|
initialMethods.map((method) => method.name),
|
||||||
|
)
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
const methods =
|
||||||
|
initialMethods.length > 0
|
||||||
|
? await tx
|
||||||
|
.insertInto('plugin_method')
|
||||||
|
.values(initialMethods.map((method) => ({ ...method, pluginId: plugin.id })))
|
||||||
|
.onConflict((oc) =>
|
||||||
|
oc.columns(['pluginId', 'name']).doUpdateSet(({ ref }) => ({
|
||||||
|
pluginId: ref('excluded.pluginId'),
|
||||||
|
name: ref('excluded.name'),
|
||||||
|
title: ref('excluded.title'),
|
||||||
|
description: ref('excluded.description'),
|
||||||
|
types: ref('excluded.types'),
|
||||||
|
hostFunctions: ref('excluded.hostFunctions'),
|
||||||
|
uiHints: ref('excluded.uiHints'),
|
||||||
|
schema: ref('excluded.schema'),
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.returningAll()
|
||||||
|
.execute()
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return { ...plugin, methods };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async load({ key, label, wasmBytes }: PluginLoad, { runInWorker, functions }: PluginLoadOptions) {
|
||||||
|
const data = new Uint8Array(wasmBytes.buffer, wasmBytes.byteOffset, wasmBytes.byteLength);
|
||||||
|
const logger = LoggingRepository.create(`Plugin:${label}`);
|
||||||
|
const pool = createPool<ExtismPlugin>(
|
||||||
|
{
|
||||||
|
create: () =>
|
||||||
|
newPlugin(
|
||||||
|
{ wasm: [{ data }] },
|
||||||
|
{
|
||||||
|
useWasi: true,
|
||||||
|
runInWorker,
|
||||||
|
functions: {
|
||||||
|
'extism:host/user': functions ?? {},
|
||||||
|
},
|
||||||
|
logger: {
|
||||||
|
trace: (message) => logger.verbose(message),
|
||||||
|
info: (message) => logger.log(message),
|
||||||
|
debug: (message) => logger.debug(message),
|
||||||
|
warn: (message) => logger.warn(message),
|
||||||
|
error: (message) => logger.error(message),
|
||||||
|
} as Console,
|
||||||
|
logLevel: asExtismLogLevel(logger.getLogLevel()),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
destroy: (plugin) => plugin.close(),
|
||||||
|
},
|
||||||
|
{ min: 1, max: 5 },
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await pool.ready();
|
||||||
|
this.pluginMap.set(key, { pool, label });
|
||||||
|
} catch (error: Error | any) {
|
||||||
|
throw new Error(`Unable to instantiate plugin: ${key}`, { cause: error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async callMethod<T>({ pluginKey, methodName }: PluginMethod, input: unknown) {
|
||||||
|
const item = this.pluginMap.get(pluginKey);
|
||||||
|
if (!item) {
|
||||||
|
throw new Error(`No loaded plugin found for ${pluginKey}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { pool, label } = item;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const plugin = await pool.acquire();
|
||||||
|
try {
|
||||||
|
const result = await plugin.call(methodName, JSON.stringify(input));
|
||||||
|
return (result ? result.json() : result) as T;
|
||||||
|
} finally {
|
||||||
|
await pool.release(plugin);
|
||||||
|
}
|
||||||
|
} catch (error: Error | any) {
|
||||||
|
throw new Error(`Plugin method call failed: ${label}#${methodName}`, { cause: error });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,15 @@ import { Injectable } from '@nestjs/common';
|
|||||||
import archiver from 'archiver';
|
import archiver from 'archiver';
|
||||||
import chokidar, { ChokidarOptions } from 'chokidar';
|
import chokidar, { ChokidarOptions } from 'chokidar';
|
||||||
import { escapePath, glob, globStream } from 'fast-glob';
|
import { escapePath, glob, globStream } from 'fast-glob';
|
||||||
import { constants, createReadStream, createWriteStream, existsSync, mkdirSync, ReadOptionsWithBuffer } from 'node:fs';
|
import {
|
||||||
|
constants,
|
||||||
|
createReadStream,
|
||||||
|
createWriteStream,
|
||||||
|
Dirent,
|
||||||
|
existsSync,
|
||||||
|
mkdirSync,
|
||||||
|
ReadOptionsWithBuffer,
|
||||||
|
} from 'node:fs';
|
||||||
import fs from 'node:fs/promises';
|
import fs from 'node:fs/promises';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import { PassThrough, Readable, Writable } from 'node:stream';
|
import { PassThrough, Readable, Writable } from 'node:stream';
|
||||||
@@ -50,6 +58,10 @@ export class StorageRepository {
|
|||||||
return fs.readdir(folder);
|
return fs.readdir(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
readdirWithTypes(folder: string): Promise<Dirent[]> {
|
||||||
|
return fs.readdir(folder, { withFileTypes: true });
|
||||||
|
}
|
||||||
|
|
||||||
copyFile(source: string, target: string) {
|
copyFile(source: string, target: string) {
|
||||||
return fs.copyFile(source, target);
|
return fs.copyFile(source, target);
|
||||||
}
|
}
|
||||||
@@ -117,17 +129,24 @@ export class StorageRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async readFile(filepath: string, options?: ReadOptionsWithBuffer<Buffer>): Promise<Buffer> {
|
async readFile(filepath: string, options?: ReadOptionsWithBuffer<Buffer>): Promise<Buffer> {
|
||||||
const file = await fs.open(filepath);
|
// read a slice
|
||||||
try {
|
if (options) {
|
||||||
const { buffer } = await file.read(options);
|
const file = await fs.open(filepath);
|
||||||
return buffer as Buffer;
|
try {
|
||||||
} finally {
|
const { buffer } = await file.read(options);
|
||||||
await file.close();
|
return buffer as Buffer;
|
||||||
|
} finally {
|
||||||
|
await file.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read everything
|
||||||
|
return fs.readFile(filepath);
|
||||||
}
|
}
|
||||||
|
|
||||||
async readTextFile(filepath: string): Promise<string> {
|
async readJsonFile<T>(filepath: string): Promise<T> {
|
||||||
return fs.readFile(filepath, 'utf8');
|
const file = await fs.readFile(filepath, 'utf8');
|
||||||
|
return JSON.parse(file) as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
async checkFileExists(filepath: string, mode = constants.F_OK): Promise<boolean> {
|
async checkFileExists(filepath: string, mode = constants.F_OK): Promise<boolean> {
|
||||||
|
|||||||
@@ -1,149 +1,180 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { Insertable, Kysely, Updateable } from 'kysely';
|
import { Insertable, Kysely, Updateable } from 'kysely';
|
||||||
|
import { jsonArrayFrom, jsonObjectFrom } from 'kysely/helpers/postgres';
|
||||||
import { InjectKysely } from 'nestjs-kysely';
|
import { InjectKysely } from 'nestjs-kysely';
|
||||||
|
import { columns } from 'src/database';
|
||||||
import { DummyValue, GenerateSql } from 'src/decorators';
|
import { DummyValue, GenerateSql } from 'src/decorators';
|
||||||
import { PluginTriggerType } from 'src/enum';
|
import { WorkflowSearchDto } from 'src/dtos/workflow.dto';
|
||||||
import { DB } from 'src/schema';
|
import { DB } from 'src/schema';
|
||||||
import { WorkflowActionTable, WorkflowFilterTable, WorkflowTable } from 'src/schema/tables/workflow.table';
|
import { WorkflowStepTable } from 'src/schema/tables/workflow-step.table';
|
||||||
|
import { WorkflowTable } from 'src/schema/tables/workflow.table';
|
||||||
|
|
||||||
|
export type WorkflowStepUpsert = Omit<Insertable<WorkflowStepTable>, 'workflowId' | 'order'>;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class WorkflowRepository {
|
export class WorkflowRepository {
|
||||||
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
constructor(@InjectKysely() private db: Kysely<DB>) {}
|
||||||
|
|
||||||
|
private queryBuilder(db?: Kysely<DB>) {
|
||||||
|
return (db ?? this.db)
|
||||||
|
.selectFrom('workflow')
|
||||||
|
.select([
|
||||||
|
'workflow.id',
|
||||||
|
'workflow.name',
|
||||||
|
'workflow.description',
|
||||||
|
'workflow.trigger',
|
||||||
|
'workflow.enabled',
|
||||||
|
'workflow.createdAt',
|
||||||
|
'workflow.updatedAt',
|
||||||
|
])
|
||||||
|
.select((eb) => [
|
||||||
|
jsonArrayFrom(
|
||||||
|
eb
|
||||||
|
.selectFrom('workflow_step')
|
||||||
|
.innerJoin('plugin_method', 'plugin_method.id', 'workflow_step.pluginMethodId')
|
||||||
|
.innerJoin('plugin', 'plugin.id', 'plugin_method.pluginId')
|
||||||
|
.whereRef('workflow.id', '=', 'workflow_step.workflowId')
|
||||||
|
.select([
|
||||||
|
'plugin.name as pluginName',
|
||||||
|
'plugin_method.name as methodName',
|
||||||
|
'workflow_step.config',
|
||||||
|
'workflow_step.enabled',
|
||||||
|
]),
|
||||||
|
).as('steps'),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
getWorkflow(id: string) {
|
search(dto: WorkflowSearchDto & { ownerId?: string }) {
|
||||||
|
return this.queryBuilder()
|
||||||
|
.$if(!!dto.ownerId, (qb) => qb.where('ownerId', '=', dto.ownerId!))
|
||||||
|
.$if(!!dto.trigger, (qb) => qb.where('trigger', '=', dto.trigger!))
|
||||||
|
.$if(dto.enabled !== undefined, (qb) => qb.where('enabled', '=', dto.enabled!))
|
||||||
|
.orderBy('createdAt', 'desc')
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
|
get(id: string) {
|
||||||
|
return this.queryBuilder().where('id', '=', id).executeTakeFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
|
getForWorkflowRun(id: string) {
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('workflow')
|
.selectFrom('workflow')
|
||||||
.selectAll()
|
.select(['workflow.id', 'workflow.name', 'workflow.trigger'])
|
||||||
|
.select((eb) => [
|
||||||
|
jsonArrayFrom(
|
||||||
|
eb
|
||||||
|
.selectFrom('workflow_step')
|
||||||
|
.innerJoin('plugin_method', 'plugin_method.id', 'workflow_step.pluginMethodId')
|
||||||
|
.whereRef('workflow_step.workflowId', '=', 'workflow.id')
|
||||||
|
.where('workflow_step.enabled', '=', true)
|
||||||
|
.select([
|
||||||
|
'workflow_step.id',
|
||||||
|
'workflow_step.config',
|
||||||
|
'plugin_method.pluginId as pluginId',
|
||||||
|
'plugin_method.name as methodName',
|
||||||
|
'plugin_method.types as types',
|
||||||
|
'plugin_method.hostFunctions',
|
||||||
|
]),
|
||||||
|
).as('steps'),
|
||||||
|
])
|
||||||
.where('id', '=', id)
|
.where('id', '=', id)
|
||||||
.orderBy('createdAt', 'desc')
|
.where('enabled', '=', true)
|
||||||
.executeTakeFirst();
|
.executeTakeFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
create(dto: Insertable<WorkflowTable>, steps?: WorkflowStepUpsert[]) {
|
||||||
getWorkflowsByOwner(ownerId: string) {
|
return this.db.transaction().execute(async (tx) => {
|
||||||
return this.db
|
const { id } = await tx.insertInto('workflow').values(dto).returning(['id']).executeTakeFirstOrThrow();
|
||||||
.selectFrom('workflow')
|
return this.replaceAndReturn(tx, id, steps);
|
||||||
.selectAll()
|
|
||||||
.where('ownerId', '=', ownerId)
|
|
||||||
.orderBy('createdAt', 'desc')
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [PluginTriggerType.AssetCreate] })
|
|
||||||
getWorkflowsByTrigger(type: PluginTriggerType) {
|
|
||||||
return this.db
|
|
||||||
.selectFrom('workflow')
|
|
||||||
.selectAll()
|
|
||||||
.where('triggerType', '=', type)
|
|
||||||
.where('enabled', '=', true)
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID, PluginTriggerType.AssetCreate] })
|
|
||||||
getWorkflowByOwnerAndTrigger(ownerId: string, type: PluginTriggerType) {
|
|
||||||
return this.db
|
|
||||||
.selectFrom('workflow')
|
|
||||||
.selectAll()
|
|
||||||
.where('ownerId', '=', ownerId)
|
|
||||||
.where('triggerType', '=', type)
|
|
||||||
.where('enabled', '=', true)
|
|
||||||
.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
async createWorkflow(
|
|
||||||
workflow: Insertable<WorkflowTable>,
|
|
||||||
filters: Insertable<WorkflowFilterTable>[],
|
|
||||||
actions: Insertable<WorkflowActionTable>[],
|
|
||||||
) {
|
|
||||||
return await this.db.transaction().execute(async (tx) => {
|
|
||||||
const createdWorkflow = await tx.insertInto('workflow').values(workflow).returningAll().executeTakeFirstOrThrow();
|
|
||||||
|
|
||||||
if (filters.length > 0) {
|
|
||||||
const newFilters = filters.map((filter) => ({
|
|
||||||
...filter,
|
|
||||||
workflowId: createdWorkflow.id,
|
|
||||||
}));
|
|
||||||
|
|
||||||
await tx.insertInto('workflow_filter').values(newFilters).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actions.length > 0) {
|
|
||||||
const newActions = actions.map((action) => ({
|
|
||||||
...action,
|
|
||||||
workflowId: createdWorkflow.id,
|
|
||||||
}));
|
|
||||||
await tx.insertInto('workflow_action').values(newActions).execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
return createdWorkflow;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateWorkflow(
|
update(id: string, dto: Updateable<WorkflowTable>, steps?: WorkflowStepUpsert[]) {
|
||||||
id: string,
|
return this.db.transaction().execute(async (tx) => {
|
||||||
workflow: Updateable<WorkflowTable>,
|
if (Object.values(dto).some((prop) => prop !== undefined)) {
|
||||||
filters: Insertable<WorkflowFilterTable>[] | undefined,
|
await tx.updateTable('workflow').set(dto).where('id', '=', id).executeTakeFirstOrThrow();
|
||||||
actions: Insertable<WorkflowActionTable>[] | undefined,
|
|
||||||
) {
|
|
||||||
return await this.db.transaction().execute(async (trx) => {
|
|
||||||
if (Object.keys(workflow).length > 0) {
|
|
||||||
await trx.updateTable('workflow').set(workflow).where('id', '=', id).execute();
|
|
||||||
}
|
}
|
||||||
|
return this.replaceAndReturn(tx, id, steps);
|
||||||
if (filters !== undefined) {
|
|
||||||
await trx.deleteFrom('workflow_filter').where('workflowId', '=', id).execute();
|
|
||||||
if (filters.length > 0) {
|
|
||||||
const filtersWithWorkflowId = filters.map((filter) => ({
|
|
||||||
...filter,
|
|
||||||
workflowId: id,
|
|
||||||
}));
|
|
||||||
await trx.insertInto('workflow_filter').values(filtersWithWorkflowId).execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (actions !== undefined) {
|
|
||||||
await trx.deleteFrom('workflow_action').where('workflowId', '=', id).execute();
|
|
||||||
if (actions.length > 0) {
|
|
||||||
const actionsWithWorkflowId = actions.map((action) => ({
|
|
||||||
...action,
|
|
||||||
workflowId: id,
|
|
||||||
}));
|
|
||||||
await trx.insertInto('workflow_action').values(actionsWithWorkflowId).execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await trx.selectFrom('workflow').selectAll().where('id', '=', id).executeTakeFirstOrThrow();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async replaceAndReturn(tx: Kysely<DB>, workflowId: string, steps?: WorkflowStepUpsert[]) {
|
||||||
|
if (steps) {
|
||||||
|
await tx.deleteFrom('workflow_step').where('workflowId', '=', workflowId).execute();
|
||||||
|
if (steps.length > 0) {
|
||||||
|
await tx
|
||||||
|
.insertInto('workflow_step')
|
||||||
|
.values(
|
||||||
|
steps.map((step, i) => ({
|
||||||
|
workflowId,
|
||||||
|
enabled: step.enabled ?? true,
|
||||||
|
pluginMethodId: step.pluginMethodId,
|
||||||
|
config: step.config,
|
||||||
|
order: i,
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
.returningAll()
|
||||||
|
.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.queryBuilder(tx).where('id', '=', workflowId).executeTakeFirstOrThrow();
|
||||||
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
@GenerateSql({ params: [DummyValue.UUID] })
|
||||||
async deleteWorkflow(id: string) {
|
async delete(id: string) {
|
||||||
await this.db.deleteFrom('workflow').where('id', '=', id).execute();
|
await this.db.deleteFrom('workflow').where('id', '=', id).execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
getForAssetV1(assetId: string) {
|
||||||
getFilters(workflowId: string) {
|
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('workflow_filter')
|
.selectFrom('asset')
|
||||||
.selectAll()
|
.leftJoin('asset_exif', 'asset_exif.assetId', 'asset.id')
|
||||||
.where('workflowId', '=', workflowId)
|
.select((eb) => [
|
||||||
.orderBy('order', 'asc')
|
...columns.workflowAssetV1,
|
||||||
.execute();
|
jsonObjectFrom(
|
||||||
}
|
eb
|
||||||
|
.selectFrom('asset_exif')
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
.select([
|
||||||
async deleteFiltersByWorkflow(workflowId: string) {
|
'asset_exif.make',
|
||||||
await this.db.deleteFrom('workflow_filter').where('workflowId', '=', workflowId).execute();
|
'asset_exif.model',
|
||||||
}
|
'asset_exif.orientation',
|
||||||
|
'asset_exif.dateTimeOriginal',
|
||||||
@GenerateSql({ params: [DummyValue.UUID] })
|
'asset_exif.modifyDate',
|
||||||
getActions(workflowId: string) {
|
'asset_exif.exifImageWidth',
|
||||||
return this.db
|
'asset_exif.exifImageHeight',
|
||||||
.selectFrom('workflow_action')
|
'asset_exif.fileSizeInByte',
|
||||||
.selectAll()
|
'asset_exif.lensModel',
|
||||||
.where('workflowId', '=', workflowId)
|
'asset_exif.fNumber',
|
||||||
.orderBy('order', 'asc')
|
'asset_exif.focalLength',
|
||||||
.execute();
|
'asset_exif.iso',
|
||||||
|
'asset_exif.latitude',
|
||||||
|
'asset_exif.longitude',
|
||||||
|
'asset_exif.city',
|
||||||
|
'asset_exif.state',
|
||||||
|
'asset_exif.country',
|
||||||
|
'asset_exif.description',
|
||||||
|
'asset_exif.fps',
|
||||||
|
'asset_exif.exposureTime',
|
||||||
|
'asset_exif.livePhotoCID',
|
||||||
|
'asset_exif.timeZone',
|
||||||
|
'asset_exif.projectionType',
|
||||||
|
'asset_exif.profileDescription',
|
||||||
|
'asset_exif.colorspace',
|
||||||
|
'asset_exif.bitsPerSample',
|
||||||
|
'asset_exif.autoStackId',
|
||||||
|
'asset_exif.rating',
|
||||||
|
'asset_exif.tags',
|
||||||
|
'asset_exif.updatedAt',
|
||||||
|
])
|
||||||
|
.whereRef('asset_exif.assetId', '=', 'asset.id'),
|
||||||
|
).as('exifInfo'),
|
||||||
|
])
|
||||||
|
.where('id', '=', assetId)
|
||||||
|
.executeTakeFirstOrThrow();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,8 @@ import { PartnerAuditTable } from 'src/schema/tables/partner-audit.table';
|
|||||||
import { PartnerTable } from 'src/schema/tables/partner.table';
|
import { PartnerTable } from 'src/schema/tables/partner.table';
|
||||||
import { PersonAuditTable } from 'src/schema/tables/person-audit.table';
|
import { PersonAuditTable } from 'src/schema/tables/person-audit.table';
|
||||||
import { PersonTable } from 'src/schema/tables/person.table';
|
import { PersonTable } from 'src/schema/tables/person.table';
|
||||||
import { PluginActionTable, PluginFilterTable, PluginTable } from 'src/schema/tables/plugin.table';
|
import { PluginMethodTable } from 'src/schema/tables/plugin-method.table';
|
||||||
|
import { PluginTable } from 'src/schema/tables/plugin.table';
|
||||||
import { SessionTable } from 'src/schema/tables/session.table';
|
import { SessionTable } from 'src/schema/tables/session.table';
|
||||||
import { SharedLinkAssetTable } from 'src/schema/tables/shared-link-asset.table';
|
import { SharedLinkAssetTable } from 'src/schema/tables/shared-link-asset.table';
|
||||||
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
|
import { SharedLinkTable } from 'src/schema/tables/shared-link.table';
|
||||||
@@ -82,7 +83,8 @@ import {
|
|||||||
VideoStreamSessionTable,
|
VideoStreamSessionTable,
|
||||||
VideoStreamVariantTable,
|
VideoStreamVariantTable,
|
||||||
} from 'src/schema/tables/video-stream.table';
|
} from 'src/schema/tables/video-stream.table';
|
||||||
import { WorkflowActionTable, WorkflowFilterTable, WorkflowTable } from 'src/schema/tables/workflow.table';
|
import { WorkflowStepTable } from 'src/schema/tables/workflow-step.table';
|
||||||
|
import { WorkflowTable } from 'src/schema/tables/workflow.table';
|
||||||
|
|
||||||
@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql'])
|
@Extensions(['uuid-ossp', 'unaccent', 'cube', 'earthdistance', 'pg_trgm', 'plpgsql'])
|
||||||
@Database({ name: 'immich' })
|
@Database({ name: 'immich' })
|
||||||
@@ -143,11 +145,9 @@ export class ImmichDatabase {
|
|||||||
VideoStreamVariantTable,
|
VideoStreamVariantTable,
|
||||||
VideoStreamSegmentTable,
|
VideoStreamSegmentTable,
|
||||||
PluginTable,
|
PluginTable,
|
||||||
PluginFilterTable,
|
PluginMethodTable,
|
||||||
PluginActionTable,
|
|
||||||
WorkflowTable,
|
WorkflowTable,
|
||||||
WorkflowFilterTable,
|
WorkflowStepTable,
|
||||||
WorkflowActionTable,
|
|
||||||
];
|
];
|
||||||
|
|
||||||
functions = [
|
functions = [
|
||||||
@@ -264,10 +264,8 @@ export interface DB {
|
|||||||
video_stream_segment: VideoStreamSegmentTable;
|
video_stream_segment: VideoStreamSegmentTable;
|
||||||
|
|
||||||
plugin: PluginTable;
|
plugin: PluginTable;
|
||||||
plugin_filter: PluginFilterTable;
|
plugin_method: PluginMethodTable;
|
||||||
plugin_action: PluginActionTable;
|
|
||||||
|
|
||||||
workflow: WorkflowTable;
|
workflow: WorkflowTable;
|
||||||
workflow_filter: WorkflowFilterTable;
|
workflow_step: WorkflowStepTable;
|
||||||
workflow_action: WorkflowActionTable;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`ALTER TABLE "workflow" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7();`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`ALTER TABLE "workflow" DROP COLUMN "updateId";`.execute(db);
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
// take #2...
|
||||||
|
await sql`DROP TABLE "workflow_action";`.execute(db);
|
||||||
|
await sql`DROP TABLE "workflow_filter";`.execute(db);
|
||||||
|
await sql`DROP TABLE "workflow";`.execute(db);
|
||||||
|
await sql`DROP TABLE "plugin_action";`.execute(db);
|
||||||
|
await sql`DROP TABLE "plugin_filter";`.execute(db);
|
||||||
|
await sql`DROP TABLE "plugin";`.execute(db);
|
||||||
|
|
||||||
|
await sql`CREATE TABLE "plugin" (
|
||||||
|
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"enabled" boolean NOT NULL DEFAULT true,
|
||||||
|
"name" character varying NOT NULL,
|
||||||
|
"version" character varying NOT NULL,
|
||||||
|
"title" character varying NOT NULL,
|
||||||
|
"description" character varying NOT NULL,
|
||||||
|
"author" character varying NOT NULL,
|
||||||
|
"wasmBytes" bytea NOT NULL,
|
||||||
|
"createdAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
"updatedAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
CONSTRAINT "plugin_name_version_uq" UNIQUE ("name", "version"),
|
||||||
|
CONSTRAINT "plugin_name_uq" UNIQUE ("name"),
|
||||||
|
CONSTRAINT "plugin_pkey" PRIMARY KEY ("id")
|
||||||
|
);`.execute(db);
|
||||||
|
await sql`CREATE INDEX "plugin_name_idx" ON "plugin" ("name");`.execute(db);
|
||||||
|
await sql`CREATE TABLE "plugin_method" (
|
||||||
|
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"pluginId" uuid NOT NULL,
|
||||||
|
"name" character varying NOT NULL,
|
||||||
|
"title" character varying NOT NULL,
|
||||||
|
"description" character varying NOT NULL,
|
||||||
|
"types" character varying[] NOT NULL,
|
||||||
|
"hostFunctions" boolean NOT NULL DEFAULT false,
|
||||||
|
"uiHints" character varying[] NOT NULL DEFAULT '{}',
|
||||||
|
"schema" jsonb,
|
||||||
|
CONSTRAINT "plugin_method_pluginId_fkey" FOREIGN KEY ("pluginId") REFERENCES "plugin" ("id") ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "plugin_method_pluginId_name_uq" UNIQUE ("pluginId", "name"),
|
||||||
|
CONSTRAINT "plugin_method_pkey" PRIMARY KEY ("id")
|
||||||
|
);`.execute(db);
|
||||||
|
await sql`CREATE INDEX "plugin_method_pluginId_idx" ON "plugin_method" ("pluginId");`.execute(db);
|
||||||
|
await sql`CREATE TABLE "workflow" (
|
||||||
|
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"ownerId" uuid NOT NULL,
|
||||||
|
"trigger" character varying NOT NULL,
|
||||||
|
"name" character varying,
|
||||||
|
"description" character varying,
|
||||||
|
"createdAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
"updatedAt" timestamp with time zone NOT NULL DEFAULT now(),
|
||||||
|
"updateId" uuid NOT NULL DEFAULT immich_uuid_v7(),
|
||||||
|
"enabled" boolean NOT NULL DEFAULT true,
|
||||||
|
CONSTRAINT "workflow_ownerId_fkey" FOREIGN KEY ("ownerId") REFERENCES "user" ("id") ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "workflow_pkey" PRIMARY KEY ("id")
|
||||||
|
);`.execute(db);
|
||||||
|
await sql`CREATE INDEX "workflow_ownerId_idx" ON "workflow" ("ownerId");`.execute(db);
|
||||||
|
await sql`CREATE OR REPLACE TRIGGER "workflow_updatedAt"
|
||||||
|
BEFORE UPDATE ON "workflow"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION updated_at();`.execute(db);
|
||||||
|
await sql`CREATE TABLE "workflow_step" (
|
||||||
|
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||||
|
"enabled" boolean NOT NULL DEFAULT true,
|
||||||
|
"workflowId" uuid NOT NULL,
|
||||||
|
"pluginMethodId" uuid NOT NULL,
|
||||||
|
"config" jsonb,
|
||||||
|
"order" integer NOT NULL,
|
||||||
|
CONSTRAINT "workflow_step_workflowId_fkey" FOREIGN KEY ("workflowId") REFERENCES "workflow" ("id") ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "workflow_step_pluginMethodId_fkey" FOREIGN KEY ("pluginMethodId") REFERENCES "plugin_method" ("id") ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
|
CONSTRAINT "workflow_step_pkey" PRIMARY KEY ("id")
|
||||||
|
);`.execute(db);
|
||||||
|
await sql`CREATE INDEX "workflow_step_workflowId_idx" ON "workflow_step" ("workflowId");`.execute(db);
|
||||||
|
await sql`CREATE INDEX "workflow_step_pluginMethodId_idx" ON "workflow_step" ("pluginMethodId");`.execute(db);
|
||||||
|
await sql`INSERT INTO "migration_overrides" ("name", "value") VALUES ('trigger_workflow_updatedAt', '{"type":"trigger","name":"workflow_updatedAt","sql":"CREATE OR REPLACE TRIGGER \\"workflow_updatedAt\\"\\n BEFORE UPDATE ON \\"workflow\\"\\n FOR EACH ROW\\n EXECUTE FUNCTION updated_at();"}'::jsonb);`.execute(
|
||||||
|
db,
|
||||||
|
);
|
||||||
|
await sql`DELETE FROM "migration_overrides" WHERE "name" = 'index_plugin_filter_supportedContexts_idx';`.execute(db);
|
||||||
|
await sql`DELETE FROM "migration_overrides" WHERE "name" = 'index_plugin_action_supportedContexts_idx';`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(): Promise<void> {
|
||||||
|
// not supported
|
||||||
|
}
|
||||||
@@ -111,7 +111,7 @@ export class AssetExifTable {
|
|||||||
tags!: string[] | null;
|
tags!: string[] | null;
|
||||||
|
|
||||||
@UpdateDateColumn({ default: () => 'clock_timestamp()' })
|
@UpdateDateColumn({ default: () => 'clock_timestamp()' })
|
||||||
updatedAt!: Generated<Date>;
|
updatedAt!: Generated<Timestamp>;
|
||||||
|
|
||||||
@UpdateIdColumn({ index: true })
|
@UpdateIdColumn({ index: true })
|
||||||
updateId!: Generated<string>;
|
updateId!: Generated<string>;
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
import { Column, ForeignKeyColumn, Generated, PrimaryGeneratedColumn, Table, Unique } from '@immich/sql-tools';
|
||||||
|
import { JsonSchemaDto } from 'src/dtos/json-schema.dto';
|
||||||
|
import { WorkflowType } from 'src/enum';
|
||||||
|
import { PluginTable } from 'src/schema/tables/plugin.table';
|
||||||
|
|
||||||
|
@Unique({ columns: ['pluginId', 'name'] })
|
||||||
|
@Table('plugin_method')
|
||||||
|
export class PluginMethodTable {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id!: Generated<string>;
|
||||||
|
|
||||||
|
@ForeignKeyColumn(() => PluginTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
||||||
|
pluginId!: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
name!: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
title!: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
description!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'character varying', array: true })
|
||||||
|
types!: Generated<WorkflowType[]>;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: false })
|
||||||
|
hostFunctions!: Generated<boolean>;
|
||||||
|
|
||||||
|
@Column({ type: 'jsonb', nullable: true })
|
||||||
|
schema!: JsonSchemaDto | null;
|
||||||
|
|
||||||
|
@Column({ type: 'character varying', default: [], array: true })
|
||||||
|
uiHints!: Generated<string[]>;
|
||||||
|
}
|
||||||
@@ -1,25 +1,29 @@
|
|||||||
import {
|
import {
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
ForeignKeyColumn,
|
|
||||||
Generated,
|
Generated,
|
||||||
Index,
|
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
Table,
|
Table,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
|
Unique,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from '@immich/sql-tools';
|
} from '@immich/sql-tools';
|
||||||
import { PluginContext } from 'src/enum';
|
|
||||||
import type { JSONSchema } from 'src/types/plugin-schema.types';
|
|
||||||
|
|
||||||
|
@Unique({ columns: ['name', 'version'] })
|
||||||
@Table('plugin')
|
@Table('plugin')
|
||||||
export class PluginTable {
|
export class PluginTable {
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
id!: Generated<string>;
|
id!: Generated<string>;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true })
|
||||||
|
enabled!: Generated<boolean>;
|
||||||
|
|
||||||
@Column({ index: true, unique: true })
|
@Column({ index: true, unique: true })
|
||||||
name!: string;
|
name!: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
version!: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
title!: string;
|
title!: string;
|
||||||
|
|
||||||
@@ -29,11 +33,8 @@ export class PluginTable {
|
|||||||
@Column()
|
@Column()
|
||||||
author!: string;
|
author!: string;
|
||||||
|
|
||||||
@Column()
|
@Column({ type: 'bytea' })
|
||||||
version!: string;
|
wasmBytes!: Buffer;
|
||||||
|
|
||||||
@Column()
|
|
||||||
wasmPath!: string;
|
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdAt!: Generated<Timestamp>;
|
createdAt!: Generated<Timestamp>;
|
||||||
@@ -41,55 +42,3 @@ export class PluginTable {
|
|||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
updatedAt!: Generated<Timestamp>;
|
updatedAt!: Generated<Timestamp>;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Index({ columns: ['supportedContexts'], using: 'gin' })
|
|
||||||
@Table('plugin_filter')
|
|
||||||
export class PluginFilterTable {
|
|
||||||
@PrimaryGeneratedColumn('uuid')
|
|
||||||
id!: Generated<string>;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => PluginTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
|
||||||
@Column({ index: true })
|
|
||||||
pluginId!: string;
|
|
||||||
|
|
||||||
@Column({ index: true, unique: true })
|
|
||||||
methodName!: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
title!: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
description!: string;
|
|
||||||
|
|
||||||
@Column({ type: 'character varying', array: true })
|
|
||||||
supportedContexts!: Generated<PluginContext[]>;
|
|
||||||
|
|
||||||
@Column({ type: 'jsonb', nullable: true })
|
|
||||||
schema!: JSONSchema | null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Index({ columns: ['supportedContexts'], using: 'gin' })
|
|
||||||
@Table('plugin_action')
|
|
||||||
export class PluginActionTable {
|
|
||||||
@PrimaryGeneratedColumn('uuid')
|
|
||||||
id!: Generated<string>;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => PluginTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
|
||||||
@Column({ index: true })
|
|
||||||
pluginId!: string;
|
|
||||||
|
|
||||||
@Column({ index: true, unique: true })
|
|
||||||
methodName!: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
title!: string;
|
|
||||||
|
|
||||||
@Column()
|
|
||||||
description!: string;
|
|
||||||
|
|
||||||
@Column({ type: 'character varying', array: true })
|
|
||||||
supportedContexts!: Generated<PluginContext[]>;
|
|
||||||
|
|
||||||
@Column({ type: 'jsonb', nullable: true })
|
|
||||||
schema!: JSONSchema | null;
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { WorkflowStepConfig } from '@immich/plugin-sdk';
|
||||||
|
import { Column, ForeignKeyColumn, PrimaryGeneratedColumn, Table } from '@immich/sql-tools';
|
||||||
|
import { Generated } from 'kysely';
|
||||||
|
import { PluginMethodTable } from 'src/schema/tables/plugin-method.table';
|
||||||
|
import { WorkflowTable } from 'src/schema/tables/workflow.table';
|
||||||
|
|
||||||
|
@Table('workflow_step')
|
||||||
|
export class WorkflowStepTable {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id!: Generated<string>;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', default: true })
|
||||||
|
enabled!: boolean;
|
||||||
|
|
||||||
|
@ForeignKeyColumn(() => WorkflowTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
||||||
|
workflowId!: string;
|
||||||
|
|
||||||
|
@ForeignKeyColumn(() => PluginMethodTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
||||||
|
pluginMethodId!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'jsonb', nullable: true })
|
||||||
|
config!: WorkflowStepConfig | null;
|
||||||
|
|
||||||
|
@Column({ type: 'integer' })
|
||||||
|
order!: number;
|
||||||
|
}
|
||||||
@@ -3,17 +3,17 @@ import {
|
|||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
ForeignKeyColumn,
|
ForeignKeyColumn,
|
||||||
Generated,
|
Generated,
|
||||||
Index,
|
|
||||||
PrimaryGeneratedColumn,
|
PrimaryGeneratedColumn,
|
||||||
Table,
|
Table,
|
||||||
Timestamp,
|
Timestamp,
|
||||||
|
UpdateDateColumn,
|
||||||
} from '@immich/sql-tools';
|
} from '@immich/sql-tools';
|
||||||
import { PluginTriggerType } from 'src/enum';
|
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||||
import { PluginActionTable, PluginFilterTable } from 'src/schema/tables/plugin.table';
|
import { WorkflowTrigger } from 'src/enum';
|
||||||
import { UserTable } from 'src/schema/tables/user.table';
|
import { UserTable } from 'src/schema/tables/user.table';
|
||||||
import type { ActionConfig, FilterConfig } from 'src/types/plugin-schema.types';
|
|
||||||
|
|
||||||
@Table('workflow')
|
@Table('workflow')
|
||||||
|
@UpdatedAtTrigger('workflow_updatedAt')
|
||||||
export class WorkflowTable {
|
export class WorkflowTable {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id!: Generated<string>;
|
id!: Generated<string>;
|
||||||
@@ -22,57 +22,23 @@ export class WorkflowTable {
|
|||||||
ownerId!: string;
|
ownerId!: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
triggerType!: PluginTriggerType;
|
trigger!: WorkflowTrigger;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
name!: string | null;
|
name!: string | null;
|
||||||
|
|
||||||
@Column()
|
@Column({ nullable: true })
|
||||||
description!: string;
|
description!: string | null;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdAt!: Generated<Timestamp>;
|
createdAt!: Generated<Timestamp>;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt!: Generated<Timestamp>;
|
||||||
|
|
||||||
|
@UpdateIdColumn()
|
||||||
|
updateId!: Generated<string>;
|
||||||
|
|
||||||
@Column({ type: 'boolean', default: true })
|
@Column({ type: 'boolean', default: true })
|
||||||
enabled!: boolean;
|
enabled!: Generated<boolean>;
|
||||||
}
|
|
||||||
|
|
||||||
@Index({ columns: ['workflowId', 'order'] })
|
|
||||||
@Index({ columns: ['pluginFilterId'] })
|
|
||||||
@Table('workflow_filter')
|
|
||||||
export class WorkflowFilterTable {
|
|
||||||
@PrimaryGeneratedColumn('uuid')
|
|
||||||
id!: Generated<string>;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => WorkflowTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
|
||||||
workflowId!: Generated<string>;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => PluginFilterTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
|
||||||
pluginFilterId!: string;
|
|
||||||
|
|
||||||
@Column({ type: 'jsonb', nullable: true })
|
|
||||||
filterConfig!: FilterConfig | null;
|
|
||||||
|
|
||||||
@Column({ type: 'integer' })
|
|
||||||
order!: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Index({ columns: ['workflowId', 'order'] })
|
|
||||||
@Index({ columns: ['pluginActionId'] })
|
|
||||||
@Table('workflow_action')
|
|
||||||
export class WorkflowActionTable {
|
|
||||||
@PrimaryGeneratedColumn('uuid')
|
|
||||||
id!: Generated<string>;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => WorkflowTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
|
||||||
workflowId!: Generated<string>;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => PluginActionTable, { onDelete: 'CASCADE', onUpdate: 'CASCADE' })
|
|
||||||
pluginActionId!: string;
|
|
||||||
|
|
||||||
@Column({ type: 'jsonb', nullable: true })
|
|
||||||
actionConfig!: ActionConfig | null;
|
|
||||||
|
|
||||||
@Column({ type: 'integer' })
|
|
||||||
order!: number;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ import { ViewRepository } from 'src/repositories/view-repository';
|
|||||||
import { WebsocketRepository } from 'src/repositories/websocket.repository';
|
import { WebsocketRepository } from 'src/repositories/websocket.repository';
|
||||||
import { WorkflowRepository } from 'src/repositories/workflow.repository';
|
import { WorkflowRepository } from 'src/repositories/workflow.repository';
|
||||||
import { UserTable } from 'src/schema/tables/user.table';
|
import { UserTable } from 'src/schema/tables/user.table';
|
||||||
|
import { ClassConstructor } from 'src/types';
|
||||||
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
|
import { AccessRequest, checkAccess, requireAccess } from 'src/utils/access';
|
||||||
import { getConfig, updateConfig } from 'src/utils/config';
|
import { getConfig, updateConfig } from 'src/utils/config';
|
||||||
|
|
||||||
@@ -187,6 +188,66 @@ export class BaseService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static create<T extends BaseService>(Service: ClassConstructor<T>, ctx: BaseService) {
|
||||||
|
const service = new Service(
|
||||||
|
LoggingRepository.create(),
|
||||||
|
ctx.accessRepository,
|
||||||
|
ctx.activityRepository,
|
||||||
|
ctx.albumRepository,
|
||||||
|
ctx.albumUserRepository,
|
||||||
|
ctx.apiKeyRepository,
|
||||||
|
ctx.appRepository,
|
||||||
|
ctx.assetRepository,
|
||||||
|
ctx.assetEditRepository,
|
||||||
|
ctx.assetJobRepository,
|
||||||
|
ctx.configRepository,
|
||||||
|
ctx.cronRepository,
|
||||||
|
ctx.cryptoRepository,
|
||||||
|
ctx.databaseRepository,
|
||||||
|
ctx.downloadRepository,
|
||||||
|
ctx.duplicateRepository,
|
||||||
|
ctx.emailRepository,
|
||||||
|
ctx.eventRepository,
|
||||||
|
ctx.jobRepository,
|
||||||
|
ctx.libraryRepository,
|
||||||
|
ctx.machineLearningRepository,
|
||||||
|
ctx.mapRepository,
|
||||||
|
ctx.mediaRepository,
|
||||||
|
ctx.memoryRepository,
|
||||||
|
ctx.metadataRepository,
|
||||||
|
ctx.moveRepository,
|
||||||
|
ctx.notificationRepository,
|
||||||
|
ctx.oauthRepository,
|
||||||
|
ctx.ocrRepository,
|
||||||
|
ctx.partnerRepository,
|
||||||
|
ctx.personRepository,
|
||||||
|
ctx.pluginRepository,
|
||||||
|
ctx.processRepository,
|
||||||
|
ctx.searchRepository,
|
||||||
|
ctx.serverInfoRepository,
|
||||||
|
ctx.sessionRepository,
|
||||||
|
ctx.sharedLinkRepository,
|
||||||
|
ctx.sharedLinkAssetRepository,
|
||||||
|
ctx.stackRepository,
|
||||||
|
ctx.storageRepository,
|
||||||
|
ctx.syncRepository,
|
||||||
|
ctx.syncCheckpointRepository,
|
||||||
|
ctx.systemMetadataRepository,
|
||||||
|
ctx.tagRepository,
|
||||||
|
ctx.telemetryRepository,
|
||||||
|
ctx.trashRepository,
|
||||||
|
ctx.userRepository,
|
||||||
|
ctx.versionRepository,
|
||||||
|
ctx.viewRepository,
|
||||||
|
ctx.websocketRepository,
|
||||||
|
ctx.workflowRepository,
|
||||||
|
);
|
||||||
|
|
||||||
|
service.logger.setContext(this.name);
|
||||||
|
|
||||||
|
return service as T;
|
||||||
|
}
|
||||||
|
|
||||||
get worker() {
|
get worker() {
|
||||||
return this.configRepository.getWorker();
|
return this.configRepository.getWorker();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ import { UserAdminService } from 'src/services/user-admin.service';
|
|||||||
import { UserService } from 'src/services/user.service';
|
import { UserService } from 'src/services/user.service';
|
||||||
import { VersionService } from 'src/services/version.service';
|
import { VersionService } from 'src/services/version.service';
|
||||||
import { ViewService } from 'src/services/view.service';
|
import { ViewService } from 'src/services/view.service';
|
||||||
|
import { WorkflowExecutionService } from 'src/services/workflow-execution.service';
|
||||||
import { WorkflowService } from 'src/services/workflow.service';
|
import { WorkflowService } from 'src/services/workflow.service';
|
||||||
|
|
||||||
export const services = [
|
export const services = [
|
||||||
@@ -93,5 +94,6 @@ export const services = [
|
|||||||
UserService,
|
UserService,
|
||||||
VersionService,
|
VersionService,
|
||||||
ViewService,
|
ViewService,
|
||||||
|
WorkflowExecutionService,
|
||||||
WorkflowService,
|
WorkflowService,
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,120 +0,0 @@
|
|||||||
import { CurrentPlugin } from '@extism/extism';
|
|
||||||
import { UnauthorizedException } from '@nestjs/common';
|
|
||||||
import { Updateable } from 'kysely';
|
|
||||||
import { Permission } from 'src/enum';
|
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
|
||||||
import { AlbumRepository } from 'src/repositories/album.repository';
|
|
||||||
import { AssetRepository } from 'src/repositories/asset.repository';
|
|
||||||
import { CryptoRepository } from 'src/repositories/crypto.repository';
|
|
||||||
import { LoggingRepository } from 'src/repositories/logging.repository';
|
|
||||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
|
||||||
import { requireAccess } from 'src/utils/access';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Plugin host functions that are exposed to WASM plugins via Extism.
|
|
||||||
* These functions allow plugins to interact with the Immich system.
|
|
||||||
*/
|
|
||||||
export class PluginHostFunctions {
|
|
||||||
constructor(
|
|
||||||
private assetRepository: AssetRepository,
|
|
||||||
private albumRepository: AlbumRepository,
|
|
||||||
private accessRepository: AccessRepository,
|
|
||||||
private cryptoRepository: CryptoRepository,
|
|
||||||
private logger: LoggingRepository,
|
|
||||||
private pluginJwtSecret: string,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates Extism host function bindings for the plugin.
|
|
||||||
* These are the functions that WASM plugins can call.
|
|
||||||
*/
|
|
||||||
getHostFunctions() {
|
|
||||||
return {
|
|
||||||
'extism:host/user': {
|
|
||||||
updateAsset: (cp: CurrentPlugin, offs: bigint) => this.handleUpdateAsset(cp, offs),
|
|
||||||
addAssetToAlbum: (cp: CurrentPlugin, offs: bigint) => this.handleAddAssetToAlbum(cp, offs),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Host function wrapper for updateAsset.
|
|
||||||
* Reads the input from the plugin, parses it, and calls the actual update function.
|
|
||||||
*/
|
|
||||||
private async handleUpdateAsset(cp: CurrentPlugin, offs: bigint) {
|
|
||||||
const input = JSON.parse(cp.read(offs)!.text());
|
|
||||||
await this.updateAsset(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Host function wrapper for addAssetToAlbum.
|
|
||||||
* Reads the input from the plugin, parses it, and calls the actual add function.
|
|
||||||
*/
|
|
||||||
private async handleAddAssetToAlbum(cp: CurrentPlugin, offs: bigint) {
|
|
||||||
const input = JSON.parse(cp.read(offs)!.text());
|
|
||||||
await this.addAssetToAlbum(input);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Validates the JWT token and returns the auth context.
|
|
||||||
*/
|
|
||||||
private validateToken(authToken: string): { userId: string } {
|
|
||||||
try {
|
|
||||||
const auth = this.cryptoRepository.verifyJwt<{ userId: string }>(authToken, this.pluginJwtSecret);
|
|
||||||
if (!auth.userId) {
|
|
||||||
throw new UnauthorizedException('Invalid token: missing userId');
|
|
||||||
}
|
|
||||||
return auth;
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error('Token validation failed:', error);
|
|
||||||
throw new UnauthorizedException('Invalid token');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Updates an asset with the given properties.
|
|
||||||
*/
|
|
||||||
async updateAsset(input: { authToken: string } & Updateable<AssetTable> & { id: string }) {
|
|
||||||
const { authToken, id, ...assetData } = input;
|
|
||||||
|
|
||||||
// Validate token
|
|
||||||
const auth = this.validateToken(authToken);
|
|
||||||
|
|
||||||
// Check access to the asset
|
|
||||||
await requireAccess(this.accessRepository, {
|
|
||||||
auth: { user: { id: auth.userId } } as any,
|
|
||||||
permission: Permission.AssetUpdate,
|
|
||||||
ids: [id],
|
|
||||||
});
|
|
||||||
|
|
||||||
this.logger.log(`Updating asset ${id} -- ${JSON.stringify(assetData)}`);
|
|
||||||
await this.assetRepository.update({ id, ...assetData });
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an asset to an album.
|
|
||||||
*/
|
|
||||||
async addAssetToAlbum(input: { authToken: string; assetId: string; albumId: string }) {
|
|
||||||
const { authToken, assetId, albumId } = input;
|
|
||||||
|
|
||||||
// Validate token
|
|
||||||
const auth = this.validateToken(authToken);
|
|
||||||
|
|
||||||
// Check access to both the asset and the album
|
|
||||||
await requireAccess(this.accessRepository, {
|
|
||||||
auth: { user: { id: auth.userId } } as any,
|
|
||||||
permission: Permission.AssetRead,
|
|
||||||
ids: [assetId],
|
|
||||||
});
|
|
||||||
|
|
||||||
await requireAccess(this.accessRepository, {
|
|
||||||
auth: { user: { id: auth.userId } } as any,
|
|
||||||
permission: Permission.AlbumUpdate,
|
|
||||||
ids: [albumId],
|
|
||||||
});
|
|
||||||
|
|
||||||
this.logger.log(`Adding asset ${assetId} to album ${albumId}`);
|
|
||||||
await this.albumRepository.addAssetIds(albumId, [assetId]);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,313 +1,34 @@
|
|||||||
import { Plugin as ExtismPlugin, newPlugin } from '@extism/extism';
|
|
||||||
import { BadRequestException, Injectable } from '@nestjs/common';
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
import { join } from 'node:path';
|
import {
|
||||||
import { Asset, WorkflowAction, WorkflowFilter } from 'src/database';
|
mapMethod,
|
||||||
import { OnEvent, OnJob } from 'src/decorators';
|
mapPlugin,
|
||||||
import { PluginManifestDto, PluginManifestSchema } from 'src/dtos/plugin-manifest.dto';
|
PluginMethodResponseDto,
|
||||||
import { mapPlugin, PluginResponseDto, PluginTriggerResponseDto } from 'src/dtos/plugin.dto';
|
PluginMethodSearchDto,
|
||||||
import { JobName, JobStatus, PluginTriggerType, QueueName } from 'src/enum';
|
PluginResponseDto,
|
||||||
import { pluginTriggers } from 'src/plugins';
|
PluginSearchDto,
|
||||||
import { ArgOf } from 'src/repositories/event.repository';
|
} from 'src/dtos/plugin.dto';
|
||||||
import { BaseService } from 'src/services/base.service';
|
import { BaseService } from 'src/services/base.service';
|
||||||
import { PluginHostFunctions } from 'src/services/plugin-host.functions';
|
import { isMethodCompatible } from 'src/utils/workflow';
|
||||||
import { IWorkflowJob, JobItem, JobOf, WorkflowData } from 'src/types';
|
|
||||||
|
|
||||||
interface WorkflowContext {
|
|
||||||
authToken: string;
|
|
||||||
asset: Asset;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PluginInput<T = unknown> {
|
|
||||||
authToken: string;
|
|
||||||
config: T;
|
|
||||||
data: {
|
|
||||||
asset: Asset;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class PluginService extends BaseService {
|
export class PluginService extends BaseService {
|
||||||
private pluginJwtSecret!: string;
|
async search(dto: PluginSearchDto): Promise<PluginResponseDto[]> {
|
||||||
private loadedPlugins: Map<string, ExtismPlugin> = new Map();
|
const plugins = await this.pluginRepository.search(dto);
|
||||||
private hostFunctions!: PluginHostFunctions;
|
|
||||||
|
|
||||||
@OnEvent({ name: 'AppBootstrap' })
|
|
||||||
async onBootstrap() {
|
|
||||||
this.pluginJwtSecret = this.cryptoRepository.randomBytesAsText(32);
|
|
||||||
|
|
||||||
await this.loadPluginsFromManifests();
|
|
||||||
|
|
||||||
this.hostFunctions = new PluginHostFunctions(
|
|
||||||
this.assetRepository,
|
|
||||||
this.albumRepository,
|
|
||||||
this.accessRepository,
|
|
||||||
this.cryptoRepository,
|
|
||||||
this.logger,
|
|
||||||
this.pluginJwtSecret,
|
|
||||||
);
|
|
||||||
|
|
||||||
await this.loadPlugins();
|
|
||||||
}
|
|
||||||
|
|
||||||
getTriggers(): PluginTriggerResponseDto[] {
|
|
||||||
return pluginTriggers;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// CRUD operations for plugins
|
|
||||||
//
|
|
||||||
async getAll(): Promise<PluginResponseDto[]> {
|
|
||||||
const plugins = await this.pluginRepository.getAllPlugins();
|
|
||||||
return plugins.map((plugin) => mapPlugin(plugin));
|
return plugins.map((plugin) => mapPlugin(plugin));
|
||||||
}
|
}
|
||||||
|
|
||||||
async get(id: string): Promise<PluginResponseDto> {
|
async get(id: string): Promise<PluginResponseDto> {
|
||||||
const plugin = await this.pluginRepository.getPlugin(id);
|
const plugin = await this.pluginRepository.get(id);
|
||||||
if (!plugin) {
|
if (!plugin) {
|
||||||
throw new BadRequestException('Plugin not found');
|
throw new BadRequestException('Plugin not found');
|
||||||
}
|
}
|
||||||
return mapPlugin(plugin);
|
return mapPlugin(plugin);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////
|
async searchMethods(dto: PluginMethodSearchDto): Promise<PluginMethodResponseDto[]> {
|
||||||
// Plugin Loader
|
const methods = await this.pluginRepository.searchMethods(dto);
|
||||||
//////////////////////////////////////////
|
return methods
|
||||||
async loadPluginsFromManifests(): Promise<void> {
|
.filter((method) => !dto.trigger || isMethodCompatible(method, dto.trigger))
|
||||||
// Load core plugin
|
.map((method) => mapMethod(method));
|
||||||
const { resourcePaths, plugins } = this.configRepository.getEnv();
|
|
||||||
const coreManifestPath = `${resourcePaths.corePlugin}/manifest.json`;
|
|
||||||
|
|
||||||
const coreManifest = await this.readAndValidateManifest(coreManifestPath);
|
|
||||||
await this.loadPluginToDatabase(coreManifest, resourcePaths.corePlugin);
|
|
||||||
|
|
||||||
this.logger.log(`Successfully processed core plugin: ${coreManifest.name} (version ${coreManifest.version})`);
|
|
||||||
|
|
||||||
// Load external plugins
|
|
||||||
if (plugins.external.allow && plugins.external.installFolder) {
|
|
||||||
await this.loadExternalPlugins(plugins.external.installFolder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadExternalPlugins(installFolder: string): Promise<void> {
|
|
||||||
try {
|
|
||||||
const entries = await this.pluginRepository.readDirectory(installFolder);
|
|
||||||
|
|
||||||
for (const entry of entries) {
|
|
||||||
if (!entry.isDirectory()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginFolder = join(installFolder, entry.name);
|
|
||||||
const manifestPath = join(pluginFolder, 'manifest.json');
|
|
||||||
try {
|
|
||||||
const manifest = await this.readAndValidateManifest(manifestPath);
|
|
||||||
await this.loadPluginToDatabase(manifest, pluginFolder);
|
|
||||||
|
|
||||||
this.logger.log(`Successfully processed external plugin: ${manifest.name} (version ${manifest.version})`);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.warn(`Failed to load external plugin from ${manifestPath}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to scan external plugins folder ${installFolder}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async loadPluginToDatabase(manifest: PluginManifestDto, basePath: string): Promise<void> {
|
|
||||||
const currentPlugin = await this.pluginRepository.getPluginByName(manifest.name);
|
|
||||||
if (currentPlugin != null && currentPlugin.version === manifest.version) {
|
|
||||||
this.logger.log(`Plugin ${manifest.name} is up to date (version ${manifest.version}). Skipping`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { plugin, filters, actions } = await this.pluginRepository.loadPlugin(manifest, basePath);
|
|
||||||
|
|
||||||
this.logger.log(`Upserted plugin: ${plugin.name} (ID: ${plugin.id}, version: ${plugin.version})`);
|
|
||||||
|
|
||||||
for (const filter of filters) {
|
|
||||||
this.logger.log(`Upserted plugin filter: ${filter.methodName} (ID: ${filter.id})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const action of actions) {
|
|
||||||
this.logger.log(`Upserted plugin action: ${action.methodName} (ID: ${action.id})`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async readAndValidateManifest(manifestPath: string): Promise<PluginManifestDto> {
|
|
||||||
const content = await this.storageRepository.readTextFile(manifestPath);
|
|
||||||
const manifestData = JSON.parse(content);
|
|
||||||
return PluginManifestSchema.parse(manifestData);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////
|
|
||||||
// Plugin Execution
|
|
||||||
///////////////////////////////////////////
|
|
||||||
private async loadPlugins() {
|
|
||||||
const plugins = await this.pluginRepository.getAllPlugins();
|
|
||||||
for (const plugin of plugins) {
|
|
||||||
try {
|
|
||||||
this.logger.debug(`Loading plugin: ${plugin.name} from ${plugin.wasmPath}`);
|
|
||||||
|
|
||||||
const extismPlugin = await newPlugin(plugin.wasmPath, {
|
|
||||||
useWasi: true,
|
|
||||||
functions: this.hostFunctions.getHostFunctions(),
|
|
||||||
});
|
|
||||||
|
|
||||||
this.loadedPlugins.set(plugin.id, extismPlugin);
|
|
||||||
this.logger.log(`Successfully loaded plugin: ${plugin.name}`);
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Failed to load plugin ${plugin.name}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnEvent({ name: 'AssetCreate' })
|
|
||||||
async handleAssetCreate({ asset }: ArgOf<'AssetCreate'>) {
|
|
||||||
await this.handleTrigger(PluginTriggerType.AssetCreate, {
|
|
||||||
ownerId: asset.ownerId,
|
|
||||||
event: { userId: asset.ownerId, asset },
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private async handleTrigger<T extends PluginTriggerType>(
|
|
||||||
triggerType: T,
|
|
||||||
params: { ownerId: string; event: WorkflowData[T] },
|
|
||||||
): Promise<void> {
|
|
||||||
const workflows = await this.workflowRepository.getWorkflowByOwnerAndTrigger(params.ownerId, triggerType);
|
|
||||||
if (workflows.length === 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const jobs: JobItem[] = workflows.map((workflow) => ({
|
|
||||||
name: JobName.WorkflowRun,
|
|
||||||
data: {
|
|
||||||
id: workflow.id,
|
|
||||||
type: triggerType,
|
|
||||||
event: params.event,
|
|
||||||
} as IWorkflowJob<T>,
|
|
||||||
}));
|
|
||||||
|
|
||||||
await this.jobRepository.queueAll(jobs);
|
|
||||||
this.logger.debug(`Queued ${jobs.length} workflow execution jobs for trigger ${triggerType}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnJob({ name: JobName.WorkflowRun, queue: QueueName.Workflow })
|
|
||||||
async handleWorkflowRun({ id: workflowId, type, event }: JobOf<JobName.WorkflowRun>): Promise<JobStatus> {
|
|
||||||
try {
|
|
||||||
const workflow = await this.workflowRepository.getWorkflow(workflowId);
|
|
||||||
if (!workflow) {
|
|
||||||
this.logger.error(`Workflow ${workflowId} not found`);
|
|
||||||
return JobStatus.Failed;
|
|
||||||
}
|
|
||||||
|
|
||||||
const workflowFilters = await this.workflowRepository.getFilters(workflowId);
|
|
||||||
const workflowActions = await this.workflowRepository.getActions(workflowId);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case PluginTriggerType.AssetCreate: {
|
|
||||||
const data = event as WorkflowData[PluginTriggerType.AssetCreate];
|
|
||||||
const asset = data.asset;
|
|
||||||
|
|
||||||
const authToken = this.cryptoRepository.signJwt({ userId: data.userId }, this.pluginJwtSecret);
|
|
||||||
|
|
||||||
const context = {
|
|
||||||
authToken,
|
|
||||||
asset,
|
|
||||||
};
|
|
||||||
|
|
||||||
const filtersPassed = await this.executeFilters(workflowFilters, context);
|
|
||||||
if (!filtersPassed) {
|
|
||||||
return JobStatus.Skipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.executeActions(workflowActions, context);
|
|
||||||
this.logger.debug(`Workflow ${workflowId} executed successfully`);
|
|
||||||
return JobStatus.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
case PluginTriggerType.PersonRecognized: {
|
|
||||||
this.logger.error('unimplemented');
|
|
||||||
return JobStatus.Skipped;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
this.logger.error(`Unknown workflow trigger type: ${type}`);
|
|
||||||
return JobStatus.Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.logger.error(`Error executing workflow ${workflowId}:`, error);
|
|
||||||
return JobStatus.Failed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async executeFilters(workflowFilters: WorkflowFilter[], context: WorkflowContext): Promise<boolean> {
|
|
||||||
for (const workflowFilter of workflowFilters) {
|
|
||||||
const filter = await this.pluginRepository.getFilter(workflowFilter.pluginFilterId);
|
|
||||||
if (!filter) {
|
|
||||||
this.logger.error(`Filter ${workflowFilter.pluginFilterId} not found`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginInstance = this.loadedPlugins.get(filter.pluginId);
|
|
||||||
if (!pluginInstance) {
|
|
||||||
this.logger.error(`Plugin ${filter.pluginId} not loaded`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const filterInput: PluginInput = {
|
|
||||||
authToken: context.authToken,
|
|
||||||
config: workflowFilter.filterConfig,
|
|
||||||
data: {
|
|
||||||
asset: context.asset,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logger.debug(`Calling filter ${filter.methodName} with input: ${JSON.stringify(filterInput)}`);
|
|
||||||
|
|
||||||
const filterResult = await pluginInstance.call(
|
|
||||||
filter.methodName,
|
|
||||||
new TextEncoder().encode(JSON.stringify(filterInput)),
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!filterResult) {
|
|
||||||
this.logger.error(`Filter ${filter.methodName} returned null`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const result = JSON.parse(filterResult.text());
|
|
||||||
if (result.passed === false) {
|
|
||||||
this.logger.debug(`Filter ${filter.methodName} returned false, stopping workflow execution`);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private async executeActions(workflowActions: WorkflowAction[], context: WorkflowContext): Promise<void> {
|
|
||||||
for (const workflowAction of workflowActions) {
|
|
||||||
const action = await this.pluginRepository.getAction(workflowAction.pluginActionId);
|
|
||||||
if (!action) {
|
|
||||||
throw new Error(`Action ${workflowAction.pluginActionId} not found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const pluginInstance = this.loadedPlugins.get(action.pluginId);
|
|
||||||
if (!pluginInstance) {
|
|
||||||
throw new Error(`Plugin ${action.pluginId} not loaded`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const actionInput: PluginInput = {
|
|
||||||
authToken: context.authToken,
|
|
||||||
config: workflowAction.actionConfig,
|
|
||||||
data: {
|
|
||||||
asset: context.asset,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
this.logger.debug(`Calling action ${action.methodName} with input: ${JSON.stringify(actionInput)}`);
|
|
||||||
|
|
||||||
await pluginInstance.call(action.methodName, JSON.stringify(actionInput));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user