@aifabrix/builder 2.40.2 → 2.42.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/docs-rules.mdc +30 -0
- package/README.md +7 -5
- package/integration/hubspot/README.md +8 -4
- package/integration/hubspot/application.json +54 -0
- package/integration/hubspot/create-hubspot.js +9 -136
- package/integration/hubspot/env.template +3 -4
- package/integration/hubspot/hubspot-datasource-company.json +343 -5
- package/integration/hubspot/hubspot-datasource-contact.json +413 -5
- package/integration/hubspot/hubspot-datasource-deal.json +341 -4
- package/integration/hubspot/hubspot-datasource-users.json +116 -0
- package/integration/hubspot/hubspot-deploy.json +1250 -108
- package/integration/hubspot/hubspot-system.json +15 -32
- package/integration/hubspot/test-dataplane-down-tests.js +17 -16
- package/integration/hubspot/test-dataplane-down.js +2 -2
- package/integration/hubspot/test.js +1 -1
- package/jest.config.manual.js +2 -1
- package/lib/api/credential.api.js +40 -0
- package/lib/api/dev.api.js +423 -0
- package/lib/api/external-test.api.js +111 -0
- package/lib/api/index.js +42 -19
- package/lib/api/pipeline.api.js +66 -120
- package/lib/api/types/credential.types.js +23 -0
- package/lib/api/types/dev.types.js +140 -0
- package/lib/api/types/pipeline.types.js +37 -0
- package/lib/api/wizard-platform.api.js +61 -0
- package/lib/api/wizard.api.js +34 -1
- package/lib/app/config.js +44 -11
- package/lib/app/down.js +2 -1
- package/lib/app/index.js +12 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/push.js +36 -12
- package/lib/app/readme.js +9 -6
- package/lib/app/run-env-compose.js +264 -0
- package/lib/app/run-helpers.js +121 -118
- package/lib/app/run.js +148 -28
- package/lib/app/show-display.js +1 -1
- package/lib/app/show.js +5 -2
- package/lib/build/index.js +11 -3
- package/lib/cli/setup-app.js +172 -15
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +206 -16
- package/lib/cli/setup-environment.js +16 -6
- package/lib/cli/setup-external-system.js +89 -24
- package/lib/cli/setup-infra.js +82 -15
- package/lib/cli/setup-secrets.js +52 -5
- package/lib/cli/setup-utility.js +129 -24
- package/lib/commands/app-install.js +172 -0
- package/lib/commands/app-shell.js +75 -0
- package/lib/commands/app-test.js +282 -0
- package/lib/commands/app.js +1 -1
- package/lib/commands/credential-env.js +162 -0
- package/lib/commands/credential-list.js +17 -22
- package/lib/commands/credential-push.js +96 -0
- package/lib/commands/datasource.js +77 -6
- package/lib/commands/dev-cli-handlers.js +141 -0
- package/lib/commands/dev-down.js +114 -0
- package/lib/commands/dev-init.js +347 -0
- package/lib/commands/repair-auth-config.js +99 -0
- package/lib/commands/repair-datasource-keys.js +208 -0
- package/lib/commands/repair-datasource.js +235 -0
- package/lib/commands/repair-env-template.js +348 -0
- package/lib/commands/repair-internal.js +85 -0
- package/lib/commands/repair-rbac.js +158 -0
- package/lib/commands/repair.js +507 -0
- package/lib/commands/secrets-list.js +118 -0
- package/lib/commands/secrets-remove.js +97 -0
- package/lib/commands/secrets-set.js +30 -17
- package/lib/commands/secrets-validate.js +50 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/up-dataplane.js +2 -2
- package/lib/commands/up-miso.js +0 -25
- package/lib/commands/upload.js +96 -40
- package/lib/commands/wizard-core-helpers.js +226 -4
- package/lib/commands/wizard-core.js +67 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +44 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +86 -64
- package/lib/core/admin-secrets.js +96 -0
- package/lib/core/config.js +7 -1
- package/lib/core/secrets-ensure.js +378 -0
- package/lib/core/secrets-env-write.js +157 -0
- package/lib/core/secrets.js +176 -89
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/field-reference-validator.js +91 -0
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/datasource/validate.js +21 -3
- package/lib/deployment/deployer.js +7 -5
- package/lib/deployment/environment-config.js +137 -0
- package/lib/deployment/environment.js +21 -98
- package/lib/deployment/push.js +32 -2
- package/lib/external-system/download.js +188 -203
- package/lib/external-system/generator.js +204 -56
- package/lib/external-system/test-auth.js +7 -3
- package/lib/external-system/test-execution.js +2 -1
- package/lib/external-system/test-system-level.js +73 -0
- package/lib/external-system/test.js +56 -19
- package/lib/generator/external-controller-manifest.js +29 -2
- package/lib/generator/external-schema-utils.js +1 -1
- package/lib/generator/external.js +10 -3
- package/lib/generator/index.js +177 -25
- package/lib/generator/split-readme.js +1 -0
- package/lib/generator/split-variables.js +7 -1
- package/lib/generator/split.js +194 -54
- package/lib/generator/wizard-prompts-secondary.js +294 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +88 -0
- package/lib/generator/wizard.js +155 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/helpers.js +103 -20
- package/lib/infrastructure/index.js +98 -12
- package/lib/infrastructure/services.js +88 -22
- package/lib/schema/application-schema.json +32 -8
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +509 -411
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +41 -13
- package/lib/utils/app-register-auth.js +25 -3
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/cli-utils.js +20 -0
- package/lib/utils/compose-generator.js +77 -76
- package/lib/utils/compose-handlebars-helpers.js +54 -0
- package/lib/utils/compose-vector-helper.js +18 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/config-paths.js +127 -2
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +357 -0
- package/lib/utils/dataplane-pipeline-warning.js +28 -0
- package/lib/utils/deployment-validation-helpers.js +4 -4
- package/lib/utils/dev-ca-install.js +139 -0
- package/lib/utils/dev-cert-helper.js +122 -0
- package/lib/utils/device-code-helpers.js +224 -0
- package/lib/utils/device-code.js +37 -336
- package/lib/utils/docker-build.js +40 -8
- package/lib/utils/env-copy.js +103 -13
- package/lib/utils/env-map.js +35 -5
- package/lib/utils/env-template.js +6 -5
- package/lib/utils/error-formatters/http-status-errors.js +20 -2
- package/lib/utils/error-formatters/permission-errors.js +0 -1
- package/lib/utils/error-formatters/validation-errors.js +0 -1
- package/lib/utils/external-readme.js +56 -29
- package/lib/utils/external-system-display.js +59 -1
- package/lib/utils/external-system-test-helpers.js +21 -8
- package/lib/utils/external-system-validators.js +3 -0
- package/lib/utils/file-upload.js +20 -50
- package/lib/utils/help-builder.js +16 -2
- package/lib/utils/infra-status.js +80 -45
- package/lib/utils/local-secrets.js +7 -52
- package/lib/utils/mutagen-install.js +195 -0
- package/lib/utils/mutagen.js +146 -0
- package/lib/utils/paths.js +128 -37
- package/lib/utils/port-resolver.js +28 -16
- package/lib/utils/remote-dev-auth.js +38 -0
- package/lib/utils/remote-docker-env.js +43 -0
- package/lib/utils/remote-secrets-loader.js +60 -0
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +114 -6
- package/lib/utils/secrets-helpers.js +108 -114
- package/lib/utils/secrets-path.js +2 -2
- package/lib/utils/secrets-utils.js +52 -1
- package/lib/utils/secrets-validation.js +84 -0
- package/lib/utils/ssh-key-helper.js +116 -0
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager-messages.js +90 -0
- package/lib/utils/token-manager.js +29 -36
- package/lib/utils/variable-transformer.js +3 -3
- package/lib/validation/env-template-auth.js +157 -0
- package/lib/validation/env-template-kv.js +41 -0
- package/lib/validation/external-manifest-validator.js +25 -0
- package/lib/validation/external-system-auth-rules.js +86 -0
- package/lib/validation/validate-batch.js +149 -0
- package/lib/validation/validate-datasource-keys-api.js +33 -0
- package/lib/validation/validate-display.js +94 -16
- package/lib/validation/validate.js +25 -12
- package/lib/validation/validator.js +72 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +8 -3
- package/scripts/install-local.js +34 -15
- package/templates/README.md +0 -1
- package/templates/applications/README.md.hbs +4 -4
- package/templates/applications/dataplane/application.yaml +6 -5
- package/templates/applications/dataplane/env.template +15 -10
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/keycloak/env.template +2 -0
- package/templates/applications/miso-controller/application.yaml +1 -0
- package/templates/applications/miso-controller/env.template +12 -10
- package/templates/external-system/README.md.hbs +65 -25
- package/templates/external-system/deploy.js.hbs +4 -2
- package/templates/external-system/external-datasource.yaml.hbs +217 -0
- package/templates/external-system/external-system.json.hbs +1 -18
- package/templates/infra/compose.yaml.hbs +6 -0
- package/templates/python/docker-compose.hbs +49 -23
- package/templates/typescript/docker-compose.hbs +48 -22
- package/integration/hubspot/application.yaml +0 -37
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Batch validation helpers for validate --integration / --builder.
|
|
3
|
+
* @fileoverview Builds batch results and runs validation across multiple apps
|
|
4
|
+
* @author AI Fabrix Team
|
|
5
|
+
* @version 2.0.0
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { listIntegrationAppNames, listBuilderAppNames } = require('../utils/paths');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Collects error strings from a single validation result (various shapes).
|
|
12
|
+
* @param {Object} result - Single-app validation result
|
|
13
|
+
* @returns {string[]} Error messages
|
|
14
|
+
*/
|
|
15
|
+
function collectResultErrors(result) {
|
|
16
|
+
const errs = [];
|
|
17
|
+
if (result.errors && Array.isArray(result.errors)) {
|
|
18
|
+
errs.push(...result.errors);
|
|
19
|
+
}
|
|
20
|
+
if (result.application && result.application.errors && Array.isArray(result.application.errors)) {
|
|
21
|
+
errs.push(...result.application.errors);
|
|
22
|
+
}
|
|
23
|
+
if (result.steps) {
|
|
24
|
+
['application', 'components', 'manifest'].forEach(step => {
|
|
25
|
+
const s = result.steps[step];
|
|
26
|
+
if (s && s.errors && Array.isArray(s.errors)) {
|
|
27
|
+
errs.push(...s.errors);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
return errs;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Collects warning strings from a single validation result.
|
|
36
|
+
* @param {Object} result - Single-app validation result
|
|
37
|
+
* @returns {string[]} Warning messages
|
|
38
|
+
*/
|
|
39
|
+
function collectResultWarnings(result) {
|
|
40
|
+
const w = [];
|
|
41
|
+
if (result.warnings && Array.isArray(result.warnings)) {
|
|
42
|
+
w.push(...result.warnings);
|
|
43
|
+
}
|
|
44
|
+
if (result.application && result.application.warnings && Array.isArray(result.application.warnings)) {
|
|
45
|
+
w.push(...result.application.warnings);
|
|
46
|
+
}
|
|
47
|
+
if (result.steps) {
|
|
48
|
+
['application', 'components', 'manifest'].forEach(step => {
|
|
49
|
+
const s = result.steps[step];
|
|
50
|
+
if (s && s.warnings && Array.isArray(s.warnings)) {
|
|
51
|
+
w.push(...s.warnings);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
return w;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Builds batch result from per-app results. Each item has appName and either result or error.
|
|
60
|
+
* @param {Array<{appName: string, result?: Object, error?: string}>} items - Per-app results
|
|
61
|
+
* @returns {{ batch: true, valid: boolean, results: Array, errors: string[], warnings: string[] }}
|
|
62
|
+
*/
|
|
63
|
+
function buildBatchResult(items) {
|
|
64
|
+
const errors = [];
|
|
65
|
+
const warnings = [];
|
|
66
|
+
items.forEach(item => {
|
|
67
|
+
if (item.error) {
|
|
68
|
+
errors.push(`${item.appName}: ${item.error}`);
|
|
69
|
+
} else if (item.result) {
|
|
70
|
+
collectResultErrors(item.result).forEach(e => errors.push(`${item.appName}: ${e}`));
|
|
71
|
+
collectResultWarnings(item.result).forEach(w => warnings.push(`${item.appName}: ${w}`));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
const valid = items.every(item => item.result && item.result.valid);
|
|
75
|
+
return {
|
|
76
|
+
batch: true,
|
|
77
|
+
valid,
|
|
78
|
+
results: items,
|
|
79
|
+
errors,
|
|
80
|
+
warnings
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Validates all apps under integration/ (each as external system).
|
|
86
|
+
* @async
|
|
87
|
+
* @param {Function} validateAppOrFile - validateAppOrFile(appName, options) from validate.js
|
|
88
|
+
* @param {Object} [options] - Validation options
|
|
89
|
+
* @returns {Promise<{ batch: true, valid: boolean, results: Array, errors: string[], warnings: string[] }>}
|
|
90
|
+
*/
|
|
91
|
+
async function validateAllIntegrations(validateAppOrFile, options = {}) {
|
|
92
|
+
const names = listIntegrationAppNames();
|
|
93
|
+
const items = [];
|
|
94
|
+
for (const appName of names) {
|
|
95
|
+
try {
|
|
96
|
+
const result = await validateAppOrFile(appName, options);
|
|
97
|
+
items.push({ appName, result });
|
|
98
|
+
} catch (error) {
|
|
99
|
+
items.push({ appName, error: error.message || String(error) });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return buildBatchResult(items);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Validates all apps under builder/.
|
|
107
|
+
* @async
|
|
108
|
+
* @param {Function} validateAppOrFile - validateAppOrFile(appName, options) from validate.js
|
|
109
|
+
* @param {Object} [options] - Validation options
|
|
110
|
+
* @returns {Promise<{ batch: true, valid: boolean, results: Array, errors: string[], warnings: string[] }>}
|
|
111
|
+
*/
|
|
112
|
+
async function validateAllBuilderApps(validateAppOrFile, options = {}) {
|
|
113
|
+
const names = listBuilderAppNames();
|
|
114
|
+
const items = [];
|
|
115
|
+
for (const appName of names) {
|
|
116
|
+
try {
|
|
117
|
+
const result = await validateAppOrFile(appName, options);
|
|
118
|
+
items.push({ appName, result });
|
|
119
|
+
} catch (error) {
|
|
120
|
+
items.push({ appName, error: error.message || String(error) });
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return buildBatchResult(items);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Validates all integration and builder apps in one run.
|
|
128
|
+
* @async
|
|
129
|
+
* @param {Function} validateAppOrFile - validateAppOrFile(appName, options) from validate.js
|
|
130
|
+
* @param {Object} [options] - Validation options
|
|
131
|
+
* @returns {Promise<{ batch: true, valid: boolean, results: Array, errors: string[], warnings: string[] }>}
|
|
132
|
+
*/
|
|
133
|
+
async function validateAll(validateAppOrFile, options = {}) {
|
|
134
|
+
const [integrationResult, builderResult] = await Promise.all([
|
|
135
|
+
validateAllIntegrations(validateAppOrFile, options),
|
|
136
|
+
validateAllBuilderApps(validateAppOrFile, options)
|
|
137
|
+
]);
|
|
138
|
+
const mergedResults = [...integrationResult.results, ...builderResult.results];
|
|
139
|
+
return buildBatchResult(mergedResults);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
module.exports = {
|
|
143
|
+
buildBatchResult,
|
|
144
|
+
collectResultErrors,
|
|
145
|
+
collectResultWarnings,
|
|
146
|
+
validateAllIntegrations,
|
|
147
|
+
validateAllBuilderApps,
|
|
148
|
+
validateAll
|
|
149
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Async validation of datasourceKeys against dataplane API
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { getPlatformDetails } = require('../api/wizard.api');
|
|
8
|
+
const { validateDatasourceKeysForPlatform } = require('./wizard-datasource-validation');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Validate datasourceKeys against platform's available datasources; throws if invalid
|
|
12
|
+
* @async
|
|
13
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
14
|
+
* @param {Object} authConfig - Auth config
|
|
15
|
+
* @param {string} platformKey - Platform key
|
|
16
|
+
* @param {string[]} datasourceKeys - Datasource keys to validate
|
|
17
|
+
* @throws {Error} If any key is invalid
|
|
18
|
+
*/
|
|
19
|
+
async function validateDatasourceKeysBeforePlatformConfig(dataplaneUrl, authConfig, platformKey, datasourceKeys) {
|
|
20
|
+
if (!Array.isArray(datasourceKeys) || datasourceKeys.length === 0) return;
|
|
21
|
+
const platformDetails = await getPlatformDetails(dataplaneUrl, authConfig, platformKey);
|
|
22
|
+
const datasources = platformDetails?.data?.datasources ?? platformDetails?.datasources ?? [];
|
|
23
|
+
const { valid, invalidKeys } = validateDatasourceKeysForPlatform(datasourceKeys, datasources);
|
|
24
|
+
if (!valid) {
|
|
25
|
+
const availableKeys = datasources.map(d => d.key).filter(Boolean);
|
|
26
|
+
throw new Error(
|
|
27
|
+
`Invalid datasource keys: [${invalidKeys.join(', ')}]. ` +
|
|
28
|
+
`Available for platform '${platformKey}': [${availableKeys.join(', ')}].`
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = { validateDatasourceKeysBeforePlatformConfig };
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
const path = require('path');
|
|
11
12
|
const chalk = require('chalk');
|
|
12
13
|
const logger = require('../utils/logger');
|
|
13
14
|
const { loadConfigFile } = require('../utils/config-format');
|
|
@@ -242,22 +243,25 @@ function displayApplicationStep(application) {
|
|
|
242
243
|
* @returns {void}
|
|
243
244
|
*/
|
|
244
245
|
function displayComponentsStep(components) {
|
|
245
|
-
|
|
246
|
+
const hasFiles = components.files && components.files.length > 0;
|
|
247
|
+
const hasErrors = components.errors && components.errors.length > 0;
|
|
248
|
+
if (!hasFiles && !hasErrors) {
|
|
246
249
|
return;
|
|
247
250
|
}
|
|
248
251
|
|
|
249
252
|
logger.log(chalk.blue('\nExternal Integration Files:'));
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
253
|
+
if (hasFiles) {
|
|
254
|
+
components.files.forEach(file => {
|
|
255
|
+
if (file.valid) {
|
|
256
|
+
logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
|
|
257
|
+
} else {
|
|
258
|
+
logger.log(chalk.red(` ✗ ${file.file} (${file.type})`));
|
|
259
|
+
}
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (hasErrors) {
|
|
259
263
|
components.errors.forEach(error => {
|
|
260
|
-
logger.log(chalk.red(`
|
|
264
|
+
logger.log(chalk.red(` • ${error}`));
|
|
261
265
|
});
|
|
262
266
|
}
|
|
263
267
|
}
|
|
@@ -301,7 +305,9 @@ function displayDimensionsStep(datasourceFiles) {
|
|
|
301
305
|
*/
|
|
302
306
|
function displayManifestStep(manifest, componentFiles) {
|
|
303
307
|
logger.log(chalk.blue('\nDeployment Manifest:'));
|
|
304
|
-
if (manifest.
|
|
308
|
+
if (manifest.skipped) {
|
|
309
|
+
logger.log(chalk.yellow(' ⊘ Skipped (fix errors above first)'));
|
|
310
|
+
} else if (manifest.valid) {
|
|
305
311
|
logger.log(chalk.green(' ✓ Full deployment manifest is valid'));
|
|
306
312
|
if (componentFiles) {
|
|
307
313
|
const datasourceFiles = componentFiles.filter(f => f.type === 'datasource' || f.type === 'external-datasource');
|
|
@@ -311,10 +317,14 @@ function displayManifestStep(manifest, componentFiles) {
|
|
|
311
317
|
}
|
|
312
318
|
} else {
|
|
313
319
|
logger.log(chalk.red(' ✗ Full deployment manifest validation failed:'));
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
320
|
+
const errs = manifest.errors && manifest.errors.length > 0 ? manifest.errors : [];
|
|
321
|
+
if (errs.length > 0) {
|
|
322
|
+
errs.forEach(error => {
|
|
323
|
+
const msg = typeof error === 'string' ? error : String(error);
|
|
324
|
+
logger.log(chalk.red(` • ${msg}`));
|
|
317
325
|
});
|
|
326
|
+
} else {
|
|
327
|
+
logger.log(chalk.red(' • No error details available (check schema and manifest structure).'));
|
|
318
328
|
}
|
|
319
329
|
}
|
|
320
330
|
if (manifest.warnings && manifest.warnings.length > 0) {
|
|
@@ -338,7 +348,7 @@ function displayStepByStepValidation(result) {
|
|
|
338
348
|
|
|
339
349
|
displayApplicationStep(result.steps.application);
|
|
340
350
|
|
|
341
|
-
if (result.steps.components.files && result.steps.components.files.length > 0) {
|
|
351
|
+
if (!result.steps.components.valid || (result.steps.components.files && result.steps.components.files.length > 0)) {
|
|
342
352
|
displayComponentsStep(result.steps.components);
|
|
343
353
|
}
|
|
344
354
|
|
|
@@ -356,6 +366,68 @@ function displayStepByStepValidation(result) {
|
|
|
356
366
|
if (result.warnings && result.warnings.length > 0) {
|
|
357
367
|
displayAggregatedWarnings(result.warnings);
|
|
358
368
|
}
|
|
369
|
+
|
|
370
|
+
displayOverallStatus(result);
|
|
371
|
+
if (result.appPath) {
|
|
372
|
+
logger.log(chalk.gray(`\n${path.resolve(result.appPath)}`));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Displays batch validation results (per-app blocks then summary).
|
|
378
|
+
* Expects batchResult.batch === true and batchResult.results.
|
|
379
|
+
* @function displayBatchValidationResults
|
|
380
|
+
* @param {Object} batchResult - Batch result from validateAllIntegrations / validateAllBuilderApps / validateAll
|
|
381
|
+
*/
|
|
382
|
+
function displayBatchValidationResults(batchResult) {
|
|
383
|
+
if (!batchResult || batchResult.batch !== true || !Array.isArray(batchResult.results)) {
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const results = batchResult.results;
|
|
388
|
+
results.forEach(item => {
|
|
389
|
+
logger.log(chalk.blue(`\n--- ${item.appName} ---`));
|
|
390
|
+
if (item.error) {
|
|
391
|
+
logger.log(chalk.red(` ✗ ${item.error}`));
|
|
392
|
+
} else if (item.result) {
|
|
393
|
+
displayValidationResults(item.result);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
const passed = results.filter(r => r.result && r.result.valid).length;
|
|
398
|
+
const failed = results.length - passed;
|
|
399
|
+
logger.log(chalk.blue('\n--- Summary ---'));
|
|
400
|
+
if (failed === 0) {
|
|
401
|
+
logger.log(chalk.green(`✓ ${passed} passed, 0 failed`));
|
|
402
|
+
logger.log(chalk.green('Overall: Passed'));
|
|
403
|
+
} else {
|
|
404
|
+
logger.log(chalk.red(`✗ ${passed} passed, ${failed} failed`));
|
|
405
|
+
logger.log(chalk.red('Overall: Failed'));
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Displays overall validation status
|
|
411
|
+
* @param {Object} result - Validation result
|
|
412
|
+
*/
|
|
413
|
+
function displayOverallStatus(result) {
|
|
414
|
+
let hasErrors = result.errors && result.errors.length > 0;
|
|
415
|
+
if (!hasErrors && result.steps) {
|
|
416
|
+
const stepErrors = [result.steps.application, result.steps.components, result.steps.manifest]
|
|
417
|
+
.filter(Boolean)
|
|
418
|
+
.some(s => s.errors && s.errors.length > 0);
|
|
419
|
+
if (stepErrors) {
|
|
420
|
+
hasErrors = true;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const hasWarnings = result.warnings && result.warnings.length > 0;
|
|
424
|
+
if (hasErrors) {
|
|
425
|
+
logger.log(chalk.red('\nOverall: Failed'));
|
|
426
|
+
} else if (hasWarnings) {
|
|
427
|
+
logger.log(chalk.yellow('\nOverall: Passed with warnings'));
|
|
428
|
+
} else {
|
|
429
|
+
logger.log(chalk.green('\nOverall: Passed'));
|
|
430
|
+
}
|
|
359
431
|
}
|
|
360
432
|
|
|
361
433
|
/**
|
|
@@ -390,10 +462,16 @@ function displayValidationResults(result) {
|
|
|
390
462
|
// Combine all warnings
|
|
391
463
|
const allWarnings = [...(result.warnings || []), ...dimensionWarnings];
|
|
392
464
|
displayAggregatedWarnings(allWarnings);
|
|
465
|
+
|
|
466
|
+
displayOverallStatus(result);
|
|
467
|
+
if (result.appPath) {
|
|
468
|
+
logger.log(chalk.gray(`\n${path.resolve(result.appPath)}`));
|
|
469
|
+
}
|
|
393
470
|
}
|
|
394
471
|
|
|
395
472
|
module.exports = {
|
|
396
473
|
displayValidationResults,
|
|
474
|
+
displayBatchValidationResults,
|
|
397
475
|
displayStepByStepValidation,
|
|
398
476
|
displayApplicationValidation,
|
|
399
477
|
displayExternalFilesValidation,
|
|
@@ -16,11 +16,16 @@ const { resolveExternalFiles } = require('../utils/schema-resolver');
|
|
|
16
16
|
const { loadExternalSystemSchema, loadExternalDataSourceSchema, detectSchemaType } = require('../utils/schema-loader');
|
|
17
17
|
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
18
18
|
const { detectAppType } = require('../utils/paths');
|
|
19
|
+
const batch = require('./validate-batch');
|
|
19
20
|
const { logOfflinePathWhenType } = require('../utils/cli-utils');
|
|
20
21
|
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
21
|
-
const { displayValidationResults } = require('./validate-display');
|
|
22
|
+
const { displayValidationResults, displayBatchValidationResults } = require('./validate-display');
|
|
22
23
|
const { generateControllerManifest } = require('../generator/external-controller-manifest');
|
|
23
24
|
const { validateControllerManifest } = require('./external-manifest-validator');
|
|
25
|
+
const {
|
|
26
|
+
validateOAuth2GrantTypeAndAuthorizationUrl,
|
|
27
|
+
validateConfigurationNoStandardAuthVariables
|
|
28
|
+
} = require('./external-system-auth-rules');
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
31
|
* Validates a file path (detects type and validates)
|
|
@@ -191,6 +196,8 @@ async function validateExternalFile(filePath, type) {
|
|
|
191
196
|
|
|
192
197
|
if (normalizedType === 'system') {
|
|
193
198
|
validateRoleReferences(parseResult.parsed, errors);
|
|
199
|
+
validateOAuth2GrantTypeAndAuthorizationUrl(parseResult.parsed, errors);
|
|
200
|
+
validateConfigurationNoStandardAuthVariables(parseResult.parsed, errors);
|
|
194
201
|
}
|
|
195
202
|
|
|
196
203
|
return {
|
|
@@ -398,6 +405,7 @@ async function validateExternalSystemComplete(appName, options = {}) {
|
|
|
398
405
|
throw new Error('App name is required and must be a string');
|
|
399
406
|
}
|
|
400
407
|
|
|
408
|
+
const { appPath } = await detectAppType(appName, options);
|
|
401
409
|
const steps = {
|
|
402
410
|
application: { valid: false, errors: [], warnings: [] },
|
|
403
411
|
components: { valid: false, errors: [], warnings: [], files: [] },
|
|
@@ -414,11 +422,13 @@ async function validateExternalSystemComplete(appName, options = {}) {
|
|
|
414
422
|
|
|
415
423
|
// If components have errors, return early (don't validate manifest)
|
|
416
424
|
if (!steps.components.valid) {
|
|
425
|
+
steps.manifest = { valid: true, errors: [], warnings: [], skipped: true };
|
|
417
426
|
return {
|
|
418
427
|
valid: false,
|
|
419
428
|
errors: [...steps.application.errors, ...steps.components.errors],
|
|
420
429
|
warnings: [...steps.application.warnings, ...steps.components.warnings],
|
|
421
|
-
steps
|
|
430
|
+
steps,
|
|
431
|
+
appPath
|
|
422
432
|
};
|
|
423
433
|
}
|
|
424
434
|
|
|
@@ -433,7 +443,8 @@ async function validateExternalSystemComplete(appName, options = {}) {
|
|
|
433
443
|
valid: allErrors.length === 0,
|
|
434
444
|
errors: allErrors,
|
|
435
445
|
warnings: allWarnings,
|
|
436
|
-
steps
|
|
446
|
+
steps,
|
|
447
|
+
appPath
|
|
437
448
|
};
|
|
438
449
|
}
|
|
439
450
|
|
|
@@ -451,29 +462,31 @@ async function validateAppOrFile(appOrFile, options = {}) {
|
|
|
451
462
|
const { appPath, isExternal } = await detectAppType(appName);
|
|
452
463
|
logOfflinePathWhenType(appPath);
|
|
453
464
|
|
|
454
|
-
if (isExternal)
|
|
455
|
-
return await validateExternalSystemComplete(appName, options);
|
|
456
|
-
}
|
|
465
|
+
if (isExternal) return await validateExternalSystemComplete(appName, options);
|
|
457
466
|
|
|
458
467
|
const appValidation = await validator.validateApplication(appName, options);
|
|
459
468
|
const rbacValidation = await validateRbacForExternalSystem(isExternal, appName);
|
|
460
|
-
|
|
461
469
|
const variablesPath = resolveApplicationConfigPath(appPath);
|
|
462
470
|
const earlyReturn = loadVariablesAndCheckExternalIntegration(variablesPath, appValidation);
|
|
463
|
-
if (earlyReturn)
|
|
464
|
-
return earlyReturn;
|
|
465
|
-
}
|
|
471
|
+
if (earlyReturn) return earlyReturn;
|
|
466
472
|
|
|
467
473
|
const externalValidations = await validateExternalFilesForApp(appName, options);
|
|
468
|
-
|
|
474
|
+
const result = aggregateValidationResults(appValidation, externalValidations, rbacValidation);
|
|
475
|
+
result.appPath = appPath;
|
|
476
|
+
return result;
|
|
469
477
|
}
|
|
470
478
|
|
|
471
479
|
module.exports = {
|
|
472
480
|
validateAppOrFile,
|
|
473
481
|
validateExternalSystemComplete,
|
|
474
482
|
displayValidationResults,
|
|
483
|
+
displayBatchValidationResults,
|
|
475
484
|
validateExternalFile,
|
|
476
485
|
validateExternalFilesForApp,
|
|
477
|
-
validateFilePath
|
|
486
|
+
validateFilePath,
|
|
487
|
+
validateAllIntegrations: (opts = {}) => batch.validateAllIntegrations(validateAppOrFile, opts),
|
|
488
|
+
validateAllBuilderApps: (opts = {}) => batch.validateAllBuilderApps(validateAppOrFile, opts),
|
|
489
|
+
validateAll: (opts = {}) => batch.validateAll(validateAppOrFile, opts),
|
|
490
|
+
buildBatchResult: batch.buildBatchResult
|
|
478
491
|
};
|
|
479
492
|
|
|
@@ -21,6 +21,8 @@ const { checkEnvironment } = require('../utils/environment-checker');
|
|
|
21
21
|
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
22
22
|
const { detectAppType, resolveApplicationConfigPath } = require('../utils/paths');
|
|
23
23
|
const { loadConfigFile } = require('../utils/config-format');
|
|
24
|
+
const { validateAuthKvCoverage } = require('./env-template-auth');
|
|
25
|
+
const { validateKvReferencesInLines } = require('./env-template-kv');
|
|
24
26
|
|
|
25
27
|
/**
|
|
26
28
|
* Validates application config file against application schema
|
|
@@ -250,7 +252,7 @@ async function validateEnvTemplate(appName, options = {}) {
|
|
|
250
252
|
}
|
|
251
253
|
|
|
252
254
|
// Support both builder/ and integration/ directories using detectAppType
|
|
253
|
-
const { appPath } = await detectAppType(appName, options);
|
|
255
|
+
const { appPath, isExternal } = await detectAppType(appName, options);
|
|
254
256
|
const templatePath = path.join(appPath, 'env.template');
|
|
255
257
|
|
|
256
258
|
if (!fs.existsSync(templatePath)) {
|
|
@@ -281,14 +283,10 @@ async function validateEnvTemplate(appName, options = {}) {
|
|
|
281
283
|
}
|
|
282
284
|
});
|
|
283
285
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
const secretKey = match[1];
|
|
289
|
-
if (!secretKey) {
|
|
290
|
-
errors.push('Invalid kv:// reference format');
|
|
291
|
-
}
|
|
286
|
+
validateKvReferencesInLines(lines, errors);
|
|
287
|
+
|
|
288
|
+
if (isExternal) {
|
|
289
|
+
await validateAuthKvCoverage(appPath, content, errors, warnings, options);
|
|
292
290
|
}
|
|
293
291
|
|
|
294
292
|
return {
|
|
@@ -365,6 +363,69 @@ function validateDeploymentJson(deployment) {
|
|
|
365
363
|
};
|
|
366
364
|
}
|
|
367
365
|
|
|
366
|
+
/** Pattern matching ${VAR} style unresolved variables in strings */
|
|
367
|
+
const UNRESOLVED_VAR_REGEX = /\$\{[^}]+\}/g;
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Recursively finds all string values in obj that contain ${...} placeholders.
|
|
371
|
+
* Used to ensure deployment manifest has no unresolved variables before deploy.
|
|
372
|
+
*
|
|
373
|
+
* @function findUnresolvedVariablesInObject
|
|
374
|
+
* @param {Object} obj - Object to scan (e.g. deployment manifest)
|
|
375
|
+
* @param {string} [prefix=''] - Path prefix for error reporting
|
|
376
|
+
* @returns {string[]} List of paths with example placeholder (e.g. "port: ${PORT}")
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* findUnresolvedVariablesInObject({ port: '${PORT}' }) // ['port: ${PORT}']
|
|
380
|
+
*/
|
|
381
|
+
function findUnresolvedVariablesInObject(obj, prefix = '') {
|
|
382
|
+
const found = [];
|
|
383
|
+
if (obj === null || obj === undefined) {
|
|
384
|
+
return found;
|
|
385
|
+
}
|
|
386
|
+
if (typeof obj === 'string') {
|
|
387
|
+
const matches = obj.match(UNRESOLVED_VAR_REGEX);
|
|
388
|
+
if (matches && matches.length > 0) {
|
|
389
|
+
const pathLabel = prefix || 'value';
|
|
390
|
+
found.push(`${pathLabel}: ${matches[0]}`);
|
|
391
|
+
}
|
|
392
|
+
return found;
|
|
393
|
+
}
|
|
394
|
+
if (Array.isArray(obj)) {
|
|
395
|
+
obj.forEach((item, i) => {
|
|
396
|
+
found.push(...findUnresolvedVariablesInObject(item, `${prefix}[${i}]`));
|
|
397
|
+
});
|
|
398
|
+
return found;
|
|
399
|
+
}
|
|
400
|
+
if (typeof obj === 'object') {
|
|
401
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
402
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
403
|
+
found.push(...findUnresolvedVariablesInObject(value, path));
|
|
404
|
+
}
|
|
405
|
+
return found;
|
|
406
|
+
}
|
|
407
|
+
return found;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/**
|
|
411
|
+
* Validates that deployment manifest contains no unresolved ${...} variables.
|
|
412
|
+
* Throws if any are found, with a message to use secret variables or literal values.
|
|
413
|
+
*
|
|
414
|
+
* @function validateNoUnresolvedVariablesInDeployment
|
|
415
|
+
* @param {Object} deployment - Deployment manifest object
|
|
416
|
+
* @throws {Error} If any ${...} placeholders are found
|
|
417
|
+
*/
|
|
418
|
+
function validateNoUnresolvedVariablesInDeployment(deployment) {
|
|
419
|
+
const unresolved = findUnresolvedVariablesInObject(deployment);
|
|
420
|
+
if (unresolved.length > 0) {
|
|
421
|
+
const examples = [...new Set(unresolved)].slice(0, 5).join(', ');
|
|
422
|
+
throw new Error(
|
|
423
|
+
`Deployment manifest contains unresolved variables (e.g. ${examples}). ` +
|
|
424
|
+
'Use secret variables (kv://) in env.template for sensitive values, and set the application port as a number in application.yaml.'
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
368
429
|
/**
|
|
369
430
|
* Validates all application configuration files
|
|
370
431
|
* Runs complete validation suite for an application
|
|
@@ -411,6 +472,8 @@ module.exports = {
|
|
|
411
472
|
validateRbac,
|
|
412
473
|
validateEnvTemplate,
|
|
413
474
|
validateDeploymentJson,
|
|
475
|
+
validateNoUnresolvedVariablesInDeployment,
|
|
476
|
+
findUnresolvedVariablesInObject,
|
|
414
477
|
validateObjectAgainstApplicationSchema,
|
|
415
478
|
checkEnvironment,
|
|
416
479
|
formatValidationErrors,
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Wizard datasource validation helpers - validate datasourceKeys and entityName against dataplane
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Validate that all datasourceKeys exist in the platform's available datasources
|
|
9
|
+
* @function validateDatasourceKeysForPlatform
|
|
10
|
+
* @param {string[]} datasourceKeys - User-provided datasource keys
|
|
11
|
+
* @param {Array<{key: string, displayName?: string, entity?: string}>} availableDatasources - Platform datasources
|
|
12
|
+
* @returns {{ valid: boolean, invalidKeys: string[] }} Validation result
|
|
13
|
+
*/
|
|
14
|
+
function validateDatasourceKeysForPlatform(datasourceKeys, availableDatasources) {
|
|
15
|
+
if (!Array.isArray(datasourceKeys) || datasourceKeys.length === 0) {
|
|
16
|
+
return { valid: true, invalidKeys: [] };
|
|
17
|
+
}
|
|
18
|
+
const availableKeys = Array.isArray(availableDatasources)
|
|
19
|
+
? availableDatasources.map(d => (d && typeof d === 'object' && d.key) ? d.key : null).filter(Boolean)
|
|
20
|
+
: [];
|
|
21
|
+
const invalidKeys = datasourceKeys.filter(k => !availableKeys.includes(k));
|
|
22
|
+
return {
|
|
23
|
+
valid: invalidKeys.length === 0,
|
|
24
|
+
invalidKeys
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Validate that entityName exists in the discovered entities list
|
|
30
|
+
* @function validateEntityNameForOpenApi
|
|
31
|
+
* @param {string} entityName - User-provided entity name
|
|
32
|
+
* @param {Array<{name: string}>} entities - Discovered entities from OpenAPI
|
|
33
|
+
* @returns {{ valid: boolean }} Validation result
|
|
34
|
+
*/
|
|
35
|
+
function validateEntityNameForOpenApi(entityName, entities) {
|
|
36
|
+
if (!entityName || typeof entityName !== 'string' || entityName.trim() === '') {
|
|
37
|
+
return { valid: true };
|
|
38
|
+
}
|
|
39
|
+
const entityNames = Array.isArray(entities)
|
|
40
|
+
? entities.map(e => (e && typeof e === 'object' && e.name) ? e.name : null).filter(Boolean)
|
|
41
|
+
: [];
|
|
42
|
+
return {
|
|
43
|
+
valid: entityNames.includes(entityName)
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
module.exports = {
|
|
48
|
+
validateDatasourceKeysForPlatform,
|
|
49
|
+
validateEntityNameForOpenApi
|
|
50
|
+
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aifabrix/builder",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.42.0",
|
|
4
4
|
"description": "AI Fabrix Local Fabric & Deployment SDK",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"aifabrix": "bin/aifabrix.js",
|
|
8
|
-
"
|
|
8
|
+
"af": "bin/aifabrix.js"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"test": "node tests/scripts/test-wrapper.js",
|
|
12
12
|
"test:ci": "bash tests/scripts/ci-simulate.sh",
|
|
13
|
+
"test:same-as-github": "cross-env CI=true npm run lint && cross-env CI=true node tests/scripts/test-wrapper.js",
|
|
13
14
|
"test:coverage": "cross-env RUN_COVERAGE=1 node tests/scripts/test-wrapper.js",
|
|
14
15
|
"test:coverage:nyc": "nyc --reporter=text --reporter=lcov --reporter=html jest --config jest.config.coverage.js --runInBand",
|
|
15
16
|
"test:watch": "jest --watch",
|
|
@@ -71,13 +72,16 @@
|
|
|
71
72
|
},
|
|
72
73
|
"dependencies": {
|
|
73
74
|
"ajv": "^8.12.0",
|
|
75
|
+
"ajv-formats": "^3.0.1",
|
|
74
76
|
"axios": "^1.6.0",
|
|
75
77
|
"chalk": "^4.1.2",
|
|
76
78
|
"commander": "^11.1.0",
|
|
77
79
|
"handlebars": "^4.7.8",
|
|
78
80
|
"inquirer": "^8.2.5",
|
|
81
|
+
"inquirer-autocomplete-prompt": "^2.0.0",
|
|
79
82
|
"js-yaml": "^4.1.0",
|
|
80
|
-
"ora": "^5.4.1"
|
|
83
|
+
"ora": "^5.4.1",
|
|
84
|
+
"yaml": "^2.4.0"
|
|
81
85
|
},
|
|
82
86
|
"devDependencies": {
|
|
83
87
|
"@babel/preset-env": "^7.29.0",
|
|
@@ -86,6 +90,7 @@
|
|
|
86
90
|
"cross-env": "^10.1.0",
|
|
87
91
|
"eslint": "^8.55.0",
|
|
88
92
|
"jest": "^30.2.0",
|
|
93
|
+
"markdownlint-cli": "^0.47.0",
|
|
89
94
|
"nyc": "^17.1.0"
|
|
90
95
|
},
|
|
91
96
|
"repository": {
|