@aifabrix/builder 2.44.3 → 2.44.5
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 +31 -15
- package/lib/api/certificates.api.js +21 -3
- package/lib/api/types/wizard.types.js +2 -1
- 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.help.js +1 -1
- package/lib/cli/setup-app.test-commands.js +75 -39
- package/lib/cli/setup-infra.js +6 -2
- package/lib/cli/setup-utility.js +20 -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-rbac.js +25 -2
- package/lib/commands/repair.js +2 -19
- package/lib/commands/test-e2e-external.js +9 -9
- package/lib/commands/up-common.js +25 -0
- package/lib/commands/upload.js +18 -4
- package/lib/commands/wizard-core.js +53 -11
- package/lib/commands/wizard-dataplane.js +14 -6
- package/lib/commands/wizard-entity-selection.js +71 -14
- package/lib/commands/wizard-headless.js +5 -2
- package/lib/commands/wizard-helpers.js +13 -1
- package/lib/commands/wizard.js +208 -60
- 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/generator/wizard-prompts.js +7 -1
- package/lib/generator/wizard.js +34 -0
- 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/flag-map-validation-run.json +1 -2
- package/lib/schema/type/document-storage.json +83 -3
- package/lib/schema/wizard-config.schema.json +1 -1
- 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-readme.js +47 -3
- 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/urls-local-registry.js +52 -10
- 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/validate-display-log-helpers.js +39 -0
- package/lib/validation/validate-display.js +89 -83
- package/package.json +1 -1
- package/templates/applications/miso-controller/env.template +6 -6
- package/templates/external-system/README.md.hbs +58 -32
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Shared log helpers for validate-display (layout.md tty-summary).
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const logger = require('../utils/logger');
|
|
11
|
+
const { sectionTitle, metadata, successGlyph } = require('../utils/cli-test-layout-chalk');
|
|
12
|
+
|
|
13
|
+
function logSectionTitle(title) {
|
|
14
|
+
logger.log(`\n${sectionTitle(title)}`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function logOkRowRest(message) {
|
|
18
|
+
logger.log(` ${successGlyph()} ${chalk.white(message)}`);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function logWarnRow(message) {
|
|
22
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(message)}`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function logDimLine(text) {
|
|
26
|
+
logger.log(metadata(` ${text}`));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function logErrorDetail(message) {
|
|
30
|
+
logger.log(` ${chalk.red(`• ${message}`)}`);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
logSectionTitle,
|
|
35
|
+
logOkRowRest,
|
|
36
|
+
logWarnRow,
|
|
37
|
+
logDimLine,
|
|
38
|
+
logErrorDetail
|
|
39
|
+
};
|
|
@@ -1,18 +1,30 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const {
|
|
2
|
+
headerKeyValue,
|
|
3
|
+
metadata,
|
|
4
|
+
formatDatasourceListRow,
|
|
5
|
+
formatBlockingError,
|
|
6
|
+
formatSuccessLine,
|
|
7
|
+
formatSuccessParagraph,
|
|
8
|
+
successGlyph,
|
|
9
|
+
failureGlyph
|
|
10
|
+
} = require('../utils/cli-test-layout-chalk');
|
|
2
11
|
/**
|
|
3
|
-
* Validation
|
|
4
|
-
*
|
|
5
|
-
* Display functions for validation results output.
|
|
6
|
-
*
|
|
7
|
-
* @fileoverview Validation display utilities for AI Fabrix Builder
|
|
12
|
+
* @fileoverview Validation display for `aifabrix validate` (tty-summary: cli-layout.mdc, layout.md).
|
|
8
13
|
* @author AI Fabrix Team
|
|
9
|
-
* @version 2.0.0
|
|
10
14
|
*/
|
|
11
15
|
|
|
12
16
|
const path = require('path');
|
|
13
17
|
const chalk = require('chalk');
|
|
14
18
|
const logger = require('../utils/logger');
|
|
15
19
|
const { loadConfigFile } = require('../utils/config-format');
|
|
20
|
+
const { flattenRootDimensionsForDisplay } = require('./dimension-display-helpers');
|
|
21
|
+
const {
|
|
22
|
+
logSectionTitle,
|
|
23
|
+
logOkRowRest,
|
|
24
|
+
logWarnRow,
|
|
25
|
+
logDimLine,
|
|
26
|
+
logErrorDetail
|
|
27
|
+
} = require('./validate-display-log-helpers');
|
|
16
28
|
|
|
17
29
|
/**
|
|
18
30
|
* Displays application validation results
|
|
@@ -24,26 +36,27 @@ function displayApplicationValidation(application) {
|
|
|
24
36
|
return;
|
|
25
37
|
}
|
|
26
38
|
|
|
27
|
-
|
|
39
|
+
logSectionTitle('Application');
|
|
28
40
|
if (application.valid) {
|
|
29
|
-
|
|
41
|
+
logOkRowRest('Application configuration is valid');
|
|
30
42
|
} else {
|
|
31
|
-
logger.log(chalk.red('
|
|
43
|
+
logger.log(` ${failureGlyph()} ${chalk.red('Application configuration has errors:')}`);
|
|
32
44
|
if (application.errors && application.errors.length > 0) {
|
|
33
45
|
application.errors.forEach(error => {
|
|
34
|
-
|
|
46
|
+
logErrorDetail(error);
|
|
35
47
|
});
|
|
36
48
|
}
|
|
37
49
|
}
|
|
38
50
|
if (application.warnings && application.warnings.length > 0) {
|
|
39
51
|
application.warnings.forEach(warning => {
|
|
40
|
-
logger.log(chalk.yellow(
|
|
52
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
41
53
|
});
|
|
42
54
|
}
|
|
43
55
|
}
|
|
44
56
|
|
|
45
57
|
/**
|
|
46
|
-
* Extracts dimensions from a datasource file
|
|
58
|
+
* Extracts dimensions from a datasource file for CLI display.
|
|
59
|
+
* Local bindings become `metadata.<field>`; FK bindings become `fk:<fk>→<dimension>...` plus optional `(actor: ...)`.
|
|
47
60
|
* @function extractDimensionsFromDatasource
|
|
48
61
|
* @param {string} filePath - Path to datasource file
|
|
49
62
|
* @returns {Object} Dimensions info { dimensions: Object, dimensionKeys: string[], hasDimensions: boolean }
|
|
@@ -52,15 +65,7 @@ function extractDimensionsFromDatasource(filePath) {
|
|
|
52
65
|
try {
|
|
53
66
|
const parsed = loadConfigFile(filePath);
|
|
54
67
|
|
|
55
|
-
const rootFlat =
|
|
56
|
-
const root = parsed.dimensions;
|
|
57
|
-
if (root && typeof root === 'object' && !Array.isArray(root)) {
|
|
58
|
-
for (const [dimKey, binding] of Object.entries(root)) {
|
|
59
|
-
if (binding && typeof binding === 'object' && typeof binding.field === 'string') {
|
|
60
|
-
rootFlat[dimKey] = `metadata.${binding.field}`;
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
}
|
|
68
|
+
const rootFlat = flattenRootDimensionsForDisplay(parsed.dimensions);
|
|
64
69
|
const abacDimensions = parsed.abac?.dimensions || {};
|
|
65
70
|
const allDimensions = { ...rootFlat, ...abacDimensions };
|
|
66
71
|
const dimensionKeys = Object.keys(allDimensions);
|
|
@@ -85,19 +90,19 @@ function displayExternalFilesValidation(externalFiles) {
|
|
|
85
90
|
return;
|
|
86
91
|
}
|
|
87
92
|
|
|
88
|
-
|
|
93
|
+
logSectionTitle('External integration files');
|
|
89
94
|
externalFiles.forEach(file => {
|
|
90
95
|
if (file.valid) {
|
|
91
|
-
logger.log(
|
|
96
|
+
logger.log(formatDatasourceListRow('ok', file.file, file.type));
|
|
92
97
|
} else {
|
|
93
|
-
logger.log(
|
|
98
|
+
logger.log(formatDatasourceListRow('fail', file.file, file.type));
|
|
94
99
|
file.errors.forEach(error => {
|
|
95
|
-
|
|
100
|
+
logErrorDetail(error);
|
|
96
101
|
});
|
|
97
102
|
}
|
|
98
103
|
if (file.warnings && file.warnings.length > 0) {
|
|
99
104
|
file.warnings.forEach(warning => {
|
|
100
|
-
logger.log(chalk.yellow(
|
|
105
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
101
106
|
});
|
|
102
107
|
}
|
|
103
108
|
});
|
|
@@ -122,7 +127,7 @@ function displayDimensionsValidation(externalFiles) {
|
|
|
122
127
|
return [];
|
|
123
128
|
}
|
|
124
129
|
|
|
125
|
-
|
|
130
|
+
logSectionTitle('Dimensions (ABAC)');
|
|
126
131
|
|
|
127
132
|
const warnings = [];
|
|
128
133
|
let anyDatasourceHasDimensions = false;
|
|
@@ -132,19 +137,19 @@ function displayDimensionsValidation(externalFiles) {
|
|
|
132
137
|
|
|
133
138
|
if (dimensionsInfo.hasDimensions) {
|
|
134
139
|
anyDatasourceHasDimensions = true;
|
|
135
|
-
logger.log(
|
|
140
|
+
logger.log(formatDatasourceListRow('ok', file.file, file.type));
|
|
136
141
|
dimensionsInfo.dimensionKeys.forEach(key => {
|
|
137
142
|
const mapping = dimensionsInfo.dimensions[key];
|
|
138
|
-
|
|
143
|
+
logDimLine(`${key} → ${mapping}`);
|
|
139
144
|
});
|
|
140
145
|
} else {
|
|
141
|
-
|
|
146
|
+
logWarnRow(`${file.file} — no dimensions configured`);
|
|
142
147
|
warnings.push(`${file.file} - no dimensions configured, ABAC filtering disabled`);
|
|
143
148
|
}
|
|
144
149
|
});
|
|
145
150
|
|
|
146
151
|
if (!anyDatasourceHasDimensions) {
|
|
147
|
-
|
|
152
|
+
logWarnRow('No dimensions configured — ABAC filtering disabled for all datasources');
|
|
148
153
|
}
|
|
149
154
|
|
|
150
155
|
return warnings;
|
|
@@ -160,18 +165,18 @@ function displayRbacValidation(rbac) {
|
|
|
160
165
|
return;
|
|
161
166
|
}
|
|
162
167
|
|
|
163
|
-
|
|
168
|
+
logSectionTitle('RBAC configuration');
|
|
164
169
|
if (rbac.valid) {
|
|
165
|
-
|
|
170
|
+
logOkRowRest('RBAC configuration is valid');
|
|
166
171
|
} else {
|
|
167
|
-
logger.log(chalk.red('
|
|
172
|
+
logger.log(` ${failureGlyph()} ${chalk.red('RBAC configuration has errors:')}`);
|
|
168
173
|
rbac.errors.forEach(error => {
|
|
169
|
-
|
|
174
|
+
logErrorDetail(error);
|
|
170
175
|
});
|
|
171
176
|
}
|
|
172
177
|
if (rbac.warnings && rbac.warnings.length > 0) {
|
|
173
178
|
rbac.warnings.forEach(warning => {
|
|
174
|
-
logger.log(chalk.yellow(
|
|
179
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
175
180
|
});
|
|
176
181
|
}
|
|
177
182
|
}
|
|
@@ -186,19 +191,20 @@ function displayFileValidation(result) {
|
|
|
186
191
|
return;
|
|
187
192
|
}
|
|
188
193
|
|
|
189
|
-
|
|
190
|
-
logger.log(
|
|
194
|
+
logSectionTitle('File');
|
|
195
|
+
logger.log(` ${headerKeyValue('Path:', result.file)}`);
|
|
196
|
+
logger.log(` ${headerKeyValue('Type:', String(result.type))}`);
|
|
191
197
|
if (result.valid) {
|
|
192
|
-
|
|
198
|
+
logOkRowRest('File is valid');
|
|
193
199
|
} else {
|
|
194
|
-
logger.log(chalk.red('
|
|
200
|
+
logger.log(` ${failureGlyph()} ${chalk.red('File has errors:')}`);
|
|
195
201
|
result.errors.forEach(error => {
|
|
196
|
-
|
|
202
|
+
logErrorDetail(error);
|
|
197
203
|
});
|
|
198
204
|
}
|
|
199
205
|
if (result.warnings && result.warnings.length > 0) {
|
|
200
206
|
result.warnings.forEach(warning => {
|
|
201
|
-
logger.log(chalk.yellow(
|
|
207
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
202
208
|
});
|
|
203
209
|
}
|
|
204
210
|
}
|
|
@@ -213,9 +219,9 @@ function displayAggregatedWarnings(warnings) {
|
|
|
213
219
|
return;
|
|
214
220
|
}
|
|
215
221
|
|
|
216
|
-
|
|
222
|
+
logSectionTitle('Warnings');
|
|
217
223
|
warnings.forEach(warning => {
|
|
218
|
-
logger.log(chalk.yellow(
|
|
224
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
219
225
|
});
|
|
220
226
|
}
|
|
221
227
|
|
|
@@ -226,18 +232,18 @@ function displayAggregatedWarnings(warnings) {
|
|
|
226
232
|
* @returns {void}
|
|
227
233
|
*/
|
|
228
234
|
function displayApplicationStep(application) {
|
|
229
|
-
|
|
235
|
+
logSectionTitle('Application');
|
|
230
236
|
if (application.valid) {
|
|
231
|
-
|
|
237
|
+
logOkRowRest('Application configuration is valid');
|
|
232
238
|
} else {
|
|
233
|
-
logger.log(chalk.red('
|
|
239
|
+
logger.log(` ${failureGlyph()} ${chalk.red('Application configuration has errors:')}`);
|
|
234
240
|
application.errors.forEach(error => {
|
|
235
|
-
|
|
241
|
+
logErrorDetail(error);
|
|
236
242
|
});
|
|
237
243
|
}
|
|
238
244
|
if (application.warnings && application.warnings.length > 0) {
|
|
239
245
|
application.warnings.forEach(warning => {
|
|
240
|
-
logger.log(chalk.yellow(
|
|
246
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
241
247
|
});
|
|
242
248
|
}
|
|
243
249
|
}
|
|
@@ -255,19 +261,19 @@ function displayComponentsStep(components) {
|
|
|
255
261
|
return;
|
|
256
262
|
}
|
|
257
263
|
|
|
258
|
-
|
|
264
|
+
logSectionTitle('External integration files');
|
|
259
265
|
if (hasFiles) {
|
|
260
266
|
components.files.forEach(file => {
|
|
261
267
|
if (file.valid) {
|
|
262
|
-
logger.log(
|
|
268
|
+
logger.log(formatDatasourceListRow('ok', file.file, file.type));
|
|
263
269
|
} else {
|
|
264
|
-
logger.log(
|
|
270
|
+
logger.log(formatDatasourceListRow('fail', file.file, file.type));
|
|
265
271
|
}
|
|
266
272
|
});
|
|
267
273
|
}
|
|
268
274
|
if (hasErrors) {
|
|
269
275
|
components.errors.forEach(error => {
|
|
270
|
-
logger.log(
|
|
276
|
+
logger.log(formatBlockingError(error));
|
|
271
277
|
});
|
|
272
278
|
}
|
|
273
279
|
}
|
|
@@ -283,18 +289,18 @@ function displayDimensionsStep(datasourceFiles) {
|
|
|
283
289
|
return;
|
|
284
290
|
}
|
|
285
291
|
|
|
286
|
-
|
|
292
|
+
logSectionTitle('Dimensions (ABAC)');
|
|
287
293
|
datasourceFiles.forEach(file => {
|
|
288
294
|
try {
|
|
289
295
|
const dimensionsInfo = extractDimensionsFromDatasource(file.path || file.file);
|
|
290
296
|
if (dimensionsInfo.hasDimensions) {
|
|
291
|
-
logger.log(
|
|
297
|
+
logger.log(formatDatasourceListRow('ok', file.file, file.type));
|
|
292
298
|
dimensionsInfo.dimensionKeys.forEach(key => {
|
|
293
299
|
const mapping = dimensionsInfo.dimensions[key];
|
|
294
|
-
|
|
300
|
+
logDimLine(`${key} → ${mapping}`);
|
|
295
301
|
});
|
|
296
302
|
} else {
|
|
297
|
-
|
|
303
|
+
logWarnRow(`${file.file} — no dimensions configured`);
|
|
298
304
|
}
|
|
299
305
|
} catch {
|
|
300
306
|
// Skip if file path not available
|
|
@@ -310,32 +316,32 @@ function displayDimensionsStep(datasourceFiles) {
|
|
|
310
316
|
* @returns {void}
|
|
311
317
|
*/
|
|
312
318
|
function displayManifestStep(manifest, componentFiles) {
|
|
313
|
-
|
|
319
|
+
logSectionTitle('Deployment manifest');
|
|
314
320
|
if (manifest.skipped) {
|
|
315
|
-
logger.log(chalk.
|
|
321
|
+
logger.log(` ${chalk.gray('⏭')} ${chalk.white('Skipped (fix errors above first)')}`);
|
|
316
322
|
} else if (manifest.valid) {
|
|
317
|
-
|
|
323
|
+
logOkRowRest('Full deployment manifest is valid');
|
|
318
324
|
if (componentFiles) {
|
|
319
325
|
const datasourceFiles = componentFiles.filter(f => f.type === 'datasource' || f.type === 'external-datasource');
|
|
320
|
-
logger.log(chalk.
|
|
321
|
-
logger.log(
|
|
322
|
-
logger.log(chalk.
|
|
326
|
+
logger.log(` ${successGlyph()} ${chalk.white('System configuration valid')}`);
|
|
327
|
+
logger.log(` ${successGlyph()} ${chalk.white(`${datasourceFiles.length} datasource(s) valid`)}`);
|
|
328
|
+
logger.log(` ${successGlyph()} ${chalk.white('Schema validation passed')}`);
|
|
323
329
|
}
|
|
324
330
|
} else {
|
|
325
|
-
logger.log(chalk.red('
|
|
331
|
+
logger.log(` ${failureGlyph()} ${chalk.red('Full deployment manifest validation failed:')}`);
|
|
326
332
|
const errs = manifest.errors && manifest.errors.length > 0 ? manifest.errors : [];
|
|
327
333
|
if (errs.length > 0) {
|
|
328
334
|
errs.forEach(error => {
|
|
329
335
|
const msg = typeof error === 'string' ? error : String(error);
|
|
330
|
-
|
|
336
|
+
logErrorDetail(msg);
|
|
331
337
|
});
|
|
332
338
|
} else {
|
|
333
|
-
|
|
339
|
+
logErrorDetail('No error details available (check schema and manifest structure).');
|
|
334
340
|
}
|
|
335
341
|
}
|
|
336
342
|
if (manifest.warnings && manifest.warnings.length > 0) {
|
|
337
343
|
manifest.warnings.forEach(warning => {
|
|
338
|
-
logger.log(chalk.yellow(
|
|
344
|
+
logger.log(` ${chalk.yellow('⚠')} ${chalk.white(warning)}`);
|
|
339
345
|
});
|
|
340
346
|
}
|
|
341
347
|
}
|
|
@@ -349,7 +355,7 @@ function displayStepByStepValidation(result) {
|
|
|
349
355
|
if (result.valid) {
|
|
350
356
|
logger.log(formatSuccessParagraph('Validation passed!'));
|
|
351
357
|
} else {
|
|
352
|
-
logger.log(
|
|
358
|
+
logger.log(`\n${formatBlockingError('Validation failed!')}`);
|
|
353
359
|
}
|
|
354
360
|
|
|
355
361
|
displayApplicationStep(result.steps.application);
|
|
@@ -375,7 +381,7 @@ function displayStepByStepValidation(result) {
|
|
|
375
381
|
|
|
376
382
|
displayOverallStatus(result);
|
|
377
383
|
if (result.appPath) {
|
|
378
|
-
logger.log(
|
|
384
|
+
logger.log(`\n${metadata(path.resolve(result.appPath))}`);
|
|
379
385
|
}
|
|
380
386
|
}
|
|
381
387
|
|
|
@@ -392,9 +398,9 @@ function displayBatchValidationResults(batchResult) {
|
|
|
392
398
|
|
|
393
399
|
const results = batchResult.results;
|
|
394
400
|
results.forEach(item => {
|
|
395
|
-
logger.log(
|
|
401
|
+
logger.log(`\n${metadata('──')} ${chalk.white.bold(item.appName)} ${metadata('──')}`);
|
|
396
402
|
if (item.error) {
|
|
397
|
-
logger.log(
|
|
403
|
+
logger.log(` ${failureGlyph()} ${chalk.red(item.error)}`);
|
|
398
404
|
} else if (item.result) {
|
|
399
405
|
displayValidationResults(item.result);
|
|
400
406
|
}
|
|
@@ -402,13 +408,13 @@ function displayBatchValidationResults(batchResult) {
|
|
|
402
408
|
|
|
403
409
|
const passed = results.filter(r => r.result && r.result.valid).length;
|
|
404
410
|
const failed = results.length - passed;
|
|
405
|
-
|
|
411
|
+
logSectionTitle('Summary');
|
|
406
412
|
if (failed === 0) {
|
|
407
|
-
logger.log(formatSuccessLine(`${passed} passed, 0 failed`));
|
|
408
|
-
logger.log(chalk.green('
|
|
413
|
+
logger.log(` ${formatSuccessLine(`${passed} passed, 0 failed`)}`);
|
|
414
|
+
logger.log(` ${headerKeyValue('Overall:', chalk.green('Passed'))}`);
|
|
409
415
|
} else {
|
|
410
|
-
logger.log(formatBlockingError(`${passed} passed, ${failed} failed`));
|
|
411
|
-
logger.log(chalk.red('
|
|
416
|
+
logger.log(` ${formatBlockingError(`${passed} passed, ${failed} failed`)}`);
|
|
417
|
+
logger.log(` ${headerKeyValue('Overall:', chalk.red('Failed'))}`);
|
|
412
418
|
}
|
|
413
419
|
}
|
|
414
420
|
|
|
@@ -427,12 +433,13 @@ function displayOverallStatus(result) {
|
|
|
427
433
|
}
|
|
428
434
|
}
|
|
429
435
|
const hasWarnings = result.warnings && result.warnings.length > 0;
|
|
436
|
+
logger.log('');
|
|
430
437
|
if (hasErrors) {
|
|
431
|
-
logger.log(chalk.red('
|
|
438
|
+
logger.log(headerKeyValue('Overall:', chalk.red('Failed')));
|
|
432
439
|
} else if (hasWarnings) {
|
|
433
|
-
logger.log(chalk.yellow('
|
|
440
|
+
logger.log(headerKeyValue('Overall:', chalk.yellow('Passed with warnings')));
|
|
434
441
|
} else {
|
|
435
|
-
logger.log(chalk.green('
|
|
442
|
+
logger.log(headerKeyValue('Overall:', chalk.green('Passed')));
|
|
436
443
|
}
|
|
437
444
|
}
|
|
438
445
|
|
|
@@ -453,7 +460,7 @@ function displayValidationResults(result) {
|
|
|
453
460
|
if (result.valid) {
|
|
454
461
|
logger.log(formatSuccessParagraph('Validation passed!'));
|
|
455
462
|
} else {
|
|
456
|
-
logger.log(
|
|
463
|
+
logger.log(`\n${formatBlockingError('Validation failed!')}`);
|
|
457
464
|
}
|
|
458
465
|
|
|
459
466
|
displayApplicationValidation(result.application);
|
|
@@ -471,10 +478,9 @@ function displayValidationResults(result) {
|
|
|
471
478
|
|
|
472
479
|
displayOverallStatus(result);
|
|
473
480
|
if (result.appPath) {
|
|
474
|
-
logger.log(
|
|
481
|
+
logger.log(`\n${metadata(path.resolve(result.appPath))}`);
|
|
475
482
|
}
|
|
476
483
|
}
|
|
477
|
-
|
|
478
484
|
module.exports = {
|
|
479
485
|
displayValidationResults,
|
|
480
486
|
displayBatchValidationResults,
|
package/package.json
CHANGED
|
@@ -28,8 +28,12 @@ SKIP_FIRST_TIME_SETUP=false
|
|
|
28
28
|
# Optional custom controller key for onboarding (default: miso-controller)
|
|
29
29
|
ONBOARDING_CONTROLLER_KEY=miso-controller
|
|
30
30
|
|
|
31
|
-
#
|
|
32
|
-
|
|
31
|
+
# Infrastructure/service name used by controller/onboarding flows
|
|
32
|
+
INFRASTRUCTURE_NAME=aifabrix
|
|
33
|
+
|
|
34
|
+
# Azure region (required for first-time onboarding: controller record / provisioning metadata).
|
|
35
|
+
# Use the region you deploy to (e.g. westeurope, eastus, northeurope). Single env name: LOCATION.
|
|
36
|
+
LOCATION=westeurope
|
|
33
37
|
|
|
34
38
|
# Required for admin user creation during onboarding
|
|
35
39
|
# Password for the initial administrator user (username: admin)
|
|
@@ -319,10 +323,6 @@ MISO_ENVIRONMENT=miso
|
|
|
319
323
|
MISO_CLIENTID=kv://miso-controller-client-idKeyVault
|
|
320
324
|
MISO_CLIENTSECRET=kv://miso-controller-client-secretKeyVault
|
|
321
325
|
|
|
322
|
-
# Allowed origins for CORS validation (comma-separated)
|
|
323
|
-
# Use wildcards for ports: http://localhost:*
|
|
324
|
-
MISO_ALLOWED_ORIGINS=http://localhost:*,url://host-public,url://host-private,url://dataplane-host-public,url://dataplane-host-private
|
|
325
|
-
|
|
326
326
|
# Evaluation mode (optional .env override of DB controller.configuration.evaluation):
|
|
327
327
|
# When true (default if DB omits flag), infra deploy may coerce :envKey to `miso` — e2e poll on `dev` can 404.
|
|
328
328
|
# Set false locally to force path envKey to match deploy + GET .../deployments/:id.
|
|
@@ -20,13 +20,15 @@
|
|
|
20
20
|
- `deploy.js` – Deploy script for the integration
|
|
21
21
|
- `wizard.yaml` – Wizard configuration (if created via wizard)
|
|
22
22
|
|
|
23
|
-
Optional: `{{rbacOptionalFile}}` – Roles and permissions merged into the system when
|
|
23
|
+
Optional: `{{rbacOptionalFile}}` – Roles and permissions merged into the system when
|
|
24
|
+
present.
|
|
24
25
|
|
|
25
26
|
## Quick Start
|
|
26
27
|
|
|
27
28
|
Login to your controller
|
|
29
|
+
|
|
28
30
|
```bash
|
|
29
|
-
aifabrix auth config --set-controller
|
|
31
|
+
aifabrix auth config --set-controller URL --set-environment dev
|
|
30
32
|
aifabrix login
|
|
31
33
|
```
|
|
32
34
|
|
|
@@ -45,22 +47,25 @@ Edit files in `integration/{{appName}}/`:
|
|
|
45
47
|
- **Authentication**: `{{systemKey}}-system{{fileExt}}` (auth type, credentials placeholders)
|
|
46
48
|
- **Field mappings**: `{{systemKey}}-datasource-*{{fileExt}}` (dimensions, attributes, operations)
|
|
47
49
|
- **Credential and configuration**: `env.template` (security settings and configuration variables)
|
|
48
|
-
|
|
49
50
|
{{#if secretPaths}}{{#if secretPaths.length}}
|
|
51
|
+
|
|
50
52
|
### Secrets
|
|
51
53
|
|
|
52
54
|
Secrets are resolved from `.aifabrix` or key vault. Set them with:
|
|
53
55
|
|
|
54
56
|
```bash
|
|
55
57
|
{{#each secretPaths}}
|
|
56
|
-
aifabrix secret set {{path}}
|
|
58
|
+
aifabrix secret set {{path}} VALUE # {{description}}
|
|
57
59
|
{{/each}}
|
|
58
60
|
```
|
|
59
61
|
{{/if}}{{/if}}
|
|
60
62
|
|
|
61
63
|
### 3. Validate configuration (local only)
|
|
62
64
|
|
|
63
|
-
`aifabrix validate` runs **on your machine**: it loads files under
|
|
65
|
+
`aifabrix validate` runs **on your machine**: it loads files under
|
|
66
|
+
`integration/{{appName}}/`, checks them against the application and
|
|
67
|
+
external-system / external-datasource JSON schemas, and runs related manifest rules.
|
|
68
|
+
It does **not** call the dataplane or any other remote API.
|
|
64
69
|
|
|
65
70
|
```bash
|
|
66
71
|
aifabrix validate {{appName}}
|
|
@@ -70,19 +75,23 @@ Use this before upload or deploy to catch structural and policy errors early.
|
|
|
70
75
|
|
|
71
76
|
### 4. Repair Deployment Manifest
|
|
72
77
|
|
|
73
|
-
**Run repair regularly.** It keeps naming conventions, filenames, and the deployment
|
|
78
|
+
**Run repair regularly.** It keeps naming conventions, filenames, and the deployment
|
|
79
|
+
manifest aligned with AI Fabrix platform best practices. Use it after editing
|
|
80
|
+
datasources, env.template, or system config—and run it often to catch drift early.
|
|
74
81
|
|
|
75
82
|
```bash
|
|
76
83
|
aifabrix repair {{appName}}
|
|
77
84
|
```
|
|
78
85
|
|
|
79
86
|
Options:
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
87
|
+
|
|
88
|
+
- `--auth METHOD` — Set authentication method (`oauth2`, `aad`, `apikey`, `basic`,
|
|
89
|
+
`queryParam`, `oidc`, `hmac`, `none`); updates system file and env.template
|
|
90
|
+
- `--dry-run` — Report changes only; do not write
|
|
91
|
+
- `--rbac` — Ensure RBAC permissions per datasource and add default Admin/Reader roles if none exist
|
|
92
|
+
- `--expose` — Set `exposed.attributes` on each datasource to all `fieldMappings.attributes` keys
|
|
93
|
+
- `--sync` — Add default sync section to datasources that lack it
|
|
94
|
+
- `--test` — Generate `testPayload.payloadTemplate` and `testPayload.expectedResult` from attributes
|
|
86
95
|
|
|
87
96
|
### 5. Upload to dataplane
|
|
88
97
|
|
|
@@ -93,12 +102,16 @@ aifabrix upload {{appName}}
|
|
|
93
102
|
## Testing
|
|
94
103
|
|
|
95
104
|
| Command | Where it runs | Calls dataplane? |
|
|
96
|
-
|
|
105
|
+
| --- | --- | --- |
|
|
97
106
|
| `aifabrix validate {{appName}}` | Local (schemas / files) | No |
|
|
98
107
|
| `aifabrix test {{appName}}` | Local (manifest / payload checks) | No |
|
|
99
|
-
| `aifabrix test-integration {{appName}}
|
|
108
|
+
| `aifabrix test-integration {{appName}}` | Auth + dataplane | Yes |
|
|
109
|
+
| `aifabrix test-e2e {{appName}}` | Auth + dataplane | Yes |
|
|
110
|
+
| Datasource `test` / `test-integration` / `test-e2e` | Auth + dataplane | Yes |
|
|
100
111
|
|
|
101
|
-
So: **validate** (and **`test`**) stay offline; **all integration and E2E test
|
|
112
|
+
So: **validate** (and **`test`**) stay offline; **all integration and E2E test
|
|
113
|
+
commands** exercise the system **via the API** (after login and a reachable
|
|
114
|
+
dataplane).
|
|
102
115
|
|
|
103
116
|
### Local checks (no API)
|
|
104
117
|
|
|
@@ -120,14 +133,16 @@ aifabrix test-e2e {{appName}}
|
|
|
120
133
|
```
|
|
121
134
|
|
|
122
135
|
Options:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
136
|
+
|
|
137
|
+
- `-e`, `--env ENV` — Environment: `dev`, `tst`, or `pro` (builder: dev/tst for container)
|
|
138
|
+
- `-v`, `--verbose` — Show detailed step output and poll progress
|
|
139
|
+
- `-d`, `--debug` — Include debug output and write log to `integration/{{appName}}/logs/`
|
|
140
|
+
- `--no-async` — Use sync mode (no polling); single POST per datasource
|
|
127
141
|
|
|
128
142
|
### E2E tests per datasource
|
|
129
143
|
|
|
130
|
-
To run a full E2E test for a single datasource (config, credential, sync, data,
|
|
144
|
+
To run a full E2E test for a single datasource (config, credential, sync, data,
|
|
145
|
+
CIP), use `aifabrix datasource test-e2e` with the datasource key and app:
|
|
131
146
|
|
|
132
147
|
{{#if hasDatasources}}
|
|
133
148
|
```bash
|
|
@@ -140,26 +155,37 @@ aifabrix datasource test-e2e {{datasourceKey}} --app {{../appName}}
|
|
|
140
155
|
{{/if}}
|
|
141
156
|
|
|
142
157
|
Options:
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
158
|
+
|
|
159
|
+
- `-a`, `--app {{appName}}` — App key (default: resolve from cwd if inside `integration/{{appName}}/`)
|
|
160
|
+
- `-e`, `--env ENV` — Environment: `dev`, `tst`, or `pro`
|
|
161
|
+
- `-v`, `--verbose` — Show detailed step output and poll progress
|
|
162
|
+
- `-d`, `--debug` — Include debug output and write log to `integration/{{appName}}/logs/`
|
|
163
|
+
- `--no-run-scenarios` — Skip expanding `testPayload.scenarios` in capacity step
|
|
164
|
+
- `--no-cleanup` — Disable cleanup after test (body cleanup: false)
|
|
165
|
+
- `--primary-key-value VALUE` — Primary key value or path to JSON file (e.g.
|
|
166
|
+
`@pk.json`) for body `primaryKeyValue`
|
|
167
|
+
- `--no-async` — Use sync mode (no polling); single POST, no asyncRun
|
|
152
168
|
|
|
153
169
|
## Deployment
|
|
154
170
|
|
|
155
|
-
Deploy via miso-controller pipeline (same as regular apps). Auth and controller
|
|
171
|
+
Deploy via miso-controller pipeline (same as regular apps). Auth and controller
|
|
172
|
+
come from `aifabrix login` and `aifabrix auth config`:
|
|
156
173
|
|
|
157
174
|
```bash
|
|
158
175
|
aifabrix deploy {{appName}}
|
|
159
176
|
```
|
|
160
177
|
|
|
178
|
+
## Delete
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
aifabrix delete {{appName}}
|
|
182
|
+
```
|
|
183
|
+
|
|
161
184
|
## Troubleshooting
|
|
162
185
|
|
|
163
|
-
- **Local validation errors**: Run `aifabrix validate {{appName}}` (and
|
|
164
|
-
|
|
186
|
+
- **Local validation errors**: Run `aifabrix validate {{appName}}` (and
|
|
187
|
+
`aifabrix test {{appName}}`) — these only inspect files on disk, not the dataplane.
|
|
188
|
+
- **Deployment / auth**: Run
|
|
189
|
+
`aifabrix auth config --set-controller URL --set-environment ENV` and
|
|
190
|
+
`aifabrix login` before `aifabrix deploy`.
|
|
165
191
|
- **File not found**: Run commands from the project root (where `package.json` and `integration/` live).
|