@hexabot-ai/api 3.2.2-alpha.9 → 3.2.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/actions.service.d.ts +1 -0
- package/dist/actions/actions.service.js +19 -1
- package/dist/actions/actions.service.js.map +1 -1
- package/dist/actions/types.d.ts +3 -1
- package/dist/app.controller.d.ts +1 -0
- package/dist/app.controller.js +1 -0
- package/dist/app.controller.js.map +1 -1
- package/dist/app.module.js +13 -1
- package/dist/app.module.js.map +1 -1
- package/dist/config/index.js +14 -2
- package/dist/config/index.js.map +1 -1
- package/dist/config/types.d.ts +9 -0
- package/dist/extensions/actions/ai/agent.action.d.ts +1 -1
- package/dist/extensions/actions/ai/agent.action.js +6 -3
- package/dist/extensions/actions/ai/agent.action.js.map +1 -1
- package/dist/extensions/actions/ai/ai-base.action.d.ts +1 -1
- package/dist/extensions/actions/ai/ai-base.action.js +18 -5
- package/dist/extensions/actions/ai/ai-base.action.js.map +1 -1
- package/dist/extensions/actions/ai/generate-object.base.action.d.ts +1 -1
- package/dist/extensions/actions/ai/generate-object.base.action.js +3 -2
- package/dist/extensions/actions/ai/generate-object.base.action.js.map +1 -1
- package/dist/extensions/actions/ai/generate-text.base.action.d.ts +1 -1
- package/dist/extensions/actions/ai/generate-text.base.action.js +3 -2
- package/dist/extensions/actions/ai/generate-text.base.action.js.map +1 -1
- package/dist/extensions/actions/ai/mcp.binding.js +2 -0
- package/dist/extensions/actions/ai/mcp.binding.js.map +1 -1
- package/dist/extensions/actions/ai/memory.binding.js +2 -0
- package/dist/extensions/actions/ai/memory.binding.js.map +1 -1
- package/dist/extensions/actions/ai/retrieve-content-rag.action.js +2 -0
- package/dist/extensions/actions/ai/retrieve-content-rag.action.js.map +1 -1
- package/dist/extensions/actions/messaging/list.action.d.ts +2 -2
- package/dist/extensions/actions/messaging/list.action.js +7 -0
- package/dist/extensions/actions/messaging/list.action.js.map +1 -1
- package/dist/extensions/actions/subscriber/update-labels.action.js +3 -0
- package/dist/extensions/actions/subscriber/update-labels.action.js.map +1 -1
- package/dist/extensions/actions/web/http-request.action.js +12 -1
- package/dist/extensions/actions/web/http-request.action.js.map +1 -1
- package/dist/extensions/actions/workflow/call-workflow.action.d.ts +19 -0
- package/dist/extensions/actions/workflow/call-workflow.action.js +83 -0
- package/dist/extensions/actions/workflow/call-workflow.action.js.map +1 -0
- package/dist/extensions/actions/workflow/i18n/en.translations.json +9 -0
- package/dist/extensions/actions/workflow/i18n/fr.translations.json +9 -0
- package/dist/extensions/index.d.ts +1 -0
- package/dist/extensions/index.js +1 -0
- package/dist/extensions/index.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/controllers/mcp-token.controller.d.ts +47 -0
- package/dist/mcp/controllers/mcp-token.controller.js +70 -0
- package/dist/mcp/controllers/mcp-token.controller.js.map +1 -0
- package/dist/mcp/decorators/mcp-permission.decorator.d.ts +8 -0
- package/dist/mcp/decorators/mcp-permission.decorator.js +11 -0
- package/dist/mcp/decorators/mcp-permission.decorator.js.map +1 -0
- package/dist/mcp/dto/mcp-token.dto.d.ts +17 -0
- package/dist/mcp/dto/mcp-token.dto.js +40 -0
- package/dist/mcp/dto/mcp-token.dto.js.map +1 -0
- package/dist/mcp/entities/mcp-token.entity.d.ts +111 -0
- package/dist/mcp/entities/mcp-token.entity.js +69 -0
- package/dist/mcp/entities/mcp-token.entity.js.map +1 -0
- package/dist/mcp/guards/hexabot-mcp-token.guard.d.ts +9 -0
- package/dist/mcp/guards/hexabot-mcp-token.guard.js +49 -0
- package/dist/mcp/guards/hexabot-mcp-token.guard.js.map +1 -0
- package/dist/mcp/guards/mcp-permission.guard.d.ts +9 -0
- package/dist/mcp/guards/mcp-permission.guard.js +47 -0
- package/dist/mcp/guards/mcp-permission.guard.js.map +1 -0
- package/dist/mcp/mcp-api.module.d.ts +2 -0
- package/dist/mcp/mcp-api.module.js +65 -0
- package/dist/mcp/mcp-api.module.js.map +1 -0
- package/dist/mcp/repositories/mcp-token.repository.d.ts +8 -0
- package/dist/mcp/repositories/mcp-token.repository.js +41 -0
- package/dist/mcp/repositories/mcp-token.repository.js.map +1 -0
- package/dist/mcp/services/mcp-token.service.d.ts +24 -0
- package/dist/mcp/services/mcp-token.service.js +106 -0
- package/dist/mcp/services/mcp-token.service.js.map +1 -0
- package/dist/mcp/tools/catalog-mcp.tools.d.ts +63 -0
- package/dist/mcp/tools/catalog-mcp.tools.js +135 -0
- package/dist/mcp/tools/catalog-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/cms-mcp.tools.d.ts +128 -0
- package/dist/mcp/tools/cms-mcp.tools.js +242 -0
- package/dist/mcp/tools/cms-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/credential-mcp.tools.d.ts +19 -0
- package/dist/mcp/tools/credential-mcp.tools.js +82 -0
- package/dist/mcp/tools/credential-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp-tool.base.d.ts +22 -0
- package/dist/mcp/tools/hexabot-mcp-tool.base.js +48 -0
- package/dist/mcp/tools/hexabot-mcp-tool.base.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp.schemas.d.ts +41 -0
- package/dist/mcp/tools/hexabot-mcp.schemas.js +36 -0
- package/dist/mcp/tools/hexabot-mcp.schemas.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp.tools.d.ts +1 -0
- package/dist/mcp/tools/hexabot-mcp.tools.js +18 -0
- package/dist/mcp/tools/hexabot-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/hexabot-mcp.utils.d.ts +2 -0
- package/dist/mcp/tools/hexabot-mcp.utils.js +21 -0
- package/dist/mcp/tools/hexabot-mcp.utils.js.map +1 -0
- package/dist/mcp/tools/index.d.ts +22 -0
- package/dist/mcp/tools/index.js +50 -0
- package/dist/mcp/tools/index.js.map +1 -0
- package/dist/mcp/tools/mcp-server-mcp.tools.d.ts +77 -0
- package/dist/mcp/tools/mcp-server-mcp.tools.js +130 -0
- package/dist/mcp/tools/mcp-server-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/memory-definition-mcp.tools.d.ts +57 -0
- package/dist/mcp/tools/memory-definition-mcp.tools.js +128 -0
- package/dist/mcp/tools/memory-definition-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/workflow-mcp.helper.d.ts +156 -0
- package/dist/mcp/tools/workflow-mcp.helper.js +128 -0
- package/dist/mcp/tools/workflow-mcp.helper.js.map +1 -0
- package/dist/mcp/tools/workflow-mcp.tools.d.ts +790 -0
- package/dist/mcp/tools/workflow-mcp.tools.js +227 -0
- package/dist/mcp/tools/workflow-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.d.ts +349 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.js +242 -0
- package/dist/mcp/tools/workflow-run-mcp.tools.js.map +1 -0
- package/dist/mcp/tools/workflow-version-mcp.tools.d.ts +238 -0
- package/dist/mcp/tools/workflow-version-mcp.tools.js +368 -0
- package/dist/mcp/tools/workflow-version-mcp.tools.js.map +1 -0
- package/dist/mcp/types.d.ts +7 -0
- package/dist/mcp/types.js +3 -0
- package/dist/mcp/types.js.map +1 -0
- package/dist/static/assets/{browser-ponyfill-Qk7qpw-i.js → browser-ponyfill-D1dKqhtP.js} +2 -2
- package/dist/static/assets/{cssMode-CdhvY6oD.js → cssMode-DplazEqd.js} +1 -1
- package/dist/static/assets/{freemarker2-BPoURZH1.js → freemarker2-oSA1bGAm.js} +1 -1
- package/dist/static/assets/{handlebars-C4oubER1.js → handlebars-CcC7EgN2.js} +1 -1
- package/dist/static/assets/{html-CDE5RgVN.js → html-CcFTB7eP.js} +1 -1
- package/dist/static/assets/{htmlMode-C8edIUUq.js → htmlMode-g5g2wCUD.js} +1 -1
- package/dist/static/assets/{index-C1NxBXuS.js → index-CMp45yDR.js} +2379 -2330
- package/dist/static/assets/{index-BI1BtkYv.css → index-D-b9KTZd.css} +1 -1
- package/dist/static/assets/{javascript-Wp5m7adK.js → javascript-Dv0J7ioP.js} +1 -1
- package/dist/static/assets/{jsonMode-0eww5Srr.js → jsonMode-CzBkKOe4.js} +1 -1
- package/dist/static/assets/{liquid-DTktXITv.js → liquid-CptRs_ZU.js} +1 -1
- package/dist/static/assets/{lspLanguageFeatures-BzIRlKhn.js → lspLanguageFeatures-DXNU_upS.js} +1 -1
- package/dist/static/assets/{mdx-D508emqD.js → mdx-af00eMTm.js} +1 -1
- package/dist/static/assets/{python-BfhIq2kX.js → python-CNcx7i3L.js} +1 -1
- package/dist/static/assets/{razor-C7F8fOds.js → razor-BL_MSmP7.js} +1 -1
- package/dist/static/assets/{tsMode-B8zPu6Wl.js → tsMode-BWD4nt2F.js} +1 -1
- package/dist/static/assets/{typescript-Dew3VavZ.js → typescript-BbKUic6G.js} +1 -1
- package/dist/static/assets/{xml-B_2tc6Mh.js → xml-DFx6l4N0.js} +1 -1
- package/dist/static/assets/{yaml-pHcw4KrS.js → yaml-BNuTzPxo.js} +1 -1
- package/dist/static/index.html +2 -2
- package/dist/static/locales/en/translation.json +89 -4
- package/dist/static/locales/fr/translation.json +90 -5
- package/dist/transfer/adapters/content-type-transfer.adapter.d.ts +14 -0
- package/dist/transfer/adapters/content-type-transfer.adapter.js +102 -0
- package/dist/transfer/adapters/content-type-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/credential-transfer.adapter.d.ts +13 -0
- package/dist/transfer/adapters/credential-transfer.adapter.js +110 -0
- package/dist/transfer/adapters/credential-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/label-transfer.adapter.d.ts +15 -0
- package/dist/transfer/adapters/label-transfer.adapter.js +189 -0
- package/dist/transfer/adapters/label-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.d.ts +16 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.js +169 -0
- package/dist/transfer/adapters/mcp-server-transfer.adapter.js.map +1 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.d.ts +14 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.js +111 -0
- package/dist/transfer/adapters/memory-definition-transfer.adapter.js.map +1 -0
- package/dist/transfer/index.d.ts +7 -0
- package/dist/transfer/index.js +24 -0
- package/dist/transfer/index.js.map +1 -0
- package/dist/transfer/workflow-transfer-adapter.registry.d.ts +27 -0
- package/dist/transfer/workflow-transfer-adapter.registry.js +164 -0
- package/dist/transfer/workflow-transfer-adapter.registry.js.map +1 -0
- package/dist/transfer/workflow-transfer-definition.service.d.ts +40 -0
- package/dist/transfer/workflow-transfer-definition.service.js +353 -0
- package/dist/transfer/workflow-transfer-definition.service.js.map +1 -0
- package/dist/transfer/workflow-transfer-resource-adapter.d.ts +33 -0
- package/dist/transfer/workflow-transfer-resource-adapter.js +81 -0
- package/dist/transfer/workflow-transfer-resource-adapter.js.map +1 -0
- package/dist/transfer/workflow-transfer.controller.d.ts +9 -0
- package/dist/transfer/workflow-transfer.controller.js +63 -0
- package/dist/transfer/workflow-transfer.controller.js.map +1 -0
- package/dist/transfer/workflow-transfer.module.d.ts +2 -0
- package/dist/transfer/workflow-transfer.module.js +47 -0
- package/dist/transfer/workflow-transfer.module.js.map +1 -0
- package/dist/transfer/workflow-transfer.service.d.ts +51 -0
- package/dist/transfer/workflow-transfer.service.js +509 -0
- package/dist/transfer/workflow-transfer.service.js.map +1 -0
- package/dist/transfer/workflow-transfer.types.d.ts +31 -0
- package/dist/transfer/workflow-transfer.types.js +35 -0
- package/dist/transfer/workflow-transfer.types.js.map +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/types/event-emitter.d.ts +12 -5
- package/dist/user/dto/user.dto.d.ts +1 -1
- package/dist/user/guards/ability.guard.js +12 -2
- package/dist/user/guards/ability.guard.js.map +1 -1
- package/dist/utils/test/fixtures/label-group.d.ts +1 -1
- package/dist/utils/test/fixtures/label.d.ts +1 -1
- package/dist/utils/test/fixtures/subscriber.d.ts +1 -1
- package/dist/utils/test/fixtures/workflow-run.js +4 -1
- package/dist/utils/test/fixtures/workflow-run.js.map +1 -1
- package/dist/utils/test/port.d.ts +8 -0
- package/dist/utils/test/port.js +34 -0
- package/dist/utils/test/port.js.map +1 -0
- package/dist/websocket/websocket.gateway.d.ts +2 -1
- package/dist/websocket/websocket.gateway.js.map +1 -1
- package/dist/workflow/contexts/workflow-runtime.context.d.ts +3 -1
- package/dist/workflow/contexts/workflow-runtime.context.js +5 -0
- package/dist/workflow/contexts/workflow-runtime.context.js.map +1 -1
- package/dist/workflow/controllers/workflow-run.controller.js +2 -0
- package/dist/workflow/controllers/workflow-run.controller.js.map +1 -1
- package/dist/workflow/dto/workflow-run.dto.d.ts +1 -0
- package/dist/workflow/dto/workflow-run.dto.js +11 -0
- package/dist/workflow/dto/workflow-run.dto.js.map +1 -1
- package/dist/workflow/entities/memory-record.entity.d.ts +2 -0
- package/dist/workflow/entities/workflow-run.entity.d.ts +62 -0
- package/dist/workflow/entities/workflow-run.entity.js +15 -1
- package/dist/workflow/entities/workflow-run.entity.js.map +1 -1
- package/dist/workflow/index.d.ts +7 -0
- package/dist/workflow/index.js +7 -0
- package/dist/workflow/index.js.map +1 -1
- package/dist/workflow/repositories/workflow-run.repository.js +7 -1
- package/dist/workflow/repositories/workflow-run.repository.js.map +1 -1
- package/dist/workflow/resource-refs.d.ts +17 -0
- package/dist/workflow/resource-refs.js +15 -0
- package/dist/workflow/resource-refs.js.map +1 -0
- package/dist/workflow/services/agentic.service.d.ts +9 -2
- package/dist/workflow/services/agentic.service.js +142 -19
- package/dist/workflow/services/agentic.service.js.map +1 -1
- package/dist/workflow/services/workflow-run.service.d.ts +3 -0
- package/dist/workflow/services/workflow-run.service.js +75 -3
- package/dist/workflow/services/workflow-run.service.js.map +1 -1
- package/dist/workflow/services/workflow-version.service.d.ts +2 -0
- package/dist/workflow/services/workflow-version.service.js +19 -0
- package/dist/workflow/services/workflow-version.service.js.map +1 -1
- package/dist/workflow/services/workflow.service.d.ts +13 -9
- package/dist/workflow/services/workflow.service.js +64 -35
- package/dist/workflow/services/workflow.service.js.map +1 -1
- package/dist/workflow/types.d.ts +31 -0
- package/dist/workflow/types.js +2 -1
- package/dist/workflow/types.js.map +1 -1
- package/dist/workflow/workflow.module.js +3 -0
- package/dist/workflow/workflow.module.js.map +1 -1
- package/package.json +8 -6
- package/src/actions/actions.service.ts +34 -4
- package/src/actions/types.ts +6 -5
- package/src/app.controller.ts +1 -0
- package/src/app.module.ts +17 -1
- package/src/config/index.ts +17 -2
- package/src/config/types.ts +9 -0
- package/src/extensions/actions/ai/agent.action.ts +6 -1
- package/src/extensions/actions/ai/ai-base.action.ts +39 -5
- package/src/extensions/actions/ai/generate-object.base.action.ts +3 -0
- package/src/extensions/actions/ai/generate-text.base.action.ts +3 -0
- package/src/extensions/actions/ai/mcp.binding.ts +2 -0
- package/src/extensions/actions/ai/memory.binding.ts +2 -0
- package/src/extensions/actions/ai/retrieve-content-rag.action.ts +2 -0
- package/src/extensions/actions/messaging/list.action.ts +7 -0
- package/src/extensions/actions/subscriber/update-labels.action.ts +3 -0
- package/src/extensions/actions/web/http-request.action.ts +17 -1
- package/src/extensions/actions/workflow/call-workflow.action.ts +112 -0
- package/src/extensions/actions/workflow/i18n/en.translations.json +9 -0
- package/src/extensions/actions/workflow/i18n/fr.translations.json +9 -0
- package/src/extensions/index.ts +2 -0
- package/src/index.ts +2 -0
- package/src/mcp/README.md +277 -0
- package/src/mcp/controllers/mcp-token.controller.ts +64 -0
- package/src/mcp/decorators/mcp-permission.decorator.ts +23 -0
- package/src/mcp/dto/mcp-token.dto.ts +48 -0
- package/src/mcp/entities/mcp-token.entity.ts +60 -0
- package/src/mcp/guards/hexabot-mcp-token.guard.ts +55 -0
- package/src/mcp/guards/mcp-permission.guard.ts +61 -0
- package/src/mcp/mcp-api.module.ts +61 -0
- package/src/mcp/repositories/mcp-token.repository.ts +34 -0
- package/src/mcp/services/mcp-token.service.ts +138 -0
- package/src/mcp/tools/catalog-mcp.tools.ts +122 -0
- package/src/mcp/tools/cms-mcp.tools.ts +239 -0
- package/src/mcp/tools/credential-mcp.tools.ts +81 -0
- package/src/mcp/tools/hexabot-mcp-tool.base.ts +74 -0
- package/src/mcp/tools/hexabot-mcp.schemas.ts +55 -0
- package/src/mcp/tools/hexabot-mcp.tools.ts +7 -0
- package/src/mcp/tools/hexabot-mcp.utils.ts +29 -0
- package/src/mcp/tools/index.ts +51 -0
- package/src/mcp/tools/mcp-server-mcp.tools.ts +135 -0
- package/src/mcp/tools/memory-definition-mcp.tools.ts +125 -0
- package/src/mcp/tools/workflow-mcp.helper.ts +219 -0
- package/src/mcp/tools/workflow-mcp.tools.ts +271 -0
- package/src/mcp/tools/workflow-run-mcp.tools.ts +292 -0
- package/src/mcp/tools/workflow-version-mcp.tools.ts +459 -0
- package/src/mcp/types.ts +14 -0
- package/src/transfer/adapters/content-type-transfer.adapter.ts +154 -0
- package/src/transfer/adapters/credential-transfer.adapter.ts +158 -0
- package/src/transfer/adapters/label-transfer.adapter.ts +291 -0
- package/src/transfer/adapters/mcp-server-transfer.adapter.ts +255 -0
- package/src/transfer/adapters/memory-definition-transfer.adapter.ts +167 -0
- package/src/transfer/index.ts +19 -0
- package/src/transfer/workflow-transfer-adapter.registry.ts +250 -0
- package/src/transfer/workflow-transfer-definition.service.ts +587 -0
- package/src/transfer/workflow-transfer-resource-adapter.ts +132 -0
- package/src/transfer/workflow-transfer.controller.ts +86 -0
- package/src/transfer/workflow-transfer.module.ts +46 -0
- package/src/transfer/workflow-transfer.service.ts +858 -0
- package/src/transfer/workflow-transfer.types.ts +98 -0
- package/src/user/guards/ability.guard.ts +15 -3
- package/src/utils/test/fixtures/workflow-run.ts +4 -1
- package/src/utils/test/port.ts +48 -0
- package/src/websocket/websocket.gateway.ts +2 -1
- package/src/workflow/contexts/workflow-runtime.context.ts +12 -1
- package/src/workflow/controllers/workflow-run.controller.ts +2 -0
- package/src/workflow/dto/workflow-run.dto.ts +10 -0
- package/src/workflow/entities/workflow-run.entity.ts +22 -1
- package/src/workflow/index.ts +14 -0
- package/src/workflow/repositories/workflow-run.repository.ts +7 -1
- package/src/workflow/resource-refs.ts +44 -0
- package/src/workflow/services/agentic.service.ts +271 -30
- package/src/workflow/services/workflow-run.service.ts +120 -7
- package/src/workflow/services/workflow-version.service.ts +33 -0
- package/src/workflow/services/workflow.service.ts +86 -31
- package/src/workflow/types.ts +52 -0
- package/src/workflow/workflow.module.ts +3 -0
- package/types/event-emitter.d.ts +12 -5
|
@@ -0,0 +1,858 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Hexabot — Fair Core License (FCL-1.0-ALv2)
|
|
3
|
+
* Copyright (c) 2026 Hexastack.
|
|
4
|
+
* Full terms: see LICENSE.md.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
Workflow as AgenticWorkflow,
|
|
9
|
+
type WorkflowDefinition,
|
|
10
|
+
} from '@hexabot-ai/agentic';
|
|
11
|
+
import {
|
|
12
|
+
WORKFLOW_EXPORT_BUNDLE_KIND,
|
|
13
|
+
type WorkflowExportBundle,
|
|
14
|
+
type WorkflowExportBundleWorkflowDependency,
|
|
15
|
+
type WorkflowFull,
|
|
16
|
+
type WorkflowImportResult,
|
|
17
|
+
type WorkflowVersion,
|
|
18
|
+
workflowExportBundleSchema,
|
|
19
|
+
workflowImportResultSchema,
|
|
20
|
+
} from '@hexabot-ai/types';
|
|
21
|
+
import {
|
|
22
|
+
BadRequestException,
|
|
23
|
+
ConflictException,
|
|
24
|
+
Injectable,
|
|
25
|
+
NotFoundException,
|
|
26
|
+
} from '@nestjs/common';
|
|
27
|
+
import { EventEmitter2 } from '@nestjs/event-emitter';
|
|
28
|
+
import sanitizeFilename from 'sanitize-filename';
|
|
29
|
+
import { DataSource, EntityManager } from 'typeorm';
|
|
30
|
+
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
31
|
+
|
|
32
|
+
import { EHook } from '@/utils/generics/base-orm.repository';
|
|
33
|
+
import { WorkflowOrmEntity } from '@/workflow/entities/workflow.entity';
|
|
34
|
+
import { WorkflowVersionService } from '@/workflow/services/workflow-version.service';
|
|
35
|
+
import { WorkflowService } from '@/workflow/services/workflow.service';
|
|
36
|
+
import { WorkflowVersionAction } from '@/workflow/types';
|
|
37
|
+
|
|
38
|
+
import { WorkflowTransferAdapterRegistry } from './workflow-transfer-adapter.registry';
|
|
39
|
+
import {
|
|
40
|
+
type WorkflowBindingResourceRefs,
|
|
41
|
+
WorkflowTransferDefinitionService,
|
|
42
|
+
type WorkflowTaskResourceRefs,
|
|
43
|
+
} from './workflow-transfer-definition.service';
|
|
44
|
+
import {
|
|
45
|
+
WorkflowTransferExportContext,
|
|
46
|
+
WorkflowTransferImportContext,
|
|
47
|
+
type WorkflowTransferResourceAdapter,
|
|
48
|
+
} from './workflow-transfer-resource-adapter';
|
|
49
|
+
import {
|
|
50
|
+
buildResourceResult,
|
|
51
|
+
buildPostCreateEvent,
|
|
52
|
+
type ImportedWorkflowTransferResources,
|
|
53
|
+
type WorkflowTransferPostCreateEvent,
|
|
54
|
+
} from './workflow-transfer.types';
|
|
55
|
+
|
|
56
|
+
type ExportedWorkflowFile = {
|
|
57
|
+
filename: string;
|
|
58
|
+
content: string;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type ParsedWorkflowImportSource = {
|
|
62
|
+
exportId?: string;
|
|
63
|
+
workflow: WorkflowExportBundle['workflow'];
|
|
64
|
+
version: WorkflowExportBundle['version'];
|
|
65
|
+
definitionYml: string;
|
|
66
|
+
definition: WorkflowDefinition;
|
|
67
|
+
isRoot: boolean;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
type ImportedWorkflowSource = ParsedWorkflowImportSource & {
|
|
71
|
+
localId: string;
|
|
72
|
+
localName: string;
|
|
73
|
+
workflowEntity: WorkflowOrmEntity;
|
|
74
|
+
workflowPayload: WorkflowCreateWithManagerPayload;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
type WorkflowCreateWithManagerPayload = Parameters<
|
|
78
|
+
WorkflowService['createWithManager']
|
|
79
|
+
>[1];
|
|
80
|
+
|
|
81
|
+
const WORKFLOW_RESOURCE_KIND = 'workflow';
|
|
82
|
+
const WORKFLOW_RESOURCE_KEY = 'workflows';
|
|
83
|
+
|
|
84
|
+
@Injectable()
|
|
85
|
+
export class WorkflowTransferService {
|
|
86
|
+
constructor(
|
|
87
|
+
private readonly dataSource: DataSource,
|
|
88
|
+
private readonly eventEmitter: EventEmitter2,
|
|
89
|
+
private readonly workflowService: WorkflowService,
|
|
90
|
+
private readonly workflowVersionService: WorkflowVersionService,
|
|
91
|
+
private readonly workflowTransferDefinitionService: WorkflowTransferDefinitionService,
|
|
92
|
+
private readonly workflowTransferAdapterRegistry: WorkflowTransferAdapterRegistry,
|
|
93
|
+
) {}
|
|
94
|
+
|
|
95
|
+
async exportWorkflow(id: string): Promise<ExportedWorkflowFile> {
|
|
96
|
+
const workflow = await this.workflowService.findOneAndPopulate(id);
|
|
97
|
+
if (!workflow) {
|
|
98
|
+
throw new NotFoundException(`Workflow with ID ${id} not found`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const version = workflow.currentVersion;
|
|
102
|
+
if (!version?.definitionYml) {
|
|
103
|
+
throw new BadRequestException(
|
|
104
|
+
'Workflow must have a current version to be exported',
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const definition =
|
|
109
|
+
this.workflowTransferDefinitionService.parseWithLocalCatalog(
|
|
110
|
+
version.definitionYml,
|
|
111
|
+
);
|
|
112
|
+
const dependencyExport = await this.buildWorkflowDependencyResources(
|
|
113
|
+
workflow.id,
|
|
114
|
+
definition,
|
|
115
|
+
);
|
|
116
|
+
const { bindingRefs, taskRefs } = this.collectDefinitionResourceRefs([
|
|
117
|
+
definition,
|
|
118
|
+
...dependencyExport.definitions,
|
|
119
|
+
]);
|
|
120
|
+
const resources = await this.buildExportResources(
|
|
121
|
+
this.withoutResourceKind(bindingRefs, WORKFLOW_RESOURCE_KIND),
|
|
122
|
+
this.withoutResourceKind(taskRefs, WORKFLOW_RESOURCE_KIND),
|
|
123
|
+
);
|
|
124
|
+
(resources as Record<string, unknown[]>)[WORKFLOW_RESOURCE_KEY] =
|
|
125
|
+
dependencyExport.resources;
|
|
126
|
+
const bundle: WorkflowExportBundle = {
|
|
127
|
+
kind: WORKFLOW_EXPORT_BUNDLE_KIND,
|
|
128
|
+
schemaVersion: 1,
|
|
129
|
+
exportedAt: new Date().toISOString(),
|
|
130
|
+
workflow: {
|
|
131
|
+
exportId: workflow.id,
|
|
132
|
+
name: workflow.name,
|
|
133
|
+
description: workflow.description ?? null,
|
|
134
|
+
type: workflow.type,
|
|
135
|
+
schedule: workflow.schedule ?? null,
|
|
136
|
+
inputSchema: workflow.inputSchema,
|
|
137
|
+
layout: {
|
|
138
|
+
x: workflow.x,
|
|
139
|
+
y: workflow.y,
|
|
140
|
+
zoom: workflow.zoom,
|
|
141
|
+
direction: workflow.direction,
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
version: {
|
|
145
|
+
number: version.version,
|
|
146
|
+
checksum: version.checksum,
|
|
147
|
+
message: version.message ?? null,
|
|
148
|
+
exportedVersionId: version.id,
|
|
149
|
+
},
|
|
150
|
+
definitionYml: version.definitionYml,
|
|
151
|
+
resources,
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
return {
|
|
155
|
+
filename: this.buildExportFilename(workflow.name),
|
|
156
|
+
content: stringifyYaml(workflowExportBundleSchema.parse(bundle), {
|
|
157
|
+
lineWidth: 0,
|
|
158
|
+
}),
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async importWorkflow(
|
|
163
|
+
content: string,
|
|
164
|
+
createdBy: string,
|
|
165
|
+
): Promise<WorkflowImportResult> {
|
|
166
|
+
const bundle = this.parseBundle(content);
|
|
167
|
+
this.assertUniqueWorkflowExportIds(bundle);
|
|
168
|
+
const definition =
|
|
169
|
+
this.workflowTransferDefinitionService.parseWithLocalCatalog(
|
|
170
|
+
bundle.definitionYml,
|
|
171
|
+
);
|
|
172
|
+
const sources = this.parseWorkflowImportSources(bundle, definition);
|
|
173
|
+
const { bindingRefs, taskRefs } = this.collectDefinitionResourceRefs(
|
|
174
|
+
sources.map((source) => source.definition),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
this.assertBundleResourceBucketsAreSupported(bundle.resources);
|
|
178
|
+
this.assertBundleContainsReferencedResources(bundle, bindingRefs, taskRefs);
|
|
179
|
+
|
|
180
|
+
const { workflowId, resources, warnings, postCreateEvents } =
|
|
181
|
+
await this.dataSource.transaction(async (manager) => {
|
|
182
|
+
const importedResources = await this.importResources(
|
|
183
|
+
manager,
|
|
184
|
+
bundle.resources,
|
|
185
|
+
createdBy,
|
|
186
|
+
);
|
|
187
|
+
const importedWorkflows = await this.importWorkflowDefinitions(
|
|
188
|
+
manager,
|
|
189
|
+
sources,
|
|
190
|
+
importedResources,
|
|
191
|
+
createdBy,
|
|
192
|
+
);
|
|
193
|
+
|
|
194
|
+
return {
|
|
195
|
+
workflowId: importedWorkflows.rootWorkflowId,
|
|
196
|
+
resources: [
|
|
197
|
+
...importedResources.resources,
|
|
198
|
+
...importedWorkflows.resources,
|
|
199
|
+
],
|
|
200
|
+
warnings: importedResources.warnings,
|
|
201
|
+
postCreateEvents: [
|
|
202
|
+
...importedResources.postCreateEvents,
|
|
203
|
+
...importedWorkflows.postCreateEvents,
|
|
204
|
+
],
|
|
205
|
+
};
|
|
206
|
+
});
|
|
207
|
+
await this.emitPostCreateEvents(postCreateEvents);
|
|
208
|
+
|
|
209
|
+
const workflow = await this.workflowService.findOne(workflowId);
|
|
210
|
+
if (!workflow) {
|
|
211
|
+
throw new Error(`Unable to load imported workflow ${workflowId}`);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return workflowImportResultSchema.parse({
|
|
215
|
+
workflow,
|
|
216
|
+
resources,
|
|
217
|
+
warnings,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
private parseBundle(content: string): WorkflowExportBundle {
|
|
222
|
+
let parsed: unknown;
|
|
223
|
+
try {
|
|
224
|
+
parsed = parseYaml(content);
|
|
225
|
+
} catch (error) {
|
|
226
|
+
throw new BadRequestException(
|
|
227
|
+
error instanceof Error ? error.message : 'Invalid workflow bundle YAML',
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const validation = workflowExportBundleSchema.safeParse(parsed);
|
|
232
|
+
if (!validation.success) {
|
|
233
|
+
throw new BadRequestException(
|
|
234
|
+
validation.error.issues
|
|
235
|
+
.map((issue) => `${issue.path.join('.')}: ${issue.message}`)
|
|
236
|
+
.join('; '),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return validation.data;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
private async buildWorkflowDependencyResources(
|
|
244
|
+
rootWorkflowId: string,
|
|
245
|
+
rootDefinition: WorkflowDefinition,
|
|
246
|
+
): Promise<{
|
|
247
|
+
resources: WorkflowExportBundleWorkflowDependency[];
|
|
248
|
+
definitions: WorkflowDefinition[];
|
|
249
|
+
}> {
|
|
250
|
+
const resources: WorkflowExportBundleWorkflowDependency[] = [];
|
|
251
|
+
const definitions: WorkflowDefinition[] = [];
|
|
252
|
+
const visitedWorkflowIds = new Set<string>([rootWorkflowId]);
|
|
253
|
+
const pendingWorkflowIds = [
|
|
254
|
+
...this.getWorkflowRefsFromDefinition(rootDefinition),
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
for (let index = 0; index < pendingWorkflowIds.length; index += 1) {
|
|
258
|
+
const workflowId = pendingWorkflowIds[index];
|
|
259
|
+
if (visitedWorkflowIds.has(workflowId)) {
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
visitedWorkflowIds.add(workflowId);
|
|
263
|
+
|
|
264
|
+
const { workflow, version } =
|
|
265
|
+
await this.loadWorkflowDependencyForExport(workflowId);
|
|
266
|
+
const definition =
|
|
267
|
+
this.workflowTransferDefinitionService.parseWithLocalCatalog(
|
|
268
|
+
version.definitionYml,
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
resources.push(
|
|
272
|
+
this.buildWorkflowDependencyExportResource(workflow, version),
|
|
273
|
+
);
|
|
274
|
+
definitions.push(definition);
|
|
275
|
+
pendingWorkflowIds.push(
|
|
276
|
+
...this.getWorkflowRefsFromDefinition(definition),
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return { resources, definitions };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
private async loadWorkflowDependencyForExport(
|
|
284
|
+
workflowId: string,
|
|
285
|
+
): Promise<{ workflow: WorkflowFull; version: WorkflowVersion }> {
|
|
286
|
+
const workflow = await this.workflowService.findOneAndPopulate(workflowId);
|
|
287
|
+
if (!workflow) {
|
|
288
|
+
throw new BadRequestException(
|
|
289
|
+
`Unable to export workflow: missing workflow(s): ${workflowId}`,
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const version = workflow.currentVersion;
|
|
294
|
+
if (!version?.definitionYml) {
|
|
295
|
+
throw new BadRequestException(
|
|
296
|
+
`Called workflow "${workflow.name}" must have a current version to be exported`,
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return { workflow, version };
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private buildWorkflowDependencyExportResource(
|
|
304
|
+
workflow: WorkflowFull,
|
|
305
|
+
version: WorkflowVersion,
|
|
306
|
+
): WorkflowExportBundleWorkflowDependency {
|
|
307
|
+
const { exportId: _exportId, ...workflowMetadata } =
|
|
308
|
+
this.buildWorkflowExportMetadata(workflow);
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
exportId: workflow.id,
|
|
312
|
+
workflow: workflowMetadata,
|
|
313
|
+
version: this.buildWorkflowVersionExportMetadata(version),
|
|
314
|
+
definitionYml: version.definitionYml,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private buildWorkflowExportMetadata(
|
|
319
|
+
workflow: WorkflowFull,
|
|
320
|
+
): WorkflowExportBundle['workflow'] {
|
|
321
|
+
return {
|
|
322
|
+
exportId: workflow.id,
|
|
323
|
+
name: workflow.name,
|
|
324
|
+
description: workflow.description ?? null,
|
|
325
|
+
type: workflow.type,
|
|
326
|
+
schedule: workflow.schedule ?? null,
|
|
327
|
+
inputSchema: workflow.inputSchema,
|
|
328
|
+
layout: {
|
|
329
|
+
x: workflow.x,
|
|
330
|
+
y: workflow.y,
|
|
331
|
+
zoom: workflow.zoom,
|
|
332
|
+
direction: workflow.direction,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private buildWorkflowVersionExportMetadata(
|
|
338
|
+
version: WorkflowVersion,
|
|
339
|
+
): WorkflowExportBundle['version'] {
|
|
340
|
+
return {
|
|
341
|
+
number: version.version,
|
|
342
|
+
checksum: version.checksum,
|
|
343
|
+
message: version.message ?? null,
|
|
344
|
+
exportedVersionId: version.id,
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
private getWorkflowRefsFromDefinition(
|
|
349
|
+
definition: WorkflowDefinition,
|
|
350
|
+
): string[] {
|
|
351
|
+
return (
|
|
352
|
+
this.workflowTransferDefinitionService.collectTaskResourceRefs(
|
|
353
|
+
definition,
|
|
354
|
+
)[WORKFLOW_RESOURCE_KIND] ?? []
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private collectDefinitionResourceRefs(definitions: WorkflowDefinition[]): {
|
|
359
|
+
bindingRefs: WorkflowBindingResourceRefs;
|
|
360
|
+
taskRefs: WorkflowTaskResourceRefs;
|
|
361
|
+
} {
|
|
362
|
+
let bindingRefs: WorkflowBindingResourceRefs = {};
|
|
363
|
+
let taskRefs: WorkflowTaskResourceRefs = {};
|
|
364
|
+
|
|
365
|
+
for (const definition of definitions) {
|
|
366
|
+
bindingRefs = this.mergeResourceRefs(
|
|
367
|
+
bindingRefs,
|
|
368
|
+
this.workflowTransferDefinitionService.collectBindingResourceRefs(
|
|
369
|
+
definition,
|
|
370
|
+
),
|
|
371
|
+
);
|
|
372
|
+
taskRefs = this.mergeResourceRefs(
|
|
373
|
+
taskRefs,
|
|
374
|
+
this.workflowTransferDefinitionService.collectTaskResourceRefs(
|
|
375
|
+
definition,
|
|
376
|
+
),
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return {
|
|
381
|
+
bindingRefs,
|
|
382
|
+
taskRefs,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
private parseWorkflowImportSources(
|
|
387
|
+
bundle: WorkflowExportBundle,
|
|
388
|
+
rootDefinition: WorkflowDefinition,
|
|
389
|
+
): ParsedWorkflowImportSource[] {
|
|
390
|
+
return [
|
|
391
|
+
{
|
|
392
|
+
exportId: bundle.workflow.exportId,
|
|
393
|
+
workflow: bundle.workflow,
|
|
394
|
+
version: bundle.version,
|
|
395
|
+
definitionYml: bundle.definitionYml,
|
|
396
|
+
definition: rootDefinition,
|
|
397
|
+
isRoot: true,
|
|
398
|
+
},
|
|
399
|
+
...bundle.resources.workflows.map((resource) => ({
|
|
400
|
+
exportId: resource.exportId,
|
|
401
|
+
workflow: resource.workflow,
|
|
402
|
+
version: resource.version,
|
|
403
|
+
definitionYml: resource.definitionYml,
|
|
404
|
+
definition:
|
|
405
|
+
this.workflowTransferDefinitionService.parseWithLocalCatalog(
|
|
406
|
+
resource.definitionYml,
|
|
407
|
+
),
|
|
408
|
+
isRoot: false,
|
|
409
|
+
})),
|
|
410
|
+
];
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
private assertUniqueWorkflowExportIds(bundle: WorkflowExportBundle): void {
|
|
414
|
+
const seen = new Set<string>();
|
|
415
|
+
const exportIds = [
|
|
416
|
+
bundle.workflow.exportId,
|
|
417
|
+
...bundle.resources.workflows.map((workflow) => workflow.exportId),
|
|
418
|
+
].filter((id): id is string => typeof id === 'string' && id.length > 0);
|
|
419
|
+
|
|
420
|
+
for (const exportId of exportIds) {
|
|
421
|
+
if (seen.has(exportId)) {
|
|
422
|
+
throw new BadRequestException(
|
|
423
|
+
`Workflow bundle contains duplicate workflow export ID "${exportId}"`,
|
|
424
|
+
);
|
|
425
|
+
}
|
|
426
|
+
seen.add(exportId);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
private async importWorkflowDefinitions(
|
|
431
|
+
manager: EntityManager,
|
|
432
|
+
sources: ParsedWorkflowImportSource[],
|
|
433
|
+
importedResources: ImportedWorkflowTransferResources,
|
|
434
|
+
createdBy: string,
|
|
435
|
+
): Promise<{
|
|
436
|
+
rootWorkflowId: string;
|
|
437
|
+
resources: WorkflowImportResult['resources'];
|
|
438
|
+
postCreateEvents: WorkflowTransferPostCreateEvent[];
|
|
439
|
+
}> {
|
|
440
|
+
const importedWorkflows: ImportedWorkflowSource[] = [];
|
|
441
|
+
const workflowIdMap: Record<string, string> = {};
|
|
442
|
+
const resources: WorkflowImportResult['resources'] = [];
|
|
443
|
+
const postCreateEvents: WorkflowTransferPostCreateEvent[] = [];
|
|
444
|
+
|
|
445
|
+
for (const source of sources) {
|
|
446
|
+
const localName = await this.buildImportedWorkflowName(
|
|
447
|
+
manager,
|
|
448
|
+
source.workflow.name,
|
|
449
|
+
);
|
|
450
|
+
const workflowPayload = this.buildImportedWorkflowPayload(
|
|
451
|
+
source.workflow,
|
|
452
|
+
localName,
|
|
453
|
+
createdBy,
|
|
454
|
+
);
|
|
455
|
+
const workflowEntity = await this.workflowService.createWithManager(
|
|
456
|
+
manager,
|
|
457
|
+
workflowPayload,
|
|
458
|
+
);
|
|
459
|
+
|
|
460
|
+
if (source.exportId) {
|
|
461
|
+
workflowIdMap[source.exportId] = workflowEntity.id;
|
|
462
|
+
}
|
|
463
|
+
if (!source.isRoot && source.exportId) {
|
|
464
|
+
resources.push(
|
|
465
|
+
buildResourceResult({
|
|
466
|
+
kind: WORKFLOW_RESOURCE_KIND,
|
|
467
|
+
exportId: source.exportId,
|
|
468
|
+
localId: workflowEntity.id,
|
|
469
|
+
name: localName,
|
|
470
|
+
action: 'created',
|
|
471
|
+
}),
|
|
472
|
+
);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
postCreateEvents.push(
|
|
476
|
+
buildPostCreateEvent('workflow', workflowEntity, workflowPayload),
|
|
477
|
+
);
|
|
478
|
+
importedWorkflows.push({
|
|
479
|
+
...source,
|
|
480
|
+
localId: workflowEntity.id,
|
|
481
|
+
localName,
|
|
482
|
+
workflowEntity,
|
|
483
|
+
workflowPayload,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
for (const importedWorkflow of importedWorkflows) {
|
|
488
|
+
const definitionYml = this.buildImportedDefinitionYml(
|
|
489
|
+
importedWorkflow.definition,
|
|
490
|
+
importedResources,
|
|
491
|
+
workflowIdMap,
|
|
492
|
+
);
|
|
493
|
+
const workflowVersionPayload = {
|
|
494
|
+
workflow: importedWorkflow.localId,
|
|
495
|
+
definitionYml,
|
|
496
|
+
action: WorkflowVersionAction.import,
|
|
497
|
+
message:
|
|
498
|
+
importedWorkflow.version.message ??
|
|
499
|
+
`Imported from workflow version ${importedWorkflow.version.number}`,
|
|
500
|
+
parentVersion: null,
|
|
501
|
+
createdBy,
|
|
502
|
+
};
|
|
503
|
+
const workflowVersion =
|
|
504
|
+
await this.workflowVersionService.createSnapshotWithManager(
|
|
505
|
+
manager,
|
|
506
|
+
workflowVersionPayload,
|
|
507
|
+
);
|
|
508
|
+
|
|
509
|
+
postCreateEvents.push(
|
|
510
|
+
buildPostCreateEvent(
|
|
511
|
+
'workflowVersion',
|
|
512
|
+
workflowVersion,
|
|
513
|
+
workflowVersionPayload,
|
|
514
|
+
),
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
const rootWorkflow = importedWorkflows.find((workflow) => workflow.isRoot);
|
|
519
|
+
if (!rootWorkflow) {
|
|
520
|
+
throw new Error('Imported workflow bundle is missing a root workflow');
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
return {
|
|
524
|
+
rootWorkflowId: rootWorkflow.localId,
|
|
525
|
+
resources,
|
|
526
|
+
postCreateEvents,
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
private buildImportedWorkflowPayload(
|
|
531
|
+
workflow: WorkflowExportBundle['workflow'],
|
|
532
|
+
name: string,
|
|
533
|
+
createdBy: string,
|
|
534
|
+
): WorkflowCreateWithManagerPayload {
|
|
535
|
+
return {
|
|
536
|
+
name,
|
|
537
|
+
description: workflow.description ?? undefined,
|
|
538
|
+
type: workflow.type,
|
|
539
|
+
schedule: workflow.schedule,
|
|
540
|
+
inputSchema: workflow.inputSchema,
|
|
541
|
+
builtin: false,
|
|
542
|
+
x: workflow.layout.x,
|
|
543
|
+
y: workflow.layout.y,
|
|
544
|
+
zoom: workflow.layout.zoom,
|
|
545
|
+
direction: workflow.layout.direction,
|
|
546
|
+
createdBy,
|
|
547
|
+
};
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
private buildImportedDefinitionYml(
|
|
551
|
+
definition: WorkflowDefinition,
|
|
552
|
+
importedResources: ImportedWorkflowTransferResources,
|
|
553
|
+
workflowIdMap: Record<string, string>,
|
|
554
|
+
): string {
|
|
555
|
+
const remappedBindingDefinition =
|
|
556
|
+
this.workflowTransferDefinitionService.remapBindingResourceRefs(
|
|
557
|
+
definition,
|
|
558
|
+
importedResources.bindingIdMaps,
|
|
559
|
+
);
|
|
560
|
+
const remappedDefinition =
|
|
561
|
+
this.workflowTransferDefinitionService.remapTaskResourceRefs(
|
|
562
|
+
remappedBindingDefinition,
|
|
563
|
+
{
|
|
564
|
+
...importedResources.taskIdMaps,
|
|
565
|
+
[WORKFLOW_RESOURCE_KIND]: workflowIdMap,
|
|
566
|
+
},
|
|
567
|
+
);
|
|
568
|
+
const definitionYml =
|
|
569
|
+
AgenticWorkflow.stringifyDefinition(remappedDefinition);
|
|
570
|
+
|
|
571
|
+
this.workflowTransferDefinitionService.parseWithLocalCatalog(definitionYml);
|
|
572
|
+
|
|
573
|
+
return definitionYml;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
private async buildExportResources(
|
|
577
|
+
bindingRefs: WorkflowBindingResourceRefs,
|
|
578
|
+
taskRefs: WorkflowTaskResourceRefs,
|
|
579
|
+
): Promise<WorkflowExportBundle['resources']> {
|
|
580
|
+
const resources = this.createEmptyResourceBuckets();
|
|
581
|
+
const exportContext = new WorkflowTransferExportContext(
|
|
582
|
+
this.mergeResourceRefs(bindingRefs, taskRefs),
|
|
583
|
+
);
|
|
584
|
+
|
|
585
|
+
for (const adapter of this.workflowTransferAdapterRegistry.listInReverseDependencyOrder()) {
|
|
586
|
+
const adapterResources =
|
|
587
|
+
await adapter.buildExportResources(exportContext);
|
|
588
|
+
this.mergeAdapterResources(resources, adapter, adapterResources);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
return resources as WorkflowExportBundle['resources'];
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
private assertBundleContainsReferencedResources(
|
|
595
|
+
bundle: WorkflowExportBundle,
|
|
596
|
+
refs: WorkflowBindingResourceRefs,
|
|
597
|
+
taskRefs: WorkflowTaskResourceRefs,
|
|
598
|
+
): void {
|
|
599
|
+
const mergedRefs = this.mergeResourceRefs(refs, taskRefs);
|
|
600
|
+
const workflowRefs = mergedRefs[WORKFLOW_RESOURCE_KIND] ?? [];
|
|
601
|
+
|
|
602
|
+
if (workflowRefs.length > 0) {
|
|
603
|
+
this.assertRefsAreBundled(
|
|
604
|
+
WORKFLOW_RESOURCE_KIND,
|
|
605
|
+
workflowRefs,
|
|
606
|
+
this.getBundledWorkflowExportIds(bundle),
|
|
607
|
+
);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
for (const [kind, resourceRefs] of Object.entries(
|
|
611
|
+
this.withoutResourceKind(mergedRefs, WORKFLOW_RESOURCE_KIND),
|
|
612
|
+
)) {
|
|
613
|
+
if (resourceRefs.length === 0) {
|
|
614
|
+
continue;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
const adapter = this.workflowTransferAdapterRegistry.get(kind);
|
|
618
|
+
if (!adapter) {
|
|
619
|
+
throw new BadRequestException(
|
|
620
|
+
`Workflow bundle references unsupported resource kind "${kind}"`,
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
this.assertRefsAreBundled(
|
|
625
|
+
kind,
|
|
626
|
+
resourceRefs,
|
|
627
|
+
this.getBundledExportIds(bundle.resources, adapter.resourceKeys[0]),
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private assertRefsAreBundled(
|
|
633
|
+
resourceLabel: string,
|
|
634
|
+
refs: string[],
|
|
635
|
+
bundledIds: string[],
|
|
636
|
+
): void {
|
|
637
|
+
const bundled = new Set(bundledIds);
|
|
638
|
+
const missing = Array.from(new Set(refs)).filter((id) => !bundled.has(id));
|
|
639
|
+
if (missing.length > 0) {
|
|
640
|
+
throw new BadRequestException(
|
|
641
|
+
`Workflow bundle is missing referenced ${resourceLabel}(s): ${missing.join(
|
|
642
|
+
', ',
|
|
643
|
+
)}`,
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
private assertBundleResourceBucketsAreSupported(
|
|
649
|
+
resources: WorkflowExportBundle['resources'],
|
|
650
|
+
): void {
|
|
651
|
+
const supportedResourceKeys = new Set([
|
|
652
|
+
...this.workflowTransferAdapterRegistry.getResourceKeys(),
|
|
653
|
+
WORKFLOW_RESOURCE_KEY,
|
|
654
|
+
]);
|
|
655
|
+
|
|
656
|
+
for (const [key, value] of Object.entries(
|
|
657
|
+
resources as Record<string, unknown[]>,
|
|
658
|
+
)) {
|
|
659
|
+
if (supportedResourceKeys.has(key) || value.length === 0) {
|
|
660
|
+
continue;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
throw new BadRequestException(
|
|
664
|
+
`Workflow bundle contains unsupported resource section "${key}"`,
|
|
665
|
+
);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
private createEmptyResourceBuckets(): Record<string, unknown[]> {
|
|
670
|
+
return Object.fromEntries(
|
|
671
|
+
this.workflowTransferAdapterRegistry
|
|
672
|
+
.listInDependencyOrder()
|
|
673
|
+
.flatMap((adapter) =>
|
|
674
|
+
adapter.resourceKeys.map((resourceKey) => [
|
|
675
|
+
resourceKey,
|
|
676
|
+
[] as unknown[],
|
|
677
|
+
]),
|
|
678
|
+
),
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
private mergeAdapterResources(
|
|
683
|
+
resources: Record<string, unknown[]>,
|
|
684
|
+
adapter: WorkflowTransferResourceAdapter,
|
|
685
|
+
adapterResources: Record<string, unknown[]>,
|
|
686
|
+
): void {
|
|
687
|
+
const declaredResourceKeys = new Set(adapter.resourceKeys);
|
|
688
|
+
|
|
689
|
+
for (const [key, value] of Object.entries(adapterResources)) {
|
|
690
|
+
if (!declaredResourceKeys.has(key)) {
|
|
691
|
+
throw new Error(
|
|
692
|
+
`Workflow transfer adapter "${adapter.kind}" returned undeclared resource key "${key}"`,
|
|
693
|
+
);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if (!Array.isArray(value)) {
|
|
697
|
+
throw new Error(
|
|
698
|
+
`Workflow transfer adapter "${adapter.kind}" returned non-array resources for "${key}"`,
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
resources[key] = value;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
for (const resourceKey of adapter.resourceKeys) {
|
|
706
|
+
resources[resourceKey] ??= [];
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
private mergeResourceRefs(
|
|
711
|
+
refs: WorkflowBindingResourceRefs,
|
|
712
|
+
taskRefs: WorkflowTaskResourceRefs,
|
|
713
|
+
): Record<string, string[]> {
|
|
714
|
+
const merged = new Map<string, Set<string>>();
|
|
715
|
+
|
|
716
|
+
for (const refSet of [refs, taskRefs]) {
|
|
717
|
+
for (const [kind, ids] of Object.entries(refSet)) {
|
|
718
|
+
const kindRefs = merged.get(kind) ?? new Set<string>();
|
|
719
|
+
for (const id of ids) {
|
|
720
|
+
kindRefs.add(id);
|
|
721
|
+
}
|
|
722
|
+
merged.set(kind, kindRefs);
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
return Object.fromEntries(
|
|
727
|
+
Array.from(merged.entries()).map(([kind, ids]) => [
|
|
728
|
+
kind,
|
|
729
|
+
Array.from(ids),
|
|
730
|
+
]),
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
private withoutResourceKind(
|
|
735
|
+
refs: Record<string, string[]>,
|
|
736
|
+
ignoredKind: string,
|
|
737
|
+
): Record<string, string[]> {
|
|
738
|
+
return Object.fromEntries(
|
|
739
|
+
Object.entries(refs).filter(([kind]) => kind !== ignoredKind),
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
private getBundledWorkflowExportIds(bundle: WorkflowExportBundle): string[] {
|
|
744
|
+
return [
|
|
745
|
+
bundle.workflow.exportId,
|
|
746
|
+
...bundle.resources.workflows.map((workflow) => workflow.exportId),
|
|
747
|
+
].filter((id): id is string => typeof id === 'string' && id.length > 0);
|
|
748
|
+
}
|
|
749
|
+
|
|
750
|
+
private getBundledExportIds(
|
|
751
|
+
resources: WorkflowExportBundle['resources'],
|
|
752
|
+
resourceKey: string,
|
|
753
|
+
): string[] {
|
|
754
|
+
const resourceBucket = (resources as Record<string, unknown[]>)[
|
|
755
|
+
resourceKey
|
|
756
|
+
];
|
|
757
|
+
|
|
758
|
+
return (resourceBucket ?? []).flatMap((resource) => {
|
|
759
|
+
if (
|
|
760
|
+
typeof resource === 'object' &&
|
|
761
|
+
resource !== null &&
|
|
762
|
+
'exportId' in resource &&
|
|
763
|
+
typeof resource.exportId === 'string'
|
|
764
|
+
) {
|
|
765
|
+
return [resource.exportId];
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
return [];
|
|
769
|
+
});
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
private async importResources(
|
|
773
|
+
manager: EntityManager,
|
|
774
|
+
resources: WorkflowExportBundle['resources'],
|
|
775
|
+
ownerId: string,
|
|
776
|
+
): Promise<ImportedWorkflowTransferResources> {
|
|
777
|
+
const adapters =
|
|
778
|
+
this.workflowTransferAdapterRegistry.listInDependencyOrder();
|
|
779
|
+
const importContext = new WorkflowTransferImportContext(
|
|
780
|
+
manager,
|
|
781
|
+
resources,
|
|
782
|
+
ownerId,
|
|
783
|
+
);
|
|
784
|
+
|
|
785
|
+
for (const adapter of adapters) {
|
|
786
|
+
importContext.addResult(
|
|
787
|
+
adapter.kind,
|
|
788
|
+
await adapter.importResources(importContext),
|
|
789
|
+
);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
const results = importContext.getResultsInOrder(
|
|
793
|
+
adapters.map((adapter) => adapter.kind),
|
|
794
|
+
);
|
|
795
|
+
const idMaps = importContext.getIdMaps();
|
|
796
|
+
|
|
797
|
+
return {
|
|
798
|
+
bindingIdMaps: idMaps,
|
|
799
|
+
taskIdMaps: idMaps,
|
|
800
|
+
resources: results.flatMap((result) => result.resources),
|
|
801
|
+
warnings: results.flatMap((result) => result.warnings),
|
|
802
|
+
postCreateEvents: results.flatMap((result) => result.postCreateEvents),
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
private async emitPostCreateEvents(
|
|
807
|
+
events: WorkflowTransferPostCreateEvent[],
|
|
808
|
+
): Promise<void> {
|
|
809
|
+
for (const event of events) {
|
|
810
|
+
await this.eventEmitter.emitAsync(`hook:${event.entityName}:postCreate`, {
|
|
811
|
+
action: EHook.postCreate,
|
|
812
|
+
entity: event.entity,
|
|
813
|
+
payload: event.payload,
|
|
814
|
+
entityName: event.entityName,
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
private async buildImportedWorkflowName(
|
|
820
|
+
manager: EntityManager,
|
|
821
|
+
sourceName: string,
|
|
822
|
+
): Promise<string> {
|
|
823
|
+
const baseName = sourceName.trim() || 'Imported workflow';
|
|
824
|
+
const suffixes = [
|
|
825
|
+
'',
|
|
826
|
+
' (imported)',
|
|
827
|
+
...Array.from({ length: 999 }, (_, index) => {
|
|
828
|
+
return ` (imported ${index + 2})`;
|
|
829
|
+
}),
|
|
830
|
+
];
|
|
831
|
+
|
|
832
|
+
for (const suffix of suffixes) {
|
|
833
|
+
const normalized = this.truncateWorkflowName(baseName, suffix);
|
|
834
|
+
const existing = await manager.findOne(WorkflowOrmEntity, {
|
|
835
|
+
where: { name: normalized },
|
|
836
|
+
});
|
|
837
|
+
if (!existing) {
|
|
838
|
+
return normalized;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
throw new ConflictException('Unable to generate a unique workflow name');
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
private truncateWorkflowName(name: string, suffix = ''): string {
|
|
846
|
+
const maxBaseLength = Math.max(1, 255 - suffix.length);
|
|
847
|
+
const normalizedBase =
|
|
848
|
+
name.length <= maxBaseLength ? name : name.slice(0, maxBaseLength);
|
|
849
|
+
|
|
850
|
+
return `${normalizedBase}${suffix}`;
|
|
851
|
+
}
|
|
852
|
+
|
|
853
|
+
private buildExportFilename(name: string): string {
|
|
854
|
+
const safeName = sanitizeFilename(name.trim()) || 'workflow';
|
|
855
|
+
|
|
856
|
+
return `${safeName}.workflow.yml`;
|
|
857
|
+
}
|
|
858
|
+
}
|