@aifabrix/builder 2.32.2 → 2.33.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/.cursor/rules/project-rules.mdc +8 -0
- package/README.md +36 -8
- package/bin/aifabrix.js +6 -8
- package/integration/hubspot/README.md +8 -7
- package/integration/hubspot/companies.json +2048 -0
- package/integration/hubspot/create-hubspot.js +665 -0
- package/integration/hubspot/{hubspot-deploy-company.json → hubspot-datasource-company.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-contact.json → hubspot-datasource-contact.json} +1 -1
- package/integration/hubspot/{hubspot-deploy-deal.json → hubspot-datasource-deal.json} +1 -1
- package/integration/hubspot/hubspot-deploy.json +832 -81
- package/integration/hubspot/hubspot-system.json +99 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-credential-real.yaml +20 -0
- package/integration/hubspot/test-artifacts/wizard-hubspot-env-vars.yaml +9 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-add-datasource.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-app-name.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-create.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-credential-select.yaml +7 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-known-platform.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-app.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-mode.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-file.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-openapi-url.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-invalid-source.yaml +4 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-array-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-test.yaml +5 -0
- package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +5 -0
- package/integration/hubspot/test-dataplane-down-helpers.js +246 -0
- package/integration/hubspot/test-dataplane-down-tests.js +419 -0
- package/integration/hubspot/test-dataplane-down.js +157 -0
- package/integration/hubspot/test.js +1517 -0
- package/integration/hubspot/variables.yaml +4 -4
- package/integration/hubspot/wizard-hubspot-e2e.yaml +16 -0
- package/integration/hubspot/wizard-hubspot-platform.yaml +8 -0
- package/lib/api/applications.api.js +1 -0
- package/lib/api/index.js +10 -5
- package/lib/api/types/wizard.types.js +176 -38
- package/lib/api/wizard.api.js +207 -38
- package/lib/app/deploy.js +116 -54
- package/lib/app/display.js +6 -5
- package/lib/app/dockerfile.js +2 -1
- package/lib/app/list.js +78 -37
- package/lib/app/prompts.js +9 -5
- package/lib/app/readme.js +41 -112
- package/lib/app/register.js +44 -9
- package/lib/app/rotate-secret.js +50 -32
- package/lib/cli.js +243 -65
- package/lib/commands/app.js +4 -9
- package/lib/commands/auth-config.js +125 -0
- package/lib/commands/auth-status.js +261 -0
- package/lib/commands/datasource.js +3 -6
- package/lib/commands/login-credentials.js +4 -4
- package/lib/commands/login-device.js +43 -29
- package/lib/commands/login.js +22 -13
- package/lib/commands/wizard-config-normalizer.js +92 -0
- package/lib/commands/wizard-core.js +515 -0
- package/lib/commands/wizard-dataplane.js +122 -0
- package/lib/commands/wizard-headless.js +115 -0
- package/lib/commands/wizard.js +129 -357
- package/lib/core/config.js +46 -0
- package/lib/core/secrets.js +3 -22
- package/lib/core/templates-env.js +1 -1
- package/lib/datasource/deploy.js +34 -23
- package/lib/datasource/list.js +8 -6
- package/lib/deployment/deployer.js +25 -0
- package/lib/deployment/environment.js +10 -13
- package/lib/external-system/delete.js +151 -0
- package/lib/external-system/deploy.js +54 -378
- package/lib/external-system/download-helpers.js +45 -65
- package/lib/external-system/download.js +34 -13
- package/lib/external-system/generator.js +11 -7
- package/lib/external-system/test-auth.js +5 -3
- package/lib/generator/builders.js +3 -1
- package/lib/generator/external-controller-manifest.js +157 -0
- package/lib/generator/external-schema-utils.js +236 -0
- package/lib/generator/external.js +55 -3
- package/lib/generator/index.js +22 -10
- package/lib/generator/wizard-prompts.js +33 -10
- package/lib/generator/wizard.js +69 -86
- package/lib/infrastructure/compose.js +100 -0
- package/lib/infrastructure/helpers.js +139 -0
- package/lib/infrastructure/index.js +52 -311
- package/lib/infrastructure/services.js +168 -0
- package/lib/schema/application-schema.json +24 -5
- package/lib/schema/external-datasource.schema.json +303 -17
- package/lib/schema/external-system.schema.json +1 -1
- package/lib/schema/wizard-config.schema.json +234 -0
- package/lib/utils/api.js +37 -42
- package/lib/utils/app-existence.js +42 -0
- package/lib/utils/app-register-config.js +7 -2
- package/lib/utils/app-register-display.js +2 -1
- package/lib/utils/auth-config-validator.js +92 -0
- package/lib/utils/cli-utils.js +3 -1
- package/lib/utils/command-header.js +43 -0
- package/lib/utils/compose-generator.js +113 -70
- package/lib/utils/controller-url.js +115 -0
- package/lib/utils/dataplane-health.js +115 -0
- package/lib/utils/dataplane-resolver.js +29 -0
- package/lib/utils/dev-config.js +6 -2
- package/lib/utils/env-copy.js +2 -1
- package/lib/utils/env-map.js +2 -1
- package/lib/utils/env-ports.js +2 -1
- package/lib/utils/env-template.js +1 -1
- package/lib/utils/error-formatter.js +149 -28
- package/lib/utils/external-readme.js +125 -0
- package/lib/utils/help-builder.js +190 -0
- package/lib/utils/infra-status.js +13 -3
- package/lib/utils/paths.js +17 -2
- package/lib/utils/port-resolver.js +111 -0
- package/lib/utils/secrets-helpers.js +3 -15
- package/lib/utils/secrets-utils.js +2 -2
- package/lib/utils/token-manager.js +69 -4
- package/lib/utils/variable-transformer.js +7 -2
- package/lib/validation/external-manifest-validator.js +202 -0
- package/lib/validation/validate-display.js +406 -0
- package/lib/validation/validate.js +159 -123
- package/lib/validation/validator.js +38 -4
- package/lib/validation/wizard-config-validator.js +267 -0
- package/package.json +4 -2
- package/templates/applications/README.md.hbs +19 -17
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/applications/miso-controller/rbac.yaml +7 -7
- package/templates/external-system/README.md.hbs +99 -0
- package/templates/external-system/external-system.json.hbs +1 -1
- package/templates/infra/compose.yaml.hbs +35 -0
- package/templates/python/docker-compose.hbs +26 -0
- package/templates/typescript/docker-compose.hbs +26 -0
|
@@ -11,13 +11,14 @@
|
|
|
11
11
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const path = require('path');
|
|
14
|
-
const chalk = require('chalk');
|
|
15
14
|
const validator = require('./validator');
|
|
16
15
|
const { resolveExternalFiles } = require('../utils/schema-resolver');
|
|
17
16
|
const { loadExternalSystemSchema, loadExternalDataSourceSchema, detectSchemaType } = require('../utils/schema-loader');
|
|
18
17
|
const { formatValidationErrors } = require('../utils/error-formatter');
|
|
19
18
|
const { detectAppType } = require('../utils/paths');
|
|
20
|
-
const
|
|
19
|
+
const { displayValidationResults } = require('./validate-display');
|
|
20
|
+
const { generateControllerManifest } = require('../generator/external-controller-manifest');
|
|
21
|
+
const { validateControllerManifest } = require('./external-manifest-validator');
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Validates a file path (detects type and validates)
|
|
@@ -204,6 +205,8 @@ async function validateExternalFile(filePath, type) {
|
|
|
204
205
|
* @async
|
|
205
206
|
* @function validateAppOrFile
|
|
206
207
|
* @param {string} appOrFile - Application name or file path
|
|
208
|
+
* @param {Object} [options] - Validation options
|
|
209
|
+
* @param {string} [options.type] - Forced application type (external)
|
|
207
210
|
* @returns {Promise<Object>} Validation result with aggregated results
|
|
208
211
|
* @throws {Error} If validation fails
|
|
209
212
|
*
|
|
@@ -277,158 +280,191 @@ function loadVariablesAndCheckExternalIntegration(variablesPath, appValidation)
|
|
|
277
280
|
return null;
|
|
278
281
|
}
|
|
279
282
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
283
|
+
/**
|
|
284
|
+
* Validates application configuration step
|
|
285
|
+
* @async
|
|
286
|
+
* @function validateApplicationStep
|
|
287
|
+
* @param {string} appName - Application name
|
|
288
|
+
* @returns {Promise<Object>} Application validation result
|
|
289
|
+
*/
|
|
290
|
+
async function validateApplicationStep(appName) {
|
|
291
|
+
try {
|
|
292
|
+
const appValidation = await validator.validateApplication(appName);
|
|
293
|
+
return {
|
|
294
|
+
valid: appValidation.valid,
|
|
295
|
+
errors: appValidation.errors || [],
|
|
296
|
+
warnings: appValidation.warnings || []
|
|
297
|
+
};
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return {
|
|
300
|
+
valid: false,
|
|
301
|
+
errors: [error.message],
|
|
302
|
+
warnings: []
|
|
303
|
+
};
|
|
299
304
|
}
|
|
300
|
-
|
|
301
|
-
const externalValidations = await validateExternalFilesForApp(appName);
|
|
302
|
-
return aggregateValidationResults(appValidation, externalValidations, rbacValidation);
|
|
303
305
|
}
|
|
304
306
|
|
|
305
307
|
/**
|
|
306
|
-
*
|
|
307
|
-
*
|
|
308
|
-
* @function
|
|
309
|
-
* @param {
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Displays application validation results
|
|
313
|
-
* @function displayApplicationValidation
|
|
314
|
-
* @param {Object} application - Application validation result
|
|
308
|
+
* Validates individual component files step
|
|
309
|
+
* @async
|
|
310
|
+
* @function validateComponentsStep
|
|
311
|
+
* @param {string} appName - Application name
|
|
312
|
+
* @returns {Promise<Object>} Components validation result
|
|
315
313
|
*/
|
|
316
|
-
function
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
314
|
+
async function validateComponentsStep(appName) {
|
|
315
|
+
try {
|
|
316
|
+
const externalValidations = await validateExternalFilesForApp(appName);
|
|
317
|
+
const componentErrors = [];
|
|
318
|
+
const componentWarnings = [];
|
|
319
|
+
const componentFiles = [];
|
|
320
|
+
|
|
321
|
+
externalValidations.forEach(validation => {
|
|
322
|
+
componentFiles.push({
|
|
323
|
+
file: validation.file,
|
|
324
|
+
type: validation.type,
|
|
325
|
+
valid: validation.valid,
|
|
326
|
+
path: validation.path
|
|
327
|
+
});
|
|
320
328
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
logger.log(chalk.red(` • ${error}`));
|
|
328
|
-
});
|
|
329
|
-
}
|
|
330
|
-
if (application.warnings && application.warnings.length > 0) {
|
|
331
|
-
application.warnings.forEach(warning => {
|
|
332
|
-
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
329
|
+
if (!validation.valid) {
|
|
330
|
+
componentErrors.push(`${validation.file} (${validation.type}): ${validation.errors.join(', ')}`);
|
|
331
|
+
}
|
|
332
|
+
if (validation.warnings && validation.warnings.length > 0) {
|
|
333
|
+
componentWarnings.push(`${validation.file}: ${validation.warnings.join(', ')}`);
|
|
334
|
+
}
|
|
333
335
|
});
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
valid: componentErrors.length === 0,
|
|
339
|
+
errors: componentErrors,
|
|
340
|
+
warnings: componentWarnings,
|
|
341
|
+
files: componentFiles
|
|
342
|
+
};
|
|
343
|
+
} catch (error) {
|
|
344
|
+
return {
|
|
345
|
+
valid: false,
|
|
346
|
+
errors: [error.message],
|
|
347
|
+
warnings: [],
|
|
348
|
+
files: []
|
|
349
|
+
};
|
|
334
350
|
}
|
|
335
351
|
}
|
|
336
352
|
|
|
337
353
|
/**
|
|
338
|
-
*
|
|
339
|
-
* @
|
|
340
|
-
* @
|
|
354
|
+
* Validates full deployment manifest step
|
|
355
|
+
* @async
|
|
356
|
+
* @function validateManifestStep
|
|
357
|
+
* @param {string} appName - Application name
|
|
358
|
+
* @returns {Promise<Object>} Manifest validation result
|
|
341
359
|
*/
|
|
342
|
-
function
|
|
343
|
-
|
|
344
|
-
|
|
360
|
+
async function validateManifestStep(appName) {
|
|
361
|
+
try {
|
|
362
|
+
const manifest = await generateControllerManifest(appName);
|
|
363
|
+
const manifestValidation = await validateControllerManifest(manifest);
|
|
364
|
+
return {
|
|
365
|
+
valid: manifestValidation.valid,
|
|
366
|
+
errors: manifestValidation.errors || [],
|
|
367
|
+
warnings: manifestValidation.warnings || []
|
|
368
|
+
};
|
|
369
|
+
} catch (error) {
|
|
370
|
+
return {
|
|
371
|
+
valid: false,
|
|
372
|
+
errors: [`Failed to validate manifest: ${error.message}`],
|
|
373
|
+
warnings: []
|
|
374
|
+
};
|
|
345
375
|
}
|
|
346
|
-
|
|
347
|
-
logger.log(chalk.blue('\nExternal Integration Files:'));
|
|
348
|
-
externalFiles.forEach(file => {
|
|
349
|
-
if (file.valid) {
|
|
350
|
-
logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
|
|
351
|
-
} else {
|
|
352
|
-
logger.log(chalk.red(` ✗ ${file.file} (${file.type}):`));
|
|
353
|
-
file.errors.forEach(error => {
|
|
354
|
-
logger.log(chalk.red(` • ${error}`));
|
|
355
|
-
});
|
|
356
|
-
}
|
|
357
|
-
if (file.warnings && file.warnings.length > 0) {
|
|
358
|
-
file.warnings.forEach(warning => {
|
|
359
|
-
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
360
|
-
});
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
376
|
}
|
|
364
377
|
|
|
365
378
|
/**
|
|
366
|
-
*
|
|
367
|
-
*
|
|
368
|
-
*
|
|
379
|
+
* Validates external system completely (components + full manifest)
|
|
380
|
+
* Performs step-by-step validation: application config → components → full manifest
|
|
381
|
+
*
|
|
382
|
+
* @async
|
|
383
|
+
* @function validateExternalSystemComplete
|
|
384
|
+
* @param {string} appName - Application name
|
|
385
|
+
* @returns {Promise<Object>} Complete validation result with step-by-step results
|
|
386
|
+
* @throws {Error} If validation fails critically
|
|
387
|
+
*
|
|
388
|
+
* @example
|
|
389
|
+
* const result = await validateExternalSystemComplete('my-hubspot');
|
|
390
|
+
* // Returns: { valid: true, errors: [], warnings: [], steps: {...} }
|
|
369
391
|
*/
|
|
370
|
-
function
|
|
371
|
-
if (!
|
|
372
|
-
|
|
392
|
+
async function validateExternalSystemComplete(appName) {
|
|
393
|
+
if (!appName || typeof appName !== 'string') {
|
|
394
|
+
throw new Error('App name is required and must be a string');
|
|
373
395
|
}
|
|
374
396
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
397
|
+
const steps = {
|
|
398
|
+
application: { valid: false, errors: [], warnings: [] },
|
|
399
|
+
components: { valid: false, errors: [], warnings: [], files: [] },
|
|
400
|
+
manifest: { valid: false, errors: [], warnings: [] }
|
|
401
|
+
};
|
|
402
|
+
|
|
403
|
+
// Step 1: Validate Application Config
|
|
404
|
+
steps.application = await validateApplicationStep(appName);
|
|
405
|
+
|
|
406
|
+
// Step 2: Validate Individual Components
|
|
407
|
+
steps.components = await validateComponentsStep(appName);
|
|
408
|
+
|
|
409
|
+
// If components have errors, return early (don't validate manifest)
|
|
410
|
+
if (!steps.components.valid) {
|
|
411
|
+
return {
|
|
412
|
+
valid: false,
|
|
413
|
+
errors: [...steps.application.errors, ...steps.components.errors],
|
|
414
|
+
warnings: [...steps.application.warnings, ...steps.components.warnings],
|
|
415
|
+
steps
|
|
416
|
+
};
|
|
388
417
|
}
|
|
418
|
+
|
|
419
|
+
// Step 3 & 4: Generate and Validate Full Manifest (only if Step 2 passes)
|
|
420
|
+
steps.manifest = await validateManifestStep(appName);
|
|
421
|
+
|
|
422
|
+
// Aggregate Results
|
|
423
|
+
const allErrors = [...steps.application.errors, ...steps.components.errors, ...steps.manifest.errors];
|
|
424
|
+
const allWarnings = [...steps.application.warnings, ...steps.components.warnings, ...steps.manifest.warnings];
|
|
425
|
+
|
|
426
|
+
return {
|
|
427
|
+
valid: allErrors.length === 0,
|
|
428
|
+
errors: allErrors,
|
|
429
|
+
warnings: allWarnings,
|
|
430
|
+
steps
|
|
431
|
+
};
|
|
389
432
|
}
|
|
390
433
|
|
|
391
|
-
function
|
|
392
|
-
if (
|
|
393
|
-
|
|
394
|
-
} else {
|
|
395
|
-
logger.log(chalk.red('\n✗ Validation failed!'));
|
|
434
|
+
async function validateAppOrFile(appOrFile, options = {}) {
|
|
435
|
+
if (!appOrFile || typeof appOrFile !== 'string') {
|
|
436
|
+
throw new Error('App name or file path is required');
|
|
396
437
|
}
|
|
397
438
|
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
// Display file validation (for direct file validation)
|
|
403
|
-
if (result.file) {
|
|
404
|
-
logger.log(chalk.blue(`\nFile: ${result.file}`));
|
|
405
|
-
logger.log(chalk.blue(`Type: ${result.type}`));
|
|
406
|
-
if (result.valid) {
|
|
407
|
-
logger.log(chalk.green(' ✓ File is valid'));
|
|
408
|
-
} else {
|
|
409
|
-
logger.log(chalk.red(' ✗ File has errors:'));
|
|
410
|
-
result.errors.forEach(error => {
|
|
411
|
-
logger.log(chalk.red(` • ${error}`));
|
|
412
|
-
});
|
|
413
|
-
}
|
|
414
|
-
if (result.warnings && result.warnings.length > 0) {
|
|
415
|
-
result.warnings.forEach(warning => {
|
|
416
|
-
logger.log(chalk.yellow(` ⚠ ${warning}`));
|
|
417
|
-
});
|
|
418
|
-
}
|
|
439
|
+
const isFilePath = fs.existsSync(appOrFile) && fs.statSync(appOrFile).isFile();
|
|
440
|
+
if (isFilePath) {
|
|
441
|
+
return await validateFilePath(appOrFile);
|
|
419
442
|
}
|
|
420
443
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
444
|
+
const appName = appOrFile;
|
|
445
|
+
const { appPath, isExternal } = await detectAppType(appName, options);
|
|
446
|
+
|
|
447
|
+
// For external systems with --type external flag, use new unified validation
|
|
448
|
+
if (isExternal && options.type === 'external') {
|
|
449
|
+
return await validateExternalSystemComplete(appName);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const appValidation = await validator.validateApplication(appName);
|
|
453
|
+
const rbacValidation = await validateRbacForExternalSystem(isExternal, appName);
|
|
454
|
+
|
|
455
|
+
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
456
|
+
const earlyReturn = loadVariablesAndCheckExternalIntegration(variablesPath, appValidation);
|
|
457
|
+
if (earlyReturn) {
|
|
458
|
+
return earlyReturn;
|
|
427
459
|
}
|
|
460
|
+
|
|
461
|
+
const externalValidations = await validateExternalFilesForApp(appName);
|
|
462
|
+
return aggregateValidationResults(appValidation, externalValidations, rbacValidation);
|
|
428
463
|
}
|
|
429
464
|
|
|
430
465
|
module.exports = {
|
|
431
466
|
validateAppOrFile,
|
|
467
|
+
validateExternalSystemComplete,
|
|
432
468
|
displayValidationResults,
|
|
433
469
|
validateExternalFile,
|
|
434
470
|
validateExternalFilesForApp,
|
|
@@ -98,6 +98,27 @@ function validateExternalIntegrationBlock(variables, errors) {
|
|
|
98
98
|
}
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Validates frontDoorRouting configuration
|
|
103
|
+
* @function validateFrontDoorRouting
|
|
104
|
+
* @param {Object} variables - Variables object
|
|
105
|
+
* @param {string[]} errors - Errors array to append to
|
|
106
|
+
*/
|
|
107
|
+
function validateFrontDoorRouting(variables, errors) {
|
|
108
|
+
const frontDoor = variables.frontDoorRouting;
|
|
109
|
+
if (!frontDoor) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (frontDoor.enabled === true && (!frontDoor.host || typeof frontDoor.host !== 'string')) {
|
|
114
|
+
errors.push('frontDoorRouting.host is required when frontDoorRouting.enabled is true');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (frontDoor.pattern && !String(frontDoor.pattern).startsWith('/')) {
|
|
118
|
+
errors.push('frontDoorRouting.pattern must start with "/"');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
101
122
|
async function validateVariables(appName) {
|
|
102
123
|
if (!appName || typeof appName !== 'string') {
|
|
103
124
|
throw new Error('App name is required and must be a string');
|
|
@@ -115,6 +136,8 @@ async function validateVariables(appName) {
|
|
|
115
136
|
validateExternalIntegrationBlock(variables, errors);
|
|
116
137
|
}
|
|
117
138
|
|
|
139
|
+
validateFrontDoorRouting(variables, errors);
|
|
140
|
+
|
|
118
141
|
return {
|
|
119
142
|
valid: valid && errors.length === 0,
|
|
120
143
|
errors,
|
|
@@ -152,6 +175,10 @@ function validateRoles(roles) {
|
|
|
152
175
|
} else {
|
|
153
176
|
roleNames.add(role.value);
|
|
154
177
|
}
|
|
178
|
+
// Reject Groups (capital G) - must use groups (lowercase)
|
|
179
|
+
if (role.Groups !== undefined) {
|
|
180
|
+
errors.push(`Role at index ${index} uses 'Groups' (capital G) but must use 'groups' (lowercase) for schema compatibility`);
|
|
181
|
+
}
|
|
155
182
|
});
|
|
156
183
|
return errors;
|
|
157
184
|
}
|
|
@@ -229,7 +256,9 @@ async function validateEnvTemplate(appName) {
|
|
|
229
256
|
throw new Error('App name is required and must be a string');
|
|
230
257
|
}
|
|
231
258
|
|
|
232
|
-
|
|
259
|
+
// Support both builder/ and integration/ directories using detectAppType
|
|
260
|
+
const { appPath } = await detectAppType(appName);
|
|
261
|
+
const templatePath = path.join(appPath, 'env.template');
|
|
233
262
|
|
|
234
263
|
if (!fs.existsSync(templatePath)) {
|
|
235
264
|
throw new Error(`env.template not found: ${templatePath}`);
|
|
@@ -296,7 +325,8 @@ function validateDeploymentJson(deployment) {
|
|
|
296
325
|
};
|
|
297
326
|
}
|
|
298
327
|
|
|
299
|
-
|
|
328
|
+
// verbose: true includes the actual data value in error objects for better error messages
|
|
329
|
+
const ajv = new Ajv({ allErrors: true, strict: false, verbose: true });
|
|
300
330
|
// Register external schemas with their $id (GitHub raw URLs)
|
|
301
331
|
// Create copies to avoid modifying the original schemas
|
|
302
332
|
const externalSystemSchemaCopy = { ...externalSystemSchema };
|
|
@@ -340,15 +370,19 @@ async function validateApplication(appName) {
|
|
|
340
370
|
const env = await validateEnvTemplate(appName);
|
|
341
371
|
|
|
342
372
|
const valid = variables.valid && rbac.valid && env.valid;
|
|
373
|
+
const errors = [...(variables.errors || []), ...(rbac.errors || []), ...(env.errors || [])];
|
|
374
|
+
const warnings = [...(variables.warnings || []), ...(rbac.warnings || []), ...(env.warnings || [])];
|
|
343
375
|
|
|
344
376
|
return {
|
|
345
377
|
valid,
|
|
346
378
|
variables,
|
|
347
379
|
rbac,
|
|
348
380
|
env,
|
|
381
|
+
errors,
|
|
382
|
+
warnings,
|
|
349
383
|
summary: {
|
|
350
|
-
totalErrors:
|
|
351
|
-
totalWarnings:
|
|
384
|
+
totalErrors: errors.length,
|
|
385
|
+
totalWarnings: warnings.length
|
|
352
386
|
}
|
|
353
387
|
};
|
|
354
388
|
}
|