@aifabrix/builder 2.44.5 → 2.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor/rules/cli-layout.mdc +8 -4
- package/.cursor/rules/project-rules.mdc +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 +104 -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/helpers.js +3 -3
- package/lib/app/index.js +3 -3
- package/lib/app/push.js +46 -23
- package/lib/app/register.js +7 -6
- package/lib/app/restart-display.js +126 -0
- package/lib/app/rotate-secret.js +7 -6
- package/lib/app/run-container-start.js +12 -6
- package/lib/app/run-env-compose.js +30 -1
- package/lib/app/run-helpers.js +58 -19
- package/lib/app/run-reload-sync.js +148 -0
- package/lib/app/run-resolve-image.js +51 -1
- package/lib/app/run.js +148 -74
- package/lib/app/show-display.js +7 -0
- package/lib/app/show.js +87 -5
- package/lib/build/index.js +83 -49
- package/lib/cli/doctor-check.js +117 -0
- package/lib/cli/index.js +8 -2
- package/lib/cli/infra-guided.js +460 -0
- package/lib/cli/installation-log-command.js +73 -0
- package/lib/cli/setup-app.js +31 -3
- package/lib/cli/setup-auth.js +98 -27
- package/lib/cli/setup-dev-path-commands.js +50 -3
- package/lib/cli/setup-infra-up-dataplane-action.js +111 -0
- package/lib/cli/setup-infra-up-platform-action.js +131 -0
- package/lib/cli/setup-infra.js +132 -118
- 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-resolve.js +132 -0
- package/lib/cli/setup-utility.js +143 -84
- package/lib/commands/app-logs.js +81 -33
- package/lib/commands/auth-config.js +116 -18
- 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 +468 -0
- package/lib/commands/setup-prompts.js +421 -0
- package/lib/commands/setup.js +254 -0
- package/lib/commands/teardown.js +277 -0
- package/lib/commands/up-common.js +113 -19
- package/lib/commands/up-dataplane.js +44 -19
- package/lib/commands/up-miso.js +18 -18
- package/lib/commands/upload.js +111 -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/audit-logger.js +1 -34
- package/lib/core/config-admin-email.js +56 -0
- package/lib/core/config-normalize.js +60 -0
- package/lib/core/config-registered-controller-urls.js +54 -0
- package/lib/core/config.js +33 -50
- 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 +428 -0
- package/lib/core/secrets-env-declarative-expand.js +170 -0
- package/lib/core/secrets-env-write.js +29 -1
- package/lib/core/secrets-load.js +252 -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 +9 -2
- 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/internal/node-fs.js +2 -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/application-schema.json +4 -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 -1
- 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-config-resolver.js +24 -1
- package/lib/utils/app-run-containers.js +2 -2
- package/lib/utils/applications-config-defaults.js +206 -0
- package/lib/utils/auth-config-validator.js +2 -12
- 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/compose-generate-docker-compose.js +111 -6
- package/lib/utils/compose-generator.js +17 -8
- package/lib/utils/controller-url.js +50 -7
- 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/env-copy.js +99 -14
- package/lib/utils/env-template.js +5 -1
- 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 +28 -10
- package/lib/utils/health-check.js +139 -107
- package/lib/utils/help-builder.js +5 -1
- package/lib/utils/image-name.js +34 -7
- package/lib/utils/infra-optional-service-flags.js +69 -0
- package/lib/utils/installation-log-core.js +282 -0
- package/lib/utils/installation-log-record.js +237 -0
- package/lib/utils/installation-log.js +123 -0
- package/lib/utils/integration-file-backup.js +74 -0
- package/lib/utils/log-redaction.js +105 -0
- package/lib/utils/manifest-location.js +164 -0
- package/lib/utils/manifest-source-emit.js +162 -0
- package/lib/utils/mutagen-install.js +30 -3
- package/lib/utils/paths.js +308 -76
- 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 +49 -4
- package/lib/utils/resolve-docker-image-ref.js +9 -3
- package/lib/utils/run-cli-flags.js +29 -0
- package/lib/utils/secrets-ancestor-paths.js +47 -0
- package/lib/utils/secrets-canonical.js +10 -3
- 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 +26 -13
- package/lib/utils/secrets-utils.js +20 -10
- package/lib/utils/system-builder-root.js +42 -0
- package/lib/utils/url-declarative-public-base.js +80 -12
- package/lib/utils/url-declarative-resolve-build-urls.js +238 -0
- package/lib/utils/url-declarative-resolve-build.js +24 -388
- package/lib/utils/url-declarative-resolve-expand-token.js +189 -0
- package/lib/utils/url-declarative-resolve-load-doc.js +12 -3
- package/lib/utils/url-declarative-resolve-surface-state.js +102 -0
- package/lib/utils/url-declarative-resolve.js +47 -7
- package/lib/utils/url-declarative-runtime-base-path.js +52 -0
- package/lib/utils/url-declarative-vdir-inactive-env.js +2 -1
- package/lib/utils/urls-local-registry-scan.js +103 -0
- package/lib/utils/urls-local-registry.js +158 -76
- 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 +3 -1
- package/templates/applications/dataplane/application.yaml +5 -1
- package/templates/applications/dataplane/rbac.yaml +10 -10
- package/templates/applications/keycloak/env.template +8 -6
- package/templates/applications/miso-controller/application.yaml +9 -0
- package/templates/applications/miso-controller/env.template +27 -29
- package/templates/applications/miso-controller/rbac.yaml +9 -9
- package/templates/external-system/README.md.hbs +83 -123
- package/.npmrc.token +0 -1
- 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
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helper functions for aligning external integration system/datasource config.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from repair.js to keep file size under 500 lines.
|
|
5
|
+
*
|
|
6
|
+
* @fileoverview External integration repair helpers (system/datasource alignment)
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const chalk = require('chalk');
|
|
14
|
+
|
|
15
|
+
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
16
|
+
const { systemKeyToKvPrefix, securityKeyToVar } = require('../utils/credential-secrets-env');
|
|
17
|
+
const { backupIntegrationFile } = require('../utils/integration-file-backup');
|
|
18
|
+
const logger = require('../utils/logger');
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Loads first system file and returns parsed object with key
|
|
22
|
+
* @param {string} appPath - Application path
|
|
23
|
+
* @param {string} systemFileName - System file name
|
|
24
|
+
* @returns {Object} Parsed system config
|
|
25
|
+
*/
|
|
26
|
+
function loadFirstSystemFile(appPath, systemFileName) {
|
|
27
|
+
const systemPath = path.join(appPath, systemFileName);
|
|
28
|
+
if (!fs.existsSync(systemPath)) {
|
|
29
|
+
throw new Error(`System file not found: ${systemPath}`);
|
|
30
|
+
}
|
|
31
|
+
return loadConfigFile(systemPath);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveSystemContext(appPath, systemFiles) {
|
|
35
|
+
const systemFilePath = path.join(appPath, systemFiles[0]);
|
|
36
|
+
const systemParsed = loadFirstSystemFile(appPath, systemFiles[0]);
|
|
37
|
+
const systemKey = systemParsed.key ||
|
|
38
|
+
path.basename(systemFiles[0], path.extname(systemFiles[0])).replace(/-system$/, '');
|
|
39
|
+
return { systemFilePath, systemParsed, systemKey };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function ensureExternalIntegrationBlock(variables, systemFiles, datasourceFiles, changes) {
|
|
43
|
+
const extInt = variables.externalIntegration;
|
|
44
|
+
let updated = false;
|
|
45
|
+
if (!extInt) {
|
|
46
|
+
variables.externalIntegration = {
|
|
47
|
+
schemaBasePath: './',
|
|
48
|
+
systems: systemFiles,
|
|
49
|
+
dataSources: datasourceFiles,
|
|
50
|
+
autopublish: true,
|
|
51
|
+
version: '1.0.0'
|
|
52
|
+
};
|
|
53
|
+
changes.push('Created externalIntegration block from discovered files');
|
|
54
|
+
updated = true;
|
|
55
|
+
} else {
|
|
56
|
+
const prevSystems = extInt.systems || [];
|
|
57
|
+
const prevDataSources = extInt.dataSources || [];
|
|
58
|
+
if (JSON.stringify(prevSystems) !== JSON.stringify(systemFiles)) {
|
|
59
|
+
changes.push(`systems: [${prevSystems.join(', ')}] → [${systemFiles.join(', ')}]`);
|
|
60
|
+
extInt.systems = systemFiles;
|
|
61
|
+
updated = true;
|
|
62
|
+
}
|
|
63
|
+
if (JSON.stringify(prevDataSources) !== JSON.stringify(datasourceFiles)) {
|
|
64
|
+
changes.push(`dataSources: [${prevDataSources.join(', ')}] → [${datasourceFiles.join(', ')}]`);
|
|
65
|
+
extInt.dataSources = datasourceFiles;
|
|
66
|
+
updated = true;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return updated;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function alignAppKeyWithSystem(variables, systemKey, _systemParsed, changes) {
|
|
73
|
+
const appKey = variables.app?.key;
|
|
74
|
+
if (!appKey || appKey === systemKey) return false;
|
|
75
|
+
if (!variables.app) variables.app = {};
|
|
76
|
+
variables.app.key = systemKey;
|
|
77
|
+
changes.push(`app.key: ${appKey} → ${systemKey}`);
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Aligns datasource systemKey values to match the system key.
|
|
83
|
+
* Updates each datasource file whose systemKey differs from the system key.
|
|
84
|
+
*
|
|
85
|
+
* @param {string} appPath - Application directory path
|
|
86
|
+
* @param {string[]} datasourceFiles - Datasource file names
|
|
87
|
+
* @param {string} systemKey - Expected system key
|
|
88
|
+
* @param {boolean} dryRun - If true, report changes but do not write
|
|
89
|
+
* @param {string[]} changes - Array to append change descriptions to
|
|
90
|
+
* @returns {boolean} True if any file was updated (or would be in dry-run)
|
|
91
|
+
*/
|
|
92
|
+
function alignDatasourceSystemKeys(appPath, datasourceFiles, systemKey, dryRun, changes, backupCtx) {
|
|
93
|
+
if (!datasourceFiles || datasourceFiles.length === 0) return false;
|
|
94
|
+
let updated = false;
|
|
95
|
+
for (const datasourceFile of datasourceFiles) {
|
|
96
|
+
const datasourcePath = path.join(appPath, datasourceFile);
|
|
97
|
+
if (!fs.existsSync(datasourcePath)) continue;
|
|
98
|
+
const parsed = loadConfigFile(datasourcePath);
|
|
99
|
+
const old = parsed.systemKey;
|
|
100
|
+
if (old !== systemKey) {
|
|
101
|
+
parsed.systemKey = systemKey;
|
|
102
|
+
if (!dryRun) {
|
|
103
|
+
backupIntegrationFile(datasourcePath, backupCtx);
|
|
104
|
+
writeConfigFile(datasourcePath, parsed);
|
|
105
|
+
}
|
|
106
|
+
changes.push(`${datasourceFile}: systemKey ${old} → ${systemKey}`);
|
|
107
|
+
updated = true;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return updated;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Derives a datasource key from filename when the file has no key.
|
|
115
|
+
* - hubspot-datasource-company.json → hubspot-company
|
|
116
|
+
* - datasource-companies.json → {systemKey}-companies (e.g. test-hubspot-companies)
|
|
117
|
+
*
|
|
118
|
+
* @param {string} fileName - Datasource file name
|
|
119
|
+
* @param {string} systemKey - System key (e.g. test-hubspot), used for datasource-*.json style names
|
|
120
|
+
* @returns {string}
|
|
121
|
+
*/
|
|
122
|
+
function deriveDatasourceKeyFromFileName(fileName, systemKey) {
|
|
123
|
+
const base = path.basename(fileName, path.extname(fileName));
|
|
124
|
+
if (/^datasource-/.test(base)) {
|
|
125
|
+
const suffix = base.slice('datasource-'.length);
|
|
126
|
+
return systemKey && typeof systemKey === 'string' ? `${systemKey}-${suffix}` : base;
|
|
127
|
+
}
|
|
128
|
+
return base.replace(/-datasource-/, '-');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Aligns system file dataSources array to match datasource keys from discovered files.
|
|
133
|
+
* The system file holds logical keys (not filenames): each key comes from that datasource
|
|
134
|
+
* file's "key" property, or is derived from the filename when missing (e.g. datasource-companies.json → {systemKey}-companies).
|
|
135
|
+
*
|
|
136
|
+
* @param {string} appPath - Application directory path
|
|
137
|
+
* @param {Object} systemParsed - Parsed system config (mutated)
|
|
138
|
+
* @param {string[]} datasourceFiles - Datasource file names
|
|
139
|
+
* @param {string} systemKey - System key for deriving key when missing
|
|
140
|
+
* @param {boolean} dryRun - If true, report changes but do not write
|
|
141
|
+
* @param {string[]} changes - Array to append change descriptions to
|
|
142
|
+
* @returns {boolean} True if dataSources was updated (or would be in dry-run)
|
|
143
|
+
*/
|
|
144
|
+
function alignSystemFileDataSources(appPath, systemParsed, datasourceFiles, systemKey, _dryRun, changes) {
|
|
145
|
+
const keys = [];
|
|
146
|
+
for (const fileName of datasourceFiles) {
|
|
147
|
+
const filePath = path.join(appPath, fileName);
|
|
148
|
+
if (!fs.existsSync(filePath)) continue;
|
|
149
|
+
try {
|
|
150
|
+
const parsed = loadConfigFile(filePath);
|
|
151
|
+
const key = parsed && typeof parsed.key === 'string' && parsed.key.trim()
|
|
152
|
+
? parsed.key.trim()
|
|
153
|
+
: deriveDatasourceKeyFromFileName(fileName, systemKey);
|
|
154
|
+
keys.push(key);
|
|
155
|
+
} catch (err) {
|
|
156
|
+
logger.log(chalk.yellow(`⚠ Could not load datasource file ${fileName}: ${err.message}; using derived key`));
|
|
157
|
+
keys.push(deriveDatasourceKeyFromFileName(fileName, systemKey));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
const prev = Array.isArray(systemParsed.dataSources) ? [...systemParsed.dataSources] : [];
|
|
161
|
+
if (JSON.stringify(prev) === JSON.stringify(keys)) return false;
|
|
162
|
+
systemParsed.dataSources = keys;
|
|
163
|
+
changes.push(`dataSources: [${prev.join(', ') || '(none)'}] → [${keys.join(', ')}] (keys from each datasource file's "key" or filename)`);
|
|
164
|
+
return true;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Builds the set of auth variable names (UPPERCASE, no underscores) from authentication.variables and authentication.security.
|
|
169
|
+
* Used to detect configuration entries that belong only in the authentication section.
|
|
170
|
+
* Canonical keys per method are in lib/schema/external-system.schema.json $defs.authenticationVariablesByMethod
|
|
171
|
+
* (e.g. oauth2: variables baseUrl, tokenUrl, scope; security clientId, clientSecret).
|
|
172
|
+
*
|
|
173
|
+
* @param {Object} systemParsed - Parsed system config
|
|
174
|
+
* @param {string} _systemKey - System key (unused; for API consistency)
|
|
175
|
+
* @returns {Set<string>}
|
|
176
|
+
*/
|
|
177
|
+
function buildAuthVarNames(systemParsed, _systemKey) {
|
|
178
|
+
const names = new Set();
|
|
179
|
+
const auth = systemParsed.authentication;
|
|
180
|
+
if (auth && typeof auth.variables === 'object') {
|
|
181
|
+
for (const k of Object.keys(auth.variables)) {
|
|
182
|
+
names.add(String(k).toUpperCase().replace(/_/g, ''));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (auth && typeof auth.security === 'object') {
|
|
186
|
+
for (const k of Object.keys(auth.security)) {
|
|
187
|
+
names.add(securityKeyToVar(k));
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return names;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Derives the normalized auth variable part from a config entry name (for matching against authNames).
|
|
195
|
+
* E.g. KV_HUBSPOT_CLIENTID → CLIENTID, BASEURL → BASEURL.
|
|
196
|
+
* @param {string} name - Entry name
|
|
197
|
+
* @param {string} systemKey - System key for KV_ prefix
|
|
198
|
+
* @returns {string}
|
|
199
|
+
*/
|
|
200
|
+
function normalizedAuthPartFromConfigName(name, systemKey) {
|
|
201
|
+
const n = String(name).trim();
|
|
202
|
+
if (!n) return '';
|
|
203
|
+
const prefix = systemKeyToKvPrefix(systemKey);
|
|
204
|
+
const kvPrefix = `KV_${prefix}_`;
|
|
205
|
+
if (n.toUpperCase().startsWith(kvPrefix)) {
|
|
206
|
+
const rest = n.slice(kvPrefix.length);
|
|
207
|
+
return rest.toUpperCase().replace(/_/g, '');
|
|
208
|
+
}
|
|
209
|
+
// Old-style or other KV_* names: treat last segment as var (e.g. KV_HUBSPOT_CLIENTID → CLIENTID)
|
|
210
|
+
if (n.toUpperCase().startsWith('KV_')) {
|
|
211
|
+
const parts = n.split('_').filter(Boolean);
|
|
212
|
+
if (parts.length >= 2) return parts[parts.length - 1].toUpperCase();
|
|
213
|
+
}
|
|
214
|
+
return n.toUpperCase().replace(/_/g, '');
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function removeAuthVarsFromConfiguration(systemParsed, systemKey, _dryRun, changes) {
|
|
218
|
+
const config = systemParsed.configuration;
|
|
219
|
+
if (!Array.isArray(config)) return false;
|
|
220
|
+
const authNames = buildAuthVarNames(systemParsed, systemKey);
|
|
221
|
+
if (authNames.size === 0) return false;
|
|
222
|
+
const removed = [];
|
|
223
|
+
const filtered = config.filter((entry) => {
|
|
224
|
+
if (!entry || !entry.name) return true;
|
|
225
|
+
const authPart = normalizedAuthPartFromConfigName(entry.name, systemKey);
|
|
226
|
+
if (authNames.has(authPart)) {
|
|
227
|
+
removed.push(entry.name);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
return true;
|
|
231
|
+
});
|
|
232
|
+
if (removed.length === 0) return false;
|
|
233
|
+
systemParsed.configuration = filtered;
|
|
234
|
+
changes.push(`Removed authentication variable(s) from configuration: ${removed.join(', ')}`);
|
|
235
|
+
return true;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
module.exports = {
|
|
239
|
+
alignAppKeyWithSystem,
|
|
240
|
+
alignDatasourceSystemKeys,
|
|
241
|
+
alignSystemFileDataSources,
|
|
242
|
+
ensureExternalIntegrationBlock,
|
|
243
|
+
removeAuthVarsFromConfiguration,
|
|
244
|
+
resolveSystemContext
|
|
245
|
+
};
|
|
246
|
+
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Repair external system permissions[] for OpenAPI autoRbac operations
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
12
|
+
|
|
13
|
+
function _safeString(v) {
|
|
14
|
+
return typeof v === 'string' && v.trim() ? v.trim() : '';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function _normalizeOperationKey(opKey) {
|
|
18
|
+
const s = _safeString(opKey);
|
|
19
|
+
if (!s) return '';
|
|
20
|
+
const withHyphens = s.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
|
|
21
|
+
const cleaned = withHyphens.replace(/[^a-z0-9-]+/g, '-');
|
|
22
|
+
return cleaned.replace(/-+/g, '-').replace(/^-+|-+$/g, '');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function _ensureDefaultRoles(systemParsed, systemKey) {
|
|
26
|
+
if (Array.isArray(systemParsed.roles) && systemParsed.roles.length > 0) return false;
|
|
27
|
+
const key = _safeString(systemKey) || _safeString(systemParsed?.key) || 'system';
|
|
28
|
+
const display = _safeString(systemParsed?.displayName) || key;
|
|
29
|
+
systemParsed.roles = [
|
|
30
|
+
{
|
|
31
|
+
name: `${display} Admin`,
|
|
32
|
+
value: `${key}-admin`,
|
|
33
|
+
description: `Full access to all ${display} operations`,
|
|
34
|
+
groups: []
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
name: `${display} Reader`,
|
|
38
|
+
value: `${key}-reader`,
|
|
39
|
+
description: `Read-only access to all ${display} data`,
|
|
40
|
+
groups: []
|
|
41
|
+
}
|
|
42
|
+
];
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function _pickRoleValue(systemParsed, suffix, fallback) {
|
|
47
|
+
const roles = Array.isArray(systemParsed.roles) ? systemParsed.roles : [];
|
|
48
|
+
const vals = roles
|
|
49
|
+
.map((r) => r && typeof r === 'object' ? _safeString(r.value) : '')
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
return vals.find((v) => v.endsWith(suffix)) || vals[0] || fallback;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function _defaultRolesForPermissionName(permName, { adminValue, readerValue }) {
|
|
55
|
+
const roles = [];
|
|
56
|
+
const cap = typeof permName === 'string' && permName.includes(':') ? permName.split(':')[1] : '';
|
|
57
|
+
const isRead = cap === 'list' || cap === 'get';
|
|
58
|
+
if (isRead && readerValue) roles.push(readerValue);
|
|
59
|
+
if (adminValue) roles.push(adminValue);
|
|
60
|
+
return roles;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function _permissionNamesFromDatasource(parsed) {
|
|
64
|
+
const resourceType = _safeString(parsed && parsed.resourceType) || 'document';
|
|
65
|
+
const openapi = parsed && parsed.openapi;
|
|
66
|
+
if (!openapi || typeof openapi !== 'object') return [];
|
|
67
|
+
if (openapi.autoRbac !== true) return [];
|
|
68
|
+
const ops = openapi.operations;
|
|
69
|
+
if (!ops || typeof ops !== 'object' || Array.isArray(ops)) return [];
|
|
70
|
+
return Object.keys(ops)
|
|
71
|
+
.map((k) => _safeString(k))
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
.map((opKey) => _normalizeOperationKey(opKey) || opKey)
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.map((opKey) => `${resourceType}:${opKey}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function _collectAutoRbacOperationPermissions(appPath, datasourceFiles) {
|
|
79
|
+
const permissionNames = new Set();
|
|
80
|
+
for (const fileName of datasourceFiles || []) {
|
|
81
|
+
const filePath = path.join(appPath, fileName);
|
|
82
|
+
if (!fs.existsSync(filePath)) continue;
|
|
83
|
+
let parsed;
|
|
84
|
+
try {
|
|
85
|
+
parsed = loadConfigFile(filePath);
|
|
86
|
+
} catch {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
for (const perm of _permissionNamesFromDatasource(parsed)) {
|
|
90
|
+
permissionNames.add(perm);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return permissionNames;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function _getExistingPermissionNameSet(systemParsed) {
|
|
97
|
+
const asArray = Array.isArray(systemParsed.permissions) ? systemParsed.permissions : [];
|
|
98
|
+
return new Set(
|
|
99
|
+
asArray
|
|
100
|
+
.map((p) => (typeof p === 'string' ? p : p && typeof p === 'object' ? p.name : null))
|
|
101
|
+
.filter((n) => typeof n === 'string' && n.trim())
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function _backfillEmptyPermissionRoles(systemParsed, dryRun, adminValue, readerValue, changes) {
|
|
106
|
+
if (dryRun) return false;
|
|
107
|
+
if (!Array.isArray(systemParsed.permissions)) return false;
|
|
108
|
+
let updated = false;
|
|
109
|
+
for (const p of systemParsed.permissions) {
|
|
110
|
+
if (!p || typeof p !== 'object') continue;
|
|
111
|
+
if (typeof p.name !== 'string' || !p.name.trim()) continue;
|
|
112
|
+
if (!Array.isArray(p.roles)) p.roles = [];
|
|
113
|
+
if (p.roles.length > 0) continue;
|
|
114
|
+
p.roles = _defaultRolesForPermissionName(p.name, { adminValue, readerValue });
|
|
115
|
+
if (p.roles.length > 0) {
|
|
116
|
+
changes.push(`RBAC: defaulted empty roles for system permission ${p.name}`);
|
|
117
|
+
updated = true;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return updated;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function _addMissingSystemPermission(systemParsed, perm, dryRun, adminValue, readerValue) {
|
|
124
|
+
if (dryRun) return;
|
|
125
|
+
if (!Array.isArray(systemParsed.permissions)) systemParsed.permissions = [];
|
|
126
|
+
systemParsed.permissions.push({
|
|
127
|
+
name: perm,
|
|
128
|
+
roles: _defaultRolesForPermissionName(perm, { adminValue, readerValue }),
|
|
129
|
+
description: `Permission: ${perm}`
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Ensure system.permissions includes autoRbac-derived permission names.
|
|
135
|
+
* @param {string} appPath
|
|
136
|
+
* @param {Object} systemParsed - parsed *-system.json/yaml (mutated)
|
|
137
|
+
* @param {string[]} datasourceFiles
|
|
138
|
+
* @param {boolean} dryRun
|
|
139
|
+
* @param {string[]} changes
|
|
140
|
+
* @returns {boolean} updated
|
|
141
|
+
*/
|
|
142
|
+
function ensureSystemPermissionsForAutoRbac(appPath, systemParsed, datasourceFiles, dryRun, changes) {
|
|
143
|
+
if (!systemParsed || typeof systemParsed !== 'object') return false;
|
|
144
|
+
const desired = _collectAutoRbacOperationPermissions(appPath, datasourceFiles);
|
|
145
|
+
if (desired.size === 0) return false;
|
|
146
|
+
|
|
147
|
+
const rolesAdded = _ensureDefaultRoles(systemParsed, systemParsed?.key);
|
|
148
|
+
if (rolesAdded) {
|
|
149
|
+
changes.push('Added default Admin/Reader roles (required for RBAC permissions)');
|
|
150
|
+
}
|
|
151
|
+
const adminValue = _pickRoleValue(systemParsed, '-admin', 'admin');
|
|
152
|
+
const readerValue = _pickRoleValue(systemParsed, '-reader', null);
|
|
153
|
+
|
|
154
|
+
const existing = _getExistingPermissionNameSet(systemParsed);
|
|
155
|
+
let updated = _backfillEmptyPermissionRoles(systemParsed, dryRun, adminValue, readerValue, changes);
|
|
156
|
+
|
|
157
|
+
for (const perm of desired) {
|
|
158
|
+
if (existing.has(perm)) continue;
|
|
159
|
+
_addMissingSystemPermission(systemParsed, perm, dryRun, adminValue, readerValue);
|
|
160
|
+
changes.push(`Added system permission: ${perm} (autoRbac)`);
|
|
161
|
+
existing.add(perm);
|
|
162
|
+
updated = true;
|
|
163
|
+
}
|
|
164
|
+
return updated;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
module.exports = { ensureSystemPermissionsForAutoRbac };
|
|
168
|
+
|