@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.
Files changed (392) hide show
  1. package/.cursor/rules/anchor-docs.mdc +15 -0
  2. package/README.md +2 -2
  3. package/anchor-docs/README.md +10 -0
  4. package/anchor-docs/_TEMPLATE +24 -0
  5. package/bin/aifabrix.js +13 -4
  6. package/integration/hubspot-test/README.md +157 -0
  7. package/integration/{hubspot → hubspot-test}/application.json +6 -6
  8. package/integration/{hubspot → hubspot-test}/create-hubspot.js +10 -10
  9. package/integration/hubspot-test/env.template +4 -0
  10. package/integration/hubspot-test/hubspot-test-datasource-company.json +138 -0
  11. package/integration/hubspot-test/hubspot-test-datasource-contact.json +146 -0
  12. package/integration/hubspot-test/hubspot-test-datasource-deal.json +146 -0
  13. package/integration/hubspot-test/hubspot-test-datasource-users.json +76 -0
  14. package/integration/{hubspot/hubspot-deploy.json → hubspot-test/hubspot-test-deploy.json} +201 -24
  15. package/integration/{hubspot/hubspot-system.json → hubspot-test/hubspot-test-system.json} +8 -7
  16. package/integration/hubspot-test/rbac.json +166 -0
  17. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-credential-real.yaml +3 -3
  18. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-hubspot-env-vars.yaml +2 -2
  19. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-add-datasource.yaml +1 -1
  20. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-create.yaml +1 -1
  21. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-credential-select.yaml +1 -1
  22. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-known-platform.yaml +1 -1
  23. package/integration/hubspot-test/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  24. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-mode.yaml +1 -1
  25. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-file.yaml +1 -1
  26. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-openapi-url.yaml +1 -1
  27. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-source.yaml +1 -1
  28. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-array-test.yaml +1 -1
  29. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  30. package/integration/hubspot-test/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  31. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-dimension-test.yaml +1 -1
  32. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-test.yaml +1 -1
  33. package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +1 -1
  34. package/integration/{hubspot → hubspot-test}/test-dataplane-down-tests.js +1 -7
  35. package/integration/{hubspot → hubspot-test}/test-dataplane-down.js +3 -3
  36. package/integration/{hubspot → hubspot-test}/test.js +137 -102
  37. package/integration/{hubspot → hubspot-test}/wizard-hubspot-e2e.yaml +2 -2
  38. package/integration/{hubspot → hubspot-test}/wizard-hubspot-platform.yaml +1 -1
  39. package/integration/hubspot-test/wizard-hubspot-test-headless.yaml +23 -0
  40. package/integration/roundtrip-test-local/README.md +144 -0
  41. package/integration/roundtrip-test-local/application.yaml +13 -0
  42. package/integration/roundtrip-test-local/env.template +15 -0
  43. package/integration/roundtrip-test-local/roundtrip-test-local-datasource-roundtrip-test-company.yaml +14 -0
  44. package/integration/roundtrip-test-local/roundtrip-test-local-deploy.json +61 -0
  45. package/integration/roundtrip-test-local/roundtrip-test-local-system.yaml +25 -0
  46. package/integration/roundtrip-test-local2/README.md +144 -0
  47. package/integration/roundtrip-test-local2/application.yaml +13 -0
  48. package/integration/roundtrip-test-local2/env.template +15 -0
  49. package/integration/roundtrip-test-local2/roundtrip-test-local2-datasource-company.yaml +31 -0
  50. package/integration/roundtrip-test-local2/roundtrip-test-local2-deploy.json +86 -0
  51. package/integration/roundtrip-test-local2/roundtrip-test-local2-system.yaml +25 -0
  52. package/integration/test/wizard.yaml +8 -0
  53. package/jest.config.default.js +10 -0
  54. package/jest.config.integration.fixtures.js +22 -0
  55. package/jest.config.integration.js +21 -18
  56. package/jest.config.isolated.js +10 -0
  57. package/jest.projects.js +288 -0
  58. package/lib/api/datasources-core.api.js +3 -3
  59. package/lib/api/dev-mtls-request.js +110 -0
  60. package/lib/api/dev-server-https.js +145 -0
  61. package/lib/api/dev.api.js +133 -144
  62. package/lib/api/index.js +0 -1
  63. package/lib/api/pipeline.api.js +67 -20
  64. package/lib/api/service-users.api.js +111 -2
  65. package/lib/api/types/dev.types.js +4 -3
  66. package/lib/api/types/pipeline.types.js +8 -5
  67. package/lib/api/types/service-users.types.js +41 -0
  68. package/lib/api/types/validation-run.types.js +56 -0
  69. package/lib/api/validation-run.api.js +99 -0
  70. package/lib/api/validation-runner.js +99 -0
  71. package/lib/app/config.js +1 -1
  72. package/lib/app/deploy-status-display.js +2 -2
  73. package/lib/app/deploy.js +7 -6
  74. package/lib/app/display.js +2 -1
  75. package/lib/app/dockerfile.js +3 -2
  76. package/lib/app/down.js +2 -1
  77. package/lib/app/helpers.js +6 -5
  78. package/lib/app/index.js +27 -8
  79. package/lib/app/list.js +7 -6
  80. package/lib/app/push.js +4 -3
  81. package/lib/app/register.js +19 -8
  82. package/lib/app/rotate-secret.js +17 -13
  83. package/lib/app/run-container-start.js +184 -0
  84. package/lib/app/run-docker-fallback.js +108 -0
  85. package/lib/app/run-env-compose.js +30 -42
  86. package/lib/app/run-helpers.js +49 -126
  87. package/lib/app/run-infra-requirements.js +30 -0
  88. package/lib/app/run-resolve-image.js +21 -0
  89. package/lib/app/run.js +74 -21
  90. package/lib/app/show-display.js +1 -1
  91. package/lib/app/show.js +1 -1
  92. package/lib/build/index.js +13 -10
  93. package/lib/cli/index.js +2 -0
  94. package/lib/cli/setup-app.help.js +67 -0
  95. package/lib/cli/setup-app.js +59 -123
  96. package/lib/cli/setup-app.test-commands.js +179 -0
  97. package/lib/cli/setup-auth.js +36 -14
  98. package/lib/cli/setup-credential-deployment.js +22 -8
  99. package/lib/cli/setup-dev-path-commands.js +124 -0
  100. package/lib/cli/setup-dev.js +190 -103
  101. package/lib/cli/setup-environment.js +11 -20
  102. package/lib/cli/setup-external-system.js +62 -22
  103. package/lib/cli/setup-infra.js +139 -47
  104. package/lib/cli/setup-parameters.js +32 -0
  105. package/lib/cli/setup-secrets.js +147 -10
  106. package/lib/cli/setup-service-user.js +146 -20
  107. package/lib/cli/setup-utility.js +47 -19
  108. package/lib/commands/app-down.js +5 -7
  109. package/lib/commands/app-install.js +14 -7
  110. package/lib/commands/app-logs.js +13 -10
  111. package/lib/commands/app-shell.js +4 -1
  112. package/lib/commands/app-test.js +25 -19
  113. package/lib/commands/app.js +22 -10
  114. package/lib/commands/auth-config.js +10 -14
  115. package/lib/commands/auth-status.js +4 -3
  116. package/lib/commands/credential-env.js +4 -3
  117. package/lib/commands/credential-list.js +5 -4
  118. package/lib/commands/credential-push.js +4 -3
  119. package/lib/commands/datasource-unified-test-cli.js +495 -0
  120. package/lib/commands/datasource-unified-test-cli.options.js +149 -0
  121. package/lib/commands/datasource-validation-cli.js +129 -0
  122. package/lib/commands/datasource.js +123 -71
  123. package/lib/commands/deployment-list.js +6 -5
  124. package/lib/commands/dev-cli-handlers.js +122 -18
  125. package/lib/commands/dev-down.js +4 -3
  126. package/lib/commands/dev-init.js +231 -116
  127. package/lib/commands/dev-show-display.js +473 -0
  128. package/lib/commands/login-credentials.js +3 -2
  129. package/lib/commands/login-device.js +4 -3
  130. package/lib/commands/login.js +5 -4
  131. package/lib/commands/logout.js +8 -7
  132. package/lib/commands/parameters-validate.js +54 -0
  133. package/lib/commands/repair-datasource.js +314 -68
  134. package/lib/commands/repair-env-template.js +16 -10
  135. package/lib/commands/repair-rbac.js +25 -19
  136. package/lib/commands/repair.js +116 -32
  137. package/lib/commands/secrets-list.js +23 -12
  138. package/lib/commands/secrets-remove-all.js +220 -0
  139. package/lib/commands/secrets-remove.js +22 -13
  140. package/lib/commands/secrets-set.js +21 -12
  141. package/lib/commands/secrets-validate.js +20 -7
  142. package/lib/commands/secure.js +10 -9
  143. package/lib/commands/service-user.js +243 -13
  144. package/lib/commands/test-e2e-external.js +27 -1
  145. package/lib/commands/up-common.js +28 -2
  146. package/lib/commands/up-dataplane.js +31 -18
  147. package/lib/commands/up-miso.js +19 -29
  148. package/lib/commands/upload.js +138 -39
  149. package/lib/commands/wizard-core-helpers.js +1 -1
  150. package/lib/commands/wizard-dataplane.js +4 -3
  151. package/lib/commands/wizard-helpers.js +3 -3
  152. package/lib/commands/wizard.js +2 -2
  153. package/lib/core/admin-secrets.js +16 -5
  154. package/lib/core/audit-logger.js +12 -4
  155. package/lib/core/config-attach-extensions.js +46 -0
  156. package/lib/core/config-runtime-paths.js +29 -0
  157. package/lib/core/config.js +59 -58
  158. package/lib/core/diff.js +3 -2
  159. package/lib/core/ensure-encryption-key.js +2 -4
  160. package/lib/core/secrets-ensure-infra.js +77 -0
  161. package/lib/core/secrets-ensure.js +120 -64
  162. package/lib/core/secrets-env-write.js +35 -7
  163. package/lib/core/secrets-infra-placeholder-sync.js +61 -0
  164. package/lib/core/secrets.js +228 -42
  165. package/lib/core/templates-env.js +4 -3
  166. package/lib/core/templates.js +1 -1
  167. package/lib/datasource/abac-validator.js +148 -0
  168. package/lib/datasource/deploy.js +75 -53
  169. package/lib/datasource/field-reference-validator.js +77 -36
  170. package/lib/datasource/integration-context.js +63 -0
  171. package/lib/datasource/list.js +8 -7
  172. package/lib/datasource/log-viewer.js +252 -0
  173. package/lib/datasource/resolve-app.js +109 -0
  174. package/lib/datasource/test-e2e.js +95 -155
  175. package/lib/datasource/test-integration.js +121 -109
  176. package/lib/datasource/unified-validation-run-body.js +65 -0
  177. package/lib/datasource/unified-validation-run-post.js +23 -0
  178. package/lib/datasource/unified-validation-run-resolve.js +43 -0
  179. package/lib/datasource/unified-validation-run.js +92 -0
  180. package/lib/datasource/validate.js +162 -15
  181. package/lib/deployment/deployer.js +4 -3
  182. package/lib/deployment/environment.js +7 -6
  183. package/lib/deployment/push.js +17 -8
  184. package/lib/external-system/delete.js +4 -3
  185. package/lib/external-system/deploy.js +131 -53
  186. package/lib/external-system/download-helpers.js +1 -1
  187. package/lib/external-system/download.js +7 -6
  188. package/lib/external-system/generator.js +104 -14
  189. package/lib/external-system/integration-test-dispatch.js +26 -0
  190. package/lib/external-system/test-execution.js +5 -1
  191. package/lib/external-system/test-helpers.js +0 -4
  192. package/lib/external-system/test-system-level-helpers.js +110 -0
  193. package/lib/external-system/test-system-level.js +83 -44
  194. package/lib/external-system/test.js +59 -8
  195. package/lib/generator/builders.js +23 -11
  196. package/lib/generator/deploy-manifest-azure-kv.js +81 -0
  197. package/lib/generator/external-controller-manifest.js +3 -3
  198. package/lib/generator/external.js +23 -11
  199. package/lib/generator/helpers.js +71 -12
  200. package/lib/generator/index.js +8 -4
  201. package/lib/generator/split-readme.js +12 -7
  202. package/lib/generator/split-variables.js +2 -1
  203. package/lib/generator/split.js +46 -11
  204. package/lib/generator/wizard-readme.js +3 -3
  205. package/lib/generator/wizard.js +16 -13
  206. package/lib/infrastructure/compose.js +60 -6
  207. package/lib/infrastructure/helpers.js +238 -51
  208. package/lib/infrastructure/index.js +64 -37
  209. package/lib/infrastructure/services.js +21 -15
  210. package/lib/internal/fs-real-sync.js +104 -0
  211. package/lib/internal/node-fs.js +98 -0
  212. package/lib/parameters/database-secret-values.js +173 -0
  213. package/lib/parameters/infra-kv-discovery.js +121 -0
  214. package/lib/parameters/infra-parameter-catalog.js +458 -0
  215. package/lib/parameters/infra-parameter-validate.js +64 -0
  216. package/lib/schema/application-schema.json +37 -17
  217. package/lib/schema/datasource-test-run.schema.json +493 -0
  218. package/lib/schema/deployment-rules.yaml +102 -63
  219. package/lib/schema/external-datasource.schema.json +1201 -433
  220. package/lib/schema/external-system.schema.json +181 -5
  221. package/lib/schema/flag-map-validation-run.json +31 -0
  222. package/lib/schema/infra-parameter.schema.json +106 -0
  223. package/lib/schema/infra.parameter.yaml +421 -0
  224. package/lib/schema/type/credential-auth-templates.json +40 -0
  225. package/lib/schema/type/document-storage.json +213 -0
  226. package/lib/schema/type/message-service.json +123 -0
  227. package/lib/schema/type/vector-store.json +88 -0
  228. package/lib/utils/aifabrix-runtime-config-dir.js +132 -0
  229. package/lib/utils/api-error-handler.js +2 -2
  230. package/lib/utils/api.js +49 -14
  231. package/lib/utils/app-config-resolver.js +23 -1
  232. package/lib/utils/app-register-api.js +3 -2
  233. package/lib/utils/app-register-auth.js +1 -1
  234. package/lib/utils/app-register-config.js +4 -4
  235. package/lib/utils/app-register-display.js +3 -2
  236. package/lib/utils/app-register-validator.js +3 -2
  237. package/lib/utils/app-run-containers.js +26 -22
  238. package/lib/utils/app-scoped-config.js +31 -0
  239. package/lib/utils/app-service-env-from-builder.js +164 -0
  240. package/lib/utils/build-copy.js +1 -1
  241. package/lib/utils/build-helpers.js +20 -20
  242. package/lib/utils/build-resolve-image.js +165 -0
  243. package/lib/utils/cli-layout-chalk.js +8 -0
  244. package/lib/utils/cli-test-layout-chalk.js +267 -0
  245. package/lib/utils/cli-utils.js +88 -11
  246. package/lib/utils/compose-db-passwords.js +138 -0
  247. package/lib/utils/compose-generate-docker-compose.js +216 -0
  248. package/lib/utils/compose-generator.js +197 -291
  249. package/lib/utils/compose-miso-env.js +18 -0
  250. package/lib/utils/compose-traefik-ingress-base.js +158 -0
  251. package/lib/utils/config-paths.js +209 -6
  252. package/lib/utils/config-scoped-resources-preference.js +41 -0
  253. package/lib/utils/controller-deployment-outcome.js +68 -0
  254. package/lib/utils/credential-display.js +2 -2
  255. package/lib/utils/credential-secrets-env.js +16 -1
  256. package/lib/utils/dataplane-pipeline-warning.js +4 -3
  257. package/lib/utils/datasource-test-run-capability-scope.js +43 -0
  258. package/lib/utils/datasource-test-run-debug-display.js +137 -0
  259. package/lib/utils/datasource-test-run-debug-slice.js +93 -0
  260. package/lib/utils/datasource-test-run-display.js +442 -0
  261. package/lib/utils/datasource-test-run-exit.js +58 -0
  262. package/lib/utils/datasource-test-run-legacy-adapter.js +93 -0
  263. package/lib/utils/datasource-test-run-report-version.js +51 -0
  264. package/lib/utils/datasource-test-run-schema-sync.js +59 -0
  265. package/lib/utils/datasource-test-run-tty-log.js +81 -0
  266. package/lib/utils/datasource-validation-watch.js +266 -0
  267. package/lib/utils/declarative-url-ports.js +47 -0
  268. package/lib/utils/derive-env-key-from-client-id.js +41 -0
  269. package/lib/utils/dev-ca-install.js +185 -23
  270. package/lib/utils/dev-cert-helper.js +266 -17
  271. package/lib/utils/dev-hosts-helper.js +307 -0
  272. package/lib/utils/dev-init-cert-hints.js +37 -0
  273. package/lib/utils/dev-init-health-messages.js +52 -0
  274. package/lib/utils/dev-init-resolve.js +86 -0
  275. package/lib/utils/dev-init-ssh-merge.js +65 -0
  276. package/lib/utils/dev-ssh-config-helper.js +196 -0
  277. package/lib/utils/dev-user-groups.js +93 -0
  278. package/lib/utils/docker-build.js +42 -17
  279. package/lib/utils/docker-exec.js +28 -0
  280. package/lib/utils/docker-manifest-public-port.js +116 -0
  281. package/lib/utils/docker-not-running-hint.js +52 -0
  282. package/lib/utils/docker.js +98 -11
  283. package/lib/utils/ensure-dev-certs-for-remote-docker.js +192 -0
  284. package/lib/utils/env-config-loader.js +10 -91
  285. package/lib/utils/env-copy.js +19 -10
  286. package/lib/utils/env-map.js +42 -11
  287. package/lib/utils/env-template.js +2 -2
  288. package/lib/utils/environment-scoped-resources.js +144 -0
  289. package/lib/utils/error-formatter.js +125 -9
  290. package/lib/utils/error-formatters/http-status-errors.js +6 -5
  291. package/lib/utils/error-formatters/network-errors.js +2 -1
  292. package/lib/utils/error-formatters/permission-errors.js +2 -1
  293. package/lib/utils/error-formatters/validation-errors.js +2 -1
  294. package/lib/utils/external-env-template.js +180 -0
  295. package/lib/utils/external-readme.js +8 -1
  296. package/lib/utils/external-system-display.js +277 -136
  297. package/lib/utils/external-system-local-test-tty.js +389 -0
  298. package/lib/utils/external-system-readiness-core.js +377 -0
  299. package/lib/utils/external-system-readiness-deploy-display.js +270 -0
  300. package/lib/utils/external-system-readiness-display-internals.js +150 -0
  301. package/lib/utils/external-system-readiness-display.js +186 -0
  302. package/lib/utils/external-system-test-helpers.js +24 -6
  303. package/lib/utils/external-system-validators.js +32 -14
  304. package/lib/utils/health-check-url.js +119 -0
  305. package/lib/utils/health-check.js +59 -25
  306. package/lib/utils/help-builder.js +14 -13
  307. package/lib/utils/image-version.js +4 -8
  308. package/lib/utils/infra-containers.js +4 -7
  309. package/lib/utils/infra-env-defaults.js +162 -0
  310. package/lib/utils/infra-status-display.js +167 -0
  311. package/lib/utils/infra-status.js +16 -8
  312. package/lib/utils/local-secrets.js +29 -7
  313. package/lib/utils/paths.js +136 -48
  314. package/lib/utils/port-resolver.js +10 -23
  315. package/lib/utils/redis-env-scope.js +62 -0
  316. package/lib/utils/register-aifabrix-shell-env.js +204 -0
  317. package/lib/utils/remote-builder-validation.js +99 -0
  318. package/lib/utils/remote-dev-auth.js +117 -21
  319. package/lib/utils/remote-docker-env.js +67 -15
  320. package/lib/utils/remote-secrets-loader.js +13 -4
  321. package/lib/utils/resolve-docker-image-ref.js +124 -0
  322. package/lib/utils/schema-loader.js +22 -9
  323. package/lib/utils/secrets-bash-kv.js +25 -0
  324. package/lib/utils/secrets-generator.js +171 -51
  325. package/lib/utils/secrets-helpers.js +70 -59
  326. package/lib/utils/secrets-kv-scope.js +60 -0
  327. package/lib/utils/secrets-utils.js +35 -37
  328. package/lib/utils/secrets-validation.js +3 -1
  329. package/lib/utils/secrets-yaml-preserve.js +109 -0
  330. package/lib/utils/secure-file-permissions.js +91 -0
  331. package/lib/utils/ssh-key-helper.js +4 -2
  332. package/lib/utils/template-helpers.js +2 -2
  333. package/lib/utils/test-log-writer.js +3 -3
  334. package/lib/utils/token-manager.js +37 -5
  335. package/lib/utils/url-declarative-public-base.js +188 -0
  336. package/lib/utils/url-declarative-resolve-build.js +493 -0
  337. package/lib/utils/url-declarative-resolve-load-doc.js +51 -0
  338. package/lib/utils/url-declarative-resolve.js +220 -0
  339. package/lib/utils/url-declarative-token-parse.js +74 -0
  340. package/lib/utils/url-declarative-url-flags.js +50 -0
  341. package/lib/utils/url-declarative-vdir-inactive-env.js +99 -0
  342. package/lib/utils/url-public-path-prefix.js +34 -0
  343. package/lib/utils/urls-local-registry.js +220 -0
  344. package/lib/utils/validation-report-tty-kit.js +77 -0
  345. package/lib/utils/validation-run-poll.js +89 -0
  346. package/lib/utils/validation-run-post-retry.js +73 -0
  347. package/lib/utils/validation-run-request.js +98 -0
  348. package/lib/utils/variable-transformer.js +21 -4
  349. package/lib/utils/yaml-preserve.js +78 -1
  350. package/lib/validation/datasource-warnings.js +56 -0
  351. package/lib/validation/env-template-auth.js +50 -2
  352. package/lib/validation/external-manifest-validator.js +35 -7
  353. package/lib/validation/validate-display.js +37 -31
  354. package/lib/validation/validate.js +9 -10
  355. package/lib/validation/validator-unresolved-placeholders.js +98 -0
  356. package/lib/validation/validator.js +32 -78
  357. package/lib/validation/wizard-config-validator.js +2 -1
  358. package/package.json +11 -3
  359. package/scripts/check-datasource-test-run-schema-sync.js +34 -0
  360. package/scripts/diagnose-cli.js +150 -0
  361. package/scripts/install-local.js +304 -55
  362. package/templates/README.md +15 -2
  363. package/templates/applications/dataplane/application.yaml +52 -2
  364. package/templates/applications/dataplane/env.template +80 -18
  365. package/templates/applications/dataplane/rbac.yaml +8 -0
  366. package/templates/applications/keycloak/application.yaml +9 -1
  367. package/templates/applications/keycloak/env.template +15 -6
  368. package/templates/applications/miso-controller/application.yaml +10 -2
  369. package/templates/applications/miso-controller/env.template +55 -14
  370. package/templates/applications/miso-controller/rbac.yaml +5 -0
  371. package/templates/external-system/README.md.hbs +20 -7
  372. package/templates/external-system/deploy.js.hbs +5 -5
  373. package/templates/external-system/env.template.hbs +22 -0
  374. package/templates/external-system/external-datasource.yaml.hbs +197 -118
  375. package/templates/infra/compose.yaml.hbs +20 -4
  376. package/templates/python/docker-compose.hbs +16 -0
  377. package/templates/typescript/docker-compose.hbs +16 -0
  378. package/integration/hubspot/README.md +0 -102
  379. package/integration/hubspot/env.template +0 -4
  380. package/integration/hubspot/hubspot-datasource-company.json +0 -541
  381. package/integration/hubspot/hubspot-datasource-contact.json +0 -639
  382. package/integration/hubspot/hubspot-datasource-deal.json +0 -588
  383. package/integration/hubspot/hubspot-datasource-users.json +0 -116
  384. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +0 -2
  385. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +0 -5
  386. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +0 -5
  387. package/lib/api/external-test.api.js +0 -111
  388. package/lib/schema/env-config.yaml +0 -43
  389. /package/integration/{hubspot → hubspot-test}/companies.json +0 -0
  390. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-app-name.yaml +0 -0
  391. /package/integration/{hubspot → hubspot-test}/test-artifacts/wizard-invalid-missing-app.yaml +0 -0
  392. /package/integration/{hubspot → hubspot-test}/test-dataplane-down-helpers.js +0 -0
