@aifabrix/builder 2.8.0 → 2.9.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 (36) hide show
  1. package/integration/hubspot/README.md +136 -0
  2. package/integration/hubspot/env.template +9 -0
  3. package/integration/hubspot/hubspot-deploy-company.json +200 -0
  4. package/integration/hubspot/hubspot-deploy-contact.json +228 -0
  5. package/integration/hubspot/hubspot-deploy-deal.json +248 -0
  6. package/integration/hubspot/hubspot-deploy.json +91 -0
  7. package/integration/hubspot/variables.yaml +17 -0
  8. package/lib/app-config.js +4 -3
  9. package/lib/app-deploy.js +8 -20
  10. package/lib/app-dockerfile.js +7 -9
  11. package/lib/app-prompts.js +6 -5
  12. package/lib/app-push.js +9 -9
  13. package/lib/app-register.js +23 -5
  14. package/lib/app-rotate-secret.js +10 -0
  15. package/lib/app-run.js +5 -11
  16. package/lib/app.js +42 -14
  17. package/lib/build.js +20 -16
  18. package/lib/cli.js +61 -2
  19. package/lib/datasource-deploy.js +14 -20
  20. package/lib/external-system-deploy.js +123 -40
  21. package/lib/external-system-download.js +431 -0
  22. package/lib/external-system-generator.js +13 -10
  23. package/lib/external-system-test.js +446 -0
  24. package/lib/generator-builders.js +323 -0
  25. package/lib/generator.js +200 -292
  26. package/lib/schema/application-schema.json +853 -852
  27. package/lib/schema/external-datasource.schema.json +823 -49
  28. package/lib/schema/external-system.schema.json +96 -78
  29. package/lib/templates.js +1 -1
  30. package/lib/utils/cli-utils.js +4 -4
  31. package/lib/utils/external-system-display.js +159 -0
  32. package/lib/utils/external-system-validators.js +245 -0
  33. package/lib/utils/paths.js +151 -1
  34. package/lib/utils/schema-resolver.js +7 -2
  35. package/lib/validator.js +5 -2
  36. package/package.json +1 -1
@@ -3,7 +3,6 @@
3
3
  "$id": "https://raw.githubusercontent.com/esystemsdev/aifabrix-builder/refs/heads/main/lib/schema/external-system.schema.json",
4
4
  "title": "AI Fabrix External System Configuration Schema",
5
5
  "description": "Schema for configuring an external system connected to the AI Fabrix Dataplane. This defines authentication, OpenAPI/MCP bindings, field mappings defaults, metadata handling and portal inputs.",
6
-
7
6
  "metadata": {
8
7
  "key": "external-system-schema",
9
8
  "name": "External System Configuration Schema",
@@ -19,7 +18,13 @@
19
18
  "maxVersion": "2.0.0",
20
19
  "deprecated": false
21
20
  },
22
- "tags": ["schema", "external-system", "dataplane", "integration", "validation"],
21
+ "tags": [
22
+ "schema",
23
+ "external-system",
24
+ "dataplane",
25
+ "integration",
26
+ "validation"
27
+ ],
23
28
  "dependencies": [],
24
29
  "changelog": [
25
30
  {
@@ -36,9 +41,7 @@
36
41
  }
37
42
  ]
38
43
  },
39
-
40
44
  "type": "object",
41
-
42
45
  "required": [
43
46
  "key",
44
47
  "displayName",
@@ -46,7 +49,6 @@
46
49
  "type",
47
50
  "authentication"
48
51
  ],
49
-
50
52
  "properties": {
51
53
  "key": {
52
54
  "type": "string",
@@ -55,32 +57,31 @@
55
57
  "minLength": 3,
56
58
  "maxLength": 40
57
59
  },
58
-
59
60
  "displayName": {
60
61
  "type": "string",
61
62
  "description": "Human-readable name for the external system",
62
63
  "minLength": 1,
63
64
  "maxLength": 100
64
65
  },
65
-
66
66
  "description": {
67
67
  "type": "string",
68
68
  "description": "Description of this external system integration",
69
69
  "minLength": 1,
70
70
  "maxLength": 500
71
71
  },
72
-
73
72
  "type": {
74
73
  "type": "string",
75
- "enum": ["openapi", "mcp", "custom"],
74
+ "enum": [
75
+ "openapi",
76
+ "mcp",
77
+ "custom"
78
+ ],
76
79
  "description": "Integration type: OpenAPI-driven, MCP-driven, or custom Python connector."
77
80
  },
78
-
79
81
  "enabled": {
80
82
  "type": "boolean",
81
83
  "default": true
82
84
  },
83
-
84
85
  "environment": {
85
86
  "type": "object",
86
87
  "description": "Environment-level configuration values used by dataplane and external data sources.",
@@ -97,95 +98,80 @@
97
98
  },
98
99
  "additionalProperties": true
99
100
  },
