@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
package/lib/generator/wizard.js
CHANGED
|
@@ -15,7 +15,8 @@ const chalk = require('chalk');
|
|
|
15
15
|
const logger = require('../utils/logger');
|
|
16
16
|
const { resolveApplicationConfigPath } = require('../utils/app-config-resolver');
|
|
17
17
|
const { loadConfigFile, writeConfigFile } = require('../utils/config-format');
|
|
18
|
-
const {
|
|
18
|
+
const { systemKeyToKvPrefix, securityKeyToVar, isValidKvPath } = require('../utils/credential-secrets-env');
|
|
19
|
+
const { generateReadme } = require('./wizard-readme');
|
|
19
20
|
|
|
20
21
|
/**
|
|
21
22
|
* Converts a string to a schema-valid key segment (lowercase letters, numbers, hyphens only).
|
|
@@ -41,45 +42,59 @@ function toKeySegment(str) {
|
|
|
41
42
|
* @returns {Promise<Object>} Object with generated file paths
|
|
42
43
|
* @throws {Error} If file generation fails
|
|
43
44
|
*/
|
|
45
|
+
/** Extension for format */
|
|
46
|
+
const FORMAT_EXT = { yaml: '.yaml', json: '.json' };
|
|
47
|
+
|
|
44
48
|
/**
|
|
45
|
-
* Writes system
|
|
49
|
+
* Writes system config file
|
|
46
50
|
* @async
|
|
47
51
|
* @function writeSystemYamlFile
|
|
48
52
|
* @param {string} appPath - Application path
|
|
49
53
|
* @param {string} finalSystemKey - Final system key
|
|
50
54
|
* @param {Object} systemConfig - System configuration
|
|
55
|
+
* @param {string} [format] - Output format: 'yaml' (default) or 'json'
|
|
51
56
|
* @returns {Promise<string>} System file path
|
|
52
57
|
*/
|
|
53
|
-
async function writeSystemYamlFile(appPath, finalSystemKey, systemConfig) {
|
|
54
|
-
const
|
|
58
|
+
async function writeSystemYamlFile(appPath, finalSystemKey, systemConfig, format = 'yaml') {
|
|
59
|
+
const ext = FORMAT_EXT[format === 'json' ? 'json' : 'yaml'] || '.yaml';
|
|
60
|
+
const systemFileName = `${finalSystemKey}-system${ext}`;
|
|
55
61
|
const systemFilePath = path.join(appPath, systemFileName);
|
|
56
|
-
writeConfigFile(systemFilePath, systemConfig);
|
|
62
|
+
writeConfigFile(systemFilePath, systemConfig, format === 'json' ? 'json' : 'yaml');
|
|
57
63
|
logger.log(chalk.green(`✓ Generated system file: ${systemFileName}`));
|
|
58
64
|
return systemFilePath;
|
|
59
65
|
}
|
|
60
66
|
|
|
61
67
|
/**
|
|
62
|
-
* Writes datasource
|
|
68
|
+
* Writes datasource config files
|
|
63
69
|
* @async
|
|
64
70
|
* @function writeDatasourceYamlFiles
|
|
65
71
|
* @param {string} appPath - Application path
|
|
66
72
|
* @param {string} finalSystemKey - Final system key
|
|
67
73
|
* @param {Object[]} datasourceConfigs - Array of datasource configurations
|
|
74
|
+
* @param {string} [format] - Output format: 'yaml' (default) or 'json'
|
|
68
75
|
* @returns {Promise<string[]>} Array of datasource file names
|
|
69
76
|
*/
|
|
70
|
-
async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfigs) {
|
|
77
|
+
async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfigs, format = 'yaml') {
|
|
78
|
+
const ext = FORMAT_EXT[format === 'json' ? 'json' : 'yaml'] || '.yaml';
|
|
79
|
+
const fmt = format === 'json' ? 'json' : 'yaml';
|
|
71
80
|
const datasourceFileNames = [];
|
|
81
|
+
const usedBaseNames = new Set();
|
|
72
82
|
for (const datasourceConfig of datasourceConfigs) {
|
|
73
|
-
const
|
|
74
|
-
const keySegment = toKeySegment(entityType);
|
|
75
|
-
const datasourceKey = datasourceConfig.key || `${finalSystemKey}-${keySegment}`;
|
|
76
|
-
// Extract datasource key (remove system key prefix if present); use normalized segment for filename
|
|
83
|
+
const datasourceKey = datasourceConfig.key || '';
|
|
77
84
|
const datasourceKeyOnly = datasourceKey.includes('-') && datasourceKey.startsWith(`${finalSystemKey}-`)
|
|
78
85
|
? datasourceKey.substring(finalSystemKey.length + 1)
|
|
79
|
-
:
|
|
80
|
-
const
|
|
86
|
+
: (datasourceConfig.entityType || datasourceConfig.entityKey || datasourceKey.split('-').pop() || 'default');
|
|
87
|
+
const keySegment = toKeySegment(datasourceKeyOnly);
|
|
88
|
+
let baseName = keySegment;
|
|
89
|
+
if (usedBaseNames.has(baseName)) {
|
|
90
|
+
let suffix = 1;
|
|
91
|
+
while (usedBaseNames.has(`${baseName}-${suffix}`)) suffix++;
|
|
92
|
+
baseName = `${baseName}-${suffix}`;
|
|
93
|
+
}
|
|
94
|
+
usedBaseNames.add(baseName);
|
|
95
|
+
const datasourceFileName = `${finalSystemKey}-datasource-${baseName}${ext}`;
|
|
81
96
|
const datasourceFilePath = path.join(appPath, datasourceFileName);
|
|
82
|
-
writeConfigFile(datasourceFilePath, datasourceConfig);
|
|
97
|
+
writeConfigFile(datasourceFilePath, datasourceConfig, fmt);
|
|
83
98
|
datasourceFileNames.push(datasourceFileName);
|
|
84
99
|
logger.log(chalk.green(`✓ Generated datasource file: ${datasourceFileName}`));
|
|
85
100
|
}
|
|
@@ -102,20 +117,21 @@ async function writeDatasourceYamlFiles(appPath, finalSystemKey, datasourceConfi
|
|
|
102
117
|
* @returns {Promise<Object>} Object with file paths
|
|
103
118
|
*/
|
|
104
119
|
async function generateConfigFilesForWizard(params) {
|
|
105
|
-
const { appPath, appName, finalSystemKey, systemFileName, datasourceFileNames, systemConfig, datasourceConfigs, aiGeneratedReadme } = params;
|
|
120
|
+
const { appPath, appName, finalSystemKey, systemFileName, datasourceFileNames, systemConfig, datasourceConfigs, aiGeneratedReadme, format } = params;
|
|
106
121
|
|
|
107
|
-
// Generate or update application
|
|
122
|
+
// Generate or update application config with externalIntegration block
|
|
108
123
|
const configPath = await generateOrUpdateVariablesYaml({
|
|
109
124
|
appPath,
|
|
110
125
|
appName,
|
|
111
126
|
systemKey: finalSystemKey,
|
|
112
127
|
systemFileName,
|
|
113
128
|
datasourceFileNames,
|
|
114
|
-
systemConfig
|
|
129
|
+
systemConfig,
|
|
130
|
+
format: format || 'yaml'
|
|
115
131
|
});
|
|
116
132
|
|
|
117
|
-
// Generate env.template with authentication variables
|
|
118
|
-
await generateEnvTemplate(appPath, systemConfig);
|
|
133
|
+
// Generate env.template with KV_* authentication variables
|
|
134
|
+
await generateEnvTemplate(appPath, systemConfig, finalSystemKey);
|
|
119
135
|
|
|
120
136
|
const envTemplatePath = path.join(appPath, 'env.template');
|
|
121
137
|
try {
|
|
@@ -126,16 +142,25 @@ async function generateConfigFilesForWizard(params) {
|
|
|
126
142
|
}
|
|
127
143
|
|
|
128
144
|
// Generate README.md (use AI-generated content if available)
|
|
129
|
-
await generateReadme(
|
|
145
|
+
await generateReadme({
|
|
146
|
+
appPath,
|
|
147
|
+
appName,
|
|
148
|
+
systemKey: finalSystemKey,
|
|
149
|
+
systemConfig,
|
|
150
|
+
datasourceConfigs,
|
|
151
|
+
aiGeneratedContent: aiGeneratedReadme,
|
|
152
|
+
format
|
|
153
|
+
});
|
|
130
154
|
|
|
131
155
|
// Generate deployment scripts
|
|
132
156
|
const deployScripts = await generateDeployScripts(appPath, finalSystemKey, systemFileName, datasourceFileNames);
|
|
133
157
|
|
|
134
158
|
// Generate deployment manifest (<systemKey>-deploy.json) using controller format
|
|
135
|
-
const { generateControllerManifest } = require('./external-controller-manifest');
|
|
159
|
+
const { generateControllerManifest, toDeployJsonShape } = require('./external-controller-manifest');
|
|
136
160
|
const manifest = await generateControllerManifest(appName, { appPath });
|
|
161
|
+
const deployJson = toDeployJsonShape(manifest);
|
|
137
162
|
const deployManifestPath = path.join(appPath, `${finalSystemKey}-deploy.json`);
|
|
138
|
-
await fs.writeFile(deployManifestPath, JSON.stringify(
|
|
163
|
+
await fs.writeFile(deployManifestPath, JSON.stringify(deployJson, null, 2), 'utf8');
|
|
139
164
|
logger.log(chalk.green(`✓ Generated deployment manifest: ${finalSystemKey}-deploy.json`));
|
|
140
165
|
|
|
141
166
|
return {
|
|
@@ -151,24 +176,40 @@ async function prepareWizardContext(appName, systemConfig, datasourceConfigs) {
|
|
|
151
176
|
const appPath = path.join(process.cwd(), 'integration', appName);
|
|
152
177
|
await fs.mkdir(appPath, { recursive: true });
|
|
153
178
|
const finalSystemKey = appName;
|
|
179
|
+
const originalSystemKey = systemConfig.key || finalSystemKey;
|
|
154
180
|
const appDisplayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
155
181
|
const updatedSystemConfig = { ...systemConfig, key: finalSystemKey, displayName: appDisplayName };
|
|
182
|
+
const originalPrefix = `${originalSystemKey}-`;
|
|
156
183
|
const updatedDatasourceConfigs = datasourceConfigs.map(ds => {
|
|
157
|
-
|
|
158
|
-
const
|
|
159
|
-
|
|
160
|
-
|
|
184
|
+
let newKey;
|
|
185
|
+
const dsKey = ds.key || '';
|
|
186
|
+
if (dsKey && dsKey.startsWith(originalPrefix)) {
|
|
187
|
+
newKey = `${finalSystemKey}-${dsKey.substring(originalPrefix.length)}`;
|
|
188
|
+
} else {
|
|
189
|
+
const entityType = ds.entityType || ds.entityKey || dsKey.split('-').pop() || 'default';
|
|
190
|
+
const keySegment = toKeySegment(entityType);
|
|
191
|
+
newKey = `${finalSystemKey}-${keySegment}`;
|
|
192
|
+
}
|
|
193
|
+
const entityType = ds.entityType || ds.entityKey || newKey.split('-').pop() || 'default';
|
|
194
|
+
const entityDisplayName = String(entityType).charAt(0).toUpperCase() + String(entityType).slice(1).replace(/-/g, ' ');
|
|
195
|
+
return { ...ds, key: newKey, systemKey: finalSystemKey, displayName: ds.displayName || `${appDisplayName} ${entityDisplayName}` };
|
|
161
196
|
});
|
|
162
197
|
return { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs, appDisplayName };
|
|
163
198
|
}
|
|
164
199
|
|
|
165
200
|
async function generateWizardFiles(appName, systemConfig, datasourceConfigs, systemKey, options = {}) {
|
|
166
201
|
try {
|
|
167
|
-
const { aiGeneratedReadme } = options || {};
|
|
202
|
+
const { aiGeneratedReadme, format } = options || {};
|
|
203
|
+
const fmt = format === 'json' ? 'json' : 'yaml';
|
|
204
|
+
const ext = FORMAT_EXT[fmt] || '.yaml';
|
|
168
205
|
const { appPath, finalSystemKey, updatedSystemConfig, updatedDatasourceConfigs } = await prepareWizardContext(appName, systemConfig, datasourceConfigs);
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
206
|
+
const systemConfigWithDataSourcesKeys = {
|
|
207
|
+
...updatedSystemConfig,
|
|
208
|
+
dataSources: updatedDatasourceConfigs.map(ds => ds.key)
|
|
209
|
+
};
|
|
210
|
+
const systemFilePath = await writeSystemYamlFile(appPath, finalSystemKey, systemConfigWithDataSourcesKeys, fmt);
|
|
211
|
+
const datasourceFileNames = await writeDatasourceYamlFiles(appPath, finalSystemKey, updatedDatasourceConfigs, fmt);
|
|
212
|
+
const systemFileName = `${finalSystemKey}-system${ext}`;
|
|
172
213
|
const configFiles = await generateConfigFilesForWizard({
|
|
173
214
|
appPath,
|
|
174
215
|
appName,
|
|
@@ -177,7 +218,8 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
|
|
|
177
218
|
datasourceFileNames,
|
|
178
219
|
systemConfig: updatedSystemConfig,
|
|
179
220
|
datasourceConfigs: updatedDatasourceConfigs,
|
|
180
|
-
aiGeneratedReadme
|
|
221
|
+
aiGeneratedReadme,
|
|
222
|
+
format: fmt
|
|
181
223
|
});
|
|
182
224
|
return {
|
|
183
225
|
appPath,
|
|
@@ -190,22 +232,48 @@ async function generateWizardFiles(appName, systemConfig, datasourceConfigs, sys
|
|
|
190
232
|
}
|
|
191
233
|
}
|
|
192
234
|
|
|
235
|
+
function mergeAppAndExternalIntegration(variables, appName, systemFileName, datasourceFileNames, systemConfig) {
|
|
236
|
+
if (!variables.app) {
|
|
237
|
+
variables.app = {
|
|
238
|
+
key: appName,
|
|
239
|
+
displayName: systemConfig.displayName || appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase()),
|
|
240
|
+
description: systemConfig.description || `External system integration for ${appName}`,
|
|
241
|
+
type: 'external',
|
|
242
|
+
version: '1.0.0'
|
|
243
|
+
};
|
|
244
|
+
} else {
|
|
245
|
+
variables.app.version = variables.app.version || '1.0.0';
|
|
246
|
+
}
|
|
247
|
+
if (!variables.deployment) {
|
|
248
|
+
variables.deployment = { controllerUrl: '', environment: 'dev' };
|
|
249
|
+
}
|
|
250
|
+
variables.externalIntegration = {
|
|
251
|
+
schemaBasePath: './',
|
|
252
|
+
systems: [systemFileName],
|
|
253
|
+
dataSources: datasourceFileNames,
|
|
254
|
+
autopublish: true,
|
|
255
|
+
version: systemConfig.version || '1.0.0'
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
193
259
|
/**
|
|
194
|
-
* Generate or update application
|
|
260
|
+
* Generate or update application config with externalIntegration block
|
|
195
261
|
* @async
|
|
196
262
|
* @function generateOrUpdateVariablesYaml
|
|
197
263
|
* @param {Object} params - Parameters object
|
|
198
264
|
* @param {string} params.appPath - Application directory path
|
|
199
265
|
* @param {string} params.appName - Application name
|
|
200
|
-
* @param {string} params.systemKey - System key
|
|
201
266
|
* @param {string} params.systemFileName - System file name
|
|
202
267
|
* @param {string[]} params.datasourceFileNames - Array of datasource file names
|
|
203
268
|
* @param {Object} params.systemConfig - System configuration
|
|
269
|
+
* @param {string} [params.format] - Output format: 'yaml' (default) or 'json'
|
|
204
270
|
* @returns {Promise<string>} Path to application config file
|
|
205
271
|
* @throws {Error} If generation fails
|
|
206
272
|
*/
|
|
207
273
|
async function generateOrUpdateVariablesYaml(params) {
|
|
208
|
-
const { appPath, appName, systemFileName, datasourceFileNames, systemConfig } = params;
|
|
274
|
+
const { appPath, appName, systemFileName, datasourceFileNames, systemConfig, format } = params;
|
|
275
|
+
const fmt = format === 'json' ? 'json' : 'yaml';
|
|
276
|
+
const ext = FORMAT_EXT[fmt] || '.yaml';
|
|
209
277
|
let configPath;
|
|
210
278
|
let variables = {};
|
|
211
279
|
try {
|
|
@@ -213,115 +281,100 @@ async function generateOrUpdateVariablesYaml(params) {
|
|
|
213
281
|
configPath = resolveApplicationConfigPath(appPath);
|
|
214
282
|
variables = loadConfigFile(configPath) || {};
|
|
215
283
|
} catch {
|
|
216
|
-
configPath = path.join(appPath,
|
|
284
|
+
configPath = path.join(appPath, `application${ext}`);
|
|
217
285
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
description: systemConfig.description || `External system integration for ${appName}`,
|
|
225
|
-
type: 'external',
|
|
226
|
-
version: '1.0.0'
|
|
227
|
-
};
|
|
228
|
-
} else {
|
|
229
|
-
variables.app.version = variables.app.version || '1.0.0';
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
// Set deployment config if not present
|
|
233
|
-
if (!variables.deployment) {
|
|
234
|
-
variables.deployment = {
|
|
235
|
-
controllerUrl: '',
|
|
236
|
-
environment: 'dev'
|
|
237
|
-
};
|
|
286
|
+
mergeAppAndExternalIntegration(variables, appName, systemFileName, datasourceFileNames, systemConfig);
|
|
287
|
+
const targetPath = path.join(appPath, `application${ext}`);
|
|
288
|
+
writeConfigFile(targetPath, variables, fmt);
|
|
289
|
+
if (path.normalize(configPath) !== path.normalize(targetPath)) {
|
|
290
|
+
const fsSync = require('fs');
|
|
291
|
+
if (fsSync.existsSync(configPath)) fsSync.unlinkSync(configPath);
|
|
238
292
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
variables.externalIntegration = {
|
|
242
|
-
schemaBasePath: './',
|
|
243
|
-
systems: [systemFileName],
|
|
244
|
-
dataSources: datasourceFileNames,
|
|
245
|
-
autopublish: true,
|
|
246
|
-
version: systemConfig.version || '1.0.0'
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
writeConfigFile(configPath, variables);
|
|
250
|
-
logger.log(chalk.green('✓ Generated/updated application.yaml'));
|
|
251
|
-
return configPath;
|
|
293
|
+
logger.log(chalk.green(`✓ Generated/updated application${ext}`));
|
|
294
|
+
return targetPath;
|
|
252
295
|
} catch (error) {
|
|
253
296
|
throw new Error(`Failed to generate application config: ${error.message}`);
|
|
254
297
|
}
|
|
255
298
|
}
|
|
256
299
|
|
|
257
|
-
/**
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
* @param {Array<string>} lines - Lines array to append to
|
|
261
|
-
*/
|
|
262
|
-
function addApiKeyAuthLines(lines) {
|
|
263
|
-
lines.push('# API Key Authentication');
|
|
264
|
-
lines.push('API_KEY=kv://secrets/api-key');
|
|
265
|
-
lines.push('');
|
|
300
|
+
/** Path-style kv value: kv://systemKey/key (camelCase key). */
|
|
301
|
+
function toPathStyleKv(systemKey, key) {
|
|
302
|
+
return `kv://${systemKey}/${key}`;
|
|
266
303
|
}
|
|
267
304
|
|
|
268
305
|
/**
|
|
269
|
-
* Adds
|
|
270
|
-
* @function addOAuth2AuthLines
|
|
306
|
+
* Adds env.template lines from authentication.security (path-style kv:// values).
|
|
271
307
|
* @param {Array<string>} lines - Lines array to append to
|
|
272
|
-
* @param {Object}
|
|
308
|
+
* @param {Object} security - authentication.security object
|
|
309
|
+
* @param {string} systemKey - System key for path namespace
|
|
310
|
+
* @param {string} prefix - KV prefix (e.g. HUBSPOT)
|
|
273
311
|
*/
|
|
274
|
-
function
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
312
|
+
function addLinesFromSecurity(lines, security, systemKey, prefix) {
|
|
313
|
+
if (!security || typeof security !== 'object') return;
|
|
314
|
+
for (const key of Object.keys(security)) {
|
|
315
|
+
const envName = `KV_${prefix}_${securityKeyToVar(key)}`;
|
|
316
|
+
let value = security[key];
|
|
317
|
+
if (typeof value === 'string' && value.trim().startsWith('kv://') && isValidKvPath(value.trim())) {
|
|
318
|
+
value = value.trim();
|
|
319
|
+
} else {
|
|
320
|
+
value = toPathStyleKv(systemKey, key);
|
|
321
|
+
}
|
|
322
|
+
lines.push(`${envName}=${value}`);
|
|
282
323
|
}
|
|
283
|
-
lines.push('');
|
|
324
|
+
if (Object.keys(security).length > 0) lines.push('');
|
|
284
325
|
}
|
|
285
326
|
|
|
286
|
-
/**
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
327
|
+
/** Fallback security keys by auth method (camelCase) for path-style kv://. */
|
|
328
|
+
const FALLBACK_SECURITY_BY_AUTH = {
|
|
329
|
+
oauth2: ['clientId', 'clientSecret'],
|
|
330
|
+
oauth: ['clientId', 'clientSecret'],
|
|
331
|
+
aad: ['clientId', 'clientSecret'],
|
|
332
|
+
apikey: ['apiKey'],
|
|
333
|
+
apiKey: ['apiKey'],
|
|
334
|
+
basic: ['username', 'password'],
|
|
335
|
+
queryParam: ['paramValue'],
|
|
336
|
+
oidc: [],
|
|
337
|
+
hmac: ['signingSecret'],
|
|
338
|
+
bearer: ['bearerToken'],
|
|
339
|
+
token: ['bearerToken'],
|
|
340
|
+
none: []
|
|
341
|
+
};
|
|
296
342
|
|
|
297
343
|
/**
|
|
298
|
-
* Adds
|
|
299
|
-
* @function addBasicAuthLines
|
|
344
|
+
* Adds auth lines by type with path-style kv:// values (fallback when security is absent).
|
|
300
345
|
* @param {Array<string>} lines - Lines array to append to
|
|
346
|
+
* @param {string} authType - Authentication type
|
|
347
|
+
* @param {string} systemKey - System key
|
|
348
|
+
* @param {string} prefix - KV prefix
|
|
301
349
|
*/
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
350
|
+
function addFallbackAuthLines(lines, authType, systemKey, prefix) {
|
|
351
|
+
const keys = FALLBACK_SECURITY_BY_AUTH[authType] || FALLBACK_SECURITY_BY_AUTH.apikey;
|
|
352
|
+
if (keys.length === 0) return;
|
|
353
|
+
const labels = { oauth2: 'OAuth2', aad: 'Azure AD', apikey: 'API Key', basic: 'Basic', queryParam: 'Query Param', hmac: 'HMAC', bearer: 'Bearer Token' };
|
|
354
|
+
const label = labels[authType] || authType;
|
|
355
|
+
lines.push(`# ${label} Authentication`);
|
|
356
|
+
for (const key of keys) {
|
|
357
|
+
lines.push(`KV_${prefix}_${securityKeyToVar(key)}=${toPathStyleKv(systemKey, key)}`);
|
|
358
|
+
}
|
|
306
359
|
lines.push('');
|
|
307
360
|
}
|
|
308
361
|
|
|
309
362
|
/**
|
|
310
|
-
* Adds authentication lines
|
|
311
|
-
* @function addAuthenticationLines
|
|
363
|
+
* Adds authentication lines: from authentication.security when present, else by auth type with path-style kv://.
|
|
312
364
|
* @param {Array<string>} lines - Lines array to append to
|
|
313
|
-
* @param {Object} auth - Authentication configuration
|
|
314
|
-
* @param {string} authType -
|
|
365
|
+
* @param {Object} auth - Authentication configuration (may have security, type/method)
|
|
366
|
+
* @param {string} authType - Normalized auth type
|
|
367
|
+
* @param {string} systemKey - System key for KV path namespace
|
|
315
368
|
*/
|
|
316
|
-
function addAuthenticationLines(lines, auth, authType) {
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
} else
|
|
324
|
-
|
|
369
|
+
function addAuthenticationLines(lines, auth, authType, systemKey) {
|
|
370
|
+
const prefix = systemKeyToKvPrefix(systemKey);
|
|
371
|
+
if (!prefix) return;
|
|
372
|
+
const security = auth?.security;
|
|
373
|
+
if (security && typeof security === 'object' && Object.keys(security).length > 0) {
|
|
374
|
+
lines.push('# Authentication (from security)');
|
|
375
|
+
addLinesFromSecurity(lines, security, systemKey, prefix);
|
|
376
|
+
} else {
|
|
377
|
+
addFallbackAuthLines(lines, authType, systemKey, prefix);
|
|
325
378
|
}
|
|
326
379
|
}
|
|
327
380
|
|
|
@@ -340,23 +393,24 @@ function addBaseUrlLines(lines, systemConfig) {
|
|
|
340
393
|
}
|
|
341
394
|
|
|
342
395
|
/**
|
|
343
|
-
* Generate env.template with authentication variables
|
|
396
|
+
* Generate env.template with KV_* authentication variables
|
|
344
397
|
* @async
|
|
345
398
|
* @function generateEnvTemplate
|
|
346
399
|
* @param {string} appPath - Application directory path
|
|
347
|
-
* @param {Object} systemConfig - System configuration
|
|
400
|
+
* @param {Object} systemConfig - System configuration (must have key for systemKey)
|
|
401
|
+
* @param {string} [finalSystemKey] - Final system key for KV_ prefix (default: systemConfig.key)
|
|
348
402
|
* @throws {Error} If generation fails
|
|
349
403
|
*/
|
|
350
|
-
async function generateEnvTemplate(appPath, systemConfig) {
|
|
404
|
+
async function generateEnvTemplate(appPath, systemConfig, finalSystemKey) {
|
|
351
405
|
try {
|
|
352
406
|
const envTemplatePath = path.join(appPath, 'env.template');
|
|
353
|
-
const
|
|
407
|
+
const systemKey = finalSystemKey || systemConfig?.key;
|
|
408
|
+
const lines = ['# Environment variables for external system integration', '# Use KV_* for credential push (aifabrix credential push)', ''];
|
|
354
409
|
|
|
355
|
-
|
|
356
|
-
const
|
|
357
|
-
const authType = auth.type || auth.authType || 'apikey';
|
|
410
|
+
const auth = systemConfig?.authentication || systemConfig?.auth || {};
|
|
411
|
+
const authType = (auth.type || auth.method || auth.authType || 'apikey').toLowerCase();
|
|
358
412
|
|
|
359
|
-
addAuthenticationLines(lines, auth, authType);
|
|
413
|
+
addAuthenticationLines(lines, auth, authType, systemKey);
|
|
360
414
|
addBaseUrlLines(lines, systemConfig);
|
|
361
415
|
|
|
362
416
|
await fs.writeFile(envTemplatePath, lines.join('\n'), 'utf8');
|
|
@@ -401,59 +455,6 @@ async function generateDeployScripts(appPath, systemKey, systemFileName, datasou
|
|
|
401
455
|
}
|
|
402
456
|
}
|
|
403
457
|
|
|
404
|
-
/**
|
|
405
|
-
* Generate README.md with basic documentation
|
|
406
|
-
* @async
|
|
407
|
-
* @function generateReadme
|
|
408
|
-
* @param {string} appPath - Application directory path
|
|
409
|
-
* @param {string} appName - Application name
|
|
410
|
-
* @param {string} systemKey - System key
|
|
411
|
-
* @param {Object} systemConfig - System configuration
|
|
412
|
-
* @param {Object[]} datasourceConfigs - Array of datasource configurations
|
|
413
|
-
* @param {string} [aiGeneratedContent] - Optional AI-generated README content from dataplane
|
|
414
|
-
* @throws {Error} If generation fails
|
|
415
|
-
*/
|
|
416
|
-
async function generateReadme(appPath, appName, systemKey, systemConfig, datasourceConfigs, aiGeneratedContent) {
|
|
417
|
-
try {
|
|
418
|
-
const readmePath = path.join(appPath, 'README.md');
|
|
419
|
-
|
|
420
|
-
// Use AI-generated content if available, otherwise generate basic README
|
|
421
|
-
if (aiGeneratedContent) {
|
|
422
|
-
await fs.writeFile(readmePath, aiGeneratedContent, 'utf8');
|
|
423
|
-
logger.log(chalk.green('✓ Generated README.md (AI-generated from dataplane)'));
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
const datasources = (Array.isArray(datasourceConfigs) ? datasourceConfigs : []).map((ds, index) => {
|
|
428
|
-
const entityType = ds.entityType || ds.entityKey || ds.key?.split('-').pop() || `datasource${index + 1}`;
|
|
429
|
-
const keySegment = toKeySegment(entityType);
|
|
430
|
-
const datasourceKey = ds.key || `${systemKey}-${keySegment}`;
|
|
431
|
-
const datasourceKeyOnly = datasourceKey.includes('-') && datasourceKey.startsWith(`${systemKey}-`)
|
|
432
|
-
? datasourceKey.substring(systemKey.length + 1)
|
|
433
|
-
: keySegment;
|
|
434
|
-
return {
|
|
435
|
-
entityType,
|
|
436
|
-
displayName: ds.displayName || ds.name || ds.key || `Datasource ${index + 1}`,
|
|
437
|
-
fileName: `${systemKey}-datasource-${datasourceKeyOnly}.yaml`
|
|
438
|
-
};
|
|
439
|
-
});
|
|
440
|
-
|
|
441
|
-
const readmeContent = generateExternalReadmeContent({
|
|
442
|
-
appName,
|
|
443
|
-
systemKey,
|
|
444
|
-
systemType: systemConfig.type || systemConfig.systemType,
|
|
445
|
-
displayName: systemConfig.displayName,
|
|
446
|
-
description: systemConfig.description,
|
|
447
|
-
datasources
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
await fs.writeFile(readmePath, readmeContent, 'utf8');
|
|
451
|
-
logger.log(chalk.green('✓ Generated README.md (template)'));
|
|
452
|
-
} catch (error) {
|
|
453
|
-
throw new Error(`Failed to generate README.md: ${error.message}`);
|
|
454
|
-
}
|
|
455
|
-
}
|
|
456
|
-
|
|
457
458
|
module.exports = {
|
|
458
459
|
generateWizardFiles,
|
|
459
460
|
generateDeployScripts
|
|
@@ -63,6 +63,8 @@ function validateTraefikConfig(traefikConfig) {
|
|
|
63
63
|
* @param {string} infraDir - Infrastructure directory
|
|
64
64
|
* @param {Object} [options] - Additional options
|
|
65
65
|
* @param {Object|boolean} [options.traefik] - Traefik configuration
|
|
66
|
+
* @param {Object} [options.pgadmin] - pgAdmin config { enabled: boolean }
|
|
67
|
+
* @param {Object} [options.redisCommander] - Redis Commander config { enabled: boolean }
|
|
66
68
|
* @returns {string} Path to generated compose file
|
|
67
69
|
*/
|
|
68
70
|
function generateComposeFile(templatePath, devId, idNum, ports, infraDir, options = {}) {
|
|
@@ -74,6 +76,12 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
|
|
|
74
76
|
const traefikConfig = typeof options.traefik === 'object'
|
|
75
77
|
? options.traefik
|
|
76
78
|
: buildTraefikConfig(!!options.traefik);
|
|
79
|
+
const pgadminConfig = options.pgadmin && typeof options.pgadmin.enabled === 'boolean'
|
|
80
|
+
? options.pgadmin
|
|
81
|
+
: { enabled: true };
|
|
82
|
+
const redisCommanderConfig = options.redisCommander && typeof options.redisCommander.enabled === 'boolean'
|
|
83
|
+
? options.redisCommander
|
|
84
|
+
: { enabled: true };
|
|
77
85
|
const composeContent = template({
|
|
78
86
|
devId: devId,
|
|
79
87
|
postgresPort: ports.postgres,
|
|
@@ -86,7 +94,9 @@ function generateComposeFile(templatePath, devId, idNum, ports, infraDir, option
|
|
|
86
94
|
serversJsonPath: serversJsonPath,
|
|
87
95
|
pgpassPath: pgpassPath,
|
|
88
96
|
infraDir: infraDir,
|
|
89
|
-
traefik: traefikConfig
|
|
97
|
+
traefik: traefikConfig,
|
|
98
|
+
pgadmin: pgadminConfig,
|
|
99
|
+
redisCommander: redisCommanderConfig
|
|
90
100
|
});
|
|
91
101
|
const composePath = path.join(infraDir, 'compose.yaml');
|
|
92
102
|
fs.writeFileSync(composePath, composeContent);
|
|
@@ -89,7 +89,7 @@ async function prepareInfrastructureEnvironment(developerId, options = {}) {
|
|
|
89
89
|
*/
|
|
90
90
|
async function startInfra(developerId = null, options = {}) {
|
|
91
91
|
const { devId, idNum, ports, templatePath, infraDir } = await prepareInfrastructureEnvironment(developerId, options);
|
|
92
|
-
const { traefik = false } = options;
|
|
92
|
+
const { traefik = false, pgadmin = true, redisCommander = true } = options;
|
|
93
93
|
const traefikConfig = buildTraefikConfig(traefik);
|
|
94
94
|
const validation = validateTraefikConfig(traefikConfig);
|
|
95
95
|
if (!validation.valid) {
|
|
@@ -100,10 +100,18 @@ async function startInfra(developerId = null, options = {}) {
|
|
|
100
100
|
registerHandlebarsHelper();
|
|
101
101
|
|
|
102
102
|
// Generate compose file
|
|
103
|
-
const composePath = generateComposeFile(templatePath, devId, idNum, ports, infraDir, {
|
|
103
|
+
const composePath = generateComposeFile(templatePath, devId, idNum, ports, infraDir, {
|
|
104
|
+
traefik: traefikConfig,
|
|
105
|
+
pgadmin: { enabled: !!pgadmin },
|
|
106
|
+
redisCommander: { enabled: !!redisCommander }
|
|
107
|
+
});
|
|
104
108
|
|
|
105
109
|
try {
|
|
106
|
-
await startDockerServicesAndConfigure(composePath, devId, idNum, infraDir
|
|
110
|
+
await startDockerServicesAndConfigure(composePath, devId, idNum, infraDir, {
|
|
111
|
+
pgadmin: !!pgadmin,
|
|
112
|
+
redisCommander: !!redisCommander,
|
|
113
|
+
traefik: !!traefik
|
|
114
|
+
});
|
|
107
115
|
} finally {
|
|
108
116
|
// Keep the compose file for stop commands
|
|
109
117
|
}
|