@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.
Files changed (130) hide show
  1. package/.cursor/rules/project-rules.mdc +8 -0
  2. package/README.md +36 -8
  3. package/bin/aifabrix.js +6 -8
  4. package/integration/hubspot/README.md +8 -7
  5. package/integration/hubspot/companies.json +2048 -0
  6. package/integration/hubspot/create-hubspot.js +665 -0
  7. package/integration/hubspot/{hubspot-deploy-company.json → hubspot-datasource-company.json} +1 -1
  8. package/integration/hubspot/{hubspot-deploy-contact.json → hubspot-datasource-contact.json} +1 -1
  9. package/integration/hubspot/{hubspot-deploy-deal.json → hubspot-datasource-deal.json} +1 -1
  10. package/integration/hubspot/hubspot-deploy.json +832 -81
  11. package/integration/hubspot/hubspot-system.json +99 -0
  12. package/integration/hubspot/test-artifacts/wizard-hubspot-credential-real.yaml +20 -0
  13. package/integration/hubspot/test-artifacts/wizard-hubspot-env-vars.yaml +9 -0
  14. package/integration/hubspot/test-artifacts/wizard-invalid-add-datasource.yaml +5 -0
  15. package/integration/hubspot/test-artifacts/wizard-invalid-app-name.yaml +5 -0
  16. package/integration/hubspot/test-artifacts/wizard-invalid-credential-create.yaml +7 -0
  17. package/integration/hubspot/test-artifacts/wizard-invalid-credential-select.yaml +7 -0
  18. package/integration/hubspot/test-artifacts/wizard-invalid-known-platform.yaml +4 -0
  19. package/integration/hubspot/test-artifacts/wizard-invalid-missing-app.yaml +4 -0
  20. package/integration/hubspot/test-artifacts/wizard-invalid-missing-source.yaml +2 -0
  21. package/integration/hubspot/test-artifacts/wizard-invalid-mode.yaml +5 -0
  22. package/integration/hubspot/test-artifacts/wizard-invalid-openapi-file.yaml +5 -0
  23. package/integration/hubspot/test-artifacts/wizard-invalid-openapi-url.yaml +4 -0
  24. package/integration/hubspot/test-artifacts/wizard-invalid-source.yaml +4 -0
  25. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-array-test.yaml +5 -0
  26. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-key-test.yaml +5 -0
  27. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-path-test.yaml +5 -0
  28. package/integration/hubspot/test-artifacts/wizard-valid-for-dimension-test.yaml +5 -0
  29. package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-test.yaml +5 -0
  30. package/integration/hubspot/test-artifacts/wizard-valid-for-rbac-yaml-test.yaml +5 -0
  31. package/integration/hubspot/test-dataplane-down-helpers.js +246 -0
  32. package/integration/hubspot/test-dataplane-down-tests.js +419 -0
  33. package/integration/hubspot/test-dataplane-down.js +157 -0
  34. package/integration/hubspot/test.js +1517 -0
  35. package/integration/hubspot/variables.yaml +4 -4
  36. package/integration/hubspot/wizard-hubspot-e2e.yaml +16 -0
  37. package/integration/hubspot/wizard-hubspot-platform.yaml +8 -0
  38. package/lib/api/applications.api.js +1 -0
  39. package/lib/api/index.js +10 -5
  40. package/lib/api/types/wizard.types.js +176 -38
  41. package/lib/api/wizard.api.js +207 -38
  42. package/lib/app/deploy.js +116 -54
  43. package/lib/app/display.js +6 -5
  44. package/lib/app/dockerfile.js +2 -1
  45. package/lib/app/list.js +78 -37
  46. package/lib/app/prompts.js +9 -5
  47. package/lib/app/readme.js +41 -112
  48. package/lib/app/register.js +44 -9
  49. package/lib/app/rotate-secret.js +50 -32
  50. package/lib/cli.js +243 -65
  51. package/lib/commands/app.js +4 -9
  52. package/lib/commands/auth-config.js +125 -0
  53. package/lib/commands/auth-status.js +261 -0
  54. package/lib/commands/datasource.js +3 -6
  55. package/lib/commands/login-credentials.js +4 -4
  56. package/lib/commands/login-device.js +43 -29
  57. package/lib/commands/login.js +22 -13
  58. package/lib/commands/wizard-config-normalizer.js +92 -0
  59. package/lib/commands/wizard-core.js +515 -0
  60. package/lib/commands/wizard-dataplane.js +122 -0
  61. package/lib/commands/wizard-headless.js +115 -0
  62. package/lib/commands/wizard.js +129 -357
  63. package/lib/core/config.js +46 -0
  64. package/lib/core/secrets.js +3 -22
  65. package/lib/core/templates-env.js +1 -1
  66. package/lib/datasource/deploy.js +34 -23
  67. package/lib/datasource/list.js +8 -6
  68. package/lib/deployment/deployer.js +25 -0
  69. package/lib/deployment/environment.js +10 -13
  70. package/lib/external-system/delete.js +151 -0
  71. package/lib/external-system/deploy.js +54 -378
  72. package/lib/external-system/download-helpers.js +45 -65
  73. package/lib/external-system/download.js +34 -13
  74. package/lib/external-system/generator.js +11 -7
  75. package/lib/external-system/test-auth.js +5 -3
  76. package/lib/generator/builders.js +3 -1
  77. package/lib/generator/external-controller-manifest.js +157 -0
  78. package/lib/generator/external-schema-utils.js +236 -0
  79. package/lib/generator/external.js +55 -3
  80. package/lib/generator/index.js +22 -10
  81. package/lib/generator/wizard-prompts.js +33 -10
  82. package/lib/generator/wizard.js +69 -86
  83. package/lib/infrastructure/compose.js +100 -0
  84. package/lib/infrastructure/helpers.js +139 -0
  85. package/lib/infrastructure/index.js +52 -311
  86. package/lib/infrastructure/services.js +168 -0
  87. package/lib/schema/application-schema.json +24 -5
  88. package/lib/schema/external-datasource.schema.json +303 -17
  89. package/lib/schema/external-system.schema.json +1 -1
  90. package/lib/schema/wizard-config.schema.json +234 -0
  91. package/lib/utils/api.js +37 -42
  92. package/lib/utils/app-existence.js +42 -0
  93. package/lib/utils/app-register-config.js +7 -2
  94. package/lib/utils/app-register-display.js +2 -1
  95. package/lib/utils/auth-config-validator.js +92 -0
  96. package/lib/utils/cli-utils.js +3 -1
  97. package/lib/utils/command-header.js +43 -0
  98. package/lib/utils/compose-generator.js +113 -70
  99. package/lib/utils/controller-url.js +115 -0
  100. package/lib/utils/dataplane-health.js +115 -0
  101. package/lib/utils/dataplane-resolver.js +29 -0
  102. package/lib/utils/dev-config.js +6 -2
  103. package/lib/utils/env-copy.js +2 -1
  104. package/lib/utils/env-map.js +2 -1
  105. package/lib/utils/env-ports.js +2 -1
  106. package/lib/utils/env-template.js +1 -1
  107. package/lib/utils/error-formatter.js +149 -28
  108. package/lib/utils/external-readme.js +125 -0
  109. package/lib/utils/help-builder.js +190 -0
  110. package/lib/utils/infra-status.js +13 -3
  111. package/lib/utils/paths.js +17 -2
  112. package/lib/utils/port-resolver.js +111 -0
  113. package/lib/utils/secrets-helpers.js +3 -15
  114. package/lib/utils/secrets-utils.js +2 -2
  115. package/lib/utils/token-manager.js +69 -4
  116. package/lib/utils/variable-transformer.js +7 -2
  117. package/lib/validation/external-manifest-validator.js +202 -0
  118. package/lib/validation/validate-display.js +406 -0
  119. package/lib/validation/validate.js +159 -123
  120. package/lib/validation/validator.js +38 -4
  121. package/lib/validation/wizard-config-validator.js +267 -0
  122. package/package.json +4 -2
  123. package/templates/applications/README.md.hbs +19 -17
  124. package/templates/applications/miso-controller/env.template +1 -1
  125. package/templates/applications/miso-controller/rbac.yaml +7 -7
  126. package/templates/external-system/README.md.hbs +99 -0
  127. package/templates/external-system/external-system.json.hbs +1 -1
  128. package/templates/infra/compose.yaml.hbs +35 -0
  129. package/templates/python/docker-compose.hbs +26 -0
  130. package/templates/typescript/docker-compose.hbs +26 -0
