@aifabrix/builder 2.43.0 → 2.44.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/anchor-docs.mdc +15 -0
- package/.cursor/rules/cli-layout.mdc +75 -0
- package/.cursor/rules/project-rules.mdc +8 -0
- package/.npmrc.token +1 -0
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -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 +301 -0
- package/lib/api/certificates.api.js +62 -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 +11 -3
- package/lib/api/pipeline.api.js +67 -20
- package/lib/api/types/certificates.types.js +48 -0
- 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 +111 -0
- package/lib/api/validation-runner.js +109 -0
- package/lib/app/certification-show-enrich.js +129 -0
- package/lib/app/certification-verify-rows.js +60 -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 +44 -1
- package/lib/app/show.js +93 -9
- package/lib/build/index.js +13 -10
- package/lib/certification/cli-cert-sync-skip.js +21 -0
- package/lib/certification/merge-certification-from-artifact.js +185 -0
- package/lib/certification/post-unified-cert-sync.js +33 -0
- package/lib/certification/sync-after-external-command.js +52 -0
- package/lib/certification/sync-system-certification.js +197 -0
- package/lib/cli/index.js +2 -0
- package/lib/cli/setup-app.help.js +67 -0
- package/lib/cli/setup-app.js +61 -121
- package/lib/cli/setup-app.test-commands.js +195 -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 +84 -23
- package/lib/cli/setup-infra.js +126 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +137 -18
- package/lib/cli/setup-service-user.js +1 -1
- package/lib/cli/setup-utility.js +54 -22
- 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 +32 -11
- 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 +428 -0
- package/lib/commands/datasource-unified-test-cli.options.js +191 -0
- package/lib/commands/datasource-unified-test-e2e-cli-helpers.js +106 -0
- package/lib/commands/datasource-validation-cli.js +143 -0
- package/lib/commands/datasource.js +125 -95
- 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 +149 -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 +189 -67
- package/lib/datasource/resolve-app.js +4 -4
- package/lib/datasource/test-e2e.js +113 -146
- package/lib/datasource/test-integration.js +114 -122
- package/lib/datasource/unified-validation-run-body.js +68 -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 +93 -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 +166 -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 +70 -7
- package/lib/infrastructure/helpers-docker-check.js +67 -0
- package/lib/infrastructure/helpers.js +203 -42
- package/lib/infrastructure/index.js +31 -18
- package/lib/infrastructure/services.js +21 -67
- 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 +203 -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 +226 -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 +77 -17
- 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/configuration-env-resolver.js +11 -8
- package/lib/utils/controller-deployment-outcome.js +68 -0
- package/lib/utils/credential-display.js +2 -2
- package/lib/utils/credential-secrets-env.js +5 -5
- 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-certificate-tty.js +82 -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 +459 -0
- package/lib/utils/datasource-test-run-exit.js +83 -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 +242 -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-system-test-tty-overview.js +120 -0
- package/lib/utils/external-system-system-test-tty.js +417 -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 +148 -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 +112 -0
- package/lib/utils/validation-run-post-retry.js +85 -0
- package/lib/utils/validation-run-request.js +116 -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-external-cert-sync.js +23 -0
- package/lib/validation/validate.js +8 -14
- 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 +9 -4
- package/scripts/check-datasource-test-run-schema-sync.js +34 -0
- package/scripts/diagnose-cli.js +150 -0
- package/scripts/install-local.js +307 -55
- package/scripts/pnpm-global-remove.js +48 -0
- package/templates/README.md +15 -2
- package/templates/applications/dataplane/application.yaml +52 -2
- package/templates/applications/dataplane/env.template +79 -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 +33 -16
- package/templates/infra/servers.json.hbs +3 -1
- 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
|
@@ -11,16 +11,19 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
|
-
* Set of attribute names plus dimension keys valid for field references.
|
|
15
|
-
* Used for primaryKey (schema allows
|
|
14
|
+
* Set of attribute names plus root dimension keys valid for field references.
|
|
15
|
+
* Used for primaryKey (schema allows dimension keys or attributes) and other paths (attributes only).
|
|
16
16
|
*
|
|
17
17
|
* @param {Object} parsed - Parsed datasource object
|
|
18
18
|
* @returns {{ attributes: string[], attributesAndDimensions: Set<string> }}
|
|
19
19
|
*/
|
|
20
20
|
function getNormalizedSets(parsed) {
|
|
21
21
|
const attributes = Object.keys(parsed?.fieldMappings?.attributes ?? {});
|
|
22
|
-
const
|
|
23
|
-
|
|
22
|
+
const rootDims = Object.keys(parsed?.dimensions ?? {}).filter(k => {
|
|
23
|
+
const b = parsed.dimensions[k];
|
|
24
|
+
return b && typeof b === 'object';
|
|
25
|
+
});
|
|
26
|
+
const attributesAndDimensions = new Set([...attributes, ...rootDims]);
|
|
24
27
|
return { attributes, attributesAndDimensions };
|
|
25
28
|
}
|
|
26
29
|
|
|
@@ -31,7 +34,7 @@ function checkPrimaryKey(parsed, attributesAndDimensions, errors) {
|
|
|
31
34
|
primaryKey.forEach((field, i) => {
|
|
32
35
|
if (typeof field === 'string' && field !== '' && !attributesAndDimensions.has(field)) {
|
|
33
36
|
errors.push(
|
|
34
|
-
`primaryKey[${i}]: field '${field}' does not exist in fieldMappings.attributes or
|
|
37
|
+
`primaryKey[${i}]: field '${field}' does not exist in fieldMappings.attributes or root dimensions. Each primaryKey value must reference an attribute or dimension key.`
|
|
35
38
|
);
|
|
36
39
|
}
|
|
37
40
|
});
|
|
@@ -98,7 +101,7 @@ function checkIndexingAndValidation(parsed, normalizedAttributes, errors) {
|
|
|
98
101
|
/**
|
|
99
102
|
* Validates that all field references in indexing, validation, quality,
|
|
100
103
|
* primaryKey, and exposed.profiles exist in fieldMappings.attributes (or
|
|
101
|
-
*
|
|
104
|
+
* root dimension keys for primaryKey per schema). When fieldMappings.attributes is
|
|
102
105
|
* missing or empty, returns no errors (skip check, matching dataplane behavior).
|
|
103
106
|
*
|
|
104
107
|
* @function validateFieldReferences
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared integration app resolution (system key, datasource file lookup).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const path = require('path');
|
|
8
|
+
const fs = require('fs').promises;
|
|
9
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
10
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
11
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @param {string} appKey - Integration app key
|
|
15
|
+
* @returns {Promise<string>} systemKey
|
|
16
|
+
*/
|
|
17
|
+
async function getSystemKeyFromAppKey(appKey) {
|
|
18
|
+
const appPath = getIntegrationPath(appKey);
|
|
19
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
20
|
+
const config = loadConfigFile(configPath);
|
|
21
|
+
if (!config.externalIntegration || !config.externalIntegration.systems || config.externalIntegration.systems.length === 0) {
|
|
22
|
+
throw new Error(`No externalIntegration.systems found in ${configPath}`);
|
|
23
|
+
}
|
|
24
|
+
const systemFile = config.externalIntegration.systems[0];
|
|
25
|
+
const systemPath = path.isAbsolute(systemFile)
|
|
26
|
+
? systemFile
|
|
27
|
+
: path.join(appPath, systemFile);
|
|
28
|
+
const systemContent = await fs.readFile(systemPath, 'utf8');
|
|
29
|
+
const yaml = require('js-yaml');
|
|
30
|
+
const systemConfig = yaml.load(systemContent);
|
|
31
|
+
return systemConfig?.key || path.basename(systemFile, '-system.yaml').replace('-system', '');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Find a datasource filename by matching the key inside the file.
|
|
36
|
+
* @param {string} appPath
|
|
37
|
+
* @param {string} schemaBasePath
|
|
38
|
+
* @param {string[]} datasourceFiles
|
|
39
|
+
* @param {string} datasourceKey
|
|
40
|
+
* @returns {string|null}
|
|
41
|
+
*/
|
|
42
|
+
function findDatasourceFileByKey(appPath, schemaBasePath, datasourceFiles, datasourceKey) {
|
|
43
|
+
const fsSync = require('fs');
|
|
44
|
+
for (const f of datasourceFiles) {
|
|
45
|
+
if (!f || typeof f !== 'string') continue;
|
|
46
|
+
const fullPath = path.isAbsolute(schemaBasePath)
|
|
47
|
+
? path.join(schemaBasePath, f)
|
|
48
|
+
: path.join(appPath, schemaBasePath, f);
|
|
49
|
+
if (!fsSync.existsSync(fullPath)) continue;
|
|
50
|
+
try {
|
|
51
|
+
const parsed = loadConfigFile(fullPath);
|
|
52
|
+
if (parsed && parsed.key === datasourceKey) return f;
|
|
53
|
+
} catch {
|
|
54
|
+
// skip unreadable or invalid files
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
module.exports = {
|
|
61
|
+
getSystemKeyFromAppKey,
|
|
62
|
+
findDatasourceFileByKey
|
|
63
|
+
};
|
package/lib/datasource/list.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
1
2
|
/**
|
|
2
3
|
* Datasource List Command
|
|
3
4
|
*
|
|
4
|
-
* Lists datasources from the dataplane (GET /api/v1/external
|
|
5
|
+
* Lists datasources from the dataplane (GET /api/v1/external).
|
|
5
6
|
* Resolves dataplane URL from the controller, then calls the dataplane list API.
|
|
6
7
|
*
|
|
7
8
|
* @fileoverview Datasource listing for AI Fabrix Builder
|
|
@@ -74,7 +75,7 @@ function extractFromPaginatedFormat(apiResponse) {
|
|
|
74
75
|
* @param {Object} apiResponse - API response object
|
|
75
76
|
*/
|
|
76
77
|
function logInvalidResponseError(apiResponse) {
|
|
77
|
-
logger.error(
|
|
78
|
+
logger.error(formatBlockingError('Invalid response: expected data array or items array'));
|
|
78
79
|
logger.error(chalk.gray('\nAPI response type:'), typeof apiResponse);
|
|
79
80
|
logger.error(chalk.gray('API response:'), JSON.stringify(apiResponse, null, 2));
|
|
80
81
|
}
|
|
@@ -177,7 +178,7 @@ async function getDeviceTokenFromConfig(config) {
|
|
|
177
178
|
*/
|
|
178
179
|
function validateDatasourceListingAuth(token, controllerUrl) {
|
|
179
180
|
if (!token || !controllerUrl || (typeof controllerUrl === 'string' && !controllerUrl.trim())) {
|
|
180
|
-
logger.error(
|
|
181
|
+
logger.error(formatBlockingError('Not logged in. Run: aifabrix login'));
|
|
181
182
|
logger.error(chalk.gray(' Use device code flow: aifabrix login --method device --controller <url>'));
|
|
182
183
|
process.exit(1);
|
|
183
184
|
}
|
|
@@ -215,7 +216,7 @@ function handleDatasourceApiError(response, dataplaneUrl = null) {
|
|
|
215
216
|
function validateControllerUrl(controllerUrl) {
|
|
216
217
|
const trimmed = controllerUrl.trim();
|
|
217
218
|
if (!trimmed) {
|
|
218
|
-
logger.error(
|
|
219
|
+
logger.error(formatBlockingError('Controller URL is empty.'));
|
|
219
220
|
logger.error(chalk.gray(` Controller URL from config: ${JSON.stringify(controllerUrl)}`));
|
|
220
221
|
logger.error(chalk.gray(' Run: aifabrix login --method device --controller <url>'));
|
|
221
222
|
process.exit(1);
|
|
@@ -238,7 +239,7 @@ async function resolveAndValidateDataplaneUrl(controllerUrl, environment, authCo
|
|
|
238
239
|
// discoverDataplaneUrl already logs progress and success messages
|
|
239
240
|
dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
240
241
|
} catch (error) {
|
|
241
|
-
logger.error(
|
|
242
|
+
logger.error(formatBlockingError('Failed to resolve dataplane URL:'), error.message);
|
|
242
243
|
logger.error(chalk.gray('\nThe dataplane URL is automatically discovered from the controller.'));
|
|
243
244
|
logger.error(chalk.gray('If discovery fails, ensure you are logged in and the controller is accessible:'));
|
|
244
245
|
logger.error(chalk.gray(' aifabrix login'));
|
|
@@ -248,7 +249,7 @@ async function resolveAndValidateDataplaneUrl(controllerUrl, environment, authCo
|
|
|
248
249
|
}
|
|
249
250
|
|
|
250
251
|
if (!dataplaneUrl || typeof dataplaneUrl !== 'string' || !dataplaneUrl.trim()) {
|
|
251
|
-
logger.error(
|
|
252
|
+
logger.error(formatBlockingError('Dataplane URL is empty.'));
|
|
252
253
|
logger.error(chalk.gray('The dataplane URL could not be discovered from the controller.'));
|
|
253
254
|
logger.error(chalk.gray('Ensure the dataplane service is registered in the controller.'));
|
|
254
255
|
process.exit(1);
|
|
@@ -295,7 +296,7 @@ async function listDatasources(_options) {
|
|
|
295
296
|
// Resolve dataplane URL first (required for list call)
|
|
296
297
|
const dataplaneUrl = await resolveAndValidateDataplaneUrl(controllerUrl, environment, authConfig);
|
|
297
298
|
|
|
298
|
-
// List datasources from dataplane (GET /api/v1/external
|
|
299
|
+
// List datasources from dataplane (GET /api/v1/external)
|
|
299
300
|
const response = await listDatasourcesFromDataplane(dataplaneUrl, authConfig);
|
|
300
301
|
|
|
301
302
|
if (!response.success || !response.data) {
|
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Log viewer for E2E
|
|
3
|
-
* @fileoverview Read and format
|
|
2
|
+
* Log viewer for E2E, integration, and structural (`datasource test`) debug logs.
|
|
3
|
+
* @fileoverview Read and format saved JSON logs under integration/<app>/logs/
|
|
4
4
|
* @author AI Fabrix Team
|
|
5
5
|
* @version 2.0.0
|
|
6
6
|
*/
|
|
7
7
|
/* eslint-disable max-statements, complexity, max-depth -- Formatter functions; display branches by design */
|
|
8
8
|
|
|
9
9
|
const path = require('path');
|
|
10
|
-
|
|
10
|
+
// Use node:fs so suites that jest.mock('fs') do not break real-disk log resolution (structural tests, CI).
|
|
11
|
+
const fsp = require('node:fs').promises;
|
|
11
12
|
const chalk = require('chalk');
|
|
12
13
|
const logger = require('../utils/logger');
|
|
13
14
|
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
14
15
|
const { getIntegrationPath } = require('../utils/paths');
|
|
16
|
+
const { sectionTitle, headerKeyValue, formatBlockingError, successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
|
|
15
17
|
|
|
16
18
|
/**
|
|
17
19
|
* Get the path to the latest log file in a directory matching a glob-like pattern
|
|
@@ -22,7 +24,7 @@ const { getIntegrationPath } = require('../utils/paths');
|
|
|
22
24
|
async function getLatestLogPath(logsDir, pattern) {
|
|
23
25
|
let entries;
|
|
24
26
|
try {
|
|
25
|
-
entries = await
|
|
27
|
+
entries = await fsp.readdir(logsDir, { withFileTypes: true });
|
|
26
28
|
} catch (err) {
|
|
27
29
|
if (err.code === 'ENOENT') return null;
|
|
28
30
|
throw err;
|
|
@@ -33,12 +35,46 @@ async function getLatestLogPath(logsDir, pattern) {
|
|
|
33
35
|
.map(e => path.join(logsDir, e.name));
|
|
34
36
|
if (files.length === 0) return null;
|
|
35
37
|
const withStats = await Promise.all(
|
|
36
|
-
files.map(async f => ({ path: f, mtime: (await
|
|
38
|
+
files.map(async f => ({ path: f, mtime: (await fsp.stat(f)).mtimeMs }))
|
|
37
39
|
);
|
|
38
40
|
withStats.sort((a, b) => b.mtime - a.mtime);
|
|
39
41
|
return withStats[0].path;
|
|
40
42
|
}
|
|
41
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Structural validation logs use prefix `test-` but must not pick up `test-e2e-` or `test-integration-`.
|
|
46
|
+
* @param {string} name - File name only
|
|
47
|
+
* @returns {boolean}
|
|
48
|
+
*/
|
|
49
|
+
function isStructuralTestLogFileName(name) {
|
|
50
|
+
if (!name || typeof name !== 'string' || !name.endsWith('.json')) return false;
|
|
51
|
+
if (name.startsWith('test-e2e-') || name.startsWith('test-integration-')) return false;
|
|
52
|
+
return name.startsWith('test-');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Latest structural `datasource test --debug` log in logsDir.
|
|
57
|
+
* @param {string} logsDir
|
|
58
|
+
* @returns {Promise<string|null>}
|
|
59
|
+
*/
|
|
60
|
+
async function getLatestStructuralTestLogPath(logsDir) {
|
|
61
|
+
let entries;
|
|
62
|
+
try {
|
|
63
|
+
entries = await fsp.readdir(logsDir, { withFileTypes: true });
|
|
64
|
+
} catch (err) {
|
|
65
|
+
if (err.code === 'ENOENT') return null;
|
|
66
|
+
throw err;
|
|
67
|
+
}
|
|
68
|
+
const names = entries
|
|
69
|
+
.filter(e => e.isFile() && isStructuralTestLogFileName(e.name))
|
|
70
|
+
.map(e => e.name);
|
|
71
|
+
if (names.length === 0) return null;
|
|
72
|
+
// Structural logs embed a sortable timestamp in the filename (`test-YYYY-...Z.json`).
|
|
73
|
+
// Prefer lexicographic ordering to avoid filesystem timestamp resolution issues in CI.
|
|
74
|
+
names.sort((a, b) => b.localeCompare(a));
|
|
75
|
+
return path.join(logsDir, names[0]);
|
|
76
|
+
}
|
|
77
|
+
|
|
42
78
|
/**
|
|
43
79
|
* Truncate string for display
|
|
44
80
|
* @param {string} s - String
|
|
@@ -55,62 +91,88 @@ function truncate(s, maxLen = 60) {
|
|
|
55
91
|
* @param {Object} data - Parsed log JSON (request, response, error)
|
|
56
92
|
* @param {string} [fileName] - Log file name for header
|
|
57
93
|
*/
|
|
58
|
-
function
|
|
59
|
-
logger.log(chalk.blue('\n——— E2E Log') + (fileName ? chalk.gray(` ${fileName}`) : ''));
|
|
94
|
+
function logE2ERequestSection(data) {
|
|
60
95
|
const req = data.request || {};
|
|
61
|
-
logger.log(
|
|
62
|
-
logger.log(
|
|
96
|
+
logger.log('');
|
|
97
|
+
logger.log(sectionTitle('Request:'));
|
|
98
|
+
logger.log(headerKeyValue('sourceIdOrKey:', String(req.sourceIdOrKey ?? '—')));
|
|
63
99
|
if (req.includeDebug !== undefined) logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
|
|
64
100
|
if (req.cleanup !== undefined) logger.log(chalk.gray(` cleanup: ${req.cleanup}`));
|
|
65
|
-
if (req.primaryKeyValue !== undefined)
|
|
101
|
+
if (req.primaryKeyValue !== undefined) {
|
|
102
|
+
logger.log(chalk.gray(` primaryKeyValue: ${truncate(JSON.stringify(req.primaryKeyValue))}`));
|
|
103
|
+
}
|
|
104
|
+
return req;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function logE2EStepsSection(steps) {
|
|
108
|
+
if (!Array.isArray(steps) || steps.length === 0) return;
|
|
109
|
+
logger.log(sectionTitle('Steps:'));
|
|
110
|
+
for (const step of steps) {
|
|
111
|
+
const name = step.name || step.step || 'unknown';
|
|
112
|
+
const ok = step.success !== false && !step.error;
|
|
113
|
+
logger.log(` ${ok ? successGlyph() : failureGlyph()} ${chalk.white(name)}`);
|
|
114
|
+
if (step.error) logger.log(chalk.red(` ${step.error}`));
|
|
115
|
+
if (step.message && ok) logger.log(chalk.gray(` ${step.message}`));
|
|
116
|
+
if ((name === 'sync' || step.step === 'sync') && step.evidence && step.evidence.jobs) {
|
|
117
|
+
for (const job of step.evidence.jobs) {
|
|
118
|
+
const audit = job.audit || {};
|
|
119
|
+
const parts = [];
|
|
120
|
+
if (job.recordsProcessed !== undefined && job.recordsProcessed !== null) parts.push(`${job.recordsProcessed} processed`);
|
|
121
|
+
if (job.totalRecords !== undefined && job.totalRecords !== null) parts.push(`total: ${job.totalRecords}`);
|
|
122
|
+
if (
|
|
123
|
+
(audit.inserted !== undefined && audit.inserted !== null) ||
|
|
124
|
+
(audit.updated !== undefined && audit.updated !== null) ||
|
|
125
|
+
(audit.deleted !== undefined && audit.deleted !== null)
|
|
126
|
+
) {
|
|
127
|
+
parts.push(`(inserted: ${audit.inserted ?? 0}, updated: ${audit.updated ?? 0}, deleted: ${audit.deleted ?? 0})`);
|
|
128
|
+
}
|
|
129
|
+
if (parts.length) logger.log(chalk.gray(` Job: ${parts.join(' ')}`));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function logE2EAuditTraceSection(req, res) {
|
|
136
|
+
if (!res.auditLog || !Array.isArray(res.auditLog) || res.auditLog.length === 0) return;
|
|
137
|
+
logger.log('');
|
|
138
|
+
logger.log(sectionTitle('CIP execution trace(s):'));
|
|
139
|
+
logger.log(chalk.gray(`${res.auditLog.length}`));
|
|
140
|
+
const baseUrl = (req.dataplaneUrl || '').toString().replace(/\/$/, '');
|
|
141
|
+
const sourceIdOrKey = req.sourceIdOrKey || '';
|
|
142
|
+
res.auditLog.slice(0, 3).forEach((trace, i) => {
|
|
143
|
+
const id = trace.executionId || trace.id || trace.traceId;
|
|
144
|
+
if (!id) return;
|
|
145
|
+
const idStr = String(id);
|
|
146
|
+
logger.log(chalk.gray(` ${i + 1}. executionId: ${idStr}`));
|
|
147
|
+
if (baseUrl && sourceIdOrKey) {
|
|
148
|
+
const executionUrl = `${baseUrl}/api/v1/external/${sourceIdOrKey}/executions/${idStr}`;
|
|
149
|
+
logger.log(chalk.gray(` Link: ${executionUrl}`));
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function logE2EResponseSection(req, data) {
|
|
66
155
|
if (data.error) {
|
|
67
|
-
logger.log(
|
|
156
|
+
logger.log(formatBlockingError(`Error: ${data.error}`));
|
|
68
157
|
return;
|
|
69
158
|
}
|
|
70
159
|
const res = data.response || {};
|
|
71
|
-
logger.log(
|
|
160
|
+
logger.log('');
|
|
161
|
+
logger.log(sectionTitle('Response:'));
|
|
72
162
|
logger.log(chalk.gray(` success: ${res.success}`));
|
|
73
163
|
if (res.status) logger.log(chalk.gray(` status: ${res.status}`));
|
|
74
164
|
if (res.error) logger.log(chalk.red(` error: ${res.error}`));
|
|
75
165
|
const steps = res.steps || res.completedActions || [];
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
const audit = job.audit || {};
|
|
87
|
-
const parts = [];
|
|
88
|
-
if (job.recordsProcessed !== undefined && job.recordsProcessed !== null) parts.push(`${job.recordsProcessed} processed`);
|
|
89
|
-
if (job.totalRecords !== undefined && job.totalRecords !== null) parts.push(`total: ${job.totalRecords}`);
|
|
90
|
-
if (audit.inserted !== undefined && audit.inserted !== null || audit.updated !== undefined && audit.updated !== null || audit.deleted !== undefined && audit.deleted !== null) {
|
|
91
|
-
parts.push(`(inserted: ${audit.inserted ?? 0}, updated: ${audit.updated ?? 0}, deleted: ${audit.deleted ?? 0})`);
|
|
92
|
-
}
|
|
93
|
-
if (parts.length) logger.log(chalk.gray(` Job: ${parts.join(' ')}`));
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
if (res.auditLog && Array.isArray(res.auditLog) && res.auditLog.length > 0) {
|
|
99
|
-
logger.log(chalk.cyan('CIP execution trace(s): ') + chalk.gray(`${res.auditLog.length}`));
|
|
100
|
-
const baseUrl = (req.dataplaneUrl || '').toString().replace(/\/$/, '');
|
|
101
|
-
const sourceIdOrKey = req.sourceIdOrKey || '';
|
|
102
|
-
res.auditLog.slice(0, 3).forEach((trace, i) => {
|
|
103
|
-
const id = trace.executionId || trace.id || trace.traceId;
|
|
104
|
-
if (id) {
|
|
105
|
-
const idStr = String(id);
|
|
106
|
-
logger.log(chalk.gray(` ${i + 1}. executionId: ${idStr}`));
|
|
107
|
-
if (baseUrl && sourceIdOrKey) {
|
|
108
|
-
const executionUrl = `${baseUrl}/api/v1/external/${sourceIdOrKey}/executions/${idStr}`;
|
|
109
|
-
logger.log(chalk.gray(` Link: ${executionUrl}`));
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
}
|
|
166
|
+
logE2EStepsSection(steps);
|
|
167
|
+
logE2EAuditTraceSection(req, res);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function formatE2ELog(data, fileName) {
|
|
171
|
+
logger.log('');
|
|
172
|
+
logger.log(sectionTitle('E2E log'));
|
|
173
|
+
if (fileName) logger.log(chalk.gray(fileName));
|
|
174
|
+
const req = logE2ERequestSection(data);
|
|
175
|
+
logE2EResponseSection(req, data);
|
|
114
176
|
}
|
|
115
177
|
|
|
116
178
|
/**
|
|
@@ -119,48 +181,96 @@ function formatE2ELog(data, fileName) {
|
|
|
119
181
|
* @param {string} [fileName] - Log file name for header
|
|
120
182
|
*/
|
|
121
183
|
function formatIntegrationLog(data, fileName) {
|
|
122
|
-
logger.log(
|
|
184
|
+
logger.log('');
|
|
185
|
+
logger.log(sectionTitle('Integration log'));
|
|
186
|
+
if (fileName) logger.log(chalk.gray(fileName));
|
|
123
187
|
const req = data.request || {};
|
|
124
|
-
logger.log(
|
|
188
|
+
logger.log('');
|
|
189
|
+
logger.log(sectionTitle('Request:'));
|
|
125
190
|
logger.log(chalk.gray(` systemKey: ${req.systemKey ?? '—'}, datasourceKey: ${req.datasourceKey ?? '—'}`));
|
|
126
191
|
if (req.includeDebug !== undefined) logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
|
|
127
192
|
if (data.error) {
|
|
128
|
-
logger.log(
|
|
193
|
+
logger.log(formatBlockingError(`Error: ${data.error}`));
|
|
129
194
|
return;
|
|
130
195
|
}
|
|
131
196
|
const res = data.response || {};
|
|
132
|
-
logger.log(
|
|
197
|
+
logger.log('');
|
|
198
|
+
logger.log(sectionTitle('Response:'));
|
|
133
199
|
logger.log(chalk.gray(` success: ${res.success}`));
|
|
134
200
|
if (res.error) logger.log(chalk.red(` error: ${res.error}`));
|
|
135
201
|
const vr = res.validationResults || {};
|
|
136
|
-
logger.log(
|
|
202
|
+
logger.log(sectionTitle('Validation:'));
|
|
137
203
|
logger.log(chalk.gray(` isValid: ${vr.isValid}`));
|
|
138
204
|
if (vr.errors && vr.errors.length) vr.errors.forEach(e => logger.log(chalk.red(` - ${e}`)));
|
|
139
205
|
const fmr = res.fieldMappingResults || {};
|
|
140
206
|
if (Object.keys(fmr).length) {
|
|
141
|
-
logger.log(
|
|
207
|
+
logger.log(sectionTitle('Field mapping:'));
|
|
142
208
|
logger.log(chalk.gray(` mappingCount: ${fmr.mappingCount ?? '—'}`));
|
|
143
209
|
if (fmr.dimensions) logger.log(chalk.gray(` dimensions: ${Object.keys(fmr.dimensions).join(', ')}`));
|
|
144
210
|
}
|
|
145
211
|
const etr = res.endpointTestResults || {};
|
|
146
212
|
if (Object.keys(etr).length) {
|
|
147
|
-
logger.log(
|
|
213
|
+
logger.log(sectionTitle('Endpoint:'));
|
|
148
214
|
logger.log(chalk.gray(` endpointConfigured: ${etr.endpointConfigured}`));
|
|
149
215
|
}
|
|
150
216
|
if (res.normalizedOutput || res.normalizedMetadata) {
|
|
151
217
|
const out = res.normalizedOutput || res.normalizedMetadata;
|
|
152
218
|
const keys = typeof out === 'object' && out !== null ? Object.keys(out) : [];
|
|
153
|
-
logger.log(
|
|
219
|
+
logger.log(sectionTitle('Normalized output:') + ' ' + chalk.gray(keys.length ? `${keys.length} fields` : '—'));
|
|
154
220
|
}
|
|
155
221
|
}
|
|
156
222
|
|
|
223
|
+
function structuralEnvelopeStatusLine(status) {
|
|
224
|
+
if (status === 'ok' || status === 'skipped') return `${successGlyph()} ${chalk.gray('status:')} ${status}`;
|
|
225
|
+
if (status === 'warn') return `${chalk.yellow('⚠')} ${chalk.gray('status:')} ${status}`;
|
|
226
|
+
if (status === 'fail') return `${failureGlyph()} ${chalk.gray('status:')} ${status}`;
|
|
227
|
+
return `${chalk.gray('?')} ${chalk.gray('status:')} ${status ?? '—'}`;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Format structural validation log (`datasource test --debug` → `test-*.json`).
|
|
232
|
+
* @param {Object} data - Parsed JSON { request, response?, error? }
|
|
233
|
+
* @param {string} [fileName] - Log file name for header
|
|
234
|
+
*/
|
|
235
|
+
function formatStructuralTestLog(data, fileName) {
|
|
236
|
+
logger.log('');
|
|
237
|
+
logger.log(sectionTitle('Structural validation log'));
|
|
238
|
+
if (fileName) logger.log(chalk.gray(fileName));
|
|
239
|
+
const req = data.request || {};
|
|
240
|
+
logger.log('');
|
|
241
|
+
logger.log(sectionTitle('Request:'));
|
|
242
|
+
logger.log(headerKeyValue('datasourceKey:', String(req.datasourceKey ?? '—')));
|
|
243
|
+
if (req.runType) logger.log(headerKeyValue('runType:', String(req.runType)));
|
|
244
|
+
if (req.includeDebug !== undefined) {
|
|
245
|
+
logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
|
|
246
|
+
}
|
|
247
|
+
if (data.error) {
|
|
248
|
+
logger.log('');
|
|
249
|
+
logger.log(formatBlockingError(`Error: ${data.error}`));
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
const res = data.response || {};
|
|
253
|
+
logger.log('');
|
|
254
|
+
logger.log(sectionTitle('Response (envelope):'));
|
|
255
|
+
logger.log(` ${structuralEnvelopeStatusLine(res.status)}`);
|
|
256
|
+
if (res.reportCompleteness) {
|
|
257
|
+
logger.log(chalk.gray(` reportCompleteness: ${res.reportCompleteness}`));
|
|
258
|
+
}
|
|
259
|
+
if (res.runId) logger.log(chalk.gray(` runId: ${res.runId}`));
|
|
260
|
+
if (res.systemKey) logger.log(chalk.gray(` systemKey: ${res.systemKey}`));
|
|
261
|
+
}
|
|
262
|
+
|
|
157
263
|
/**
|
|
158
264
|
* Format log content by type
|
|
159
265
|
* @param {Object} parsed - Parsed JSON log
|
|
160
|
-
* @param {'test-e2e'|'test-integration'} logType - Log type
|
|
266
|
+
* @param {'test'|'test-e2e'|'test-integration'} logType - Log type
|
|
161
267
|
* @param {string} [fileName] - File name for header
|
|
162
268
|
*/
|
|
163
269
|
function formatLogContent(parsed, logType, fileName) {
|
|
270
|
+
if (logType === 'test') {
|
|
271
|
+
formatStructuralTestLog(parsed, fileName);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
164
274
|
if (logType === 'test-e2e') {
|
|
165
275
|
formatE2ELog(parsed, fileName);
|
|
166
276
|
} else {
|
|
@@ -175,7 +285,7 @@ function formatLogContent(parsed, logType, fileName) {
|
|
|
175
285
|
* @param {Object} options - Options
|
|
176
286
|
* @param {string} [options.app] - App key (optional, resolved from key if omitted)
|
|
177
287
|
* @param {string} [options.file] - Path to log file (overrides app resolution)
|
|
178
|
-
* @param {'test-e2e'|'test-integration'} options.logType - Log type
|
|
288
|
+
* @param {'test'|'test-e2e'|'test-integration'} options.logType - Log type
|
|
179
289
|
* @throws {Error} When file not found or invalid JSON
|
|
180
290
|
*/
|
|
181
291
|
/* eslint-disable-next-line max-statements -- Resolve path, read, parse, format */
|
|
@@ -193,16 +303,25 @@ async function runLogViewer(datasourceKey, options) {
|
|
|
193
303
|
const { appKey } = await resolveAppKeyForDatasource(datasourceKey.trim(), app);
|
|
194
304
|
const appPath = getIntegrationPath(appKey);
|
|
195
305
|
const logsDir = path.join(appPath, 'logs');
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
306
|
+
if (logType === 'test') {
|
|
307
|
+
logPath = await getLatestStructuralTestLogPath(logsDir);
|
|
308
|
+
if (!logPath) {
|
|
309
|
+
throw new Error(
|
|
310
|
+
`No structural validation log found in ${logsDir}. Run: aifabrix datasource test <key> --debug`
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
} else {
|
|
314
|
+
const pattern = logType === 'test-e2e' ? 'test-e2e-' : 'test-integration-';
|
|
315
|
+
logPath = await getLatestLogPath(logsDir, pattern);
|
|
316
|
+
if (!logPath) {
|
|
317
|
+
throw new Error(
|
|
318
|
+
`No ${logType} log found in ${logsDir}. Run the test with --debug first.`
|
|
319
|
+
);
|
|
320
|
+
}
|
|
202
321
|
}
|
|
203
322
|
fileName = path.basename(logPath);
|
|
204
323
|
}
|
|
205
|
-
const content = await
|
|
324
|
+
const content = await fsp.readFile(logPath, 'utf8');
|
|
206
325
|
let parsed;
|
|
207
326
|
try {
|
|
208
327
|
parsed = JSON.parse(content);
|
|
@@ -214,8 +333,11 @@ async function runLogViewer(datasourceKey, options) {
|
|
|
214
333
|
|
|
215
334
|
module.exports = {
|
|
216
335
|
getLatestLogPath,
|
|
336
|
+
getLatestStructuralTestLogPath,
|
|
337
|
+
isStructuralTestLogFileName,
|
|
217
338
|
formatLogContent,
|
|
218
339
|
formatE2ELog,
|
|
219
340
|
formatIntegrationLog,
|
|
341
|
+
formatStructuralTestLog,
|
|
220
342
|
runLogViewer
|
|
221
343
|
};
|
|
@@ -17,7 +17,7 @@ const { loadConfigFile } = require('../utils/config-format');
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* For one app, check if any of its datasource files has the given key
|
|
20
|
-
* @param {string} appKey - Integration
|
|
20
|
+
* @param {string} appKey - Integration folder name (system key context)
|
|
21
21
|
* @param {string} datasourceKey - Datasource key to match
|
|
22
22
|
* @returns {boolean} True if this app has a datasource with that key
|
|
23
23
|
*/
|
|
@@ -52,7 +52,7 @@ function appHasDatasourceKey(appKey, datasourceKey) {
|
|
|
52
52
|
* Resolve app key for a datasource: explicit --app, cwd, scan by key, or parse key convention.
|
|
53
53
|
* @async
|
|
54
54
|
* @param {string} datasourceKey - Datasource key (e.g. hubspot-test-company)
|
|
55
|
-
* @param {string} [explicitApp] - Explicit
|
|
55
|
+
* @param {string} [explicitApp] - Explicit integration folder from --app
|
|
56
56
|
* @returns {Promise<{appKey: string}>} Resolved app key
|
|
57
57
|
* @throws {Error} When app cannot be determined or multiple apps match
|
|
58
58
|
*/
|
|
@@ -85,7 +85,7 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
|
|
|
85
85
|
}
|
|
86
86
|
if (matches.length > 1) {
|
|
87
87
|
throw new Error(
|
|
88
|
-
`More than one app has this datasource; add --app <
|
|
88
|
+
`More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
|
|
89
89
|
);
|
|
90
90
|
}
|
|
91
91
|
|
|
@@ -99,7 +99,7 @@ async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
|
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
throw new Error(
|
|
102
|
-
'Could not determine app context. Use --app <
|
|
102
|
+
'Could not determine app context. Use --app <app> or run from integration/<systemKey>/ directory.'
|
|
103
103
|
);
|
|
104
104
|
}
|
|
105
105
|
|