@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
package/lib/commands/upload.js
CHANGED
|
@@ -1,25 +1,29 @@
|
|
|
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
|
|
6
6
|
* @version 2.0.0
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
const path = require('path');
|
|
9
10
|
const chalk = require('chalk');
|
|
10
11
|
const logger = require('../utils/logger');
|
|
11
12
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
12
13
|
const { getDeploymentAuth, requireBearerForDataplanePipeline } = require('../utils/token-manager');
|
|
13
14
|
const { resolveDataplaneUrl } = require('../utils/dataplane-resolver');
|
|
15
|
+
const { getIntegrationPath } = require('../utils/paths');
|
|
16
|
+
const { pushCredentialSecrets } = require('../utils/credential-secrets-env');
|
|
17
|
+
const {
|
|
18
|
+
buildResolvedEnvMapForIntegration,
|
|
19
|
+
resolveConfigurationValues
|
|
20
|
+
} = require('../utils/configuration-env-resolver');
|
|
14
21
|
const { validateExternalSystemComplete } = require('../validation/validate');
|
|
15
22
|
const { displayValidationResults } = require('../validation/validate-display');
|
|
16
23
|
const { generateControllerManifest } = require('../generator/external-controller-manifest');
|
|
17
|
-
const {
|
|
18
|
-
uploadApplicationViaPipeline,
|
|
19
|
-
validateUploadViaPipeline,
|
|
20
|
-
publishUploadViaPipeline
|
|
21
|
-
} = require('../api/pipeline.api');
|
|
24
|
+
const { uploadApplicationViaPipeline } = require('../api/pipeline.api');
|
|
22
25
|
const { formatApiError } = require('../utils/api-error-handler');
|
|
26
|
+
const { logDataplanePipelineWarning } = require('../utils/dataplane-pipeline-warning');
|
|
23
27
|
|
|
24
28
|
/**
|
|
25
29
|
* Validates system-key format (same as download).
|
|
@@ -37,25 +41,25 @@ function validateSystemKeyFormat(systemKey) {
|
|
|
37
41
|
|
|
38
42
|
/**
|
|
39
43
|
* Builds pipeline upload payload from controller manifest.
|
|
40
|
-
* Payload: { version, application, dataSources };
|
|
44
|
+
* Payload: { version, application, dataSources, status }; Builder always uses status "draft".
|
|
41
45
|
* @param {Object} manifest - Controller manifest from generateControllerManifest
|
|
42
|
-
* @returns {Object} { version, application, dataSources }
|
|
46
|
+
* @returns {Object} { version, application, dataSources, status: "draft" }
|
|
43
47
|
*/
|
|
44
48
|
function buildUploadPayload(manifest) {
|
|
45
49
|
return {
|
|
46
50
|
version: manifest.version || '1.0.0',
|
|
47
51
|
application: manifest.system,
|
|
48
|
-
dataSources: manifest.dataSources || []
|
|
52
|
+
dataSources: manifest.dataSources || [],
|
|
53
|
+
status: 'draft'
|
|
49
54
|
};
|
|
50
55
|
}
|
|
51
56
|
|
|
52
57
|
/**
|
|
53
58
|
* Resolves dataplane URL and auth (same pattern as download).
|
|
54
59
|
* @param {string} systemKey - System key
|
|
55
|
-
* @param {Object} options - Options with optional dataplane override
|
|
56
60
|
* @returns {Promise<{ dataplaneUrl: string, authConfig: Object, environment: string }>}
|
|
57
61
|
*/
|
|
58
|
-
async function resolveDataplaneAndAuth(systemKey
|
|
62
|
+
async function resolveDataplaneAndAuth(systemKey) {
|
|
59
63
|
const { resolveEnvironment } = require('../core/config');
|
|
60
64
|
const environment = await resolveEnvironment();
|
|
61
65
|
const controllerUrl = await resolveControllerUrl();
|
|
@@ -65,42 +69,42 @@ async function resolveDataplaneAndAuth(systemKey, options) {
|
|
|
65
69
|
throw new Error('Authentication required. Run "aifabrix login" or "aifabrix app register <system-key>" first.');
|
|
66
70
|
}
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
dataplaneUrl = options.dataplane.replace(/\/$/, '');
|
|
71
|
-
} else {
|
|
72
|
-
logger.log(chalk.blue('Resolving dataplane URL...'));
|
|
73
|
-
dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
74
|
-
}
|
|
75
|
-
|
|
72
|
+
logger.log(chalk.blue('Resolving dataplane URL...'));
|
|
73
|
+
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig);
|
|
76
74
|
return { dataplaneUrl, authConfig, environment };
|
|
77
75
|
}
|
|
78
76
|
|
|
79
77
|
/**
|
|
80
|
-
* Runs upload → validate → publish on the dataplane.
|
|
78
|
+
* Runs single pipeline upload (upload → validate → publish) on the dataplane.
|
|
81
79
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
82
80
|
* @param {Object} authConfig - Auth config
|
|
83
|
-
* @param {Object} payload - { version, application, dataSources }
|
|
84
|
-
* @returns {Promise<
|
|
81
|
+
* @param {Object} payload - { version, application, dataSources, status: "draft" }
|
|
82
|
+
* @returns {Promise<Object>} Publication result from Dataplane
|
|
85
83
|
*/
|
|
86
84
|
async function runUploadValidatePublish(dataplaneUrl, authConfig, payload) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const msg = uploadRes?.success === false
|
|
91
|
-
? formatApiError(uploadRes, dataplaneUrl)
|
|
92
|
-
: '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);
|
|
93
88
|
throw new Error(msg);
|
|
94
89
|
}
|
|
90
|
+
return res;
|
|
91
|
+
}
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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.';
|
|
100
102
|
}
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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.`;
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
/**
|
|
@@ -111,22 +115,62 @@ async function runUploadValidatePublish(dataplaneUrl, authConfig, payload) {
|
|
|
111
115
|
function throwIfValidationFailed(validationResult) {
|
|
112
116
|
if (!validationResult.valid) {
|
|
113
117
|
displayValidationResults(validationResult);
|
|
114
|
-
throw new Error(
|
|
118
|
+
throw new Error(formatValidationErrorSummary(validationResult));
|
|
115
119
|
}
|
|
116
120
|
}
|
|
117
121
|
|
|
118
122
|
/**
|
|
119
|
-
*
|
|
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
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Pushes credential secrets from .env and payload to dataplane; logs result or warning.
|
|
141
|
+
* @param {string} dataplaneUrl - Dataplane URL
|
|
142
|
+
* @param {Object} authConfig - Auth config
|
|
143
|
+
* @param {string} systemKey - System key
|
|
144
|
+
* @param {Object} payload - Upload payload
|
|
145
|
+
*/
|
|
146
|
+
async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload) {
|
|
147
|
+
const envFilePath = path.join(getIntegrationPath(systemKey), '.env');
|
|
148
|
+
const pushResult = await pushCredentialSecrets(dataplaneUrl, authConfig, {
|
|
149
|
+
envFilePath,
|
|
150
|
+
appName: systemKey,
|
|
151
|
+
payload
|
|
152
|
+
});
|
|
153
|
+
if (pushResult.pushed > 0) {
|
|
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'));
|
|
158
|
+
}
|
|
159
|
+
if (pushResult.warning) {
|
|
160
|
+
logger.log(chalk.yellow(`Warning: ${pushResult.warning}`));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Uploads external system to dataplane (single pipeline upload). No controller deploy.
|
|
120
166
|
* @param {string} systemKey - External system key (integration/<system-key>/)
|
|
121
167
|
* @param {Object} [options] - Options
|
|
122
168
|
* @param {boolean} [options.dryRun] - Validate and build payload only; no API calls
|
|
123
|
-
* @param {string} [options.dataplane] - Override dataplane URL
|
|
124
169
|
* @returns {Promise<void>}
|
|
125
170
|
* @throws {Error} If validation or API calls fail
|
|
126
171
|
*/
|
|
127
172
|
async function uploadExternalSystem(systemKey, options = {}) {
|
|
128
173
|
validateSystemKeyFormat(systemKey);
|
|
129
|
-
|
|
130
174
|
logger.log(chalk.blue(`\nUploading external system to dataplane: ${systemKey}`));
|
|
131
175
|
|
|
132
176
|
const validationResult = await validateExternalSystemComplete(systemKey, { type: 'external' });
|
|
@@ -135,6 +179,7 @@ async function uploadExternalSystem(systemKey, options = {}) {
|
|
|
135
179
|
|
|
136
180
|
const manifest = await generateControllerManifest(systemKey, { type: 'external' });
|
|
137
181
|
const payload = buildUploadPayload(manifest);
|
|
182
|
+
await resolvePayloadConfiguration(systemKey, payload);
|
|
138
183
|
|
|
139
184
|
if (options.dryRun) {
|
|
140
185
|
logger.log(chalk.yellow('Dry run: would upload payload (no API calls).'));
|
|
@@ -142,12 +187,23 @@ async function uploadExternalSystem(systemKey, options = {}) {
|
|
|
142
187
|
return;
|
|
143
188
|
}
|
|
144
189
|
|
|
145
|
-
const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey
|
|
190
|
+
const { dataplaneUrl, authConfig, environment } = await resolveDataplaneAndAuth(systemKey);
|
|
146
191
|
requireBearerForDataplanePipeline(authConfig);
|
|
147
192
|
logger.log(chalk.blue(`Dataplane: ${dataplaneUrl}`));
|
|
193
|
+
logDataplanePipelineWarning();
|
|
148
194
|
|
|
195
|
+
await pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey, payload);
|
|
149
196
|
await runUploadValidatePublish(dataplaneUrl, authConfig, payload);
|
|
197
|
+
logUploadSuccess(environment, systemKey, dataplaneUrl);
|
|
198
|
+
}
|
|
150
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) {
|
|
151
207
|
logger.log(chalk.green('\nUpload validated and published to dataplane.'));
|
|
152
208
|
logger.log(chalk.blue(`Environment: ${environment}`));
|
|
153
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
|
};
|