@aifabrix/builder 2.50.0 → 2.51.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/.cursor/plans/Done/157.1-security-quality-violations-by-type.csv +374 -0
- package/.cursor/rules/assistant-driven-integration-build.mdc +40 -0
- package/integration/hubspot-test/create-hubspot.js +1 -16
- package/jest.isolated-projects.js +77 -1
- package/jest.projects.js +5 -426
- package/lib/api/assistants.api.js +67 -0
- package/lib/api/datasources-extended.api.js +6 -6
- package/lib/api/evidence-authoring-helpers.js +267 -0
- package/lib/api/evidence-authoring.api.js +337 -0
- package/lib/api/evidence-kinds.api.js +128 -0
- package/lib/api/evidence.api.js +57 -1
- package/lib/api/index.js +24 -7
- package/lib/api/pipeline.api.js +47 -3
- package/lib/api/records-bulk.api.js +28 -3
- package/lib/api/resource-types.api.js +4 -4
- package/lib/api/roles.api.js +121 -0
- package/lib/api/types/evidence-kinds.types.js +42 -0
- package/lib/api/types/evidence.types.js +156 -8
- package/lib/api/types/roles.types.js +36 -0
- package/lib/api/work.api.js +283 -0
- package/lib/app/certification-verify-rows.js +1 -1
- package/lib/app/run-deploy-job.js +108 -0
- package/lib/app/run-helpers.js +10 -3
- package/lib/app/run.js +8 -0
- package/lib/app/show.js +2 -2
- package/lib/certification/merge-certification-from-artifact.js +1 -1
- package/lib/channels/add-channel.js +54 -23
- package/lib/channels/approval-guides/chatgpt.js +8 -7
- package/lib/channels/approval-guides/microsoft-copilot.js +7 -8
- package/lib/channels/channel-artifact-generator.js +16 -3
- package/lib/channels/channel-credential.js +3 -1
- package/lib/channels/channel-probe-evidence.js +79 -0
- package/lib/channels/channel-probe.js +265 -13
- package/lib/channels/channel-runtime-resolver.js +120 -29
- package/lib/channels/channel-status-display.js +44 -0
- package/lib/channels/channel-targets.js +7 -0
- package/lib/channels/chatgpt-runtime-contract.js +29 -0
- package/lib/channels/microsoft-copilot-package.js +280 -0
- package/lib/channels/runtime-tool-catalog.js +165 -0
- package/lib/cli/index.js +6 -0
- package/lib/cli/setup-app.js +26 -5
- package/lib/cli/setup-dev.js +23 -7
- package/lib/cli/setup-external-system-help.js +8 -2
- package/lib/cli/setup-external-system.js +12 -0
- package/lib/cli/setup-utility-help-after.js +12 -0
- package/lib/cli/setup-utility-validate.js +41 -2
- package/lib/commands/app-test.js +2 -14
- package/lib/commands/datasource-load-export-cli.js +6 -1
- package/lib/commands/deploy-gate-failure-details.js +23 -0
- package/lib/commands/deploy-gate-orchestrator.js +64 -18
- package/lib/commands/dev-cli-handlers.js +6 -3
- package/lib/commands/dev-down.js +2 -6
- package/lib/commands/dev-init.js +7 -109
- package/lib/commands/dev-refresh.js +197 -0
- package/lib/commands/evidence-kind.js +478 -0
- package/lib/commands/governance-command-action.js +4 -2
- package/lib/commands/governance-generate-external.js +56 -21
- package/lib/commands/lifecycle-external.js +10 -4
- package/lib/commands/repair-datasource-apply.js +10 -1
- package/lib/commands/repair-datasource-run.js +4 -2
- package/lib/commands/repair-datasource-test-payload.js +80 -19
- package/lib/commands/repair-datasource-webhooks-sync-bridge.js +42 -3
- package/lib/commands/repair-internal.js +9 -1
- package/lib/commands/repair-openapi-export.js +1 -1
- package/lib/commands/repair-openapi-sync.js +43 -18
- package/lib/commands/resource-type.js +101 -18
- package/lib/commands/role-assistant.js +313 -0
- package/lib/commands/role.js +452 -0
- package/lib/commands/setup.js +1 -1
- package/lib/commands/upload-data-sync-finalize.js +5 -4
- package/lib/commands/upload-sidecar-followups.js +144 -47
- package/lib/commands/upload.js +43 -6
- package/lib/commands/validate-json.js +230 -0
- package/lib/commands/verify-governance-combined-external.js +73 -9
- package/lib/commands/verify-governance-db.js +80 -25
- package/lib/commands/verify-governance-external.js +29 -5
- package/lib/commands/verify-operations-step-plan.js +78 -40
- package/lib/commands/wizard-core-helpers.js +7 -3
- package/lib/commands/wizard-core.js +78 -9
- package/lib/core/audit-logger.js +1 -1
- package/lib/core/secrets-load.js +1 -3
- package/lib/datasource/bulk-loader-run-batches.js +4 -2
- package/lib/datasource/bulk-loader-service.js +5 -1
- package/lib/datasource/capability/relate-operations.js +1 -1
- package/lib/datasource/datasource-validate-summary.js +2 -2
- package/lib/datasource/validate.js +13 -6
- package/lib/external-system/deploy-helpers.js +35 -1
- package/lib/external-system/deploy.js +23 -10
- package/lib/external-system/download-helpers.js +111 -2
- package/lib/external-system/download-role-assistant.js +55 -0
- package/lib/external-system/download.js +45 -26
- package/lib/external-system/test-helpers.js +1 -1
- package/lib/external-system/test.js +39 -3
- package/lib/generator/builders.js +3 -0
- package/lib/generator/helpers.js +13 -5
- package/lib/generator/split-variables.js +9 -3
- package/lib/generator/wizard.js +51 -4
- package/lib/infrastructure/helpers-docker-check.js +6 -0
- package/lib/infrastructure/helpers.js +1 -1
- package/lib/lifecycle/access-control-abac.js +121 -0
- package/lib/lifecycle/lifecycle-governance-options.js +4 -1
- package/lib/lifecycle/product-model.js +3 -2
- package/lib/lifecycle/report-display.js +14 -2
- package/lib/programmatic/auth-config.js +3 -2
- package/lib/programmatic/bearer-auth.js +1 -1
- package/lib/programmatic/capability-registry.js +20 -1
- package/lib/programmatic/command-options.js +1 -0
- package/lib/programmatic/enterprise-mcp-client.js +18 -2
- package/lib/programmatic/index.js +4 -0
- package/lib/programmatic/manifest-patch-section.js +48 -9
- package/lib/programmatic/manifest-section-registry-data.js +3 -2
- package/lib/programmatic/manifest-section-registry.js +18 -1
- package/lib/programmatic/openapi-apply-result-schemas.js +44 -0
- package/lib/programmatic/openapi-component-schemas.js +16 -1
- package/lib/programmatic/openapi-description-enricher.js +3 -1
- package/lib/programmatic/openapi-descriptions.js +55 -5
- package/lib/programmatic/openapi-evidence-schemas.js +161 -0
- package/lib/programmatic/openapi-manifest-schemas.js +6 -2
- package/lib/programmatic/openapi-operation-bindings.js +99 -0
- package/lib/programmatic/openapi-request-schemas.js +36 -1
- package/lib/programmatic/openapi-result-schemas.js +29 -12
- package/lib/programmatic/openapi-schema-descriptions-certify.js +5 -1
- package/lib/programmatic/openapi-schema-descriptions-core.js +6 -3
- package/lib/programmatic/openapi-schema-descriptions-evidence.js +156 -0
- package/lib/programmatic/openapi-schema-descriptions-manifest.js +4 -3
- package/lib/programmatic/openapi-schema-descriptions-operations.js +70 -0
- package/lib/programmatic/openapi-schema-helpers.js +16 -1
- package/lib/programmatic/openapi-spec.js +48 -29
- package/lib/programmatic/orchestration-openapi-meta.js +5 -0
- package/lib/programmatic/request-timeout.js +3 -0
- package/lib/programmatic/route-handler-map.js +89 -2
- package/lib/programmatic/run-certify.js +15 -2
- package/lib/programmatic/run-deploy.js +74 -0
- package/lib/programmatic/run-evidence-authoring.js +80 -0
- package/lib/programmatic/run-protection.js +124 -0
- package/lib/programmatic/run-repository-emcp.js +11 -3
- package/lib/programmatic/run-template-workspace.js +42 -2
- package/lib/programmatic/workspace-context.js +6 -0
- package/lib/protection/auth-context.js +48 -6
- package/lib/protection/paths.js +22 -0
- package/lib/protection/run-protection-create-helpers.js +22 -1
- package/lib/protection/run-protection-create.js +1 -1
- package/lib/protection/sync-after-upload.js +1 -1
- package/lib/resolvers/evidence-kind-file.js +230 -0
- package/lib/resolvers/resource-type-file.js +36 -1
- package/lib/resolvers/role-file.js +227 -0
- package/lib/role-assistant/evidence-sync.js +450 -0
- package/lib/role-assistant/github-workflow.js +96 -0
- package/lib/role-assistant/knowledge-sync.js +465 -0
- package/lib/role-assistant/settings.js +126 -0
- package/lib/role-assistant/test-cases-evaluate.js +170 -0
- package/lib/role-assistant/test-cases-honesty.js +184 -0
- package/lib/role-assistant/test-cases.js +364 -0
- package/lib/role-assistant/test-runner-workhub-answers.js +203 -0
- package/lib/role-assistant/test-runner-workhub.js +385 -0
- package/lib/role-assistant/test-runner.js +500 -0
- package/lib/schema/application-schema.json +36 -2
- package/lib/schema/deployment-rules.yaml +1 -0
- package/lib/schema/external-datasource.schema.json +121 -4
- package/lib/schema/external-system.schema.json +20 -6
- package/lib/schema/infra.parameter.yaml +45 -1
- package/lib/schema/role-assistant-settings.schema.json +108 -0
- package/lib/schema/type/credential-field-registry.json +19 -3
- package/lib/utils/api.js +52 -21
- package/lib/utils/app-path-resolve.js +72 -24
- package/lib/utils/app-register-config.js +4 -3
- package/lib/utils/authentication-shape.js +3 -1
- package/lib/utils/cli-sync-options.js +59 -3
- package/lib/utils/cli-utils.js +11 -3
- package/lib/utils/deployment-validation.js +28 -3
- package/lib/utils/dev-ca-install.js +5 -6
- package/lib/utils/dev-config.js +2 -2
- package/lib/utils/dev-list-sort.js +145 -0
- package/lib/utils/dev-platform-tls.js +0 -63
- package/lib/utils/docker-build.js +46 -27
- package/lib/utils/docker-daemon-tls-ca.js +1 -1
- package/lib/utils/docker-exec.js +91 -6
- package/lib/utils/docker-run-ephemeral.js +55 -0
- package/lib/utils/enterprise-knowledge-paths.js +29 -12
- package/lib/utils/env-copy-keycloak.js +196 -0
- package/lib/utils/env-copy.js +35 -4
- package/lib/utils/error-formatters/error-parser.js +0 -3
- package/lib/utils/external-env-template.js +1 -1
- package/lib/utils/external-system-readiness-compact-blocks.js +28 -1
- package/lib/utils/external-system-readiness-compact-display.js +3 -0
- package/lib/utils/health-check-request.js +2 -3
- package/lib/utils/help-builder.js +4 -0
- package/lib/utils/image-name.js +22 -4
- package/lib/utils/logger.js +37 -4
- package/lib/utils/manifest-source-emit.js +1 -1
- package/lib/utils/paths.js +98 -26
- package/lib/utils/pipeline-upload-auth-retry.js +19 -3
- package/lib/utils/postgres-platform-bootstrap.js +24 -17
- package/lib/utils/postgres-wipe.js +5 -2
- package/lib/utils/remote-dev-auth.js +39 -4
- package/lib/utils/resolve-docker-image-ref.js +36 -6
- package/lib/utils/role-assistant-paths.js +98 -0
- package/lib/utils/schema-loader.js +41 -0
- package/lib/utils/schema-resolver.js +36 -4
- package/lib/utils/token-manager.js +47 -22
- package/lib/utils/upload-sync-options.js +17 -3
- package/lib/utils/url-declarative-resolve-load-doc.js +38 -1
- package/lib/validation/external-manifest-validator.js +31 -8
- package/lib/validation/openapi-contract-surface-validator.js +4 -0
- package/lib/validation/validate-external-file.js +8 -1
- package/lib/validation/validator.js +6 -5
- package/package.json +35 -12
- package/scripts/export-builder-api-evidence.js +3 -3
- package/scripts/lib/plan-actor.js +2 -2
- package/scripts/lib/plan-meta.js +1 -1
- package/scripts/lib/plan-parse.js +1 -1
- package/scripts/lib/plan-prevalidate-gates.js +12 -1
- package/scripts/lib/plan-run-report.js +13 -2
- package/scripts/pnpm/lib/resolve-aifabrix-bin.mjs +10 -10
- package/templates/applications/dataplane/application.yaml +47 -17
- package/templates/applications/dataplane/env.template +28 -9
- package/templates/applications/dataplane/rbac.yaml +78 -2
- package/templates/applications/miso-controller/application.yaml +10 -1
- package/templates/applications/miso-controller/env.template +13 -8
- package/templates/applications/miso-controller/rbac.yaml +72 -0
- package/templates/channels/chatgpt/install.template.md.hbs +42 -24
- package/templates/channels/chatgpt/integration/README.md +10 -7
- package/templates/channels/chatgpt/integration/application.json +2 -2
- package/templates/channels/chatgpt/integration/chatgpt-system.json +3 -3
- package/templates/channels/chatgpt/integration/env.template +1 -1
- package/templates/channels/chatgpt/manifest.template.json.hbs +36 -5
- package/templates/channels/microsoft-copilot/ai-plugin.template.json.hbs +28 -0
- package/templates/channels/microsoft-copilot/declarative-agent.template.json.hbs +22 -0
- package/templates/channels/microsoft-copilot/install.template.md.hbs +32 -22
- package/templates/channels/microsoft-copilot/integration/README.md +9 -7
- package/templates/channels/microsoft-copilot/integration/application.json +2 -2
- package/templates/channels/microsoft-copilot/integration/env.template +1 -1
- package/templates/channels/microsoft-copilot/integration/microsoft-copilot-system.json +7 -6
- package/templates/channels/microsoft-copilot/manifest.template.json.hbs +34 -6
- package/templates/channels/slack/integration/slack-system.json +60 -2
- package/templates/python/requirements.txt +1 -1
|
@@ -30,6 +30,7 @@ const { displayValidationResults } = require('../validation/validate-display');
|
|
|
30
30
|
const { syncDeployJsonFromSources } = require('./sync-deploy-manifest');
|
|
31
31
|
const { runDeployGateOrchestrator } = require('../commands/deploy-gate-orchestrator');
|
|
32
32
|
const { runPostPublishDataSync } = require('../commands/run-post-publish-data-sync');
|
|
33
|
+
const { uploadDigitalWorkerIfNeeded } = require('./deploy-helpers');
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Validates manifest on dataplane (dry-run) before controller deploy.
|
|
@@ -342,8 +343,20 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
|
|
|
342
343
|
* @param {Object} options - Deployment options
|
|
343
344
|
* @returns {Promise<Object>} Deployment configuration
|
|
344
345
|
*/
|
|
345
|
-
async function prepareDeploymentConfig(appName,
|
|
346
|
+
async function prepareDeploymentConfig(appName, options = {}) {
|
|
346
347
|
const { resolveEnvironment } = require('../core/config');
|
|
348
|
+
if (options.authConfig && (options.authConfig.token || options.authConfig.clientId)) {
|
|
349
|
+
const environment = options.environment || (await resolveEnvironment());
|
|
350
|
+
let controllerUrl = options.controllerUrl || '';
|
|
351
|
+
if (!controllerUrl) {
|
|
352
|
+
controllerUrl = await resolveControllerUrl();
|
|
353
|
+
}
|
|
354
|
+
if (!controllerUrl) {
|
|
355
|
+
throw new Error('Controller URL is required. Run "aifabrix login" first.');
|
|
356
|
+
}
|
|
357
|
+
return { environment, controllerUrl, authConfig: options.authConfig };
|
|
358
|
+
}
|
|
359
|
+
|
|
347
360
|
const environment = await resolveEnvironment();
|
|
348
361
|
const controllerUrl = await resolveControllerUrl();
|
|
349
362
|
const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
|
|
@@ -413,24 +426,26 @@ async function runExternalSystemDeployFlow(appName, options) {
|
|
|
413
426
|
const compact = options.verbose !== true;
|
|
414
427
|
const { appPath } = await detectAppType(appName);
|
|
415
428
|
logOfflinePathWhenType(appPath);
|
|
416
|
-
|
|
417
429
|
if (!compact) {
|
|
418
430
|
logger.log(chalk.blue(`\n🚀 Deploying external system: ${appName}`));
|
|
419
431
|
}
|
|
420
|
-
|
|
421
432
|
await validateExternalSystemForDeploy(appName, options, compact);
|
|
422
|
-
|
|
423
433
|
const gateOutcomes = await runDeployGateOrchestrator(appName, {
|
|
424
434
|
...options,
|
|
425
435
|
quietDeployUi: compact
|
|
426
436
|
});
|
|
427
|
-
|
|
428
437
|
await syncDeployJsonFromSources(appName);
|
|
429
|
-
|
|
430
438
|
const manifest = await generateControllerManifest(appName, options);
|
|
431
|
-
|
|
439
|
+
const digitalWorkerResult = await uploadDigitalWorkerIfNeeded(
|
|
440
|
+
appName,
|
|
441
|
+
options,
|
|
442
|
+
compact,
|
|
443
|
+
manifest
|
|
444
|
+
);
|
|
445
|
+
if (digitalWorkerResult) {
|
|
446
|
+
return digitalWorkerResult;
|
|
447
|
+
}
|
|
432
448
|
const { environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
|
|
433
|
-
|
|
434
449
|
let dataplaneUrl;
|
|
435
450
|
try {
|
|
436
451
|
dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, {
|
|
@@ -439,9 +454,7 @@ async function runExternalSystemDeployFlow(appName, options) {
|
|
|
439
454
|
} catch (err) {
|
|
440
455
|
throw new Error(`Failed to resolve dataplane URL before deploy: ${err.message}`);
|
|
441
456
|
}
|
|
442
|
-
|
|
443
457
|
await validateDataplaneManifestForDeploy(manifest, authConfig, dataplaneUrl, compact);
|
|
444
|
-
|
|
445
458
|
return executeDeployAndDisplay(manifest, controllerUrl, environment, authConfig, {
|
|
446
459
|
...options,
|
|
447
460
|
gateOutcomes,
|
|
@@ -11,12 +11,45 @@
|
|
|
11
11
|
const {
|
|
12
12
|
formatSuccessLine,
|
|
13
13
|
formatSuccessParagraph,
|
|
14
|
+
formatWarningLine,
|
|
14
15
|
sectionTitle,
|
|
15
16
|
headerKeyValue
|
|
16
17
|
} = require('../utils/cli-test-layout-chalk');
|
|
17
18
|
const logger = require('../utils/logger');
|
|
18
19
|
const { generateExternalReadmeContent } = require('../utils/external-readme');
|
|
19
20
|
const { getAuthCredentialTemplate } = require('../utils/authentication-shape');
|
|
21
|
+
const {
|
|
22
|
+
getCwdIntegrationRoot,
|
|
23
|
+
getCwdBuilderRoot,
|
|
24
|
+
getAppsMaterializationParent
|
|
25
|
+
} = require('../utils/paths');
|
|
26
|
+
const {
|
|
27
|
+
SYSTEM_KIND_ENTERPRISE_KNOWLEDGE,
|
|
28
|
+
getCwdEnterpriseKnowledgeRoot,
|
|
29
|
+
getEnterpriseKnowledgeRoot
|
|
30
|
+
} = require('../utils/enterprise-knowledge-paths');
|
|
31
|
+
const {
|
|
32
|
+
SYSTEM_KIND_DIGITAL_WORKER,
|
|
33
|
+
getCwdRoleAssistantRoot,
|
|
34
|
+
getRoleAssistantRoot
|
|
35
|
+
} = require('../utils/role-assistant-paths');
|
|
36
|
+
const path = require('path');
|
|
37
|
+
|
|
38
|
+
/** Runtime/API fields that must not appear on external-system manifests. */
|
|
39
|
+
const NON_MANIFEST_SYSTEM_KEYS = ['showOpenApiDocs'];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* True when cwd looks like a product/workspace repo (integration, builder, or EK tree).
|
|
43
|
+
* @returns {boolean}
|
|
44
|
+
*/
|
|
45
|
+
function cwdHasWorkspaceTree() {
|
|
46
|
+
return Boolean(
|
|
47
|
+
getCwdIntegrationRoot() ||
|
|
48
|
+
getCwdBuilderRoot() ||
|
|
49
|
+
getCwdEnterpriseKnowledgeRoot() ||
|
|
50
|
+
getCwdRoleAssistantRoot()
|
|
51
|
+
);
|
|
52
|
+
}
|
|
20
53
|
|
|
21
54
|
/**
|
|
22
55
|
* Validates system type from downloaded application
|
|
@@ -68,6 +101,61 @@ function validateDownloadedData(application, dataSources) {
|
|
|
68
101
|
}
|
|
69
102
|
}
|
|
70
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Strip dataplane runtime fields that are not part of external-system.schema.json.
|
|
106
|
+
* @param {Object} application
|
|
107
|
+
* @returns {Object}
|
|
108
|
+
*/
|
|
109
|
+
function sanitizeDownloadedApplication(application) {
|
|
110
|
+
if (!application || typeof application !== 'object') {
|
|
111
|
+
return application;
|
|
112
|
+
}
|
|
113
|
+
const cleaned = { ...application };
|
|
114
|
+
for (const key of NON_MANIFEST_SYSTEM_KEYS) {
|
|
115
|
+
delete cleaned[key];
|
|
116
|
+
}
|
|
117
|
+
return cleaned;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Download destination:
|
|
122
|
+
* - If cwd has `integration/`, `builder/`, or `enterprise-knowledge/` → write under cwd
|
|
123
|
+
* - Else → `aifabrix-work` / materialization parent
|
|
124
|
+
* Ignores materialization orphans when cwd has a workspace tree so re-download lands in-repo.
|
|
125
|
+
* @param {string} systemKey
|
|
126
|
+
* @param {Object} application
|
|
127
|
+
* @returns {string}
|
|
128
|
+
*/
|
|
129
|
+
function resolveDownloadDestinationPath(systemKey, application) {
|
|
130
|
+
const kind =
|
|
131
|
+
application && typeof application.systemKind === 'string'
|
|
132
|
+
? application.systemKind.trim()
|
|
133
|
+
: '';
|
|
134
|
+
const useCwd = cwdHasWorkspaceTree();
|
|
135
|
+
if (kind === SYSTEM_KIND_DIGITAL_WORKER) {
|
|
136
|
+
if (useCwd) {
|
|
137
|
+
const cwdRoleAssistant = getCwdRoleAssistantRoot();
|
|
138
|
+
const roleAssistantRoot = cwdRoleAssistant || path.join(path.resolve(process.cwd()), 'role-assistant');
|
|
139
|
+
return path.join(roleAssistantRoot, systemKey);
|
|
140
|
+
}
|
|
141
|
+
return path.join(getRoleAssistantRoot(), systemKey);
|
|
142
|
+
}
|
|
143
|
+
if (kind === SYSTEM_KIND_ENTERPRISE_KNOWLEDGE) {
|
|
144
|
+
if (useCwd) {
|
|
145
|
+
const cwdEk = getCwdEnterpriseKnowledgeRoot();
|
|
146
|
+
const ekRoot = cwdEk || path.join(path.resolve(process.cwd()), 'enterprise-knowledge');
|
|
147
|
+
return path.join(ekRoot, systemKey);
|
|
148
|
+
}
|
|
149
|
+
return path.join(getEnterpriseKnowledgeRoot(), systemKey);
|
|
150
|
+
}
|
|
151
|
+
if (useCwd) {
|
|
152
|
+
const cwdInt = getCwdIntegrationRoot();
|
|
153
|
+
const intRoot = cwdInt || path.join(path.resolve(process.cwd()), 'integration');
|
|
154
|
+
return path.join(intRoot, systemKey);
|
|
155
|
+
}
|
|
156
|
+
return path.join(getAppsMaterializationParent(), 'integration', systemKey);
|
|
157
|
+
}
|
|
158
|
+
|
|
71
159
|
function logDownloadCommandHeader(systemKey) {
|
|
72
160
|
logger.log('');
|
|
73
161
|
logger.log(sectionTitle('Download'));
|
|
@@ -83,13 +171,31 @@ function displayDownloadSuccess(systemKey, finalPath, datasourceCount) {
|
|
|
83
171
|
logger.log(headerKeyValue('Location:', finalPath));
|
|
84
172
|
}
|
|
85
173
|
|
|
174
|
+
/**
|
|
175
|
+
* Convert YAML components to JSON after download. On validation failure, keep takeout
|
|
176
|
+
* and warn — do not discard downloaded files.
|
|
177
|
+
* @param {string} systemKey
|
|
178
|
+
* @returns {Promise<{converted: boolean, error?: string}>}
|
|
179
|
+
*/
|
|
86
180
|
async function runConvertToJsonIfRequested(systemKey) {
|
|
87
181
|
const { runConvert } = require('../commands/convert');
|
|
88
182
|
try {
|
|
89
183
|
await runConvert(systemKey, { format: 'json', force: true });
|
|
90
184
|
logger.log(formatSuccessLine('Converted component files to JSON'));
|
|
185
|
+
return { converted: true };
|
|
91
186
|
} catch (convertErr) {
|
|
92
|
-
|
|
187
|
+
const message = convertErr && convertErr.message ? convertErr.message : String(convertErr);
|
|
188
|
+
logger.log(
|
|
189
|
+
formatWarningLine(
|
|
190
|
+
`Download saved files, but convert to JSON failed: ${message}`
|
|
191
|
+
)
|
|
192
|
+
);
|
|
193
|
+
logger.log(
|
|
194
|
+
formatWarningLine(
|
|
195
|
+
'Fix schema errors in the downloaded files, then run: aifabrix convert <systemKey> --format json'
|
|
196
|
+
)
|
|
197
|
+
);
|
|
198
|
+
return { converted: false, error: message };
|
|
93
199
|
}
|
|
94
200
|
}
|
|
95
201
|
|
|
@@ -183,10 +289,13 @@ function generateReadme(systemKey, application, dataSources) {
|
|
|
183
289
|
module.exports = {
|
|
184
290
|
validateSystemType,
|
|
185
291
|
validateDownloadedData,
|
|
292
|
+
sanitizeDownloadedApplication,
|
|
293
|
+
resolveDownloadDestinationPath,
|
|
186
294
|
logDownloadCommandHeader,
|
|
187
295
|
displayDownloadSuccess,
|
|
188
296
|
runConvertToJsonIfRequested,
|
|
189
297
|
generateVariablesYaml,
|
|
190
|
-
generateReadme
|
|
298
|
+
generateReadme,
|
|
299
|
+
NON_MANIFEST_SYSTEM_KEYS
|
|
191
300
|
};
|
|
192
301
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Role Assistant sidecar handling during external-system download.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Post-download evidence/knowledge/workflow actions for digitalWorker systems
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
const { formatWarningLine } = require('../utils/cli-test-layout-chalk');
|
|
13
|
+
const { exportRoleAssistantKnowledge, logKnowledgeExportSummary } = require('../role-assistant/knowledge-sync');
|
|
14
|
+
const { exportRoleAssistantEvidence, logRoleAssistantEvidenceExportSummary } = require('../role-assistant/evidence-sync');
|
|
15
|
+
|
|
16
|
+
async function maybeExportRoleAssistantEvidenceArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options) {
|
|
17
|
+
try {
|
|
18
|
+
const evidenceResult = await exportRoleAssistantEvidence({
|
|
19
|
+
systemKey,
|
|
20
|
+
dataplaneUrl,
|
|
21
|
+
authConfig,
|
|
22
|
+
manifest,
|
|
23
|
+
pathOptions: options.pathOptions || {}
|
|
24
|
+
});
|
|
25
|
+
logRoleAssistantEvidenceExportSummary(systemKey, evidenceResult, { verbose: options.verbose === true });
|
|
26
|
+
} catch (evidenceErr) {
|
|
27
|
+
logger.log(formatWarningLine(`Role assistant evidence export skipped: ${evidenceErr.message}`));
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function maybeExportRoleAssistantKnowledgeArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options) {
|
|
32
|
+
try {
|
|
33
|
+
const knowledgeResult = await exportRoleAssistantKnowledge({
|
|
34
|
+
systemKey,
|
|
35
|
+
dataplaneUrl,
|
|
36
|
+
authConfig,
|
|
37
|
+
manifest,
|
|
38
|
+
environment: options.env || options.environment || 'dev',
|
|
39
|
+
pathOptions: options.pathOptions || {}
|
|
40
|
+
});
|
|
41
|
+
logKnowledgeExportSummary(systemKey, knowledgeResult, { verbose: options.verbose === true });
|
|
42
|
+
} catch (knowledgeErr) {
|
|
43
|
+
logger.log(formatWarningLine(`Role assistant knowledge export skipped: ${knowledgeErr.message}`));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async function runRoleAssistantDownloadSidecars(params) {
|
|
48
|
+
const { systemKey, manifest, dataplaneUrl, authConfig, options = {} } = params;
|
|
49
|
+
await maybeExportRoleAssistantEvidenceArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options);
|
|
50
|
+
await maybeExportRoleAssistantKnowledgeArtifacts(systemKey, manifest, dataplaneUrl, authConfig, options);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = {
|
|
54
|
+
runRoleAssistantDownloadSidecars
|
|
55
|
+
};
|
|
@@ -32,7 +32,6 @@ const yaml = require('js-yaml');
|
|
|
32
32
|
const { getExternalSystemConfig } = require('../api/external-systems.api');
|
|
33
33
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
34
34
|
const { getConfig } = require('../core/config');
|
|
35
|
-
const { getIntegrationPath } = require('../utils/paths');
|
|
36
35
|
const { retemplateConfigurationForDownload, retemplateAllVariableDefinitions } = require('../utils/configuration-env-resolver');
|
|
37
36
|
const { syncRuntimeConfigurationFromDataplane } = require('../utils/runtime-configuration-download');
|
|
38
37
|
const logger = require('../utils/logger');
|
|
@@ -41,10 +40,13 @@ const generator = require('../generator');
|
|
|
41
40
|
const {
|
|
42
41
|
validateSystemType,
|
|
43
42
|
validateDownloadedData,
|
|
43
|
+
sanitizeDownloadedApplication,
|
|
44
|
+
resolveDownloadDestinationPath,
|
|
44
45
|
logDownloadCommandHeader,
|
|
45
46
|
displayDownloadSuccess,
|
|
46
47
|
runConvertToJsonIfRequested
|
|
47
48
|
} = require('./download-helpers');
|
|
49
|
+
const { runRoleAssistantDownloadSidecars } = require('./download-role-assistant');
|
|
48
50
|
|
|
49
51
|
/**
|
|
50
52
|
* Setup authentication and get dataplane URL
|
|
@@ -117,7 +119,11 @@ async function downloadFullManifest(dataplaneUrl, systemKey, authConfig, options
|
|
|
117
119
|
throw new Error('Application configuration not found in download response');
|
|
118
120
|
}
|
|
119
121
|
|
|
120
|
-
return {
|
|
122
|
+
return {
|
|
123
|
+
application: sanitizeDownloadedApplication(application),
|
|
124
|
+
dataSources,
|
|
125
|
+
version
|
|
126
|
+
};
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
/**
|
|
@@ -304,9 +310,8 @@ async function applyRetemplateToSystemFile(systemKey, systemFilePath) {
|
|
|
304
310
|
* @returns {Promise<string>} Final destination path
|
|
305
311
|
* @throws {Error} If processing fails
|
|
306
312
|
*/
|
|
307
|
-
async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
|
|
313
|
+
async function processDownloadedSystem(systemKey, manifest, finalPath, splitOptions = {}) {
|
|
308
314
|
const { application, dataSources, version } = manifest;
|
|
309
|
-
const finalPath = getIntegrationPath(systemKey);
|
|
310
315
|
|
|
311
316
|
logger.log(formatProgress('Creating integration directory…'));
|
|
312
317
|
logger.log(metadata(finalPath));
|
|
@@ -331,6 +336,28 @@ async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
|
|
|
331
336
|
return finalPath;
|
|
332
337
|
}
|
|
333
338
|
|
|
339
|
+
async function maybeSyncRuntimeConfigurationForDownload(systemKey, manifest, finalPath, dataplaneUrl, authConfig) {
|
|
340
|
+
try {
|
|
341
|
+
const configSync = await syncRuntimeConfigurationFromDataplane({
|
|
342
|
+
dataplaneUrl,
|
|
343
|
+
authConfig,
|
|
344
|
+
systemKey,
|
|
345
|
+
integrationPath: finalPath,
|
|
346
|
+
dataSources: manifest.dataSources
|
|
347
|
+
});
|
|
348
|
+
if (configSync.merged > 0) {
|
|
349
|
+
const nameList = configSync.names.length ? ` (${configSync.names.join(', ')})` : '';
|
|
350
|
+
logger.log(
|
|
351
|
+
formatSuccessLine(
|
|
352
|
+
`Merged ${configSync.merged} runtime configuration variable(s) into env.template${nameList}.`
|
|
353
|
+
)
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
} catch (syncErr) {
|
|
357
|
+
logger.log(formatWarningLine(`Runtime configuration sync skipped: ${syncErr.message}`));
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
334
361
|
/**
|
|
335
362
|
* Post-split download steps: governance packs, optional JSON convert, runtime config sync.
|
|
336
363
|
* @param {Object} params
|
|
@@ -348,7 +375,8 @@ async function finalizeDownloadArtifacts({
|
|
|
348
375
|
finalPath,
|
|
349
376
|
dataplaneUrl,
|
|
350
377
|
authConfig,
|
|
351
|
-
format
|
|
378
|
+
format,
|
|
379
|
+
options = {}
|
|
352
380
|
}) {
|
|
353
381
|
const { downloadGovernanceScenarioPacksFromManifest } = require('../lifecycle/scenario-upload');
|
|
354
382
|
await downloadGovernanceScenarioPacksFromManifest(
|
|
@@ -360,25 +388,14 @@ async function finalizeDownloadArtifacts({
|
|
|
360
388
|
if (format === 'json') {
|
|
361
389
|
await runConvertToJsonIfRequested(systemKey);
|
|
362
390
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
if (configSync.merged > 0) {
|
|
372
|
-
const nameList = configSync.names.length ? ` (${configSync.names.join(', ')})` : '';
|
|
373
|
-
logger.log(
|
|
374
|
-
formatSuccessLine(
|
|
375
|
-
`Merged ${configSync.merged} runtime configuration variable(s) into env.template${nameList}.`
|
|
376
|
-
)
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
} catch (syncErr) {
|
|
380
|
-
logger.log(formatWarningLine(`Runtime configuration sync skipped: ${syncErr.message}`));
|
|
381
|
-
}
|
|
391
|
+
await maybeSyncRuntimeConfigurationForDownload(systemKey, manifest, finalPath, dataplaneUrl, authConfig);
|
|
392
|
+
await runRoleAssistantDownloadSidecars({
|
|
393
|
+
systemKey,
|
|
394
|
+
manifest,
|
|
395
|
+
dataplaneUrl,
|
|
396
|
+
authConfig,
|
|
397
|
+
options
|
|
398
|
+
});
|
|
382
399
|
displayDownloadSuccess(systemKey, finalPath, manifest.dataSources.length);
|
|
383
400
|
}
|
|
384
401
|
|
|
@@ -408,10 +425,11 @@ async function downloadExternalSystem(systemKey, options = {}) {
|
|
|
408
425
|
|
|
409
426
|
const manifest = await downloadFullManifest(dataplaneUrl, systemKey, authConfig, options);
|
|
410
427
|
validateAndLogDownloadedData(manifest.application, manifest.dataSources);
|
|
411
|
-
const finalPath =
|
|
428
|
+
const finalPath = resolveDownloadDestinationPath(systemKey, manifest.application);
|
|
412
429
|
await processDownloadedSystem(
|
|
413
430
|
systemKey,
|
|
414
431
|
manifest,
|
|
432
|
+
finalPath,
|
|
415
433
|
await resolveDownloadSplitOptions(finalPath, options)
|
|
416
434
|
);
|
|
417
435
|
await finalizeDownloadArtifacts({
|
|
@@ -420,7 +438,8 @@ async function downloadExternalSystem(systemKey, options = {}) {
|
|
|
420
438
|
finalPath,
|
|
421
439
|
dataplaneUrl,
|
|
422
440
|
authConfig,
|
|
423
|
-
format
|
|
441
|
+
format,
|
|
442
|
+
options
|
|
424
443
|
});
|
|
425
444
|
} catch (error) {
|
|
426
445
|
throw new Error(`Failed to download external system: ${error.message}`);
|
|
@@ -78,7 +78,7 @@ function validateSystemFilesForTest(systemFiles, results) {
|
|
|
78
78
|
* @param {Function} determineDatasourcesToTest - Function to determine datasources to test
|
|
79
79
|
*/
|
|
80
80
|
function validateDatasourceFilesForTest(datasourceFiles, systemFiles, results, options, validateSingleDatasource, determineDatasourcesToTest) {
|
|
81
|
-
const datasourcesToTest = determineDatasourcesToTest(datasourceFiles, options.datasource);
|
|
81
|
+
const datasourcesToTest = determineDatasourcesToTest(datasourceFiles, options.datasource, options);
|
|
82
82
|
const systemKey = systemFiles.length > 0 ? systemFiles[0].data.key : null;
|
|
83
83
|
|
|
84
84
|
for (const datasourceFile of datasourcesToTest) {
|
|
@@ -39,6 +39,20 @@ const { runSystemLevelTest } = require('./test-system-level');
|
|
|
39
39
|
const { shouldUseSystemLevelIntegrationCall } = require('./integration-test-dispatch');
|
|
40
40
|
const { cliOptsSkipPublish } = require('../utils/cli-sync-options');
|
|
41
41
|
|
|
42
|
+
const EMPTY_DATASOURCE_ALLOWED_KINDS = new Set(['enterpriseKnowledge', 'digitalWorker']);
|
|
43
|
+
|
|
44
|
+
function resolvePrimarySystemKind(systemFiles) {
|
|
45
|
+
if (!Array.isArray(systemFiles) || systemFiles.length === 0) {
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
const kind = systemFiles[0] && systemFiles[0].data ? systemFiles[0].data.systemKind : '';
|
|
49
|
+
return typeof kind === 'string' ? kind.trim() : '';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function allowEmptyDatasourcesForKind(systemKind) {
|
|
53
|
+
return EMPTY_DATASOURCE_ALLOWED_KINDS.has(String(systemKind || '').trim());
|
|
54
|
+
}
|
|
55
|
+
|
|
42
56
|
/**
|
|
43
57
|
* Loads and parses application config file
|
|
44
58
|
* @async
|
|
@@ -320,11 +334,25 @@ async function testExternalSystem(appName, options = {}) {
|
|
|
320
334
|
|
|
321
335
|
// Load files
|
|
322
336
|
const { variables: _variables, systemFiles, datasourceFiles } = await loadExternalSystemFiles(appName);
|
|
337
|
+
const systemKind = resolvePrimarySystemKind(systemFiles);
|
|
338
|
+
const allowEmptyDatasources = allowEmptyDatasourcesForKind(systemKind);
|
|
323
339
|
|
|
324
340
|
const results = initializeTestResults();
|
|
325
341
|
|
|
326
342
|
validateSystemFilesForTest(systemFiles, results);
|
|
327
|
-
validateDatasourceFilesForTest(
|
|
343
|
+
validateDatasourceFilesForTest(
|
|
344
|
+
datasourceFiles,
|
|
345
|
+
systemFiles,
|
|
346
|
+
results,
|
|
347
|
+
{ ...options, allowEmptyDatasources, systemKindForEmptyDatasources: systemKind },
|
|
348
|
+
validateSingleDatasource,
|
|
349
|
+
determineDatasourcesToTest
|
|
350
|
+
);
|
|
351
|
+
if (allowEmptyDatasources && results.datasourceResults.length === 0) {
|
|
352
|
+
results.warnings.push(
|
|
353
|
+
`No datasources defined; integration validation is N/A for systemKind "${systemKind}".`
|
|
354
|
+
);
|
|
355
|
+
}
|
|
328
356
|
|
|
329
357
|
results.systemKey = systemFiles.length > 0 ? systemFiles[0].data.key : appName;
|
|
330
358
|
results.appName = appName;
|
|
@@ -383,12 +411,15 @@ async function testExternalSystem(appName, options = {}) {
|
|
|
383
411
|
* @returns {Array} Filtered datasource files
|
|
384
412
|
* @throws {Error} If no datasources found
|
|
385
413
|
*/
|
|
386
|
-
function determineDatasourcesToTest(datasourceFiles, datasourceFilter) {
|
|
414
|
+
function determineDatasourcesToTest(datasourceFiles, datasourceFilter, options = {}) {
|
|
387
415
|
const datasourcesToTest = datasourceFilter
|
|
388
416
|
? datasourceFiles.filter(ds => ds.data.key === datasourceFilter || path.basename(ds.path).includes(datasourceFilter))
|
|
389
417
|
: datasourceFiles;
|
|
390
418
|
|
|
391
419
|
if (datasourcesToTest.length === 0) {
|
|
420
|
+
if (options.allowEmptyDatasources === true) {
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
392
423
|
throw new Error('No datasources found to test');
|
|
393
424
|
}
|
|
394
425
|
|
|
@@ -429,13 +460,18 @@ async function prepareIntegrationTestEnvironment(appName, options) {
|
|
|
429
460
|
}
|
|
430
461
|
|
|
431
462
|
const systemKey = systemFiles[0].data.key;
|
|
463
|
+
const systemKind = resolvePrimarySystemKind(systemFiles);
|
|
464
|
+
const allowEmptyDatasources = allowEmptyDatasourcesForKind(systemKind);
|
|
432
465
|
|
|
433
466
|
// Setup authentication and dataplane URL
|
|
434
467
|
const config = await getConfig();
|
|
435
468
|
const { authConfig, dataplaneUrl } = await setupIntegrationTestAuth(appName, options, config);
|
|
436
469
|
|
|
437
470
|
// Determine datasources to test
|
|
438
|
-
const datasourcesToTest = determineDatasourcesToTest(datasourceFiles, options.datasource
|
|
471
|
+
const datasourcesToTest = determineDatasourcesToTest(datasourceFiles, options.datasource, {
|
|
472
|
+
allowEmptyDatasources,
|
|
473
|
+
systemKindForEmptyDatasources: systemKind
|
|
474
|
+
});
|
|
439
475
|
|
|
440
476
|
// Load custom payload if provided
|
|
441
477
|
const customPayload = await testHelpers.loadCustomPayload(options.payload);
|
|
@@ -276,6 +276,9 @@ function validateBuildFields(build) {
|
|
|
276
276
|
if (typeof build.imageRunBeat === 'string' && build.imageRunBeat.trim()) {
|
|
277
277
|
buildConfig.imageRunBeat = build.imageRunBeat.trim();
|
|
278
278
|
}
|
|
279
|
+
if (typeof build.deployJob === 'string' && build.deployJob.trim()) {
|
|
280
|
+
buildConfig.deployJob = build.deployJob.trim();
|
|
281
|
+
}
|
|
279
282
|
|
|
280
283
|
return Object.keys(buildConfig).length > 0 ? buildConfig : null;
|
|
281
284
|
}
|
package/lib/generator/helpers.js
CHANGED
|
@@ -156,10 +156,10 @@ function validatePortalInput(portalInput, variableName) {
|
|
|
156
156
|
* @throws {Error} If portalInput structure is invalid
|
|
157
157
|
*/
|
|
158
158
|
/**
|
|
159
|
-
* Creates a map of
|
|
159
|
+
* Creates a map of portal overlay metadata from variables config
|
|
160
160
|
* @function createPortalInputMap
|
|
161
161
|
* @param {Object|null} variablesConfig - Configuration from application.yaml
|
|
162
|
-
* @returns {Map} Map of variable names to portalInput
|
|
162
|
+
* @returns {Map} Map of variable names to { portalInput, category? }
|
|
163
163
|
*/
|
|
164
164
|
function createPortalInputMap(variablesConfig) {
|
|
165
165
|
const portalInputMap = new Map();
|
|
@@ -168,7 +168,11 @@ function createPortalInputMap(variablesConfig) {
|
|
|
168
168
|
if (configItem.name && configItem.portalInput) {
|
|
169
169
|
// Validate portalInput before adding to map
|
|
170
170
|
validatePortalInput(configItem.portalInput, configItem.name);
|
|
171
|
-
|
|
171
|
+
const overlay = { portalInput: configItem.portalInput };
|
|
172
|
+
if (typeof configItem.category === 'string' && configItem.category.trim().length > 0) {
|
|
173
|
+
overlay.category = configItem.category.trim();
|
|
174
|
+
}
|
|
175
|
+
portalInputMap.set(configItem.name, overlay);
|
|
172
176
|
}
|
|
173
177
|
}
|
|
174
178
|
}
|
|
@@ -291,9 +295,13 @@ function createConfigItem(key, value, location, required, portalInputMap, appKey
|
|
|
291
295
|
required
|
|
292
296
|
};
|
|
293
297
|
|
|
294
|
-
// Merge portalInput if
|
|
298
|
+
// Merge portalInput / category if present in application config
|
|
295
299
|
if (portalInputMap.has(key)) {
|
|
296
|
-
|
|
300
|
+
const overlay = portalInputMap.get(key);
|
|
301
|
+
configItem.portalInput = overlay.portalInput;
|
|
302
|
+
if (typeof overlay.category === 'string' && overlay.category.length > 0) {
|
|
303
|
+
configItem.category = overlay.category;
|
|
304
|
+
}
|
|
297
305
|
}
|
|
298
306
|
|
|
299
307
|
return configItem;
|
|
@@ -66,15 +66,21 @@ function extractOptionalSections(deployment) {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
|
-
* Portal-only configuration for application.yaml (name +
|
|
69
|
+
* Portal-only configuration for application.yaml (name + optional category + portalInput).
|
|
70
70
|
* @param {Array} configuration - Configuration array from deployment JSON
|
|
71
|
-
* @returns {Array<{ name: string, portalInput: Object }>}
|
|
71
|
+
* @returns {Array<{ name: string, category?: string, portalInput: Object }>}
|
|
72
72
|
*/
|
|
73
73
|
function extractPortalInputConfiguration(configuration) {
|
|
74
74
|
if (!Array.isArray(configuration) || configuration.length === 0) return [];
|
|
75
75
|
return configuration
|
|
76
76
|
.filter(item => item && item.portalInput && typeof item.name === 'string')
|
|
77
|
-
.map(item =>
|
|
77
|
+
.map(item => {
|
|
78
|
+
const entry = { name: item.name, portalInput: item.portalInput };
|
|
79
|
+
if (typeof item.category === 'string' && item.category.trim().length > 0) {
|
|
80
|
+
entry.category = item.category.trim();
|
|
81
|
+
}
|
|
82
|
+
return entry;
|
|
83
|
+
});
|
|
78
84
|
}
|
|
79
85
|
|
|
80
86
|
/**
|