@aifabrix/builder 2.7.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.
- package/.cursor/rules/project-rules.mdc +680 -0
- package/integration/hubspot/README.md +136 -0
- package/integration/hubspot/env.template +9 -0
- package/integration/hubspot/hubspot-deploy-company.json +200 -0
- package/integration/hubspot/hubspot-deploy-contact.json +228 -0
- package/integration/hubspot/hubspot-deploy-deal.json +248 -0
- package/integration/hubspot/hubspot-deploy.json +91 -0
- package/integration/hubspot/variables.yaml +17 -0
- package/lib/app-config.js +13 -2
- package/lib/app-deploy.js +9 -3
- package/lib/app-dockerfile.js +14 -1
- package/lib/app-prompts.js +177 -13
- package/lib/app-push.js +16 -1
- package/lib/app-register.js +37 -5
- package/lib/app-rotate-secret.js +10 -0
- package/lib/app-run.js +19 -0
- package/lib/app.js +70 -25
- package/lib/audit-logger.js +9 -4
- package/lib/build.js +25 -13
- package/lib/cli.js +109 -2
- package/lib/commands/login.js +40 -3
- package/lib/config.js +121 -114
- package/lib/datasource-deploy.js +14 -20
- package/lib/environment-deploy.js +305 -0
- package/lib/external-system-deploy.js +345 -0
- package/lib/external-system-download.js +431 -0
- package/lib/external-system-generator.js +190 -0
- package/lib/external-system-test.js +446 -0
- package/lib/generator-builders.js +323 -0
- package/lib/generator.js +200 -292
- package/lib/schema/application-schema.json +830 -800
- package/lib/schema/external-datasource.schema.json +868 -46
- package/lib/schema/external-system.schema.json +98 -80
- package/lib/schema/infrastructure-schema.json +1 -1
- package/lib/templates.js +32 -1
- package/lib/utils/cli-utils.js +4 -4
- package/lib/utils/device-code.js +10 -2
- package/lib/utils/external-system-display.js +159 -0
- package/lib/utils/external-system-validators.js +245 -0
- package/lib/utils/paths.js +151 -1
- package/lib/utils/schema-resolver.js +7 -2
- package/lib/utils/token-encryption.js +68 -0
- package/lib/validator.js +52 -5
- package/package.json +1 -1
- package/tatus +181 -0
- package/templates/external-system/external-datasource.json.hbs +55 -0
- package/templates/external-system/external-system.json.hbs +37 -0
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://
|
|
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": [
|
|
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": [
|
|
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
|
|
104
|
-
"required": [
|
|
103
|
+
"description": "Authentication configuration for external system",
|
|
104
|
+
"required": [
|
|
105
|
+
"type"
|
|
106
|
+
],
|
|
105
107
|
"properties": {
|
|
106
|
-
"
|
|
108
|
+
"type": {
|
|
107
109
|
"type": "string",
|
|
108
|
-
"enum": [
|
|
109
|
-
|
|
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
|
|
115
|
-
"
|
|
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
|
-
"
|
|
137
|
-
|
|
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
|
-
"
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"
|
|
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":
|
|
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": "
|
|
148
|
+
"description": "Key of the OpenAPI document in the registry"
|
|
162
149
|
},
|
|
163
|
-
"
|
|
164
|
-
"type": "
|
|
165
|
-
"
|
|
166
|
-
"
|
|
150
|
+
"specUrl": {
|
|
151
|
+
"type": "string",
|
|
152
|
+
"description": "URL to the OpenAPI specification",
|
|
153
|
+
"pattern": "^(http|https)://.*$"
|
|
167
154
|
}
|
|
168
155
|
},
|
|
169
|
-
"additionalProperties":
|
|
156
|
+
"additionalProperties": true
|
|
170
157
|
},
|
|
171
|
-
|
|
172
158
|
"mcp": {
|
|
173
159
|
"type": "object",
|
|
174
|
-
"description": "Model Context Protocol integration
|
|
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
|
-
"
|
|
183
|
-
"
|
|
169
|
+
"description": "Prefix for MCP tool names",
|
|
170
|
+
"pattern": "^[a-zA-Z0-9_-]+$"
|
|
184
171
|
}
|
|
185
172
|
},
|
|
186
|
-
"additionalProperties":
|
|
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": [
|
|
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": [
|
|
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": [
|
|
216
|
+
"required": [
|
|
217
|
+
"field",
|
|
218
|
+
"label"
|
|
219
|
+
],
|
|
224
220
|
"properties": {
|
|
225
221
|
"field": {
|
|
226
222
|
"type": "string",
|
|
227
|
-
"enum": [
|
|
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": {
|
|
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": {
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
"
|
|
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": {
|
|
274
|
+
"items": {
|
|
275
|
+
"type": "string"
|
|
276
|
+
}
|
|
258
277
|
}
|
|
259
278
|
},
|
|
260
|
-
|
|
261
279
|
"additionalProperties": false
|
|
262
280
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
-
"$id": "https://
|
|
3
|
+
"$id": "https://raw.githubusercontent.com/esystemsdev/aifabrix-builder/refs/heads/main/lib/schema/infrastructure-schema.json",
|
|
4
4
|
"title": "AI Fabrix Infrastructure Configuration Schema",
|
|
5
5
|
"description": "Infrastructure-level configuration schema for AI Fabrix deployment",
|
|
6
6
|
"metadata": {
|
package/lib/templates.js
CHANGED
|
@@ -16,6 +16,37 @@ const yaml = require('js-yaml');
|
|
|
16
16
|
*/
|
|
17
17
|
function generateVariablesYaml(appName, config) {
|
|
18
18
|
const displayName = appName.replace(/-/g, ' ').replace(/\b\w/g, l => l.toUpperCase());
|
|
19
|
+
const appType = config.type || 'webapp';
|
|
20
|
+
|
|
21
|
+
// For external type, create minimal variables.yaml
|
|
22
|
+
if (appType === 'external') {
|
|
23
|
+
const variables = {
|
|
24
|
+
app: {
|
|
25
|
+
key: appName,
|
|
26
|
+
displayName: displayName,
|
|
27
|
+
description: `${appName.replace(/-/g, ' ')} external system`,
|
|
28
|
+
type: 'external'
|
|
29
|
+
},
|
|
30
|
+
deployment: {
|
|
31
|
+
controllerUrl: '',
|
|
32
|
+
environment: 'dev'
|
|
33
|
+
},
|
|
34
|
+
externalIntegration: {
|
|
35
|
+
schemaBasePath: './',
|
|
36
|
+
systems: [],
|
|
37
|
+
dataSources: [],
|
|
38
|
+
autopublish: true,
|
|
39
|
+
version: '1.0.0'
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
return yaml.dump(variables, {
|
|
44
|
+
indent: 2,
|
|
45
|
+
lineWidth: 120,
|
|
46
|
+
noRefs: true,
|
|
47
|
+
sortKeys: false
|
|
48
|
+
});
|
|
49
|
+
}
|
|
19
50
|
|
|
20
51
|
// Parse image name to separate name and tag
|
|
21
52
|
let imageName = appName;
|
|
@@ -32,7 +63,7 @@ function generateVariablesYaml(appName, config) {
|
|
|
32
63
|
key: appName,
|
|
33
64
|
displayName: displayName,
|
|
34
65
|
description: `${appName.replace(/-/g, ' ')} application`,
|
|
35
|
-
type:
|
|
66
|
+
type: appType
|
|
36
67
|
},
|
|
37
68
|
image: {
|
|
38
69
|
name: imageName,
|
package/lib/utils/cli-utils.js
CHANGED
|
@@ -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');
|
package/lib/utils/device-code.js
CHANGED
|
@@ -61,20 +61,28 @@ function parseDeviceCodeResponse(response) {
|
|
|
61
61
|
* @function initiateDeviceCodeFlow
|
|
62
62
|
* @param {string} controllerUrl - Base URL of the controller
|
|
63
63
|
* @param {string} environment - Environment key (e.g., 'miso', 'dev', 'tst', 'pro')
|
|
64
|
+
* @param {string} [scope] - OAuth2 scope string (default: 'openid profile email')
|
|
64
65
|
* @returns {Promise<Object>} Device code response with device_code, user_code, verification_uri, expires_in, interval
|
|
65
66
|
* @throws {Error} If initiation fails
|
|
66
67
|
*/
|
|
67
|
-
async function initiateDeviceCodeFlow(controllerUrl, environment) {
|
|
68
|
+
async function initiateDeviceCodeFlow(controllerUrl, environment, scope) {
|
|
68
69
|
if (!environment || typeof environment !== 'string') {
|
|
69
70
|
throw new Error('Environment key is required');
|
|
70
71
|
}
|
|
71
72
|
|
|
73
|
+
// Default scope for backward compatibility
|
|
74
|
+
const defaultScope = 'openid profile email';
|
|
75
|
+
const requestScope = scope || defaultScope;
|
|
76
|
+
|
|
72
77
|
const url = `${controllerUrl}/api/v1/auth/login?environment=${encodeURIComponent(environment)}`;
|
|
73
78
|
const response = await getMakeApiCall()(url, {
|
|
74
79
|
method: 'POST',
|
|
75
80
|
headers: {
|
|
76
81
|
'Content-Type': 'application/json'
|
|
77
|
-
}
|
|
82
|
+
},
|
|
83
|
+
body: JSON.stringify({
|
|
84
|
+
scope: requestScope
|
|
85
|
+
})
|
|
78
86
|
});
|
|
79
87
|
|
|
80
88
|
if (!response.success) {
|
|
@@ -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
|
+
|