@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
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional v2.4.x datasource validation warnings (beyond JSON Schema).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Post-schema warnings for external datasource configs
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const STORAGE_ENTITIES = new Set(['recordStorage', 'documentStorage']);
|
|
10
|
+
|
|
11
|
+
function warnMissingDimensions(parsed, warnings) {
|
|
12
|
+
const entityType = parsed.entityType;
|
|
13
|
+
if (!STORAGE_ENTITIES.has(entityType)) {
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const dims = parsed.dimensions;
|
|
17
|
+
if (!dims || typeof dims !== 'object' || Array.isArray(dims) || Object.keys(dims).length === 0) {
|
|
18
|
+
warnings.push(
|
|
19
|
+
`Datasource "${parsed.key || '(unknown)'}": dimensions missing or empty for entityType=${entityType} (recommended for ABAC; see schema 2.4.x notes).`
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function warnFkWithoutActor(parsed, warnings) {
|
|
25
|
+
const dimensions = parsed.dimensions;
|
|
26
|
+
if (!dimensions || typeof dimensions !== 'object' || Array.isArray(dimensions)) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
for (const [dimKey, binding] of Object.entries(dimensions)) {
|
|
30
|
+
if (!binding || typeof binding !== 'object' || binding.type !== 'fk') {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!Object.prototype.hasOwnProperty.call(binding, 'actor')) {
|
|
34
|
+
warnings.push(
|
|
35
|
+
`Datasource "${parsed.key || '(unknown)'}": dimension "${dimKey}" uses type=fk without actor; set actor (displayName, email, userId, groups, roles) for predictable ABAC binding.`
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Collects non-fatal warnings for an external datasource document (already parsed).
|
|
43
|
+
* @param {Object} parsed - Parsed datasource JSON
|
|
44
|
+
* @returns {string[]} Warning messages (empty if none)
|
|
45
|
+
*/
|
|
46
|
+
function collectExternalDatasourceWarnings(parsed) {
|
|
47
|
+
const warnings = [];
|
|
48
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
49
|
+
return warnings;
|
|
50
|
+
}
|
|
51
|
+
warnMissingDimensions(parsed, warnings);
|
|
52
|
+
warnFkWithoutActor(parsed, warnings);
|
|
53
|
+
return warnings;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { collectExternalDatasourceWarnings };
|
|
@@ -184,7 +184,7 @@ async function validateAuthSecurityPathConsistency(appPath, errors, _warnings) {
|
|
|
184
184
|
const canonicalPath = canonicalSegment ? `kv://${systemKey}/${canonicalSegment}` : null;
|
|
185
185
|
if (canonicalPath && value !== canonicalPath) {
|
|
186
186
|
errors.push(
|
|
187
|
-
`authentication.security.${key} has path ${value}; canonical path is ${canonicalPath}. Run \`aifabrix repair <
|
|
187
|
+
`authentication.security.${key} has path ${value}; canonical path is ${canonicalPath}. Run \`aifabrix repair <systemKey>\` to normalize.`
|
|
188
188
|
);
|
|
189
189
|
}
|
|
190
190
|
}
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const Ajv = require('ajv');
|
|
13
|
+
const addFormats = require('ajv-formats');
|
|
13
14
|
const fs = require('fs');
|
|
14
15
|
const path = require('path');
|
|
15
16
|
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
@@ -28,6 +29,7 @@ async function setupAjvWithSchemas() {
|
|
|
28
29
|
strict: false,
|
|
29
30
|
removeAdditional: false
|
|
30
31
|
});
|
|
32
|
+
addFormats(ajv);
|
|
31
33
|
|
|
32
34
|
// Load raw schema objects (not compiled validators)
|
|
33
35
|
const externalSystemSchemaPath = path.join(__dirname, '..', 'schema', 'external-system.schema.json');
|
|
@@ -43,11 +45,20 @@ async function setupAjvWithSchemas() {
|
|
|
43
45
|
externalDatasourceSchema = schemaCopy;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
if (!externalSystemSchema.$id || typeof externalSystemSchema.$id !== 'string') {
|
|
49
|
+
throw new Error('External system schema is missing required $id');
|
|
50
|
+
}
|
|
51
|
+
if (!externalDatasourceSchema.$id || typeof externalDatasourceSchema.$id !== 'string') {
|
|
52
|
+
throw new Error('External datasource schema is missing required $id');
|
|
53
|
+
}
|
|
48
54
|
|
|
49
|
-
|
|
50
|
-
ajv.addSchema(
|
|
55
|
+
// external-datasource.schema.json references these by $id (aifabrix://schema/type/*)
|
|
56
|
+
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
57
|
+
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
58
|
+
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
59
|
+
|
|
60
|
+
ajv.addSchema(externalSystemSchema, externalSystemSchema.$id);
|
|
61
|
+
ajv.addSchema(externalDatasourceSchema, externalDatasourceSchema.$id);
|
|
51
62
|
|
|
52
63
|
return { ajv, externalSystemSchema, externalDatasourceSchema };
|
|
53
64
|
}
|
|
@@ -66,7 +77,10 @@ function validateManifestStructure(manifest, ajv, applicationSchema, errors) {
|
|
|
66
77
|
const manifestValid = validateManifest(manifest);
|
|
67
78
|
|
|
68
79
|
if (!manifestValid) {
|
|
69
|
-
const manifestErrors = formatValidationErrors(validateManifest.errors
|
|
80
|
+
const manifestErrors = formatValidationErrors(validateManifest.errors, {
|
|
81
|
+
rootData: manifest,
|
|
82
|
+
deploymentManifest: manifest
|
|
83
|
+
});
|
|
70
84
|
errors.push(...manifestErrors.map(err => `Manifest validation: ${err}`));
|
|
71
85
|
}
|
|
72
86
|
}
|
|
@@ -86,7 +100,10 @@ function validateInlineSystem(manifest, ajv, externalSystemSchema, errors) {
|
|
|
86
100
|
const systemValid = validateSystem(manifest.system);
|
|
87
101
|
|
|
88
102
|
if (!systemValid) {
|
|
89
|
-
const systemErrors = formatValidationErrors(validateSystem.errors
|
|
103
|
+
const systemErrors = formatValidationErrors(validateSystem.errors, {
|
|
104
|
+
rootData: manifest.system,
|
|
105
|
+
deploymentManifest: manifest.system
|
|
106
|
+
});
|
|
90
107
|
errors.push(...systemErrors.map(err => `System validation: ${err}`));
|
|
91
108
|
}
|
|
92
109
|
} else if (manifest.type === 'external') {
|
|
@@ -113,7 +130,10 @@ function validateDatasources(manifest, ajv, externalDatasourceSchema, errors, wa
|
|
|
113
130
|
manifest.dataSources.forEach((datasource, index) => {
|
|
114
131
|
const datasourceValid = validateDatasource(datasource);
|
|
115
132
|
if (!datasourceValid) {
|
|
116
|
-
const datasourceErrors = formatValidationErrors(validateDatasource.errors
|
|
133
|
+
const datasourceErrors = formatValidationErrors(validateDatasource.errors, {
|
|
134
|
+
rootData: datasource,
|
|
135
|
+
deploymentManifest: datasource
|
|
136
|
+
});
|
|
117
137
|
errors.push(...datasourceErrors.map(err => `Datasource ${index + 1} (${datasource.key || 'unknown'}): ${err}`));
|
|
118
138
|
} else {
|
|
119
139
|
const fieldRefErrors = validateFieldReferences(datasource);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatBlockingError, formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* Validation Display Utilities
|
|
3
4
|
*
|
|
@@ -25,9 +26,9 @@ function displayApplicationValidation(application) {
|
|
|
25
26
|
|
|
26
27
|
logger.log(chalk.blue('\nApplication:'));
|
|
27
28
|
if (application.valid) {
|
|
28
|
-
logger.log(chalk.green('
|
|
29
|
+
logger.log(chalk.green(' ✔ Application configuration is valid'));
|
|
29
30
|
} else {
|
|
30
|
-
logger.log(chalk.red('
|
|
31
|
+
logger.log(chalk.red(' ✖ Application configuration has errors:'));
|
|
31
32
|
if (application.errors && application.errors.length > 0) {
|
|
32
33
|
application.errors.forEach(error => {
|
|
33
34
|
logger.log(chalk.red(` • ${error}`));
|
|
@@ -51,12 +52,17 @@ function extractDimensionsFromDatasource(filePath) {
|
|
|
51
52
|
try {
|
|
52
53
|
const parsed = loadConfigFile(filePath);
|
|
53
54
|
|
|
54
|
-
|
|
55
|
-
const
|
|
55
|
+
const rootFlat = {};
|
|
56
|
+
const root = parsed.dimensions;
|
|
57
|
+
if (root && typeof root === 'object' && !Array.isArray(root)) {
|
|
58
|
+
for (const [dimKey, binding] of Object.entries(root)) {
|
|
59
|
+
if (binding && typeof binding === 'object' && typeof binding.field === 'string') {
|
|
60
|
+
rootFlat[dimKey] = `metadata.${binding.field}`;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
56
64
|
const abacDimensions = parsed.abac?.dimensions || {};
|
|
57
|
-
|
|
58
|
-
// Merge both sources (abac.dimensions can override fieldMappings.dimensions)
|
|
59
|
-
const allDimensions = { ...dimensions, ...abacDimensions };
|
|
65
|
+
const allDimensions = { ...rootFlat, ...abacDimensions };
|
|
60
66
|
const dimensionKeys = Object.keys(allDimensions);
|
|
61
67
|
|
|
62
68
|
return {
|
|
@@ -82,9 +88,9 @@ function displayExternalFilesValidation(externalFiles) {
|
|
|
82
88
|
logger.log(chalk.blue('\nExternal Integration Files:'));
|
|
83
89
|
externalFiles.forEach(file => {
|
|
84
90
|
if (file.valid) {
|
|
85
|
-
logger.log(chalk.green(`
|
|
91
|
+
logger.log(chalk.green(` ✔ ${file.file} (${file.type})`));
|
|
86
92
|
} else {
|
|
87
|
-
logger.log(chalk.red(`
|
|
93
|
+
logger.log(chalk.red(` ✖ ${file.file} (${file.type}):`));
|
|
88
94
|
file.errors.forEach(error => {
|
|
89
95
|
logger.log(chalk.red(` • ${error}`));
|
|
90
96
|
});
|
|
@@ -126,7 +132,7 @@ function displayDimensionsValidation(externalFiles) {
|
|
|
126
132
|
|
|
127
133
|
if (dimensionsInfo.hasDimensions) {
|
|
128
134
|
anyDatasourceHasDimensions = true;
|
|
129
|
-
logger.log(chalk.green(`
|
|
135
|
+
logger.log(chalk.green(` ✔ ${file.file}`));
|
|
130
136
|
dimensionsInfo.dimensionKeys.forEach(key => {
|
|
131
137
|
const mapping = dimensionsInfo.dimensions[key];
|
|
132
138
|
logger.log(chalk.gray(` ${key} → ${mapping}`));
|
|
@@ -156,9 +162,9 @@ function displayRbacValidation(rbac) {
|
|
|
156
162
|
|
|
157
163
|
logger.log(chalk.blue('\nRBAC Configuration:'));
|
|
158
164
|
if (rbac.valid) {
|
|
159
|
-
logger.log(chalk.green('
|
|
165
|
+
logger.log(chalk.green(' ✔ RBAC configuration is valid'));
|
|
160
166
|
} else {
|
|
161
|
-
logger.log(chalk.red('
|
|
167
|
+
logger.log(chalk.red(' ✖ RBAC configuration has errors:'));
|
|
162
168
|
rbac.errors.forEach(error => {
|
|
163
169
|
logger.log(chalk.red(` • ${error}`));
|
|
164
170
|
});
|
|
@@ -183,9 +189,9 @@ function displayFileValidation(result) {
|
|
|
183
189
|
logger.log(chalk.blue(`\nFile: ${result.file}`));
|
|
184
190
|
logger.log(chalk.blue(`Type: ${result.type}`));
|
|
185
191
|
if (result.valid) {
|
|
186
|
-
logger.log(chalk.green('
|
|
192
|
+
logger.log(chalk.green(' ✔ File is valid'));
|
|
187
193
|
} else {
|
|
188
|
-
logger.log(chalk.red('
|
|
194
|
+
logger.log(chalk.red(' ✖ File has errors:'));
|
|
189
195
|
result.errors.forEach(error => {
|
|
190
196
|
logger.log(chalk.red(` • ${error}`));
|
|
191
197
|
});
|
|
@@ -222,9 +228,9 @@ function displayAggregatedWarnings(warnings) {
|
|
|
222
228
|
function displayApplicationStep(application) {
|
|
223
229
|
logger.log(chalk.blue('\nApplication:'));
|
|
224
230
|
if (application.valid) {
|
|
225
|
-
logger.log(chalk.green('
|
|
231
|
+
logger.log(chalk.green(' ✔ Application configuration is valid'));
|
|
226
232
|
} else {
|
|
227
|
-
logger.log(chalk.red('
|
|
233
|
+
logger.log(chalk.red(' ✖ Application configuration has errors:'));
|
|
228
234
|
application.errors.forEach(error => {
|
|
229
235
|
logger.log(chalk.red(` • ${error}`));
|
|
230
236
|
});
|
|
@@ -253,9 +259,9 @@ function displayComponentsStep(components) {
|
|
|
253
259
|
if (hasFiles) {
|
|
254
260
|
components.files.forEach(file => {
|
|
255
261
|
if (file.valid) {
|
|
256
|
-
logger.log(chalk.green(`
|
|
262
|
+
logger.log(chalk.green(` ✔ ${file.file} (${file.type})`));
|
|
257
263
|
} else {
|
|
258
|
-
logger.log(chalk.red(`
|
|
264
|
+
logger.log(chalk.red(` ✖ ${file.file} (${file.type})`));
|
|
259
265
|
}
|
|
260
266
|
});
|
|
261
267
|
}
|
|
@@ -282,7 +288,7 @@ function displayDimensionsStep(datasourceFiles) {
|
|
|
282
288
|
try {
|
|
283
289
|
const dimensionsInfo = extractDimensionsFromDatasource(file.path || file.file);
|
|
284
290
|
if (dimensionsInfo.hasDimensions) {
|
|
285
|
-
logger.log(chalk.green(`
|
|
291
|
+
logger.log(chalk.green(` ✔ ${file.file}`));
|
|
286
292
|
dimensionsInfo.dimensionKeys.forEach(key => {
|
|
287
293
|
const mapping = dimensionsInfo.dimensions[key];
|
|
288
294
|
logger.log(chalk.gray(` ${key} → ${mapping}`));
|
|
@@ -308,15 +314,15 @@ function displayManifestStep(manifest, componentFiles) {
|
|
|
308
314
|
if (manifest.skipped) {
|
|
309
315
|
logger.log(chalk.yellow(' ⊘ Skipped (fix errors above first)'));
|
|
310
316
|
} else if (manifest.valid) {
|
|
311
|
-
logger.log(chalk.green('
|
|
317
|
+
logger.log(chalk.green(' ✔ Full deployment manifest is valid'));
|
|
312
318
|
if (componentFiles) {
|
|
313
319
|
const datasourceFiles = componentFiles.filter(f => f.type === 'datasource' || f.type === 'external-datasource');
|
|
314
|
-
logger.log(chalk.green('
|
|
315
|
-
logger.log(chalk.green(`
|
|
316
|
-
logger.log(chalk.green('
|
|
320
|
+
logger.log(chalk.green(' ✔ System configuration valid'));
|
|
321
|
+
logger.log(chalk.green(` ✔ ${datasourceFiles.length} datasource(s) valid`));
|
|
322
|
+
logger.log(chalk.green(' ✔ Schema validation passed'));
|
|
317
323
|
}
|
|
318
324
|
} else {
|
|
319
|
-
logger.log(chalk.red('
|
|
325
|
+
logger.log(chalk.red(' ✖ Full deployment manifest validation failed:'));
|
|
320
326
|
const errs = manifest.errors && manifest.errors.length > 0 ? manifest.errors : [];
|
|
321
327
|
if (errs.length > 0) {
|
|
322
328
|
errs.forEach(error => {
|
|
@@ -341,9 +347,9 @@ function displayManifestStep(manifest, componentFiles) {
|
|
|
341
347
|
*/
|
|
342
348
|
function displayStepByStepValidation(result) {
|
|
343
349
|
if (result.valid) {
|
|
344
|
-
logger.log(
|
|
350
|
+
logger.log(formatSuccessParagraph('Validation passed!'));
|
|
345
351
|
} else {
|
|
346
|
-
logger.log(chalk.red('\n
|
|
352
|
+
logger.log(chalk.red('\n✖ Validation failed!'));
|
|
347
353
|
}
|
|
348
354
|
|
|
349
355
|
displayApplicationStep(result.steps.application);
|
|
@@ -388,7 +394,7 @@ function displayBatchValidationResults(batchResult) {
|
|
|
388
394
|
results.forEach(item => {
|
|
389
395
|
logger.log(chalk.blue(`\n--- ${item.appName} ---`));
|
|
390
396
|
if (item.error) {
|
|
391
|
-
logger.log(chalk.red(`
|
|
397
|
+
logger.log(chalk.red(` ✖ ${item.error}`));
|
|
392
398
|
} else if (item.result) {
|
|
393
399
|
displayValidationResults(item.result);
|
|
394
400
|
}
|
|
@@ -398,10 +404,10 @@ function displayBatchValidationResults(batchResult) {
|
|
|
398
404
|
const failed = results.length - passed;
|
|
399
405
|
logger.log(chalk.blue('\n--- Summary ---'));
|
|
400
406
|
if (failed === 0) {
|
|
401
|
-
logger.log(
|
|
407
|
+
logger.log(formatSuccessLine(`${passed} passed, 0 failed`));
|
|
402
408
|
logger.log(chalk.green('Overall: Passed'));
|
|
403
409
|
} else {
|
|
404
|
-
logger.log(
|
|
410
|
+
logger.log(formatBlockingError(`${passed} passed, ${failed} failed`));
|
|
405
411
|
logger.log(chalk.red('Overall: Failed'));
|
|
406
412
|
}
|
|
407
413
|
}
|
|
@@ -445,9 +451,9 @@ function displayValidationResults(result) {
|
|
|
445
451
|
|
|
446
452
|
// Legacy format (for regular apps)
|
|
447
453
|
if (result.valid) {
|
|
448
|
-
logger.log(
|
|
454
|
+
logger.log(formatSuccessParagraph('Validation passed!'));
|
|
449
455
|
} else {
|
|
450
|
-
logger.log(chalk.red('\n
|
|
456
|
+
logger.log(chalk.red('\n✖ Validation failed!'));
|
|
451
457
|
}
|
|
452
458
|
|
|
453
459
|
displayApplicationValidation(result.application);
|
|
@@ -104,15 +104,6 @@ function aggregateValidationResults(appValidation, externalValidations, rbacVali
|
|
|
104
104
|
};
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/**
|
|
108
|
-
* Validates a single external file against its schema
|
|
109
|
-
*
|
|
110
|
-
* @async
|
|
111
|
-
* @function validateExternalFile
|
|
112
|
-
* @param {string} filePath - Path to the file
|
|
113
|
-
* @param {string} type - File type: 'system' | 'datasource'
|
|
114
|
-
* @returns {Promise<Object>} Validation result
|
|
115
|
-
*/
|
|
116
107
|
/**
|
|
117
108
|
* Parses external system/datasource file content (JSON or YAML).
|
|
118
109
|
* @function parseJsonFileContent
|
|
@@ -179,6 +170,7 @@ function validateRoleReferences(parsed, errors) {
|
|
|
179
170
|
});
|
|
180
171
|
}
|
|
181
172
|
|
|
173
|
+
/** @async */
|
|
182
174
|
async function validateExternalFile(filePath, type) {
|
|
183
175
|
if (!fs.existsSync(filePath)) {
|
|
184
176
|
throw new Error(`File not found: ${filePath}`);
|
|
@@ -203,9 +195,9 @@ async function validateExternalFile(filePath, type) {
|
|
|
203
195
|
}
|
|
204
196
|
|
|
205
197
|
if (normalizedType === 'datasource') {
|
|
206
|
-
const
|
|
207
|
-
|
|
208
|
-
|
|
198
|
+
const { collectExternalDatasourceWarnings } = require('./datasource-warnings');
|
|
199
|
+
errors.push(...validateFieldReferences(parseResult.parsed), ...validateAbac(parseResult.parsed));
|
|
200
|
+
warnings.push(...collectExternalDatasourceWarnings(parseResult.parsed));
|
|
209
201
|
}
|
|
210
202
|
|
|
211
203
|
return {
|
|
@@ -497,4 +489,3 @@ module.exports = {
|
|
|
497
489
|
validateAll: (opts = {}) => batch.validateAll(validateAppOrFile, opts),
|
|
498
490
|
buildBatchResult: batch.buildBatchResult
|
|
499
491
|
};
|
|
500
|
-
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Scan deployment/config objects for unresolved ${VAR} placeholders (validator helper).
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview Extracted from validator.js for ESLint max-lines
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 1.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
/** Pattern matching ${VAR} style unresolved variables in strings */
|
|
12
|
+
const UNRESOLVED_VAR_REGEX = /\$\{[^}]+\}/g;
|
|
13
|
+
|
|
14
|
+
/** Allowed manifest placeholders in frontDoorRouting.host (expanded at run/resolve time; plan 122). */
|
|
15
|
+
const FRONT_DOOR_HOST_ALLOWED = /\$\{DEV_USERNAME\}|\$\{REMOTE_HOST\}/g;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* First ${...} still present after stripping allowed DEV_USERNAME / REMOTE_HOST segments, or null.
|
|
19
|
+
* @param {string} str
|
|
20
|
+
* @returns {string|null}
|
|
21
|
+
*/
|
|
22
|
+
function getFrontDoorHostFirstForbiddenPlaceholder(str) {
|
|
23
|
+
let t = String(str).trim();
|
|
24
|
+
t = t.replace(FRONT_DOOR_HOST_ALLOWED, '');
|
|
25
|
+
t = t.replace(/^\.+|\.+$/g, '').trim();
|
|
26
|
+
const m = t.match(UNRESOLVED_VAR_REGEX);
|
|
27
|
+
return m ? m[0] : null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} obj
|
|
32
|
+
* @param {string} prefix
|
|
33
|
+
* @returns {string[]}
|
|
34
|
+
*/
|
|
35
|
+
function collectStringUnresolved(obj, prefix) {
|
|
36
|
+
if (prefix === 'frontDoorRouting.host') {
|
|
37
|
+
const bad = getFrontDoorHostFirstForbiddenPlaceholder(obj);
|
|
38
|
+
return bad ? [`${prefix}: ${bad}`] : [];
|
|
39
|
+
}
|
|
40
|
+
const matches = obj.match(UNRESOLVED_VAR_REGEX);
|
|
41
|
+
if (matches && matches.length > 0) {
|
|
42
|
+
const pathLabel = prefix || 'value';
|
|
43
|
+
return [`${pathLabel}: ${matches[0]}`];
|
|
44
|
+
}
|
|
45
|
+
return [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Recursively finds all string values in obj that contain ${...} placeholders.
|
|
50
|
+
*
|
|
51
|
+
* @param {Object} obj - Object to scan (e.g. deployment manifest)
|
|
52
|
+
* @param {string} [prefix=''] - Path prefix for error reporting
|
|
53
|
+
* @returns {string[]} List of paths with example placeholder (e.g. "port: ${PORT}")
|
|
54
|
+
*/
|
|
55
|
+
function findUnresolvedVariablesInObject(obj, prefix = '') {
|
|
56
|
+
const found = [];
|
|
57
|
+
if (obj === null || obj === undefined) {
|
|
58
|
+
return found;
|
|
59
|
+
}
|
|
60
|
+
if (typeof obj === 'string') {
|
|
61
|
+
return collectStringUnresolved(obj, prefix);
|
|
62
|
+
}
|
|
63
|
+
if (Array.isArray(obj)) {
|
|
64
|
+
obj.forEach((item, i) => {
|
|
65
|
+
found.push(...findUnresolvedVariablesInObject(item, `${prefix}[${i}]`));
|
|
66
|
+
});
|
|
67
|
+
return found;
|
|
68
|
+
}
|
|
69
|
+
if (typeof obj === 'object') {
|
|
70
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
71
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
72
|
+
found.push(...findUnresolvedVariablesInObject(value, path));
|
|
73
|
+
}
|
|
74
|
+
return found;
|
|
75
|
+
}
|
|
76
|
+
return found;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Validates that deployment manifest contains no unresolved ${...} variables.
|
|
81
|
+
* @param {Object} deployment - Deployment manifest object
|
|
82
|
+
* @throws {Error} If any ${...} placeholders are found
|
|
83
|
+
*/
|
|
84
|
+
function validateNoUnresolvedVariablesInDeployment(deployment) {
|
|
85
|
+
const unresolved = findUnresolvedVariablesInObject(deployment);
|
|
86
|
+
if (unresolved.length > 0) {
|
|
87
|
+
const examples = [...new Set(unresolved)].slice(0, 5).join(', ');
|
|
88
|
+
throw new Error(
|
|
89
|
+
`Deployment manifest contains unresolved variables (e.g. ${examples}). ` +
|
|
90
|
+
'Use secret variables (kv://) in env.template for sensitive values, and set the application port as a number in application.yaml.'
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
module.exports = {
|
|
96
|
+
findUnresolvedVariablesInObject,
|
|
97
|
+
validateNoUnresolvedVariablesInDeployment
|
|
98
|
+
};
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
14
|
const Ajv = require('ajv');
|
|
15
|
+
const addFormats = require('ajv-formats');
|
|
15
16
|
const applicationSchema = require('../schema/application-schema.json');
|
|
16
17
|
const externalSystemSchema = require('../schema/external-system.schema.json');
|
|
17
18
|
const externalDataSourceSchema = require('../schema/external-datasource.schema.json');
|
|
@@ -57,7 +58,8 @@ async function loadVariablesYaml(appName, options = {}) {
|
|
|
57
58
|
* @returns {Function} Compiled validator function
|
|
58
59
|
*/
|
|
59
60
|
function setupAjvValidator() {
|
|
60
|
-
const ajv = new Ajv({ allErrors: true, strict: false });
|
|
61
|
+
const ajv = new Ajv({ allErrors: true, strict: false, verbose: true });
|
|
62
|
+
addFormats(ajv);
|
|
61
63
|
const externalSystemSchemaCopy = { ...externalSystemSchema };
|
|
62
64
|
const externalDataSourceSchemaCopy = { ...externalDataSourceSchema };
|
|
63
65
|
|
|
@@ -65,6 +67,11 @@ function setupAjvValidator() {
|
|
|
65
67
|
delete externalDataSourceSchemaCopy.$schema;
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
// external-datasource.schema.json references these by $id (aifabrix://schema/type/*)
|
|
71
|
+
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
72
|
+
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
73
|
+
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
74
|
+
|
|
68
75
|
ajv.addSchema(externalSystemSchemaCopy, externalSystemSchema.$id);
|
|
69
76
|
ajv.addSchema(externalDataSourceSchemaCopy, externalDataSourceSchema.$id);
|
|
70
77
|
return ajv.compile(applicationSchema);
|
|
@@ -341,7 +348,8 @@ function validateDeploymentJson(deployment) {
|
|
|
341
348
|
|
|
342
349
|
// verbose: true includes the actual data value in error objects for better error messages
|
|
343
350
|
const ajv = new Ajv({ allErrors: true, strict: false, verbose: true });
|
|
344
|
-
|
|
351
|
+
addFormats(ajv);
|
|
352
|
+
// Register external schemas with their $id
|
|
345
353
|
// Create copies to avoid modifying the original schemas
|
|
346
354
|
const externalSystemSchemaCopy = { ...externalSystemSchema };
|
|
347
355
|
const externalDataSourceSchemaCopy = { ...externalDataSourceSchema };
|
|
@@ -349,6 +357,10 @@ function validateDeploymentJson(deployment) {
|
|
|
349
357
|
if (externalDataSourceSchemaCopy.$schema && externalDataSourceSchemaCopy.$schema.includes('2020-12')) {
|
|
350
358
|
delete externalDataSourceSchemaCopy.$schema;
|
|
351
359
|
}
|
|
360
|
+
// external-datasource.schema.json references these by $id (aifabrix://schema/type/*)
|
|
361
|
+
ajv.addSchema(require('../schema/type/document-storage.json'));
|
|
362
|
+
ajv.addSchema(require('../schema/type/message-service.json'));
|
|
363
|
+
ajv.addSchema(require('../schema/type/vector-store.json'));
|
|
352
364
|
ajv.addSchema(externalSystemSchemaCopy, externalSystemSchema.$id);
|
|
353
365
|
ajv.addSchema(externalDataSourceSchemaCopy, externalDataSourceSchema.$id);
|
|
354
366
|
const validate = ajv.compile(applicationSchema);
|
|
@@ -356,72 +368,17 @@ function validateDeploymentJson(deployment) {
|
|
|
356
368
|
|
|
357
369
|
return {
|
|
358
370
|
valid,
|
|
359
|
-
errors: valid ? [] : formatValidationErrors(validate.errors
|
|
371
|
+
errors: valid ? [] : formatValidationErrors(validate.errors, {
|
|
372
|
+
deploymentManifest: deployment,
|
|
373
|
+
rootData: deployment
|
|
374
|
+
})
|
|
360
375
|
};
|
|
361
376
|
}
|
|
362
377
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
* Recursively finds all string values in obj that contain ${...} placeholders.
|
|
368
|
-
* Used to ensure deployment manifest has no unresolved variables before deploy.
|
|
369
|
-
*
|
|
370
|
-
* @function findUnresolvedVariablesInObject
|
|
371
|
-
* @param {Object} obj - Object to scan (e.g. deployment manifest)
|
|
372
|
-
* @param {string} [prefix=''] - Path prefix for error reporting
|
|
373
|
-
* @returns {string[]} List of paths with example placeholder (e.g. "port: ${PORT}")
|
|
374
|
-
*
|
|
375
|
-
* @example
|
|
376
|
-
* findUnresolvedVariablesInObject({ port: '${PORT}' }) // ['port: ${PORT}']
|
|
377
|
-
*/
|
|
378
|
-
function findUnresolvedVariablesInObject(obj, prefix = '') {
|
|
379
|
-
const found = [];
|
|
380
|
-
if (obj === null || obj === undefined) {
|
|
381
|
-
return found;
|
|
382
|
-
}
|
|
383
|
-
if (typeof obj === 'string') {
|
|
384
|
-
const matches = obj.match(UNRESOLVED_VAR_REGEX);
|
|
385
|
-
if (matches && matches.length > 0) {
|
|
386
|
-
const pathLabel = prefix || 'value';
|
|
387
|
-
found.push(`${pathLabel}: ${matches[0]}`);
|
|
388
|
-
}
|
|
389
|
-
return found;
|
|
390
|
-
}
|
|
391
|
-
if (Array.isArray(obj)) {
|
|
392
|
-
obj.forEach((item, i) => {
|
|
393
|
-
found.push(...findUnresolvedVariablesInObject(item, `${prefix}[${i}]`));
|
|
394
|
-
});
|
|
395
|
-
return found;
|
|
396
|
-
}
|
|
397
|
-
if (typeof obj === 'object') {
|
|
398
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
399
|
-
const path = prefix ? `${prefix}.${key}` : key;
|
|
400
|
-
found.push(...findUnresolvedVariablesInObject(value, path));
|
|
401
|
-
}
|
|
402
|
-
return found;
|
|
403
|
-
}
|
|
404
|
-
return found;
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
/**
|
|
408
|
-
* Validates that deployment manifest contains no unresolved ${...} variables.
|
|
409
|
-
* Throws if any are found, with a message to use secret variables or literal values.
|
|
410
|
-
*
|
|
411
|
-
* @function validateNoUnresolvedVariablesInDeployment
|
|
412
|
-
* @param {Object} deployment - Deployment manifest object
|
|
413
|
-
* @throws {Error} If any ${...} placeholders are found
|
|
414
|
-
*/
|
|
415
|
-
function validateNoUnresolvedVariablesInDeployment(deployment) {
|
|
416
|
-
const unresolved = findUnresolvedVariablesInObject(deployment);
|
|
417
|
-
if (unresolved.length > 0) {
|
|
418
|
-
const examples = [...new Set(unresolved)].slice(0, 5).join(', ');
|
|
419
|
-
throw new Error(
|
|
420
|
-
`Deployment manifest contains unresolved variables (e.g. ${examples}). ` +
|
|
421
|
-
'Use secret variables (kv://) in env.template for sensitive values, and set the application port as a number in application.yaml.'
|
|
422
|
-
);
|
|
423
|
-
}
|
|
424
|
-
}
|
|
378
|
+
const {
|
|
379
|
+
findUnresolvedVariablesInObject,
|
|
380
|
+
validateNoUnresolvedVariablesInDeployment
|
|
381
|
+
} = require('./validator-unresolved-placeholders');
|
|
425
382
|
|
|
426
383
|
/**
|
|
427
384
|
* Validates all application configuration files
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* @fileoverview Wizard configuration validator for wizard.yaml files
|
|
3
4
|
* @author AI Fabrix Team
|
|
@@ -281,7 +282,7 @@ function displayValidationResults(result) {
|
|
|
281
282
|
console.log(chalk.green('Wizard configuration is valid'));
|
|
282
283
|
} else {
|
|
283
284
|
// eslint-disable-next-line no-console
|
|
284
|
-
console.log(
|
|
285
|
+
console.log(formatBlockingError('Wizard configuration validation failed:'));
|
|
285
286
|
result.errors.forEach(error => {
|
|
286
287
|
// eslint-disable-next-line no-console
|
|
287
288
|
console.log(chalk.red(` - ${error}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/builder",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.44.0",
|
|
4
4
|
"description": "AI Fabrix Local Fabric & Deployment SDK",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"af": "bin/aifabrix.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
|
+
"all": "npm run precommit && npm run test:manual && npm run test:integration:fixtures && npm run test:hubspot-wizard",
|
|
11
12
|
"test": "node tests/scripts/test-wrapper.js",
|
|
12
13
|
"test:ci": "bash tests/scripts/ci-simulate.sh",
|
|
13
14
|
"test:same-as-github": "npm run build:ci",
|
|
@@ -15,6 +16,7 @@
|
|
|
15
16
|
"test:coverage:nyc": "nyc --reporter=text --reporter=lcov --reporter=html jest --config jest.config.coverage.js --runInBand",
|
|
16
17
|
"test:watch": "jest --watch",
|
|
17
18
|
"test:integration": "jest --config jest.config.integration.js --runInBand",
|
|
19
|
+
"test:integration:fixtures": "jest --config jest.config.integration.fixtures.js --runInBand",
|
|
18
20
|
"test:integration:python": "cross-env TEST_LANGUAGE=python jest --config jest.config.integration.js --runInBand",
|
|
19
21
|
"test:integration:typescript": "cross-env TEST_LANGUAGE=typescript jest --config jest.config.integration.js --runInBand",
|
|
20
22
|
"test:hubspot-wizard": "node integration/hubspot-test/test.js",
|
|
@@ -32,8 +34,10 @@
|
|
|
32
34
|
"validate": "npm run build",
|
|
33
35
|
"prepublishOnly": "npm run validate",
|
|
34
36
|
"precommit": "npm run lint:fix && npm run test",
|
|
35
|
-
"install:local": "node scripts/install-local.js",
|
|
36
|
-
"uninstall:local": "node scripts/install-local.js uninstall"
|
|
37
|
+
"install:local": "node scripts/install-local.js && which aifabrix && which af",
|
|
38
|
+
"uninstall:local": "node scripts/install-local.js uninstall && which aifabrix && which af",
|
|
39
|
+
"diagnose:cli": "node scripts/diagnose-cli.js",
|
|
40
|
+
"check:schema-sync": "node scripts/check-datasource-test-run-schema-sync.js"
|
|
37
41
|
},
|
|
38
42
|
"keywords": [
|
|
39
43
|
"aifabrix",
|