100
-
101
101
  "authentication": {
102
102
  "type": "object",
103
- "description": "Authentication configuration for the external system.",
104
- "required": ["type"],
103
+ "description": "Authentication configuration for external system",
104
+ "required": [
105
+ "type"
106
+ ],
105
107
  "properties": {
106
108
  "type": {
107
109
  "type": "string",
108
- "enum": ["oauth2", "apikey", "basic", "aad", "none"],
109
- "description": "Authentication method used for API access."
110
+ "enum": [
111
+ "oauth2",
112
+ "apikey",
113
+ "basic",
114
+ "aad",
115
+ "none"
116
+ ],
117
+ "description": "Authentication type"
110
118
  },
111
-
112
119
  "oauth2": {
113
120
  "type": "object",
114
- "description": "OAuth2 configuration used when type == 'oauth2'. All secrets are stored in Key Vault.",
115
- "properties": {
116
- "tokenUrl": {
117
- "type": "string",
118
- "pattern": "^(http|https)://.*$"
119
- },
120
- "clientId": {
121
- "type": "string"
122
- },
123
- "clientSecret": {
124
- "type": "string"
125
- },
126
- "scopes": {
127
- "type": "array",
128
- "items": { "type": "string" }
129
- }
130
- },
131
- "additionalProperties": false
121
+ "description": "OAuth2 authentication configuration",
122
+ "additionalProperties": true
132
123
  },
133
-
134
124
  "apikey": {
135
125
  "type": "object",
136
- "properties": {
137
- "headerName": { "type": "string" },
138
- "key": { "type": "string" }
139
- },
140
- "additionalProperties": false
126
+ "description": "API key authentication configuration",
127
+ "additionalProperties": true
141
128
  },
142
-
143
129
  "basic": {
144
130
  "type": "object",
145
- "properties": {
146
- "username": { "type": "string" },
147
- "password": { "type": "string" }
148
- },
149
- "additionalProperties": false
131
+ "description": "Basic authentication configuration",
132
+ "additionalProperties": true
133
+ },
134
+ "aad": {
135
+ "type": "object",
136
+ "description": "Azure AD authentication configuration",
137
+ "additionalProperties": true
150
138
  }
151
139
  },
152
- "additionalProperties": false
140
+ "additionalProperties": true
153
141
  },
154
-
155
142
  "openapi": {
156
143
  "type": "object",
157
- "description": "OpenAPI integration configuration.",
144
+ "description": "OpenAPI integration configuration",
158
145
  "properties": {
159
146
  "documentKey": {
160
147
  "type": "string",
161
- "description": "Reference to OpenAPI spec registered via builder. Example: 'hubspot-v3'."
148
+ "description": "Key of the OpenAPI document in the registry"
162
149
  },
163
- "autoDiscoverEntities": {
164
- "type": "boolean",
165
- "default": false,
166
- "description": "Automatically discover resources/entities from OpenAPI schema."
150
+ "specUrl": {
151
+ "type": "string",
152
+ "description": "URL to the OpenAPI specification",
153
+ "pattern": "^(http|https)://.*$"
167
154
  }
168
155
  },
169
- "additionalProperties": false
156
+ "additionalProperties": true
170
157
  },
171
-
172
158
  "mcp": {
173
159
  "type": "object",
174
- "description": "Model Context Protocol integration config.",
160
+ "description": "Model Context Protocol integration configuration",
175
161
  "properties": {
176
162
  "serverUrl": {
177
163
  "type": "string",
164
+ "description": "MCP server URL",
178
165
  "pattern": "^(http|https)://.*$"
179
166
  },
180
167
  "toolPrefix": {
181
168
  "type": "string",
182
- "pattern": "^[a-z0-9-]+$",
183
- "description": "Prefix for MCP tool names generated from this system."
169
+ "description": "Prefix for MCP tool names",
170
+ "pattern": "^[a-zA-Z0-9_-]+$"
184
171
  }
185
172
  },
186
- "additionalProperties": false
173
+ "additionalProperties": true
187
174
  },
188
-
189
175
  "dataSources": {
190
176
  "type": "array",
191
177
  "description": "List of data source keys belonging to this external system. Each must match external-datasource.schema.json.",
@@ -195,13 +181,17 @@
195
181
  },
196
182
  "uniqueItems": true
197
183
  },