@@ -1,8 +1,9 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * External System Deployment Module
3
4
  *
4
5
  * Handles deployment of external systems via controller pipeline.
5
- * Uses unified controller pipeline (same as regular apps) - no direct dataplane calls.
6
+ * After deploy, fetches dataplane list + system for readiness summary.
6
7
  *
7
8
  * @fileoverview External system deployment for AI Fabrix Builder
8
9
  * @author AI Fabrix Team
@@ -17,69 +18,116 @@ const { detectAppType } = require('../utils/paths');
17
18
  const { logOfflinePathWhenType } = require('../utils/cli-utils');
18
19
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
19
20
  const { getExternalSystem } = require('../api/external-systems.api');
21
+ const { listDatasources } = require('../api/datasources-core.api');
22
+ const { testSystemViaPipeline } = require('../api/pipeline.api');
23
+ const { extractDatasources } = require('../datasource/list');
24
+ const { unwrapApiData } = require('../utils/external-system-readiness-core');
25
+ const { logDeployReadinessSummary } = require('../utils/external-system-readiness-deploy-display');
26
+ const { parseControllerDeploymentOutcome } = require('../utils/controller-deployment-outcome');
20
27
  const { generateControllerManifest } = require('../generator/external-controller-manifest');
21
28
  const { validateExternalSystemComplete } = require('../validation/validate');
