@aifabrix/builder 2.10.1 → 2.20.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 (47) hide show
  1. package/.cursor/rules/project-rules.mdc +194 -0
  2. package/README.md +12 -0
  3. package/integration/hubspot/README.md +2 -2
  4. package/integration/hubspot/hubspot-deploy.json +12 -4
  5. package/lib/api/applications.api.js +164 -0
  6. package/lib/api/auth.api.js +304 -0
  7. package/lib/api/datasources-core.api.js +87 -0
  8. package/lib/api/datasources-extended.api.js +117 -0
  9. package/lib/api/datasources.api.js +13 -0
  10. package/lib/api/deployments.api.js +126 -0
  11. package/lib/api/environments.api.js +245 -0
  12. package/lib/api/external-systems.api.js +251 -0
  13. package/lib/api/index.js +221 -0
  14. package/lib/api/pipeline.api.js +234 -0
  15. package/lib/api/types/applications.types.js +136 -0
  16. package/lib/api/types/auth.types.js +218 -0
  17. package/lib/api/types/datasources.types.js +272 -0
  18. package/lib/api/types/deployments.types.js +184 -0
  19. package/lib/api/types/environments.types.js +197 -0
  20. package/lib/api/types/external-systems.types.js +244 -0
  21. package/lib/api/types/pipeline.types.js +125 -0
  22. package/lib/app-list.js +5 -7
  23. package/lib/app-register.js +70 -403
  24. package/lib/app-rotate-secret.js +4 -10
  25. package/lib/commands/login.js +19 -12
  26. package/lib/datasource-deploy.js +7 -30
  27. package/lib/datasource-list.js +9 -6
  28. package/lib/deployer.js +103 -135
  29. package/lib/environment-deploy.js +15 -26
  30. package/lib/external-system-deploy.js +12 -39
  31. package/lib/external-system-download.js +5 -13
  32. package/lib/external-system-test.js +9 -12
  33. package/lib/utils/api-error-handler.js +11 -453
  34. package/lib/utils/app-register-api.js +66 -0
  35. package/lib/utils/app-register-auth.js +72 -0
  36. package/lib/utils/app-register-config.js +205 -0
  37. package/lib/utils/app-register-display.js +69 -0
  38. package/lib/utils/app-register-validator.js +143 -0
  39. package/lib/utils/deployment-errors.js +88 -6
  40. package/lib/utils/device-code.js +1 -1
  41. package/lib/utils/error-formatters/error-parser.js +150 -0
  42. package/lib/utils/error-formatters/http-status-errors.js +189 -0
  43. package/lib/utils/error-formatters/network-errors.js +46 -0
  44. package/lib/utils/error-formatters/permission-errors.js +94 -0
  45. package/lib/utils/error-formatters/validation-errors.js +133 -0
  46. package/package.json +1 -1
  47. package/templates/applications/README.md.hbs +1 -1
@@ -47,6 +47,23 @@ lib/
47
47
  ├── templates.js # Template rendering
48
48
  ├── secrets.js # Secret resolution (kv://)
49
49
  ├── config.js # Configuration management
50
+ ├── api/ # Centralized API client structure
51
+ │ ├── index.js # Main API client class
52
+ │ ├── types/ # JSDoc type definitions
53
+ │ │ ├── auth.types.js
54
+ │ │ ├── applications.types.js
55
+ │ │ ├── deployments.types.js
56
+ │ │ ├── environments.types.js
57
+ │ │ ├── datasources.types.js
58
+ │ │ ├── external-systems.types.js
59
+ │ │ └── pipeline.types.js
60
+ │ ├── auth.api.js # Authentication API functions
61
+ │ ├── applications.api.js
62
+ │ ├── deployments.api.js
63
+ │ ├── environments.api.js
64
+ │ ├── datasources.api.js
65
+ │ ├── external-systems.api.js
66
+ │ └── pipeline.api.js
50
67
  ├── utils/ # Utility functions