@@ -0,0 +1,202 @@
1
+ /**
2
+ * External System Manifest Validator
3
+ *
4
+ * Validates controller deployment manifest for external systems.
5
+ * Validates against application-schema.json and component schemas.
6
+ *
7
+ * @fileoverview Manifest validation for external systems
8
+ * @author AI Fabrix Team
9
+ * @version 2.0.0
10
+ */
11
+
12
+ const Ajv = require('ajv');
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+ const { formatValidationErrors } = require('../utils/error-formatter');
16
+
17
+ /**
18
+ * Sets up AJV validator with external schemas
19
+ * @async
20
+ * @function setupAjvWithSchemas
21
+ * @returns {Promise<Object>} AJV instance and schemas
22
+ */
23
+ async function setupAjvWithSchemas() {
24
+ const ajv = new Ajv({
25
+ allErrors: true,
26
+ strict: false,
27
+ removeAdditional: false
28
+ });
29
+
30
+ // Load raw schema objects (not compiled validators)
31
+ const externalSystemSchemaPath = path.join(__dirname, '..', 'schema', 'external-system.schema.json');
32
+ const externalDatasourceSchemaPath = path.join(__dirname, '..', 'schema', 'external-datasource.schema.json');
33
+
34
+ const externalSystemSchema = JSON.parse(fs.readFileSync(externalSystemSchemaPath, 'utf8'));
35
+ let externalDatasourceSchema = JSON.parse(fs.readFileSync(externalDatasourceSchemaPath, 'utf8'));
36
+
37
+ // Remove $schema for draft-2020-12 to avoid AJV issues
38
+ if (externalDatasourceSchema.$schema && externalDatasourceSchema.$schema.includes('2020-12')) {
39
+ const schemaCopy = { ...externalDatasourceSchema };
40
+ delete schemaCopy.$schema;
41
+ externalDatasourceSchema = schemaCopy;
42
+ }
43
+
44
+ const externalSystemSchemaId = externalSystemSchema.$id || 'https://raw.githubusercontent.com/esystemsdev/aifabrix-builder/refs/heads/main/lib/schema/external-system.schema.json';
45
+ const externalDatasourceSchemaId = externalDatasourceSchema.$id || 'https://raw.githubusercontent.com/esystemsdev/aifabrix-builder/refs/heads/main/lib/schema/external-datasource.schema.json';
46
+
47
+ ajv.addSchema(externalSystemSchema, externalSystemSchemaId);
48
+ ajv.addSchema(externalDatasourceSchema, externalDatasourceSchemaId);
49
+
50
+ return { ajv, externalSystemSchema, externalDatasourceSchema };
51
+ }
52
+
53
+ /**
54
+ * Validates manifest structure
55
+ * @function validateManifestStructure
56
+ * @param {Object} manifest - Manifest object
57
+ * @param {Object} ajv - AJV instance
58
+ * @param {Object} applicationSchema - Application schema
59
+ * @param {Array} errors - Errors array to append to
60
+ * @returns {void}
61
+ */
62
+ function validateManifestStructure(manifest, ajv, applicationSchema, errors) {
63
+ const validateManifest = ajv.compile(applicationSchema);
64
+ const manifestValid = validateManifest(manifest);
65
+
66
+ if (!manifestValid) {
67
+ const manifestErrors = formatValidationErrors(validateManifest.errors);
68
+ errors.push(...manifestErrors.map(err => `Manifest validation: ${err}`));
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Validates inline system
74
+ * @function validateInlineSystem
75
+ * @param {Object} manifest - Manifest object
76
+ * @param {Object} ajv - AJV instance
77
+ * @param {Object} externalSystemSchema - External system schema
78
+ * @param {Array} errors - Errors array to append to
79
+ * @returns {void}
80
+ */
81
+ function validateInlineSystem(manifest, ajv, externalSystemSchema, errors) {
82
+ if (manifest.system) {
83
+ const validateSystem = ajv.compile(externalSystemSchema);
84
+ const systemValid = validateSystem(manifest.system);
85
+
86
+ if (!systemValid) {
87
+ const systemErrors = formatValidationErrors(validateSystem.errors);
88
+ errors.push(...systemErrors.map(err => `System validation: ${err}`));
89
+ }
90
+ } else if (manifest.type === 'external') {
91
+ errors.push('System is required for external type applications');
92
+ }
93
+ }
94
+
95
+ /**
96
+ * Validates datasources
97
+ * @function validateDatasources
98
+ * @param {Object} manifest - Manifest object
99
+ * @param {Object} ajv - AJV instance
100
+ * @param {Object} externalDatasourceSchema - External datasource schema
101
+ * @param {Array} errors - Errors array to append to
102
+ * @param {Array} warnings - Warnings array to append to
103
+ * @returns {void}
104
+ */
105
+ function validateDatasources(manifest, ajv, externalDatasourceSchema, errors, warnings) {
106
+ if (manifest.dataSources) {
107
+ if (!Array.isArray(manifest.dataSources)) {
108
+ errors.push('dataSources must be an array');
109
+ } else {
110
+ const validateDatasource = ajv.compile(externalDatasourceSchema);
111
+ manifest.dataSources.forEach((datasource, index) => {
112
+ const datasourceValid = validateDatasource(datasource);
113
+ if (!datasourceValid) {
114
+ const datasourceErrors = formatValidationErrors(validateDatasource.errors);
115
+ errors.push(...datasourceErrors.map(err => `Datasource ${index + 1} (${datasource.key || 'unknown'}): ${err}`));
116
+ }
117
+ });
118
+ }
119
+ } else if (manifest.type === 'external') {
120
+ warnings.push('No dataSources specified - external system may not have any datasources');
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Validates conditional requirements
126
+ * @function validateConditionalRequirements
127
+ * @param {Object} manifest - Manifest object
128
+ * @param {Array} errors - Errors array to append to
129
+ * @param {Array} warnings - Warnings array to append to
130
+ * @returns {void}
131
+ */
132
+ function validateConditionalRequirements(manifest, errors, warnings) {
133
+ if (manifest.type === 'external') {
134
+ if (!manifest.system) {
135
+ errors.push('System is required for external type applications');
136
+ }
137
+ if (!manifest.dataSources || manifest.dataSources.length === 0) {
138
+ warnings.push('No dataSources specified for external system');
139
+ }
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Validates required fields
145
+ * @function validateRequiredFields
146
+ * @param {Object} manifest - Manifest object
147
+ * @param {Array} errors - Errors array to append to
148
+ * @returns {void}
149
+ */
150
+ function validateRequiredFields(manifest, errors) {
151
+ const requiredFields = ['key', 'displayName', 'description', 'type', 'deploymentKey'];
152
+ requiredFields.forEach(field => {
153
+ if (!manifest[field]) {
154
+ errors.push(`Required field "${field}" is missing`);
155
+ }
156
+ });
157
+ }
158
+
159
+ /**
160
+ * Validates controller deployment manifest for external systems
161
+ * Validates manifest structure and inline system/dataSources against their schemas
162
+ *
163
+ * @async
164
+ * @function validateControllerManifest
165
+ * @param {Object} manifest - Controller manifest object
166
+ * @returns {Promise<Object>} Validation result
167
+ * @throws {Error} If validation fails critically
168
+ *
169
+ * @example
170
+ * const result = await validateControllerManifest(manifest);
171
+ * // Returns: { valid: true, errors: [], warnings: [] }
172
+ */
173
+ async function validateControllerManifest(manifest) {
174
+ if (!manifest || typeof manifest !== 'object') {
175
+ return {
176
+ valid: false,
177
+ errors: ['Manifest is required and must be an object'],
178
+ warnings: []
179
+ };
180
+ }
181
+
182
+ const errors = [];
183
+ const warnings = [];
184
+ const applicationSchema = require('../schema/application-schema.json');
185
+ const { ajv, externalSystemSchema, externalDatasourceSchema } = await setupAjvWithSchemas();
186
+
187
+ validateManifestStructure(manifest, ajv, applicationSchema, errors);
188
+ validateInlineSystem(manifest, ajv, externalSystemSchema, errors);
189
+ validateDatasources(manifest, ajv, externalDatasourceSchema, errors, warnings);
190
+ validateConditionalRequirements(manifest, errors, warnings);
191
+ validateRequiredFields(manifest, errors);
192
+
193
+ return {
194
+ valid: errors.length === 0,
195
+ errors,
196
+ warnings
197
+ };
198
+ }
199
+
200
+ module.exports = {
201
+ validateControllerManifest
202
+ };
@@ -0,0 +1,406 @@
1
+ /**
2
+ * Validation Display Utilities
3
+ *
4
+ * Display functions for validation results output.
5
+ *
6
+ * @fileoverview Validation display utilities for AI Fabrix Builder
7
+ * @author AI Fabrix Team
8
+ * @version 2.0.0
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const chalk = require('chalk');
13
+ const logger = require('../utils/logger');
14
+
15
+ /**
16
+ * Displays application validation results
17
+ * @function displayApplicationValidation
18
+ * @param {Object} application - Application validation result
19
+ */
20
+ function displayApplicationValidation(application) {
21
+ if (!application) {
22
+ return;
23
+ }
24
+
25
+ logger.log(chalk.blue('\nApplication:'));
26
+ if (application.valid) {
27
+ logger.log(chalk.green(' ✓ Application configuration is valid'));
28
+ } else {
29
+ logger.log(chalk.red(' ✗ Application configuration has errors:'));
30
+ if (application.errors && application.errors.length > 0) {
31
+ application.errors.forEach(error => {
32
+ logger.log(chalk.red(` • ${error}`));
33
+ });
34
+ }
35
+ }
36
+ if (application.warnings && application.warnings.length > 0) {
37
+ application.warnings.forEach(warning => {
38
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
39
+ });
40
+ }
41
+ }
42
+
43
+ /**
44
+ * Extracts dimensions from a datasource file
45
+ * @function extractDimensionsFromDatasource
46
+ * @param {string} filePath - Path to datasource file
47
+ * @returns {Object} Dimensions info { dimensions: Object, hasDimensions: boolean }
48
+ */
49
+ function extractDimensionsFromDatasource(filePath) {
50
+ try {
51
+ const content = fs.readFileSync(filePath, 'utf8');
52
+ const parsed = JSON.parse(content);
53
+
54
+ // Check fieldMappings.dimensions (primary location)
55
+ const dimensions = parsed.fieldMappings?.dimensions || {};
56
+ const abacDimensions = parsed.abac?.dimensions || {};
57
+
58
+ // Merge both sources (abac.dimensions can override fieldMappings.dimensions)
59
+ const allDimensions = { ...dimensions, ...abacDimensions };
60
+ const dimensionKeys = Object.keys(allDimensions);
61
+
62
+ return {
63
+ dimensions: allDimensions,
64
+ dimensionKeys,
65
+ hasDimensions: dimensionKeys.length > 0
66
+ };
67
+ } catch {
68
+ return { dimensions: {}, dimensionKeys: [], hasDimensions: false };
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Displays external files validation results
74
+ * @function displayExternalFilesValidation
75
+ * @param {Array} externalFiles - External files validation results
76
+ */
77
+ function displayExternalFilesValidation(externalFiles) {
78
+ if (!externalFiles || externalFiles.length === 0) {
79
+ return;
80
+ }
81
+
82
+ logger.log(chalk.blue('\nExternal Integration Files:'));
83
+ externalFiles.forEach(file => {
84
+ if (file.valid) {
85
+ logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
86
+ } else {
87
+ logger.log(chalk.red(` ✗ ${file.file} (${file.type}):`));
88
+ file.errors.forEach(error => {
89
+ logger.log(chalk.red(` • ${error}`));
90
+ });
91
+ }
92
+ if (file.warnings && file.warnings.length > 0) {
93
+ file.warnings.forEach(warning => {
94
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
95
+ });
96
+ }
97
+ });
98
+ }
99
+
100
+ /**
101
+ * Displays Dimensions (ABAC) validation results for datasources
102
+ * @function displayDimensionsValidation
103
+ * @param {Array} externalFiles - External files validation results
104
+ * @returns {Array} Warnings to add to aggregated warnings
105
+ */
106
+ function displayDimensionsValidation(externalFiles) {
107
+ if (!externalFiles || externalFiles.length === 0) {
108
+ return [];
109
+ }
110
+
111
+ const datasourceFiles = externalFiles.filter(f =>
112
+ f.type === 'datasource' || f.type === 'external-datasource'
113
+ );
114
+
115
+ if (datasourceFiles.length === 0) {
116
+ return [];
117
+ }
118
+
119
+ logger.log(chalk.blue('\nDimensions (ABAC):'));
120
+
121
+ const warnings = [];
122
+ let anyDatasourceHasDimensions = false;
123
+
124
+ datasourceFiles.forEach(file => {
125
+ const dimensionsInfo = extractDimensionsFromDatasource(file.path);
126
+
127
+ if (dimensionsInfo.hasDimensions) {
128
+ anyDatasourceHasDimensions = true;
129
+ logger.log(chalk.green(` ✓ ${file.file}`));
130
+ dimensionsInfo.dimensionKeys.forEach(key => {
131
+ const mapping = dimensionsInfo.dimensions[key];
132
+ logger.log(chalk.gray(` ${key} → ${mapping}`));
133
+ });
134
+ } else {
135
+ logger.log(chalk.yellow(` ⚠ ${file.file} - no dimensions configured`));
136
+ warnings.push(`${file.file} - no dimensions configured, ABAC filtering disabled`);
137
+ }
138
+ });
139
+
140
+ if (!anyDatasourceHasDimensions) {
141
+ logger.log(chalk.yellow(' ⚠ No dimensions configured - ABAC filtering disabled for all datasources'));
142
+ }
143
+
144
+ return warnings;
145
+ }
146
+
147
+ /**
148
+ * Displays RBAC validation results
149
+ * @function displayRbacValidation
150
+ * @param {Object} rbac - RBAC validation result
151
+ */
152
+ function displayRbacValidation(rbac) {
153
+ if (!rbac) {
154
+ return;
155
+ }
156
+
157
+ logger.log(chalk.blue('\nRBAC Configuration:'));
158
+ if (rbac.valid) {
159
+ logger.log(chalk.green(' ✓ RBAC configuration is valid'));
160
+ } else {
161
+ logger.log(chalk.red(' ✗ RBAC configuration has errors:'));
162
+ rbac.errors.forEach(error => {
163
+ logger.log(chalk.red(` • ${error}`));
164
+ });
165
+ }
166
+ if (rbac.warnings && rbac.warnings.length > 0) {
167
+ rbac.warnings.forEach(warning => {
168
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
169
+ });
170
+ }
171
+ }
172
+
173
+ /**
174
+ * Displays file validation results (for direct file validation)
175
+ * @function displayFileValidation
176
+ * @param {Object} result - Validation result
177
+ */
178
+ function displayFileValidation(result) {
179
+ if (!result.file) {
180
+ return;
181
+ }
182
+
183
+ logger.log(chalk.blue(`\nFile: ${result.file}`));
184
+ logger.log(chalk.blue(`Type: ${result.type}`));
185
+ if (result.valid) {
186
+ logger.log(chalk.green(' ✓ File is valid'));
187
+ } else {
188
+ logger.log(chalk.red(' ✗ File has errors:'));
189
+ result.errors.forEach(error => {
190
+ logger.log(chalk.red(` • ${error}`));
191
+ });
192
+ }
193
+ if (result.warnings && result.warnings.length > 0) {
194
+ result.warnings.forEach(warning => {
195
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
196
+ });
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Displays aggregated warnings
202
+ * @function displayAggregatedWarnings
203
+ * @param {Array} warnings - Array of warning messages
204
+ */
205
+ function displayAggregatedWarnings(warnings) {
206
+ if (!warnings || warnings.length === 0) {
207
+ return;
208
+ }
209
+
210
+ logger.log(chalk.yellow('\nWarnings:'));
211
+ warnings.forEach(warning => {
212
+ logger.log(chalk.yellow(` • ${warning}`));
213
+ });
214
+ }
215
+
216
+ /**
217
+ * Displays application validation step
218
+ * @function displayApplicationStep
219
+ * @param {Object} application - Application validation result
220
+ * @returns {void}
221
+ */
222
+ function displayApplicationStep(application) {
223
+ logger.log(chalk.blue('\nApplication:'));
224
+ if (application.valid) {
225
+ logger.log(chalk.green(' ✓ Application configuration is valid'));
226
+ } else {
227
+ logger.log(chalk.red(' ✗ Application configuration has errors:'));
228
+ application.errors.forEach(error => {
229
+ logger.log(chalk.red(` • ${error}`));
230
+ });
231
+ }
232
+ if (application.warnings && application.warnings.length > 0) {
233
+ application.warnings.forEach(warning => {
234
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
235
+ });
236
+ }
237
+ }
238
+
239
+ /**
240
+ * Displays external integration files step
241
+ * @function displayComponentsStep
242
+ * @param {Object} components - Components validation result
243
+ * @returns {void}
244
+ */
245
+ function displayComponentsStep(components) {
246
+ if (!components.files || components.files.length === 0) {
247
+ return;
248
+ }
249
+
250
+ logger.log(chalk.blue('\nExternal Integration Files:'));
251
+ components.files.forEach(file => {
252
+ if (file.valid) {
253
+ logger.log(chalk.green(` ✓ ${file.file} (${file.type})`));
254
+ } else {
255
+ logger.log(chalk.red(` ✗ ${file.file} (${file.type})`));
256
+ }
257
+ });
258
+
259
+ if (components.errors && components.errors.length > 0) {
260
+ components.errors.forEach(error => {
261
+ logger.log(chalk.red(` • ${error}`));
262
+ });
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Displays dimensions validation for datasources
268
+ * @function displayDimensionsStep
269
+ * @param {Array} datasourceFiles - Datasource files
270
+ * @returns {void}
271
+ */
272
+ function displayDimensionsStep(datasourceFiles) {
273
+ if (datasourceFiles.length === 0) {
274
+ return;
275
+ }
276
+
277
+ logger.log(chalk.blue('\nDimensions (ABAC):'));
278
+ datasourceFiles.forEach(file => {
279
+ try {
280
+ const dimensionsInfo = extractDimensionsFromDatasource(file.path || file.file);
281
+ if (dimensionsInfo.hasDimensions) {
282
+ logger.log(chalk.green(` ✓ ${file.file}`));
283
+ dimensionsInfo.dimensionKeys.forEach(key => {
284
+ const mapping = dimensionsInfo.dimensions[key];
285
+ logger.log(chalk.gray(` ${key} → ${mapping}`));
286
+ });
287
+ } else {
288
+ logger.log(chalk.yellow(` ⚠ ${file.file} - no dimensions configured`));
289
+ }
290
+ } catch {
291
+ // Skip if file path not available
292
+ }
293
+ });
294
+ }
295
+
296
+ /**
297
+ * Displays deployment manifest step
298
+ * @function displayManifestStep
299
+ * @param {Object} manifest - Manifest validation result
300
+ * @param {Array} componentFiles - Component files
301
+ * @returns {void}
302
+ */
303
+ function displayManifestStep(manifest, componentFiles) {
304
+ logger.log(chalk.blue('\nDeployment Manifest:'));
305
+ if (manifest.valid) {
306
+ logger.log(chalk.green(' ✓ Full deployment manifest is valid'));
307
+ if (componentFiles) {
308
+ const datasourceFiles = componentFiles.filter(f => f.type === 'datasource' || f.type === 'external-datasource');
309
+ logger.log(chalk.green(' ✓ System configuration valid'));
310
+ logger.log(chalk.green(` ✓ ${datasourceFiles.length} datasource(s) valid`));
311
+ logger.log(chalk.green(' ✓ Schema validation passed'));
312
+ }
313
+ } else {
314
+ logger.log(chalk.red(' ✗ Full deployment manifest validation failed:'));
315
+ if (manifest.errors && manifest.errors.length > 0) {
316
+ manifest.errors.forEach(error => {
317
+ logger.log(chalk.red(` • ${error}`));
318
+ });
319
+ }
320
+ }
321
+ if (manifest.warnings && manifest.warnings.length > 0) {
322
+ manifest.warnings.forEach(warning => {
323
+ logger.log(chalk.yellow(` ⚠ ${warning}`));
324
+ });
325
+ }
326
+ }
327
+
328
+ /**
329
+ * Displays step-by-step validation results for external systems
330
+ * @function displayStepByStepValidation
331
+ * @param {Object} result - Validation result with steps
332
+ */
333
+ function displayStepByStepValidation(result) {
334
+ if (result.valid) {
335
+ logger.log(chalk.green('\n✓ Validation passed!'));
336
+ } else {
337
+ logger.log(chalk.red('\n✗ Validation failed!'));
338
+ }
339
+
340
+ displayApplicationStep(result.steps.application);
341
+
342
+ if (result.steps.components.files && result.steps.components.files.length > 0) {
343
+ displayComponentsStep(result.steps.components);
344
+ }
345
+
346
+ const datasourceFiles = result.steps.components.files?.filter(f =>
347
+ f.type === 'datasource' || f.type === 'external-datasource'
348
+ ) || [];
349
+ displayDimensionsStep(datasourceFiles);
350
+
351
+ if (result.rbac) {
352
+ displayRbacValidation(result.rbac);
353
+ }
354
+
355
+ displayManifestStep(result.steps.manifest, result.steps.components.files);
356
+
357
+ if (result.warnings && result.warnings.length > 0) {
358
+ displayAggregatedWarnings(result.warnings);
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Displays validation results in a user-friendly format
364
+ *
365
+ * @function displayValidationResults
366
+ * @param {Object} result - Validation result from validateAppOrFile
367
+ */
368
+ function displayValidationResults(result) {
369
+ // Check if this is a step-by-step result (from validateExternalSystemComplete)
370
+ if (result.steps) {
371
+ displayStepByStepValidation(result);
372
+ return;
373
+ }
374
+
375
+ // Legacy format (for regular apps)
376
+ if (result.valid) {
377
+ logger.log(chalk.green('\n✓ Validation passed!'));
378
+ } else {
379
+ logger.log(chalk.red('\n✗ Validation failed!'));
380
+ }
381
+
382
+ displayApplicationValidation(result.application);
383
+ displayExternalFilesValidation(result.externalFiles);
384
+
385
+ // Display Dimensions (ABAC) for datasources and collect warnings
386
+ const dimensionWarnings = displayDimensionsValidation(result.externalFiles);
387
+
388
+ displayRbacValidation(result.rbac);
389
+ displayFileValidation(result);
390
+
391
+ // Combine all warnings
392
+ const allWarnings = [...(result.warnings || []), ...dimensionWarnings];
393
+ displayAggregatedWarnings(allWarnings);
394
+ }
395
+
396
+ module.exports = {
397
+ displayValidationResults,
398
+ displayStepByStepValidation,
399
+ displayApplicationValidation,
400
+ displayExternalFilesValidation,
401
+ displayDimensionsValidation,
402
+ displayRbacValidation,
403
+ displayFileValidation,
404
+ displayAggregatedWarnings,
405
+ extractDimensionsFromDatasource
406
+ };