@aifabrix/builder 2.44.2 → 2.44.4
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/.npmrc.token +1 -1
- package/integration/roundtrip-test-local/README.md +1 -2
- package/integration/roundtrip-test-local2/README.md +1 -2
- package/jest.projects.js +12 -1
- package/lib/api/certificates.api.js +21 -3
- package/lib/api/types/certificates.types.js +1 -1
- package/lib/app/show-display.js +60 -16
- package/lib/certification/merge-certification-from-artifact.js +46 -16
- package/lib/certification/post-unified-cert-sync.js +13 -2
- package/lib/certification/sync-after-external-command.js +6 -3
- package/lib/certification/sync-system-certification.js +60 -14
- package/lib/cli/setup-app.test-commands.js +67 -35
- package/lib/cli/setup-utility.js +1 -1
- package/lib/commands/datasource-unified-test-cli.js +81 -46
- package/lib/commands/datasource-unified-test-cli.options.js +4 -2
- package/lib/commands/datasource.js +3 -31
- package/lib/commands/repair-datasource-keys.js +1 -1
- package/lib/commands/repair-datasource-openapi.js +57 -0
- package/lib/commands/repair-datasource.js +5 -0
- package/lib/commands/repair-internal.js +2 -4
- package/lib/commands/repair.js +1 -2
- package/lib/commands/test-e2e-external.js +5 -6
- package/lib/commands/upload.js +18 -4
- package/lib/commands/wizard-dataplane.js +14 -6
- package/lib/datasource/datasource-validate-display.js +162 -0
- package/lib/datasource/datasource-validate-summary.js +194 -0
- package/lib/datasource/test-e2e.js +65 -37
- package/lib/datasource/unified-validation-run-body.js +1 -2
- package/lib/datasource/validate.js +14 -6
- package/lib/external-system/test.js +12 -8
- package/lib/generator/external-controller-manifest.js +12 -2
- package/lib/schema/cip-capacity-display.fallback.json +7 -0
- package/lib/schema/datasource-test-run.schema.json +79 -1
- package/lib/schema/external-datasource.schema.json +94 -2
- package/lib/schema/external-system.schema.json +29 -6
- package/lib/schema/flag-map-validation-run.json +1 -2
- package/lib/schema/type/document-storage.json +83 -3
- package/lib/utils/configuration-env-resolver.js +38 -0
- package/lib/utils/dataplane-resolver.js +3 -2
- package/lib/utils/datasource-test-run-capacity-operations.js +149 -0
- package/lib/utils/datasource-test-run-debug-display.js +143 -1
- package/lib/utils/datasource-test-run-display.js +46 -33
- package/lib/utils/datasource-test-run-tty-log.js +6 -2
- package/lib/utils/datasource-test-run-tty-meta-lines.js +123 -0
- package/lib/utils/error-formatter.js +32 -2
- package/lib/utils/external-system-readiness-core.js +39 -0
- package/lib/utils/external-system-readiness-deploy-display.js +2 -3
- package/lib/utils/external-system-readiness-display-internals.js +3 -2
- package/lib/utils/external-system-system-test-tty.js +33 -9
- package/lib/utils/external-system-validators.js +62 -5
- package/lib/utils/load-cip-capacity-display-config.js +130 -0
- package/lib/utils/paths.js +10 -3
- package/lib/utils/schema-resolver.js +98 -2
- package/lib/utils/validation-run-poll.js +15 -4
- package/lib/utils/validation-run-request.js +4 -6
- package/lib/validation/dimension-display-helpers.js +60 -0
- package/lib/validation/external-manifest-validator.js +22 -15
- package/lib/validation/validate-display-log-helpers.js +39 -0
- package/lib/validation/validate-display.js +89 -83
- package/package.json +1 -1
- package/templates/applications/dataplane/env.template +2 -1
- package/templates/external-system/README.md.hbs +1 -2
package/lib/cli/setup-utility.js
CHANGED
|
@@ -118,7 +118,7 @@ function setupResolveCommand(program) {
|
|
|
118
118
|
);
|
|
119
119
|
logger.log(`✔ Generated .env file: ${envPath}`);
|
|
120
120
|
if (envOnly) {
|
|
121
|
-
logger.log(chalk.gray(' (env-only mode: validation skipped; no application
|
|
121
|
+
logger.log(chalk.gray(' (env-only mode: validation skipped; no application config file)'));
|
|
122
122
|
} else if (!options.skipValidation) {
|
|
123
123
|
const validate = require('../validation/validate');
|
|
124
124
|
const result = await validate.validateAppOrFile(appName);
|
|
@@ -94,7 +94,7 @@ async function runDatasourceUnifiedTestOnceForWatch(datasourceKey, runOpts, disp
|
|
|
94
94
|
try {
|
|
95
95
|
const result = await runUnifiedDatasourceValidation(datasourceKey, runOpts);
|
|
96
96
|
const exitCode = finalizeUnifiedValidationResult(result, displayOpts);
|
|
97
|
-
await afterUnifiedValidationCertSync(exitCode, datasourceKey, certCliOptions, 'datasource test');
|
|
97
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, certCliOptions, 'datasource test', result.envelope);
|
|
98
98
|
return {
|
|
99
99
|
exitCode,
|
|
100
100
|
envelope: result.envelope
|
|
@@ -121,7 +121,7 @@ async function datasourceTestCommandAction(datasourceKey, options) {
|
|
|
121
121
|
const result = await runUnifiedDatasourceValidation(datasourceKey, runOpts);
|
|
122
122
|
await writeDatasourceTestDebugLogIfRequested(appKey, datasourceKey, result, options);
|
|
123
123
|
const exitCode = finalizeUnifiedValidationResult(result, displayOpts);
|
|
124
|
-
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test (watch)');
|
|
124
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test (watch)', result.envelope);
|
|
125
125
|
return {
|
|
126
126
|
exitCode,
|
|
127
127
|
envelope: result.envelope
|
|
@@ -140,7 +140,7 @@ async function datasourceTestCommandAction(datasourceKey, options) {
|
|
|
140
140
|
}
|
|
141
141
|
}
|
|
142
142
|
const exitCode = finalizeUnifiedValidationResult(result, displayOpts);
|
|
143
|
-
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test');
|
|
143
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test', result.envelope);
|
|
144
144
|
process.exit(exitCode);
|
|
145
145
|
} catch (error) {
|
|
146
146
|
logger.error(formatBlockingError('Datasource test failed:'), error.message);
|
|
@@ -195,7 +195,13 @@ async function runIntegrationOnceForWatch(datasourceKey, integOpts, options, uni
|
|
|
195
195
|
if (unifiedModes) {
|
|
196
196
|
const uni = unifiedCliResultFromIntegrationReturn(result);
|
|
197
197
|
const exitCode = finalizeUnifiedValidationResult(uni, unifiedDisplayOpts);
|
|
198
|
-
await afterUnifiedValidationCertSync(
|
|
198
|
+
await afterUnifiedValidationCertSync(
|
|
199
|
+
exitCode,
|
|
200
|
+
datasourceKey,
|
|
201
|
+
options,
|
|
202
|
+
'datasource test-integration (watch)',
|
|
203
|
+
uni.envelope
|
|
204
|
+
);
|
|
199
205
|
return { exitCode, envelope: uni.envelope };
|
|
200
206
|
}
|
|
201
207
|
displayIntegrationTestResults(
|
|
@@ -211,7 +217,13 @@ async function runIntegrationOnceForWatch(datasourceKey, integOpts, options, uni
|
|
|
211
217
|
warningsAsErrors: unifiedDisplayOpts.warningsAsErrors === true,
|
|
212
218
|
requireCert: unifiedDisplayOpts.requireCert === true
|
|
213
219
|
});
|
|
214
|
-
await afterUnifiedValidationCertSync(
|
|
220
|
+
await afterUnifiedValidationCertSync(
|
|
221
|
+
exitCode,
|
|
222
|
+
datasourceKey,
|
|
223
|
+
options,
|
|
224
|
+
'datasource test-integration (watch)',
|
|
225
|
+
result.datasourceTestRun || null
|
|
226
|
+
);
|
|
215
227
|
return { exitCode, envelope: result.datasourceTestRun || null };
|
|
216
228
|
} catch (err) {
|
|
217
229
|
logger.error(formatBlockingError('Integration test failed:'), err.message);
|
|
@@ -219,49 +231,67 @@ async function runIntegrationOnceForWatch(datasourceKey, integOpts, options, uni
|
|
|
219
231
|
}
|
|
220
232
|
}
|
|
221
233
|
|
|
234
|
+
async function integrationTestCommandActionWatch(datasourceKey, options, integOpts, unifiedDisplayOpts) {
|
|
235
|
+
const { appKey } = await resolveAppKeyForDatasource(datasourceKey, options.app);
|
|
236
|
+
await runDatasourceValidationWatchLoop({
|
|
237
|
+
appKey,
|
|
238
|
+
extraPaths: options.watchPath || [],
|
|
239
|
+
includeApplicationYaml: options.watchApplicationYaml === true,
|
|
240
|
+
watchCi: options.watchCi === true,
|
|
241
|
+
watchFullDiff: options.watchFullDiff === true,
|
|
242
|
+
runOnce: () => runIntegrationOnceForWatch(datasourceKey, integOpts, options, unifiedDisplayOpts)
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async function integrationTestCommandActionNonWatch(datasourceKey, integOpts, options, unifiedDisplayOpts) {
|
|
247
|
+
const result = await runDatasourceTestIntegration(datasourceKey, integOpts);
|
|
248
|
+
const unifiedModes =
|
|
249
|
+
options.json || options.summary || options.warningsAsErrors || options.requireCert;
|
|
250
|
+
if (unifiedModes) {
|
|
251
|
+
const uni = unifiedCliResultFromIntegrationReturn(result);
|
|
252
|
+
const exitCode = finalizeUnifiedValidationResult(uni, unifiedDisplayOpts);
|
|
253
|
+
await afterUnifiedValidationCertSync(
|
|
254
|
+
exitCode,
|
|
255
|
+
datasourceKey,
|
|
256
|
+
options,
|
|
257
|
+
'datasource test-integration',
|
|
258
|
+
uni.envelope
|
|
259
|
+
);
|
|
260
|
+
process.exit(exitCode);
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
displayIntegrationTestResults(
|
|
264
|
+
{
|
|
265
|
+
systemKey: result.systemKey || 'unknown',
|
|
266
|
+
datasourceResults: [result],
|
|
267
|
+
success: result.success
|
|
268
|
+
},
|
|
269
|
+
options.verbose,
|
|
270
|
+
{ debug: options.debug, runType: 'integration' }
|
|
271
|
+
);
|
|
272
|
+
const integExit = finalizeAfterIntegrationDisplay(result, {
|
|
273
|
+
warningsAsErrors: unifiedDisplayOpts.warningsAsErrors === true,
|
|
274
|
+
requireCert: unifiedDisplayOpts.requireCert === true
|
|
275
|
+
});
|
|
276
|
+
await afterUnifiedValidationCertSync(
|
|
277
|
+
integExit,
|
|
278
|
+
datasourceKey,
|
|
279
|
+
options,
|
|
280
|
+
'datasource test-integration',
|
|
281
|
+
result.datasourceTestRun || null
|
|
282
|
+
);
|
|
283
|
+
process.exit(integExit);
|
|
284
|
+
}
|
|
285
|
+
|
|
222
286
|
async function integrationTestCommandAction(datasourceKey, options) {
|
|
223
287
|
const integOpts = buildIntegrationTestOpts(options);
|
|
224
288
|
const unifiedDisplayOpts = buildIntegrationUnifiedDisplayOpts(options);
|
|
225
289
|
try {
|
|
226
290
|
if (options.watch) {
|
|
227
|
-
|
|
228
|
-
await runDatasourceValidationWatchLoop({
|
|
229
|
-
appKey,
|
|
230
|
-
extraPaths: options.watchPath || [],
|
|
231
|
-
includeApplicationYaml: options.watchApplicationYaml === true,
|
|
232
|
-
watchCi: options.watchCi === true,
|
|
233
|
-
watchFullDiff: options.watchFullDiff === true,
|
|
234
|
-
runOnce: () => runIntegrationOnceForWatch(datasourceKey, integOpts, options, unifiedDisplayOpts)
|
|
235
|
-
});
|
|
236
|
-
return;
|
|
237
|
-
}
|
|
238
|
-
const result = await runDatasourceTestIntegration(datasourceKey, integOpts);
|
|
239
|
-
const unifiedModes =
|
|
240
|
-
options.json || options.summary || options.warningsAsErrors || options.requireCert;
|
|
241
|
-
if (unifiedModes) {
|
|
242
|
-
const exitCode = finalizeUnifiedValidationResult(
|
|
243
|
-
unifiedCliResultFromIntegrationReturn(result),
|
|
244
|
-
unifiedDisplayOpts
|
|
245
|
-
);
|
|
246
|
-
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-integration');
|
|
247
|
-
process.exit(exitCode);
|
|
291
|
+
await integrationTestCommandActionWatch(datasourceKey, options, integOpts, unifiedDisplayOpts);
|
|
248
292
|
return;
|
|
249
293
|
}
|
|
250
|
-
|
|
251
|
-
{
|
|
252
|
-
systemKey: result.systemKey || 'unknown',
|
|
253
|
-
datasourceResults: [result],
|
|
254
|
-
success: result.success
|
|
255
|
-
},
|
|
256
|
-
options.verbose,
|
|
257
|
-
{ debug: options.debug, runType: 'integration' }
|
|
258
|
-
);
|
|
259
|
-
const integExit = finalizeAfterIntegrationDisplay(result, {
|
|
260
|
-
warningsAsErrors: unifiedDisplayOpts.warningsAsErrors === true,
|
|
261
|
-
requireCert: unifiedDisplayOpts.requireCert === true
|
|
262
|
-
});
|
|
263
|
-
await afterUnifiedValidationCertSync(integExit, datasourceKey, options, 'datasource test-integration');
|
|
264
|
-
process.exit(integExit);
|
|
294
|
+
await integrationTestCommandActionNonWatch(datasourceKey, integOpts, options, unifiedDisplayOpts);
|
|
265
295
|
} catch (error) {
|
|
266
296
|
logger.error(formatBlockingError('Integration test failed:'), error.message);
|
|
267
297
|
process.exit(4);
|
|
@@ -296,9 +326,8 @@ async function runDatasourceTestE2ECliOnce(datasourceKey, options) {
|
|
|
296
326
|
debug: options.debug,
|
|
297
327
|
verbose: options.verbose,
|
|
298
328
|
async: options.async !== false,
|
|
299
|
-
testCrud: options.testCrud,
|
|
300
|
-
recordId: options.recordId,
|
|
301
329
|
cleanup: options.cleanup,
|
|
330
|
+
runScenarios: options.runScenarios,
|
|
302
331
|
primaryKeyValue: options.primaryKeyValue,
|
|
303
332
|
minVectorHits: options.minVectorHits,
|
|
304
333
|
minProcessed: options.minProcessed,
|
|
@@ -340,8 +369,8 @@ async function runDatasourceTestE2ECliOnce(datasourceKey, options) {
|
|
|
340
369
|
}
|
|
341
370
|
|
|
342
371
|
async function runDatasourceTestE2ECliAction(datasourceKey, options) {
|
|
343
|
-
const { exitCode } = await runDatasourceTestE2ECliOnce(datasourceKey, options);
|
|
344
|
-
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-e2e');
|
|
372
|
+
const { exitCode, envelope } = await runDatasourceTestE2ECliOnce(datasourceKey, options);
|
|
373
|
+
await afterUnifiedValidationCertSync(exitCode, datasourceKey, options, 'datasource test-e2e', envelope);
|
|
345
374
|
process.exit(exitCode);
|
|
346
375
|
}
|
|
347
376
|
|
|
@@ -371,7 +400,13 @@ async function e2eTestCommandAction(datasourceKey, capabilityKey, options) {
|
|
|
371
400
|
runOnce: async() => {
|
|
372
401
|
try {
|
|
373
402
|
const r = await runDatasourceTestE2ECliOnce(datasourceKey, mergedOptions);
|
|
374
|
-
await afterUnifiedValidationCertSync(
|
|
403
|
+
await afterUnifiedValidationCertSync(
|
|
404
|
+
r.exitCode,
|
|
405
|
+
datasourceKey,
|
|
406
|
+
mergedOptions,
|
|
407
|
+
'datasource test-e2e (watch)',
|
|
408
|
+
r.envelope
|
|
409
|
+
);
|
|
375
410
|
return r;
|
|
376
411
|
} catch (err) {
|
|
377
412
|
logger.error(formatBlockingError('E2E test failed:'), err.message);
|
|
@@ -166,8 +166,10 @@ function attachDatasourceTestE2eExclusiveOptions(cmd) {
|
|
|
166
166
|
'Minimum record count assertion (e2eOptions.minRecordCount)',
|
|
167
167
|
(v) => parseInt(String(v), 10)
|
|
168
168
|
)
|
|
169
|
-
.option(
|
|
170
|
-
|
|
169
|
+
.option(
|
|
170
|
+
'--no-run-scenarios',
|
|
171
|
+
'Do not expand testPayload.scenarios in capacity step (e2eOptions.runScenarios: false)'
|
|
172
|
+
)
|
|
171
173
|
.option('--no-cleanup', 'Disable cleanup after test (e2eOptions.cleanup: false)')
|
|
172
174
|
.option(
|
|
173
175
|
'--primary-key-value <value|@path>',
|
|
@@ -10,10 +10,10 @@
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
const path = require('path');
|
|
13
|
-
const chalk = require('chalk');
|
|
14
13
|
const logger = require('../utils/logger');
|
|
15
|
-
const {
|
|
14
|
+
const { formatBlockingError } = require('../utils/cli-test-layout-chalk');
|
|
16
15
|
const { validateDatasourceFile } = require('../datasource/validate');
|
|
16
|
+
const { logDatasourceValidateOutcome } = require('../datasource/datasource-validate-display');
|
|
17
17
|
const { listDatasources } = require('../datasource/list');
|
|
18
18
|
const { compareDatasources } = require('../datasource/diff');
|
|
19
19
|
const { deployDatasource } = require('../datasource/deploy');
|
|
@@ -53,34 +53,6 @@ Examples:
|
|
|
53
53
|
$ af ds upload ../integration/hubspot/hubspot-datasource-deals.json
|
|
54
54
|
`;
|
|
55
55
|
|
|
56
|
-
/**
|
|
57
|
-
* TTY layout for local datasource JSON validation (aligned with cli-test-layout-chalk).
|
|
58
|
-
* @param {{ valid: boolean, errors: string[], resolvedPath: string }} result
|
|
59
|
-
* @param {string} trimmed - original CLI argument
|
|
60
|
-
* @param {boolean} showMapping - show Key + File when key resolved to a path
|
|
61
|
-
*/
|
|
62
|
-
function logDatasourceValidateOutcome(result, trimmed, showMapping) {
|
|
63
|
-
logger.log('');
|
|
64
|
-
logger.log(sectionTitle('Datasource validation'));
|
|
65
|
-
logger.log(metadata('Offline — JSON schema and integration wiring'));
|
|
66
|
-
logger.log('');
|
|
67
|
-
if (!result.valid) {
|
|
68
|
-
logger.log(headerKeyValue('File:', result.resolvedPath));
|
|
69
|
-
logger.log('');
|
|
70
|
-
logger.log(formatBlockingError('Datasource file has errors:'));
|
|
71
|
-
result.errors.forEach(error => logger.log(chalk.red(` • ${error}`)));
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (showMapping) {
|
|
75
|
-
logger.log(headerKeyValue('Key:', trimmed));
|
|
76
|
-
logger.log(headerKeyValue('File:', result.resolvedPath));
|
|
77
|
-
} else {
|
|
78
|
-
logger.log(headerKeyValue('File:', result.resolvedPath));
|
|
79
|
-
}
|
|
80
|
-
logger.log('');
|
|
81
|
-
logger.log(formatSuccessLine('Datasource file is valid.'));
|
|
82
|
-
}
|
|
83
|
-
|
|
84
56
|
function setupDatasourceValidateCommand(datasource) {
|
|
85
57
|
datasource.command('validate <file-or-key>')
|
|
86
58
|
.description('Validate datasource JSON (file path or datasource key under integration/<app>/)')
|
|
@@ -97,7 +69,7 @@ function setupDatasourceValidateCommand(datasource) {
|
|
|
97
69
|
process.exit(1);
|
|
98
70
|
}
|
|
99
71
|
} catch (error) {
|
|
100
|
-
logger.error(formatBlockingError(
|
|
72
|
+
logger.error(formatBlockingError(`Validation failed: ${error.message}`));
|
|
101
73
|
process.exit(1);
|
|
102
74
|
}
|
|
103
75
|
});
|
|
@@ -193,7 +193,7 @@ function normalizeDatasourceKeysAndFilenames(appPath, datasourceFiles, systemKey
|
|
|
193
193
|
}
|
|
194
194
|
|
|
195
195
|
if (updated && variables.externalIntegration && Array.isArray(variables.externalIntegration.dataSources)) {
|
|
196
|
-
variables.externalIntegration.dataSources = [...newDatasourceFiles]
|
|
196
|
+
variables.externalIntegration.dataSources = [...newDatasourceFiles];
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
return { updated, datasourceFiles: newDatasourceFiles };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Repair OpenAPI block on external datasource JSON.
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
function hasNonEmptyObject(v) {
|
|
10
|
+
return !!v && typeof v === 'object' && !Array.isArray(v) && Object.keys(v).length > 0;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Repair `openapi` section:
|
|
15
|
+
* - If operations exist, ensure `openapi.enabled=true`
|
|
16
|
+
* - If enabled/operations and missing documentKey/fileId, default documentKey to datasource key (common convention)
|
|
17
|
+
* - If enabled+operations and autoRbac missing, default to true (wizard/fixtures expectation)
|
|
18
|
+
*
|
|
19
|
+
* @param {Object} parsed - Parsed datasource (mutated)
|
|
20
|
+
* @param {string[]} changes - Change log
|
|
21
|
+
* @returns {boolean} True if updated
|
|
22
|
+
*/
|
|
23
|
+
function repairOpenapiSection(parsed, changes) {
|
|
24
|
+
const openapi = parsed?.openapi;
|
|
25
|
+
if (!openapi || typeof openapi !== 'object') {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const hasOps = hasNonEmptyObject(openapi.operations);
|
|
30
|
+
const enabled = openapi.enabled === true;
|
|
31
|
+
let updated = false;
|
|
32
|
+
|
|
33
|
+
if (hasOps && openapi.enabled !== true) {
|
|
34
|
+
openapi.enabled = true;
|
|
35
|
+
changes.push('Set openapi.enabled=true (operations present)');
|
|
36
|
+
updated = true;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if ((enabled || hasOps) && !openapi.documentKey && !openapi.fileId) {
|
|
40
|
+
if (typeof parsed.key === 'string' && parsed.key.trim()) {
|
|
41
|
+
openapi.documentKey = parsed.key.trim();
|
|
42
|
+
changes.push(`Set openapi.documentKey=${openapi.documentKey}`);
|
|
43
|
+
updated = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if ((enabled || hasOps) && hasOps && openapi.autoRbac === undefined) {
|
|
48
|
+
openapi.autoRbac = true;
|
|
49
|
+
changes.push('Set openapi.autoRbac=true (enabled operations, default)');
|
|
50
|
+
updated = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return updated;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
module.exports = { repairOpenapiSection };
|
|
57
|
+
|
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
|
|
10
10
|
'use strict';
|
|
11
11
|
|
|
12
|
+
const { repairOpenapiSection } = require('./repair-datasource-openapi');
|
|
13
|
+
|
|
12
14
|
const DEFAULT_SYNC = {
|
|
13
15
|
mode: 'pull',
|
|
14
16
|
batchSize: 500
|
|
@@ -446,6 +448,8 @@ function repairDatasourceFile(parsed, options = {}, changes = []) {
|
|
|
446
448
|
updated = repairMetadataSchemaFromAttributes(parsed, out) || updated;
|
|
447
449
|
}
|
|
448
450
|
|
|
451
|
+
updated = repairOpenapiSection(parsed, out) || updated;
|
|
452
|
+
|
|
449
453
|
if (options.expose) {
|
|
450
454
|
updated = repairExposeFromAttributes(parsed, out) || updated;
|
|
451
455
|
}
|
|
@@ -472,6 +476,7 @@ module.exports = {
|
|
|
472
476
|
repairExposeFromAttributes,
|
|
473
477
|
repairSyncSection,
|
|
474
478
|
repairTestPayload,
|
|
479
|
+
repairOpenapiSection,
|
|
475
480
|
sanitizeTestPayloadTopLevel,
|
|
476
481
|
repairDatasourceFile,
|
|
477
482
|
DEFAULT_SYNC,
|
|
@@ -40,7 +40,6 @@ function discoverIntegrationFiles(appPath) {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
systemFiles.sort();
|
|
43
|
-
datasourceFiles.sort();
|
|
44
43
|
return { systemFiles, datasourceFiles };
|
|
45
44
|
}
|
|
46
45
|
|
|
@@ -48,8 +47,8 @@ function discoverIntegrationFiles(appPath) {
|
|
|
48
47
|
* Builds the effective datasource file list from application.yaml and discovered files.
|
|
49
48
|
* @param {string} appPath - Application directory path
|
|
50
49
|
* @param {string[]} discoveredDatasourceFiles - Filenames from discoverIntegrationFiles
|
|
51
|
-
* @param {string[]} [existingDataSources] - externalIntegration.dataSources from application
|
|
52
|
-
* @returns {string[]}
|
|
50
|
+
* @param {string[]} [existingDataSources] - externalIntegration.dataSources from application config
|
|
51
|
+
* @returns {string[]} Deduplicated list of datasource filenames (application order first, then remaining discovered files in directory order)
|
|
53
52
|
*/
|
|
54
53
|
function buildEffectiveDatasourceFiles(appPath, discoveredDatasourceFiles, existingDataSources) {
|
|
55
54
|
const existing = Array.isArray(existingDataSources) ? existingDataSources : [];
|
|
@@ -75,7 +74,6 @@ function buildEffectiveDatasourceFiles(appPath, discoveredDatasourceFiles, exist
|
|
|
75
74
|
seen.add(name);
|
|
76
75
|
}
|
|
77
76
|
}
|
|
78
|
-
result.sort();
|
|
79
77
|
return result;
|
|
80
78
|
}
|
|
81
79
|
|
package/lib/commands/repair.js
CHANGED
|
@@ -204,8 +204,7 @@ function alignSystemFileDataSources(appPath, systemParsed, datasourceFiles, syst
|
|
|
204
204
|
keys.push(deriveDatasourceKeyFromFileName(fileName, systemKey));
|
|
205
205
|
}
|
|
206
206
|
}
|
|
207
|
-
|
|
208
|
-
const prev = Array.isArray(systemParsed.dataSources) ? [...systemParsed.dataSources].sort() : [];
|
|
207
|
+
const prev = Array.isArray(systemParsed.dataSources) ? [...systemParsed.dataSources] : [];
|
|
209
208
|
if (JSON.stringify(prev) === JSON.stringify(keys)) return false;
|
|
210
209
|
systemParsed.dataSources = keys;
|
|
211
210
|
changes.push(`dataSources: [${prev.join(', ') || '(none)'}] → [${keys.join(', ')}] (keys from each datasource file's "key" or filename)`);
|
|
@@ -42,8 +42,8 @@ function deriveDatasourceKeyFromFileName(fileName, systemKey) {
|
|
|
42
42
|
* @param {Object} variables - Loaded application variables (externalIntegration.dataSources = filenames)
|
|
43
43
|
* @param {string} systemKey - System key from system file
|
|
44
44
|
* @param {Object} systemParsed - Parsed system config (may have dataSources array of keys)
|
|
45
|
-
* @param {string[]} datasourceFiles - Discovered datasource filenames
|
|
46
|
-
* @returns {string[]}
|
|
45
|
+
* @param {string[]} datasourceFiles - Discovered datasource filenames (application order when built via repair)
|
|
46
|
+
* @returns {string[]} Datasource keys in declaration order (system JSON or file list — never sort; FK targets must run first)
|
|
47
47
|
*/
|
|
48
48
|
function getDatasourceKeys(appPath, configPath, variables, systemKey, systemParsed, datasourceFiles) {
|
|
49
49
|
const fromSystem = Array.isArray(systemParsed.dataSources) && systemParsed.dataSources.length > 0
|
|
@@ -54,11 +54,11 @@ function getDatasourceKeys(appPath, configPath, variables, systemKey, systemPars
|
|
|
54
54
|
if (fromSystem) {
|
|
55
55
|
fromSystem.forEach(k => {
|
|
56
56
|
if (k && typeof k === 'string' && !seen.has(k)) {
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
const trimmed = k.trim();
|
|
58
|
+
keys.push(trimmed);
|
|
59
|
+
seen.add(trimmed);
|
|
59
60
|
}
|
|
60
61
|
});
|
|
61
|
-
keys.sort();
|
|
62
62
|
return keys;
|
|
63
63
|
}
|
|
64
64
|
for (const fileName of datasourceFiles) {
|
|
@@ -81,7 +81,6 @@ function getDatasourceKeys(appPath, configPath, variables, systemKey, systemPars
|
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
keys.sort();
|
|
85
84
|
return keys;
|
|
86
85
|
}
|
|
87
86
|
|
package/lib/commands/upload.js
CHANGED
|
@@ -17,7 +17,8 @@ const { getIntegrationPath } = require('../utils/paths');
|
|
|
17
17
|
const { pushCredentialSecrets } = require('../utils/credential-secrets-env');
|
|
18
18
|
const {
|
|
19
19
|
buildResolvedEnvMapForIntegration,
|
|
20
|
-
resolveConfigurationValues
|
|
20
|
+
resolveConfigurationValues,
|
|
21
|
+
collectResolvedVariableConfigurationNames
|
|
21
22
|
} = require('../utils/configuration-env-resolver');
|
|
22
23
|
const { validateExternalSystemComplete } = require('../validation/validate');
|
|
23
24
|
const { displayValidationResults } = require('../validation/validate-display');
|
|
@@ -70,9 +71,11 @@ function buildUploadPayload(manifest) {
|
|
|
70
71
|
/**
|
|
71
72
|
* Resolves dataplane URL and auth (same pattern as download).
|
|
72
73
|
* @param {string} systemKey - System key
|
|
74
|
+
* @param {{ silent?: boolean }} [opts] - silent: omit “Resolving dataplane URL…” and discovery success lines
|
|
73
75
|
* @returns {Promise<{ dataplaneUrl: string, authConfig: Object, environment: string }>}
|
|
74
76
|
*/
|
|
75
|
-
async function resolveDataplaneAndAuth(systemKey) {
|
|
77
|
+
async function resolveDataplaneAndAuth(systemKey, opts = {}) {
|
|
78
|
+
const silent = opts.silent === true;
|
|
76
79
|
const { resolveEnvironment } = require('../core/config');
|
|
77
80
|
const environment = await resolveEnvironment();
|
|
78
81
|
const controllerUrl = await resolveControllerUrl();
|
|
@@ -82,8 +85,10 @@ async function resolveDataplaneAndAuth(systemKey) {
|
|
|
82
85
|
throw new Error('Authentication required. Run "aifabrix login" or "aifabrix app register <systemKey>" first.');
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
if (!silent) {
|
|
89
|
+
logger.log(chalk.gray('Resolving dataplane URL...'));
|
|
90
|
+
}
|
|
91
|
+
const dataplaneUrl = await resolveDataplaneUrl(controllerUrl, environment, authConfig, { silent });
|
|
87
92
|
return { dataplaneUrl, authConfig, environment };
|
|
88
93
|
}
|
|
89
94
|
|
|
@@ -167,6 +172,15 @@ async function pushAndLogCredentialSecrets(dataplaneUrl, authConfig, systemKey,
|
|
|
167
172
|
} else {
|
|
168
173
|
logger.log(chalk.yellow('Secret push skipped'));
|
|
169
174
|
}
|
|
175
|
+
const resolvedParamNames = collectResolvedVariableConfigurationNames(payload);
|
|
176
|
+
if (resolvedParamNames.length > 0) {
|
|
177
|
+
const nameList = ` (${resolvedParamNames.join(', ')})`;
|
|
178
|
+
logger.log(
|
|
179
|
+
formatSuccessLine(
|
|
180
|
+
`Resolved ${resolvedParamNames.length} integration parameter(s) from .env for publish${nameList}.`
|
|
181
|
+
)
|
|
182
|
+
);
|
|
183
|
+
}
|
|
170
184
|
if (pushResult.warning) {
|
|
171
185
|
logger.log(chalk.yellow(`Warning: ${pushResult.warning}`));
|
|
172
186
|
}
|
|
@@ -73,10 +73,12 @@ function createDataplaneNotFoundError() {
|
|
|
73
73
|
* @returns {Promise<string>} Dataplane URL
|
|
74
74
|
* @throws {Error} If dataplane URL cannot be retrieved
|
|
75
75
|
*/
|
|
76
|
-
async function tryFallbackDataplaneUrl(controllerUrl, environment, authConfig) {
|
|
76
|
+
async function tryFallbackDataplaneUrl(controllerUrl, environment, authConfig, silent) {
|
|
77
77
|
try {
|
|
78
78
|
const fallbackUrl = await getDataplaneUrl(controllerUrl, 'dataplane', environment, authConfig);
|
|
79
|
-
|
|
79
|
+
if (!silent) {
|
|
80
|
+
logger.log(formatSuccessLine(`Dataplane URL: ${fallbackUrl}`));
|
|
81
|
+
}
|
|
80
82
|
return fallbackUrl;
|
|
81
83
|
} catch (fallbackError) {
|
|
82
84
|
if (isNotFoundError(fallbackError)) {
|
|
@@ -93,19 +95,25 @@ async function tryFallbackDataplaneUrl(controllerUrl, environment, authConfig) {
|
|
|
93
95
|
* @param {string} controllerUrl - Controller URL
|
|
94
96
|
* @param {string} environment - Environment key
|
|
95
97
|
* @param {Object} authConfig - Authentication configuration
|
|
98
|
+
* @param {{ silent?: boolean }} [opts] - When silent, skip progress/success lines (e.g. cert sync right after a run).
|
|
96
99
|
* @returns {Promise<string>} Dataplane URL
|
|
97
100
|
* @throws {Error} If dataplane URL cannot be discovered
|
|
98
101
|
*/
|
|
99
|
-
async function discoverDataplaneUrl(controllerUrl, environment, authConfig) {
|
|
100
|
-
|
|
102
|
+
async function discoverDataplaneUrl(controllerUrl, environment, authConfig, opts = {}) {
|
|
103
|
+
const silent = opts.silent === true;
|
|
104
|
+
if (!silent) {
|
|
105
|
+
logger.log(infoLine('🌐 Getting dataplane URL from controller...'));
|
|
106
|
+
}
|
|
101
107
|
try {
|
|
102
108
|
const dataplaneAppKey = await findDataplaneServiceAppKey(controllerUrl, environment, authConfig);
|
|
103
109
|
if (dataplaneAppKey) {
|
|
104
110
|
const dataplaneUrl = await getDataplaneUrl(controllerUrl, dataplaneAppKey, environment, authConfig);
|
|
105
|
-
|
|
111
|
+
if (!silent) {
|
|
112
|
+
logger.log(formatSuccessLine(`Dataplane URL: ${dataplaneUrl}`));
|
|
113
|
+
}
|
|
106
114
|
return dataplaneUrl;
|
|
107
115
|
}
|
|
108
|
-
return await tryFallbackDataplaneUrl(controllerUrl, environment, authConfig);
|
|
116
|
+
return await tryFallbackDataplaneUrl(controllerUrl, environment, authConfig, silent);
|
|
109
117
|
} catch (error) {
|
|
110
118
|
if (error.message.includes('Could not discover dataplane URL')) {
|
|
111
119
|
throw error;
|