@aifabrix/builder 2.6.2 → 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/aifabrix.js +4 -0
- package/lib/cli.js +51 -2
- package/lib/commands/datasource.js +94 -0
- package/lib/config.js +25 -36
- package/lib/datasource-deploy.js +182 -0
- package/lib/datasource-diff.js +73 -0
- package/lib/datasource-list.js +138 -0
- package/lib/datasource-validate.js +63 -0
- package/lib/diff.js +266 -0
- package/lib/schema/application-schema.json +829 -687
- package/lib/schema/external-datasource.schema.json +464 -0
- package/lib/schema/external-system.schema.json +262 -0
- package/lib/secrets.js +20 -1
- package/lib/utils/api.js +19 -3
- package/lib/utils/env-copy.js +24 -0
- package/lib/utils/env-endpoints.js +50 -11
- package/lib/utils/schema-loader.js +220 -0
- package/lib/utils/schema-resolver.js +174 -0
- package/lib/utils/secrets-helpers.js +65 -17
- package/lib/utils/token-manager.js +57 -21
- package/lib/validate.js +299 -0
- package/package.json +1 -1
package/lib/validate.js
ADDED
|
@@ -0,0 +1,299 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main Validation Command
|
|
3
|
+
*
|
|
4
|
+
* Validates applications or external integration files.
|
|
5
|
+
* Supports app name validation (including externalIntegration block) or direct file validation.
|
|
6
|
+
*
|
|
7
|
+
* @fileoverview Main validation command for AI Fabrix Builder
|
|
8
|
+
* @author AI Fabrix Team
|
|
9
|
+
* @version 2.0.0
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const fs = require('fs');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const chalk = require('chalk');
|
|
15
|
+
const validator = require('./validator');
|
|
16
|
+
const { resolveExternalFiles } = require('./utils/schema-resolver');
|
|
17
|
+
const { loadExternalSystemSchema, loadExternalDataSourceSchema, detectSchemaType } = require('./utils/schema-loader');
|
|
18
|
+
const { formatValidationErrors } = require('./utils/error-formatter');
|
|
19
|
+
const logger = require('./utils/logger');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Validates a single external file against its schema
|
|
23
|
+
*
|
|
24
|
+
* @async
|
|
25
|
+
* @function validateExternalFile
|
|
26
|
+
* @param {string} filePath - Path to the file
|
|
27
|
+
* @param {string} type - File type: 'system' | 'datasource'
|
|
28
|
+
* @returns {Promise<Object>} Validation result
|
|
29
|
+
*/
|
|
30
|
+
async function validateExternalFile(filePath, type) {
|
|
31
|
+
if (!fs.existsSync(filePath)) {
|
|
32
|
+
throw new Error(`File not found: ${filePath}`);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
36
|
+
let parsed;
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(content);
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
valid: false,
|
|
43
|
+
errors: [`Invalid JSON syntax: ${error.message}`],
|
|
44
|
+
warnings: []
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
let validate;
|
|
49
|
+
if (type === 'system') {
|
|
50
|
+
validate = loadExternalSystemSchema();
|
|
51
|
+
} else if (type === 'datasource') {
|
|
52
|
+
validate = loadExternalDataSourceSchema();
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error(`Unknown file type: ${type}`);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const valid = validate(parsed);
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
valid,
|
|
61
|
+
errors: valid ? [] : formatValidationErrors(validate.errors),
|
|
62
|
+
warnings: []
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Validates application or external integration file
|
|
68
|
+
* Detects if input is app name or file path and validates accordingly
|
|
69
|
+
*
|
|
70
|
+
* @async
|
|
71
|
+
* @function validateAppOrFile
|
|
72
|
+
* @param {string} appOrFile - Application name or file path
|
|
73
|
+
* @returns {Promise<Object>} Validation result with aggregated results
|
|
74
|
+
* @throws {Error} If validation fails
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* const result = await validateAppOrFile('myapp');
|
|
78
|
+
* // Returns: { valid: true, application: {...}, externalFiles: [...] }
|
|
79
|
+
*/
|
|
80
|
+
async function validateAppOrFile(appOrFile) {
|
|
81
|
+
if (!appOrFile || typeof appOrFile !== 'string') {
|
|
82
|
+
throw new Error('App name or file path is required');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Check if it's a file path (exists and is a file)
|
|
86
|
+
const isFilePath = fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile();
|
|
87
|
+
|
|
88
|
+
if (isFilePath) {
|
|
89
|
+
// Validate single file
|
|
90
|
+
const schemaType = detectSchemaType(appOrFile);
|
|
91
|
+
let result;
|
|
92
|
+
|
|
93
|
+
if (schemaType === 'application') {
|
|
94
|
+
// For application files, we'd need to transform them first
|
|
95
|
+
// For now, just validate JSON structure
|
|
96
|
+
const content = fs.readFileSync(appOrFile, 'utf8');
|
|
97
|
+
try {
|
|
98
|
+
JSON.parse(content);
|
|
99
|
+
} catch (error) {
|
|
100
|
+
return {
|
|
101
|
+
valid: false,
|
|
102
|
+
errors: [`Invalid JSON syntax: ${error.message}`],
|
|
103
|
+
warnings: []
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
// Note: Full application validation requires variables.yaml transformation
|
|
107
|
+
// This is a simplified validation for direct JSON files
|
|
108
|
+
result = {
|
|
109
|
+
valid: true,
|
|
110
|
+
errors: [],
|
|
111
|
+
warnings: ['Application file validation is simplified. Use app name for full validation.']
|
|
112
|
+
};
|
|
113
|
+
} else if (schemaType === 'external-system') {
|
|
114
|
+
result = await validateExternalFile(appOrFile, 'system');
|
|
115
|
+
} else if (schemaType === 'external-datasource') {
|
|
116
|
+
result = await validateExternalFile(appOrFile, 'datasource');
|
|
117
|
+
} else {
|
|
118
|
+
throw new Error(`Unknown schema type: ${schemaType}`);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return {
|
|
122
|
+
valid: result.valid,
|
|
123
|
+
file: appOrFile,
|
|
124
|
+
type: schemaType,
|
|
125
|
+
errors: result.errors,
|
|
126
|
+
warnings: result.warnings
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Treat as app name
|
|
131
|
+
const appName = appOrFile;
|
|
132
|
+
|
|
133
|
+
// Validate application
|
|
134
|
+
const appValidation = await validator.validateApplication(appName);
|
|
135
|
+
|
|
136
|
+
// Check for externalIntegration block
|
|
137
|
+
const variablesPath = path.join(process.cwd(), 'builder', appName, 'variables.yaml');
|
|
138
|
+
if (!fs.existsSync(variablesPath)) {
|
|
139
|
+
return {
|
|
140
|
+
valid: appValidation.valid,
|
|
141
|
+
application: appValidation,
|
|
142
|
+
externalFiles: []
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const yamlLib = require('js-yaml');
|
|
147
|
+
const content = fs.readFileSync(variablesPath, 'utf8');
|
|
148
|
+
let variables;
|
|
149
|
+
|
|
150
|
+
try {
|
|
151
|
+
variables = yamlLib.load(content);
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return {
|
|
154
|
+
valid: appValidation.valid,
|
|
155
|
+
application: appValidation,
|
|
156
|
+
externalFiles: [],
|
|
157
|
+
warnings: [`Could not parse variables.yaml to check externalIntegration: ${error.message}`]
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// If no externalIntegration block, return app validation only
|
|
162
|
+
if (!variables.externalIntegration) {
|
|
163
|
+
return {
|
|
164
|
+
valid: appValidation.valid,
|
|
165
|
+
application: appValidation,
|
|
166
|
+
externalFiles: []
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Resolve and validate external files
|
|
171
|
+
const externalFiles = await resolveExternalFiles(appName);
|
|
172
|
+
const externalValidations = [];
|
|
173
|
+
|
|
174
|
+
for (const fileInfo of externalFiles) {
|
|
175
|
+
try {
|
|
176
|
+
const validation = await validateExternalFile(fileInfo.path, fileInfo.type);
|
|
177
|
+
externalValidations.push({
|
|
178
|
+
file: fileInfo.fileName,
|
|
179
|
+
path: fileInfo.path,
|
|
180
|
+
type: fileInfo.type,
|
|
181
|
+
...validation
|
|
182
|
+
});
|
|
183
|
+
} catch (error) {
|
|
184
|
+
externalValidations.push({
|
|
185
|
+
file: fileInfo.fileName,
|
|
186
|
+
path: fileInfo.path,
|
|
187
|
+
type: fileInfo.type,
|
|
188
|
+
valid: false,
|
|
189
|
+
errors: [error.message],
|
|
190
|
+
warnings: []
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Aggregate results
|
|
196
|
+
const allValid = appValidation.valid && externalValidations.every(v => v.valid);
|
|
197
|
+
const allErrors = [
|
|
198
|
+
...appValidation.errors,
|
|
199
|
+
...externalValidations.flatMap(v => v.errors.map(e => `${v.file}: ${e}`))
|
|
200
|
+
];
|
|
201
|
+
const allWarnings = [
|
|
202
|
+
...appValidation.warnings,
|
|
203
|
+
...externalValidations.flatMap(v => v.warnings)
|
|
204
|
+
];
|
|
205
|
+
|
|
206
|
+
return {
|
|
207
|
+
valid: allValid,
|
|
208
|
+
application: appValidation,
|
|
209
|
+
externalFiles: externalValidations,
|
|
210
|
+
errors: allErrors,
|
|
211
|
+
warnings: allWarnings
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Displays validation results in a user-friendly format
|
|
217
|
+
*
|
|
218
|
+
* @function displayValidationResults
|
|
219
|
+
* @param {Object} result - Validation result from validateAppOrFile
|
|
220
|
+
*/
|
|
221
|
+
function displayValidationResults(result) {
|
|
222
|
+
if (result.valid) {
|
|
223
|
+
logger.log(chalk.green('\n✓ Validation passed!'));
|
|
224
|
+
} else {
|
|
225
|
+
logger.log(chalk.red('\n✗ Validation failed!'));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Display application validation
|
|
229
|
+
if (result.application) {
|
|
230
|
+
logger.log(chalk.blue('\nApplication:'));
|
|
231
|
+
if (result.application.valid) {
|
|
232
|
+
logger.log(chalk.green(' ✓ Application configuration is valid'));
|
|
233
|
+
} else {
|
|
234
|
+
logger.log(chalk.red(' ✗ Application configuration has errors:'));
|
|
235
|
+
result.application.errors.forEach(error => {
|
|
236
|
+
logger.log(chalk.red(` • ${error}`));
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
if (result.application.warnings && result.application.warnings.length > 0) {
|
|
240
|
+
result.application.warnings.forEach(warning => {
|
|
241
|
+
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Display external files validation
|
|
247
|
+
if (result.externalFiles && result.externalFiles.length > 0) {
|
|
248
|
+
logger.log(chalk.blue('\nExternal Integration Files:'));
|
|
249
|
+
result.externalFiles.forEach(file => {
|
|
250
|
+
if (file.valid) {
|
|
251
|
+
logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
|
|
252
|
+
} else {
|
|
253
|
+
logger.log(chalk.red(` ✗ ${file.file} (${file.type}):`));
|
|
254
|
+
file.errors.forEach(error => {
|
|
255
|
+
logger.log(chalk.red(` • ${error}`));
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
if (file.warnings && file.warnings.length > 0) {
|
|
259
|
+
file.warnings.forEach(warning => {
|
|
260
|
+
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Display file validation (for direct file validation)
|
|
267
|
+
if (result.file) {
|
|
268
|
+
logger.log(chalk.blue(`\nFile: ${result.file}`));
|
|
269
|
+
logger.log(chalk.blue(`Type: ${result.type}`));
|
|
270
|
+
if (result.valid) {
|
|
271
|
+
logger.log(chalk.green(' ✓ File is valid'));
|
|
272
|
+
} else {
|
|
273
|
+
logger.log(chalk.red(' ✗ File has errors:'));
|
|
274
|
+
result.errors.forEach(error => {
|
|
275
|
+
logger.log(chalk.red(` • ${error}`));
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
279
|
+
result.warnings.forEach(warning => {
|
|
280
|
+
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Display aggregated warnings
|
|
286
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
287
|
+
logger.log(chalk.yellow('\nWarnings:'));
|
|
288
|
+
result.warnings.forEach(warning => {
|
|
289
|
+
logger.log(chalk.yellow(` • ${warning}`));
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
module.exports = {
|
|
295
|
+
validateAppOrFile,
|
|
296
|
+
displayValidationResults,
|
|
297
|
+
validateExternalFile
|
|
298
|
+
};
|
|
299
|
+
|