22
29
  const { displayValidationResults } = require('../validation/validate-display');
23
30
 
24
31
  /**
25
- * Displays API and MCP documentation URLs from dataplane when available
32
+ * Lists datasources for a system and loads system record for docs URLs.
26
33
  * @async
27
- * @function displayDeploymentDocs
28
34
  * @param {string} controllerUrl - Controller base URL
29
35
  * @param {string} environment - Environment key
30
- * @param {Object} authConfig - Authentication configuration
31
- * @param {string} systemKey - External system key
36
+ * @param {Object} authConfig - Auth config
37
+ * @param {string} systemKey - System key
38
+ * @returns {Promise<{ dataplaneUrl: string, datasources: Object[], system: Object|null, error: Error|null }>}
32
39
  */
33
- async function displayDeploymentDocs(controllerUrl, environment, authConfig, systemKey) {
40
+ async function fetchDataplaneDeployReadiness(controllerUrl, environment, authConfig, systemKey) {
41
+ let dataplaneUrl;
34
42
  try {
35
- const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
36
- const res = await getExternalSystem(dataplaneUrl, systemKey, authConfig);
37
- const sys = res?.data || res;
38
- if (!sys) return;
39
-
40
- const apiDocumentUrl = sys.apiDocumentUrl;
41
- const mcpServerUrl = sys.mcpServerUrl;
42
- const openApiDocsPageUrl = sys.openApiDocsPageUrl;
43
-
44
- const urls = [];
45
- if (apiDocumentUrl && typeof apiDocumentUrl === 'string') {
46
- urls.push({ label: 'API Docs', url: apiDocumentUrl });
47
- }
48
- if (mcpServerUrl && typeof mcpServerUrl === 'string') {
49
- urls.push({ label: 'MCP Server', url: mcpServerUrl });
50
- }
51
- if (openApiDocsPageUrl && typeof openApiDocsPageUrl === 'string') {
52
- urls.push({ label: 'OpenAPI Docs Page', url: openApiDocsPageUrl });
43
+ dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
44
+ } catch (err) {
45
+ return { dataplaneUrl: null, datasources: [], system: null, error: err };
46
+ }
47
+
48
+ let datasources = [];
49
+ try {
50
+ const listRes = await listDatasources(dataplaneUrl, authConfig, {
51
+ sourceSystemIdOrKey: systemKey,
52
+ pageSize: 100
53
+ });
54
+ if (listRes.success && listRes.data) {
55
+ try {
56
+ datasources = extractDatasources(listRes);
57
+ } catch (e) {
58
+ logger.log(
59
+ chalk.yellow(
60
+ `⚠ Unable to parse datasources list from dataplane response: ${e && e.message ? e.message : 'unknown error'}`
61
+ )
62
+ );
63
+ datasources = [];
64
+ }
53
65
  }
66
+ } catch (err) {
67
+ return { dataplaneUrl, datasources: [], system: null, error: err };
68
+ }
54
69
 
55
- if (urls.length > 0) {
56
- logger.log(chalk.blue('\nDocumentation:'));
57
- urls.forEach(({ label, url }) => {
58
- logger.log(chalk.blue(` ${label}: ${url}`));
59
- });
70
+ try {
71
+ const getRes = await getExternalSystem(dataplaneUrl, systemKey, authConfig);
72
+ const system = unwrapApiData(getRes);
73
+ return {
74
+ dataplaneUrl,
75
+ datasources,
76
+ system: system && typeof system === 'object' ? system : null,
77
+ error: null
78
+ };
79
+ } catch (err) {
80
+ if (datasources.length === 0) {
81
+ return { dataplaneUrl, datasources, system: null, error: err };
60
82
  }
61
- } catch (_err) {
62
- // Silently ignore: dataplane may be unreachable or docs not configured
83
+ return { dataplaneUrl, datasources, system: null, error: null };
63
84
  }
64
85
  }
65
86
 
66
87
  /**
67
- * Deploys via controller and displays success summary with docs
88
+ * Deploys via controller and prints readiness summary (config / deployment / runtime layers).
68
89
  * @async
69
- * @function executeDeployAndDisplay
70
90
  * @param {Object} manifest - Controller manifest
71
91
  * @param {string} controllerUrl - Controller base URL
72
92
  * @param {string} environment - Environment key
73
93
  * @param {Object} authConfig - Authentication configuration
74
- * @param {Object} options - Deployment options
94
+ * @param {Object} options - Deployment options (poll, probe, probeTimeout)
75
95
  * @returns {Promise<Object>} Deployment result
76
96
  */
97
+ /**
98
+ * @param {Object} deploymentOutcome - from parseControllerDeploymentOutcome
99
+ */
100
+ function logImmediateControllerDeploymentOutcome(deploymentOutcome) {
101
+ if (deploymentOutcome.ok) {
102
+ logger.log(formatSuccessParagraph('Controller deployment OK'));
103
+ return;
104
+ }
105
+ logger.log(chalk.red('\n✖ Controller deployment did not complete successfully'));
106
+ const parts = [deploymentOutcome.error, deploymentOutcome.message].filter(Boolean);
107
+ if (parts.length > 0) {
108
+ for (const line of parts) {
109
+ logger.log(chalk.red(` ${line}`));
110
+ }
111
+ return;
112
+ }
113
+ if (deploymentOutcome.statusLabel) {
114
+ logger.log(
115
+ chalk.yellow(
116
+ ` Controller status: ${deploymentOutcome.statusLabel} (no message or error in API response)`
117
+ )
118
+ );
119
+ return;
120
+ }
121
+ logger.log(chalk.gray(' See Deployment section below for details.'));
122
+ }
123
+
77
124
  async function executeDeployAndDisplay(manifest, controllerUrl, environment, authConfig, options) {
78
125
  const deployer = require('../deployment/deployer');
79
126
  const pollOpts = {
80
127
  poll: options.poll,
81
128
  pollInterval: options.pollInterval !== undefined ? options.pollInterval : 500,
82
- pollMaxAttempts: options.pollMaxAttempts,
129
+ // External-system deploy only: ~10s poll window (matches controller→dataplane publish timeout). Not used for Azure/webapp deploy polling.
130
+ pollMaxAttempts: options.pollMaxAttempts !== undefined ? options.pollMaxAttempts : 22,
83
131
  ...options
84
132
  };
85
133
  const result = await deployer.deployToController(
@@ -89,17 +137,54 @@ async function executeDeployAndDisplay(manifest, controllerUrl, environment, aut
89
137
  authConfig,
90
138
  pollOpts
91
139
  );
92
- logger.log(chalk.green('\n✅ External system deployed successfully!'));
93
- logger.log(chalk.blue(`System: ${manifest.key}`));
94
- logger.log(chalk.blue(`Datasources: ${manifest.dataSources.length}`));
95
- await displayDeploymentDocs(controllerUrl, environment, authConfig, manifest.key);
140
+
141
+ const deploymentOutcome = parseControllerDeploymentOutcome(result);
142
+ logImmediateControllerDeploymentOutcome(deploymentOutcome);
143
+
144
+ const ctx = await fetchDataplaneDeployReadiness(
145
+ controllerUrl,
146
+ environment,
147
+ authConfig,
148
+ manifest.key
149
+ );
150
+
151
+ let probeData = null;
152
+ if (options.probe && ctx.dataplaneUrl && !ctx.error) {
153
+ logger.log(chalk.blue('\nRunning runtime checks (--probe)...'));
154
+ try {
155
+ const pr = await testSystemViaPipeline(ctx.dataplaneUrl, manifest.key, authConfig, {}, {
156
+ timeout: options.probeTimeout || 120000
157
+ });
158
+ if (pr.success === false) {
159
+ logger.log(
160
+ chalk.yellow(`⚠ Probe request failed: ${pr.formattedError || pr.error || 'unknown error'}`)
161
+ );
162
+ } else {
163
+ probeData = unwrapApiData(pr);
164
+ }
165
+ } catch (e) {
166
+ logger.log(chalk.yellow(`⚠ Probe failed: ${e.message}`));
167
+ }
168
+ }
169
+
170
+ logDeployReadinessSummary({
171
+ environment,
172
+ dataplaneUrl: ctx.dataplaneUrl,
173
+ manifest,
174
+ datasources: ctx.datasources,
175
+ systemFromDataplane: ctx.system,
176
+ fetchError: ctx.error,
177
+ deploymentOk: deploymentOutcome.ok,
178
+ deploymentDetail: deploymentOutcome.ok ? null : deploymentOutcome,
179
+ probeData
180
+ });
181
+
96
182
  return result;
97
183
  }
98
184
 
99
185
  /**
100
186
  * Prepares deployment configuration (auth, controller URL, environment)
101
187
  * @async
102
- * @function prepareDeploymentConfig
103
188
  * @param {string} appName - Application name
104
189
  * @param {Object} options - Deployment options
105
190
  * @returns {Promise<Object>} Deployment configuration
@@ -119,16 +204,12 @@ async function prepareDeploymentConfig(appName, _options) {
119
204
 
120
205
  /**
121
206
  * Deploys external system via controller pipeline (same as regular apps)
122
- * Uses unified controller pipeline - no direct dataplane calls
123
207
  *
124
208
  * @async
125
- * @function deployExternalSystem
126
209
  * @param {string} appName - Application name
127
210
  * @param {Object} options - Deployment options
128
- * @param {string} [options.environment] - Environment (dev, tst, pro)
129
- * @param {string} [options.controller] - Controller URL
130
- * @param {boolean} [options.poll] - Poll for deployment status
131
- * @param {number} [options.pollInterval] - Polling interval in milliseconds (default: 500ms for external systems)
211
+ * @param {boolean} [options.probe] - Run dataplane validation/run after deploy
212
+ * @param {number} [options.probeTimeout] - Probe timeout ms (default 120000)
132
213
  * @returns {Promise<Object>} Deployment result
133
214
  * @throws {Error} If deployment fails
134
215
  */
@@ -139,7 +220,6 @@ async function deployExternalSystem(appName, options = {}) {
139
220
 
140
221
  logger.log(chalk.blue(`\n🚀 Deploying external system: ${appName}`));
141
222
 
142
- // Step 0: Validate before deployment (same as validate command)
143
223
  logger.log(chalk.blue('🔍 Validating external system before deployment...'));
144
224
  const validationResult = await validateExternalSystemComplete(appName, options);
145
225
 
@@ -148,15 +228,12 @@ async function deployExternalSystem(appName, options = {}) {
148
228
  throw new Error('Validation failed. Fix errors before deploying.');
149
229
  }
150
230
 
151
- logger.log(chalk.green(' Validation passed, proceeding with deployment...'));
231
+ logger.log(formatSuccessLine('Local validation passed, proceeding with deployment...'));
152
232
 
153
- // Step 1: Generate controller manifest (validated, ready for deployment)
154
233
  const manifest = await generateControllerManifest(appName, options);
155
234
 
156
- // Step 2: Get deployment configuration (auth, controller URL, etc.)
157
235
  const { environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
158
236
 
159
- // Step 3: Deploy via controller pipeline (same as regular apps)
160
237
  const result = await executeDeployAndDisplay(
161
238
  manifest,
162
239
  controllerUrl,
@@ -175,6 +252,7 @@ async function deployExternalSystem(appName, options = {}) {
175
252
  }
176
253
 
177
254
  module.exports = {
178
- deployExternalSystem
255
+ deployExternalSystem,
256
+ executeDeployAndDisplay,
257
+ fetchDataplaneDeployReadiness
179
258
  };
180
-
@@ -73,7 +73,7 @@ function generateReadme(systemKey, application, dataSources) {
73
73
  return {
74
74
  entityType: datasourceKeyOnly,
75
75
  displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
76
- fileName: `${systemKey}-datasource-${datasourceKeyOnly}.yaml`
76
+ fileName: `${systemKey}-datasource-${datasourceKeyOnly}.json`
77
77
  };
78
78
  });
79
79
 
@@ -1,3 +1,4 @@
1
+ const { formatSuccessLine, formatSuccessParagraph } = require('../utils/cli-test-layout-chalk');
1
2
  /**
2
3
  * External System Download Module
3
4
  *
@@ -129,7 +130,7 @@ async function setupAuthenticationAndDataplane(systemKey, _options, _config) {
129
130
  const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
130
131
  logger.log(chalk.blue('🌐 Resolving dataplane URL...'));
131
132
  const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
132
- logger.log(chalk.green(`✓ Dataplane URL: ${dataplaneUrl}`));
133
+ logger.log(formatSuccessLine(`Dataplane URL: ${dataplaneUrl}`));
133
134
 
134
135
  return { authConfig, dataplaneUrl };
135
136
  }
@@ -287,8 +288,8 @@ function validateAndLogDownloadedData(application, dataSources) {
287
288
  logger.log(chalk.blue('🔍 Validating downloaded data...'));
288
289
  validateDownloadedData(application, dataSources);
289
290
  const systemType = validateSystemType(application);
290
- logger.log(chalk.green(`✓ System type: ${systemType}`));
291
- logger.log(chalk.green(`✓ Found ${dataSources.length} datasource(s)`));
291
+ logger.log(formatSuccessLine(`System type: ${systemType}`));
292
+ logger.log(formatSuccessLine(`Found ${dataSources.length} datasource(s)`));
292
293
  return systemType;
293
294
  }
294
295
 
@@ -367,7 +368,7 @@ async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
367
368
  const deployJson = buildDeployJsonFromManifest(application, dataSources, version);
368
369
  const deployJsonPath = path.join(finalPath, `${systemKey}-deploy.json`);
369
370
  await fs.writeFile(deployJsonPath, JSON.stringify(deployJson, null, 2), 'utf8');
370
- logger.log(chalk.green(`✓ Created: ${path.relative(process.cwd(), deployJsonPath)}`));
371
+ logger.log(formatSuccessLine(`Created: ${path.relative(process.cwd(), deployJsonPath)}`));
371
372
 
372
373
  logger.log(chalk.blue('📂 Splitting deploy JSON into component files...'));
373
374
  const splitResult = await generator.splitDeployJson(deployJsonPath, finalPath, splitOptions);
@@ -390,7 +391,7 @@ async function processDownloadedSystem(systemKey, manifest, splitOptions = {}) {
390
391
  * @param {number} datasourceCount - Number of datasources
391
392
  */
392
393
  function displayDownloadSuccess(systemKey, finalPath, datasourceCount) {
393
- logger.log(chalk.green('\n✅ External system downloaded successfully!'));
394
+ logger.log(formatSuccessParagraph('External system downloaded successfully!'));
394
395
  logger.log(chalk.blue(`Location: ${finalPath}`));
395
396
  logger.log(chalk.blue(`System: ${systemKey}`));
396
397
  logger.log(chalk.blue(`Datasources: ${datasourceCount}`));
@@ -414,7 +415,7 @@ async function runConvertToJsonIfRequested(systemKey) {
414
415
  const { runConvert } = require('../commands/convert');
415
416
  try {
416
417
  await runConvert(systemKey, { format: 'json', force: true });
417
- logger.log(chalk.green('Converted component files to JSON'));
418
+ logger.log(formatSuccessLine('Converted component files to JSON'));
418
419
  } catch (convertErr) {
419
420
  throw new Error(`Download succeeded but convert to JSON failed: ${convertErr.message}`);
420
421
  }
@@ -9,13 +9,14 @@
9
9
  * @version 2.0.0
10
10
  */
11
11
 
12
+ const { formatSuccessLine } = require('../utils/cli-test-layout-chalk');
12
13
  const fs = require('fs').promises;
13
14
  const path = require('path');
14
15
  const handlebars = require('handlebars');
15
- const chalk = require('chalk');
16
16
  const logger = require('../utils/logger');
17
17
  const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
18
18
  const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
19
+ const { getKvPathSegmentForSecurityKey } = require('../utils/credential-secrets-env');
19
20
 
20
21
  // Register Handlebars helper for equality check
21
22
  handlebars.registerHelper('eq', (a, b) => a === b);
@@ -23,36 +24,39 @@ handlebars.registerHelper('json', (obj) => JSON.stringify(obj));
23
24
 
24
25
  /**
25
26
  * Build authentication object per schema authenticationVariablesByMethod.
26
- * Security values use kv://<systemKey>/<key> pattern.
27
+ * Security values use canonical kv://<systemKey>/<segment> paths (segment from getKvPathSegmentForSecurityKey).
27
28
  * @param {string} systemKey - External system key
28
29
  * @param {string} authType - Auth method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none)
29
30
  * @returns {{ method: string, variables: Object, security: Object }} Authentication object
30
31
  */
31
32
  function buildAuthenticationFromMethod(systemKey, authType) {
32
- const kv = (key) => `kv://${systemKey}/${key}`;
33
+ const kvPath = (securityKey) => {
34
+ const segment = getKvPathSegmentForSecurityKey(securityKey);
35
+ return segment ? `kv://${systemKey}/${segment}` : null;
36
+ };
33
37
  const method = authType || 'apikey';
34
38
  const base = 'https://api.example.com';
35
39
 
36
40
  const authMap = {
37
41
  oauth2: {
38
42
  variables: { baseUrl: base, tokenUrl: `${base}/oauth/token`, authorizationUrl: `${base}/oauth/authorize` },
39
- security: { clientId: kv('clientid'), clientSecret: kv('clientsecret') }
43
+ security: { clientId: kvPath('clientId'), clientSecret: kvPath('clientSecret') }
40
44
  },
41
45
  aad: {
42
46
  variables: { baseUrl: base, tokenUrl: 'https://login.microsoftonline.com/{tenant}/oauth2/v2.0/token', tenantId: '{tenant-id}' },
43
- security: { clientId: kv('clientid'), clientSecret: kv('clientsecret') }
47
+ security: { clientId: kvPath('clientId'), clientSecret: kvPath('clientSecret') }
44
48
  },
45
49
  apikey: {
46
50
  variables: { baseUrl: base, headerName: 'X-API-Key' },
47
- security: { apiKey: kv('apikey') }
51
+ security: { apiKey: kvPath('apiKey') }
48
52
  },
49
53
  basic: {
50
54
  variables: { baseUrl: base },
51
- security: { username: kv('username'), password: kv('password') }
55
+ security: { username: kvPath('username'), password: kvPath('password') }
52
56
  },
53
57
  queryParam: {
54
58
  variables: { baseUrl: base, paramName: 'api_key' },
55
- security: { paramValue: kv('paramvalue') }
59
+ security: { paramValue: kvPath('paramValue') }
56
60
  },
57
61
  oidc: {
58
62
  variables: { openIdConfigUrl: 'https://example.com/.well-known/openid-configuration', clientId: 'app-id' },
@@ -60,7 +64,7 @@ function buildAuthenticationFromMethod(systemKey, authType) {
60
64
  },
61
65
  hmac: {
62
66
  variables: { baseUrl: base, algorithm: 'sha256', signatureHeader: 'X-Signature' },
63
- security: { signingSecret: kv('signingsecret') }
67
+ security: { signingSecret: kvPath('signingSecret') }
64
68
  },
65
69
  none: {
66
70
  variables: {},
@@ -131,6 +135,68 @@ function resourceTypeToSchemaEntityType(resourceType) {
131
135
  return resourceType === 'document' ? 'documentStorage' : 'recordStorage';
132
136
  }
133
137
 
138
+ /**
139
+ * Build { dimKey, field } entries for root dimensions (v2.4+ dimensionBinding).
140
+ * @param {Object} dimensions - Map of dimension key → attribute path (e.g. metadata.country)
141
+ * @returns {Array<{ dimKey: string, field: string }>}
142
+ */
143
+ function buildDimensionBindingEntries(dimensions) {
144
+ if (!dimensions || typeof dimensions !== 'object' || Array.isArray(dimensions)) return [];
145
+ return Object.entries(dimensions).map(([dimKey, path]) => {
146
+ const field = typeof path === 'string' && path.startsWith('metadata.')
147
+ ? path.slice('metadata.'.length).trim()
148
+ : dimKey;
149
+ return { dimKey, field };
150
+ });
151
+ }
152
+
153
+ /**
154
+ * @param {string} schemaEntityType - Resolved schema entityType
155
+ * @returns {boolean}
156
+ */
157
+ function isStorageEntityType(schemaEntityType) {
158
+ return schemaEntityType === 'recordStorage' || schemaEntityType === 'documentStorage';
159
+ }
160
+
161
+ /**
162
+ * Puts externalId first for storage entity types when present (schema v2.4 join identity).
163
+ * @param {string[]} keys - Attribute / metadata property names
164
+ * @param {string} schemaEntityType - Resolved schema entityType
165
+ * @returns {string[]}
166
+ */
167
+ function orderMetadataAttributeKeys(keys, schemaEntityType) {
168
+ const out = [...keys];
169
+ if (isStorageEntityType(schemaEntityType) && out.includes('externalId')) {
170
+ return ['externalId', ...out.filter(k => k !== 'externalId')];
171
+ }
172
+ return out;
173
+ }
174
+
175
+ /**
176
+ * Default fieldMappings.attributes + metadata key order when the wizard/config omits attributes.
177
+ * @param {string} schemaEntityType - Resolved schema entityType
178
+ * @returns {{ merged: Object, keys: string[] }}
179
+ */
180
+ function defaultAttributesForEntityType(schemaEntityType) {
181
+ if (isStorageEntityType(schemaEntityType)) {
182
+ return {
183
+ merged: {
184
+ externalId: { expression: '{{raw.id}}' },
185
+ id: { expression: '{{raw.id}}' },
186
+ name: { expression: '{{raw.name}}' }
187
+ },
188
+ keys: ['externalId', 'id', 'name']
189
+ };
190
+ }
191
+ return {
192
+ merged: {
193
+ id: { expression: '{{raw.id}}' },
194
+ name: { expression: '{{raw.name}}' }
195
+ },
196
+ keys: ['id', 'name']
197
+ };
198
+ }
199
+
134
200
  /**
135
201
  * Build datasource context object for template rendering
136
202
  * @param {Object} opts - Options
@@ -147,9 +213,31 @@ function resourceTypeToSchemaEntityType(resourceType) {
147
213
  function buildDatasourceContext({ config, datasourceKey, dimensions, attributes, fullDatasourceKey, entityKey, schemaEntityType, resourceType }) {
148
214
  const displayName = config.datasourceDisplayName || datasourceKey.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
149
215
  const description = config.datasourceDescription || `External datasource for ${datasourceKey}`;
216
+ const attrsIn = attributes && typeof attributes === 'object' && !Array.isArray(attributes) ? attributes : {};
217
+ let mergedAttributes = { ...attrsIn };
218
+
219
+ let attributeKeysForMetadata;
220
+ if (Object.keys(mergedAttributes).length > 0) {
221
+ attributeKeysForMetadata = orderMetadataAttributeKeys(Object.keys(mergedAttributes), schemaEntityType);
222
+ } else {
223
+ const defaults = defaultAttributesForEntityType(schemaEntityType);
224
+ mergedAttributes = defaults.merged;
225
+ attributeKeysForMetadata = defaults.keys;
226
+ }
227
+
228
+ if (isStorageEntityType(schemaEntityType) && !mergedAttributes.externalId) {
229
+ mergedAttributes.externalId = { expression: '{{raw.id}}' };
230
+ attributeKeysForMetadata = orderMetadataAttributeKeys(Object.keys(mergedAttributes), schemaEntityType);
231
+ }
232
+
150
233
  const primaryKey = Array.isArray(config.primaryKey) && config.primaryKey.length > 0
151
234
  ? config.primaryKey
152
- : ['id'];
235
+ : (attributeKeysForMetadata.includes('id') ? ['id'] : [attributeKeysForMetadata[0]]);
236
+ const labelKey = Array.isArray(config.labelKey) && config.labelKey.length > 0
237
+ ? config.labelKey
238
+ : attributeKeysForMetadata;
239
+ const dimensionBindingEntries = buildDimensionBindingEntries(dimensions);
240
+
153
241
  return {
154
242
  fullDatasourceKey,
155
243
  entityKey,
@@ -159,9 +247,11 @@ function buildDatasourceContext({ config, datasourceKey, dimensions, attributes,
159
247
  schemaEntityType,
160
248
  resourceType,
161
249
  primaryKey,
250
+ labelKey,
251
+ attributeKeysForMetadata,
252
+ dimensionBindingEntries,
162
253
  systemType: config.systemType || 'openapi',
163
- dimensions: Object.keys(dimensions).length > 0 ? dimensions : null,
164
- attributes: Object.keys(attributes).length > 0 ? attributes : null,
254
+ attributes: mergedAttributes,
165
255
  raw: { id: '{{raw.id}}', name: '{{raw.name}}' }
166
256
  };
167
257
  }
@@ -246,7 +336,7 @@ async function generateExternalSystemFiles(appPath, appName, config, format = 'y
246
336
 
247
337
  // Generate external system file
248
338
  const systemPath = await generateExternalSystemTemplate(appPath, systemKey, config, fmt);
249
- logger.log(chalk.green(`✓ Generated external system: ${path.basename(systemPath)}`));
339
+ logger.log(formatSuccessLine(`Generated external system: ${path.basename(systemPath)}`));
250
340
 
251
341
  // Generate datasource JSON files
252
342
  const datasourcePaths = [];
@@ -274,7 +364,7 @@ async function generateExternalSystemFiles(appPath, appName, config, format = 'y
274
364
 
275
365
  const datasourcePath = await generateExternalDataSourceTemplate(appPath, datasourceKey, datasourceConfig, fmt);
276
366
  datasourcePaths.push(datasourcePath);
277
- logger.log(chalk.green(`✓ Generated datasource: ${path.basename(datasourcePath)}`));
367
+ logger.log(formatSuccessLine(`Generated datasource: ${path.basename(datasourcePath)}`));
278
368
  }
279
369
 
280
370
  // Update application config with externalIntegration block
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @fileoverview Dispatch rules for external test-integration (system vs per-datasource POST /validation/run).
3
+ */
4
+
5
+ 'use strict';
6
+
7
+ /**
8
+ * Use one externalSystem-scoped validation run when multiple datasources exist (default).
9
+ * Per-datasource runs when {@code options.perDatasource}, a datasource filter, or custom payload is set.
10
+ *
11
+ * @param {Object} options - CLI options
12
+ * @param {string} [options.datasource] - Single-datasource filter
13
+ * @param {boolean} [options.perDatasource] - Force one POST per datasource (externalDataSource scope)
14
+ * @param {Array<{data?: Object}>} datasourcesToTest - Entries from manifest
15
+ * @param {*} customPayload - Parsed payload from {@code --payload}, or null
16
+ * @returns {boolean}
17
+ */
18
+ function shouldUseSystemLevelIntegrationCall(options, datasourcesToTest, customPayload) {
19
+ const noDatasourceFilter = !options || !options.datasource;
20
+ const multi = Array.isArray(datasourcesToTest) && datasourcesToTest.length > 1;
21
+ const noCustomPayload = customPayload === null || customPayload === undefined;
22
+ const forcePerDs = options && options.perDatasource === true;
23
+ return noDatasourceFilter && multi && noCustomPayload && !forcePerDs;
24
+ }
25
+
26
+ module.exports = { shouldUseSystemLevelIntegrationCall };
@@ -11,6 +11,7 @@
11
11
  const chalk = require('chalk');
12
12
  const logger = require('../utils/logger');
13
13
  const testHelpers = require('../utils/external-system-test-helpers');
14
+ const { infoLine } = require('../utils/cli-test-layout-chalk');
14
15
 
15
16
  /**
16
17
  * Executes test for a single datasource
@@ -62,7 +63,10 @@ async function testSingleDatasourceIntegration(datasourceFile, systemKey, datapl
62
63
  const datasource = datasourceFile.data;
63
64
  const datasourceKey = datasource.key;
64
65
 
65
- logger.log(chalk.blue(`\n📡 Testing datasource: ${datasourceKey}`));
66
+ if (options.verbose) {
67
+ logger.log('');
68
+ logger.log(infoLine(`📡 Testing datasource: ${datasourceKey}`));
69
+ }
66
70
 
67
71
  // Determine payload to use
68
72
  const payloadTemplate = testHelpers.determinePayloadTemplate(datasource, datasourceKey, customPayload);
@@ -8,8 +8,6 @@
8
8
  * @version 2.0.0
9
9
  */
10
10
 
11
- const chalk = require('chalk');
12
- const logger = require('../utils/logger');
13
11
  const externalSystemSchema = require('../schema/external-system.schema.json');
14
12
  const { validateAgainstSchema } = require('../utils/external-system-validators');
15
13
 
@@ -63,7 +61,6 @@ function validateSystemFiles(systemFiles, schema) {
63
61
  * @param {Object} results - Test results object
64
62
  */
65
63
  function validateSystemFilesForTest(systemFiles, results) {
66
- logger.log(chalk.blue('📋 Validating system files...'));
67
64
  const systemValidation = validateSystemFiles(systemFiles, externalSystemSchema);
68
65
  results.valid = systemValidation.valid;
69
66
  results.errors.push(...systemValidation.errors);
@@ -81,7 +78,6 @@ function validateSystemFilesForTest(systemFiles, results) {
81
78
  * @param {Function} determineDatasourcesToTest - Function to determine datasources to test
82
79
  */
83
80
  function validateDatasourceFilesForTest(datasourceFiles, systemFiles, results, options, validateSingleDatasource, determineDatasourcesToTest) {
84
- logger.log(chalk.blue('📋 Validating datasource files...'));
85
81
  const datasourcesToTest = determineDatasourcesToTest(datasourceFiles, options.datasource);
86
82
  const systemKey = systemFiles.length > 0 ? systemFiles[0].data.key : null;
87
83