@aifabrix/builder 2.41.0 → 2.42.1
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 +2 -2
- package/integration/hubspot/README.md +11 -5
- 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 +36 -2
- package/lib/app/config.js +23 -11
- package/lib/app/index.js +5 -3
- package/lib/app/prompts.js +46 -31
- package/lib/app/readme.js +11 -4
- 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 +45 -14
- 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/auth-config.js +22 -12
- 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 +518 -0
- package/lib/commands/secrets-set.js +6 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/up-dataplane.js +90 -6
- package/lib/commands/upload.js +71 -40
- package/lib/commands/wizard-core-helpers.js +230 -5
- package/lib/commands/wizard-core.js +68 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +49 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +93 -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-helpers.js +3 -1
- 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 +4 -2
- 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 +326 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +91 -0
- package/lib/generator/wizard.js +180 -179
- 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 +23 -1
- 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 +89 -30
- 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 +75 -22
- 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
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* @version 2.0.0
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
const readline = require('readline');
|
|
14
15
|
const chalk = require('chalk');
|
|
15
16
|
const pathsUtil = require('../utils/paths');
|
|
16
17
|
const { loadConfigFile } = require('../utils/config-format');
|
|
@@ -18,13 +19,87 @@ const logger = require('../utils/logger');
|
|
|
18
19
|
const config = require('../core/config');
|
|
19
20
|
const { checkAuthentication } = require('../utils/app-register-auth');
|
|
20
21
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
21
|
-
const { resolveEnvironment } = require('../core/config');
|
|
22
|
+
const { resolveEnvironment, setControllerUrl } = require('../core/config');
|
|
22
23
|
const { registerApplication } = require('../app/register');
|
|
23
24
|
const { rotateSecret } = require('../app/rotate-secret');
|
|
24
25
|
const { checkApplicationExists } = require('../utils/app-existence');
|
|
26
|
+
const { checkHealthEndpoint } = require('../utils/health-check');
|
|
27
|
+
const { validateControllerUrl } = require('../utils/auth-config-validator');
|
|
25
28
|
const app = require('../app');
|
|
26
29
|
const { ensureAppFromTemplate, validateEnvOutputPathFolderOrNull } = require('./up-common');
|
|
27
30
|
|
|
31
|
+
const CONTROLLER_HEALTH_PATH = '/health';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Check if controller is reachable (health endpoint).
|
|
35
|
+
* @param {string} baseUrl - Controller base URL (no trailing slash)
|
|
36
|
+
* @returns {Promise<boolean>} True if healthy
|
|
37
|
+
*/
|
|
38
|
+
async function isControllerHealthy(baseUrl) {
|
|
39
|
+
const healthUrl = `${baseUrl.replace(/\/+$/, '')}${CONTROLLER_HEALTH_PATH}`;
|
|
40
|
+
try {
|
|
41
|
+
return await checkHealthEndpoint(healthUrl, false);
|
|
42
|
+
} catch {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Prompt user for controller URL when current controller is not available.
|
|
49
|
+
* @param {string} currentUrl - Current controller URL that failed health check
|
|
50
|
+
* @returns {Promise<string|null>} New URL or null if user aborted (empty input)
|
|
51
|
+
*/
|
|
52
|
+
function promptForControllerUrl(currentUrl) {
|
|
53
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
54
|
+
return new Promise((resolve) => {
|
|
55
|
+
rl.question(
|
|
56
|
+
chalk.yellow(`Controller at ${currentUrl} is not available. Enter controller URL (or press Enter to abort): `),
|
|
57
|
+
(answer) => {
|
|
58
|
+
rl.close();
|
|
59
|
+
const trimmed = (answer || '').trim();
|
|
60
|
+
resolve(trimmed === '' ? null : trimmed);
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Resolve controller URL and ensure it is healthy; if not, prompt once for new URL.
|
|
68
|
+
* @returns {Promise<string>} Controller URL to use
|
|
69
|
+
* @throws {Error} If controller unavailable and user aborts or new URL still unhealthy
|
|
70
|
+
*/
|
|
71
|
+
async function resolveControllerUrlWithHealthCheck() {
|
|
72
|
+
let controllerUrl = await resolveControllerUrl();
|
|
73
|
+
controllerUrl = controllerUrl.replace(/\/+$/, '');
|
|
74
|
+
|
|
75
|
+
let healthy = await isControllerHealthy(controllerUrl);
|
|
76
|
+
if (healthy) {
|
|
77
|
+
return controllerUrl;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
logger.log(chalk.yellow(`\nController at ${controllerUrl} is not responding (health check failed).\n`));
|
|
81
|
+
const newUrl = await promptForControllerUrl(controllerUrl);
|
|
82
|
+
if (!newUrl) {
|
|
83
|
+
throw new Error('Controller URL is required. Run "aifabrix up-dataplane" again and enter a valid controller URL, or set it with: aifabrix auth config --set-controller <url>');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
validateControllerUrl(newUrl);
|
|
88
|
+
} catch (err) {
|
|
89
|
+
throw new Error(`Invalid controller URL: ${err.message}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
await setControllerUrl(newUrl);
|
|
93
|
+
const normalizedNew = newUrl.trim().replace(/\/+$/, '');
|
|
94
|
+
healthy = await isControllerHealthy(normalizedNew);
|
|
95
|
+
if (!healthy) {
|
|
96
|
+
throw new Error(`Controller at ${normalizedNew} is not responding. Ensure the controller is running and reachable, then run "aifabrix up-dataplane" again.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
logger.log(chalk.green(`✓ Using controller: ${normalizedNew}`));
|
|
100
|
+
return normalizedNew;
|
|
101
|
+
}
|
|
102
|
+
|
|
28
103
|
/**
|
|
29
104
|
* Register or rotate dataplane: if app exists in controller, rotate secret; otherwise register.
|
|
30
105
|
* @async
|
|
@@ -45,6 +120,17 @@ async function registerOrRotateDataplane(options, controllerUrl, environmentKey,
|
|
|
45
120
|
}
|
|
46
121
|
}
|
|
47
122
|
|
|
123
|
+
/**
|
|
124
|
+
* Deploy dataplane app (send manifest to controller).
|
|
125
|
+
* @param {Object} options - Commander options (registry, registryMode, image)
|
|
126
|
+
* @returns {Promise<void>}
|
|
127
|
+
*/
|
|
128
|
+
async function deployDataplaneToController(options) {
|
|
129
|
+
const imageOverride = options.image || (options.registry ? buildDataplaneImageRef(options.registry) : undefined);
|
|
130
|
+
const deployOpts = { imageOverride, image: imageOverride, registryMode: options.registryMode };
|
|
131
|
+
await app.deployApp('dataplane', deployOpts);
|
|
132
|
+
}
|
|
133
|
+
|
|
48
134
|
/**
|
|
49
135
|
* Build full image ref from registry and dataplane config (registry/name:tag)
|
|
50
136
|
* @param {string} registry - Registry URL
|
|
@@ -85,7 +171,8 @@ async function handleUpDataplane(options = {}) {
|
|
|
85
171
|
}
|
|
86
172
|
logger.log(chalk.blue('Starting up-dataplane (register/rotate, deploy, then run dataplane locally)...\n'));
|
|
87
173
|
|
|
88
|
-
const
|
|
174
|
+
const controllerUrl = await resolveControllerUrlWithHealthCheck();
|
|
175
|
+
const environmentKey = await resolveEnvironment();
|
|
89
176
|
const authConfig = await checkAuthentication(controllerUrl, environmentKey, { throwOnFailure: true });
|
|
90
177
|
|
|
91
178
|
const cfg = await config.getConfig();
|
|
@@ -103,10 +190,7 @@ async function handleUpDataplane(options = {}) {
|
|
|
103
190
|
|
|
104
191
|
await registerOrRotateDataplane(options, controllerUrl, environmentKey, authConfig);
|
|
105
192
|
|
|
106
|
-
|
|
107
|
-
const deployOpts = { imageOverride, image: imageOverride, registryMode: options.registryMode };
|
|
108
|
-
|
|
109
|
-
await app.deployApp('dataplane', deployOpts);
|
|
193
|
+
await deployDataplaneToController(options);
|
|
110
194
|
logger.log('');
|
|
111
195
|
await app.runApp('dataplane', { skipEnvOutputPath: true });
|
|
112
196
|
|
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}`));
|
|
@@ -3,11 +3,18 @@
|
|
|
3
3
|
* @author AI Fabrix Team
|
|
4
4
|
* @version 2.0.0
|
|
5
5
|
*/
|
|
6
|
+
/* eslint-disable max-lines -- 502 lines; wizard payload and helpers */
|
|
6
7
|
|
|
7
8
|
const chalk = require('chalk');
|
|
8
9
|
const ora = require('ora');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const fs = require('fs').promises;
|
|
12
|
+
const yaml = require('js-yaml');
|
|
9
13
|
const logger = require('../utils/logger');
|
|
14
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
10
15
|
const { parseOpenApi, testMcpConnection, credentialSelection } = require('../api/wizard.api');
|
|
16
|
+
const { listCredentials } = require('../api/credentials.api');
|
|
17
|
+
const { listExternalSystems, getExternalSystem } = require('../api/external-systems.api');
|
|
11
18
|
|
|
12
19
|
/**
|
|
13
20
|
* Parse OpenAPI file or URL
|
|
@@ -158,6 +165,7 @@ function buildConfigPreferences(configPrefs) {
|
|
|
158
165
|
intent: configPrefs?.intent || 'general integration',
|
|
159
166
|
fieldOnboardingLevel: configPrefs?.fieldOnboardingLevel || 'full',
|
|
160
167
|
enableOpenAPIGeneration: configPrefs?.enableOpenAPIGeneration !== false,
|
|
168
|
+
debug: configPrefs?.debug === true,
|
|
161
169
|
userPreferences: {
|
|
162
170
|
enableMCP: configPrefs?.enableMCP || false,
|
|
163
171
|
enableABAC: configPrefs?.enableABAC || false,
|
|
@@ -174,10 +182,12 @@ function buildConfigPreferences(configPrefs) {
|
|
|
174
182
|
* @param {string} params.mode - Selected mode
|
|
175
183
|
* @param {Object} params.prefs - Configuration preferences
|
|
176
184
|
* @param {string} [params.credentialIdOrKey] - Credential ID or key
|
|
177
|
-
* @param {string} [params.systemIdOrKey] - System ID or key
|
|
185
|
+
* @param {string} [params.systemIdOrKey] - System ID or key (application/system key, not datasource/entity key)
|
|
186
|
+
* @param {string} [params.entityName] - Entity name for multi-entity OpenAPI (from discover-entities)
|
|
187
|
+
* @param {string|null} [params.systemDisplayName] - System-level display name for credential (e.g. 'Hubspot Demo')
|
|
178
188
|
* @returns {Object} Configuration payload
|
|
179
189
|
*/
|
|
180
|
-
function buildConfigPayload({ openapiSpec, detectedType, mode, prefs, credentialIdOrKey, systemIdOrKey }) {
|
|
190
|
+
function buildConfigPayload({ openapiSpec, detectedType, mode, prefs, credentialIdOrKey, systemIdOrKey, entityName, systemDisplayName }) {
|
|
181
191
|
const detectedTypeValue = detectedType?.recommendedType || detectedType?.apiType || detectedType?.selectedType || 'record-based';
|
|
182
192
|
const payload = {
|
|
183
193
|
openapiSpec,
|
|
@@ -190,6 +200,33 @@ function buildConfigPayload({ openapiSpec, detectedType, mode, prefs, credential
|
|
|
190
200
|
};
|
|
191
201
|
if (credentialIdOrKey) payload.credentialIdOrKey = credentialIdOrKey;
|
|
192
202
|
if (systemIdOrKey) payload.systemIdOrKey = systemIdOrKey;
|
|
203
|
+
if (entityName) payload.entityName = entityName;
|
|
204
|
+
if (systemDisplayName) payload.systemDisplayName = systemDisplayName;
|
|
205
|
+
if (prefs.debug) payload.debug = true;
|
|
206
|
+
return payload;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Build payload for platform config endpoint.
|
|
211
|
+
* Schema allows only: datasourceKeys?, credentialIdOrKey?, configurationValues?, debug?
|
|
212
|
+
* (additionalProperties: false - do NOT send intent, mode, fieldOnboardingLevel, etc.)
|
|
213
|
+
* @param {Object} params - Parameters object
|
|
214
|
+
* @param {string} [params.credentialIdOrKey] - Credential ID or key
|
|
215
|
+
* @param {string[]} [params.datasourceKeys] - Datasource keys to include (defaults to all)
|
|
216
|
+
* @param {Object} [params.configurationValues] - Configuration value overrides
|
|
217
|
+
* @param {boolean} [params.debug] - When true, capture debug log
|
|
218
|
+
* @returns {Object} Platform config payload
|
|
219
|
+
*/
|
|
220
|
+
function buildPlatformConfigPayload({ credentialIdOrKey, datasourceKeys, configurationValues, debug }) {
|
|
221
|
+
const payload = {};
|
|
222
|
+
if (credentialIdOrKey) payload.credentialIdOrKey = credentialIdOrKey;
|
|
223
|
+
if (datasourceKeys && Array.isArray(datasourceKeys) && datasourceKeys.length > 0) {
|
|
224
|
+
payload.datasourceKeys = datasourceKeys;
|
|
225
|
+
}
|
|
226
|
+
if (configurationValues && typeof configurationValues === 'object' && Object.keys(configurationValues).length > 0) {
|
|
227
|
+
payload.configurationValues = configurationValues;
|
|
228
|
+
}
|
|
229
|
+
if (debug) payload.debug = true;
|
|
193
230
|
return payload;
|
|
194
231
|
}
|
|
195
232
|
|
|
@@ -250,14 +287,19 @@ function formatValidationDetailsPlain(errorData) {
|
|
|
250
287
|
/**
|
|
251
288
|
* Create and throw config generation error with optional formatted message
|
|
252
289
|
* @param {Object} generateResponse - API response (error)
|
|
290
|
+
* @param {Object} [options] - Optional options
|
|
291
|
+
* @param {string} [options.debugManifestHint] - Hint to append when debug manifest was saved (e.g. review debug.log and fix manually)
|
|
253
292
|
* @throws {Error}
|
|
254
293
|
*/
|
|
255
|
-
function throwConfigGenerationError(generateResponse) {
|
|
294
|
+
function throwConfigGenerationError(generateResponse, options = {}) {
|
|
256
295
|
const summary = generateResponse.error || generateResponse.formattedError || 'Unknown error';
|
|
257
296
|
const detailsPlain = formatValidationDetailsPlain(generateResponse.errorData);
|
|
258
|
-
|
|
297
|
+
let message = detailsPlain
|
|
259
298
|
? `Configuration generation failed: ${summary}\n${detailsPlain}`
|
|
260
299
|
: `Configuration generation failed: ${summary}`;
|
|
300
|
+
if (options.debugManifestHint) {
|
|
301
|
+
message += `\n\n${options.debugManifestHint}`;
|
|
302
|
+
}
|
|
261
303
|
const err = new Error(message);
|
|
262
304
|
if (generateResponse.formattedError) {
|
|
263
305
|
err.formatted = generateResponse.formattedError;
|
|
@@ -265,6 +307,181 @@ function throwConfigGenerationError(generateResponse) {
|
|
|
265
307
|
throw err;
|
|
266
308
|
}
|
|
267
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Write debug log to integration/<appName>/debug.log
|
|
312
|
+
* @async
|
|
313
|
+
* @param {string} appName - Application name
|
|
314
|
+
* @param {string} content - Debug log content
|
|
315
|
+
*/
|
|
316
|
+
async function writeDebugLog(appName, content) {
|
|
317
|
+
try {
|
|
318
|
+
const dir = getIntegrationPath(appName);
|
|
319
|
+
await fs.mkdir(dir, { recursive: true });
|
|
320
|
+
const debugPath = path.join(dir, 'debug.log');
|
|
321
|
+
await fs.writeFile(debugPath, content, 'utf8');
|
|
322
|
+
logger.log(chalk.gray(` Debug log saved to integration/${appName}/debug.log`));
|
|
323
|
+
} catch (e) {
|
|
324
|
+
logger.warn(`Could not save debug.log: ${e.message}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Write systemConfig and datasourceConfig from error response for manual fix
|
|
330
|
+
* @async
|
|
331
|
+
* @param {string} appName - Application name
|
|
332
|
+
* @param {Object} [systemConfig] - System config from errorData
|
|
333
|
+
* @param {Object|Object[]} [datasourceConfig] - Datasource config(s) from errorData
|
|
334
|
+
* @returns {Promise<string[]>} Names of saved files
|
|
335
|
+
*/
|
|
336
|
+
async function writeDebugManifest(appName, systemConfig, datasourceConfig) {
|
|
337
|
+
const saved = [];
|
|
338
|
+
try {
|
|
339
|
+
const dir = getIntegrationPath(appName);
|
|
340
|
+
await fs.mkdir(dir, { recursive: true });
|
|
341
|
+
if (systemConfig && typeof systemConfig === 'object') {
|
|
342
|
+
const systemPath = path.join(dir, 'debug-system.yaml');
|
|
343
|
+
await fs.writeFile(systemPath, yaml.dump(systemConfig, { lineWidth: -1 }), 'utf8');
|
|
344
|
+
saved.push('debug-system.yaml');
|
|
345
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-system.yaml`));
|
|
346
|
+
}
|
|
347
|
+
if (datasourceConfig !== undefined && datasourceConfig !== null) {
|
|
348
|
+
const configs = Array.isArray(datasourceConfig) ? datasourceConfig : [datasourceConfig];
|
|
349
|
+
if (configs.length > 0 && configs.every(c => c && typeof c === 'object')) {
|
|
350
|
+
const datasourcePath = path.join(dir, 'debug-datasource.yaml');
|
|
351
|
+
const toWrite = configs.length === 1 ? configs[0] : configs;
|
|
352
|
+
await fs.writeFile(datasourcePath, yaml.dump(toWrite, { lineWidth: -1 }), 'utf8');
|
|
353
|
+
saved.push('debug-datasource.yaml');
|
|
354
|
+
logger.log(chalk.gray(` Debug manifest saved to integration/${appName}/debug-datasource.yaml`));
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
} catch (e) {
|
|
358
|
+
logger.warn(`Could not save debug manifest: ${e.message}`);
|
|
359
|
+
}
|
|
360
|
+
return saved;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Save debug manifest on error and throw
|
|
365
|
+
* @param {Object} generateResponse - API error response
|
|
366
|
+
* @param {Object} opts - Options
|
|
367
|
+
* @param {boolean} opts.enableDebug - Whether debug was enabled
|
|
368
|
+
* @param {string} [opts.appName] - App name for writing files
|
|
369
|
+
*/
|
|
370
|
+
async function saveDebugManifestOnErrorAndThrow(generateResponse, opts) {
|
|
371
|
+
const { enableDebug, appName } = opts;
|
|
372
|
+
let debugManifestHint = null;
|
|
373
|
+
if (enableDebug && appName) {
|
|
374
|
+
const errorData = generateResponse.errorData || {};
|
|
375
|
+
const debugLog = errorData.debugLog;
|
|
376
|
+
if (debugLog && typeof debugLog === 'string') {
|
|
377
|
+
await writeDebugLog(appName, debugLog);
|
|
378
|
+
}
|
|
379
|
+
const systemConfig = errorData.systemConfig;
|
|
380
|
+
const datasourceConfig = errorData.datasourceConfig || errorData.datasourceConfigs;
|
|
381
|
+
const savedManifest = await writeDebugManifest(appName, systemConfig, datasourceConfig);
|
|
382
|
+
if (debugLog || savedManifest.length > 0) {
|
|
383
|
+
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
384
|
+
debugManifestHint = `Debug manifest saved to integration/${appName}/ (${files}). Review the log and fix the manifest manually, then run: aifabrix wizard ${appName}`;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
throwConfigGenerationError(generateResponse, { debugManifestHint });
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** Throws with validation error; saves debug manifest when options.debug and options.appName are set. */
|
|
391
|
+
async function throwValidationFailureWithDebug(validateResponse, systemConfig, configs, errorMsg, options) {
|
|
392
|
+
if (!options.debug || !options.appName) throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
393
|
+
const errorData = validateResponse.errorData || validateResponse.data || {};
|
|
394
|
+
const debugLog = errorData.debugLog;
|
|
395
|
+
if (debugLog && typeof debugLog === 'string') await writeDebugLog(options.appName, debugLog);
|
|
396
|
+
const savedManifest = await writeDebugManifest(
|
|
397
|
+
options.appName, errorData.systemConfig || systemConfig, errorData.datasourceConfig || errorData.datasourceConfigs || configs
|
|
398
|
+
);
|
|
399
|
+
if (!debugLog && savedManifest.length === 0) throw new Error(`Configuration validation failed: ${errorMsg}`);
|
|
400
|
+
const files = [debugLog && 'debug.log', ...savedManifest].filter(Boolean).join(', ');
|
|
401
|
+
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}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
/**
|
|
405
|
+
* Resolve credential config from user action (select/skip/create)
|
|
406
|
+
* @async
|
|
407
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
408
|
+
* @param {Object} authConfig - Auth config
|
|
409
|
+
* @param {Object} credentialAction - Result from promptForCredentialAction
|
|
410
|
+
* @returns {Promise<Object>} configCredential for handleCredentialSelection
|
|
411
|
+
*/
|
|
412
|
+
async function resolveCredentialConfig(dataplaneUrl, authConfig, credentialAction) {
|
|
413
|
+
const { promptForExistingCredential } = require('../generator/wizard-prompts');
|
|
414
|
+
if (credentialAction.action !== 'select') {
|
|
415
|
+
return credentialAction.action === 'skip' ? { action: 'skip' } : { action: credentialAction.action };
|
|
416
|
+
}
|
|
417
|
+
let credentialsList = [];
|
|
418
|
+
try {
|
|
419
|
+
const listResponse = await listCredentials(dataplaneUrl, authConfig, {
|
|
420
|
+
activeOnly: true,
|
|
421
|
+
pageSize: 100
|
|
422
|
+
});
|
|
423
|
+
const body = listResponse?.data ?? listResponse;
|
|
424
|
+
const inner = Array.isArray(body) ? body : (body?.data ?? body);
|
|
425
|
+
credentialsList = Array.isArray(inner) ? inner : (inner?.credentials ?? inner?.items ?? []) || [];
|
|
426
|
+
} catch (_) {
|
|
427
|
+
credentialsList = [];
|
|
428
|
+
}
|
|
429
|
+
const selected = await promptForExistingCredential(Array.isArray(credentialsList) ? credentialsList : []);
|
|
430
|
+
return { action: 'select', credentialIdOrKey: selected.credentialIdOrKey };
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Fetch external systems list from dataplane
|
|
435
|
+
* @async
|
|
436
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
437
|
+
* @param {Object} authConfig - Auth config
|
|
438
|
+
* @returns {Promise<Object[]>} Systems list
|
|
439
|
+
*/
|
|
440
|
+
async function fetchSystemsListForAddDatasource(dataplaneUrl, authConfig) {
|
|
441
|
+
try {
|
|
442
|
+
const listResponse = await listExternalSystems(dataplaneUrl, authConfig, { pageSize: 100 });
|
|
443
|
+
const body = listResponse?.data ?? listResponse;
|
|
444
|
+
const inner = Array.isArray(body) ? body : (body?.data ?? body);
|
|
445
|
+
const list = Array.isArray(inner) ? inner : (inner?.items ?? inner?.systems ?? []) || [];
|
|
446
|
+
return Array.isArray(list) ? list : [];
|
|
447
|
+
} catch (_) {
|
|
448
|
+
return [];
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Resolve and validate external system for add-datasource (prompt until valid)
|
|
454
|
+
* @async
|
|
455
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
456
|
+
* @param {Object} authConfig - Auth config
|
|
457
|
+
* @param {Object[]} systemsList - Systems list
|
|
458
|
+
* @param {string} initialSystemIdOrKey - Initial system ID or key
|
|
459
|
+
* @returns {Promise<{systemResponse: Object, systemIdOrKey: string}>}
|
|
460
|
+
*/
|
|
461
|
+
async function resolveExternalSystemForAddDatasource(dataplaneUrl, authConfig, systemsList, initialSystemIdOrKey) {
|
|
462
|
+
const { promptForExistingSystem } = require('../generator/wizard-prompts');
|
|
463
|
+
const { isExternalSystemForAddDatasource } = require('./wizard-helpers');
|
|
464
|
+
let systemIdOrKey = initialSystemIdOrKey;
|
|
465
|
+
let systemResponse;
|
|
466
|
+
for (;;) {
|
|
467
|
+
try {
|
|
468
|
+
systemResponse = await getExternalSystem(dataplaneUrl, systemIdOrKey, authConfig);
|
|
469
|
+
const sys = systemResponse?.data || systemResponse;
|
|
470
|
+
if (sys && (systemResponse?.data || systemResponse?.success)) {
|
|
471
|
+
if (!isExternalSystemForAddDatasource(sys)) {
|
|
472
|
+
logger.log(chalk.red('Cannot add datasource to a webapp. Please select an external system.'));
|
|
473
|
+
} else {
|
|
474
|
+
break;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} catch (err) {
|
|
478
|
+
logger.log(chalk.red(`System not found or error: ${err.message}`));
|
|
479
|
+
}
|
|
480
|
+
systemIdOrKey = await promptForExistingSystem(systemsList, systemIdOrKey);
|
|
481
|
+
}
|
|
482
|
+
return { systemResponse, systemIdOrKey };
|
|
483
|
+
}
|
|
484
|
+
|
|
268
485
|
module.exports = {
|
|
269
486
|
parseOpenApiSource,
|
|
270
487
|
testMcpServerConnection,
|
|
@@ -272,7 +489,15 @@ module.exports = {
|
|
|
272
489
|
runCredentialSelectionLoop,
|
|
273
490
|
buildConfigPreferences,
|
|
274
491
|
buildConfigPayload,
|
|
492
|
+
buildPlatformConfigPayload,
|
|
275
493
|
extractConfigurationFromResponse,
|
|
276
494
|
formatValidationDetailsPlain,
|
|
277
|
-
throwConfigGenerationError
|
|
495
|
+
throwConfigGenerationError,
|
|
496
|
+
writeDebugLog,
|
|
497
|
+
writeDebugManifest,
|
|
498
|
+
saveDebugManifestOnErrorAndThrow,
|
|
499
|
+
throwValidationFailureWithDebug,
|
|
500
|
+
resolveCredentialConfig,
|
|
501
|
+
fetchSystemsListForAddDatasource,
|
|
502
|
+
resolveExternalSystemForAddDatasource
|
|
278
503
|
};
|