@aifabrix/builder 2.32.3 → 2.33.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/project-rules.mdc +8 -0
- package/README.md +36 -8
- package/bin/aifabrix.js +6 -8
- package/integration/hubspot/README.md +12 -11
- package/integration/hubspot/companies.json +2048 -0
- package/integration/hubspot/create-hubspot.js +665 -0
- package/integration/hubspot/{hubspot-deploy-company.json → hubspot-datasource-company.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-contact.json → hubspot-datasource-contact.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-deal.json → hubspot-datasource-deal.json} +1 -1
- package/integration/hubspot/hubspot-deploy.json +832 -81
- package/integration/hubspot/hubspot-system.json +99 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-credential-real.yaml +20 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-env-vars.yaml +9 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-add-datasource.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-app-name.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-create.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-select.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-known-platform.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-app.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-mode.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-file.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-url.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-source.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-array-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +5 -0
- package/integration/hubspot/test-dataplane-down-helpers.js +246 -0
- package/integration/hubspot/test-dataplane-down-tests.js +419 -0
- package/integration/hubspot/test-dataplane-down.js +157 -0
- package/integration/hubspot/test.js +1517 -0
- package/integration/hubspot/variables.yaml +4 -4
- package/integration/hubspot/wizard-hubspot-e2e.yaml +16 -0
- package/integration/hubspot/wizard-hubspot-platform.yaml +8 -0
- package/lib/api/applications.api.js +1 -0
- package/lib/api/index.js +6 -2
- package/lib/api/types/wizard.types.js +176 -38
- package/lib/api/wizard.api.js +161 -23
- package/lib/app/deploy.js +116 -54
- package/lib/app/display.js +6 -5
- package/lib/app/dockerfile.js +2 -1
- package/lib/app/list.js +17 -10
- package/lib/app/readme.js +41 -112
- package/lib/app/register.js +44 -9
- package/lib/app/rotate-secret.js +48 -31
- package/lib/cli.js +219 -70
- package/lib/commands/app.js +4 -9
- package/lib/commands/auth-config.js +125 -0
- package/lib/commands/auth-status.js +7 -8
- package/lib/commands/datasource.js +3 -6
- package/lib/commands/login-credentials.js +4 -4
- package/lib/commands/login-device.js +26 -17
- package/lib/commands/login.js +12 -10
- package/lib/commands/wizard-config-normalizer.js +92 -0
- package/lib/commands/wizard-core.js +515 -0
- package/lib/commands/wizard-dataplane.js +122 -0
- package/lib/commands/wizard-headless.js +115 -0
- package/lib/commands/wizard.js +110 -332
- package/lib/core/config.js +46 -0
- package/lib/core/secrets.js +3 -22
- package/lib/core/templates-env.js +1 -1
- package/lib/datasource/deploy.js +59 -23
- package/lib/datasource/list.js +108 -19
- package/lib/deployment/deployer.js +25 -0
- package/lib/deployment/environment.js +10 -13
- package/lib/external-system/delete.js +151 -0
- package/lib/external-system/deploy.js +53 -378
- package/lib/external-system/download-helpers.js +45 -65
- package/lib/external-system/download.js +33 -13
- package/lib/external-system/generator.js +11 -7
- package/lib/external-system/test-auth.js +4 -3
- package/lib/generator/builders.js +3 -1
- package/lib/generator/external-controller-manifest.js +157 -0
- package/lib/generator/external-schema-utils.js +236 -0
- package/lib/generator/external.js +55 -3
- package/lib/generator/index.js +22 -10
- package/lib/generator/wizard-prompts.js +33 -10
- package/lib/generator/wizard.js +69 -86
- package/lib/infrastructure/compose.js +100 -0
- package/lib/infrastructure/helpers.js +139 -0
- package/lib/infrastructure/index.js +52 -311
- package/lib/infrastructure/services.js +168 -0
- package/lib/schema/application-schema.json +23 -4
- package/lib/schema/external-datasource.schema.json +2 -2
- package/lib/schema/wizard-config.schema.json +234 -0
- package/lib/utils/api.js +102 -52
- package/lib/utils/app-existence.js +42 -0
- package/lib/utils/app-register-config.js +7 -2
- package/lib/utils/auth-config-validator.js +92 -0
- package/lib/utils/command-header.js +43 -0
- package/lib/utils/compose-generator.js +113 -70
- package/lib/utils/controller-url.js +65 -17
- package/lib/utils/dataplane-health.js +115 -0
- package/lib/utils/dataplane-resolver.js +29 -0
- package/lib/utils/dev-config.js +6 -2
- package/lib/utils/env-copy.js +2 -1
- package/lib/utils/env-ports.js +2 -1
- package/lib/utils/env-template.js +1 -1
- package/lib/utils/error-formatter.js +49 -0
- package/lib/utils/error-formatters/network-errors.js +13 -3
- package/lib/utils/external-readme.js +125 -0
- package/lib/utils/help-builder.js +190 -0
- package/lib/utils/infra-status.js +13 -3
- package/lib/utils/paths.js +17 -2
- package/lib/utils/port-resolver.js +111 -0
- package/lib/utils/secrets-helpers.js +3 -15
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/token-manager.js +9 -4
- package/lib/utils/variable-transformer.js +7 -2
- package/lib/validation/external-manifest-validator.js +202 -0
- package/lib/validation/validate-display.js +406 -0
- package/lib/validation/validate.js +159 -123
- package/lib/validation/validator.js +36 -3
- package/lib/validation/wizard-config-validator.js +267 -0
- package/package.json +4 -2
- package/templates/applications/README.md.hbs +18 -16
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +7 -7
- package/templates/external-system/README.md.hbs +99 -0
- package/templates/github/ci.yaml.hbs +44 -1
- package/templates/github/release.yaml.hbs +44 -0
- package/templates/infra/compose.yaml.hbs +35 -0
- package/templates/python/docker-compose.hbs +26 -0
- package/templates/typescript/docker-compose.hbs +26 -0
|
@@ -1,432 +1,107 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* External System Deployment Module
|
|
3
3
|
*
|
|
4
|
-
* Handles deployment of external systems
|
|
5
|
-
*
|
|
4
|
+
* Handles deployment of external systems via controller pipeline.
|
|
5
|
+
* Uses unified controller pipeline (same as regular apps) - no direct dataplane calls.
|
|
6
6
|
*
|
|
7
7
|
* @fileoverview External system deployment for AI Fabrix Builder
|
|
8
8
|
* @author AI Fabrix Team
|
|
9
9
|
* @version 2.0.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
const fs = require('fs').promises;
|
|
13
|
-
const path = require('path');
|
|
14
12
|
const chalk = require('chalk');
|
|
15
|
-
const {
|
|
16
|
-
deployExternalSystemViaPipeline,
|
|
17
|
-
deployDatasourceViaPipeline,
|
|
18
|
-
uploadApplicationViaPipeline,
|
|
19
|
-
validateUploadViaPipeline,
|
|
20
|
-
publishUploadViaPipeline
|
|
21
|
-
} = require('../api/pipeline.api');
|
|
22
13
|
const { getDeploymentAuth } = require('../utils/token-manager');
|
|
23
|
-
const { getConfig } = require('../core/config');
|
|
24
14
|
const logger = require('../utils/logger');
|
|
25
|
-
const { getDataplaneUrl } = require('../datasource/deploy');
|
|
26
|
-
const { detectAppType } = require('../utils/paths');
|
|
27
|
-
const { generateExternalSystemApplicationSchema } = require('../generator/external');
|
|
28
15
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
29
|
-
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
validateDatasourceFiles,
|
|
33
|
-
extractSystemKey
|
|
34
|
-
} = require('./deploy-helpers');
|
|
16
|
+
const { generateControllerManifest } = require('../generator/external-controller-manifest');
|
|
17
|
+
const { validateExternalSystemComplete } = require('../validation/validate');
|
|
18
|
+
const { displayValidationResults } = require('../validation/validate-display');
|
|
35
19
|
|
|
36
20
|
/**
|
|
37
|
-
*
|
|
38
|
-
* @async
|
|
39
|
-
* @function loadVariablesYaml
|
|
40
|
-
* @param {string} appName - Application name
|
|
41
|
-
* @returns {Promise<Object>} Variables configuration
|
|
42
|
-
* @throws {Error} If file cannot be loaded
|
|
43
|
-
*/
|
|
44
|
-
/**
|
|
45
|
-
* Validates external system files exist
|
|
46
|
-
* @async
|
|
47
|
-
* @function validateExternalSystemFiles
|
|
48
|
-
* @param {string} appName - Application name
|
|
49
|
-
* @returns {Promise<Object>} Validation result with file paths
|
|
50
|
-
* @throws {Error} If validation fails
|
|
51
|
-
*/
|
|
52
|
-
|
|
53
|
-
async function validateExternalSystemFiles(appName) {
|
|
54
|
-
const variables = await loadVariablesYaml(appName);
|
|
55
|
-
|
|
56
|
-
if (!variables.externalIntegration) {
|
|
57
|
-
throw new Error('externalIntegration block not found in variables.yaml');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Detect app type and get correct path (integration or builder)
|
|
61
|
-
const { appPath } = await detectAppType(appName);
|
|
62
|
-
|
|
63
|
-
// For new structure, files are in same folder (schemaBasePath is usually './')
|
|
64
|
-
// For backward compatibility, support old schemas/ subfolder
|
|
65
|
-
const schemaBasePath = variables.externalIntegration.schemaBasePath || './';
|
|
66
|
-
const schemasPath = path.isAbsolute(schemaBasePath)
|
|
67
|
-
? schemaBasePath
|
|
68
|
-
: path.join(appPath, schemaBasePath);
|
|
69
|
-
|
|
70
|
-
// Validate system files
|
|
71
|
-
const systemFilesList = variables.externalIntegration.systems || [];
|
|
72
|
-
if (systemFilesList.length === 0) {
|
|
73
|
-
throw new Error('No external system files specified in externalIntegration.systems');
|
|
74
|
-
}
|
|
75
|
-
const systemFiles = await validateSystemFiles(systemFilesList, appName, schemasPath);
|
|
76
|
-
|
|
77
|
-
// Validate datasource files
|
|
78
|
-
const datasourceFilesList = variables.externalIntegration.dataSources || [];
|
|
79
|
-
const datasourceFiles = await validateDatasourceFiles(datasourceFilesList, appPath, schemasPath);
|
|
80
|
-
|
|
81
|
-
// Extract systemKey from system file
|
|
82
|
-
const systemKey = extractSystemKey(systemFiles[0]);
|
|
83
|
-
|
|
84
|
-
return {
|
|
85
|
-
systemFiles,
|
|
86
|
-
datasourceFiles,
|
|
87
|
-
systemKey
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Deploys external system to dataplane (build step - deploy, not publish)
|
|
93
|
-
* @async
|
|
94
|
-
* @function buildExternalSystem
|
|
95
|
-
* @param {string} appName - Application name
|
|
96
|
-
* @param {Object} options - Deployment options
|
|
97
|
-
* @returns {Promise<void>} Resolves when deployment completes
|
|
98
|
-
* @throws {Error} If deployment fails
|
|
99
|
-
*/
|
|
100
|
-
/**
|
|
101
|
-
* Validates and prepares deployment configuration
|
|
21
|
+
* Prepares deployment configuration (auth, controller URL, environment)
|
|
102
22
|
* @async
|
|
103
23
|
* @function prepareDeploymentConfig
|
|
104
24
|
* @param {string} appName - Application name
|
|
105
25
|
* @param {Object} options - Deployment options
|
|
106
26
|
* @returns {Promise<Object>} Deployment configuration
|
|
107
27
|
*/
|
|
108
|
-
async function prepareDeploymentConfig(appName,
|
|
109
|
-
const {
|
|
110
|
-
|
|
111
|
-
const
|
|
112
|
-
const environment = options.environment || 'dev';
|
|
113
|
-
const controllerUrl = await resolveControllerUrl(options, config);
|
|
114
|
-
const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
|
|
115
|
-
|
|
116
|
-
if (!authConfig.token && !authConfig.clientId) {
|
|
117
|
-
throw new Error('Authentication required. Run "aifabrix login" or "aifabrix app register" first.');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return { systemFiles, datasourceFiles, systemKey, environment, controllerUrl, authConfig };
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Gets dataplane URL from controller
|
|
125
|
-
* @async
|
|
126
|
-
* @function getDataplaneUrlForDeployment
|
|
127
|
-
* @param {string} controllerUrl - Controller URL
|
|
128
|
-
* @param {string} appName - Application name
|
|
129
|
-
* @param {string} environment - Environment key
|
|
130
|
-
* @param {Object} authConfig - Authentication configuration
|
|
131
|
-
* @returns {Promise<string>} Dataplane URL
|
|
132
|
-
*/
|
|
133
|
-
async function getDataplaneUrlForDeployment(controllerUrl, appName, environment, authConfig) {
|
|
134
|
-
logger.log(chalk.blue('🌐 Getting dataplane URL from controller...'));
|
|
135
|
-
const dataplaneUrl = await getDataplaneUrl(controllerUrl, appName, environment, authConfig);
|
|
136
|
-
logger.log(chalk.green(`✓ Dataplane URL: ${dataplaneUrl}`));
|
|
137
|
-
return dataplaneUrl;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
* Deploys external system via pipeline
|
|
142
|
-
* @async
|
|
143
|
-
* @function deploySystem
|
|
144
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
145
|
-
* @param {Object} authConfig - Authentication configuration
|
|
146
|
-
* @param {string} systemFilePath - Path to system file
|
|
147
|
-
* @param {string} systemKey - System key
|
|
148
|
-
* @returns {Promise<void>}
|
|
149
|
-
*/
|
|
150
|
-
async function deploySystem(dataplaneUrl, authConfig, systemFilePath, systemKey) {
|
|
151
|
-
logger.log(chalk.blue(`Deploying external system: ${systemKey}...`));
|
|
152
|
-
const systemContent = await fs.readFile(systemFilePath, 'utf8');
|
|
153
|
-
const systemJson = JSON.parse(systemContent);
|
|
154
|
-
|
|
155
|
-
const systemResponse = await deployExternalSystemViaPipeline(dataplaneUrl, authConfig, systemJson);
|
|
156
|
-
|
|
157
|
-
if (!systemResponse.success) {
|
|
158
|
-
throw new Error(`Failed to deploy external system: ${systemResponse.error || systemResponse.formattedError}`);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
logger.log(chalk.green(`✓ External system deployed: ${systemKey}`));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Deploys a single datasource via pipeline
|
|
166
|
-
* @async
|
|
167
|
-
* @function deploySingleDatasource
|
|
168
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
169
|
-
* @param {string} systemKey - System key
|
|
170
|
-
* @param {Object} authConfig - Authentication configuration
|
|
171
|
-
* @param {string} datasourceFile - Path to datasource file
|
|
172
|
-
* @returns {Promise<void>}
|
|
173
|
-
*/
|
|
174
|
-
async function deploySingleDatasource(dataplaneUrl, systemKey, authConfig, datasourceFile) {
|
|
175
|
-
const datasourceName = path.basename(datasourceFile, '.json');
|
|
176
|
-
logger.log(chalk.blue(`Deploying datasource: ${datasourceName}...`));
|
|
177
|
-
|
|
178
|
-
const datasourceContent = await fs.readFile(datasourceFile, 'utf8');
|
|
179
|
-
const datasourceJson = JSON.parse(datasourceContent);
|
|
180
|
-
|
|
181
|
-
const datasourceResponse = await deployDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceJson);
|
|
182
|
-
|
|
183
|
-
if (!datasourceResponse.success) {
|
|
184
|
-
throw new Error(`Failed to deploy datasource ${datasourceName}: ${datasourceResponse.error || datasourceResponse.formattedError}`);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
logger.log(chalk.green(`✓ Datasource deployed: ${datasourceName}`));
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Deploys all datasources
|
|
192
|
-
* @async
|
|
193
|
-
* @function deployAllDatasources
|
|
194
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
195
|
-
* @param {string} systemKey - System key
|
|
196
|
-
* @param {Object} authConfig - Authentication configuration
|
|
197
|
-
* @param {string[]} datasourceFiles - Array of datasource file paths
|
|
198
|
-
* @returns {Promise<void>}
|
|
199
|
-
*/
|
|
200
|
-
async function deployAllDatasources(dataplaneUrl, systemKey, authConfig, datasourceFiles) {
|
|
201
|
-
for (const datasourceFile of datasourceFiles) {
|
|
202
|
-
await deploySingleDatasource(dataplaneUrl, systemKey, authConfig, datasourceFile);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
async function buildExternalSystem(appName, options = {}) {
|
|
207
|
-
try {
|
|
208
|
-
logger.log(chalk.blue(`\n🔨 Building external system: ${appName}`));
|
|
209
|
-
|
|
210
|
-
const { systemFiles, datasourceFiles, systemKey, environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
|
|
211
|
-
const dataplaneUrl = await getDataplaneUrlForDeployment(controllerUrl, appName, environment, authConfig);
|
|
212
|
-
|
|
213
|
-
await deploySystem(dataplaneUrl, authConfig, systemFiles[0], systemKey);
|
|
214
|
-
await deployAllDatasources(dataplaneUrl, systemKey, authConfig, datasourceFiles);
|
|
215
|
-
|
|
216
|
-
logger.log(chalk.green('\n✅ External system built successfully!'));
|
|
217
|
-
logger.log(chalk.blue(`System: ${systemKey}`));
|
|
218
|
-
logger.log(chalk.blue(`Datasources: ${datasourceFiles.length}`));
|
|
219
|
-
} catch (error) {
|
|
220
|
-
throw new Error(`Failed to build external system: ${error.message}`);
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Validate deployment prerequisites
|
|
226
|
-
* @async
|
|
227
|
-
* @param {string} appName - Application name
|
|
228
|
-
* @returns {Promise<Object>} Validation result with systemFiles, datasourceFiles, and systemKey
|
|
229
|
-
*/
|
|
230
|
-
async function validateDeploymentPrerequisites(appName) {
|
|
231
|
-
const { systemFiles: _systemFiles, datasourceFiles, systemKey } = await validateExternalSystemFiles(appName);
|
|
232
|
-
return { systemFiles: _systemFiles, datasourceFiles, systemKey };
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Prepare deployment files and get authentication
|
|
237
|
-
* @async
|
|
238
|
-
* @param {string} appName - Application name
|
|
239
|
-
* @param {Object} options - Deployment options
|
|
240
|
-
* @returns {Promise<Object>} Object with applicationSchema, authConfig, controllerUrl, environment, and systemKey
|
|
241
|
-
*/
|
|
242
|
-
async function prepareDeploymentFiles(appName, options) {
|
|
243
|
-
logger.log(chalk.blue('📋 Generating application schema...'));
|
|
244
|
-
const applicationSchema = await generateExternalSystemApplicationSchema(appName);
|
|
245
|
-
logger.log(chalk.green('✓ Application schema generated'));
|
|
246
|
-
|
|
247
|
-
const config = await getConfig();
|
|
248
|
-
const environment = options.environment || 'dev';
|
|
249
|
-
const controllerUrl = await resolveControllerUrl(options, config);
|
|
28
|
+
async function prepareDeploymentConfig(appName, _options) {
|
|
29
|
+
const { resolveEnvironment } = require('../core/config');
|
|
30
|
+
const environment = await resolveEnvironment();
|
|
31
|
+
const controllerUrl = await resolveControllerUrl();
|
|
250
32
|
const authConfig = await getDeploymentAuth(controllerUrl, environment, appName);
|
|
251
33
|
|
|
252
34
|
if (!authConfig.token && !authConfig.clientId) {
|
|
253
35
|
throw new Error('Authentication required. Run "aifabrix login" or "aifabrix app register" first.');
|
|
254
36
|
}
|
|
255
37
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return { applicationSchema, authConfig, controllerUrl, environment, systemKey };
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
/**
|
|
262
|
-
* Upload application and get upload ID
|
|
263
|
-
* @async
|
|
264
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
265
|
-
* @param {Object} authConfig - Authentication configuration
|
|
266
|
-
* @param {Object} applicationSchema - Application schema
|
|
267
|
-
* @returns {Promise<string>} Upload ID
|
|
268
|
-
* @throws {Error} If upload fails
|
|
269
|
-
*/
|
|
270
|
-
async function uploadApplication(dataplaneUrl, authConfig, applicationSchema) {
|
|
271
|
-
logger.log(chalk.blue('📤 Uploading application configuration...'));
|
|
272
|
-
const uploadResponse = await uploadApplicationViaPipeline(dataplaneUrl, authConfig, applicationSchema);
|
|
273
|
-
|
|
274
|
-
if (!uploadResponse.success || !uploadResponse.data) {
|
|
275
|
-
throw new Error(`Failed to upload application: ${uploadResponse.error || uploadResponse.formattedError || 'Unknown error'}`);
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const uploadData = uploadResponse.data.data || uploadResponse.data;
|
|
279
|
-
const uploadId = uploadData.uploadId || uploadData.id;
|
|
280
|
-
|
|
281
|
-
if (!uploadId) {
|
|
282
|
-
throw new Error('Upload ID not found in upload response');
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
logger.log(chalk.green(`✓ Upload successful (ID: ${uploadId})`));
|
|
286
|
-
return uploadId;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Validate upload and display changes
|
|
291
|
-
* @async
|
|
292
|
-
* @param {string} dataplaneUrl - Dataplane URL
|
|
293
|
-
* @param {string} uploadId - Upload ID
|
|
294
|
-
* @param {Object} authConfig - Authentication configuration
|
|
295
|
-
* @returns {Promise<void>}
|
|
296
|
-
* @throws {Error} If validation fails
|
|
297
|
-
*/
|
|
298
|
-
/**
|
|
299
|
-
* Displays validation changes
|
|
300
|
-
* @function displayValidationChanges
|
|
301
|
-
* @param {Object[]} changes - Array of changes
|
|
302
|
-
*/
|
|
303
|
-
function displayValidationChanges(changes) {
|
|
304
|
-
if (changes && changes.length > 0) {
|
|
305
|
-
logger.log(chalk.blue('\n📋 Changes to be published:'));
|
|
306
|
-
for (const change of changes) {
|
|
307
|
-
const changeType = change.type || 'unknown';
|
|
308
|
-
const changeEntity = change.entity || change.key || 'unknown';
|
|
309
|
-
const emoji = changeType === 'new' ? '➕' : changeType === 'modified' ? '✏️' : '🗑️';
|
|
310
|
-
logger.log(chalk.gray(` ${emoji} ${changeType}: ${changeEntity}`));
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Validates upload response
|
|
317
|
-
* @function validateUploadResponse
|
|
318
|
-
* @param {Object} validateResponse - Validation response
|
|
319
|
-
* @returns {Object} Validation data
|
|
320
|
-
* @throws {Error} If validation failed
|
|
321
|
-
*/
|
|
322
|
-
function validateUploadResponse(validateResponse) {
|
|
323
|
-
if (!validateResponse.success || !validateResponse.data) {
|
|
324
|
-
throw new Error(`Validation failed: ${validateResponse.error || validateResponse.formattedError || 'Unknown error'}`);
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
return validateResponse.data.data || validateResponse.data;
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
async function validateUpload(dataplaneUrl, uploadId, authConfig) {
|
|
331
|
-
logger.log(chalk.blue('🔍 Validating upload...'));
|
|
332
|
-
const validateResponse = await validateUploadViaPipeline(dataplaneUrl, uploadId, authConfig);
|
|
333
|
-
|
|
334
|
-
const validateData = validateUploadResponse(validateResponse);
|
|
335
|
-
|
|
336
|
-
displayValidationChanges(validateData.changes);
|
|
337
|
-
|
|
338
|
-
if (validateData.summary) {
|
|
339
|
-
logger.log(chalk.blue(`\n📊 Summary: ${validateData.summary}`));
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
logger.log(chalk.green('✓ Validation successful'));
|
|
38
|
+
return { environment, controllerUrl, authConfig };
|
|
343
39
|
}
|
|
344
40
|
|
|
345
41
|
/**
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
* @param {string} uploadId - Upload ID
|
|
350
|
-
* @param {Object} authConfig - Authentication configuration
|
|
351
|
-
* @param {Object} options - Publish options
|
|
352
|
-
* @param {boolean} [options.generateMcpContract] - Generate MCP contract (default: true)
|
|
353
|
-
* @returns {Promise<Object>} Publish response data
|
|
354
|
-
* @throws {Error} If publish fails
|
|
355
|
-
*/
|
|
356
|
-
async function publishApplication(dataplaneUrl, uploadId, authConfig, options) {
|
|
357
|
-
const generateMcpContract = options.generateMcpContract !== false; // Default to true
|
|
358
|
-
logger.log(chalk.blue(`📢 Publishing application (MCP contract: ${generateMcpContract ? 'enabled' : 'disabled'})...`));
|
|
359
|
-
|
|
360
|
-
const publishResponse = await publishUploadViaPipeline(dataplaneUrl, uploadId, authConfig, { generateMcpContract });
|
|
361
|
-
|
|
362
|
-
if (!publishResponse.success || !publishResponse.data) {
|
|
363
|
-
throw new Error(`Failed to publish application: ${publishResponse.error || publishResponse.formattedError || 'Unknown error'}`);
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
return publishResponse.data.data || publishResponse.data;
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
/**
|
|
370
|
-
* Publishes external system to dataplane using application-level workflow
|
|
371
|
-
* Uses upload → validate → publish workflow for atomic deployment
|
|
42
|
+
* Deploys external system via controller pipeline (same as regular apps)
|
|
43
|
+
* Uses unified controller pipeline - no direct dataplane calls
|
|
44
|
+
*
|
|
372
45
|
* @async
|
|
373
46
|
* @function deployExternalSystem
|
|
374
47
|
* @param {string} appName - Application name
|
|
375
48
|
* @param {Object} options - Deployment options
|
|
376
49
|
* @param {string} [options.environment] - Environment (dev, tst, pro)
|
|
377
50
|
* @param {string} [options.controller] - Controller URL
|
|
378
|
-
* @param {boolean} [options.
|
|
379
|
-
* @param {
|
|
380
|
-
* @returns {Promise<
|
|
51
|
+
* @param {boolean} [options.poll] - Poll for deployment status
|
|
52
|
+
* @param {number} [options.pollInterval] - Polling interval in milliseconds (default: 500ms for external systems)
|
|
53
|
+
* @returns {Promise<Object>} Deployment result
|
|
381
54
|
* @throws {Error} If deployment fails
|
|
382
55
|
*/
|
|
383
56
|
async function deployExternalSystem(appName, options = {}) {
|
|
384
57
|
try {
|
|
385
|
-
logger.log(chalk.blue(`\n🚀
|
|
386
|
-
|
|
387
|
-
// Validate prerequisites
|
|
388
|
-
const { datasourceFiles } = await validateDeploymentPrerequisites(appName);
|
|
389
|
-
|
|
390
|
-
// Prepare deployment files and get authentication
|
|
391
|
-
const { applicationSchema, authConfig, controllerUrl, environment, systemKey } = await prepareDeploymentFiles(appName, options);
|
|
58
|
+
logger.log(chalk.blue(`\n🚀 Deploying external system: ${appName}`));
|
|
392
59
|
|
|
393
|
-
//
|
|
394
|
-
logger.log(chalk.blue('
|
|
395
|
-
const
|
|
396
|
-
logger.log(chalk.green(`✓ Dataplane URL: ${dataplaneUrl}`));
|
|
60
|
+
// Step 0: Validate before deployment (same as validate command)
|
|
61
|
+
logger.log(chalk.blue('🔍 Validating external system before deployment...'));
|
|
62
|
+
const validationResult = await validateExternalSystemComplete(appName);
|
|
397
63
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
// Step 2: Validate upload (optional, can be skipped)
|
|
402
|
-
if (!options.skipValidation) {
|
|
403
|
-
await validateUpload(dataplaneUrl, uploadId, authConfig);
|
|
404
|
-
} else {
|
|
405
|
-
logger.log(chalk.yellow('⚠ Skipping validation step'));
|
|
64
|
+
if (!validationResult.valid) {
|
|
65
|
+
displayValidationResults(validationResult);
|
|
66
|
+
throw new Error('Validation failed. Fix errors before deploying.');
|
|
406
67
|
}
|
|
407
68
|
|
|
408
|
-
|
|
409
|
-
|
|
69
|
+
logger.log(chalk.green('✓ Validation passed, proceeding with deployment...'));
|
|
70
|
+
|
|
71
|
+
// Step 1: Generate controller manifest (validated, ready for deployment)
|
|
72
|
+
const manifest = await generateControllerManifest(appName);
|
|
73
|
+
|
|
74
|
+
// Step 2: Get deployment configuration (auth, controller URL, etc.)
|
|
75
|
+
const { environment, controllerUrl, authConfig } = await prepareDeploymentConfig(appName, options);
|
|
76
|
+
|
|
77
|
+
// Step 3: Deploy via controller pipeline (same as regular apps)
|
|
78
|
+
// Use 500ms polling for external systems (faster than web apps which use 5000ms)
|
|
79
|
+
const deployer = require('../deployment/deployer');
|
|
80
|
+
const result = await deployer.deployToController(
|
|
81
|
+
manifest,
|
|
82
|
+
controllerUrl,
|
|
83
|
+
environment,
|
|
84
|
+
authConfig,
|
|
85
|
+
{
|
|
86
|
+
poll: options.poll,
|
|
87
|
+
pollInterval: options.pollInterval !== undefined ? options.pollInterval : 500,
|
|
88
|
+
pollMaxAttempts: options.pollMaxAttempts,
|
|
89
|
+
...options
|
|
90
|
+
}
|
|
91
|
+
);
|
|
410
92
|
|
|
411
93
|
// Display success summary
|
|
412
|
-
logger.log(chalk.green('\n✅ External system
|
|
413
|
-
logger.log(chalk.blue(`System: ${
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
if (publishData.dataSources && publishData.dataSources.length > 0) {
|
|
418
|
-
logger.log(chalk.blue(`Published datasources: ${publishData.dataSources.length}`));
|
|
419
|
-
} else {
|
|
420
|
-
logger.log(chalk.blue(`Datasources: ${datasourceFiles.length}`));
|
|
421
|
-
}
|
|
94
|
+
logger.log(chalk.green('\n✅ External system deployed successfully!'));
|
|
95
|
+
logger.log(chalk.blue(`System: ${manifest.key}`));
|
|
96
|
+
logger.log(chalk.blue(`Datasources: ${manifest.dataSources.length}`));
|
|
97
|
+
|
|
98
|
+
return result;
|
|
422
99
|
} catch (error) {
|
|
423
100
|
throw new Error(`Failed to deploy external system: ${error.message}`);
|
|
424
101
|
}
|
|
425
102
|
}
|
|
426
103
|
|
|
427
104
|
module.exports = {
|
|
428
|
-
|
|
429
|
-
deployExternalSystem,
|
|
430
|
-
validateExternalSystemFiles
|
|
105
|
+
deployExternalSystem
|
|
431
106
|
};
|
|
432
107
|
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
const { generateExternalReadmeContent } = require('../utils/external-readme');
|
|
12
|
+
|
|
11
13
|
/**
|
|
12
14
|
* Generates variables.yaml content for downloaded system
|
|
13
15
|
* @param {string} systemKey - System key
|
|
@@ -16,17 +18,31 @@
|
|
|
16
18
|
* @returns {Object} Variables YAML object
|
|
17
19
|
*/
|
|
18
20
|
function generateVariablesYaml(systemKey, application, dataSources) {
|
|
19
|
-
const systemFileName = `${systemKey}-
|
|
21
|
+
const systemFileName = `${systemKey}-system.json`;
|
|
20
22
|
const datasourceFiles = dataSources.map(ds => {
|
|
21
|
-
// Extract
|
|
22
|
-
const
|
|
23
|
-
|
|
23
|
+
// Extract datasource key (remove system key prefix if present)
|
|
24
|
+
const datasourceKey = ds.key || '';
|
|
25
|
+
let datasourceKeyOnly;
|
|
26
|
+
if (datasourceKey.startsWith(`${systemKey}-`)) {
|
|
27
|
+
datasourceKeyOnly = datasourceKey.substring(systemKey.length + 1);
|
|
28
|
+
} else {
|
|
29
|
+
const entityType = ds.entityType || ds.entityKey || datasourceKey.split('-').pop();
|
|
30
|
+
datasourceKeyOnly = entityType;
|
|
31
|
+
}
|
|
32
|
+
return `${systemKey}-datasource-${datasourceKeyOnly}.json`;
|
|
24
33
|
});
|
|
25
34
|
|
|
26
35
|
return {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
36
|
+
app: {
|
|
37
|
+
key: systemKey,
|
|
38
|
+
displayName: application.displayName || systemKey,
|
|
39
|
+
description: application.description || `External system integration for ${systemKey}`,
|
|
40
|
+
type: 'external'
|
|
41
|
+
},
|
|
42
|
+
deployment: {
|
|
43
|
+
controllerUrl: '',
|
|
44
|
+
environment: 'dev'
|
|
45
|
+
},
|
|
30
46
|
externalIntegration: {
|
|
31
47
|
schemaBasePath: './',
|
|
32
48
|
systems: [systemFileName],
|
|
@@ -45,66 +61,30 @@ function generateVariablesYaml(systemKey, application, dataSources) {
|
|
|
45
61
|
* @returns {string} README.md content
|
|
46
62
|
*/
|
|
47
63
|
function generateReadme(systemKey, application, dataSources) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
'',
|
|
63
|
-
'## Files',
|
|
64
|
-
'',
|
|
65
|
-
'- `variables.yaml` - Application configuration with externalIntegration block',
|
|
66
|
-
`- \`${systemKey}-deploy.json\` - External system definition`
|
|
67
|
-
];
|
|
68
|
-
|
|
69
|
-
dataSources.forEach(ds => {
|
|
70
|
-
const entityType = ds.entityType || ds.entityKey || ds.key.split('-').pop();
|
|
71
|
-
lines.push(`- \`${systemKey}-deploy-${entityType}.json\` - Datasource: ${ds.displayName || ds.key}`);
|
|
64
|
+
const datasources = (Array.isArray(dataSources) ? dataSources : []).map((ds, index) => {
|
|
65
|
+
const datasourceKey = ds.key || '';
|
|
66
|
+
let datasourceKeyOnly;
|
|
67
|
+
if (datasourceKey.startsWith(`${systemKey}-`)) {
|
|
68
|
+
datasourceKeyOnly = datasourceKey.substring(systemKey.length + 1);
|
|
69
|
+
} else {
|
|
70
|
+
const entityType = ds.entityType || ds.entityKey || datasourceKey.split('-').pop() || `entity${index + 1}`;
|
|
71
|
+
datasourceKeyOnly = entityType;
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
entityType: datasourceKeyOnly,
|
|
75
|
+
displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
|
|
76
|
+
fileName: `${systemKey}-datasource-${datasourceKeyOnly}.json`
|
|
77
|
+
};
|
|
72
78
|
});
|
|
73
79
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
'4. Run integration tests: `aifabrix test-integration ${systemKey}`',
|
|
83
|
-
'5. Deploy: `aifabrix deploy ${systemKey} --environment dev`',
|
|
84
|
-
'',
|
|
85
|
-
'## Testing',
|
|
86
|
-
'',
|
|
87
|
-
'### Unit Tests',
|
|
88
|
-
'Run local validation without API calls:',
|
|
89
|
-
'```bash',
|
|
90
|
-
`aifabrix test ${systemKey}`,
|
|
91
|
-
'```',
|
|
92
|
-
'',
|
|
93
|
-
'### Integration Tests',
|
|
94
|
-
'Run integration tests via dataplane:',
|
|
95
|
-
'```bash',
|
|
96
|
-
`aifabrix test-integration ${systemKey} --environment dev`,
|
|
97
|
-
'```',
|
|
98
|
-
'',
|
|
99
|
-
'## Deployment',
|
|
100
|
-
'',
|
|
101
|
-
'Deploy to dataplane via miso-controller:',
|
|
102
|
-
'```bash',
|
|
103
|
-
`aifabrix deploy ${systemKey} --environment dev`,
|
|
104
|
-
'```'
|
|
105
|
-
);
|
|
106
|
-
|
|
107
|
-
return lines.join('\n');
|
|
80
|
+
return generateExternalReadmeContent({
|
|
81
|
+
appName: systemKey,
|
|
82
|
+
systemKey,
|
|
83
|
+
systemType: application.type,
|
|
84
|
+
displayName: application.displayName,
|
|
85
|
+
description: application.description,
|
|
86
|
+
datasources
|
|
87
|
+
});
|
|
108
88
|
}
|
|
109
89
|
|
|
110
90
|
module.exports = {
|