@aifabrix/builder 2.41.0 → 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 +1 -1
- 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/jest.config.manual.js +2 -1
- 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/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 +23 -11
- package/lib/app/index.js +3 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/readme.js +8 -3
- package/lib/app/run-env-compose.js +64 -1
- package/lib/app/run-helpers.js +1 -1
- package/lib/app/show-display.js +1 -1
- package/lib/cli/setup-app.js +42 -11
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +27 -0
- package/lib/cli/setup-environment.js +12 -4
- package/lib/cli/setup-external-system.js +19 -4
- package/lib/cli/setup-infra.js +54 -14
- package/lib/cli/setup-utility.js +117 -21
- 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-init.js +39 -1
- 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/test-e2e-external.js +165 -0
- package/lib/commands/upload.js +71 -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/config.js +7 -1
- package/lib/core/secrets.js +33 -12
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/deployment/deployer.js +7 -5
- package/lib/external-system/download.js +182 -204
- package/lib/external-system/generator.js +204 -56
- 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 +51 -18
- 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 +4 -1
- 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 +147 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/index.js +11 -3
- package/lib/infrastructure/services.js +22 -11
- package/lib/schema/application-schema.json +8 -5
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +82 -6
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +38 -10
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/compose-generator.js +1 -1
- package/lib/utils/compose-handlebars-helpers.js +11 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +115 -25
- 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/env-copy.js +23 -3
- package/lib/utils/error-formatters/http-status-errors.js +0 -1
- 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 +1 -0
- package/lib/utils/infra-status.js +50 -44
- package/lib/utils/local-secrets.js +5 -5
- package/lib/utils/paths.js +85 -4
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +20 -0
- package/lib/utils/secrets-helpers.js +75 -89
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager.js +24 -32
- 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 +7 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +7 -2
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/miso-controller/env.template +1 -1
- 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 +4 -4
- package/templates/typescript/docker-compose.hbs +4 -4
- package/integration/hubspot/application.yaml +0 -37
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run E2E tests for all datasources of an external system.
|
|
3
|
+
*
|
|
4
|
+
* @fileoverview test-e2e <external system> – run E2E for every datasource
|
|
5
|
+
* @author AI Fabrix Team
|
|
6
|
+
* @version 2.0.0
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
'use strict';
|
|
10
|
+
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const chalk = require('chalk');
|
|
14
|
+
const logger = require('../utils/logger');
|
|
15
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
16
|
+
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
17
|
+
const { loadConfigFile } = require('../utils/config-format');
|
|
18
|
+
const { discoverIntegrationFiles, buildEffectiveDatasourceFiles } = require('./repair-internal');
|
|
19
|
+
const { runDatasourceTestE2E } = require('../datasource/test-e2e');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Derives datasource key from filename when file has no key (same logic as repair).
|
|
23
|
+
* @param {string} fileName - Datasource file name
|
|
24
|
+
* @param {string} systemKey - System key
|
|
25
|
+
* @returns {string}
|
|
26
|
+
*/
|
|
27
|
+
function deriveDatasourceKeyFromFileName(fileName, systemKey) {
|
|
28
|
+
const base = path.basename(fileName, path.extname(fileName));
|
|
29
|
+
if (/^datasource-/.test(base)) {
|
|
30
|
+
const suffix = base.slice('datasource-'.length);
|
|
31
|
+
return systemKey && typeof systemKey === 'string' ? `${systemKey}-${suffix}` : base;
|
|
32
|
+
}
|
|
33
|
+
return base.replace(/-datasource-/, '-');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* eslint-disable max-statements -- Key resolution from system or files */
|
|
37
|
+
/**
|
|
38
|
+
* Resolves the list of datasource keys for an external system (from system file or discovered files).
|
|
39
|
+
* @param {string} appPath - Integration app path
|
|
40
|
+
* @param {string} configPath - Application config path
|
|
41
|
+
* @param {Object} variables - Loaded application variables (externalIntegration.dataSources = filenames)
|
|
42
|
+
* @param {string} systemKey - System key from system file
|
|
43
|
+
* @param {Object} systemParsed - Parsed system config (may have dataSources array of keys)
|
|
44
|
+
* @param {string[]} datasourceFiles - Discovered datasource filenames
|
|
45
|
+
* @returns {string[]} Sorted list of datasource keys
|
|
46
|
+
*/
|
|
47
|
+
function getDatasourceKeys(appPath, configPath, variables, systemKey, systemParsed, datasourceFiles) {
|
|
48
|
+
const fromSystem = Array.isArray(systemParsed.dataSources) && systemParsed.dataSources.length > 0
|
|
49
|
+
? systemParsed.dataSources
|
|
50
|
+
: null;
|
|
51
|
+
const keys = [];
|
|
52
|
+
const seen = new Set();
|
|
53
|
+
if (fromSystem) {
|
|
54
|
+
fromSystem.forEach(k => {
|
|
55
|
+
if (k && typeof k === 'string' && !seen.has(k)) {
|
|
56
|
+
keys.push(k.trim());
|
|
57
|
+
seen.add(k.trim());
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
keys.sort();
|
|
61
|
+
return keys;
|
|
62
|
+
}
|
|
63
|
+
for (const fileName of datasourceFiles) {
|
|
64
|
+
const filePath = path.join(appPath, fileName);
|
|
65
|
+
if (!fs.existsSync(filePath)) continue;
|
|
66
|
+
try {
|
|
67
|
+
const parsed = loadConfigFile(filePath);
|
|
68
|
+
const key = parsed && typeof parsed.key === 'string' && parsed.key.trim()
|
|
69
|
+
? parsed.key.trim()
|
|
70
|
+
: deriveDatasourceKeyFromFileName(fileName, systemKey);
|
|
71
|
+
if (key && !seen.has(key)) {
|
|
72
|
+
keys.push(key);
|
|
73
|
+
seen.add(key);
|
|
74
|
+
}
|
|
75
|
+
} catch {
|
|
76
|
+
const key = deriveDatasourceKeyFromFileName(fileName, systemKey);
|
|
77
|
+
if (key && !seen.has(key)) {
|
|
78
|
+
keys.push(key);
|
|
79
|
+
seen.add(key);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
keys.sort();
|
|
84
|
+
return keys;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/* eslint-disable max-lines-per-function, max-statements -- Load context, then loop over keys */
|
|
88
|
+
/**
|
|
89
|
+
* Runs E2E for all datasources of an external system. Uses each datasource's payloadTemplate (no extra params required).
|
|
90
|
+
*
|
|
91
|
+
* @async
|
|
92
|
+
* @param {string} externalSystem - System key (e.g. hubspot-demo)
|
|
93
|
+
* @param {Object} options - Options passed to each runDatasourceTestE2E
|
|
94
|
+
* @param {string} [options.env] - Environment (dev, tst, pro)
|
|
95
|
+
* @param {boolean} [options.debug] - Include debug, write log
|
|
96
|
+
* @param {boolean} [options.verbose] - Verbose output
|
|
97
|
+
* @param {boolean} [options.async] - If false, sync mode (default true)
|
|
98
|
+
* @returns {Promise<{ success: boolean, results: Array<{ key: string, success: boolean, error?: string }> }>}
|
|
99
|
+
*/
|
|
100
|
+
async function runTestE2EForExternalSystem(externalSystem, options = {}) {
|
|
101
|
+
if (!externalSystem || typeof externalSystem !== 'string') {
|
|
102
|
+
throw new Error('External system name is required');
|
|
103
|
+
}
|
|
104
|
+
const appPath = getIntegrationPath(externalSystem);
|
|
105
|
+
if (!fs.existsSync(appPath)) {
|
|
106
|
+
throw new Error(`Integration path not found: ${appPath}`);
|
|
107
|
+
}
|
|
108
|
+
const configPath = resolveApplicationConfigPath(appPath);
|
|
109
|
+
let variables = {};
|
|
110
|
+
if (fs.existsSync(configPath)) {
|
|
111
|
+
variables = loadConfigFile(configPath);
|
|
112
|
+
}
|
|
113
|
+
const { systemFiles, datasourceFiles: discovered } = discoverIntegrationFiles(appPath);
|
|
114
|
+
if (systemFiles.length === 0) {
|
|
115
|
+
throw new Error(`No system file found in ${appPath}. Expected *-system.yaml or *-system.json`);
|
|
116
|
+
}
|
|
117
|
+
const datasourceFiles = buildEffectiveDatasourceFiles(
|
|
118
|
+
appPath,
|
|
119
|
+
discovered,
|
|
120
|
+
variables.externalIntegration?.dataSources
|
|
121
|
+
);
|
|
122
|
+
const systemPath = path.join(appPath, systemFiles[0]);
|
|
123
|
+
const systemParsed = loadConfigFile(systemPath);
|
|
124
|
+
const systemKey = systemParsed.key ||
|
|
125
|
+
path.basename(systemFiles[0], path.extname(systemFiles[0])).replace(/-system$/, '');
|
|
126
|
+
|
|
127
|
+
const keys = getDatasourceKeys(
|
|
128
|
+
appPath,
|
|
129
|
+
configPath,
|
|
130
|
+
variables,
|
|
131
|
+
systemKey,
|
|
132
|
+
systemParsed,
|
|
133
|
+
datasourceFiles
|
|
134
|
+
);
|
|
135
|
+
if (keys.length === 0) {
|
|
136
|
+
logger.log(chalk.yellow(`No datasources found for ${externalSystem}. Add datasource files and run aifabrix repair.`));
|
|
137
|
+
return { success: true, results: [] };
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const results = [];
|
|
141
|
+
const opts = {
|
|
142
|
+
app: externalSystem,
|
|
143
|
+
environment: options.env,
|
|
144
|
+
debug: options.debug,
|
|
145
|
+
verbose: options.verbose,
|
|
146
|
+
async: options.async !== false
|
|
147
|
+
};
|
|
148
|
+
for (const key of keys) {
|
|
149
|
+
try {
|
|
150
|
+
const data = await runDatasourceTestE2E(key, opts);
|
|
151
|
+
const steps = data.steps || data.completedActions || [];
|
|
152
|
+
const failed = data.success === false || steps.some(s => s.success === false || s.error);
|
|
153
|
+
results.push({ key, success: !failed });
|
|
154
|
+
} catch (err) {
|
|
155
|
+
results.push({ key, success: false, error: err.message });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
const success = results.every(r => r.success);
|
|
159
|
+
return { success, results };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
runTestE2EForExternalSystem,
|
|
164
|
+
getDatasourceKeys
|
|
165
|
+
};
|
package/lib/commands/upload.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Upload external system to dataplane (upload → validate → publish).
|
|
2
|
+
* Upload external system to dataplane (single pipeline upload: upload → validate → publish).
|
|
3
3
|
*
|
|
4
4
|
* @fileoverview Upload command handler for aifabrix upload <system-key>
|
|
5
5
|
* @author AI Fabrix Team
|
|
@@ -14,15 +14,16 @@ const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../uti
|
|
|
14
14
|
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
15
15
|
const { getIntegrationPath } = require('../utils/paths');
|
|
16
16
|
const { pushCredentialSecrets } = require('../utils/credential-secrets-env');
|
|
17
|
+
const {
|
|
18
|
+
buildResolvedEnvMapForIntegration,
|
|
19
|
+
resolveConfigurationValues
|
|
20
|
+
} = require('../utils/configuration-env-resolver');
|
|
17
21
|
const { validateExternalSystemComplete } = require('../validation/validate');
|
|
18
22
|
const { displayValidationResults } = require('../validation/validate-display');
|
|
19
23
|
const { generateControllerManifest } = require('../generator/external-controller-manifest');
|
|
20
|
-
const {
|
|
21
|
-
uploadApplicationViaPipeline,
|
|
22
|
-
validateUploadViaPipeline,
|
|
23
|
-
publishUploadViaPipeline
|
|
24
|
-
} = require('../api/pipeline.api');
|
|
24
|
+
const { uploadApplicationViaPipeline } = require('../api/pipeline.api');
|
|
25
25
|
const { formatApiError } = require('../utils/api-error-handler');
|
|
26
|
+
const { logDataplanePipelineWarning } = require('../utils/dataplane-pipeline-warning');
|
|
26
27
|
|
|
27
28
|
/**
|
|
28
29
|
* Validates system-key format (same as download).
|
|
@@ -40,25 +41,25 @@ function validateSystemKeyFormat(systemKey) {
|
|
|
40
41
|
|
|
41
42
|
/**
|
|
42
43
|
* Builds pipeline upload payload from controller manifest.
|
|
43
|
-
* Payload: { version, application, dataSources };
|
|
44
|
+
* Payload: { version, application, dataSources, status }; Builder always uses status "draft".
|
|
44
45
|
* @param {Object} manifest - Controller manifest from generateControllerManifest
|
|
45
|
-
* @returns {Object} { version, application, dataSources }
|
|
46
|
+
* @returns {Object} { version, application, dataSources, status: "draft" }
|
|
46
47
|
*/
|
|
47
48
|
function buildUploadPayload(manifest) {
|
|
48
49
|
return {
|
|
49
50
|
version: manifest.version || '1.0.0',
|
|
50
51
|
application: manifest.system,
|
|
51
|
-
dataSources: manifest.dataSources || []
|
|
52
|
+
dataSources: manifest.dataSources || [],
|
|
53
|
+
status: 'draft'
|
|
52
54
|
};
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
/**
|
|
56
58
|
* Resolves dataplane URL and auth (same pattern as download).
|
|
57
59
|
* @param {string} systemKey - System key
|
|
58
|
-
* @param {Object} options - Options with optional dataplane override
|
|
59
60
|
* @returns {Promise<{ dataplaneUrl: string, authConfig: Object, environment: string }>}
|
|
60
61
|
*/
|
|
61
|
-
async function resolveDataplaneAndAuth(systemKey
|
|
62
|
+
async function resolveDataplaneAndAuth(systemKey) {
|
|
62
63
|
const { resolveEnvironment } = require('../core/config');
|
|
63
64
|
const environment = await resolveEnvironment();
|
|
64
65
|
const controllerUrl = await resolveControllerUrl();
|
|
@@ -68,42 +69,42 @@ async function resolveDataplaneAndAuth(systemKey, options) {
|
|
|
68
69
|
throw new Error('Authentication required. Run "aifabrix login" or "aifabrix app register <system-key>" first.');
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
dataplaneUrl = options.dataplane.replace(/\/$/, '');
|
|
74
|
-
} else {
|
|
75
|
-
logger.log(chalk.blue('Resolving dataplane URL...'));
|
|
76
|
-
dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
77
|
-
}
|
|
78
|
-
|
|
72
|
+
logger.log(chalk.blue('Resolving dataplane URL...'));
|
|
73
|
+
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
79
74
|
return { dataplaneUrl, authConfig, environment };
|
|
80
75
|
}
|
|
81
76
|
|
|
82
77
|
/**
|
|
83
|
-
* Runs upload → validate → publish on the dataplane.
|
|
78
|
+
* Runs single pipeline upload (upload → validate → publish) on the dataplane.
|
|
84
79
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
85
80
|
* @param {Object} authConfig - Auth config
|
|
86
|
-
* @param {Object} payload - { version, application, dataSources }
|
|
87
|
-
* @returns {Promise<
|
|
81
|
+
* @param {Object} payload - { version, application, dataSources, status: "draft" }
|
|
82
|
+
* @returns {Promise<Object>} Publication result from Dataplane
|
|
88
83
|
*/
|
|
89
84
|
async function runUploadValidatePublish(dataplaneUrl, authConfig, payload) {
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
const msg = uploadRes?.success === false
|
|
94
|
-
? formatApiError(uploadRes, dataplaneUrl)
|
|
95
|
-
: 'Upload did not return an upload ID';
|
|
85
|
+
const res = await uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload);
|
|
86
|
+
if (res?.success === false) {
|
|
87
|
+
const msg = formatApiError(res, dataplaneUrl);
|
|
96
88
|
throw new Error(msg);
|
|
97
89
|
}
|
|
90
|
+
return res;
|
|
91
|
+
}
|
|
98
92
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Builds a short summary of validation errors for the thrown message.
|
|
95
|
+
* @param {Object} validationResult - Result from validateExternalSystemComplete
|
|
96
|
+
* @returns {string} First few errors joined for the error message
|
|
97
|
+
*/
|
|
98
|
+
function formatValidationErrorSummary(validationResult) {
|
|
99
|
+
const errors = validationResult.errors || [];
|
|
100
|
+
if (errors.length === 0) {
|
|
101
|
+
return 'Validation failed. Fix errors before uploading.';
|
|
103
102
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
103
|
+
const maxShow = 3;
|
|
104
|
+
const shown = errors.slice(0, maxShow).map(e => (typeof e === 'string' ? e : String(e)));
|
|
105
|
+
const summary = shown.join('; ');
|
|
106
|
+
const more = errors.length > maxShow ? ` (and ${errors.length - maxShow} more)` : '';
|
|
107
|
+
return `Validation failed: ${summary}${more}. Fix errors above and run the command again.`;
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
/**
|
|
@@ -114,7 +115,24 @@ async function runUploadValidatePublish(dataplaneUrl, authConfig, payload) {
|
|
|
114
115
|
function throwIfValidationFailed(validationResult) {
|
|
115
116
|
if (!validationResult.valid) {
|
|
116
117
|
displayValidationResults(validationResult);
|
|
117
|
-
throw new Error(
|
|
118
|
+
throw new Error(formatValidationErrorSummary(validationResult));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Resolves configuration arrays in the upload payload (application + dataSources) from env and secrets.
|
|
124
|
+
* @param {string} systemKey - System key
|
|
125
|
+
* @param {Object} payload - Upload payload (mutated)
|
|
126
|
+
*/
|
|
127
|
+
async function resolvePayloadConfiguration(systemKey, payload) {
|
|
128
|
+
const { envMap, secrets } = await buildResolvedEnvMapForIntegration(systemKey);
|
|
129
|
+
if (Array.isArray(payload.application?.configuration) && payload.application.configuration.length > 0) {
|
|
130
|
+
resolveConfigurationValues(payload.application.configuration, envMap, secrets, systemKey);
|
|
131
|
+
}
|
|
132
|
+
for (const ds of payload.dataSources || []) {
|
|
133
|
+
if (Array.isArray(ds?.configuration) && ds.configuration.length > 0) {
|
|
134
|
+
resolveConfigurationValues(ds.configuration, envMap, secrets, systemKey);
|
|
135
|
+
}
|
|
118
136
|
}
|
|
119
137
|
}
|
|
120
138
|
|
|
@@ -133,7 +151,10 @@ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey,
|
|
|
133
151
|
payload
|
|
134
152
|
});
|
|
135
153
|
if (pushResult.pushed > 0) {
|
|
136
|
-
|
|
154
|
+
const keyList = pushResult.keys?.length ? ` (${pushResult.keys.join(', ')})` : '';
|
|
155
|
+
logger.log(chalk.green(`Pushed ${pushResult.pushed} credential secret(s) to dataplane${keyList}.`));
|
|
156
|
+
} else {
|
|
157
|
+
logger.log(chalk.yellow('Secret push skipped'));
|
|
137
158
|
}
|
|
138
159
|
if (pushResult.warning) {
|
|
139
160
|
logger.log(chalk.yellow(`Warning: ${pushResult.warning}`));
|
|
@@ -141,11 +162,10 @@ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey,
|
|
|
141
162
|
}
|
|
142
163
|
|
|
143
164
|
/**
|
|
144
|
-
* Uploads external system to dataplane (
|
|
165
|
+
* Uploads external system to dataplane (single pipeline upload). No controller deploy.
|
|
145
166
|
* @param {string} systemKey - External system key (integration/<system-key>/)
|
|
146
167
|
* @param {Object} [options] - Options
|
|
147
168
|
* @param {boolean} [options.dryRun] - Validate and build payload only; no API calls
|
|
148
|
-
* @param {string} [options.dataplane] - Override dataplane URL
|
|
149
169
|
* @returns {Promise<void>}
|
|
150
170
|
* @throws {Error} If validation or API calls fail
|
|
151
171
|
*/
|
|
@@ -159,6 +179,7 @@ async function uploadExternalSystem(systemKey, options = {}) {
|
|
|
159
179
|
|
|
160
180
|
const manifest = await generateControllerManifest(systemKey, { type: 'external' });
|
|
161
181
|
const payload = buildUploadPayload(manifest);
|
|
182
|
+
await resolvePayloadConfiguration(systemKey, payload);
|
|
162
183
|
|
|
163
184
|
if (options.dryRun) {
|
|
164
185
|
logger.log(chalk.yellow('Dry run: would upload payload (no API calls).'));
|
|
@@ -166,13 +187,23 @@ async function uploadExternalSystem(systemKey, options = {}) {
|
|
|
166
187
|
return;
|
|
167
188
|
}
|
|
168
189
|
|
|
169
|
-
const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey
|
|
190
|
+
const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey);
|
|
170
191
|
requireBearerForDataplanePipeline(authConfig);
|
|
171
192
|
logger.log(chalk.blue(`Dataplane: ${dataplaneUrl}`));
|
|
193
|
+
logDataplanePipelineWarning();
|
|
172
194
|
|
|
173
195
|
await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload);
|
|
174
196
|
await runUploadValidatePublish(dataplaneUrl, authConfig, payload);
|
|
197
|
+
logUploadSuccess(environment, systemKey, dataplaneUrl);
|
|
198
|
+
}
|
|
175
199
|
|
|
200
|
+
/**
|
|
201
|
+
* Logs upload success summary.
|
|
202
|
+
* @param {string} environment - Environment key
|
|
203
|
+
* @param {string} systemKey - System key
|
|
204
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
205
|
+
*/
|
|
206
|
+
function logUploadSuccess(environment, systemKey, dataplaneUrl) {
|
|
176
207
|
logger.log(chalk.green('\nUpload validated and published to dataplane.'));
|
|
177
208
|
logger.log(chalk.blue(`Environment: ${environment}`));
|
|
178
209
|
logger.log(chalk.blue(`System: ${systemKey}`));
|
|
@@ -6,8 +6,14 @@
|
|
|
6
6
|
|
|
7
7
|
const chalk = require('chalk');
|
|
8
8
|
const ora = require('ora');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const fs = require('fs').promises;
|
|
11
|
+
const yaml = require('js-yaml');
|
|
9
12
|
const logger = require('../utils/logger');
|
|
13
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
10
14
|
const { parseOpenApi, testMcpConnection, credentialSelection } = require('../api/wizard.api');
|
|
15
|
+
const { listCredentials } = require('../api/credentials.api');
|
|
16
|
+
const { listExternalSystems, getExternalSystem } = require('../api/external-systems.api');
|
|
11
17
|
|
|
12
18
|
/**
|
|
13
19
|
* Parse OpenAPI file or URL
|
|
@@ -158,6 +164,7 @@ function buildConfigPreferences(configPrefs) {
|
|
|
158
164
|
intent: configPrefs?.intent || 'general integration',
|
|
159
165
|
fieldOnboardingLevel: configPrefs?.fieldOnboardingLevel || 'full',
|
|
160
166
|
enableOpenAPIGeneration: configPrefs?.enableOpenAPIGeneration !== false,
|
|
167
|
+
debug: configPrefs?.debug === true,
|
|
161
168
|
userPreferences: {
|
|
162
169
|
enableMCP: configPrefs?.enableMCP || false,
|
|
163
170
|
enableABAC: configPrefs?.enableABAC || false,
|
|
@@ -175,9 +182,10 @@ function buildConfigPreferences(configPrefs) {
|
|
|
175
182
|
* @param {Object} params.prefs - Configuration preferences
|
|
176
183
|
* @param {string} [params.credentialIdOrKey] - Credential ID or key
|
|
177
184
|
* @param {string} [params.systemIdOrKey] - System ID or key
|
|
185
|
+
* @param {string} [params.entityName] - Entity name for multi-entity OpenAPI (from discover-entities)
|
|
178
186
|
* @returns {Object} Configuration payload
|
|
179
187
|
*/
|
|
180
|
-
function buildConfigPayload({ openapiSpec, detectedType, mode, prefs, credentialIdOrKey, systemIdOrKey }) {
|
|
188
|
+
function buildConfigPayload({ openapiSpec, detectedType, mode, prefs, credentialIdOrKey, systemIdOrKey, entityName }) {
|
|
181
189
|
const detectedTypeValue = detectedType?.recommendedType || detectedType?.apiType || detectedType?.selectedType || 'record-based';
|
|
182
190
|
const payload = {
|
|
183
191
|
openapiSpec,
|
|
@@ -190,6 +198,32 @@ function buildConfigPayload({ openapiSpec, detectedType, mode, prefs, credential
|
|
|
190
198
|
};
|
|
191
199
|
if (credentialIdOrKey) payload.credentialIdOrKey = credentialIdOrKey;
|
|
192
200
|
if (systemIdOrKey) payload.systemIdOrKey = systemIdOrKey;
|
|
201
|
+
if (entityName) payload.entityName = entityName;
|
|
202
|
+
if (prefs.debug) payload.debug = true;
|
|
203
|
+
return payload;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Build payload for platform config endpoint.
|
|
208
|
+
* Schema allows only: datasourceKeys?, credentialIdOrKey?, configurationValues?, debug?
|
|
209
|
+
* (additionalProperties: false - do NOT send intent, mode, fieldOnboardingLevel, etc.)
|
|
210
|
+
* @param {Object} params - Parameters object
|
|
211
|
+
* @param {string} [params.credentialIdOrKey] - Credential ID or key
|
|
212
|
+
* @param {string[]} [params.datasourceKeys] - Datasource keys to include (defaults to all)
|
|
213
|
+
* @param {Object} [params.configurationValues] - Configuration value overrides
|
|
214
|
+
* @param {boolean} [params.debug] - When true, capture debug log
|
|
215
|
+
* @returns {Object} Platform config payload
|
|
216
|
+
*/
|
|
217
|
+
function buildPlatformConfigPayload({ credentialIdOrKey, datasourceKeys, configurationValues, debug }) {
|
|
218
|
+
const payload = {};
|
|
219
|
+
if (credentialIdOrKey) payload.credentialIdOrKey = credentialIdOrKey;
|
|
220
|
+
if (datasourceKeys && Array.isArray(datasourceKeys) && datasourceKeys.length > 0) {
|
|
221
|
+
payload.datasourceKeys = datasourceKeys;
|
|
222
|
+
}
|
|
223
|
+
if (configurationValues && typeof configurationValues === 'object' && Object.keys(configurationValues).length > 0) {
|
|
224
|
+
payload.configurationValues = configurationValues;
|
|
225
|
+
}
|
|
226
|
+
if (debug) payload.debug = true;
|
|
193
227
|
return payload;
|
|
194
228
|
}
|
|
195
229
|
|
|
@@ -250,14 +284,19 @@ function formatValidationDetailsPlain(errorData) {
|
|
|
250
284
|
/**
|
|
251
285
|
* Create and throw config generation error with optional formatted message
|
|
252
286
|
* @param {Object} generateResponse - API response (error)
|
|
287
|
+
* @param {Object} [options] - Optional options
|
|
288
|
+
* @param {string} [options.debugManifestHint] - Hint to append when debug manifest was saved (e.g. review debug.log and fix manually)
|
|
253
289
|
* @throws {Error}
|
|
254
290
|
*/
|
|
255
|
-
function throwConfigGenerationError(generateResponse) {
|
|
291
|
+
function throwConfigGenerationError(generateResponse, options = {}) {
|
|
256
292
|
const summary = generateResponse.error || generateResponse.formattedError || 'Unknown error';
|
|
257
293
|
const detailsPlain = formatValidationDetailsPlain(generateResponse.errorData);
|
|
258
|
-
|
|
294
|
+
let message = detailsPlain
|
|
259
295
|
? `Configuration generation failed: ${summary}\n${detailsPlain}`
|
|
260
296
|
: `Configuration generation failed: ${summary}`;
|
|
297
|
+
if (options.debugManifestHint) {
|
|
298
|
+
message += `\n\n${options.debugManifestHint}`;
|
|
299
|
+
}
|
|
261
300
|
const err = new Error(message);
|
|
262
301
|
if (generateResponse.formattedError) {
|
|
263
302
|
err.formatted = generateResponse.formattedError;
|
|
@@ -265,6 +304,181 @@ function throwConfigGenerationError(generateResponse) {
|
|
|
265
304
|
throw err;
|
|
266
305
|
}
|
|
267
306
|
|
|
307
|
+
/**
|
|
308
|
+
* Write debug log to integration/<appName>/debug.log
|
|
309
|
+
* @async
|
|
310
|
+
* @param {string} appName - Application name
|
|
311
|
+
* @param {string} content - Debug log content
|
|
312
|
+
*/
|
|
313
|
+
async function writeDebugLog(appName, content) {
|
|
314
|
+
try {
|
|
315
|
+
const dir = getIntegrationPath(appName);
|
|
316
|
+
await fs.mkdir(dir, { recursive: true });
|
|
317
|
+
const debugPath = path.join(dir, 'debug.log');
|
|
318
|
+
await fs.writeFile(debugPath, content, 'utf8');
|
|
319
|
+
logger.log(chalk.gray(` Debug log saved to integration/${appName}/debug.log`));
|
|
320
|
+
} catch (e) {
|
|
321
|
+
logger.warn(`Could not save debug.log: ${e.message}`);
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Write systemConfig and datasourceConfig from error response for manual fix
|
|
327
|
+
* @async
|
|
328
|
+
* @param {string} appName - Application name
|
|
329
|
+
* @param {Object} [systemConfig] - System config from errorData
|
|
330
|
+
* @param {Object|Object[]} [datasourceConfig] - Datasource config(s) from errorData
|
|
331
|
+
* @returns {Promise<string[]>} Names of saved files
|
|
332
|
+
*/
|
|
333
|
+
async function writeDebugManifest(appName, systemConfig, datasourceConfig) {
|
|
334
|
+
const saved = [];
|
|
335
|
+
try {
|
|
336
|
+
const dir = getIntegrationPath(appName);
|
|
337
|
+
await fs.mkdir(dir, { recursive: true });
|
|
338
|
+
if (systemConfig && typeof systemConfig === 'object') {
|
|
339
|
+
const systemPath = path.join(dir, 'debug-system.yaml');
|
|
340
|
+
await fs.writeFile(systemPath, yaml.dump(systemConfig, { lineWidth: -1 }), 'utf8');
|
|
341
|
+
saved.push('debug-system.yaml');
|
|
342
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-system.yaml`));
|
|
343
|
+
}
|
|
344
|
+
if (datasourceConfig !== undefined && datasourceConfig !== null) {
|
|
345
|
+
const configs = Array.isArray(datasourceConfig) ? datasourceConfig : [datasourceConfig];
|
|
346
|
+
if (configs.length > 0 && configs.every(c => c && typeof c === 'object')) {
|
|
347
|
+
const datasourcePath = path.join(dir, 'debug-datasource.yaml');
|
|
348
|
+
const toWrite = configs.length === 1 ? configs[0] : configs;
|
|
349
|
+
await fs.writeFile(datasourcePath, yaml.dump(toWrite, { lineWidth: -1 }), 'utf8');
|
|
350
|
+
saved.push('debug-datasource.yaml');
|
|
351
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-datasource.yaml`));
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
} catch (e) {
|
|
355
|
+
logger.warn(`Could not save debug manifest: ${e.message}`);
|
|
356
|
+
}
|
|
357
|
+
return saved;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Save debug manifest on error and throw
|
|
362
|
+
* @param {Object} generateResponse - API error response
|
|
363
|
+
* @param {Object} opts - Options
|
|
364
|
+
* @param {boolean} opts.enableDebug - Whether debug was enabled
|
|
365
|
+
* @param {string} [opts.appName] - App name for writing files
|
|
366
|
+
*/
|
|
367
|
+
async function saveDebugManifestOnErrorAndThrow(generateResponse, opts) {
|
|
368
|
+
const { enableDebug, appName } = opts;
|
|
369
|
+
let debugManifestHint = null;
|
|
370
|
+
if (enableDebug && appName) {
|
|
371
|
+
const errorData = generateResponse.errorData || {};
|
|
372
|
+
const debugLog = errorData.debugLog;
|
|
373
|
+
if (debugLog && typeof debugLog === 'string') {
|
|
374
|
+
await writeDebugLog(appName, debugLog);
|
|
375
|
+
}
|
|
376
|
+
const systemConfig = errorData.systemConfig;
|
|
377
|
+
const datasourceConfig = errorData.datasourceConfig || errorData.datasourceConfigs;
|
|
378
|
+
const savedManifest = await writeDebugManifest(appName, systemConfig, datasourceConfig);
|
|
379
|
+
if (debugLog || savedManifest.length > 0) {
|
|
380
|
+
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
381
|
+
debugManifestHint = `Debug manifest saved to integration/${appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${appName}`;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
throwConfigGenerationError(generateResponse, { debugManifestHint });
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/** Throws with validation error; saves debug manifest when options.debug and options.appName are set. */
|
|
388
|
+
async function throwValidationFailureWithDebug(validateResponse, systemConfig, configs, errorMsg, options) {
|
|
389
|
+
if (!options.debug || !options.appName) throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
390
|
+
const errorData = validateResponse.errorData || validateResponse.data || {};
|
|
391
|
+
const debugLog = errorData.debugLog;
|
|
392
|
+
if (debugLog && typeof debugLog === 'string') await writeDebugLog(options.appName, debugLog);
|
|
393
|
+
const savedManifest = await writeDebugManifest(
|
|
394
|
+
options.appName, errorData.systemConfig || systemConfig, errorData.datasourceConfig || errorData.datasourceConfigs || configs
|
|
395
|
+
);
|
|
396
|
+
if (!debugLog && savedManifest.length === 0) throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
397
|
+
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
398
|
+
throw new Error(`Configuration validation failed: ${errorMsg}\n\nDebug manifest saved to integration/${options.appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${options.appName}`);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Resolve credential config from user action (select/skip/create)
|
|
403
|
+
* @async
|
|
404
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
405
|
+
* @param {Object} authConfig - Auth config
|
|
406
|
+
* @param {Object} credentialAction - Result from promptForCredentialAction
|
|
407
|
+
* @returns {Promise<Object>} configCredential for handleCredentialSelection
|
|
408
|
+
*/
|
|
409
|
+
async function resolveCredentialConfig(dataplaneUrl, authConfig, credentialAction) {
|
|
410
|
+
const { promptForExistingCredential } = require('../generator/wizard-prompts');
|
|
411
|
+
if (credentialAction.action !== 'select') {
|
|
412
|
+
return credentialAction.action === 'skip' ? { action: 'skip' } : { action: credentialAction.action };
|
|
413
|
+
}
|
|
414
|
+
let credentialsList = [];
|
|
415
|
+
try {
|
|
416
|
+
const listResponse = await listCredentials(dataplaneUrl, authConfig, {
|
|
417
|
+
activeOnly: true,
|
|
418
|
+
pageSize: 100
|
|
419
|
+
});
|
|
420
|
+
const body = listResponse?.data ?? listResponse;
|
|
421
|
+
const inner = Array.isArray(body) ? body : (body?.data ?? body);
|
|
422
|
+
credentialsList = Array.isArray(inner) ? inner : (inner?.credentials ?? inner?.items ?? []) || [];
|
|
423
|
+
} catch (_) {
|
|
424
|
+
credentialsList = [];
|
|
425
|
+
}
|
|
426
|
+
const selected = await promptForExistingCredential(Array.isArray(credentialsList) ? credentialsList : []);
|
|
427
|
+
return { action: 'select', credentialIdOrKey: selected.credentialIdOrKey };
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Fetch external systems list from dataplane
|
|
432
|
+
* @async
|
|
433
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
434
|
+
* @param {Object} authConfig - Auth config
|
|
435
|
+
* @returns {Promise<Object[]>} Systems list
|
|
436
|
+
*/
|
|
437
|
+
async function fetchSystemsListForAddDatasource(dataplaneUrl, authConfig) {
|
|
438
|
+
try {
|
|
439
|
+
const listResponse = await listExternalSystems(dataplaneUrl, authConfig, { pageSize: 100 });
|
|
440
|
+
const body = listResponse?.data ?? listResponse;
|
|
441
|
+
const inner = Array.isArray(body) ? body : (body?.data ?? body);
|
|
442
|
+
const list = Array.isArray(inner) ? inner : (inner?.items ?? inner?.systems ?? []) || [];
|
|
443
|
+
return Array.isArray(list) ? list : [];
|
|
444
|
+
} catch (_) {
|
|
445
|
+
return [];
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/**
|
|
450
|
+
* Resolve and validate external system for add-datasource (prompt until valid)
|
|
451
|
+
* @async
|
|
452
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
453
|
+
* @param {Object} authConfig - Auth config
|
|
454
|
+
* @param {Object[]} systemsList - Systems list
|
|
455
|
+
* @param {string} initialSystemIdOrKey - Initial system ID or key
|
|
456
|
+
* @returns {Promise<{systemResponse: Object, systemIdOrKey: string}>}
|
|
457
|
+
*/
|
|
458
|
+
async function resolveExternalSystemForAddDatasource(dataplaneUrl, authConfig, systemsList, initialSystemIdOrKey) {
|
|
459
|
+
const { promptForExistingSystem } = require('../generator/wizard-prompts');
|
|
460
|
+
const { isExternalSystemForAddDatasource } = require('./wizard-helpers');
|
|
461
|
+
let systemIdOrKey = initialSystemIdOrKey;
|
|
462
|
+
let systemResponse;
|
|
463
|
+
for (;;) {
|
|
464
|
+
try {
|
|
465
|
+
systemResponse = await getExternalSystem(dataplaneUrl, systemIdOrKey, authConfig);
|
|
466
|
+
const sys = systemResponse?.data || systemResponse;
|
|
467
|
+
if (sys && (systemResponse?.data || systemResponse?.success)) {
|
|
468
|
+
if (!isExternalSystemForAddDatasource(sys)) {
|
|
469
|
+
logger.log(chalk.red('Cannot add datasource to a webapp. Please select an external system.'));
|
|
470
|
+
} else {
|
|
471
|
+
break;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
} catch (err) {
|
|
475
|
+
logger.log(chalk.red(`System not found or error: ${err.message}`));
|
|
476
|
+
}
|
|
477
|
+
systemIdOrKey = await promptForExistingSystem(systemsList, systemIdOrKey);
|
|
478
|
+
}
|
|
479
|
+
return { systemResponse, systemIdOrKey };
|
|
480
|
+
}
|
|
481
|
+
|
|
268
482
|
module.exports = {
|
|
269
483
|
parseOpenApiSource,
|
|
270
484
|
testMcpServerConnection,
|
|
@@ -272,7 +486,15 @@ module.exports = {
|
|
|
272
486
|
runCredentialSelectionLoop,
|
|
273
487
|
buildConfigPreferences,
|
|
274
488
|
buildConfigPayload,
|
|
489
|
+
buildPlatformConfigPayload,
|
|
275
490
|
extractConfigurationFromResponse,
|
|
276
491
|
formatValidationDetailsPlain,
|
|
277
|
-
throwConfigGenerationError
|
|
492
|
+
throwConfigGenerationError,
|
|
493
|
+
writeDebugLog,
|
|
494
|
+
writeDebugManifest,
|
|
495
|
+
saveDebugManifestOnErrorAndThrow,
|
|
496
|
+
throwValidationFailureWithDebug,
|
|
497
|
+
resolveCredentialConfig,
|
|
498
|
+
fetchSystemsListForAddDatasource,
|
|
499
|
+
resolveExternalSystemForAddDatasource
|
|
278
500
|
};
|