51
68
  └── schema/ # JSON schemas
52
69
  ```
@@ -140,6 +157,92 @@ if (!valid) {
140
157
  }
141
158
  ```
142
159
 
160
+ ### API Client Structure Pattern
161
+ Use the centralized API client structure in `lib/api/` for all API calls. This provides typed interfaces, domain separation, and consistent error handling.
162
+
163
+ **Structure**:
164
+ - Base client (`lib/api/index.js`) - Main HTTP client with authentication and error handling
165
+ - Type definitions (`lib/api/types/`) - JSDoc type definitions for request/response types
166
+ - Domain modules (`lib/api/*.api.js`) - Domain-specific API functions
167
+
168
+ **Type Definitions Pattern**:
169
+ Use JSDoc `@typedef` for all request/response types in `lib/api/types/`:
170
+ ```javascript
171
+ /**
172
+ * @fileoverview Authentication API type definitions
173
+ * @author AI Fabrix Team
174
+ * @version 2.0.0
175
+ */
176
+
177
+ /**
178
+ * Token request payload
179
+ * @typedef {Object} TokenRequest
180
+ * @property {string} clientId - Client ID
181
+ * @property {string} clientSecret - Client secret
182
+ */
183
+
184
+ /**
185
+ * Token response payload
186
+ * @typedef {Object} TokenResponse
187
+ * @property {boolean} success - Request success flag
188
+ * @property {string} token - Authentication token
189
+ * @property {number} expiresIn - Token expiration time in seconds
190
+ * @property {string} expiresAt - Token expiration timestamp
191
+ */
192
+ ```
193
+
194
+ **API Module Pattern**:
195
+ Each domain module exports typed API functions:
196
+ ```javascript
197
+ /**
198
+ * @fileoverview Authentication API functions
199
+ * @author AI Fabrix Team
200
+ * @version 2.0.0
201
+ */
202
+
203
+ const { ApiClient } = require('./index');
204
+ const { TokenRequest, TokenResponse } = require('./types/auth.types');
205
+
206
+ /**
207
+ * Get authentication token using client credentials
208
+ * @async
209
+ * @function getToken
210
+ * @param {string} clientId - Client ID
211
+ * @param {string} clientSecret - Client secret
212
+ * @param {string} controllerUrl - Controller base URL
213
+ * @returns {Promise<TokenResponse>} Token response with access token
214
+ * @throws {Error} If authentication fails
215
+ */
216
+ async function getToken(clientId, clientSecret, controllerUrl) {
217
+ const client = new ApiClient(controllerUrl);
218
+ return await client.post('/api/v1/auth/token', {
219
+ headers: {
220
+ 'x-client-id': clientId,
221
+ 'x-client-secret': clientSecret
222
+ }
223
+ });
224
+ }
225
+
226
+ module.exports = { getToken };
227
+ ```
228
+
229
+ **Usage Pattern**:
230
+ Import and use domain-specific API modules:
231
+ ```javascript
232
+ const { getToken } = require('../api/auth.api');
233
+ const { registerApplication } = require('../api/applications.api');
234
+
235
+ // Use typed API functions
236
+ const tokenResponse = await getToken(clientId, clientSecret, controllerUrl);
237
+ const appResponse = await registerApplication(controllerUrl, environment, data, token);
238
+ ```
239
+
240
+ **Migration Strategy**:
241
+ - New code should use `lib/api/` modules
242
+ - Existing code can continue using `lib/utils/api.js` (backward compatible)
243
+ - Gradually migrate modules to use centralized API client
244
+ - Eventually deprecate direct usage of `lib/utils/api.js`
245
+
143
246
  ## Code Style
144
247
 
145
248
  ### JavaScript Conventions
@@ -255,15 +358,18 @@ tests/
255
358
  ### Mock Patterns
256
359
  - Mock fs operations: `jest.mock('fs')` or `jest.mock('fs').promises`