198
-
199
184
  "configuration": {
200
185
  "type": "array",
201
186
  "description": "External system configuration variables (same pattern as application-schema).",
202
187
  "items": {
203
188
  "type": "object",
204
- "required": ["name", "value", "location", "required"],
189
+ "required": [
190
+ "name",
191
+ "value",
192
+ "location",
193
+ "required"
194
+ ],
205
195
  "properties": {
206
196
  "name": {
207
197
  "type": "string",
@@ -213,33 +203,61 @@
213
203
  },
214
204
  "location": {
215
205
  "type": "string",
216
- "enum": ["variable", "keyvault"]
206
+ "enum": [
207
+ "variable",
208
+ "keyvault"
209
+ ]
217
210
  },
218
211
  "required": {
219
212
  "type": "boolean"
220
213
  },
221
214
  "portalInput": {
222
215
  "type": "object",
223
- "required": ["field", "label"],
216
+ "required": [
217
+ "field",
218
+ "label"
219
+ ],
224
220
  "properties": {
225
221
  "field": {
226
222
  "type": "string",
227
- "enum": ["password", "text", "textarea", "select", "json"]
223
+ "enum": [
224
+ "password",
225
+ "text",
226
+ "textarea",
227
+ "select",
228
+ "json"
229
+ ]
230
+ },
231
+ "label": {
232
+ "type": "string"
233
+ },
234
+ "placeholder": {
235
+ "type": "string"
228
236
  },
229
- "label": { "type": "string" },
230
- "placeholder": { "type": "string" },
231
237
  "options": {
232
238
  "type": "array",
233
- "items": { "type": "string" }
239
+ "items": {
240
+ "type": "string"
241
+ }
242
+ },
243
+ "masked": {
244
+ "type": "boolean"
234
245
  },
235
- "masked": { "type": "boolean" },
236
246
  "validation": {
237
247
  "type": "object",
238
248
  "properties": {
239
- "minLength": { "type": "integer" },
240
- "maxLength": { "type": "integer" },
241
- "pattern": { "type": "string" },
242
- "required": { "type": "boolean" }
249
+ "minLength": {
250
+ "type": "integer"
251
+ },
252
+ "maxLength": {
253
+ "type": "integer"
254
+ },
255
+ "pattern": {
256
+ "type": "string"
257
+ },
258
+ "required": {
259
+ "type": "boolean"
260
+ }
243
261
  },
244
262
  "additionalProperties": false
245
263
  }
@@ -250,13 +268,13 @@
250
268
  "additionalProperties": false
251
269
  }
252
270
  },
253
-
254
271
  "tags": {
255
272
  "type": "array",
256
273
  "description": "Optional tags for search / filtering",
257
- "items": { "type": "string" }
274
+ "items": {
275
+ "type": "string"
276
+ }
258
277
  }
259
278
  },
260
-
261
279
  "additionalProperties": false
262
280
  }
package/lib/templates.js CHANGED
@@ -32,7 +32,7 @@ function generateVariablesYaml(appName, config) {
32
32
  environment: 'dev'
33
33
  },
34
34
  externalIntegration: {
35
- schemaBasePath: './schemas',
35
+ schemaBasePath: './',
36
36
  systems: [],
37
37
  dataSources: [],
38
38
  autopublish: true,
@@ -57,7 +57,7 @@ function formatError(error) {
57
57
  } else if (errorMsg.includes('Docker') && (errorMsg.includes('not running') || errorMsg.includes('not installed') || errorMsg.includes('Cannot connect'))) {
58
58
  messages.push(' Docker is not running or not installed.');
59
59
  messages.push(' Please start Docker Desktop and try again.');
60
- } else if (errorMsg.includes('port')) {
60
+ } else if (errorMsg.toLowerCase().includes('port') && (errorMsg.includes('already in use') || errorMsg.includes('in use') || errorMsg.includes('conflict'))) {
61
61
  messages.push(' Port conflict detected.');
62
62
  messages.push(' Run "aifabrix doctor" to check which ports are in use.');
63
63
  } else if ((errorMsg.includes('permission denied') || errorMsg.includes('EACCES') || errorMsg.includes('Permission denied')) && !errorMsg.includes('permissions/') && !errorMsg.includes('Field "permissions')) {
@@ -69,13 +69,13 @@ function formatError(error) {
69
69
  messages.push(' Azure CLI is not installed or not working properly.');
70
70
  messages.push(' Install from: https://docs.microsoft.com/cli/azure/install-azure-cli');
71
71
  messages.push(' Run: az login');
72
+ } else if (errorMsg.includes('Invalid ACR URL') || errorMsg.includes('Invalid registry URL') || errorMsg.includes('Expected format')) {
73
+ messages.push(' Invalid registry URL format.');
74
+ messages.push(' Use format: *.azurecr.io (e.g., myacr.azurecr.io)');
72
75
  } else if (errorMsg.includes('authenticate') || errorMsg.includes('ACR') || errorMsg.includes('Authentication required')) {
73
76
  messages.push(' Azure Container Registry authentication failed.');
74
77
  messages.push(' Run: az acr login --name <registry-name>');
75
78
  messages.push(' Or login to Azure: az login');
76
- } else if (errorMsg.includes('Invalid ACR URL') || errorMsg.includes('Expected format')) {
77
- messages.push(' Invalid registry URL format.');
78
- messages.push(' Use format: *.azurecr.io (e.g., myacr.azurecr.io)');
79
79
  } else if (errorMsg.includes('Registry URL is required')) {
80
80
  messages.push(' Registry URL is required.');
81
81
  messages.push(' Provide via --registry flag or configure in variables.yaml under image.registry');
@@ -0,0 +1,159 @@
1
+ /**
2
+ * External System Test Display Helpers
3
+ *
4
+ * Provides formatted output for test results.
5
+ *
6
+ * @fileoverview Display helpers for external system testing
7
+ * @author AI Fabrix Team
8
+ * @version 2.0.0
9
+ */
10
+
11
+ const chalk = require('chalk');
12
+ const logger = require('./logger');
13
+
14
+ /**
15
+ * Displays formatted test results
16
+ * @param {Object} results - Test results
17
+ * @param {boolean} verbose - Show detailed output
18
+ */
19
+ function displayTestResults(results, verbose = false) {
20
+ logger.log(chalk.blue('\nšŸ“Š Test Results\n'));
21
+
22
+ if (results.systemResults.length > 0) {
23
+ logger.log(chalk.blue('System Files:'));
24
+ for (const systemResult of results.systemResults) {
25
+ if (systemResult.valid) {
26
+ logger.log(chalk.green(` āœ“ ${systemResult.file}`));
27
+ } else {
28
+ logger.log(chalk.red(` āœ— ${systemResult.file}`));
29
+ }
30
+ }
31
+ }
32
+
33
+ if (results.datasourceResults.length > 0) {
34
+ logger.log(chalk.blue('\nDatasource Files:'));
35
+ for (const dsResult of results.datasourceResults) {
36
+ if (dsResult.valid) {
37
+ logger.log(chalk.green(` āœ“ ${dsResult.key} (${dsResult.file})`));
38
+ } else {
39
+ logger.log(chalk.red(` āœ— ${dsResult.key} (${dsResult.file})`));
40
+ if (verbose && dsResult.errors.length > 0) {
41
+ dsResult.errors.forEach(err => logger.log(chalk.red(` - ${err}`)));
42
+ }
43
+ }
44
+
45
+ if (verbose) {
46
+ if (dsResult.warnings.length > 0) {
47
+ dsResult.warnings.forEach(warn => logger.log(chalk.yellow(` ⚠ ${warn}`)));
48
+ }
49
+
50
+ if (dsResult.fieldMappingResults) {
51
+ const fm = dsResult.fieldMappingResults;
52
+ logger.log(chalk.gray(` Field mappings: ${Object.keys(fm.mappedFields || {}).length} fields`));
53
+ }
54
+
55
+ if (dsResult.metadataSchemaResults) {
56
+ const ms = dsResult.metadataSchemaResults;
57
+ if (ms.valid) {
58
+ logger.log(chalk.gray(' Metadata schema: āœ“ Valid'));
59
+ } else {
60
+ logger.log(chalk.red(' Metadata schema: āœ— Invalid'));
61
+ }
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ if (results.errors.length > 0) {
68
+ logger.log(chalk.red('\nāŒ Errors:'));
69
+ results.errors.forEach(err => logger.log(chalk.red(` - ${err}`)));
70
+ }
71
+
72
+ if (results.warnings.length > 0) {
73
+ logger.log(chalk.yellow('\n⚠ Warnings:'));
74
+ results.warnings.forEach(warn => logger.log(chalk.yellow(` - ${warn}`)));
75
+ }
76
+
77
+ if (results.valid) {
78
+ logger.log(chalk.green('\nāœ… All tests passed!'));
79
+ } else {
80
+ logger.log(chalk.red('\nāŒ Some tests failed'));
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Displays formatted integration test results
86
+ * @param {Object} results - Integration test results
87
+ * @param {boolean} verbose - Show detailed output
88
+ */
89
+ function displayIntegrationTestResults(results, verbose = false) {
90
+ logger.log(chalk.blue('\nšŸ“Š Integration Test Results\n'));
91
+ logger.log(chalk.blue(`System: ${results.systemKey}`));
92
+
93
+ if (results.datasourceResults.length === 0) {
94
+ logger.log(chalk.yellow('No datasources tested'));
95
+ return;
96
+ }
97
+
98
+ for (const dsResult of results.datasourceResults) {
99
+ if (dsResult.skipped) {
100
+ logger.log(chalk.yellow(` ⚠ ${dsResult.key}: ${dsResult.reason}`));
101
+ continue;
102
+ }
103
+
104
+ if (dsResult.success) {
105
+ logger.log(chalk.green(` āœ“ ${dsResult.key}`));
106
+ } else {
107
+ logger.log(chalk.red(` āœ— ${dsResult.key}`));
108
+ if (dsResult.error) {
109
+ logger.log(chalk.red(` Error: ${dsResult.error}`));
110
+ }
111
+ }
112
+
113
+ if (verbose) {
114
+ if (dsResult.validationResults) {
115
+ const vr = dsResult.validationResults;
116
+ if (vr.isValid) {
117
+ logger.log(chalk.gray(' Validation: āœ“ Valid'));
118
+ } else {
119
+ logger.log(chalk.red(' Validation: āœ— Invalid'));
120
+ if (vr.errors && vr.errors.length > 0) {
121
+ vr.errors.forEach(err => logger.log(chalk.red(` - ${err}`)));
122
+ }
123
+ }
124
+ if (vr.warnings && vr.warnings.length > 0) {
125
+ vr.warnings.forEach(warn => logger.log(chalk.yellow(` ⚠ ${warn}`)));
126
+ }
127
+ }
128
+
129
+ if (dsResult.fieldMappingResults) {
130
+ const fmr = dsResult.fieldMappingResults;
131
+ logger.log(chalk.gray(` Field mappings: ${fmr.mappingCount || 0} fields`));
132
+ if (fmr.accessFields && fmr.accessFields.length > 0) {
133
+ logger.log(chalk.gray(` Access fields: ${fmr.accessFields.join(', ')}`));
134
+ }
135
+ }
136
+
137
+ if (dsResult.endpointTestResults) {
138
+ const etr = dsResult.endpointTestResults;
139
+ if (etr.endpointConfigured) {
140
+ logger.log(chalk.gray(' Endpoint: āœ“ Configured'));
141
+ } else {
142
+ logger.log(chalk.gray(' Endpoint: Not configured'));
143
+ }
144
+ }
145
+ }
146
+ }
147
+
148
+ if (results.success) {
149
+ logger.log(chalk.green('\nāœ… All integration tests passed!'));
150
+ } else {
151
+ logger.log(chalk.red('\nāŒ Some integration tests failed'));
152
+ }
153
+ }
154
+
155
+ module.exports = {
156
+ displayTestResults,
157
+ displayIntegrationTestResults
158
+ };
159
+