@aifabrix/builder 2.44.5 → 2.44.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/cli-layout.mdc +1 -1
- package/.cursor/rules/project-rules.mdc +1 -1
- package/.npmrc.token +1 -1
- package/README.md +15 -23
- package/integration/hubspot-test/README.md +2 -0
- package/integration/hubspot-test/test.js +5 -3
- package/jest.projects.js +48 -2
- package/lib/api/controller-health.api.js +49 -0
- package/lib/api/dimension-values.api.js +82 -0
- package/lib/api/dimensions.api.js +114 -0
- package/lib/api/external-systems.api.js +1 -0
- package/lib/api/integration-clients.api.js +168 -0
- package/lib/api/types/dimension-values.types.js +28 -0
- package/lib/api/types/dimensions.types.js +31 -0
- package/lib/api/types/integration-clients.types.js +45 -0
- package/lib/api/validation-runner.js +46 -25
- package/lib/app/deploy-config.js +11 -1
- package/lib/app/deploy-status-display.js +3 -3
- package/lib/app/deploy.js +36 -14
- package/lib/app/display.js +15 -11
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +1 -1
- package/lib/app/restart-display.js +95 -0
- package/lib/app/rotate-secret.js +1 -1
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +44 -12
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +99 -73
- package/lib/build/index.js +75 -45
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +445 -0
- package/lib/cli/setup-app.js +20 -2
- package/lib/cli/setup-auth.js +26 -0
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra.js +134 -61
- package/lib/cli/setup-integration-client.js +182 -0
- package/lib/cli/setup-parameters.js +21 -2
- package/lib/cli/setup-platform.js +102 -0
- package/lib/cli/setup-secrets.js +18 -6
- package/lib/cli/setup-utility.js +78 -33
- package/lib/commands/datasource-capability-dimension-cli.js +128 -0
- package/lib/commands/datasource-capability-output.js +29 -0
- package/lib/commands/datasource-capability-relate-cli.js +140 -0
- package/lib/commands/datasource-capability.js +411 -0
- package/lib/commands/datasource-unified-test-cli.options.js +1 -1
- package/lib/commands/datasource.js +53 -13
- package/lib/commands/dev-down.js +3 -3
- package/lib/commands/dev-infra-gate.js +32 -0
- package/lib/commands/dev-init.js +13 -7
- package/lib/commands/dimension-value.js +179 -0
- package/lib/commands/dimension.js +330 -0
- package/lib/commands/integration-client.js +430 -0
- package/lib/commands/login-device.js +65 -30
- package/lib/commands/login.js +21 -10
- package/lib/commands/parameters-validate.js +78 -13
- package/lib/commands/repair-datasource-auto-rbac.js +166 -0
- package/lib/commands/repair-datasource-keys.js +10 -5
- package/lib/commands/repair-datasource.js +19 -7
- package/lib/commands/repair-env-template.js +4 -1
- package/lib/commands/repair-openapi-sync.js +172 -0
- package/lib/commands/repair-persist.js +102 -0
- package/lib/commands/repair-rbac-extract.js +27 -0
- package/lib/commands/repair-rbac-migrate.js +186 -0
- package/lib/commands/repair-rbac.js +214 -31
- package/lib/commands/repair-system-alignment.js +246 -0
- package/lib/commands/repair-system-permissions.js +168 -0
- package/lib/commands/repair.js +120 -338
- package/lib/commands/secure.js +1 -1
- package/lib/commands/setup-modes.js +455 -0
- package/lib/commands/setup-prompts.js +388 -0
- package/lib/commands/setup.js +149 -0
- package/lib/commands/teardown.js +228 -0
- package/lib/commands/up-common.js +79 -19
- package/lib/commands/up-dataplane.js +33 -11
- package/lib/commands/up-miso.js +7 -11
- package/lib/commands/upload.js +109 -23
- package/lib/commands/wizard-core-helpers.js +14 -11
- package/lib/commands/wizard-core.js +6 -5
- package/lib/commands/wizard-dataplane.js +2 -2
- package/lib/commands/wizard-entity-selection.js +4 -3
- package/lib/commands/wizard-headless.js +2 -1
- package/lib/commands/wizard.js +2 -1
- package/lib/constants/infra-compose-service-names.js +40 -0
- package/lib/core/env-reader.js +16 -3
- package/lib/core/secrets-admin-env.js +101 -0
- package/lib/core/secrets-ensure-infra.js +34 -1
- package/lib/core/secrets-ensure.js +88 -66
- package/lib/core/secrets-env-content.js +432 -0
- package/lib/core/secrets-env-write.js +27 -1
- package/lib/core/secrets-load.js +248 -0
- package/lib/core/secrets-names.js +32 -0
- package/lib/core/secrets.js +17 -757
- package/lib/datasource/capability/basic-exposure.js +76 -0
- package/lib/datasource/capability/capability-diff-slice.js +41 -0
- package/lib/datasource/capability/capability-key.js +34 -0
- package/lib/datasource/capability/capability-resolve.js +172 -0
- package/lib/datasource/capability/capability-storage-keys.js +22 -0
- package/lib/datasource/capability/copy-operations.js +348 -0
- package/lib/datasource/capability/copy-test-payload.js +139 -0
- package/lib/datasource/capability/create-operations.js +235 -0
- package/lib/datasource/capability/dimension-operations.js +151 -0
- package/lib/datasource/capability/dimension-validate.js +219 -0
- package/lib/datasource/capability/json-pointer.js +31 -0
- package/lib/datasource/capability/reference-rewrite.js +51 -0
- package/lib/datasource/capability/relate-operations.js +325 -0
- package/lib/datasource/capability/relate-validate.js +219 -0
- package/lib/datasource/capability/remove-operations.js +275 -0
- package/lib/datasource/capability/run-capability-copy.js +152 -0
- package/lib/datasource/capability/run-capability-diff.js +135 -0
- package/lib/datasource/capability/run-capability-dimension.js +291 -0
- package/lib/datasource/capability/run-capability-edit.js +377 -0
- package/lib/datasource/capability/run-capability-relate.js +193 -0
- package/lib/datasource/capability/run-capability-remove.js +105 -0
- package/lib/datasource/capability/templates/minimal-fetch.json +18 -0
- package/lib/datasource/capability/validate-capability-slice.js +35 -0
- package/lib/datasource/list.js +136 -23
- package/lib/datasource/log-viewer.js +2 -4
- package/lib/datasource/unified-validation-run.js +51 -16
- package/lib/datasource/validate.js +53 -1
- package/lib/deployment/deploy-poll-ui.js +60 -0
- package/lib/deployment/deployer-status.js +29 -3
- package/lib/deployment/deployer.js +48 -30
- package/lib/deployment/environment.js +7 -2
- package/lib/deployment/poll-interval.js +72 -0
- package/lib/deployment/push.js +11 -9
- package/lib/external-system/deploy.js +4 -1
- package/lib/external-system/download.js +61 -32
- package/lib/external-system/sync-deploy-manifest.js +33 -0
- package/lib/infrastructure/index.js +49 -19
- package/lib/infrastructure/orphan-infra-docker-teardown.js +177 -0
- package/lib/parameters/infra-kv-discovery.js +29 -4
- package/lib/parameters/infra-parameter-catalog.js +6 -3
- package/lib/parameters/infra-parameter-validate.js +67 -19
- package/lib/resolvers/datasource-resolver.js +53 -0
- package/lib/resolvers/dimension-file.js +52 -0
- package/lib/resolvers/manifest-resolver.js +133 -0
- package/lib/schema/external-datasource.schema.json +183 -53
- package/lib/schema/external-system.schema.json +23 -10
- package/lib/schema/infra.parameter.yaml +26 -11
- package/lib/schema/wizard-config.schema.json +1 -1
- package/lib/utils/aifabrix-config-dir-walk.js +40 -0
- package/lib/utils/aifabrix-runtime-config-dir.js +26 -3
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/bash-secret-env.js +59 -0
- package/lib/utils/cli-secrets-error-format.js +78 -0
- package/lib/utils/cli-test-layout-chalk.js +31 -9
- package/lib/utils/cli-utils.js +4 -36
- package/lib/utils/datasource-test-run-display.js +8 -0
- package/lib/utils/dev-hosts-helper.js +3 -2
- package/lib/utils/dev-init-ssh-merge.js +2 -1
- package/lib/utils/docker-build.js +17 -9
- package/lib/utils/docker-reload-mount.js +127 -0
- package/lib/utils/external-readme.js +71 -2
- package/lib/utils/external-system-local-test-tty.js +3 -2
- package/lib/utils/external-system-readiness-core.js +45 -12
- package/lib/utils/external-system-readiness-deploy-display.js +3 -3
- package/lib/utils/external-system-readiness-display-internals.js +33 -3
- package/lib/utils/external-system-readiness-display.js +10 -1
- package/lib/utils/file-upload.js +40 -3
- package/lib/utils/health-check-db-init.js +107 -0
- package/lib/utils/health-check-public-warn.js +69 -0
- package/lib/utils/health-check-url.js +19 -4
- package/lib/utils/health-check.js +135 -105
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +108 -25
- package/lib/utils/postgres-wipe.js +212 -0
- package/lib/utils/register-aifabrix-shell-env.js +15 -0
- package/lib/utils/remote-dev-auth.js +21 -5
- package/lib/utils/remote-docker-env.js +9 -1
- package/lib/utils/remote-secrets-loader.js +42 -3
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-helpers.js +17 -10
- package/lib/utils/secrets-kv-refs.js +42 -0
- package/lib/utils/secrets-kv-scope.js +19 -2
- package/lib/utils/secrets-materialize-local.js +134 -0
- package/lib/utils/secrets-path.js +24 -10
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/system-builder-root.js +34 -0
- package/lib/utils/url-declarative-resolve-build.js +6 -1
- package/lib/utils/url-declarative-runtime-base-path.js +32 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry.js +23 -12
- package/lib/utils/validation-poll-ui.js +81 -0
- package/lib/utils/validation-run-poll.js +29 -5
- package/lib/utils/with-muted-logger.js +53 -0
- package/package.json +1 -1
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +7 -0
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.nyc_output/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/55e9d034-ddab-4579-a706-e02a91d75c91.json +0 -1
- package/.nyc_output/processinfo/index.json +0 -1
- package/lib/api/service-users.api.js +0 -150
- package/lib/api/types/service-users.types.js +0 -65
- package/lib/cli/setup-service-user.js +0 -187
- package/lib/commands/service-user.js +0 -429
package/lib/cli/setup-secrets.js
CHANGED
|
@@ -24,8 +24,8 @@ Subcommands:
|
|
|
24
24
|
|
|
25
25
|
Also: aifabrix secure Encrypt secrets.local.yaml (ISO 27001)
|
|
26
26
|
|
|
27
|
-
Default "secret set" (no --shared): writes only to your user secrets.local.yaml
|
|
28
|
-
|
|
27
|
+
Default "secret set" (no --shared): writes only to your user secrets.local.yaml under the
|
|
28
|
+
resolved AI Fabrix home (config key aifabrix-home, else ~/.aifabrix). Use --shared to write the
|
|
29
29
|
shared store from config aifabrix-secrets (another file or https), not that user file.
|
|
30
30
|
|
|
31
31
|
Examples:
|
|
@@ -46,8 +46,8 @@ Examples:
|
|
|
46
46
|
|
|
47
47
|
const SECRET_SET_HELP_AFTER = `
|
|
48
48
|
Where the value is stored:
|
|
49
|
-
No --shared User file secrets.local.yaml
|
|
50
|
-
|
|
49
|
+
No --shared User file secrets.local.yaml under the resolved AI Fabrix home
|
|
50
|
+
(config key aifabrix-home, else ~/.aifabrix/). Used for app/integration
|
|
51
51
|
secrets and kv:// resolution on your machine.
|
|
52
52
|
--shared The store set in config.yaml as aifabrix-secrets: a YAML file path or
|
|
53
53
|
an https secrets API (never the user-only file above unless you point
|
|
@@ -70,6 +70,13 @@ Examples:
|
|
|
70
70
|
`;
|
|
71
71
|
|
|
72
72
|
const SECRET_REMOVE_ALL_HELP_AFTER = `
|
|
73
|
+
Where keys are removed:
|
|
74
|
+
Default All keys in your user secrets.local.yaml (under resolved aifabrix home,
|
|
75
|
+
often ~/.aifabrix/). This is NOT the shared store unless you deliberately
|
|
76
|
+
point aifabrix-secrets there.
|
|
77
|
+
--shared Every key in the shared store from config aifabrix-secrets (YAML file
|
|
78
|
+
path or https secrets API). Requires that setting; use secret set-secrets-file if needed.
|
|
79
|
+
|
|
73
80
|
You will be asked to type "yes" to confirm unless you pass --yes.
|
|
74
81
|
|
|
75
82
|
Examples:
|
|
@@ -103,9 +110,14 @@ function setupSecretRemoveAllCommand(secretCmd) {
|
|
|
103
110
|
const { handleSecretsRemoveAll } = require('../commands/secrets-remove-all');
|
|
104
111
|
secretCmd
|
|
105
112
|
.command('remove-all')
|
|
106
|
-
.description(
|
|
113
|
+
.description(
|
|
114
|
+
'Remove every key from user secrets.local.yaml (add --shared for aifabrix-secrets store; confirm unless --yes)'
|
|
115
|
+
)
|
|
107
116
|
.addHelpText('after', SECRET_REMOVE_ALL_HELP_AFTER)
|
|
108
|
-
.option(
|
|
117
|
+
.option(
|
|
118
|
+
'--shared',
|
|
119
|
+
'Remove every key from shared secrets (config aifabrix-secrets: YAML file or https API)'
|
|
120
|
+
)
|
|
109
121
|
.option('-y, --yes', 'Skip confirmation prompt (non-interactive / scripts)')
|
|
110
122
|
.action(async options => {
|
|
111
123
|
try {
|
package/lib/cli/setup-utility.js
CHANGED
|
@@ -46,6 +46,8 @@ Examples:
|
|
|
46
46
|
|
|
47
47
|
$ aifabrix repair hubspot-demo --doc
|
|
48
48
|
Regenerate README.md from the current deployment manifest only (other drift fixes unchanged).
|
|
49
|
+
|
|
50
|
+
Before overwriting files, repair writes timestamped backups under integration/<systemKey>/backup/ (same as datasource capability copy). Use --no-backup to skip.
|
|
49
51
|
`;
|
|
50
52
|
|
|
51
53
|
/**
|
|
@@ -193,50 +195,93 @@ function setupSplitJsonCommand(program) {
|
|
|
193
195
|
});
|
|
194
196
|
}
|
|
195
197
|
|
|
198
|
+
function buildRepairExternalIntegrationOptions(appName, options, format) {
|
|
199
|
+
const all = options.all === true;
|
|
200
|
+
return {
|
|
201
|
+
auth: options.auth,
|
|
202
|
+
doc: options.doc || all,
|
|
203
|
+
dryRun: options.dryRun,
|
|
204
|
+
format,
|
|
205
|
+
rbac: options.rbac || all,
|
|
206
|
+
expose: options.expose || all,
|
|
207
|
+
sync: options.sync || all,
|
|
208
|
+
api: options.api || all,
|
|
209
|
+
test: options.test || all,
|
|
210
|
+
backup: options.backup,
|
|
211
|
+
noBackup: options.noBackup
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function logRepairResult(options, result) {
|
|
216
|
+
if (options.dryRun && result.updated && result.changes.length > 0) {
|
|
217
|
+
logger.log(chalk.yellow('\nWould apply:'));
|
|
218
|
+
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
if (Array.isArray(result.backupPaths) && result.backupPaths.length > 0) {
|
|
222
|
+
result.backupPaths.forEach((p) => logger.log(chalk.gray(`Backup: ${p}`)));
|
|
223
|
+
}
|
|
224
|
+
if (result.updated) {
|
|
225
|
+
logger.log(formatSuccessParagraph('Repaired external integration config.'));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
if (Array.isArray(result.changes) && result.changes.length > 0) {
|
|
229
|
+
logger.log(formatSuccessParagraph('Repair actions completed.'));
|
|
230
|
+
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
if (result.readmeRegenerated) {
|
|
234
|
+
logger.log(formatSuccessParagraph('Regenerated README.md from deployment manifest.'));
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
logger.log(chalk.gray('No changes needed; config already matches files on disk.'));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Handler for `aifabrix repair <systemKey>`.
|
|
242
|
+
* @param {string} appName
|
|
243
|
+
* @param {object} options - Commander options
|
|
244
|
+
* @returns {Promise<void>}
|
|
245
|
+
*/
|
|
246
|
+
async function handleRepairCommand(appName, options) {
|
|
247
|
+
const { repairExternalIntegration } = require('../commands/repair');
|
|
248
|
+
const { detectAppType } = require('../utils/paths');
|
|
249
|
+
const { appPath } = await detectAppType(appName);
|
|
250
|
+
logOfflinePathWhenType(appPath);
|
|
251
|
+
let format = 'yaml';
|
|
252
|
+
try {
|
|
253
|
+
const config = require('../core/config');
|
|
254
|
+
format = (await config.getFormat()) || format;
|
|
255
|
+
} catch (_) {
|
|
256
|
+
/* default yaml when config unavailable */
|
|
257
|
+
}
|
|
258
|
+
const result = await repairExternalIntegration(
|
|
259
|
+
appName,
|
|
260
|
+
buildRepairExternalIntegrationOptions(appName, options, format)
|
|
261
|
+
);
|
|
262
|
+
logRepairResult(options, result);
|
|
263
|
+
}
|
|
264
|
+
|
|
196
265
|
function setupRepairCommand(program) {
|
|
197
266
|
program.command('repair <systemKey>')
|
|
198
267
|
.description('Fix external integration drift (files, RBAC, manifest, …)')
|
|
268
|
+
.option('--all', 'Run all repair actions (api, doc, expose, rbac, sync, test)')
|
|
269
|
+
.option(
|
|
270
|
+
'--api',
|
|
271
|
+
'Validate and sync API contracts needed for OpenAPI/MCP (uses local OpenAPI specs at integration/<systemKey>/openapi/*.json when present)'
|
|
272
|
+
)
|
|
199
273
|
.option('--auth <method>', 'Set authentication method (oauth2, aad, apikey, basic, queryParam, oidc, hmac, none); updates system file and env.template')
|
|
200
274
|
.option('--doc', 'Regenerate README.md from deployment manifest')
|
|
201
|
-
.option('--dry-run', 'Report changes only; do not write')
|
|
202
|
-
.option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist')
|
|
203
275
|
.option('--expose', 'Set exposed.schema on each datasource from all fieldMappings.attributes keys (metadata.<key>); removes deprecated exposed.attributes if present')
|
|
276
|
+
.option('--rbac', 'Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist')
|
|
204
277
|
.option('--sync', 'Add default sync section to datasources that lack it')
|
|
205
278
|
.option('--test', 'Generate testPayload.payloadTemplate and testPayload.expectedResult from attributes')
|
|
279
|
+
.option('--no-backup', 'Skip timestamped copies under integration/<systemKey>/backup/')
|
|
280
|
+
.option('--dry-run', 'Report changes only; do not write')
|
|
206
281
|
.addHelpText('after', REPAIR_HELP_AFTER)
|
|
207
282
|
.action(async(appName, options) => {
|
|
208
283
|
try {
|
|
209
|
-
|
|
210
|
-
const { detectAppType } = require('../utils/paths');
|
|
211
|
-
const { appPath } = await detectAppType(appName);
|
|
212
|
-
logOfflinePathWhenType(appPath);
|
|
213
|
-
let format = 'yaml';
|
|
214
|
-
try {
|
|
215
|
-
const config = require('../core/config');
|
|
216
|
-
format = (await config.getFormat()) || format;
|
|
217
|
-
} catch (_) {
|
|
218
|
-
// use default yaml when config unavailable
|
|
219
|
-
}
|
|
220
|
-
const result = await repairExternalIntegration(appName, {
|
|
221
|
-
auth: options.auth,
|
|
222
|
-
doc: options.doc,
|
|
223
|
-
dryRun: options.dryRun,
|
|
224
|
-
format,
|
|
225
|
-
rbac: options.rbac,
|
|
226
|
-
expose: options.expose,
|
|
227
|
-
sync: options.sync,
|
|
228
|
-
test: options.test
|
|
229
|
-
});
|
|
230
|
-
if (options.dryRun && result.updated && result.changes.length > 0) {
|
|
231
|
-
logger.log(chalk.yellow('\nWould apply:'));
|
|
232
|
-
result.changes.forEach(c => logger.log(chalk.gray(` ${c}`)));
|
|
233
|
-
} else if (result.updated) {
|
|
234
|
-
logger.log(formatSuccessParagraph('Repaired external integration config.'));
|
|
235
|
-
} else if (result.readmeRegenerated) {
|
|
236
|
-
logger.log(formatSuccessParagraph('Regenerated README.md from deployment manifest.'));
|
|
237
|
-
} else {
|
|
238
|
-
logger.log(chalk.gray('No changes needed; config already matches files on disk.'));
|
|
239
|
-
}
|
|
284
|
+
await handleRepairCommand(appName, options);
|
|
240
285
|
} catch (error) {
|
|
241
286
|
handleCommandError(error, 'repair');
|
|
242
287
|
process.exit(1);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `datasource capability dimension` command registration.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview dimension binding CLI
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const logger = require('../utils/logger');
|
|
12
|
+
const {
|
|
13
|
+
formatBlockingError,
|
|
14
|
+
headerKeyValue,
|
|
15
|
+
infoLine,
|
|
16
|
+
formatSuccessLine,
|
|
17
|
+
colorRollupPrefixedLine
|
|
18
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
19
|
+
const { runCapabilityDimension } = require('../datasource/capability/run-capability-dimension');
|
|
20
|
+
const { printCapabilitySuccessFooter } = require('./datasource-capability-output');
|
|
21
|
+
|
|
22
|
+
const CAP_DIMENSION_HELP = `
|
|
23
|
+
Adds or replaces one root **dimensions.<key>** binding (metadata-only; no pipeline).
|
|
24
|
+
|
|
25
|
+
Examples:
|
|
26
|
+
$ aifabrix datasource capability dimension test-e2e-hubspot-companies --dimension market --type local --field country
|
|
27
|
+
$ aifabrix datasource capability dimension test-e2e-hubspot-companies --dimension owner --type fk --via hubspotOwner:owner --actor email
|
|
28
|
+
`;
|
|
29
|
+
|
|
30
|
+
function parseVia(raw) {
|
|
31
|
+
const list = Array.isArray(raw) ? raw : [];
|
|
32
|
+
return list.map((x) => String(x).trim()).filter(Boolean);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function logDimensionValidationOutcome(result) {
|
|
36
|
+
logger.log(formatSuccessLine('Local validation passed'));
|
|
37
|
+
if (result.remoteValidation?.ok) {
|
|
38
|
+
logger.log(formatSuccessLine('Remote validation passed'));
|
|
39
|
+
} else {
|
|
40
|
+
logger.log(colorRollupPrefixedLine('⚠ Remote validation skipped (not authenticated)'));
|
|
41
|
+
}
|
|
42
|
+
if (Array.isArray(result.semanticWarnings) && result.semanticWarnings.length > 0) {
|
|
43
|
+
result.semanticWarnings.forEach((w) => logger.log(colorRollupPrefixedLine(`⚠ ${w}`)));
|
|
44
|
+
}
|
|
45
|
+
logger.log(formatSuccessLine('Dimension binding updated'));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} fileOrKey
|
|
50
|
+
* @param {object} options - Commander options
|
|
51
|
+
* @returns {Promise<void>}
|
|
52
|
+
*/
|
|
53
|
+
async function runDimensionAction(fileOrKey, options) {
|
|
54
|
+
const via = parseVia(options.via);
|
|
55
|
+
|
|
56
|
+
const result = await runCapabilityDimension({
|
|
57
|
+
fileOrKey,
|
|
58
|
+
dimension: options.dimension,
|
|
59
|
+
type: options.type,
|
|
60
|
+
field: options.field,
|
|
61
|
+
via,
|
|
62
|
+
actor: options.actor,
|
|
63
|
+
operator: options.operator,
|
|
64
|
+
required: options.required,
|
|
65
|
+
dryRun: Boolean(options.dryRun),
|
|
66
|
+
noBackup: Boolean(options.noBackup),
|
|
67
|
+
overwrite: Boolean(options.overwrite)
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (result.dryRun) {
|
|
71
|
+
logger.log(infoLine('Dry run — planned JSON Patch operations:'));
|
|
72
|
+
logger.log('');
|
|
73
|
+
logger.log(JSON.stringify(result.patchOperations, null, 2));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
logDimensionValidationOutcome(result);
|
|
78
|
+
|
|
79
|
+
if (result.backupPath) {
|
|
80
|
+
logger.log(headerKeyValue('Backup:', result.backupPath));
|
|
81
|
+
}
|
|
82
|
+
printCapabilitySuccessFooter(result.resolvedPath, result.updatedSections, 'Updated');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {import('commander').Command} cap
|
|
87
|
+
* @returns {void}
|
|
88
|
+
*/
|
|
89
|
+
function setupCapabilityDimensionCommand(cap) {
|
|
90
|
+
cap
|
|
91
|
+
.command('dimension <file-or-key>')
|
|
92
|
+
.description('Add or replace one root dimensions binding (local or FK-backed)')
|
|
93
|
+
.requiredOption('--dimension <key>', 'Dimension key (ABAC-facing key; underscores allowed)')
|
|
94
|
+
.requiredOption('--type <type>', 'local | fk')
|
|
95
|
+
.option('--field <name>', 'For type=local: normalized attribute name in metadataSchema.properties')
|
|
96
|
+
.option(
|
|
97
|
+
'--via <fk:dimension>',
|
|
98
|
+
'For type=fk: hop as <fkName>:<dimensionKey>; repeat for multi-hop traversal',
|
|
99
|
+
(value, prev) => {
|
|
100
|
+
const list = prev || [];
|
|
101
|
+
list.push(value);
|
|
102
|
+
return list;
|
|
103
|
+
},
|
|
104
|
+
[]
|
|
105
|
+
)
|
|
106
|
+
.option('--actor <actor>', 'For type=fk: displayName | email | userId | groups | roles')
|
|
107
|
+
.option('--operator <op>', 'For type=fk: eq | in (default depends on actor)')
|
|
108
|
+
.option('--required', 'Set dimensions.<key>.required=true')
|
|
109
|
+
.option('--no-required', 'Set dimensions.<key>.required=false')
|
|
110
|
+
.option('--dry-run', 'Print JSON Patch operations; do not write')
|
|
111
|
+
.option('--overwrite', 'Replace existing dimensions.<key> binding')
|
|
112
|
+
.option('--no-backup', 'Skip backup copy under integration/<app>/backup/')
|
|
113
|
+
.addHelpText('after', CAP_DIMENSION_HELP)
|
|
114
|
+
.action(async(fileOrKey, options) => {
|
|
115
|
+
try {
|
|
116
|
+
await runDimensionAction(fileOrKey, options);
|
|
117
|
+
} catch (error) {
|
|
118
|
+
logger.error(formatBlockingError(`capability dimension failed: ${error.message}`));
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
module.exports = {
|
|
125
|
+
setupCapabilityDimensionCommand,
|
|
126
|
+
runDimensionAction
|
|
127
|
+
};
|
|
128
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared success footer for datasource capability mutating commands.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview capability CLI output footer
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const logger = require('../utils/logger');
|
|
10
|
+
const { formatBulletSection, formatNextActions } = require('../utils/cli-test-layout-chalk');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} resolvedPath
|
|
14
|
+
* @param {string[]} updatedSections
|
|
15
|
+
* @param {string} [heading='Updated']
|
|
16
|
+
* @returns {void}
|
|
17
|
+
*/
|
|
18
|
+
function printCapabilitySuccessFooter(resolvedPath, updatedSections, heading = 'Updated') {
|
|
19
|
+
const display =
|
|
20
|
+
resolvedPath.includes(' ') ? `"${resolvedPath}"` : resolvedPath;
|
|
21
|
+
logger.log('');
|
|
22
|
+
logger.log(formatBulletSection(`${heading}:`, updatedSections));
|
|
23
|
+
logger.log('');
|
|
24
|
+
logger.log(formatNextActions([`aifabrix datasource validate ${display}`]));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = {
|
|
28
|
+
printCapabilitySuccessFooter
|
|
29
|
+
};
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `datasource capability relate` command registration.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview relate CLI
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const logger = require('../utils/logger');
|
|
10
|
+
const {
|
|
11
|
+
formatBlockingError,
|
|
12
|
+
headerKeyValue,
|
|
13
|
+
infoLine,
|
|
14
|
+
formatSuccessLine,
|
|
15
|
+
colorRollupPrefixedLine
|
|
16
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
17
|
+
const { runCapabilityRelate } = require('../datasource/capability/run-capability-relate');
|
|
18
|
+
const { printCapabilitySuccessFooter } = require('./datasource-capability-output');
|
|
19
|
+
|
|
20
|
+
const CAP_RELATE_HELP = `
|
|
21
|
+
Adds or replaces one **foreignKeys[]** row (metadata-only; no pipeline). Optional **metadataSchema.properties** stub unless **--skip-metadata-property**.
|
|
22
|
+
|
|
23
|
+
$ aifabrix datasource capability relate hubspot-deals --relation-name company --to hubspot-companies --field companyId --target-field externalId
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
function parseTargetFields(raw) {
|
|
27
|
+
const tf = raw;
|
|
28
|
+
if (Array.isArray(tf) && tf.length > 0) {
|
|
29
|
+
return tf.map((x) => String(x).trim()).filter(Boolean);
|
|
30
|
+
}
|
|
31
|
+
if (tf !== undefined && tf !== null && String(tf).trim()) {
|
|
32
|
+
return [String(tf).trim()];
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function logRelateValidationOutcome(result, joinLeft, joinRight) {
|
|
38
|
+
logger.log(formatSuccessLine('Local validation passed'));
|
|
39
|
+
if (result.remoteValidation?.ok) {
|
|
40
|
+
logger.log(formatSuccessLine('Remote validation passed'));
|
|
41
|
+
} else {
|
|
42
|
+
logger.log(colorRollupPrefixedLine('⚠ Remote validation skipped (not authenticated)'));
|
|
43
|
+
}
|
|
44
|
+
if (Array.isArray(result.semanticWarnings) && result.semanticWarnings.length > 0) {
|
|
45
|
+
result.semanticWarnings.forEach((w) => logger.log(colorRollupPrefixedLine(`⚠ ${w}`)));
|
|
46
|
+
}
|
|
47
|
+
logger.log(formatSuccessLine(`Relation created: ${joinLeft} → ${joinRight}`));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* @param {string} fileOrKey
|
|
52
|
+
* @param {object} options - Commander options
|
|
53
|
+
* @returns {Promise<void>}
|
|
54
|
+
*/
|
|
55
|
+
async function runRelateAction(fileOrKey, options) {
|
|
56
|
+
const fields = [];
|
|
57
|
+
if (options.field) {
|
|
58
|
+
fields.push(String(options.field).trim());
|
|
59
|
+
}
|
|
60
|
+
const targetFields = parseTargetFields(options.targetField);
|
|
61
|
+
|
|
62
|
+
const result = await runCapabilityRelate({
|
|
63
|
+
fileOrKey,
|
|
64
|
+
relationName: options.relationName,
|
|
65
|
+
targetDatasource: options.to,
|
|
66
|
+
fields,
|
|
67
|
+
targetFields,
|
|
68
|
+
required: options.required,
|
|
69
|
+
description: options.description,
|
|
70
|
+
dryRun: Boolean(options.dryRun),
|
|
71
|
+
noBackup: Boolean(options.noBackup),
|
|
72
|
+
overwrite: Boolean(options.overwrite),
|
|
73
|
+
addMetadataProperty: !options.skipMetadataProperty
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (result.dryRun) {
|
|
77
|
+
logger.log(infoLine('Dry run — planned JSON Patch operations:'));
|
|
78
|
+
logger.log('');
|
|
79
|
+
logger.log(JSON.stringify(result.patchOperations, null, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const joinLeft = fields.length === 1 ? fields[0] : 'fields';
|
|
84
|
+
const joinRight =
|
|
85
|
+
Array.isArray(targetFields) && targetFields.length === 1
|
|
86
|
+
? `${options.to}.${targetFields[0]}`
|
|
87
|
+
: options.to;
|
|
88
|
+
logRelateValidationOutcome(result, joinLeft, joinRight);
|
|
89
|
+
|
|
90
|
+
if (result.backupPath) {
|
|
91
|
+
logger.log(headerKeyValue('Backup:', result.backupPath));
|
|
92
|
+
}
|
|
93
|
+
printCapabilitySuccessFooter(result.resolvedPath, result.updatedSections, 'Updated');
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @param {import('commander').Command} cap
|
|
98
|
+
* @returns {void}
|
|
99
|
+
*/
|
|
100
|
+
function setupCapabilityRelateCommand(cap) {
|
|
101
|
+
cap
|
|
102
|
+
.command('relate <file-or-key>')
|
|
103
|
+
.description(
|
|
104
|
+
'Add or replace foreignKeys[] metadata (+ optional metadataSchema property for relation name)'
|
|
105
|
+
)
|
|
106
|
+
.requiredOption('--relation-name <name>', 'FK name (camelCase; unique per datasource)')
|
|
107
|
+
.requiredOption('--to <targetDatasource>', 'Target datasource key (cross-system JSON key)')
|
|
108
|
+
.requiredOption('--field <name>', 'Local normalized attribute (foreignKeys.fields[])')
|
|
109
|
+
.option('--description <text>', 'FK description (defaults to a generated description)')
|
|
110
|
+
.option('--required', 'Mark FK required (foreignKeys[].required=true)')
|
|
111
|
+
.option('--no-required', 'Mark FK optional (foreignKeys[].required=false)')
|
|
112
|
+
.option(
|
|
113
|
+
'--target-field <name>',
|
|
114
|
+
'Target join field(s); repeat flag for composite; omit to use runtime default (externalId)',
|
|
115
|
+
(value, prev) => {
|
|
116
|
+
const list = prev || [];
|
|
117
|
+
list.push(value);
|
|
118
|
+
return list;
|
|
119
|
+
},
|
|
120
|
+
[]
|
|
121
|
+
)
|
|
122
|
+
.option('--dry-run', 'Print JSON Patch operations; do not write')
|
|
123
|
+
.option('--overwrite', 'Replace existing foreignKeys row with the same name')
|
|
124
|
+
.option('--skip-metadata-property', 'Do not add metadataSchema.properties.<relationName>')
|
|
125
|
+
.option('--no-backup', 'Skip backup copy under integration/<app>/backup/')
|
|
126
|
+
.addHelpText('after', CAP_RELATE_HELP)
|
|
127
|
+
.action(async(fileOrKey, options) => {
|
|
128
|
+
try {
|
|
129
|
+
await runRelateAction(fileOrKey, options);
|
|
130
|
+
} catch (error) {
|
|
131
|
+
logger.error(formatBlockingError(`capability relate failed: ${error.message}`));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = {
|
|
138
|
+
setupCapabilityRelateCommand,
|
|
139
|
+
runRelateAction
|
|
140
|
+
};
|