257
360
  - Mock axios: `jest.mock('axios')` or use `makeApiCall` mock
361
+ - Mock API client: `jest.mock('../lib/api')` or mock individual API modules
258
362
  - Mock child_process: `jest.mock('child_process')`
259
363
  - Mock templates: provide test templates in `tests/fixtures/`
260
364
  - Pattern:
261
365
  ```javascript
262
366
  jest.mock('fs');
263
367
  jest.mock('fs').promises;
368
+ jest.mock('../lib/api/auth.api');
264
369
 
265
370
  const fs = require('fs');
266
371
  const fsp = require('fs').promises;
372
+ const { getToken } = require('../lib/api/auth.api');
267
373
 
268
374
  describe('ModuleName', () => {
269
375
  beforeEach(() => {
@@ -272,16 +378,50 @@ describe('ModuleName', () => {
272
378
 
273
379
  it('should handle success case', async () => {
274
380
  fsp.readFile = jest.fn().resolves('content');
381
+ getToken = jest.fn().resolves({ success: true, token: 'test-token' });
275
382
  // Test implementation
276
383
  });
277
384
 
278
385
  it('should handle error case', async () => {
279
386
  fsp.readFile = jest.fn().rejects(new Error('File not found'));
387
+ getToken = jest.fn().resolves({ success: false, error: 'Auth failed' });
280
388
  // Test error handling
281
389
  });
282
390
  });
283
391
  ```
284
392
 
393
+ ### API Client Testing Pattern
394
+ Test API modules by mocking the base client:
395
+ ```javascript
396
+ jest.mock('../lib/api/index');
397
+
398
+ const { ApiClient } = require('../lib/api/index');
399
+ const { getToken } = require('../lib/api/auth.api');
400
+
401
+ describe('auth.api', () => {
402
+ beforeEach(() => {
403
+ jest.clearAllMocks();
404
+ });
405
+
406
+ it('should get token successfully', async () => {
407
+ const mockResponse = { success: true, token: 'test-token' };
408
+ ApiClient.prototype.post = jest.fn().resolves(mockResponse);
409
+
410
+ const result = await getToken('client-id', 'secret', 'https://controller');
411
+ expect(result).toEqual(mockResponse);
412
+ expect(ApiClient.prototype.post).toHaveBeenCalledWith(
413
+ '/api/v1/auth/token',
414
+ expect.objectContaining({
415
+ headers: expect.objectContaining({
416
+ 'x-client-id': 'client-id',
417
+ 'x-client-secret': 'secret'
418
+ })
419
+ })
420
+ );
421
+ });
422
+ });
423
+ ```
424
+
285
425
  ### Test Coverage
286
426
  - Aim for 80%+ branch coverage
287
427
  - Test edge cases (null tokens, empty arrays, file errors)
@@ -585,6 +725,54 @@ const rendered = template(context);
585
725
  await fs.writeFile(outputPath, rendered, 'utf8');
