@aifabrix/builder 2.43.0 → 2.44.0

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