@aifabrix/builder 2.42.1 → 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 +2 -2
- 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 +157 -0
- package/integration/{hubspot → hubspot-test}/application.json +6 -6
- package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
- package/integration/hubspot-test/env.template +4 -0
- package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
- package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
- package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
- package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
- package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
- package/integration/hubspot-test/rbac.json +166 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
- package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
- package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
- package/integration/{hubspot → hubspot-test}/test.js +137 -102
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
- package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
- 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/service-users.api.js +111 -2
- package/lib/api/types/dev.types.js +4 -3
- package/lib/api/types/pipeline.types.js +8 -5
- package/lib/api/types/service-users.types.js +41 -0
- 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 +19 -8
- package/lib/app/rotate-secret.js +17 -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 +59 -123
- package/lib/cli/setup-app.test-commands.js +179 -0
- package/lib/cli/setup-auth.js +36 -14
- 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 +190 -103
- package/lib/cli/setup-environment.js +11 -20
- package/lib/cli/setup-external-system.js +62 -22
- package/lib/cli/setup-infra.js +139 -47
- package/lib/cli/setup-parameters.js +32 -0
- package/lib/cli/setup-secrets.js +147 -10
- package/lib/cli/setup-service-user.js +146 -20
- package/lib/cli/setup-utility.js +47 -19
- 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 +10 -14
- 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 +123 -71
- 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 +16 -10
- package/lib/commands/repair-rbac.js +25 -19
- package/lib/commands/repair.js +116 -32
- package/lib/commands/secrets-list.js +23 -12
- package/lib/commands/secrets-remove-all.js +220 -0
- package/lib/commands/secrets-remove.js +22 -13
- package/lib/commands/secrets-set.js +21 -12
- package/lib/commands/secrets-validate.js +20 -7
- package/lib/commands/secure.js +10 -9
- package/lib/commands/service-user.js +243 -13
- package/lib/commands/test-e2e-external.js +27 -1
- package/lib/commands/up-common.js +28 -2
- package/lib/commands/up-dataplane.js +31 -18
- 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 +16 -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 +59 -58
- package/lib/core/diff.js +3 -2
- package/lib/core/ensure-encryption-key.js +2 -4
- 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 +228 -42
- package/lib/core/templates-env.js +4 -3
- package/lib/core/templates.js +1 -1
- package/lib/datasource/abac-validator.js +148 -0
- package/lib/datasource/deploy.js +75 -53
- package/lib/datasource/field-reference-validator.js +77 -36
- package/lib/datasource/integration-context.js +63 -0
- package/lib/datasource/list.js +8 -7
- package/lib/datasource/log-viewer.js +252 -0
- package/lib/datasource/resolve-app.js +109 -0
- package/lib/datasource/test-e2e.js +95 -155
- package/lib/datasource/test-integration.js +121 -109
- 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 +162 -15
- 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 +104 -14
- 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-controller-manifest.js +3 -3
- package/lib/generator/external.js +23 -11
- package/lib/generator/helpers.js +71 -12
- package/lib/generator/index.js +8 -4
- package/lib/generator/split-readme.js +12 -7
- package/lib/generator/split-variables.js +2 -1
- package/lib/generator/split.js +46 -11
- package/lib/generator/wizard-readme.js +3 -3
- package/lib/generator/wizard.js +16 -13
- package/lib/infrastructure/compose.js +60 -6
- package/lib/infrastructure/helpers.js +238 -51
- package/lib/infrastructure/index.js +64 -37
- 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 +1201 -433
- 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-config-resolver.js +23 -1
- 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 +209 -6
- 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/credential-secrets-env.js +16 -1
- 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 +42 -11
- package/lib/utils/env-template.js +2 -2
- package/lib/utils/environment-scoped-resources.js +144 -0
- package/lib/utils/error-formatter.js +125 -9
- 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-env-template.js +180 -0
- package/lib/utils/external-readme.js +8 -1
- package/lib/utils/external-system-display.js +277 -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 +32 -14
- package/lib/utils/health-check-url.js +119 -0
- package/lib/utils/health-check.js +59 -25
- package/lib/utils/help-builder.js +14 -13
- 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 +29 -7
- package/lib/utils/paths.js +136 -48
- 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 +171 -51
- package/lib/utils/secrets-helpers.js +70 -59
- package/lib/utils/secrets-kv-scope.js +60 -0
- package/lib/utils/secrets-utils.js +35 -37
- package/lib/utils/secrets-validation.js +3 -1
- package/lib/utils/secrets-yaml-preserve.js +109 -0
- package/lib/utils/secure-file-permissions.js +91 -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 +37 -5
- 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 +78 -1
- package/lib/validation/datasource-warnings.js +56 -0
- package/lib/validation/env-template-auth.js +50 -2
- package/lib/validation/external-manifest-validator.js +35 -7
- package/lib/validation/validate-display.js +37 -31
- package/lib/validation/validate.js +9 -10
- package/lib/validation/validator-unresolved-placeholders.js +98 -0
- package/lib/validation/validator.js +32 -78
- package/lib/validation/wizard-config-validator.js +2 -1
- package/package.json +11 -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 +80 -18
- 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 +55 -14
- 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/env.template.hbs +22 -0
- 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/integration/hubspot/README.md +0 -102
- package/integration/hubspot/env.template +0 -4
- package/integration/hubspot/hubspot-datasource-company.json +0 -541
- package/integration/hubspot/hubspot-datasource-contact.json +0 -639
- package/integration/hubspot/hubspot-datasource-deal.json +0 -588
- package/integration/hubspot/hubspot-datasource-users.json +0 -116
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
- package/lib/api/external-test.api.js +0 -111
- package/lib/schema/env-config.yaml +0 -43
- /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
- /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Log viewer for E2E and integration test logs - format and display JSON logs.
|
|
3
|
+
* @fileoverview Read and format test-e2e / test-integration debug logs for terminal
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
/* eslint-disable max-statements, complexity, max-depth -- Formatter functions; display branches by design */
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs').promises;
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
const logger = require('../utils/logger');
|
|
13
|
+
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
14
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
15
|
+
const { sectionTitle, headerKeyValue, formatBlockingError, successGlyph, failureGlyph } = require('../utils/cli-test-layout-chalk');
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get the path to the latest log file in a directory matching a glob-like pattern
|
|
19
|
+
* @param {string} logsDir - Directory containing log files
|
|
20
|
+
* @param {string} pattern - Prefix pattern (e.g. 'test-e2e' matches test-e2e-*.json)
|
|
21
|
+
* @returns {Promise<string|null>} Full path to latest file or null if none
|
|
22
|
+
*/
|
|
23
|
+
async function getLatestLogPath(logsDir, pattern) {
|
|
24
|
+
let entries;
|
|
25
|
+
try {
|
|
26
|
+
entries = await fs.readdir(logsDir, { withFileTypes: true });
|
|
27
|
+
} catch (err) {
|
|
28
|
+
if (err.code === 'ENOENT') return null;
|
|
29
|
+
throw err;
|
|
30
|
+
}
|
|
31
|
+
const prefix = pattern.replace(/\*$/, '');
|
|
32
|
+
const files = entries
|
|
33
|
+
.filter(e => e.isFile() && e.name.startsWith(prefix) && e.name.endsWith('.json'))
|
|
34
|
+
.map(e => path.join(logsDir, e.name));
|
|
35
|
+
if (files.length === 0) return null;
|
|
36
|
+
const withStats = await Promise.all(
|
|
37
|
+
files.map(async f => ({ path: f, mtime: (await fs.stat(f)).mtimeMs }))
|
|
38
|
+
);
|
|
39
|
+
withStats.sort((a, b) => b.mtime - a.mtime);
|
|
40
|
+
return withStats[0].path;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Truncate string for display
|
|
45
|
+
* @param {string} s - String
|
|
46
|
+
* @param {number} maxLen - Max length
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/
|
|
49
|
+
function truncate(s, maxLen = 60) {
|
|
50
|
+
if (typeof s !== 'string') return String(s);
|
|
51
|
+
return s.length <= maxLen ? s : `${s.slice(0, maxLen - 1)}…`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Format E2E log content for terminal display
|
|
56
|
+
* @param {Object} data - Parsed log JSON (request, response, error)
|
|
57
|
+
* @param {string} [fileName] - Log file name for header
|
|
58
|
+
*/
|
|
59
|
+
function logE2ERequestSection(data) {
|
|
60
|
+
const req = data.request || {};
|
|
61
|
+
logger.log('');
|
|
62
|
+
logger.log(sectionTitle('Request:'));
|
|
63
|
+
logger.log(headerKeyValue('sourceIdOrKey:', String(req.sourceIdOrKey ?? '—')));
|
|
64
|
+
if (req.includeDebug !== undefined) logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
|
|
65
|
+
if (req.cleanup !== undefined) logger.log(chalk.gray(` cleanup: ${req.cleanup}`));
|
|
66
|
+
if (req.primaryKeyValue !== undefined) {
|
|
67
|
+
logger.log(chalk.gray(` primaryKeyValue: ${truncate(JSON.stringify(req.primaryKeyValue))}`));
|
|
68
|
+
}
|
|
69
|
+
return req;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function logE2EStepsSection(steps) {
|
|
73
|
+
if (!Array.isArray(steps) || steps.length === 0) return;
|
|
74
|
+
logger.log(sectionTitle('Steps:'));
|
|
75
|
+
for (const step of steps) {
|
|
76
|
+
const name = step.name || step.step || 'unknown';
|
|
77
|
+
const ok = step.success !== false && !step.error;
|
|
78
|
+
logger.log(` ${ok ? successGlyph() : failureGlyph()} ${chalk.white(name)}`);
|
|
79
|
+
if (step.error) logger.log(chalk.red(` ${step.error}`));
|
|
80
|
+
if (step.message && ok) logger.log(chalk.gray(` ${step.message}`));
|
|
81
|
+
if ((name === 'sync' || step.step === 'sync') && step.evidence && step.evidence.jobs) {
|
|
82
|
+
for (const job of step.evidence.jobs) {
|
|
83
|
+
const audit = job.audit || {};
|
|
84
|
+
const parts = [];
|
|
85
|
+
if (job.recordsProcessed !== undefined && job.recordsProcessed !== null) parts.push(`${job.recordsProcessed} processed`);
|
|
86
|
+
if (job.totalRecords !== undefined && job.totalRecords !== null) parts.push(`total: ${job.totalRecords}`);
|
|
87
|
+
if (
|
|
88
|
+
(audit.inserted !== undefined && audit.inserted !== null) ||
|
|
89
|
+
(audit.updated !== undefined && audit.updated !== null) ||
|
|
90
|
+
(audit.deleted !== undefined && audit.deleted !== null)
|
|
91
|
+
) {
|
|
92
|
+
parts.push(`(inserted: ${audit.inserted ?? 0}, updated: ${audit.updated ?? 0}, deleted: ${audit.deleted ?? 0})`);
|
|
93
|
+
}
|
|
94
|
+
if (parts.length) logger.log(chalk.gray(` Job: ${parts.join(' ')}`));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function logE2EAuditTraceSection(req, res) {
|
|
101
|
+
if (!res.auditLog || !Array.isArray(res.auditLog) || res.auditLog.length === 0) return;
|
|
102
|
+
logger.log('');
|
|
103
|
+
logger.log(sectionTitle('CIP execution trace(s):'));
|
|
104
|
+
logger.log(chalk.gray(`${res.auditLog.length}`));
|
|
105
|
+
const baseUrl = (req.dataplaneUrl || '').toString().replace(/\/$/, '');
|
|
106
|
+
const sourceIdOrKey = req.sourceIdOrKey || '';
|
|
107
|
+
res.auditLog.slice(0, 3).forEach((trace, i) => {
|
|
108
|
+
const id = trace.executionId || trace.id || trace.traceId;
|
|
109
|
+
if (!id) return;
|
|
110
|
+
const idStr = String(id);
|
|
111
|
+
logger.log(chalk.gray(` ${i + 1}. executionId: ${idStr}`));
|
|
112
|
+
if (baseUrl && sourceIdOrKey) {
|
|
113
|
+
const executionUrl = `${baseUrl}/api/v1/external/${sourceIdOrKey}/executions/${idStr}`;
|
|
114
|
+
logger.log(chalk.gray(` Link: ${executionUrl}`));
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function logE2EResponseSection(req, data) {
|
|
120
|
+
if (data.error) {
|
|
121
|
+
logger.log(formatBlockingError(`Error: ${data.error}`));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const res = data.response || {};
|
|
125
|
+
logger.log('');
|
|
126
|
+
logger.log(sectionTitle('Response:'));
|
|
127
|
+
logger.log(chalk.gray(` success: ${res.success}`));
|
|
128
|
+
if (res.status) logger.log(chalk.gray(` status: ${res.status}`));
|
|
129
|
+
if (res.error) logger.log(chalk.red(` error: ${res.error}`));
|
|
130
|
+
const steps = res.steps || res.completedActions || [];
|
|
131
|
+
logE2EStepsSection(steps);
|
|
132
|
+
logE2EAuditTraceSection(req, res);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function formatE2ELog(data, fileName) {
|
|
136
|
+
logger.log('');
|
|
137
|
+
logger.log(sectionTitle('E2E log'));
|
|
138
|
+
if (fileName) logger.log(chalk.gray(fileName));
|
|
139
|
+
const req = logE2ERequestSection(data);
|
|
140
|
+
logE2EResponseSection(req, data);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Format integration test log content for terminal display
|
|
145
|
+
* @param {Object} data - Parsed log JSON
|
|
146
|
+
* @param {string} [fileName] - Log file name for header
|
|
147
|
+
*/
|
|
148
|
+
function formatIntegrationLog(data, fileName) {
|
|
149
|
+
logger.log('');
|
|
150
|
+
logger.log(sectionTitle('Integration log'));
|
|
151
|
+
if (fileName) logger.log(chalk.gray(fileName));
|
|
152
|
+
const req = data.request || {};
|
|
153
|
+
logger.log('');
|
|
154
|
+
logger.log(sectionTitle('Request:'));
|
|
155
|
+
logger.log(chalk.gray(` systemKey: ${req.systemKey ?? '—'}, datasourceKey: ${req.datasourceKey ?? '—'}`));
|
|
156
|
+
if (req.includeDebug !== undefined) logger.log(chalk.gray(` includeDebug: ${req.includeDebug}`));
|
|
157
|
+
if (data.error) {
|
|
158
|
+
logger.log(formatBlockingError(`Error: ${data.error}`));
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const res = data.response || {};
|
|
162
|
+
logger.log('');
|
|
163
|
+
logger.log(sectionTitle('Response:'));
|
|
164
|
+
logger.log(chalk.gray(` success: ${res.success}`));
|
|
165
|
+
if (res.error) logger.log(chalk.red(` error: ${res.error}`));
|
|
166
|
+
const vr = res.validationResults || {};
|
|
167
|
+
logger.log(sectionTitle('Validation:'));
|
|
168
|
+
logger.log(chalk.gray(` isValid: ${vr.isValid}`));
|
|
169
|
+
if (vr.errors && vr.errors.length) vr.errors.forEach(e => logger.log(chalk.red(` - ${e}`)));
|
|
170
|
+
const fmr = res.fieldMappingResults || {};
|
|
171
|
+
if (Object.keys(fmr).length) {
|
|
172
|
+
logger.log(sectionTitle('Field mapping:'));
|
|
173
|
+
logger.log(chalk.gray(` mappingCount: ${fmr.mappingCount ?? '—'}`));
|
|
174
|
+
if (fmr.dimensions) logger.log(chalk.gray(` dimensions: ${Object.keys(fmr.dimensions).join(', ')}`));
|
|
175
|
+
}
|
|
176
|
+
const etr = res.endpointTestResults || {};
|
|
177
|
+
if (Object.keys(etr).length) {
|
|
178
|
+
logger.log(sectionTitle('Endpoint:'));
|
|
179
|
+
logger.log(chalk.gray(` endpointConfigured: ${etr.endpointConfigured}`));
|
|
180
|
+
}
|
|
181
|
+
if (res.normalizedOutput || res.normalizedMetadata) {
|
|
182
|
+
const out = res.normalizedOutput || res.normalizedMetadata;
|
|
183
|
+
const keys = typeof out === 'object' && out !== null ? Object.keys(out) : [];
|
|
184
|
+
logger.log(sectionTitle('Normalized output:') + ' ' + chalk.gray(keys.length ? `${keys.length} fields` : '—'));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Format log content by type
|
|
190
|
+
* @param {Object} parsed - Parsed JSON log
|
|
191
|
+
* @param {'test-e2e'|'test-integration'} logType - Log type
|
|
192
|
+
* @param {string} [fileName] - File name for header
|
|
193
|
+
*/
|
|
194
|
+
function formatLogContent(parsed, logType, fileName) {
|
|
195
|
+
if (logType === 'test-e2e') {
|
|
196
|
+
formatE2ELog(parsed, fileName);
|
|
197
|
+
} else {
|
|
198
|
+
formatIntegrationLog(parsed, fileName);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Run log viewer: resolve log file, read, parse, format and print
|
|
204
|
+
* @async
|
|
205
|
+
* @param {string} datasourceKey - Datasource key (used when no --file)
|
|
206
|
+
* @param {Object} options - Options
|
|
207
|
+
* @param {string} [options.app] - App key (optional, resolved from key if omitted)
|
|
208
|
+
* @param {string} [options.file] - Path to log file (overrides app resolution)
|
|
209
|
+
* @param {'test-e2e'|'test-integration'} options.logType - Log type
|
|
210
|
+
* @throws {Error} When file not found or invalid JSON
|
|
211
|
+
*/
|
|
212
|
+
/* eslint-disable-next-line max-statements -- Resolve path, read, parse, format */
|
|
213
|
+
async function runLogViewer(datasourceKey, options) {
|
|
214
|
+
const { app, file, logType } = options;
|
|
215
|
+
let logPath;
|
|
216
|
+
let fileName;
|
|
217
|
+
if (file && typeof file === 'string' && file.trim()) {
|
|
218
|
+
logPath = path.isAbsolute(file) ? file : path.resolve(process.cwd(), file.trim());
|
|
219
|
+
fileName = path.basename(logPath);
|
|
220
|
+
} else {
|
|
221
|
+
if (!datasourceKey || typeof datasourceKey !== 'string') {
|
|
222
|
+
throw new Error('Datasource key is required when --file is not provided');
|
|
223
|
+
}
|
|
224
|
+
const { appKey } = await resolveAppKeyForDatasource(datasourceKey.trim(), app);
|
|
225
|
+
const appPath = getIntegrationPath(appKey);
|
|
226
|
+
const logsDir = path.join(appPath, 'logs');
|
|
227
|
+
const pattern = logType === 'test-e2e' ? 'test-e2e-' : 'test-integration-';
|
|
228
|
+
logPath = await getLatestLogPath(logsDir, pattern);
|
|
229
|
+
if (!logPath) {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`No ${logType} log found in ${logsDir}. Run the test with --debug first.`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
fileName = path.basename(logPath);
|
|
235
|
+
}
|
|
236
|
+
const content = await fs.readFile(logPath, 'utf8');
|
|
237
|
+
let parsed;
|
|
238
|
+
try {
|
|
239
|
+
parsed = JSON.parse(content);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
throw new Error(`Invalid JSON in ${logPath}: ${err.message}`);
|
|
242
|
+
}
|
|
243
|
+
formatLogContent(parsed, logType, fileName);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
module.exports = {
|
|
247
|
+
getLatestLogPath,
|
|
248
|
+
formatLogContent,
|
|
249
|
+
formatE2ELog,
|
|
250
|
+
formatIntegrationLog,
|
|
251
|
+
runLogViewer
|
|
252
|
+
};
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve app key for datasource commands from explicit --app, cwd, scan, or key parse.
|
|
3
|
+
* @fileoverview App resolution for datasource test-e2e, test-integration, log-e2e, log-integration
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const path = require('path');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const {
|
|
11
|
+
getIntegrationPath,
|
|
12
|
+
listIntegrationAppNames,
|
|
13
|
+
resolveIntegrationAppKeyFromCwd
|
|
14
|
+
} = require('../utils/paths');
|
|
15
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
16
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* For one app, check if any of its datasource files has the given key
|
|
20
|
+
* @param {string} appKey - Integration folder name (system key context)
|
|
21
|
+
* @param {string} datasourceKey - Datasource key to match
|
|
22
|
+
* @returns {boolean} True if this app has a datasource with that key
|
|
23
|
+
*/
|
|
24
|
+
function appHasDatasourceKey(appKey, datasourceKey) {
|
|
25
|
+
const appPath = getIntegrationPath(appKey);
|
|
26
|
+
let config;
|
|
27
|
+
try {
|
|
28
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
29
|
+
config = loadConfigFile(configPath);
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
const schemaBasePath = config.externalIntegration?.schemaBasePath || './';
|
|
34
|
+
const datasourceFiles = config.externalIntegration?.dataSources || [];
|
|
35
|
+
for (const f of datasourceFiles) {
|
|
36
|
+
if (!f || typeof f !== 'string') continue;
|
|
37
|
+
const fullPath = path.isAbsolute(schemaBasePath)
|
|
38
|
+
? path.join(schemaBasePath, f)
|
|
39
|
+
: path.join(appPath, schemaBasePath, f);
|
|
40
|
+
if (!fs.existsSync(fullPath)) continue;
|
|
41
|
+
try {
|
|
42
|
+
const parsed = loadConfigFile(fullPath);
|
|
43
|
+
if (parsed && parsed.key === datasourceKey) return true;
|
|
44
|
+
} catch {
|
|
45
|
+
// skip unreadable or invalid files
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Resolve app key for a datasource: explicit --app, cwd, scan by key, or parse key convention.
|
|
53
|
+
* @async
|
|
54
|
+
* @param {string} datasourceKey - Datasource key (e.g. hubspot-test-company)
|
|
55
|
+
* @param {string} [explicitApp] - Explicit integration folder from --app
|
|
56
|
+
* @returns {Promise<{appKey: string}>} Resolved app key
|
|
57
|
+
* @throws {Error} When app cannot be determined or multiple apps match
|
|
58
|
+
*/
|
|
59
|
+
/* eslint-disable-next-line max-statements -- Resolution order: explicit, cwd, scan, parse */
|
|
60
|
+
async function resolveAppKeyForDatasource(datasourceKey, explicitApp) {
|
|
61
|
+
if (!datasourceKey || typeof datasourceKey !== 'string') {
|
|
62
|
+
throw new Error('Datasource key is required');
|
|
63
|
+
}
|
|
64
|
+
const key = String(datasourceKey).trim();
|
|
65
|
+
if (key.length === 0) {
|
|
66
|
+
throw new Error('Datasource key cannot be empty');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (explicitApp && typeof explicitApp === 'string' && explicitApp.trim()) {
|
|
70
|
+
const appPath = getIntegrationPath(explicitApp.trim());
|
|
71
|
+
if (fs.existsSync(appPath)) {
|
|
72
|
+
return { appKey: explicitApp.trim() };
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const fromCwd = resolveIntegrationAppKeyFromCwd();
|
|
77
|
+
if (fromCwd && appHasDatasourceKey(fromCwd, key)) {
|
|
78
|
+
return { appKey: fromCwd };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const appNames = listIntegrationAppNames();
|
|
82
|
+
const matches = appNames.filter(appName => appHasDatasourceKey(appName, key));
|
|
83
|
+
if (matches.length === 1) {
|
|
84
|
+
return { appKey: matches[0] };
|
|
85
|
+
}
|
|
86
|
+
if (matches.length > 1) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
`More than one app has this datasource; add --app <app>. Apps: ${matches.join(', ')}`
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const segments = key.split('-');
|
|
93
|
+
if (segments.length >= 2) {
|
|
94
|
+
const candidate = segments.slice(0, -1).join('-');
|
|
95
|
+
const candidatePath = getIntegrationPath(candidate);
|
|
96
|
+
if (fs.existsSync(candidatePath) && appHasDatasourceKey(candidate, key)) {
|
|
97
|
+
return { appKey: candidate };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
throw new Error(
|
|
102
|
+
'Could not determine app context. Use --app <app> or run from integration/<systemKey>/ directory.'
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
module.exports = {
|
|
107
|
+
resolveAppKeyForDatasource,
|
|
108
|
+
appHasDatasourceKey
|
|
109
|
+
};
|
|
@@ -1,43 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Datasource E2E test
|
|
3
|
-
* @fileoverview Datasource E2E
|
|
2
|
+
* Datasource E2E test — unified POST /api/v1/validation/run (runType=e2e).
|
|
3
|
+
* @fileoverview Datasource E2E via DatasourceTestRun envelope
|
|
4
4
|
* @author AI Fabrix Team
|
|
5
5
|
* @version 2.0.0
|
|
6
6
|
*/
|
|
7
|
-
/* eslint-disable max-statements -- Auth setup, API call, polling, debug log */
|
|
8
7
|
|
|
9
8
|
const path = require('path');
|
|
10
9
|
const fs = require('fs').promises;
|
|
11
10
|
const chalk = require('chalk');
|
|
12
11
|
const logger = require('../utils/logger');
|
|
13
|
-
const { getIntegrationPath
|
|
14
|
-
const {
|
|
15
|
-
const {
|
|
16
|
-
const {
|
|
17
|
-
const {
|
|
12
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
13
|
+
const { resolveAppKeyForDatasource } = require('./resolve-app');
|
|
14
|
+
const { infoLine } = require('../utils/cli-test-layout-chalk');
|
|
15
|
+
const { runUnifiedDatasourceValidation } = require('./unified-validation-run');
|
|
16
|
+
const { includeDebugForRequest } = require('../utils/validation-run-request');
|
|
17
|
+
const { e2eShapeFromEnvelope } = require('../utils/datasource-test-run-legacy-adapter');
|
|
18
18
|
const { writeTestLog } = require('../utils/test-log-writer');
|
|
19
19
|
|
|
20
|
-
const DEFAULT_POLL_INTERVAL_MS = 2500;
|
|
21
20
|
const DEFAULT_POLL_TIMEOUT_MS = 15 * 60 * 1000;
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
*/
|
|
28
|
-
function resolveAppKey(appKey) {
|
|
29
|
-
if (appKey) return appKey;
|
|
30
|
-
const fromCwd = resolveIntegrationAppKeyFromCwd();
|
|
31
|
-
if (fromCwd) return fromCwd;
|
|
32
|
-
throw new Error(
|
|
33
|
-
'Could not determine app context. Use --app <appKey> or run from integration/<appKey>/ directory.'
|
|
34
|
-
);
|
|
22
|
+
function logE2eDatasourceBanner(datasourceKey, verbose) {
|
|
23
|
+
if (!verbose) return;
|
|
24
|
+
logger.log('');
|
|
25
|
+
logger.log(infoLine(`🧪 Running E2E test for datasource: ${datasourceKey}`));
|
|
35
26
|
}
|
|
36
27
|
|
|
37
28
|
/**
|
|
38
29
|
* Resolve primaryKeyValue for request body: string as-is, or read and parse JSON from @path
|
|
39
|
-
* @param {string} [value]
|
|
40
|
-
* @returns {Promise<string|Object|null>}
|
|
30
|
+
* @param {string} [value]
|
|
31
|
+
* @returns {Promise<string|Object|null>}
|
|
41
32
|
*/
|
|
42
33
|
async function resolvePrimaryKeyValue(value) {
|
|
43
34
|
if (value === null || value === undefined || value === '') return null;
|
|
@@ -50,170 +41,119 @@ async function resolvePrimaryKeyValue(value) {
|
|
|
50
41
|
return str;
|
|
51
42
|
}
|
|
52
43
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
* @param {Object} options - Command options
|
|
56
|
-
* @returns {Promise<Object>} Request body
|
|
57
|
-
*/
|
|
58
|
-
async function buildE2EBody(options) {
|
|
59
|
-
const body = {};
|
|
60
|
-
if (options.debug) body.includeDebug = true;
|
|
61
|
-
if (options.testCrud === true) body.testCrud = true;
|
|
62
|
-
if (options.recordId !== undefined && options.recordId !== null && options.recordId !== '') body.recordId = String(options.recordId);
|
|
63
|
-
if (options.cleanup === false) body.cleanup = false;
|
|
64
|
-
else if (options.cleanup === true) body.cleanup = true;
|
|
65
|
-
const pk = await resolvePrimaryKeyValue(options.primaryKeyValue);
|
|
66
|
-
if (pk !== null && pk !== undefined) body.primaryKeyValue = pk;
|
|
67
|
-
return body;
|
|
44
|
+
function e2eIntegrationLogDir(appKey) {
|
|
45
|
+
return path.dirname(getIntegrationPath(appKey));
|
|
68
46
|
}
|
|
69
47
|
|
|
70
48
|
/**
|
|
71
|
-
*
|
|
72
|
-
* @
|
|
73
|
-
* @param {string} sourceIdOrKey - Source ID or key
|
|
74
|
-
* @param {string} testRunId - Test run ID
|
|
75
|
-
* @param {Object} authConfig - Auth config
|
|
76
|
-
* @param {Object} opts - Poll options
|
|
77
|
-
* @param {number} [opts.intervalMs] - Poll interval (ms)
|
|
78
|
-
* @param {number} [opts.timeoutMs] - Max wait (ms)
|
|
79
|
-
* @param {boolean} [opts.verbose] - Log each poll
|
|
80
|
-
* @returns {Promise<Object>} Final poll result (status completed or failed)
|
|
49
|
+
* Throw when unified run failed, timed out, or needs async; optionally write debug log.
|
|
50
|
+
* @returns {Promise<void>}
|
|
81
51
|
*/
|
|
82
|
-
async function
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
52
|
+
async function throwIfUnifiedE2EBlocked(unifiedResult, appKey, options, requestMeta) {
|
|
53
|
+
if (unifiedResult.apiError) {
|
|
54
|
+
const msg =
|
|
55
|
+
unifiedResult.apiError.formattedError ||
|
|
56
|
+
unifiedResult.apiError.error ||
|
|
57
|
+
'E2E request failed';
|
|
58
|
+
if (options.debug) {
|
|
59
|
+
await writeTestLog(
|
|
60
|
+
appKey,
|
|
61
|
+
{ request: requestMeta, error: msg },
|
|
62
|
+
'test-e2e',
|
|
63
|
+
e2eIntegrationLogDir(appKey)
|
|
64
|
+
);
|
|
92
65
|
}
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
66
|
+
throw new Error(msg);
|
|
67
|
+
}
|
|
68
|
+
if (unifiedResult.pollTimedOut) {
|
|
69
|
+
const err = new Error('Report incomplete: timeout');
|
|
70
|
+
if (options.debug) {
|
|
71
|
+
await writeTestLog(
|
|
72
|
+
appKey,
|
|
73
|
+
{ request: requestMeta, error: err.message },
|
|
74
|
+
'test-e2e',
|
|
75
|
+
e2eIntegrationLogDir(appKey)
|
|
76
|
+
);
|
|
96
77
|
}
|
|
97
|
-
|
|
78
|
+
throw err;
|
|
79
|
+
}
|
|
80
|
+
if (unifiedResult.incompleteNoAsync) {
|
|
81
|
+
throw new Error(
|
|
82
|
+
'Report incomplete: async polling disabled (--no-async) but server returned partial report.'
|
|
83
|
+
);
|
|
98
84
|
}
|
|
99
|
-
throw new Error(
|
|
100
|
-
`E2E test run did not complete within ${timeoutMs / 1000}s (run ID: ${testRunId})`
|
|
101
|
-
);
|
|
102
85
|
}
|
|
103
86
|
|
|
104
87
|
/**
|
|
105
|
-
* Run E2E test for one datasource (
|
|
106
|
-
* Default: async start + polling until completed/failed. Use options.async === false for sync.
|
|
107
|
-
*
|
|
88
|
+
* Run E2E test for one datasource (unified validation API; deployment auth like test-integration).
|
|
108
89
|
* @async
|
|
109
|
-
* @param {string} datasourceKey
|
|
110
|
-
* @param {Object} options
|
|
111
|
-
* @param {
|
|
112
|
-
* @
|
|
113
|
-
* @param {boolean} [options.debug] - Include debug, write log file
|
|
114
|
-
* @param {boolean} [options.verbose] - Verbose output (e.g. poll progress)
|
|
115
|
-
* @param {boolean} [options.async] - If false, use sync mode (no polling). Default true.
|
|
116
|
-
* @param {boolean} [options.testCrud] - Set body testCrud true
|
|
117
|
-
* @param {string} [options.recordId] - Set body recordId
|
|
118
|
-
* @param {boolean} [options.cleanup] - Set body cleanup (default true)
|
|
119
|
-
* @param {string} [options.primaryKeyValue] - Set body primaryKeyValue (string or @path to JSON)
|
|
120
|
-
* @param {number} [options.pollIntervalMs] - Poll interval in ms (default 2500)
|
|
121
|
-
* @param {number} [options.pollTimeoutMs] - Poll timeout in ms (default 15 min)
|
|
122
|
-
* @returns {Promise<Object>} E2E test result (steps, success, error, etc.)
|
|
90
|
+
* @param {string} datasourceKey
|
|
91
|
+
* @param {Object} options
|
|
92
|
+
* @param {boolean} [options.sync] - Publish local datasource JSON before validation when true
|
|
93
|
+
* @returns {Promise<Object>} Shape compatible with displayE2EResults (steps, success, status)
|
|
123
94
|
*/
|
|
124
95
|
async function runDatasourceTestE2E(datasourceKey, options = {}) {
|
|
125
96
|
if (!datasourceKey || typeof datasourceKey !== 'string') {
|
|
126
97
|
throw new Error('Datasource key is required');
|
|
127
98
|
}
|
|
128
|
-
const appKey =
|
|
129
|
-
const controllerUrl = await resolveControllerUrl();
|
|
130
|
-
const { resolveEnvironment } = require('../core/config');
|
|
131
|
-
const environment = options.environment || await resolveEnvironment();
|
|
132
|
-
const authConfig = await getDeviceOnlyAuth(controllerUrl);
|
|
133
|
-
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
99
|
+
const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
|
|
134
100
|
|
|
135
|
-
|
|
101
|
+
logE2eDatasourceBanner(datasourceKey, options.verbose);
|
|
102
|
+
|
|
103
|
+
const pk = await resolvePrimaryKeyValue(options.primaryKeyValue);
|
|
104
|
+
const timeoutRaw =
|
|
105
|
+
options.timeout !== undefined && options.timeout !== null && options.timeout !== ''
|
|
106
|
+
? parseInt(String(options.timeout), 10)
|
|
107
|
+
: options.pollTimeoutMs;
|
|
108
|
+
const timeoutMs =
|
|
109
|
+
Number.isFinite(timeoutRaw) && timeoutRaw > 0 ? timeoutRaw : DEFAULT_POLL_TIMEOUT_MS;
|
|
136
110
|
|
|
137
|
-
const body = await buildE2EBody(options);
|
|
138
|
-
const useAsync = options.async !== false;
|
|
139
111
|
const requestMeta = {
|
|
140
|
-
|
|
141
|
-
|
|
112
|
+
datasourceKey,
|
|
113
|
+
runType: 'e2e',
|
|
114
|
+
includeDebug: includeDebugForRequest(options.debug),
|
|
142
115
|
testCrud: options.testCrud,
|
|
143
116
|
recordId: options.recordId,
|
|
144
117
|
cleanup: options.cleanup,
|
|
145
|
-
primaryKeyValue:
|
|
118
|
+
primaryKeyValue: pk !== undefined && pk !== null
|
|
146
119
|
};
|
|
147
120
|
|
|
148
|
-
const
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
useAsync,
|
|
121
|
+
const unifiedResult = await runUnifiedDatasourceValidation(datasourceKey, {
|
|
122
|
+
app: options.app,
|
|
123
|
+
environment: options.environment,
|
|
124
|
+
runType: 'e2e',
|
|
125
|
+
debug: options.debug,
|
|
154
126
|
verbose: options.verbose,
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
await writeTestLog(appKey, { request: requestMeta, error: error.message }, 'test-e2e', integrationDir);
|
|
166
|
-
}
|
|
167
|
-
throw error;
|
|
168
|
-
}
|
|
127
|
+
async: options.async !== false,
|
|
128
|
+
noAsync: options.async === false,
|
|
129
|
+
testCrud: options.testCrud,
|
|
130
|
+
recordId: options.recordId,
|
|
131
|
+
cleanup: options.cleanup,
|
|
132
|
+
primaryKeyValue: pk,
|
|
133
|
+
capabilityKey: options.capabilityKey,
|
|
134
|
+
timeout: timeoutMs,
|
|
135
|
+
sync: options.sync === true
|
|
136
|
+
});
|
|
169
137
|
|
|
170
|
-
|
|
171
|
-
const appPath = getIntegrationPath(appKey);
|
|
172
|
-
const integrationDir = path.dirname(appPath);
|
|
173
|
-
const logPath = await writeTestLog(appKey, { request: requestMeta, response: data }, 'test-e2e', integrationDir);
|
|
174
|
-
logger.log(chalk.gray(` Debug log: ${logPath}`));
|
|
175
|
-
}
|
|
138
|
+
await throwIfUnifiedE2EBlocked(unifiedResult, appKey, options, requestMeta);
|
|
176
139
|
|
|
177
|
-
|
|
178
|
-
}
|
|
140
|
+
const display = e2eShapeFromEnvelope(unifiedResult.envelope);
|
|
141
|
+
Object.assign(display, { datasourceTestRun: unifiedResult.envelope });
|
|
179
142
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
* @param {Object} opts.body - Request body
|
|
187
|
-
* @param {boolean} opts.useAsync - Whether to use async + poll
|
|
188
|
-
* @param {boolean} opts.verbose - Verbose poll progress
|
|
189
|
-
* @param {number} [opts.pollIntervalMs] - Override poll interval (ms)
|
|
190
|
-
* @param {number} [opts.pollTimeoutMs] - Override poll timeout (ms)
|
|
191
|
-
* @returns {Promise<Object>} Final result data
|
|
192
|
-
*/
|
|
193
|
-
/* eslint-disable-next-line max-params -- single opts object; destructuring in body */
|
|
194
|
-
async function executeE2EWithOptionalPoll(opts) {
|
|
195
|
-
const { dataplaneUrl, datasourceKey, authConfig, body, useAsync, verbose, pollIntervalMs, pollTimeoutMs } = opts;
|
|
196
|
-
const response = await testDatasourceE2E(dataplaneUrl, datasourceKey, authConfig, body, {
|
|
197
|
-
asyncRun: useAsync
|
|
198
|
-
});
|
|
199
|
-
let data = response.data || response;
|
|
200
|
-
if (useAsync && data !== null && data !== undefined && data.testRunId) {
|
|
201
|
-
data = await pollE2ETestRun(
|
|
202
|
-
dataplaneUrl,
|
|
203
|
-
datasourceKey,
|
|
204
|
-
data.testRunId,
|
|
205
|
-
authConfig,
|
|
206
|
-
{
|
|
207
|
-
intervalMs: pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS,
|
|
208
|
-
timeoutMs: pollTimeoutMs ?? DEFAULT_POLL_TIMEOUT_MS,
|
|
209
|
-
verbose
|
|
210
|
-
}
|
|
143
|
+
if (options.debug) {
|
|
144
|
+
const logPath = await writeTestLog(
|
|
145
|
+
appKey,
|
|
146
|
+
{ request: requestMeta, response: unifiedResult.envelope },
|
|
147
|
+
'test-e2e',
|
|
148
|
+
e2eIntegrationLogDir(appKey)
|
|
211
149
|
);
|
|
150
|
+
logger.log(chalk.gray(` Debug log: ${logPath}`));
|
|
212
151
|
}
|
|
213
|
-
|
|
152
|
+
|
|
153
|
+
return display;
|
|
214
154
|
}
|
|
215
155
|
|
|
216
156
|
module.exports = {
|
|
217
157
|
runDatasourceTestE2E,
|
|
218
|
-
|
|
158
|
+
resolvePrimaryKeyValue
|
|
219
159
|
};
|