586
726
  ```
587
727
 
728
+ ### API Client Usage
729
+ Use the centralized API client for all API calls:
730
+ ```javascript
731
+ const { getToken } = require('../api/auth.api');
732
+ const { registerApplication } = require('../api/applications.api');
733
+
734
+ // Typed API calls with automatic error handling
735
+ try {
736
+ const tokenResponse = await getToken(clientId, clientSecret, controllerUrl);
737
+ if (!tokenResponse.success) {
738
+ throw new Error(tokenResponse.formattedError || 'Authentication failed');
739
+ }
740
+
741
+ const appResponse = await registerApplication(
742
+ controllerUrl,
743
+ environment,
744
+ registrationData,
745
+ tokenResponse.token
746
+ );
747
+ } catch (error) {
748
+ console.error(chalk.red(`Error: ${error.message}`));
749
+ throw error;
750
+ }
751
+ ```
752
+
753
+ ### Type Definition Pattern
754
+ Define request/response types using JSDoc `@typedef`:
755
+ ```javascript
756
+ /**
757
+ * Application registration request
758
+ * @typedef {Object} RegisterApplicationRequest
759
+ * @property {string} appKey - Application key
760
+ * @property {string} name - Application name
761
+ * @property {string} [description] - Application description (optional)
762
+ * @property {string[]} [tags] - Application tags (optional)
763
+ */
764
+
765
+ /**
766
+ * Application registration response
767
+ * @typedef {Object} RegisterApplicationResponse
768
+ * @property {boolean} success - Request success flag
769
+ * @property {Object} data - Response data
770
+ * @property {string} data.appKey - Registered application key
771
+ * @property {string} data.clientId - Generated client ID
772
+ * @property {string} data.clientSecret - Generated client secret
773
+ */
774
+ ```
775
+
588
776
  ## Quality Gates
589
777
 
590
778
  ### Mandatory Checks Before Commit
@@ -663,6 +851,9 @@ await fs.writeFile(outputPath, rendered, 'utf8');
663
851
  - ✅ Use path.join() for cross-platform paths
664
852
  - ✅ Validate YAML syntax before parsing
665
853
  - ✅ Never log secrets or sensitive data
854
+ - ✅ Use centralized API client (`lib/api/`) for new API calls
855
+ - ✅ Define request/response types using JSDoc `@typedef` in `lib/api/types/`
856
+ - ✅ Use domain-specific API modules (`lib/api/*.api.js`) instead of direct `makeApiCall`
666
857
 
667
858
  ### Must Not Do (❌)
668
859
  - ❌ Never hardcode secrets, passwords, or tokens
@@ -674,6 +865,9 @@ await fs.writeFile(outputPath, rendered, 'utf8');
674
865
  - ❌ Never skip tests for new functionality
675
866
  - ❌ Never use `eval()` or `Function()` constructor
676
867
  - ❌ Never use raw paths (always use path.join)
868
+ - ❌ Never make direct API calls using `makeApiCall` in new code (use `lib/api/` modules)
869
+ - ❌ Never skip type definitions for API request/response types
870
+ - ❌ Never log authentication tokens or secrets in API calls
677
871
 
678
872
  ---
679
873
 
package/README.md CHANGED
@@ -68,6 +68,18 @@ aifabrix run miso-controller
68
68
  4. **Run** - Start locally, connected to infrastructure
69
69
  5. **Deploy** - Push to ACR and deploy via controller
70
70
 
71
+ ```mermaid
72
+ flowchart TD
73
+ Install[Install CLI] --> Up[Start Infrastructure]
74
+ Up --> Create[Create App]
75
+ Create --> Build[Build Image]
76
+ Build --> Run[Run Locally]
77
+ Run --> Deploy[Deploy to Azure]
78
+
79
+ style Install fill:#e1f5ff
80
+ style Deploy fill:#c8e6c9
81
+ ```
82
+
71
83
  ## Requirements
72
84
 
73
85
  - **Docker Desktop** - For running containers
@@ -48,13 +48,13 @@ aifabrix validate hubspot
48
48
 
49
49
  ```bash
50
50
  # Login to controller
51
- aifabrix login --controller https://controller.aifabrix.ai --method device --environment dev
51
+ aifabrix login --controller http://localhost:3100 --method device --environment dev
52
52
 
53
53
  # Register application
54
54
  aifabrix app register hubspot --environment dev
55
55
 
56
56
  # Deploy entire system
57
- aifabrix deploy hubspot --controller https://controller.aifabrix.ai --environment dev
57
+ aifabrix deploy hubspot --controller http://localhost:3100 --environment dev
58
58
 
59
59
  # Or deploy individual datasources for testing
60
60
  aifabrix datasource deploy hubspot-company --environment dev --file integration/hubspot/hubspot-deploy-company.json
@@ -58,7 +58,11 @@
58
58
  "field": "select",
59
59
  "label": "HubSpot API Version",
60
60
  "placeholder": "Select API version",
61
- "options": ["v1", "v2", "v3"],
61
+ "options": [
62
+ "v1",
63
+ "v2",
64
+ "v3"
65
+ ],
62
66
  "validation": {
63
67
  "required": false
64
68
  }
@@ -86,6 +90,10 @@
86
90
  "documentKey": "hubspot-v3",
87
91
  "autoDiscoverEntities": false
88
92
  },
89
- "tags": ["crm", "sales", "marketing", "hubspot"]
90
- }
91
-
93
+ "tags": [
94
+ "crm",
95
+ "sales",
96
+ "marketing",
97
+ "hubspot"
98
+ ]
99
+ }
@@ -0,0 +1,164 @@
1
+ /**
2
+ * @fileoverview Applications API functions
3
+ * @author AI Fabrix Team
4
+ * @version 2.0.0
5
+ */
6
+
7
+ const { ApiClient } = require('./index');
8
+
9
+ /**
10
+ * List all template applications
11
+ * GET /api/v1/applications
12
+ * @async
13
+ * @function listApplications
14
+ * @param {string} controllerUrl - Controller base URL
15
+ * @param {Object} authConfig - Authentication configuration
16
+ * @param {Object} [options] - List options
17
+ * @param {number} [options.page] - Page number
18
+ * @param {number} [options.pageSize] - Items per page
19
+ * @param {string} [options.sort] - Sort parameter
20
+ * @param {string} [options.filter] - Filter parameter
21
+ * @param {string} [options.search] - Search term
22
+ * @returns {Promise<Object>} Paginated list of template applications
23
+ * @throws {Error} If request fails
24
+ */
25
+ async function listApplications(controllerUrl, authConfig, options = {}) {
26
+ const client = new ApiClient(controllerUrl, authConfig);
27
+ return await client.get('/api/v1/applications', {
28
+ params: options
29
+ });
30
+ }
31
+
32
+ /**
33
+ * Create new template application
34
+ * POST /api/v1/applications
35
+ * @async
36
+ * @function createApplication
37
+ * @param {string} controllerUrl - Controller base URL
38
+ * @param {Object} authConfig - Authentication configuration
39
+ * @param {Object} applicationData - Application data
40
+ * @param {string} applicationData.key - Application key
41
+ * @param {string} applicationData.displayName - Display name
42
+ * @param {Object} applicationData.configuration - Application configuration
43
+ * @param {string} [applicationData.description] - Application description
44
+ * @param {string} [applicationData.url] - Application URL
45
+ * @returns {Promise<Object>} Created application response
46
+ * @throws {Error} If creation fails
47
+ */
48
+ async function createApplication(controllerUrl, authConfig, applicationData) {
49
+ const client = new ApiClient(controllerUrl, authConfig);
50
+ return await client.post('/api/v1/applications', {
51
+ body: applicationData
52
+ });
53
+ }
54
+
55
+ /**
56
+ * Get template application details
57
+ * GET /api/v1/applications/{appKey}
58
+ * @async
59
+ * @function getApplication
60
+ * @param {string} controllerUrl - Controller base URL
61
+ * @param {string} appKey - Application key
62
+ * @param {Object} authConfig - Authentication configuration
63
+ * @returns {Promise<Object>} Application details response
64
+ * @throws {Error} If request fails
65
+ */
66
+ async function getApplication(controllerUrl, appKey, authConfig) {
67
+ const client = new ApiClient(controllerUrl, authConfig);
68
+ return await client.get(`/api/v1/applications/${appKey}`);
69
+ }
70
+
71
+ /**
72
+ * Update template application
73
+ * PATCH /api/v1/applications/{appKey}
74
+ * @async
75
+ * @function updateApplication
76
+ * @param {string} controllerUrl - Controller base URL
77
+ * @param {string} appKey - Application key
78
+ * @param {Object} authConfig - Authentication configuration
79
+ * @param {Object} updateData - Update data
80
+ * @param {string} [updateData.displayName] - Display name
81
+ * @param {string} [updateData.description] - Description
82
+ * @param {string} [updateData.url] - URL
83
+ * @param {Object} [updateData.configuration] - Configuration
84
+ * @param {string} [updateData.status] - Status
85
+ * @returns {Promise<Object>} Updated application response
86
+ * @throws {Error} If update fails
87
+ */
88
+ async function updateApplication(controllerUrl, appKey, authConfig, updateData) {
89
+ const client = new ApiClient(controllerUrl, authConfig);
90
+ return await client.patch(`/api/v1/applications/${appKey}`, {
91
+ body: updateData
92
+ });
93
+ }
94
+
95
+ /**
96
+ * Delete template application
97
+ * DELETE /api/v1/applications/{appKey}
98
+ * @async
99
+ * @function deleteApplication
100
+ * @param {string} controllerUrl - Controller base URL
101
+ * @param {string} appKey - Application key
102
+ * @param {Object} authConfig - Authentication configuration
103
+ * @returns {Promise<Object>} Delete response
104
+ * @throws {Error} If deletion fails
105
+ */
106
+ async function deleteApplication(controllerUrl, appKey, authConfig) {
107
+ const client = new ApiClient(controllerUrl, authConfig);
108
+ return await client.delete(`/api/v1/applications/${appKey}`);
109
+ }
110
+
111
+ /**
112
+ * Register application in an environment
113
+ * POST /api/v1/environments/{envKey}/applications/register
114
+ * @async
115
+ * @function registerApplication
116
+ * @param {string} controllerUrl - Controller base URL
117
+ * @param {string} envKey - Environment key
118
+ * @param {Object} authConfig - Authentication configuration
119
+ * @param {Object} registrationData - Registration data
120
+ * @param {string} registrationData.key - Application key
121
+ * @param {string} registrationData.displayName - Display name
122
+ * @param {string} registrationData.type - Application type
123
+ * @param {string} [registrationData.description] - Application description
124
+ * @param {string} [registrationData.registryMode] - Registry mode
125
+ * @param {number} [registrationData.port] - Application port
126
+ * @param {string} [registrationData.image] - Container image
127
+ * @param {Object} [registrationData.externalIntegration] - External integration config
128
+ * @returns {Promise<Object>} Registration response with application and credentials
129
+ * @throws {Error} If registration fails
130
+ */
131
+ async function registerApplication(controllerUrl, envKey, authConfig, registrationData) {
132
+ const client = new ApiClient(controllerUrl, authConfig);
133
+ return await client.post(`/api/v1/environments/${envKey}/applications/register`, {
134
+ body: registrationData
135
+ });
136
+ }
137
+
138
+ /**
139
+ * Rotate application secret
140
+ * POST /api/v1/environments/{envKey}/applications/{appKey}/rotate-secret
141
+ * @async
142
+ * @function rotateApplicationSecret
143
+ * @param {string} controllerUrl - Controller base URL
144
+ * @param {string} envKey - Environment key
145
+ * @param {string} appKey - Application key
146
+ * @param {Object} authConfig - Authentication configuration
147
+ * @returns {Promise<Object>} Response with new credentials
148
+ * @throws {Error} If rotation fails
149
+ */
150
+ async function rotateApplicationSecret(controllerUrl, envKey, appKey, authConfig) {
151
+ const client = new ApiClient(controllerUrl, authConfig);
152
+ return await client.post(`/api/v1/environments/${envKey}/applications/${appKey}/rotate-secret`);
153
+ }
154
+
155
+ module.exports = {
156
+ listApplications,
157
+ createApplication,
158
+ getApplication,
159
+ updateApplication,
160
+ deleteApplication,
161
+ registerApplication,
162
+ rotateApplicationSecret
163
+ };
164
+