@aifabrix/builder 2.43.0 → 2.44.0
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/rules/anchor-docs.mdc +15 -0
- package/README.md +1 -1
- package/anchor-docs/README.md +10 -0
- package/anchor-docs/_TEMPLATE +24 -0
- package/bin/aifabrix.js +13 -4
- package/integration/hubspot-test/README.md +31 -0
- package/integration/hubspot-test/create-hubspot.js +5 -5
- package/integration/hubspot-test/hubspot-test-datasource-company.json +58 -462
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +61 -555
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +63 -506
- package/integration/hubspot-test/hubspot-test-datasource-users.json +42 -83
- package/integration/hubspot-test/hubspot-test-deploy.json +3 -3
- package/integration/hubspot-test/test-dataplane-down-tests.js +1 -7
- package/integration/hubspot-test/test-dataplane-down.js +3 -3
- package/integration/hubspot-test/test.js +35 -43
- package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
- package/integration/roundtrip-test-local/README.md +144 -0
- package/integration/roundtrip-test-local/application.yaml +13 -0
- package/integration/roundtrip-test-local/env.template +15 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
- package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
- package/integration/roundtrip-test-local2/README.md +144 -0
- package/integration/roundtrip-test-local2/application.yaml +13 -0
- package/integration/roundtrip-test-local2/env.template +15 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
- package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
- package/integration/test/wizard.yaml +8 -0
- package/jest.config.default.js +10 -0
- package/jest.config.integration.fixtures.js +22 -0
- package/jest.config.integration.js +21 -18
- package/jest.config.isolated.js +10 -0
- package/jest.projects.js +288 -0
- package/lib/api/datasources-core.api.js +3 -3
- package/lib/api/dev-mtls-request.js +110 -0
- package/lib/api/dev-server-https.js +145 -0
- package/lib/api/dev.api.js +133 -144
- package/lib/api/index.js +0 -1
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/validation-run.types.js +56 -0
- package/lib/api/validation-run.api.js +99 -0
- package/lib/api/validation-runner.js +99 -0
- package/lib/app/config.js +1 -1
- package/lib/app/deploy-status-display.js +2 -2
- package/lib/app/deploy.js +7 -6
- package/lib/app/display.js +2 -1
- package/lib/app/dockerfile.js +3 -2
- package/lib/app/down.js +2 -1
- package/lib/app/helpers.js +6 -5
- package/lib/app/index.js +27 -8
- package/lib/app/list.js +7 -6
- package/lib/app/push.js +4 -3
- package/lib/app/register.js +16 -7
- package/lib/app/rotate-secret.js +14 -13
- package/lib/app/run-container-start.js +184 -0
- package/lib/app/run-docker-fallback.js +108 -0
- package/lib/app/run-env-compose.js +30 -42
- package/lib/app/run-helpers.js +49 -126
- package/lib/app/run-infra-requirements.js +30 -0
- package/lib/app/run-resolve-image.js +21 -0
- package/lib/app/run.js +74 -21
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +1 -1
- package/lib/build/index.js +13 -10
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +57 -121
- package/lib/cli/setup-app.test-commands.js +179 -0
- package/lib/cli/setup-auth.js +19 -5
- package/lib/cli/setup-credential-deployment.js +22 -8
- package/lib/cli/setup-dev-path-commands.js +124 -0
- package/lib/cli/setup-dev.js +170 -113
- package/lib/cli/setup-environment.js +7 -1
- package/lib/cli/setup-external-system.js +62 -22
- package/lib/cli/setup-infra.js +126 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +106 -8
- package/lib/cli/setup-service-user.js +1 -1
- package/lib/cli/setup-utility.js +36 -20
- package/lib/commands/app-down.js +5 -7
- package/lib/commands/app-install.js +14 -7
- package/lib/commands/app-logs.js +13 -10
- package/lib/commands/app-shell.js +4 -1
- package/lib/commands/app-test.js +25 -19
- package/lib/commands/app.js +22 -10
- package/lib/commands/auth-config.js +6 -6
- package/lib/commands/auth-status.js +4 -3
- package/lib/commands/credential-env.js +4 -3
- package/lib/commands/credential-list.js +5 -4
- package/lib/commands/credential-push.js +4 -3
- package/lib/commands/datasource-unified-test-cli.js +495 -0
- package/lib/commands/datasource-unified-test-cli.options.js +149 -0
- package/lib/commands/datasource-validation-cli.js +129 -0
- package/lib/commands/datasource.js +105 -98
- package/lib/commands/deployment-list.js +6 -5
- package/lib/commands/dev-cli-handlers.js +122 -18
- package/lib/commands/dev-down.js +4 -3
- package/lib/commands/dev-init.js +231 -116
- package/lib/commands/dev-show-display.js +473 -0
- package/lib/commands/login-credentials.js +3 -2
- package/lib/commands/login-device.js +4 -3
- package/lib/commands/login.js +5 -4
- package/lib/commands/logout.js +8 -7
- package/lib/commands/parameters-validate.js +54 -0
- package/lib/commands/repair-datasource.js +314 -68
- package/lib/commands/repair-env-template.js +2 -2
- package/lib/commands/repair.js +21 -3
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +21 -12
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +4 -4
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +26 -25
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +3 -2
- package/lib/commands/up-dataplane.js +29 -16
- package/lib/commands/up-miso.js +19 -29
- package/lib/commands/upload.js +138 -39
- package/lib/commands/wizard-core-helpers.js +1 -1
- package/lib/commands/wizard-dataplane.js +4 -3
- package/lib/commands/wizard-helpers.js +3 -3
- package/lib/commands/wizard.js +2 -2
- package/lib/core/admin-secrets.js +14 -5
- package/lib/core/audit-logger.js +12 -4
- package/lib/core/config-attach-extensions.js +46 -0
- package/lib/core/config-runtime-paths.js +29 -0
- package/lib/core/config.js +55 -56
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +1 -1
- package/lib/core/secrets-ensure-infra.js +77 -0
- package/lib/core/secrets-ensure.js +120 -64
- package/lib/core/secrets-env-write.js +35 -7
- package/lib/core/secrets-infra-placeholder-sync.js +61 -0
- package/lib/core/secrets.js +200 -37
- package/lib/core/templates-env.js +4 -3
- package/lib/datasource/abac-validator.js +1 -10
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +9 -6
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +84 -53
- package/lib/datasource/resolve-app.js +4 -4
- package/lib/datasource/test-e2e.js +95 -146
- package/lib/datasource/test-integration.js +114 -122
- package/lib/datasource/unified-validation-run-body.js +65 -0
- package/lib/datasource/unified-validation-run-post.js +23 -0
- package/lib/datasource/unified-validation-run-resolve.js +43 -0
- package/lib/datasource/unified-validation-run.js +92 -0
- package/lib/datasource/validate.js +157 -13
- package/lib/deployment/deployer.js +4 -3
- package/lib/deployment/environment.js +7 -6
- package/lib/deployment/push.js +17 -8
- package/lib/external-system/delete.js +4 -3
- package/lib/external-system/deploy.js +131 -53
- package/lib/external-system/download-helpers.js +1 -1
- package/lib/external-system/download.js +7 -6
- package/lib/external-system/generator.js +92 -6
- package/lib/external-system/integration-test-dispatch.js +26 -0
- package/lib/external-system/test-execution.js +5 -1
- package/lib/external-system/test-helpers.js +0 -4
- package/lib/external-system/test-system-level-helpers.js +110 -0
- package/lib/external-system/test-system-level.js +83 -44
- package/lib/external-system/test.js +59 -8
- package/lib/generator/builders.js +23 -11
- package/lib/generator/deploy-manifest-azure-kv.js +81 -0
- package/lib/generator/external.js +16 -4
- package/lib/generator/helpers.js +58 -3
- package/lib/generator/index.js +4 -0
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +1 -1
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +8 -8
- package/lib/infrastructure/compose.js +60 -6
- package/lib/infrastructure/helpers.js +201 -29
- package/lib/infrastructure/index.js +28 -17
- package/lib/infrastructure/services.js +21 -15
- package/lib/internal/fs-real-sync.js +104 -0
- package/lib/internal/node-fs.js +98 -0
- package/lib/parameters/database-secret-values.js +173 -0
- package/lib/parameters/infra-kv-discovery.js +121 -0
- package/lib/parameters/infra-parameter-catalog.js +458 -0
- package/lib/parameters/infra-parameter-validate.js +64 -0
- package/lib/schema/application-schema.json +37 -17
- package/lib/schema/datasource-test-run.schema.json +493 -0
- package/lib/schema/deployment-rules.yaml +102 -63
- package/lib/schema/external-datasource.schema.json +1200 -442
- package/lib/schema/external-system.schema.json +181 -5
- package/lib/schema/flag-map-validation-run.json +31 -0
- package/lib/schema/infra-parameter.schema.json +106 -0
- package/lib/schema/infra.parameter.yaml +421 -0
- package/lib/schema/type/credential-auth-templates.json +40 -0
- package/lib/schema/type/document-storage.json +213 -0
- package/lib/schema/type/message-service.json +123 -0
- package/lib/schema/type/vector-store.json +88 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
- package/lib/utils/api-error-handler.js +2 -2
- package/lib/utils/api.js +49 -14
- package/lib/utils/app-register-api.js +3 -2
- package/lib/utils/app-register-auth.js +1 -1
- package/lib/utils/app-register-config.js +4 -4
- package/lib/utils/app-register-display.js +3 -2
- package/lib/utils/app-register-validator.js +3 -2
- package/lib/utils/app-run-containers.js +26 -22
- package/lib/utils/app-scoped-config.js +31 -0
- package/lib/utils/app-service-env-from-builder.js +164 -0
- package/lib/utils/build-copy.js +1 -1
- package/lib/utils/build-helpers.js +20 -20
- package/lib/utils/build-resolve-image.js +165 -0
- package/lib/utils/cli-layout-chalk.js +8 -0
- package/lib/utils/cli-test-layout-chalk.js +267 -0
- package/lib/utils/cli-utils.js +88 -11
- package/lib/utils/compose-db-passwords.js +138 -0
- package/lib/utils/compose-generate-docker-compose.js +216 -0
- package/lib/utils/compose-generator.js +197 -291
- package/lib/utils/compose-miso-env.js +18 -0
- package/lib/utils/compose-traefik-ingress-base.js +158 -0
- package/lib/utils/config-paths.js +166 -7
- package/lib/utils/config-scoped-resources-preference.js +41 -0
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/dataplane-pipeline-warning.js +4 -3
- package/lib/utils/datasource-test-run-capability-scope.js +43 -0
- package/lib/utils/datasource-test-run-debug-display.js +137 -0
- package/lib/utils/datasource-test-run-debug-slice.js +93 -0
- package/lib/utils/datasource-test-run-display.js +442 -0
- package/lib/utils/datasource-test-run-exit.js +58 -0
- package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
- package/lib/utils/datasource-test-run-report-version.js +51 -0
- package/lib/utils/datasource-test-run-schema-sync.js +59 -0
- package/lib/utils/datasource-test-run-tty-log.js +81 -0
- package/lib/utils/datasource-validation-watch.js +266 -0
- package/lib/utils/declarative-url-ports.js +47 -0
- package/lib/utils/derive-env-key-from-client-id.js +41 -0
- package/lib/utils/dev-ca-install.js +185 -23
- package/lib/utils/dev-cert-helper.js +266 -17
- package/lib/utils/dev-hosts-helper.js +307 -0
- package/lib/utils/dev-init-cert-hints.js +37 -0
- package/lib/utils/dev-init-health-messages.js +52 -0
- package/lib/utils/dev-init-resolve.js +86 -0
- package/lib/utils/dev-init-ssh-merge.js +65 -0
- package/lib/utils/dev-ssh-config-helper.js +196 -0
- package/lib/utils/dev-user-groups.js +93 -0
- package/lib/utils/docker-build.js +42 -17
- package/lib/utils/docker-exec.js +28 -0
- package/lib/utils/docker-manifest-public-port.js +116 -0
- package/lib/utils/docker-not-running-hint.js +52 -0
- package/lib/utils/docker.js +98 -11
- package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
- package/lib/utils/env-config-loader.js +10 -91
- package/lib/utils/env-copy.js +19 -10
- package/lib/utils/env-map.js +35 -8
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +92 -13
- package/lib/utils/error-formatters/http-status-errors.js +6 -5
- package/lib/utils/error-formatters/network-errors.js +2 -1
- package/lib/utils/error-formatters/permission-errors.js +2 -1
- package/lib/utils/error-formatters/validation-errors.js +2 -1
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +234 -136
- package/lib/utils/external-system-local-test-tty.js +389 -0
- package/lib/utils/external-system-readiness-core.js +377 -0
- package/lib/utils/external-system-readiness-deploy-display.js +270 -0
- package/lib/utils/external-system-readiness-display-internals.js +150 -0
- package/lib/utils/external-system-readiness-display.js +186 -0
- package/lib/utils/external-system-test-helpers.js +24 -6
- package/lib/utils/external-system-validators.js +30 -12
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +11 -8
- package/lib/utils/image-version.js +4 -8
- package/lib/utils/infra-containers.js +4 -7
- package/lib/utils/infra-env-defaults.js +162 -0
- package/lib/utils/infra-status-display.js +167 -0
- package/lib/utils/infra-status.js +16 -8
- package/lib/utils/local-secrets.js +3 -4
- package/lib/utils/paths.js +134 -47
- package/lib/utils/port-resolver.js +10 -23
- package/lib/utils/redis-env-scope.js +62 -0
- package/lib/utils/register-aifabrix-shell-env.js +204 -0
- package/lib/utils/remote-builder-validation.js +99 -0
- package/lib/utils/remote-dev-auth.js +117 -21
- package/lib/utils/remote-docker-env.js +67 -15
- package/lib/utils/remote-secrets-loader.js +13 -4
- package/lib/utils/resolve-docker-image-ref.js +124 -0
- package/lib/utils/schema-loader.js +22 -9
- package/lib/utils/secrets-bash-kv.js +25 -0
- package/lib/utils/secrets-generator.js +169 -49
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +32 -38
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/ssh-key-helper.js +4 -2
- package/lib/utils/template-helpers.js +2 -2
- package/lib/utils/test-log-writer.js +3 -3
- package/lib/utils/token-manager.js +1 -2
- package/lib/utils/url-declarative-public-base.js +188 -0
- package/lib/utils/url-declarative-resolve-build.js +493 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
- package/lib/utils/url-declarative-resolve.js +220 -0
- package/lib/utils/url-declarative-token-parse.js +74 -0
- package/lib/utils/url-declarative-url-flags.js +50 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
- package/lib/utils/url-public-path-prefix.js +34 -0
- package/lib/utils/urls-local-registry.js +220 -0
- package/lib/utils/validation-report-tty-kit.js +77 -0
- package/lib/utils/validation-run-poll.js +89 -0
- package/lib/utils/validation-run-post-retry.js +73 -0
- package/lib/utils/validation-run-request.js +98 -0
- package/lib/utils/variable-transformer.js +21 -4
- package/lib/utils/yaml-preserve.js +33 -14
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +1 -1
- package/lib/validation/external-manifest-validator.js +27 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate.js +4 -13
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +22 -65
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +7 -3
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +304 -55
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +75 -17
- package/templates/applications/dataplane/rbac.yaml +8 -0
- package/templates/applications/keycloak/application.yaml +9 -1
- package/templates/applications/keycloak/env.template +15 -6
- package/templates/applications/miso-controller/application.yaml +10 -2
- package/templates/applications/miso-controller/env.template +42 -12
- package/templates/applications/miso-controller/rbac.yaml +5 -0
- package/templates/external-system/README.md.hbs +20 -7
- package/templates/external-system/deploy.js.hbs +5 -5
- package/templates/external-system/external-datasource.yaml.hbs +197 -118
- package/templates/infra/compose.yaml.hbs +20 -4
- package/templates/python/docker-compose.hbs +16 -0
- package/templates/typescript/docker-compose.hbs +16 -0
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -60
|
@@ -1,17 +1,81 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* System-level
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
* @fileoverview System-level unified validation runner for external systems (aggregate layer).
|
|
3
|
+
*
|
|
4
|
+
* Preferred behavior is a single externalSystem-scoped POST that returns an aggregate body.
|
|
5
|
+
* Until that aggregate is guaranteed everywhere, this module performs the plan §2.2 “interim”
|
|
6
|
+
* strategy: fan-out datasource-scoped runs and merge into a single system result.
|
|
6
7
|
*/
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
8
10
|
|
|
9
11
|
const path = require('path');
|
|
10
12
|
const chalk = require('chalk');
|
|
11
13
|
const logger = require('../utils/logger');
|
|
12
|
-
const { testSystemViaPipeline } = require('../api/pipeline.api');
|
|
13
14
|
const { writeTestLog } = require('../utils/test-log-writer');
|
|
14
15
|
const { getIntegrationPath } = require('../utils/paths');
|
|
16
|
+
const { runUnifiedDatasourceValidation } = require('../datasource/unified-validation-run');
|
|
17
|
+
|
|
18
|
+
async function runOneDatasourceKey(appName, datasourceKey, opts) {
|
|
19
|
+
const { environment, runType, debug, timeoutMs } = opts;
|
|
20
|
+
try {
|
|
21
|
+
const uni = await runUnifiedDatasourceValidation(datasourceKey, {
|
|
22
|
+
app: appName,
|
|
23
|
+
environment,
|
|
24
|
+
runType,
|
|
25
|
+
debug,
|
|
26
|
+
verbose: false,
|
|
27
|
+
timeout: timeoutMs,
|
|
28
|
+
async: true,
|
|
29
|
+
noAsync: false
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
if (uni.apiError) {
|
|
33
|
+
return {
|
|
34
|
+
key: datasourceKey,
|
|
35
|
+
success: false,
|
|
36
|
+
skipped: false,
|
|
37
|
+
error: uni.apiError.formattedError || uni.apiError.error || 'Request failed',
|
|
38
|
+
datasourceTestRun: null
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
if (uni.pollTimedOut) {
|
|
42
|
+
return {
|
|
43
|
+
key: datasourceKey,
|
|
44
|
+
success: false,
|
|
45
|
+
skipped: false,
|
|
46
|
+
error: 'Report incomplete: timeout',
|
|
47
|
+
datasourceTestRun: uni.envelope
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (uni.incompleteNoAsync) {
|
|
51
|
+
return {
|
|
52
|
+
key: datasourceKey,
|
|
53
|
+
success: false,
|
|
54
|
+
skipped: false,
|
|
55
|
+
error: 'Report incomplete (async required)',
|
|
56
|
+
datasourceTestRun: uni.envelope
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
const env = uni.envelope;
|
|
60
|
+
const ok = env && typeof env.status === 'string' ? env.status !== 'fail' : false;
|
|
61
|
+
return { key: datasourceKey, success: ok, skipped: false, datasourceTestRun: env };
|
|
62
|
+
} catch (err) {
|
|
63
|
+
return { key: datasourceKey, success: false, skipped: false, error: err.message };
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async function maybeWriteSystemRunLog(appName, systemKey, datasourceKeys, runType, debug, payload) {
|
|
68
|
+
if (!debug) return;
|
|
69
|
+
const appPath = getIntegrationPath(appName);
|
|
70
|
+
const integrationDir = path.dirname(appPath);
|
|
71
|
+
const logPath = await writeTestLog(
|
|
72
|
+
appName,
|
|
73
|
+
{ request: { systemKey, datasourceKeys, runType, includeDebug: true }, response: payload },
|
|
74
|
+
runType === 'e2e' ? 'test-e2e' : 'test-integration',
|
|
75
|
+
integrationDir
|
|
76
|
+
);
|
|
77
|
+
logger.log(chalk.gray(` Debug log: ${logPath}`));
|
|
78
|
+
}
|
|
15
79
|
|
|
16
80
|
/**
|
|
17
81
|
* Run system-level pipeline test and map response to datasource results
|
|
@@ -25,49 +89,24 @@ const { getIntegrationPath } = require('../utils/paths');
|
|
|
25
89
|
* @param {number} [params.timeout] - Request timeout
|
|
26
90
|
* @returns {Promise<{success: boolean, datasourceResults: Object[]}>}
|
|
27
91
|
*/
|
|
28
|
-
async function runSystemLevelTest({ appName, systemKey,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const data = response.data || response;
|
|
32
|
-
|
|
33
|
-
if (debug) {
|
|
34
|
-
const appPath = getIntegrationPath(appName);
|
|
35
|
-
const integrationDir = path.dirname(appPath);
|
|
36
|
-
const logPath = await writeTestLog(appName, { request: { systemKey, includeDebug: true }, response: data }, 'test-integration', integrationDir);
|
|
37
|
-
logger.log(chalk.gray(` Debug log: ${logPath}`));
|
|
92
|
+
async function runSystemLevelTest({ appName, systemKey, datasourceKeys, environment, runType, debug, timeout }) {
|
|
93
|
+
if (!Array.isArray(datasourceKeys) || datasourceKeys.length === 0) {
|
|
94
|
+
return { success: true, datasourceResults: [] };
|
|
38
95
|
}
|
|
96
|
+
const timeoutMs = Number.isFinite(timeout) ? timeout : 30000;
|
|
39
97
|
|
|
40
|
-
const
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const dsResult = {
|
|
47
|
-
key: dsKey,
|
|
48
|
-
success: r.success !== false,
|
|
49
|
-
skipped: !!r.skipped,
|
|
50
|
-
reason: r.reason,
|
|
51
|
-
validationResults: r.validationResults || {},
|
|
52
|
-
fieldMappingResults: r.fieldMappingResults || {},
|
|
53
|
-
endpointTestResults: r.endpointTestResults || {}
|
|
54
|
-
};
|
|
55
|
-
if (r.error) dsResult.error = r.error;
|
|
56
|
-
if (!dsResult.success && !dsResult.skipped) success = false;
|
|
57
|
-
datasourceResults.push(dsResult);
|
|
98
|
+
const results = [];
|
|
99
|
+
for (const key of datasourceKeys) {
|
|
100
|
+
// Keep sequential to avoid spiking external APIs / dataplane concurrency in CLI runs.
|
|
101
|
+
// (Plan allows parallel later, but UX needs stable logs first.)
|
|
102
|
+
// eslint-disable-next-line no-await-in-loop -- intentional sequential execution
|
|
103
|
+
results.push(await runOneDatasourceKey(appName, key, { environment, runType, debug, timeoutMs }));
|
|
58
104
|
}
|
|
59
105
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
datasourceResults.push({
|
|
63
|
-
key: 'system',
|
|
64
|
-
success: false,
|
|
65
|
-
skipped: false,
|
|
66
|
-
error: data.error || data.formattedError || 'System test failed'
|
|
67
|
-
});
|
|
68
|
-
}
|
|
106
|
+
const success = results.every(r => r.success);
|
|
107
|
+
await maybeWriteSystemRunLog(appName, systemKey, datasourceKeys, runType, debug, { success, results });
|
|
69
108
|
|
|
70
|
-
return { success, datasourceResults };
|
|
109
|
+
return { success, datasourceResults: results };
|
|
71
110
|
}
|
|
72
111
|
|
|
73
112
|
module.exports = { runSystemLevelTest };
|
|
@@ -11,12 +11,14 @@ const fs = require('fs').promises;
|
|
|
11
11
|
const fsSync = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const chalk = require('chalk');
|
|
14
|
+
const { infoLine, formatSuccessLine } = require('../utils/cli-test-layout-chalk');
|
|
14
15
|
const testHelpers = require('../utils/external-system-test-helpers');
|
|
15
16
|
const { retryApiCall } = require('../utils/external-system-test-helpers');
|
|
16
17
|
const { getConfig } = require('../core/config');
|
|
17
18
|
const { detectAppType } = require('../utils/paths');
|
|
18
19
|
const { setupIntegrationTestAuth } = require('./test-auth');
|
|
19
20
|
const logger = require('../utils/logger');
|
|
21
|
+
const { writeTestLog } = require('../utils/test-log-writer');
|
|
20
22
|
const {
|
|
21
23
|
validateFieldMappings,
|
|
22
24
|
validateMetadataSchema,
|
|
@@ -35,6 +37,7 @@ const {
|
|
|
35
37
|
testSingleDatasourceIntegration
|
|
36
38
|
} = require('./test-execution');
|
|
37
39
|
const { runSystemLevelTest } = require('./test-system-level');
|
|
40
|
+
const { shouldUseSystemLevelIntegrationCall } = require('./integration-test-dispatch');
|
|
38
41
|
|
|
39
42
|
/**
|
|
40
43
|
* Loads and parses application config file
|
|
@@ -51,7 +54,7 @@ async function loadVariablesYamlFile(variablesPath) {
|
|
|
51
54
|
if (!variables.externalIntegration) {
|
|
52
55
|
throw new Error(
|
|
53
56
|
'externalIntegration block not found in application config. ' +
|
|
54
|
-
'test-integration is for external systems only (integration/<
|
|
57
|
+
'test-integration is for external systems only (integration/<systemKey> with externalIntegration in application.yaml). ' +
|
|
55
58
|
'For builder apps use: aifabrix test <app> or aifabrix test-e2e <app>'
|
|
56
59
|
);
|
|
57
60
|
}
|
|
@@ -251,11 +254,15 @@ function validateSingleDatasource(datasourceFile, systemKey, externalDataSourceS
|
|
|
251
254
|
errors: [],
|
|
252
255
|
warnings: [],
|
|
253
256
|
fieldMappingResults: null,
|
|
254
|
-
metadataSchemaResults: null
|
|
257
|
+
metadataSchemaResults: null,
|
|
258
|
+
structuralValid: true,
|
|
259
|
+
payloadTestsRan: false,
|
|
260
|
+
payloadTestsOk: true
|
|
255
261
|
};
|
|
256
262
|
|
|
257
263
|
// Validate against schema
|
|
258
264
|
const schemaValidation = validateDatasourceSchema(datasource, systemKey, externalDataSourceSchema);
|
|
265
|
+
datasourceResult.structuralValid = schemaValidation.valid;
|
|
259
266
|
if (!schemaValidation.valid) {
|
|
260
267
|
datasourceResult.valid = false;
|
|
261
268
|
datasourceResult.errors.push(...schemaValidation.errors);
|
|
@@ -263,16 +270,22 @@ function validateSingleDatasource(datasourceFile, systemKey, externalDataSourceS
|
|
|
263
270
|
|
|
264
271
|
// Test with testPayload if available
|
|
265
272
|
if (datasource.testPayload && datasource.testPayload.payloadTemplate) {
|
|
266
|
-
|
|
273
|
+
if (verbose) {
|
|
274
|
+
logger.log(`${chalk.cyan(' ℹ')} ${chalk.gray('Testing datasource:')} ${chalk.white.bold(datasource.key)}`);
|
|
275
|
+
}
|
|
276
|
+
datasourceResult.payloadTestsRan = true;
|
|
267
277
|
const payloadTestResults = testDatasourceWithPayload(datasource, verbose);
|
|
268
278
|
datasourceResult.fieldMappingResults = payloadTestResults.fieldMappingResults;
|
|
269
279
|
datasourceResult.metadataSchemaResults = payloadTestResults.metadataSchemaResults;
|
|
280
|
+
datasourceResult.payloadTestsOk = payloadTestResults.valid;
|
|
270
281
|
if (!payloadTestResults.valid) {
|
|
271
282
|
datasourceResult.valid = false;
|
|
272
283
|
datasourceResult.errors.push(...payloadTestResults.errors);
|
|
273
284
|
}
|
|
274
285
|
datasourceResult.warnings.push(...payloadTestResults.warnings);
|
|
275
286
|
} else {
|
|
287
|
+
datasourceResult.payloadTestsRan = false;
|
|
288
|
+
datasourceResult.payloadTestsOk = true;
|
|
276
289
|
datasourceResult.warnings.push('No testPayload.payloadTemplate found - skipping field mapping and metadata schema tests');
|
|
277
290
|
}
|
|
278
291
|
|
|
@@ -302,7 +315,8 @@ async function testExternalSystem(appName, options = {}) {
|
|
|
302
315
|
}
|
|
303
316
|
|
|
304
317
|
try {
|
|
305
|
-
logger.log(
|
|
318
|
+
logger.log('');
|
|
319
|
+
logger.log(infoLine(`Local test: ${appName}`));
|
|
306
320
|
|
|
307
321
|
// Load files
|
|
308
322
|
const { variables: _variables, systemFiles, datasourceFiles } = await loadExternalSystemFiles(appName);
|
|
@@ -312,6 +326,23 @@ async function testExternalSystem(appName, options = {}) {
|
|
|
312
326
|
validateSystemFilesForTest(systemFiles, results);
|
|
313
327
|
validateDatasourceFilesForTest(datasourceFiles, systemFiles, results, options, validateSingleDatasource, determineDatasourcesToTest);
|
|
314
328
|
|
|
329
|
+
results.systemKey = systemFiles.length > 0 ? systemFiles[0].data.key : appName;
|
|
330
|
+
results.appName = appName;
|
|
331
|
+
|
|
332
|
+
if (options.debug) {
|
|
333
|
+
try {
|
|
334
|
+
const logPath = await writeTestLog(
|
|
335
|
+
appName,
|
|
336
|
+
{
|
|
337
|
+
request: { appName, datasource: options.datasource || null, verbose: !!options.verbose },
|
|
338
|
+
response: results
|
|
339
|
+
},
|
|
340
|
+
'test'
|
|
341
|
+
);
|
|
342
|
+
logger.log(chalk.gray(` Debug log: ${logPath}`));
|
|
343
|
+
} catch (_) { /* ignore */ }
|
|
344
|
+
}
|
|
345
|
+
|
|
315
346
|
return results;
|
|
316
347
|
} catch (error) {
|
|
317
348
|
// Preserve original error message for better test compatibility
|
|
@@ -371,11 +402,13 @@ function determineDatasourcesToTest(datasourceFiles, datasourceFilter) {
|
|
|
371
402
|
* @param {string} appName - Application name
|
|
372
403
|
* @param {Object} options - Test options
|
|
373
404
|
* @param {string} [options.datasource] - Test specific datasource only
|
|
405
|
+
* @param {boolean} [options.perDatasource] - When multiple datasources, run POST /validation/run per datasource instead of one externalSystem call
|
|
374
406
|
* @param {string} [options.payload] - Path to custom test payload file
|
|
375
407
|
* @param {string} [options.environment] - Environment (dev, tst, pro)
|
|
376
408
|
* @param {string} [options.controller] - Controller URL
|
|
377
409
|
* @param {boolean} [options.verbose] - Show detailed test output
|
|
378
410
|
* @param {number} [options.timeout] - Request timeout in milliseconds
|
|
411
|
+
* @param {boolean} [options.sync] - When true, run full `aifabrix upload`-equivalent publish before tests
|
|
379
412
|
* @returns {Promise<Object>} Integration test results
|
|
380
413
|
* @throws {Error} If testing fails
|
|
381
414
|
*/
|
|
@@ -415,23 +448,41 @@ async function testExternalSystemIntegration(appName, options = {}) {
|
|
|
415
448
|
}
|
|
416
449
|
|
|
417
450
|
try {
|
|
418
|
-
logger.log(
|
|
451
|
+
logger.log('');
|
|
452
|
+
logger.log(infoLine(`🔗 Running integration tests for: ${appName}`));
|
|
419
453
|
|
|
420
454
|
const { systemKey, authConfig, dataplaneUrl, datasourcesToTest, customPayload } = await prepareIntegrationTestEnvironment(appName, options);
|
|
421
455
|
|
|
456
|
+
if (options.sync === true) {
|
|
457
|
+
logger.log(chalk.cyan('Syncing local config to dataplane…'));
|
|
458
|
+
const { uploadExternalSystem } = require('../commands/upload');
|
|
459
|
+
await uploadExternalSystem(systemKey, {
|
|
460
|
+
verbose: !!options.verbose,
|
|
461
|
+
minimal: true
|
|
462
|
+
});
|
|
463
|
+
logger.log(formatSuccessLine('Sync complete'));
|
|
464
|
+
}
|
|
465
|
+
|
|
422
466
|
const results = {
|
|
423
467
|
success: true,
|
|
424
468
|
systemKey,
|
|
425
469
|
datasourceResults: []
|
|
426
470
|
};
|
|
427
471
|
|
|
428
|
-
const useSystemLevel = !options.datasource && datasourcesToTest.length > 0 && !customPayload;
|
|
429
472
|
const timeout = parseInt(options.timeout, 10) || 30000;
|
|
473
|
+
const useSystemLevel = shouldUseSystemLevelIntegrationCall(options, datasourcesToTest, customPayload);
|
|
430
474
|
|
|
431
|
-
if (useSystemLevel
|
|
475
|
+
if (useSystemLevel) {
|
|
432
476
|
try {
|
|
477
|
+
const datasourceKeys = datasourcesToTest.map(ds => ds?.data?.key).filter(Boolean);
|
|
433
478
|
const systemResult = await runSystemLevelTest({
|
|
434
|
-
appName,
|
|
479
|
+
appName,
|
|
480
|
+
systemKey,
|
|
481
|
+
datasourceKeys,
|
|
482
|
+
environment: options.environment,
|
|
483
|
+
runType: 'integration',
|
|
484
|
+
debug: options.debug,
|
|
485
|
+
timeout
|
|
435
486
|
});
|
|
436
487
|
results.success = systemResult.success;
|
|
437
488
|
results.datasourceResults = systemResult.datasourceResults;
|
|
@@ -59,24 +59,33 @@ function buildImageReference(variables) {
|
|
|
59
59
|
return `${imageName}:${tag}`;
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Deployment JSON health paths mirror `application.yaml`: probes target the workload (container),
|
|
64
|
+
* not the public Front Door / Traefik URL. Vdir is carried by `frontDoorRouting` and url://
|
|
65
|
+
* expansion elsewhere — do not prepend pattern base here (see dataplane `/health`, Keycloak `/health/ready`).
|
|
66
|
+
*
|
|
67
|
+
* @param {Object} variables - Transformed application variables
|
|
68
|
+
* @returns {Object} healthCheck block for deploy manifest
|
|
69
|
+
*/
|
|
62
70
|
function buildHealthCheck(variables) {
|
|
71
|
+
const hc = variables.healthCheck || {};
|
|
72
|
+
const path = typeof hc.path === 'string' && hc.path.startsWith('/') ? hc.path : '/health';
|
|
63
73
|
const healthCheck = {
|
|
64
|
-
path
|
|
65
|
-
interval:
|
|
74
|
+
path,
|
|
75
|
+
interval: hc.interval || 30
|
|
66
76
|
};
|
|
67
77
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
healthCheck.probePath = variables.healthCheck.probePath;
|
|
78
|
+
if (hc.probePath) {
|
|
79
|
+
healthCheck.probePath = hc.probePath;
|
|
71
80
|
}
|
|
72
|
-
if (
|
|
73
|
-
healthCheck.probeRequestType =
|
|
81
|
+
if (hc.probeRequestType) {
|
|
82
|
+
healthCheck.probeRequestType = hc.probeRequestType;
|
|
74
83
|
}
|
|
75
|
-
if (
|
|
76
|
-
healthCheck.probeProtocol =
|
|
84
|
+
if (hc.probeProtocol) {
|
|
85
|
+
healthCheck.probeProtocol = hc.probeProtocol;
|
|
77
86
|
}
|
|
78
|
-
if (
|
|
79
|
-
healthCheck.probeIntervalInSeconds =
|
|
87
|
+
if (hc.probeIntervalInSeconds) {
|
|
88
|
+
healthCheck.probeIntervalInSeconds = hc.probeIntervalInSeconds;
|
|
80
89
|
}
|
|
81
90
|
|
|
82
91
|
return healthCheck;
|
|
@@ -341,6 +350,9 @@ function addSimpleOptionalFields(deployment, variables) {
|
|
|
341
350
|
if (variables.frontDoorRouting) {
|
|
342
351
|
deployment.frontDoorRouting = variables.frontDoorRouting;
|
|
343
352
|
}
|
|
353
|
+
if (variables.environmentScopedResources === true) {
|
|
354
|
+
deployment.environmentScopedResources = true;
|
|
355
|
+
}
|
|
344
356
|
}
|
|
345
357
|
|
|
346
358
|
function buildOptionalFields(deployment, variables, rbac) {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Map url:// placeholders and Traefik host templates to Azure Key Vault secret names
|
|
3
|
+
* in deployment manifests (Miso Controller / App Service @Microsoft.KeyVault references).
|
|
4
|
+
*
|
|
5
|
+
* @fileoverview Plan 122 — deploy JSON must not ship url:// or ${DEV_USERNAME} for Azure
|
|
6
|
+
* @author AI Fabrix Team
|
|
7
|
+
* @version 1.0.0
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
'use strict';
|
|
11
|
+
|
|
12
|
+
const { parseUrlToken } = require('../utils/url-declarative-token-parse');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {string|undefined} surface
|
|
16
|
+
* @param {string} subject
|
|
17
|
+
* @param {boolean} isPublic
|
|
18
|
+
* @returns {string|undefined} Secret name when surface is vdir/host
|
|
19
|
+
*/
|
|
20
|
+
function secretNameFromSurface(surface, subject, isPublic) {
|
|
21
|
+
if (surface === 'vdir') {
|
|
22
|
+
return `${subject}-vdir-${isPublic ? 'public' : 'internal'}`;
|
|
23
|
+
}
|
|
24
|
+
if (surface === 'host') {
|
|
25
|
+
return `${subject}-host-${isPublic ? 'public' : 'internal'}`;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Key Vault secret name for a url:// token (path after url://).
|
|
32
|
+
* Aligns with infrastructure/bicep (e.g. keycloak-web-server-url, miso-controller-web-server-url).
|
|
33
|
+
*
|
|
34
|
+
* @param {string} ownerAppKey - application.yaml app.key for the manifest being generated
|
|
35
|
+
* @param {string} token - Token after url:// (no scheme)
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
function urlTokenToKeyVaultSecretName(ownerAppKey, token) {
|
|
39
|
+
const selfKey = String(ownerAppKey || '').trim() || 'app';
|
|
40
|
+
const t = String(token || '').trim();
|
|
41
|
+
if (!t) {
|
|
42
|
+
throw new Error('Empty url:// reference (expected a token after url://)');
|
|
43
|
+
}
|
|
44
|
+
const { targetKey, kind, surface } = parseUrlToken(t);
|
|
45
|
+
const isPublic = kind === 'public';
|
|
46
|
+
const subject = targetKey ? String(targetKey).trim() : selfKey;
|
|
47
|
+
|
|
48
|
+
const fromSurface = secretNameFromSurface(surface, subject, isPublic);
|
|
49
|
+
if (fromSurface !== undefined) {
|
|
50
|
+
return fromSurface;
|
|
51
|
+
}
|
|
52
|
+
if (targetKey === 'keycloak' || (!targetKey && selfKey === 'keycloak')) {
|
|
53
|
+
return isPublic ? 'keycloak-web-server-url' : 'keycloak-internal-server-url';
|
|
54
|
+
}
|
|
55
|
+
if (!targetKey) {
|
|
56
|
+
return isPublic ? `${selfKey}-web-server-url` : `${selfKey}-internal-server-url`;
|
|
57
|
+
}
|
|
58
|
+
return isPublic ? `${subject}-web-server-url` : `${subject}-internal-server-url`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Replace Traefik-style host templates with a Key Vault secret name consumed by Azure provisioning.
|
|
63
|
+
*
|
|
64
|
+
* @param {Object} deployment - Deployment manifest (mutated in place)
|
|
65
|
+
* @returns {void}
|
|
66
|
+
*/
|
|
67
|
+
function rewriteFrontDoorHostForAzureDeploy(deployment) {
|
|
68
|
+
if (!deployment || !deployment.frontDoorRouting || typeof deployment.frontDoorRouting.host !== 'string') {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const h = deployment.frontDoorRouting.host;
|
|
72
|
+
if (/\$\{(DEV_USERNAME|REMOTE_HOST)\}/.test(h)) {
|
|
73
|
+
const key = deployment.key || 'app';
|
|
74
|
+
deployment.frontDoorRouting.host = `${key}-frontdoor-routing-host`;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
module.exports = {
|
|
79
|
+
urlTokenToKeyVaultSecretName,
|
|
80
|
+
rewriteFrontDoorHostForAzureDeploy
|
|
81
|
+
};
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const Ajv = require('ajv');
|
|
14
|
+
const addFormats = require('ajv-formats');
|
|
14
15
|
const { detectAppType, getDeployJsonPath } = require('../utils/paths');
|
|
15
16
|
const { resolveApplicationConfigPath, resolveRbacPath } = require('../utils/app-config-resolver');
|
|
16
17
|
const { loadConfigFile } = require('../utils/config-format');
|
|
@@ -255,8 +256,10 @@ function formatValidationErrors(errors) {
|
|
|
255
256
|
* @throws {Error} If validation fails
|
|
256
257
|
*/
|
|
257
258
|
function validateSystemSchema(systemJson, externalSystemSchema, ajv) {
|
|
258
|
-
|
|
259
|
-
|
|
259
|
+
if (!externalSystemSchema.$id || typeof externalSystemSchema.$id !== 'string') {
|
|
260
|
+
throw new Error('External system schema is missing required $id');
|
|
261
|
+
}
|
|
262
|
+
ajv.addSchema(externalSystemSchema, externalSystemSchema.$id);
|
|
260
263
|
const validateSystem = ajv.compile(externalSystemSchema);
|
|
261
264
|
const systemValid = validateSystem(systemJson);
|
|
262
265
|
|
|
@@ -277,8 +280,16 @@ function validateSystemSchema(systemJson, externalSystemSchema, ajv) {
|
|
|
277
280
|
* @throws {Error} If validation fails
|
|
278
281
|
*/
|
|
279
282
|
function validateDatasourceSchemas(datasourceJsons, externalDatasourceSchema, ajv) {
|
|
280
|
-
|
|
281
|
-
|
|
283
|
+
if (!externalDatasourceSchema.$id || typeof externalDatasourceSchema.$id !== 'string') {
|
|
284
|
+
throw new Error('External datasource schema is missing required $id');
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// external-datasource.schema.json references these by $id (aifabrix://schema/type/*)
|
|
288
|
+
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
289
|
+
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
290
|
+
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
291
|
+
|
|
292
|
+
ajv.addSchema(externalDatasourceSchema, externalDatasourceSchema.$id);
|
|
282
293
|
const validateDatasource = ajv.compile(externalDatasourceSchema);
|
|
283
294
|
|
|
284
295
|
for (let i = 0; i < datasourceJsons.length; i++) {
|
|
@@ -353,6 +364,7 @@ function prepareSchemasForValidation() {
|
|
|
353
364
|
*/
|
|
354
365
|
function validateApplicationSchemaComponents(systemJson, datasourceJsons, externalSystemSchema, datasourceSchemaToAdd) {
|
|
355
366
|
const ajv = new Ajv({ allErrors: true, strict: false, removeAdditional: false });
|
|
367
|
+
addFormats(ajv);
|
|
356
368
|
|
|
357
369
|
// Validate system against external-system schema
|
|
358
370
|
validateSystemSchema(systemJson, externalSystemSchema, ajv);
|
package/lib/generator/helpers.js
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
const fs = require('fs');
|
|
12
12
|
const path = require('path');
|
|
13
13
|
const { loadConfigFile } = require('../utils/config-format');
|
|
14
|
+
const { urlTokenToKeyVaultSecretName } = require('./deploy-manifest-azure-kv');
|
|
14
15
|
|
|
15
16
|
/**
|
|
16
17
|
* Loads application config file (application.yaml, application.json, or legacy path) via converter.
|
|
@@ -218,6 +219,9 @@ function determineVariableLocation(value, key) {
|
|
|
218
219
|
if (value.startsWith('kv://')) {
|
|
219
220
|
location = 'keyvault';
|
|
220
221
|
required = true;
|
|
222
|
+
} else if (value.startsWith('url://')) {
|
|
223
|
+
location = 'keyvault';
|
|
224
|
+
required = true;
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
// Check if it's a sensitive variable
|
|
@@ -229,6 +233,31 @@ function determineVariableLocation(value, key) {
|
|
|
229
233
|
return { location, required };
|
|
230
234
|
}
|
|
231
235
|
|
|
236
|
+
/**
|
|
237
|
+
* Maps env.template url:// value(s) to Key Vault secret name(s) for deploy JSON.
|
|
238
|
+
* Comma-separated lists are supported (e.g. CORS ALLOWED_ORIGINS): each segment
|
|
239
|
+
* starting with url:// is mapped; other segments are left unchanged (e.g. http://localhost:*).
|
|
240
|
+
*
|
|
241
|
+
* @param {string} appKey - application.yaml app.key
|
|
242
|
+
* @param {string} value - Full value after KEY= (must start with url:// for caller)
|
|
243
|
+
* @returns {string} Comma-joined secret names / literals
|
|
244
|
+
*/
|
|
245
|
+
function mapUrlTemplateValueToKeyVaultNames(appKey, value) {
|
|
246
|
+
const segments = value
|
|
247
|
+
.split(',')
|
|
248
|
+
.map((s) => s.trim())
|
|
249
|
+
.filter((s) => s.length > 0);
|
|
250
|
+
return segments
|
|
251
|
+
.map((seg) => {
|
|
252
|
+
if (seg.startsWith('url://')) {
|
|
253
|
+
const token = seg.slice('url://'.length).trim();
|
|
254
|
+
return urlTokenToKeyVaultSecretName(appKey, token);
|
|
255
|
+
}
|
|
256
|
+
return seg;
|
|
257
|
+
})
|
|
258
|
+
.join(',');
|
|
259
|
+
}
|
|
260
|
+
|
|
232
261
|
/**
|
|
233
262
|
* Creates a configuration item from parsed variable
|
|
234
263
|
* @function createConfigItem
|
|
@@ -237,12 +266,27 @@ function determineVariableLocation(value, key) {
|
|
|
237
266
|
* @param {string} location - Variable location
|
|
238
267
|
* @param {boolean} required - Whether variable is required
|
|
239
268
|
* @param {Map} portalInputMap - Map of portalInput configurations
|
|
269
|
+
* @param {string|null} appKey - application.yaml app.key (required when value uses url://)
|
|
240
270
|
* @returns {Object} Configuration item
|
|
241
271
|
*/
|
|
242
|
-
function createConfigItem(key, value, location, required, portalInputMap) {
|
|
272
|
+
function createConfigItem(key, value, location, required, portalInputMap, appKey) {
|
|
273
|
+
let storedValue = value;
|
|
274
|
+
if (location === 'keyvault') {
|
|
275
|
+
if (value.startsWith('kv://')) {
|
|
276
|
+
storedValue = value.replace('kv://', '');
|
|
277
|
+
} else if (value.startsWith('url://')) {
|
|
278
|
+
if (!appKey) {
|
|
279
|
+
throw new Error(
|
|
280
|
+
`Cannot resolve ${key}=${value}: application app.key is required to map url:// to a Key Vault secret name`
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
storedValue = mapUrlTemplateValueToKeyVaultNames(appKey, value);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
243
287
|
const configItem = {
|
|
244
288
|
name: key,
|
|
245
|
-
value:
|
|
289
|
+
value: storedValue,
|
|
246
290
|
location,
|
|
247
291
|
required
|
|
248
292
|
};
|
|
@@ -259,6 +303,10 @@ function parseEnvironmentVariables(envTemplate, variablesConfig = null) {
|
|
|
259
303
|
const configuration = [];
|
|
260
304
|
const lines = envTemplate.split('\n');
|
|
261
305
|
const portalInputMap = createPortalInputMap(variablesConfig);
|
|
306
|
+
const appKey =
|
|
307
|
+
variablesConfig && variablesConfig.app && variablesConfig.app.key
|
|
308
|
+
? String(variablesConfig.app.key).trim()
|
|
309
|
+
: null;
|
|
262
310
|
|
|
263
311
|
for (const line of lines) {
|
|
264
312
|
const parsed = parseEnvironmentVariableLine(line);
|
|
@@ -267,7 +315,14 @@ function parseEnvironmentVariables(envTemplate, variablesConfig = null) {
|
|
|
267
315
|
}
|
|
268
316
|
|
|
269
317
|
const { location, required } = determineVariableLocation(parsed.value, parsed.key);
|
|
270
|
-
const configItem = createConfigItem(
|
|
318
|
+
const configItem = createConfigItem(
|
|
319
|
+
parsed.key,
|
|
320
|
+
parsed.value,
|
|
321
|
+
location,
|
|
322
|
+
required,
|
|
323
|
+
portalInputMap,
|
|
324
|
+
appKey
|
|
325
|
+
);
|
|
271
326
|
configuration.push(configItem);
|
|
272
327
|
}
|
|
273
328
|
|
package/lib/generator/index.js
CHANGED
|
@@ -22,6 +22,7 @@ const { generateControllerManifest } = require('./external-controller-manifest')
|
|
|
22
22
|
const { resolveVersionForApp } = require('../utils/image-version');
|
|
23
23
|
const { getContainerPort } = require('../utils/port-resolver');
|
|
24
24
|
const { buildEnvVarMap } = require('../utils/env-map');
|
|
25
|
+
const { rewriteFrontDoorHostForAzureDeploy } = require('./deploy-manifest-azure-kv');
|
|
25
26
|
|
|
26
27
|
/**
|
|
27
28
|
* Generates deployment JSON from application configuration files
|
|
@@ -191,6 +192,8 @@ function buildAndValidateDeployment(appName, variables, envTemplate, rbac, optio
|
|
|
191
192
|
substituteEnvVarsInDeployment(deployment, envVarMap);
|
|
192
193
|
}
|
|
193
194
|
|
|
195
|
+
rewriteFrontDoorHostForAzureDeploy(deployment);
|
|
196
|
+
|
|
194
197
|
// Ensure no other ${...} placeholders remain in manifest
|
|
195
198
|
_validator.validateNoUnresolvedVariablesInDeployment(deployment);
|
|
196
199
|
|
|
@@ -236,6 +239,7 @@ async function buildDeploymentManifestInMemory(appName, options = {}) {
|
|
|
236
239
|
}
|
|
237
240
|
const envVarMap = await buildEnvVarMap('docker', null, null, { appPort: effectivePort });
|
|
238
241
|
substituteEnvVarsInDeployment(deployment, envVarMap);
|
|
242
|
+
rewriteFrontDoorHostForAzureDeploy(deployment);
|
|
239
243
|
_validator.validateNoUnresolvedVariablesInDeployment(deployment);
|
|
240
244
|
return { deployment, appPath };
|
|
241
245
|
}
|