@aifabrix/builder 2.53.1 → 2.54.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/bin/aifabrix.js +2 -2
- package/lib/api/assistants.api.js +8 -5
- package/lib/api/channel-identity.api.js +41 -0
- package/lib/api/evidence-authoring.api.js +5 -1
- package/lib/api/evidence.api.js +2 -0
- package/lib/api/types/evidence.types.js +2 -0
- package/lib/api/work-search.api.js +136 -0
- package/lib/channels/add-channel.js +163 -126
- package/lib/channels/approval-guides/chatgpt.js +14 -18
- package/lib/channels/approval-guides/claude.js +29 -0
- package/lib/channels/approval-guides/index.js +88 -22
- package/lib/channels/approval-guides/mcp-client.js +27 -0
- package/lib/channels/approval-guides/microsoft-copilot.js +52 -3
- package/lib/channels/approval-guides/slack.js +10 -10
- package/lib/channels/approval-guides/teams-bot.js +8 -12
- package/lib/channels/channel-add-continuations.js +91 -0
- package/lib/channels/channel-add-ensure.js +124 -27
- package/lib/channels/channel-add-existing.js +160 -28
- package/lib/channels/channel-add-failure.js +60 -0
- package/lib/channels/channel-add-remaining.js +486 -0
- package/lib/channels/channel-add-tty.js +190 -0
- package/lib/channels/channel-add-wizard-config.js +138 -0
- package/lib/channels/channel-add-wizard.js +250 -0
- package/lib/channels/channel-artifact-generator.js +27 -13
- package/lib/channels/channel-cardinality.js +83 -0
- package/lib/channels/channel-catalog-icon.js +45 -0
- package/lib/channels/channel-cli-instructions.js +75 -0
- package/lib/channels/channel-configuration-map.js +213 -0
- package/lib/channels/channel-credential.js +11 -14
- package/lib/channels/channel-definition-project.js +101 -0
- package/lib/channels/channel-definition-registry.js +154 -0
- package/lib/channels/channel-description.js +59 -2
- package/lib/channels/channel-discovery.js +16 -2
- package/lib/channels/channel-external-system.js +86 -9
- package/lib/channels/channel-install-envelope-actions.js +88 -0
- package/lib/channels/channel-install-envelope-microsoft.js +155 -0
- package/lib/channels/channel-install-envelope.js +356 -0
- package/lib/channels/channel-install-help-md.js +172 -0
- package/lib/channels/channel-install-http-payload.js +62 -0
- package/lib/channels/channel-install-inventory.js +115 -0
- package/lib/channels/channel-install-profiles.js +90 -0
- package/lib/channels/channel-instance-collision.js +118 -0
- package/lib/channels/channel-instance-key.js +100 -0
- package/lib/channels/channel-instance-materialize.js +240 -0
- package/lib/channels/channel-integration-generator.js +16 -9
- package/lib/channels/channel-integration-template.js +101 -22
- package/lib/channels/channel-inventory-fetch.js +79 -0
- package/lib/channels/channel-oauth-preflight.js +104 -0
- package/lib/channels/channel-package-version.js +76 -0
- package/lib/channels/channel-predefined-client.js +154 -0
- package/lib/channels/channel-probe.js +7 -2
- package/lib/channels/channel-runtime-resolver.js +180 -72
- package/lib/channels/channel-status-display.js +86 -16
- package/lib/channels/channel-targets.js +106 -92
- package/lib/channels/chatgpt-callback.js +318 -0
- package/lib/channels/chatgpt-channel-add.js +229 -0
- package/lib/channels/chatgpt-preflight.js +396 -0
- package/lib/channels/chatgpt-runtime-contract.js +17 -0
- package/lib/channels/chatgpt-setup-values.js +124 -0
- package/lib/channels/claude-callback.js +67 -0
- package/lib/channels/claude-channel-add.js +254 -0
- package/lib/channels/claude-runtime-contract.js +40 -0
- package/lib/channels/claude-runtime.js +58 -0
- package/lib/channels/claude-setup-values.js +89 -0
- package/lib/channels/mcp-client/config-merge.js +148 -0
- package/lib/channels/mcp-client/ensure-client.js +231 -0
- package/lib/channels/mcp-client/paths.js +53 -0
- package/lib/channels/mcp-client/profiles.js +122 -0
- package/lib/channels/mcp-client/redirect-uris.js +99 -0
- package/lib/channels/mcp-client/runtime.js +59 -0
- package/lib/channels/mcp-client-channel-add.js +186 -0
- package/lib/channels/microsoft-copilot-channel-add.js +117 -7
- package/lib/channels/microsoft-copilot-entra-identifier-uris.js +187 -0
- package/lib/channels/microsoft-copilot-entra-sso-form.js +106 -18
- package/lib/channels/microsoft-copilot-entra-sso.js +31 -2
- package/lib/channels/microsoft-copilot-package-prior.js +107 -0
- package/lib/channels/microsoft-copilot-package.js +29 -28
- package/lib/channels/microsoft-provisioning/a365.js +80 -0
- package/lib/channels/microsoft-provisioning/auth-config.js +240 -0
- package/lib/channels/microsoft-provisioning/auth.js +179 -0
- package/lib/channels/microsoft-provisioning/cli.js +71 -0
- package/lib/channels/microsoft-provisioning/constants.js +75 -0
- package/lib/channels/microsoft-provisioning/errors.js +117 -0
- package/lib/channels/microsoft-provisioning/index.js +55 -0
- package/lib/channels/microsoft-provisioning/install-atk.js +158 -0
- package/lib/channels/microsoft-provisioning/install-teams.js +115 -0
- package/lib/channels/microsoft-provisioning/login-portal-device.js +236 -0
- package/lib/channels/microsoft-provisioning/login-portal-parse.js +71 -0
- package/lib/channels/microsoft-provisioning/login-portal-teams.js +193 -0
- package/lib/channels/microsoft-provisioning/login-portal.js +230 -0
- package/lib/channels/microsoft-provisioning/persist.js +110 -0
- package/lib/channels/microsoft-provisioning/teams-bot-args.js +119 -0
- package/lib/channels/microsoft-provisioning/teams-bot-controller.js +123 -0
- package/lib/channels/microsoft-provisioning/teams-bot-parse.js +134 -0
- package/lib/channels/microsoft-provisioning/teams-bot-persist.js +113 -0
- package/lib/channels/microsoft-provisioning/teams-bot-provision.js +234 -0
- package/lib/channels/microsoft-provisioning/teams-bot-secrets.js +45 -0
- package/lib/channels/microsoft-provisioning/teams-cli.js +82 -0
- package/lib/channels/runtime-tool-catalog.js +5 -2
- package/lib/channels/slack-channel-add-portal.js +130 -0
- package/lib/channels/slack-channel-add-tty-auth.js +95 -0
- package/lib/channels/slack-channel-add.js +258 -0
- package/lib/channels/slack-provisioning/apps.js +181 -0
- package/lib/channels/slack-provisioning/auth-store.js +110 -0
- package/lib/channels/slack-provisioning/auth-ticket.js +185 -0
- package/lib/channels/slack-provisioning/auth.js +115 -0
- package/lib/channels/slack-provisioning/callbacks.js +41 -0
- package/lib/channels/slack-provisioning/cli.js +226 -0
- package/lib/channels/slack-provisioning/constants.js +37 -0
- package/lib/channels/slack-provisioning/credentials.js +169 -0
- package/lib/channels/slack-provisioning/curl-shim.js +132 -0
- package/lib/channels/slack-provisioning/developer-install.js +118 -0
- package/lib/channels/slack-provisioning/env-file.js +120 -0
- package/lib/channels/slack-provisioning/errors.js +47 -0
- package/lib/channels/slack-provisioning/go-ca-bundle.js +137 -0
- package/lib/channels/slack-provisioning/index.js +250 -0
- package/lib/channels/slack-provisioning/install-cli.js +323 -0
- package/lib/channels/slack-provisioning/manifest.js +89 -0
- package/lib/channels/slack-provisioning/oauth-lock.js +33 -0
- package/lib/channels/slack-provisioning/offer-cli.js +113 -0
- package/lib/channels/slack-provisioning/onboard-members.js +122 -0
- package/lib/channels/slack-provisioning/onboard.js +89 -0
- package/lib/channels/slack-provisioning/persist.js +95 -0
- package/lib/channels/slack-provisioning/remaining-urls.js +158 -0
- package/lib/channels/slack-provisioning/with-curl-on-path.js +48 -0
- package/lib/channels/teams-bot-channel-add.js +94 -0
- package/lib/channels/vendor-dataplane-url.js +249 -0
- package/lib/cli/setup-certification.help.js +2 -0
- package/lib/cli/setup-external-system-help.js +2 -0
- package/lib/cli/setup-external-system.js +1 -1
- package/lib/commands/channel-add-help.js +366 -0
- package/lib/commands/channel-add-setup.js +162 -80
- package/lib/commands/channel-configure.js +118 -0
- package/lib/commands/channel.js +10 -1
- package/lib/commands/governance-upload-external.js +6 -3
- package/lib/commands/lifecycle-command-action.js +2 -5
- package/lib/commands/lifecycle-external.js +93 -21
- package/lib/commands/repair-datasource-test-payload.js +4 -2
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +23 -2
- package/lib/commands/repair-datasource.js +3 -0
- package/lib/commands/repair-external-run.js +59 -25
- package/lib/commands/repair-openapi-rest-runtime.js +127 -0
- package/lib/commands/repair-orchestration-tag-merge.js +339 -0
- package/lib/commands/repair-rbac-catalog-defaults.js +96 -0
- package/lib/commands/repair-rbac-platform-preset.js +26 -15
- package/lib/commands/repair-rbac-reader-roles.js +8 -21
- package/lib/commands/repair-rbac.js +10 -34
- package/lib/commands/repair-system-permissions.js +24 -22
- package/lib/commands/role-assistant-test-output.js +14 -0
- package/lib/commands/role-assistant.js +59 -21
- package/lib/commands/test-e2e-external.js +148 -21
- package/lib/commands/upload-credential-push-log.js +2 -1
- package/lib/commands/upload-publish-context.js +1 -1
- package/lib/commands/upload-role-assistant-shell.js +124 -0
- package/lib/commands/upload-sidecar-followups.js +6 -3
- package/lib/commands/upload.js +18 -3
- package/lib/core/secrets-docker-env.js +4 -1
- package/lib/core/secrets-env-content.js +17 -3
- package/lib/datasource/capability/capability-manifest.js +0 -3
- package/lib/datasource/capability/copy-test-payload.js +3 -0
- package/lib/external-system/deploy-helpers.js +26 -1
- package/lib/external-system/deploy.js +10 -6
- package/lib/external-system/download-helpers.js +29 -2
- package/lib/external-system/download-role-assistant.js +83 -4
- package/lib/external-system/download.js +40 -30
- package/lib/external-system/sync-deploy-manifest.js +3 -2
- package/lib/external-system/upload-identity.js +42 -0
- package/lib/generator/index.js +19 -15
- package/lib/generator/split.js +13 -2
- package/lib/lifecycle/role-assistant-governance.js +146 -0
- package/lib/lifecycle/scenario-upload.js +181 -30
- package/lib/programmatic/builder-api-cors.js +143 -0
- package/lib/programmatic/builder-api-request.js +6 -0
- package/lib/programmatic/builder-api-route-dispatch.js +56 -42
- package/lib/programmatic/builder-api-server.js +8 -1
- package/lib/programmatic/capability-registry.js +54 -0
- package/lib/programmatic/channel-install-help-resolve.js +359 -0
- package/lib/programmatic/channel-install-session.js +218 -0
- package/lib/programmatic/help-content/channel-onboarding.md +2 -0
- package/lib/programmatic/help-content/cip-overview.md +3 -1
- package/lib/programmatic/http-raw-response.js +62 -0
- package/lib/programmatic/openapi-channel-install-bindings.js +179 -0
- package/lib/programmatic/openapi-channel-install-schemas.js +286 -0
- package/lib/programmatic/openapi-component-schemas.js +18 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +74 -3
- package/lib/programmatic/openapi-envelope-examples.js +117 -0
- package/lib/programmatic/openapi-operation-bindings.js +4 -1
- package/lib/programmatic/openapi-schema-descriptions-channel.js +316 -0
- package/lib/programmatic/openapi-spec.js +12 -8
- package/lib/programmatic/platform-rbac-catalog.js +36 -18
- package/lib/programmatic/route-handler-map.js +51 -1
- package/lib/programmatic/route-handlers-context.js +2 -1
- package/lib/programmatic/run-auth-permissions.js +54 -0
- package/lib/programmatic/run-channel-catalog.js +196 -0
- package/lib/programmatic/run-channel-install-artifacts.js +137 -0
- package/lib/programmatic/run-channel-install-copilot.js +353 -0
- package/lib/programmatic/run-channel-install-options.js +232 -0
- package/lib/programmatic/run-channel-install-teams-bot.js +204 -0
- package/lib/programmatic/run-channel-install.js +346 -0
- package/lib/programmatic/run-channel-package.js +87 -7
- package/lib/programmatic/workspace-context.js +2 -0
- package/lib/role-assistant/evidence-sync.js +4 -2
- package/lib/role-assistant/evidence-validation-issues.js +51 -0
- package/lib/role-assistant/knowledge-sync.js +10 -4
- package/lib/role-assistant/settings.js +49 -2
- package/lib/role-assistant/test-cases-evaluate.js +30 -2
- package/lib/role-assistant/test-cases-evidence.js +175 -0
- package/lib/role-assistant/test-cases-search.js +229 -0
- package/lib/role-assistant/test-cases.js +37 -4
- package/lib/role-assistant/test-runner-search.js +149 -0
- package/lib/role-assistant/test-runner-workhub-helpers.js +40 -2
- package/lib/role-assistant/test-runner-workhub-soft.js +9 -3
- package/lib/role-assistant/test-runner-workhub.js +29 -11
- package/lib/role-assistant/test-runner.js +16 -1
- package/lib/schema/external-datasource.schema.json +112 -18
- package/lib/schema/external-system.schema.json +159 -2
- package/lib/schema/mcp-client-profile.schema.json +103 -0
- package/lib/utils/app-path-resolve.js +4 -0
- package/lib/utils/authentication-env-template.js +44 -6
- package/lib/utils/credential-secrets-env.js +6 -1
- package/lib/utils/env-copy.js +19 -7
- package/lib/utils/external-env-template.js +20 -2
- package/lib/utils/paths.js +18 -1
- package/lib/utils/platform-shell-package.js +69 -0
- package/lib/utils/port-resolver.js +50 -1
- package/lib/utils/rewrite-name-flag.js +20 -0
- package/lib/utils/role-assistant-paths.js +13 -10
- package/lib/utils/schema-resolver.js +11 -1
- package/lib/utils/secrets-helpers.js +12 -21
- package/lib/utils/token-manager.js +10 -5
- package/lib/validation/validate.js +40 -0
- package/package.json +1 -1
- package/templates/README.md +1 -1
- package/templates/applications/builder-api/Dockerfile +1 -1
- package/templates/applications/builder-api/env.template +7 -0
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +2 -2
- package/templates/applications/dataplane/rbac.yaml +7 -2
- package/templates/applications/keycloak/Dockerfile +30 -4
- package/templates/applications/keycloak/application.yaml +2 -2
- package/templates/applications/keycloak/cimd-lenient-parse/AbstractClientIdMetadataDocumentExecutor.java +896 -0
- package/templates/applications/keycloak/cimd-lenient-parse/SOURCE.md +19 -0
- package/templates/applications/keycloak/env.template +3 -0
- package/templates/applications/miso-controller/application.yaml +1 -2
- package/templates/applications/miso-controller/env.template +2 -0
- package/templates/channels/README.md +5 -5
- package/templates/channels/chatgpt/TROUBLESHOOTING.md +51 -0
- package/templates/channels/chatgpt/channel.json +59 -0
- package/templates/channels/chatgpt/icon.svg +1 -0
- package/templates/channels/chatgpt/install.template.md.hbs +20 -95
- package/templates/channels/chatgpt/integration/README.md +1 -1
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +49 -4
- package/templates/channels/chatgpt/integration/env.template +2 -4
- package/templates/channels/chatgpt/integration/rbac.json +13 -0
- package/templates/channels/claude/TROUBLESHOOTING.md +41 -0
- package/templates/channels/claude/channel.json +59 -0
- package/templates/channels/claude/icon.svg +1 -0
- package/templates/channels/claude/install.template.md.hbs +21 -0
- package/templates/channels/claude/integration/README.md +14 -0
- package/templates/channels/claude/integration/application.json +15 -0
- package/templates/channels/claude/integration/claude-system.json +59 -0
- package/templates/channels/claude/integration/env.template +2 -0
- package/templates/channels/claude/integration/rbac.json +13 -0
- package/templates/channels/claude/manifest.template.json.hbs +40 -0
- package/templates/channels/mcp-client/channel.json +72 -0
- package/templates/channels/mcp-client/icon.svg +1 -0
- package/templates/channels/mcp-client/install.template.md.hbs +24 -0
- package/templates/channels/mcp-client/integration/README.md +13 -0
- package/templates/channels/mcp-client/integration/application.json +15 -0
- package/templates/channels/mcp-client/integration/env.template +2 -0
- package/templates/channels/mcp-client/integration/mcp-client-system.json +72 -0
- package/templates/channels/mcp-client/integration/rbac.json +13 -0
- package/templates/channels/mcp-client/manifest.template.json.hbs +41 -0
- package/templates/channels/mcp-client/profiles/cursor.json +26 -0
- package/templates/channels/mcp-client/profiles/github-copilot.json +29 -0
- package/templates/channels/mcp-client/profiles/vscode.json +29 -0
- package/templates/channels/microsoft-copilot/channel.json +73 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +8 -4
- package/templates/channels/microsoft-copilot/entra-sso-registration.template.md.hbs +65 -5
- package/templates/channels/microsoft-copilot/icon.svg +1 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +9 -9
- package/templates/channels/microsoft-copilot/integration/README.md +1 -1
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +3 -6
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +63 -4
- package/templates/channels/microsoft-copilot/integration/rbac.json +13 -0
- package/templates/channels/microsoft-copilot/oauth-plugin-vault-required.template.md.hbs +5 -5
- package/templates/channels/slack/channel.json +223 -0
- package/templates/channels/slack/icon.svg +1 -0
- package/templates/channels/slack/install.template.md.hbs +36 -28
- package/templates/channels/slack/integration/README.md +245 -22
- package/templates/channels/slack/integration/application.json +44 -7
- package/templates/channels/slack/integration/env.template +15 -6
- package/templates/channels/slack/integration/openapi/openapi.json +126 -0
- package/templates/channels/slack/integration/rbac.json +84 -0
- package/templates/channels/slack/integration/slack-datasource-commands.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-conversations.json +28 -3
- package/templates/channels/slack/integration/slack-datasource-events.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-interactive.json +154 -0
- package/templates/channels/slack/integration/slack-datasource-messages.json +40 -2
- package/templates/channels/slack/integration/slack-system.json +155 -77
- package/templates/channels/slack/manifest.template.json.hbs +5 -2
- package/templates/channels/teams-bot/channel.json +161 -0
- package/templates/channels/teams-bot/icon.svg +1 -0
- package/templates/channels/teams-bot/install.template.md.hbs +12 -13
- package/templates/channels/teams-bot/integration/README.md +5 -4
- package/templates/channels/teams-bot/integration/application.json +6 -4
- package/templates/channels/teams-bot/integration/env.template +5 -4
- package/templates/channels/teams-bot/integration/openapi/openapi.json +68 -0
- package/templates/channels/teams-bot/integration/rbac.json +21 -0
- package/templates/channels/teams-bot/integration/teams-bot-datasource-activities.json +154 -0
- package/templates/channels/teams-bot/integration/teams-bot-system.json +128 -23
- package/templates/channels/web-embed/channel.json +60 -0
- package/templates/channels/web-embed/install.template.md.hbs +1 -1
- package/templates/channels/web-embed/integration/README.md +2 -2
- package/templates/channels/web-embed/integration/application.json +2 -2
- package/templates/channels/web-embed/integration/env.template +1 -1
- package/templates/channels/web-embed/integration/rbac.json +13 -0
- package/templates/channels/web-embed/integration/web-embed-system.json +50 -4
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant lifecycle is not Connected System governance.
|
|
3
|
+
*
|
|
4
|
+
* Generated digital-worker datasources do not own scenario packs. Those packs
|
|
5
|
+
* belong on the Connected System. Missing-pack FAILED must not fail RA prove.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const { VERDICT } = require('./product-model');
|
|
11
|
+
|
|
12
|
+
const GOVERNANCE_SCENARIOS_MISSING = 'GOVERNANCE_SCENARIOS_MISSING';
|
|
13
|
+
const GOVERNANCE_NOT_VERIFIED = 'GOVERNANCE_NOT_VERIFIED';
|
|
14
|
+
const ROLE_ASSISTANT_GOVERNANCE_WARNING = 'ROLE_ASSISTANT_GOVERNANCE_PACKS';
|
|
15
|
+
|
|
16
|
+
const VERDICT_RANK = Object.freeze({
|
|
17
|
+
[VERDICT.FAILED]: 4,
|
|
18
|
+
[VERDICT.NOT_VERIFIED]: 3,
|
|
19
|
+
[VERDICT.VERIFIED]: 2,
|
|
20
|
+
[VERDICT.NOT_APPLICABLE]: 1
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
function readGovernanceCode(block) {
|
|
24
|
+
if (!block || typeof block !== 'object') {
|
|
25
|
+
return '';
|
|
26
|
+
}
|
|
27
|
+
return String(block.errorCode || block.message || '').trim();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function governanceIsMissingPack(block) {
|
|
31
|
+
if (!block || typeof block !== 'object') {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (String(block.verdict || '').trim() !== VERDICT.FAILED) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return readGovernanceCode(block) === GOVERNANCE_SCENARIOS_MISSING;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function notApplicableMissingPack(block) {
|
|
41
|
+
const base = block && typeof block === 'object' ? { ...block } : {};
|
|
42
|
+
base.verdict = VERDICT.NOT_APPLICABLE;
|
|
43
|
+
base.errorCode = GOVERNANCE_SCENARIOS_MISSING;
|
|
44
|
+
return base;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function remapDatasourceRow(row) {
|
|
48
|
+
if (!row || typeof row !== 'object' || !governanceIsMissingPack(row.governance)) {
|
|
49
|
+
return { row, remapped: false };
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
row: { ...row, governance: notApplicableMissingPack(row.governance) },
|
|
53
|
+
remapped: true
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function worseGovernance(left, right) {
|
|
58
|
+
const leftRank = VERDICT_RANK[left && left.verdict] || 0;
|
|
59
|
+
const rightRank = VERDICT_RANK[right && right.verdict] || 0;
|
|
60
|
+
return rightRank > leftRank ? right : left;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function rollupGovernanceFromDatasources(datasources) {
|
|
64
|
+
let worst = null;
|
|
65
|
+
for (const row of datasources) {
|
|
66
|
+
if (row && row.governance) {
|
|
67
|
+
worst = worseGovernance(worst, row.governance);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return worst;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function stripGovernanceNotVerified(recommendations) {
|
|
74
|
+
if (!Array.isArray(recommendations)) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return recommendations.filter((rec) => rec && rec.code !== GOVERNANCE_NOT_VERIFIED);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function appendRoleAssistantGovernanceWarning(warnings) {
|
|
81
|
+
const next = Array.isArray(warnings) ? [...warnings] : [];
|
|
82
|
+
const exists = next.some((row) => row && row.code === ROLE_ASSISTANT_GOVERNANCE_WARNING);
|
|
83
|
+
if (!exists) {
|
|
84
|
+
next.push({
|
|
85
|
+
code: ROLE_ASSISTANT_GOVERNANCE_WARNING,
|
|
86
|
+
message:
|
|
87
|
+
'Role Assistant datasources do not use Connected System governance packs. ' +
|
|
88
|
+
'Isolation is Work Hub cases and role-assistant.yaml.'
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return next;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Treat CS-only missing packs as not applicable on a Role Assistant report.
|
|
96
|
+
* @param {Object} report
|
|
97
|
+
* @returns {Object}
|
|
98
|
+
*/
|
|
99
|
+
function remapRoleAssistantGovernanceReport(report) {
|
|
100
|
+
if (!report || typeof report !== 'object') {
|
|
101
|
+
return report;
|
|
102
|
+
}
|
|
103
|
+
const next = { ...report };
|
|
104
|
+
let remapped = false;
|
|
105
|
+
if (Array.isArray(report.datasources)) {
|
|
106
|
+
next.datasources = report.datasources.map((row) => {
|
|
107
|
+
const mapped = remapDatasourceRow(row);
|
|
108
|
+
if (mapped.remapped) {
|
|
109
|
+
remapped = true;
|
|
110
|
+
}
|
|
111
|
+
return mapped.row;
|
|
112
|
+
});
|
|
113
|
+
if (remapped) {
|
|
114
|
+
next.governance =
|
|
115
|
+
rollupGovernanceFromDatasources(next.datasources) ||
|
|
116
|
+
notApplicableMissingPack(report.governance);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
if (governanceIsMissingPack(next.governance)) {
|
|
120
|
+
next.governance = notApplicableMissingPack(next.governance);
|
|
121
|
+
remapped = true;
|
|
122
|
+
}
|
|
123
|
+
if (!remapped) {
|
|
124
|
+
return report;
|
|
125
|
+
}
|
|
126
|
+
next.recommendations = stripGovernanceNotVerified(report.recommendations);
|
|
127
|
+
next.warnings = appendRoleAssistantGovernanceWarning(report.warnings);
|
|
128
|
+
return next;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Pillar is satisfied for overall lifecycle (verified or not applicable).
|
|
133
|
+
* @param {string|undefined} verdict
|
|
134
|
+
* @returns {boolean}
|
|
135
|
+
*/
|
|
136
|
+
function lifecyclePillarSatisfied(verdict) {
|
|
137
|
+
return verdict === VERDICT.VERIFIED || verdict === VERDICT.NOT_APPLICABLE;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
module.exports = {
|
|
141
|
+
GOVERNANCE_SCENARIOS_MISSING,
|
|
142
|
+
ROLE_ASSISTANT_GOVERNANCE_WARNING,
|
|
143
|
+
governanceIsMissingPack,
|
|
144
|
+
remapRoleAssistantGovernanceReport,
|
|
145
|
+
lifecyclePillarSatisfied
|
|
146
|
+
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @fileoverview Upload local governance scenario YAML packs to dataplane (419.0).
|
|
3
3
|
* @author AI Fabrix Team
|
|
4
|
-
* @version
|
|
4
|
+
* @version 2.0.0
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
'use strict';
|
|
@@ -17,6 +17,9 @@ const {
|
|
|
17
17
|
const yaml = require('js-yaml');
|
|
18
18
|
const logger = require('../utils/logger');
|
|
19
19
|
const { getIntegrationPath } = require('../utils/paths');
|
|
20
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
21
|
+
const { discoverIntegrationFiles } = require('../commands/repair-internal');
|
|
22
|
+
const { parseAccessControlAbac } = require('./access-control-abac');
|
|
20
23
|
const { upsertDatasourceGovernancePack } = require('../api/governance-scenario-pack.api');
|
|
21
24
|
const { formatSuccessLine, formatWarningLine } = require('../utils/cli-test-layout-chalk');
|
|
22
25
|
const { shortGovernanceApiError } = require('../governance/governance-cli-errors');
|
|
@@ -63,6 +66,158 @@ function loadPackFromFile(filePath) {
|
|
|
63
66
|
return doc;
|
|
64
67
|
}
|
|
65
68
|
|
|
69
|
+
/**
|
|
70
|
+
* @param {string} filePath
|
|
71
|
+
* @returns {string}
|
|
72
|
+
*/
|
|
73
|
+
function scenarioFileStem(filePath) {
|
|
74
|
+
return path.basename(filePath).replace(/\.ya?ml$/i, '').trim();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Sidecar and CLI callers historically received a number. Keep both shapes.
|
|
79
|
+
* @param {unknown} result
|
|
80
|
+
* @returns {{ uploaded: number, warnings: string[] }}
|
|
81
|
+
*/
|
|
82
|
+
function normalizeGovernanceUploadResult(result) {
|
|
83
|
+
if (typeof result === 'number') {
|
|
84
|
+
return { uploaded: result, warnings: [] };
|
|
85
|
+
}
|
|
86
|
+
const uploaded = Number(result && result.uploaded);
|
|
87
|
+
const warnings = result && Array.isArray(result.warnings) ? result.warnings : [];
|
|
88
|
+
return {
|
|
89
|
+
uploaded: Number.isFinite(uploaded) ? uploaded : 0,
|
|
90
|
+
warnings
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @param {string} appPath
|
|
96
|
+
* @returns {Map<string, Object>}
|
|
97
|
+
*/
|
|
98
|
+
function loadPackageDatasourceMap(appPath) {
|
|
99
|
+
const map = new Map();
|
|
100
|
+
if (!existsSync(appPath)) {
|
|
101
|
+
return map;
|
|
102
|
+
}
|
|
103
|
+
const { datasourceFiles } = discoverIntegrationFiles(appPath);
|
|
104
|
+
for (const name of datasourceFiles) {
|
|
105
|
+
try {
|
|
106
|
+
const doc = loadConfigFile(path.join(appPath, name));
|
|
107
|
+
const key = doc && typeof doc.key === 'string' ? doc.key.trim() : '';
|
|
108
|
+
if (key) {
|
|
109
|
+
map.set(key, doc);
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// skip unreadable datasource files
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return map;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* @param {string} message
|
|
120
|
+
* @param {{ silent?: boolean }} opts
|
|
121
|
+
* @param {string[]} collected
|
|
122
|
+
*/
|
|
123
|
+
function emitWarning(message, opts, collected) {
|
|
124
|
+
collected.push(message);
|
|
125
|
+
if (!opts.silent) {
|
|
126
|
+
logger.log(formatWarningLine(message));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Owner = YAML stem when it matches a package datasource key.
|
|
132
|
+
* Otherwise fall back to search.datasourceKeys (legacy filenames).
|
|
133
|
+
* @param {string} stem
|
|
134
|
+
* @param {string[]} searchKeys
|
|
135
|
+
* @param {Set<string>} packageKeys
|
|
136
|
+
* @returns {{ ownerKeys: string[], warnings: string[], skipped: boolean }}
|
|
137
|
+
*/
|
|
138
|
+
function resolveOwnerTargets(stem, searchKeys, packageKeys) {
|
|
139
|
+
if (packageKeys.has(stem)) {
|
|
140
|
+
const warnings = [];
|
|
141
|
+
if (searchKeys.length > 0 && !searchKeys.includes(stem)) {
|
|
142
|
+
warnings.push(
|
|
143
|
+
`${stem}.yaml searches ${searchKeys.join(', ')} — uploading onto ${stem} (carrier search)`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
return { ownerKeys: [stem], warnings, skipped: false };
|
|
147
|
+
}
|
|
148
|
+
if (searchKeys.length === 0) {
|
|
149
|
+
return {
|
|
150
|
+
ownerKeys: [],
|
|
151
|
+
warnings: [
|
|
152
|
+
`Skipping ${stem}.yaml — filename is not a package datasource key and scenarios have no datasourceKeys`
|
|
153
|
+
],
|
|
154
|
+
skipped: true
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
ownerKeys: searchKeys,
|
|
159
|
+
warnings: [
|
|
160
|
+
`${stem}.yaml is not a package datasource key — uploading onto search keys (${searchKeys.join(', ')}). Rename to <datasourceKey>.yaml`
|
|
161
|
+
],
|
|
162
|
+
skipped: false
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* @param {Object} ctx
|
|
168
|
+
* @returns {Promise<number>}
|
|
169
|
+
*/
|
|
170
|
+
async function uploadOneScenarioFile(ctx) {
|
|
171
|
+
const { filePath, packageKeys, dataplaneUrl, authConfig, opts, warnings, ownerStems } = ctx;
|
|
172
|
+
const pack = loadPackFromFile(filePath);
|
|
173
|
+
const searchKeys = datasourceKeysFromPack(pack);
|
|
174
|
+
const stem = scenarioFileStem(filePath);
|
|
175
|
+
const resolved = resolveOwnerTargets(stem, searchKeys, packageKeys);
|
|
176
|
+
for (const warning of resolved.warnings) {
|
|
177
|
+
emitWarning(warning, opts, warnings);
|
|
178
|
+
}
|
|
179
|
+
if (resolved.skipped || resolved.ownerKeys.length === 0) {
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
if (packageKeys.has(stem)) {
|
|
183
|
+
ownerStems.push(stem);
|
|
184
|
+
}
|
|
185
|
+
let uploaded = 0;
|
|
186
|
+
for (const dsKey of resolved.ownerKeys) {
|
|
187
|
+
await upsertDatasourceGovernancePack(dataplaneUrl, authConfig, dsKey, pack);
|
|
188
|
+
uploaded += 1;
|
|
189
|
+
if (!opts.silent) {
|
|
190
|
+
logger.log(
|
|
191
|
+
formatSuccessLine(
|
|
192
|
+
`Governance scenario pack uploaded for ${dsKey} (${path.basename(filePath)})`
|
|
193
|
+
)
|
|
194
|
+
);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return uploaded;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* @param {Map<string, Object>} packageMap
|
|
202
|
+
* @param {string[]} ownerStems
|
|
203
|
+
* @param {{ silent?: boolean }} opts
|
|
204
|
+
* @param {string[]} warnings
|
|
205
|
+
*/
|
|
206
|
+
function warnMissingAbacOwnerFiles(packageMap, ownerStems, opts, warnings) {
|
|
207
|
+
const present = new Set(ownerStems);
|
|
208
|
+
for (const [key, doc] of packageMap) {
|
|
209
|
+
const abac = parseAccessControlAbac(doc);
|
|
210
|
+
if (abac.applicability === 'notApplicable' || present.has(key)) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
emitWarning(
|
|
214
|
+
`No scenarios/${key}.yaml for ABAC-required datasource ${key}`,
|
|
215
|
+
opts,
|
|
216
|
+
warnings
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
66
221
|
/**
|
|
67
222
|
* Upload scenario packs under integration/<systemKey>/scenarios/ to dataplane.
|
|
68
223
|
* @async
|
|
@@ -70,40 +225,33 @@ function loadPackFromFile(filePath) {
|
|
|
70
225
|
* @param {string} dataplaneUrl
|
|
71
226
|
* @param {Object} authConfig
|
|
72
227
|
* @param {{ silent?: boolean }} [opts]
|
|
73
|
-
* @returns {Promise<number
|
|
228
|
+
* @returns {Promise<{ uploaded: number, warnings: string[] }>}
|
|
74
229
|
*/
|
|
75
230
|
async function uploadGovernanceScenarioPacks(systemKey, dataplaneUrl, authConfig, opts = {}) {
|
|
76
|
-
const
|
|
77
|
-
const files = listScenarioYamlFiles(
|
|
78
|
-
|
|
79
|
-
|
|
231
|
+
const appPath = getIntegrationPath(systemKey);
|
|
232
|
+
const files = listScenarioYamlFiles(path.join(appPath, 'scenarios'));
|
|
233
|
+
const warnings = [];
|
|
234
|
+
const packageMap = loadPackageDatasourceMap(appPath);
|
|
235
|
+
if (files.length === 0) {
|
|
236
|
+
warnMissingAbacOwnerFiles(packageMap, [], opts, warnings);
|
|
237
|
+
return { uploaded: 0, warnings };
|
|
238
|
+
}
|
|
239
|
+
const packageKeys = new Set(packageMap.keys());
|
|
240
|
+
const ownerStems = [];
|
|
80
241
|
let uploaded = 0;
|
|
81
242
|
for (const filePath of files) {
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
continue;
|
|
93
|
-
}
|
|
94
|
-
for (const dsKey of dsKeys) {
|
|
95
|
-
await upsertDatasourceGovernancePack(dataplaneUrl, authConfig, dsKey, pack);
|
|
96
|
-
uploaded += 1;
|
|
97
|
-
if (!opts.silent) {
|
|
98
|
-
logger.log(
|
|
99
|
-
formatSuccessLine(
|
|
100
|
-
`Governance scenario pack uploaded for ${dsKey} (${path.basename(filePath)})`
|
|
101
|
-
)
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
243
|
+
uploaded += await uploadOneScenarioFile({
|
|
244
|
+
filePath,
|
|
245
|
+
packageKeys,
|
|
246
|
+
dataplaneUrl,
|
|
247
|
+
authConfig,
|
|
248
|
+
opts,
|
|
249
|
+
warnings,
|
|
250
|
+
ownerStems
|
|
251
|
+
});
|
|
105
252
|
}
|
|
106
|
-
|
|
253
|
+
warnMissingAbacOwnerFiles(packageMap, ownerStems, opts, warnings);
|
|
254
|
+
return { uploaded, warnings };
|
|
107
255
|
}
|
|
108
256
|
|
|
109
257
|
/**
|
|
@@ -185,6 +333,9 @@ module.exports = {
|
|
|
185
333
|
listScenarioYamlFiles,
|
|
186
334
|
datasourceKeysFromPack,
|
|
187
335
|
loadPackFromFile,
|
|
336
|
+
scenarioFileStem,
|
|
337
|
+
resolveOwnerTargets,
|
|
338
|
+
normalizeGovernanceUploadResult,
|
|
188
339
|
uploadGovernanceScenarioPacks,
|
|
189
340
|
downloadGovernanceScenarioPacks,
|
|
190
341
|
downloadGovernanceScenarioPacksFromManifest
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser CORS for builder-api from configured public URL allowlists.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Reflect Origin only when it matches ALLOWED_ORIGINS
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const CORS_ALLOW_HEADERS = 'Authorization, Content-Type, X-Request-Id, X-Correlation-Id, Idempotency-Key';
|
|
12
|
+
const CORS_ALLOW_METHODS = 'GET, POST, PATCH, OPTIONS';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
16
|
+
* @returns {string[]}
|
|
17
|
+
*/
|
|
18
|
+
function parseOriginAllowlist(env = process.env) {
|
|
19
|
+
const raw = [env.ALLOWED_ORIGINS, env.MISO_ALLOWED_ORIGINS]
|
|
20
|
+
.filter((value) => typeof value === 'string' && value.trim())
|
|
21
|
+
.join(',');
|
|
22
|
+
return raw
|
|
23
|
+
.split(',')
|
|
24
|
+
.map((part) => part.trim())
|
|
25
|
+
.filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* @param {string} origin
|
|
30
|
+
* @param {string} pattern
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
function originMatchesPattern(origin, pattern) {
|
|
34
|
+
if (!origin || !pattern) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
if (origin === pattern) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
if (!pattern.endsWith(':*')) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
try {
|
|
44
|
+
const url = new URL(origin);
|
|
45
|
+
return `${url.protocol}//${url.hostname}` === pattern.slice(0, -2) && Boolean(url.port);
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {import('http').IncomingMessage} req
|
|
53
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
54
|
+
* @returns {string|null}
|
|
55
|
+
*/
|
|
56
|
+
function allowedOriginForRequest(req, env = process.env) {
|
|
57
|
+
const origin = req && req.headers && req.headers.origin;
|
|
58
|
+
if (!origin || typeof origin !== 'string') {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
const matched = parseOriginAllowlist(env).some((pattern) => originMatchesPattern(origin, pattern));
|
|
62
|
+
return matched ? origin : null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* @param {import('http').IncomingMessage} req
|
|
67
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
68
|
+
* @returns {Record<string, string>}
|
|
69
|
+
*/
|
|
70
|
+
function corsHeadersForRequest(req, env = process.env) {
|
|
71
|
+
const allowed = allowedOriginForRequest(req, env);
|
|
72
|
+
if (!allowed) {
|
|
73
|
+
return {};
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
'Access-Control-Allow-Origin': allowed,
|
|
77
|
+
Vary: 'Origin',
|
|
78
|
+
'Access-Control-Allow-Headers': CORS_ALLOW_HEADERS,
|
|
79
|
+
'Access-Control-Allow-Methods': CORS_ALLOW_METHODS
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Merge CORS onto every writeHead so JSON, problems, and raw bytes stay consistent.
|
|
85
|
+
* @param {import('http').IncomingMessage} req
|
|
86
|
+
* @param {import('http').ServerResponse} res
|
|
87
|
+
* @param {NodeJS.ProcessEnv} [env]
|
|
88
|
+
*/
|
|
89
|
+
function applyCorsToResponse(req, res, env = process.env) {
|
|
90
|
+
const originalWriteHead = res.writeHead.bind(res);
|
|
91
|
+
res.writeHead = function writeHeadWithCors(statusCode, reasonOrHeaders, maybeHeaders) {
|
|
92
|
+
const hasMessage = typeof reasonOrHeaders === 'string';
|
|
93
|
+
const headers = hasMessage ? maybeHeaders : reasonOrHeaders;
|
|
94
|
+
const merged = { ...corsHeadersForRequest(req, env), ...(headers || {}) };
|
|
95
|
+
if (hasMessage) {
|
|
96
|
+
return originalWriteHead(statusCode, reasonOrHeaders, merged);
|
|
97
|
+
}
|
|
98
|
+
return originalWriteHead(statusCode, merged);
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* @param {import('http').IncomingMessage} req
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
106
|
+
function isCorsPreflight(req) {
|
|
107
|
+
return String((req && req.method) || '').toUpperCase() === 'OPTIONS';
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* @param {import('http').IncomingMessage} req
|
|
112
|
+
* @param {import('http').ServerResponse} res
|
|
113
|
+
* @param {string} requestId
|
|
114
|
+
* @param {string} pathname
|
|
115
|
+
* @param {Function} logRequestOutcome
|
|
116
|
+
* @returns {boolean} true when the request was fully handled
|
|
117
|
+
*/
|
|
118
|
+
function handleCorsPreflight(req, res, requestId, pathname, logRequestOutcome) {
|
|
119
|
+
applyCorsToResponse(req, res);
|
|
120
|
+
if (!isCorsPreflight(req)) {
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
res.writeHead(204);
|
|
124
|
+
res.end();
|
|
125
|
+
logRequestOutcome({
|
|
126
|
+
requestId,
|
|
127
|
+
route: pathname,
|
|
128
|
+
method: req.method,
|
|
129
|
+
status: 204,
|
|
130
|
+
userId: 'anonymous'
|
|
131
|
+
});
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
module.exports = {
|
|
136
|
+
parseOriginAllowlist,
|
|
137
|
+
originMatchesPattern,
|
|
138
|
+
allowedOriginForRequest,
|
|
139
|
+
corsHeadersForRequest,
|
|
140
|
+
applyCorsToResponse,
|
|
141
|
+
isCorsPreflight,
|
|
142
|
+
handleCorsPreflight
|
|
143
|
+
};
|
|
@@ -13,6 +13,8 @@ const { dispatchHandler } = require('./route-handlers');
|
|
|
13
13
|
const { withWorkspaceLock } = require('./workspace-lock');
|
|
14
14
|
const { getIdempotentResult, setIdempotentResult } = require('./idempotency-cache');
|
|
15
15
|
const { withRequestTimeout } = require('./request-timeout');
|
|
16
|
+
const { isHttpRawResult } = require('./http-raw-response');
|
|
17
|
+
const { publicOriginFromRequest } = require('../channels/vendor-dataplane-url');
|
|
16
18
|
|
|
17
19
|
/**
|
|
18
20
|
* @param {Object} capability
|
|
@@ -140,6 +142,7 @@ async function executeCapabilityRequest(options) {
|
|
|
140
142
|
const auth = requiresAuth(capability)
|
|
141
143
|
? await authenticateRequest(req)
|
|
142
144
|
: { userId: 'anonymous', bearerToken: '' };
|
|
145
|
+
auth.publicOrigin = publicOriginFromRequest(req);
|
|
143
146
|
|
|
144
147
|
const body = req.method === 'GET' || req.method === 'DELETE' ? {} : await readJsonBody(req);
|
|
145
148
|
|
|
@@ -157,6 +160,9 @@ async function executeCapabilityRequest(options) {
|
|
|
157
160
|
}
|
|
158
161
|
|
|
159
162
|
const result = await runHandler(auth, capability, params, body, query);
|
|
163
|
+
if (isHttpRawResult(result)) {
|
|
164
|
+
return { httpStatus: 200, raw: result, requestId, userId: auth.userId };
|
|
165
|
+
}
|
|
160
166
|
return buildSuccessResponse({
|
|
161
167
|
auth,
|
|
162
168
|
idempotencyKey: req.headers['idempotency-key'],
|
|
@@ -16,6 +16,7 @@ const { executeCapabilityRequest } = require('./builder-api-request');
|
|
|
16
16
|
* @property {import('http').ServerResponse} res
|
|
17
17
|
* @property {Function} sendJson
|
|
18
18
|
* @property {Function} sendProblem
|
|
19
|
+
* @property {Function} [sendRaw]
|
|
19
20
|
* @property {Function} readJsonBody
|
|
20
21
|
* @property {Object} resolved
|
|
21
22
|
* @property {Object} query
|
|
@@ -26,54 +27,67 @@ const { executeCapabilityRequest } = require('./builder-api-request');
|
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* @param {DispatchCapabilityRouteOptions} options
|
|
30
|
+
* @param {Object} outcome
|
|
31
|
+
* @returns {void}
|
|
29
32
|
*/
|
|
30
|
-
|
|
31
|
-
const {
|
|
32
|
-
|
|
33
|
-
res,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
function deliverCapabilityOutcome(options, outcome) {
|
|
34
|
+
const { req, res, sendJson, sendRaw, resolved, logRequestOutcome } = options;
|
|
35
|
+
if (outcome.raw && typeof sendRaw === 'function') {
|
|
36
|
+
sendRaw(res, outcome.raw, outcome.requestId);
|
|
37
|
+
} else {
|
|
38
|
+
sendJson(res, outcome.httpStatus, outcome.body, outcome.requestId);
|
|
39
|
+
}
|
|
40
|
+
logRequestOutcome({
|
|
41
|
+
requestId: outcome.requestId,
|
|
42
|
+
correlationId: (outcome.body && outcome.body.correlationId) || outcome.requestId,
|
|
43
|
+
route: resolved.routeKey,
|
|
44
|
+
method: req.method,
|
|
45
|
+
status: outcome.httpStatus,
|
|
46
|
+
systemKey: resolved.params.systemKey,
|
|
47
|
+
userId: outcome.userId
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @param {DispatchCapabilityRouteOptions} options
|
|
53
|
+
* @param {Error & { statusCode?: number, retryAfter?: number }} err
|
|
54
|
+
* @returns {void}
|
|
55
|
+
*/
|
|
56
|
+
function handleDispatchError(options, err) {
|
|
57
|
+
const { req, res, sendProblem, resolved, requestId, pathname, logRequestOutcome } = options;
|
|
58
|
+
const status = err.statusCode || 500;
|
|
59
|
+
if (status === 409 && err.retryAfter) {
|
|
60
|
+
res.setHeader('Retry-After', String(err.retryAfter));
|
|
61
|
+
}
|
|
62
|
+
sendProblem(req, res, err, status, requestId, pathname);
|
|
63
|
+
logRequestOutcome({
|
|
39
64
|
requestId,
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
65
|
+
route: resolved.routeKey,
|
|
66
|
+
method: req.method,
|
|
67
|
+
status,
|
|
68
|
+
systemKey: resolved.params.systemKey,
|
|
69
|
+
error: err.message
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* @param {DispatchCapabilityRouteOptions} options
|
|
75
|
+
* @returns {Promise<void>}
|
|
76
|
+
*/
|
|
77
|
+
async function dispatchCapabilityRoute(options) {
|
|
43
78
|
try {
|
|
44
79
|
const outcome = await executeCapabilityRequest({
|
|
45
|
-
req,
|
|
46
|
-
capability: resolved.capability,
|
|
47
|
-
params: resolved.params,
|
|
48
|
-
routeKey: resolved.routeKey,
|
|
49
|
-
requestId,
|
|
50
|
-
readJsonBody,
|
|
51
|
-
query
|
|
52
|
-
});
|
|
53
|
-
sendJson(res, outcome.httpStatus, outcome.body, outcome.requestId);
|
|
54
|
-
logRequestOutcome({
|
|
55
|
-
requestId: outcome.requestId,
|
|
56
|
-
correlationId: outcome.body.correlationId,
|
|
57
|
-
route: resolved.routeKey,
|
|
58
|
-
method: req.method,
|
|
59
|
-
status: outcome.httpStatus,
|
|
60
|
-
systemKey: resolved.params.systemKey,
|
|
61
|
-
userId: outcome.userId
|
|
80
|
+
req: options.req,
|
|
81
|
+
capability: options.resolved.capability,
|
|
82
|
+
params: options.resolved.params,
|
|
83
|
+
routeKey: options.resolved.routeKey,
|
|
84
|
+
requestId: options.requestId,
|
|
85
|
+
readJsonBody: options.readJsonBody,
|
|
86
|
+
query: options.query
|
|
62
87
|
});
|
|
88
|
+
deliverCapabilityOutcome(options, outcome);
|
|
63
89
|
} catch (err) {
|
|
64
|
-
|
|
65
|
-
if (status === 409 && err.retryAfter) {
|
|
66
|
-
res.setHeader('Retry-After', String(err.retryAfter));
|
|
67
|
-
}
|
|
68
|
-
sendProblem(req, res, err, status, requestId, pathname);
|
|
69
|
-
logRequestOutcome({
|
|
70
|
-
requestId,
|
|
71
|
-
route: resolved.routeKey,
|
|
72
|
-
method: req.method,
|
|
73
|
-
status,
|
|
74
|
-
systemKey: resolved.params.systemKey,
|
|
75
|
-
error: err.message
|
|
76
|
-
});
|
|
90
|
+
handleDispatchError(options, err);
|
|
77
91
|
}
|
|
78
92
|
}
|
|
79
93
|
|