@aifabrix/builder 2.11.0 ā 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.
- package/.cursor/rules/project-rules.mdc +194 -0
- package/README.md +12 -0
- package/lib/api/applications.api.js +164 -0
- package/lib/api/auth.api.js +304 -0
- package/lib/api/datasources-core.api.js +87 -0
- package/lib/api/datasources-extended.api.js +117 -0
- package/lib/api/datasources.api.js +13 -0
- package/lib/api/deployments.api.js +126 -0
- package/lib/api/environments.api.js +245 -0
- package/lib/api/external-systems.api.js +251 -0
- package/lib/api/index.js +221 -0
- package/lib/api/pipeline.api.js +234 -0
- package/lib/api/types/applications.types.js +136 -0
- package/lib/api/types/auth.types.js +218 -0
- package/lib/api/types/datasources.types.js +272 -0
- package/lib/api/types/deployments.types.js +184 -0
- package/lib/api/types/environments.types.js +197 -0
- package/lib/api/types/external-systems.types.js +244 -0
- package/lib/api/types/pipeline.types.js +125 -0
- package/lib/app-list.js +5 -7
- package/lib/app-rotate-secret.js +4 -10
- package/lib/commands/login.js +19 -12
- package/lib/datasource-deploy.js +7 -30
- package/lib/datasource-list.js +9 -6
- package/lib/deployer.js +103 -135
- package/lib/environment-deploy.js +15 -26
- package/lib/external-system-deploy.js +12 -39
- package/lib/external-system-download.js +5 -13
- package/lib/external-system-test.js +9 -12
- package/lib/utils/app-register-api.js +5 -10
- package/lib/utils/deployment-errors.js +88 -6
- package/package.json +1 -1
package/lib/datasource-deploy.js
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
const fs = require('fs');
|
|
13
13
|
const chalk = require('chalk');
|
|
14
14
|
const { getDeploymentAuth } = require('./utils/token-manager');
|
|
15
|
-
const {
|
|
15
|
+
const { getEnvironmentApplication } = require('./api/environments.api');
|
|
16
|
+
const { publishDatasourceViaPipeline } = require('./api/pipeline.api');
|
|
16
17
|
const { formatApiError } = require('./utils/api-error-handler');
|
|
17
18
|
const logger = require('./utils/logger');
|
|
18
19
|
const { validateDatasourceFile } = require('./datasource-validate');
|
|
@@ -30,18 +31,8 @@ const { validateDatasourceFile } = require('./datasource-validate');
|
|
|
30
31
|
* @throws {Error} If dataplane URL cannot be retrieved
|
|
31
32
|
*/
|
|
32
33
|
async function getDataplaneUrl(controllerUrl, appKey, environment, authConfig) {
|
|
33
|
-
// Call controller API to get application details
|
|
34
|
-
|
|
35
|
-
const endpoint = `${controllerUrl}/api/v1/environments/${environment}/applications/${appKey}`;
|
|
36
|
-
|
|
37
|
-
let response;
|
|
38
|
-
if (authConfig.type === 'bearer' && authConfig.token) {
|
|
39
|
-
response = await authenticatedApiCall(endpoint, {}, authConfig.token);
|
|
40
|
-
} else {
|
|
41
|
-
// For credentials, we'd need to use a different API call method
|
|
42
|
-
// For now, use bearer token approach
|
|
43
|
-
throw new Error('Bearer token authentication required for getting dataplane URL');
|
|
44
|
-
}
|
|
34
|
+
// Call controller API to get application details using centralized API client
|
|
35
|
+
const response = await getEnvironmentApplication(controllerUrl, environment, appKey, authConfig);
|
|
45
36
|
|
|
46
37
|
if (!response.success || !response.data) {
|
|
47
38
|
const formattedError = response.formattedError || formatApiError(response);
|
|
@@ -129,24 +120,10 @@ async function deployDatasource(appKey, filePath, options) {
|
|
|
129
120
|
const dataplaneUrl = await getDataplaneUrl(options.controller, appKey, options.environment, authConfig);
|
|
130
121
|
logger.log(chalk.green(`ā Dataplane URL: ${dataplaneUrl}`));
|
|
131
122
|
|
|
132
|
-
// Publish to dataplane
|
|
123
|
+
// Publish to dataplane using pipeline workflow endpoint
|
|
133
124
|
logger.log(chalk.blue('\nš Publishing datasource to dataplane...'));
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
// Prepare publish request - send datasource configuration directly
|
|
137
|
-
let publishResponse;
|
|
138
|
-
if (authConfig.type === 'bearer' && authConfig.token) {
|
|
139
|
-
publishResponse = await authenticatedApiCall(
|
|
140
|
-
publishEndpoint,
|
|
141
|
-
{
|
|
142
|
-
method: 'POST',
|
|
143
|
-
body: JSON.stringify(datasourceConfig)
|
|
144
|
-
},
|
|
145
|
-
authConfig.token
|
|
146
|
-
);
|
|
147
|
-
} else {
|
|
148
|
-
throw new Error('Bearer token authentication required for dataplane publish');
|
|
149
|
-
}
|
|
125
|
+
|
|
126
|
+
const publishResponse = await publishDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceConfig);
|
|
150
127
|
|
|
151
128
|
if (!publishResponse.success) {
|
|
152
129
|
const formattedError = publishResponse.formattedError || formatApiError(publishResponse);
|
package/lib/datasource-list.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
const chalk = require('chalk');
|
|
12
12
|
const { getConfig } = require('./config');
|
|
13
13
|
const { getOrRefreshDeviceToken } = require('./utils/token-manager');
|
|
14
|
-
const {
|
|
14
|
+
const { listEnvironmentDatasources } = require('./api/environments.api');
|
|
15
15
|
const { formatApiError } = require('./utils/api-error-handler');
|
|
16
16
|
const logger = require('./utils/logger');
|
|
17
17
|
|
|
@@ -20,11 +20,15 @@ const logger = require('./utils/logger');
|
|
|
20
20
|
* Handles multiple response formats similar to applications list
|
|
21
21
|
*
|
|
22
22
|
* @function extractDatasources
|
|
23
|
-
* @param {Object} response - API response from
|
|
23
|
+
* @param {Object} response - API response from centralized API client
|
|
24
24
|
* @returns {Array} Array of datasources
|
|
25
25
|
* @throws {Error} If response format is invalid
|
|
26
26
|
*/
|
|
27
27
|
function extractDatasources(response) {
|
|
28
|
+
if (!response.success || !response.data) {
|
|
29
|
+
throw new Error('Invalid API response: missing success or data');
|
|
30
|
+
}
|
|
31
|
+
|
|
28
32
|
const apiResponse = response.data;
|
|
29
33
|
let datasources;
|
|
30
34
|
|
|
@@ -112,10 +116,9 @@ async function listDatasources(options) {
|
|
|
112
116
|
process.exit(1);
|
|
113
117
|
}
|
|
114
118
|
|
|
115
|
-
// Call controller API
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
const response = await authenticatedApiCall(endpoint, {}, token);
|
|
119
|
+
// Call controller API using centralized API client
|
|
120
|
+
const authConfig = { type: 'bearer', token };
|
|
121
|
+
const response = await listEnvironmentDatasources(controllerUrl, options.environment, authConfig);
|
|
119
122
|
|
|
120
123
|
if (!response.success || !response.data) {
|
|
121
124
|
const formattedError = response.formattedError || formatApiError(response);
|
package/lib/deployer.js
CHANGED
|
@@ -9,84 +9,12 @@
|
|
|
9
9
|
* @version 2.0.0
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
const axios = require('axios');
|
|
13
12
|
const chalk = require('chalk');
|
|
14
13
|
const auditLogger = require('./audit-logger');
|
|
15
14
|
const logger = require('./utils/logger');
|
|
16
|
-
const { createAuthHeaders } = require('./utils/auth-headers');
|
|
17
15
|
const { validateControllerUrl, validateEnvironmentKey } = require('./utils/deployment-validation');
|
|
18
16
|
const { handleDeploymentError, handleDeploymentErrors } = require('./utils/deployment-errors');
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Handles deployment request retry logic
|
|
22
|
-
* @async
|
|
23
|
-
* @function handleDeploymentRetry
|
|
24
|
-
* @param {string} endpoint - API endpoint
|
|
25
|
-
* @param {Object} manifest - Deployment manifest
|
|
26
|
-
* @param {Object} requestConfig - Request configuration
|
|
27
|
-
* @param {number} maxRetries - Maximum retry attempts
|
|
28
|
-
* @returns {Promise<Object>} Deployment result
|
|
29
|
-
* @throws {Error} If deployment fails after retries
|
|
30
|
-
*/
|
|
31
|
-
async function handleDeploymentRetry(endpoint, manifest, requestConfig, maxRetries) {
|
|
32
|
-
let lastError;
|
|
33
|
-
|
|
34
|
-
// Validate manifest before sending
|
|
35
|
-
if (!manifest || typeof manifest !== 'object') {
|
|
36
|
-
throw new Error('Deployment manifest is required and must be an object');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
40
|
-
try {
|
|
41
|
-
// Axios automatically serializes objects to JSON when Content-Type is application/json
|
|
42
|
-
const response = await axios.post(endpoint, manifest, requestConfig);
|
|
43
|
-
|
|
44
|
-
if (response.status >= 400) {
|
|
45
|
-
const error = new Error(`Controller returned error: ${response.status} ${response.statusText}`);
|
|
46
|
-
error.status = response.status;
|
|
47
|
-
error.response = {
|
|
48
|
-
status: response.status,
|
|
49
|
-
statusText: response.statusText,
|
|
50
|
-
data: response.data
|
|
51
|
-
};
|
|
52
|
-
error.data = response.data;
|
|
53
|
-
throw error;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// OpenAPI spec: Response 202 structure { success: boolean, deploymentId: string, status: string, deploymentUrl?: string, healthCheckUrl?: string, message?: string }
|
|
57
|
-
// Handle both OpenAPI format and legacy format for backward compatibility
|
|
58
|
-
const responseData = response.data;
|
|
59
|
-
if (responseData && typeof responseData === 'object') {
|
|
60
|
-
// OpenAPI format: { success, deploymentId, status, deploymentUrl, healthCheckUrl, message }
|
|
61
|
-
return responseData;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return response.data;
|
|
65
|
-
} catch (error) {
|
|
66
|
-
lastError = error;
|
|
67
|
-
|
|
68
|
-
if (attempt < maxRetries) {
|
|
69
|
-
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
|
70
|
-
logger.log(chalk.yellow(`ā ļø Deployment attempt ${attempt} failed, retrying in ${delay}ms...`));
|
|
71
|
-
await new Promise(resolve => setTimeout(resolve, delay));
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// Preserve formatted error if available
|
|
77
|
-
const errorMessage = lastError.formatted || lastError.message;
|
|
78
|
-
const error = new Error(`Deployment failed after ${maxRetries} attempts: ${errorMessage}`);
|
|
79
|
-
if (lastError.formatted) {
|
|
80
|
-
error.formatted = lastError.formatted;
|
|
81
|
-
}
|
|
82
|
-
if (lastError.status) {
|
|
83
|
-
error.status = lastError.status;
|
|
84
|
-
}
|
|
85
|
-
if (lastError.data) {
|
|
86
|
-
error.data = lastError.data;
|
|
87
|
-
}
|
|
88
|
-
throw error;
|
|
89
|
-
}
|
|
17
|
+
const { validatePipeline, deployPipeline, getPipelineDeployment } = require('./api/pipeline.api');
|
|
90
18
|
|
|
91
19
|
/**
|
|
92
20
|
* Validates deployment configuration via validate endpoint
|
|
@@ -103,8 +31,6 @@ async function handleDeploymentRetry(endpoint, manifest, requestConfig, maxRetri
|
|
|
103
31
|
*/
|
|
104
32
|
async function validateDeployment(url, envKey, manifest, authConfig, options = {}) {
|
|
105
33
|
const validatedEnvKey = validateEnvironmentKey(envKey);
|
|
106
|
-
const endpoint = `${url}/api/v1/pipeline/${validatedEnvKey}/validate`;
|
|
107
|
-
const timeout = options.timeout || 30000;
|
|
108
34
|
const maxRetries = options.maxRetries || 3;
|
|
109
35
|
|
|
110
36
|
// Extract clientId and clientSecret
|
|
@@ -118,58 +44,68 @@ async function validateDeployment(url, envKey, manifest, authConfig, options = {
|
|
|
118
44
|
|
|
119
45
|
// Build validation request
|
|
120
46
|
const repositoryUrl = options.repositoryUrl || `https://github.com/aifabrix/${manifest.key}`;
|
|
121
|
-
const
|
|
47
|
+
const validationData = {
|
|
122
48
|
clientId: clientId,
|
|
123
49
|
clientSecret: clientSecret,
|
|
124
50
|
repositoryUrl: repositoryUrl,
|
|
125
51
|
applicationConfig: manifest
|
|
126
52
|
};
|
|
127
53
|
|
|
128
|
-
//
|
|
129
|
-
const
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
'x-client-id': clientId,
|
|
134
|
-
'x-client-secret': clientSecret
|
|
135
|
-
},
|
|
136
|
-
timeout,
|
|
137
|
-
validateStatus: (status) => status < 600 // Don't throw on any status
|
|
54
|
+
// Use centralized API client with retry logic
|
|
55
|
+
const pipelineAuthConfig = {
|
|
56
|
+
type: 'client-credentials',
|
|
57
|
+
clientId: clientId,
|
|
58
|
+
clientSecret: clientSecret
|
|
138
59
|
};
|
|
139
60
|
|
|
140
61
|
let lastError;
|
|
141
62
|
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
142
63
|
try {
|
|
143
|
-
const response = await
|
|
64
|
+
const response = await validatePipeline(url, validatedEnvKey, pipelineAuthConfig, validationData);
|
|
144
65
|
|
|
145
66
|
// Handle successful validation (200 OK with valid: true)
|
|
146
|
-
if (response.
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
67
|
+
if (response.success && response.data) {
|
|
68
|
+
const responseData = response.data.data || response.data;
|
|
69
|
+
if (responseData.valid === true) {
|
|
70
|
+
return {
|
|
71
|
+
success: true,
|
|
72
|
+
validateToken: responseData.validateToken,
|
|
73
|
+
draftDeploymentId: responseData.draftDeploymentId,
|
|
74
|
+
imageServer: responseData.imageServer,
|
|
75
|
+
imageUsername: responseData.imageUsername,
|
|
76
|
+
imagePassword: responseData.imagePassword,
|
|
77
|
+
expiresAt: responseData.expiresAt
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
// Handle validation failure (valid: false)
|
|
81
|
+
if (responseData.valid === false) {
|
|
82
|
+
const errorMessage = responseData.errors && responseData.errors.length > 0
|
|
83
|
+
? `Validation failed: ${responseData.errors.join(', ')}`
|
|
84
|
+
: 'Validation failed: Invalid configuration';
|
|
85
|
+
const error = new Error(errorMessage);
|
|
86
|
+
error.status = 400;
|
|
87
|
+
error.data = responseData;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
156
90
|
}
|
|
157
91
|
|
|
158
|
-
// Handle validation errors
|
|
159
|
-
if (response.
|
|
160
|
-
const error = new Error(`Validation request failed: ${response.
|
|
161
|
-
error.status = response.status;
|
|
162
|
-
error.response = {
|
|
163
|
-
status: response.status,
|
|
164
|
-
statusText: response.statusText,
|
|
165
|
-
data: response.data
|
|
166
|
-
};
|
|
92
|
+
// Handle validation errors (non-success responses)
|
|
93
|
+
if (!response.success) {
|
|
94
|
+
const error = new Error(`Validation request failed: ${response.formattedError || response.error || 'Unknown error'}`);
|
|
95
|
+
error.status = response.status || 400;
|
|
167
96
|
error.data = response.data;
|
|
168
97
|
throw error;
|
|
169
98
|
}
|
|
99
|
+
|
|
100
|
+
// If we get here, response.success is true but valid is not true and not false
|
|
101
|
+
// This is an unexpected state, throw an error
|
|
102
|
+
const error = new Error('Validation response is in an unexpected state');
|
|
103
|
+
error.status = 400;
|
|
104
|
+
error.data = response.data;
|
|
105
|
+
throw error;
|
|
170
106
|
} catch (error) {
|
|
171
107
|
lastError = error;
|
|
172
|
-
const shouldRetry = attempt < maxRetries && error.
|
|
108
|
+
const shouldRetry = attempt < maxRetries && error.status && error.status >= 500;
|
|
173
109
|
if (shouldRetry) {
|
|
174
110
|
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
|
175
111
|
logger.log(chalk.yellow(`ā ļø Validation attempt ${attempt} failed, retrying in ${delay}ms...`));
|
|
@@ -198,8 +134,6 @@ async function validateDeployment(url, envKey, manifest, authConfig, options = {
|
|
|
198
134
|
*/
|
|
199
135
|
async function sendDeploymentRequest(url, envKey, validateToken, authConfig, options = {}) {
|
|
200
136
|
const validatedEnvKey = validateEnvironmentKey(envKey);
|
|
201
|
-
const endpoint = `${url}/api/v1/pipeline/${validatedEnvKey}/deploy`;
|
|
202
|
-
const timeout = options.timeout || 30000;
|
|
203
137
|
const maxRetries = options.maxRetries || 3;
|
|
204
138
|
|
|
205
139
|
// Extract clientId and clientSecret for deploy endpoint
|
|
@@ -212,24 +146,58 @@ async function sendDeploymentRequest(url, envKey, validateToken, authConfig, opt
|
|
|
212
146
|
|
|
213
147
|
// Build deployment request
|
|
214
148
|
const imageTag = options.imageTag || 'latest';
|
|
215
|
-
const
|
|
149
|
+
const deployData = {
|
|
216
150
|
validateToken: validateToken,
|
|
217
151
|
imageTag: imageTag
|
|
218
152
|
};
|
|
219
153
|
|
|
220
|
-
//
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
'x-client-id': clientId,
|
|
226
|
-
'x-client-secret': clientSecret
|
|
227
|
-
},
|
|
228
|
-
timeout,
|
|
229
|
-
validateStatus: (status) => status < 500
|
|
154
|
+
// Use centralized API client with retry logic
|
|
155
|
+
const pipelineAuthConfig = {
|
|
156
|
+
type: 'client-credentials',
|
|
157
|
+
clientId: clientId,
|
|
158
|
+
clientSecret: clientSecret
|
|
230
159
|
};
|
|
231
160
|
|
|
232
|
-
|
|
161
|
+
// Wrap API call with retry logic
|
|
162
|
+
let lastError;
|
|
163
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
164
|
+
try {
|
|
165
|
+
const response = await deployPipeline(url, validatedEnvKey, pipelineAuthConfig, deployData);
|
|
166
|
+
|
|
167
|
+
if (response.success) {
|
|
168
|
+
return response.data.data || response.data;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Handle deployment errors
|
|
172
|
+
if (response.status >= 400) {
|
|
173
|
+
const error = new Error(`Deployment request failed: ${response.formattedError || response.error || 'Unknown error'}`);
|
|
174
|
+
error.status = response.status;
|
|
175
|
+
error.data = response.data;
|
|
176
|
+
throw error;
|
|
177
|
+
}
|
|
178
|
+
} catch (error) {
|
|
179
|
+
lastError = error;
|
|
180
|
+
if (attempt < maxRetries) {
|
|
181
|
+
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
|
182
|
+
logger.log(chalk.yellow(`ā ļø Deployment attempt ${attempt} failed, retrying in ${delay}ms...`));
|
|
183
|
+
await new Promise(resolve => setTimeout(resolve, delay));
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// Preserve formatted error if available
|
|
189
|
+
const errorMessage = lastError.formatted || lastError.message;
|
|
190
|
+
const error = new Error(`Deployment failed after ${maxRetries} attempts: ${errorMessage}`);
|
|
191
|
+
if (lastError.formatted) {
|
|
192
|
+
error.formatted = lastError.formatted;
|
|
193
|
+
}
|
|
194
|
+
if (lastError.status) {
|
|
195
|
+
error.status = lastError.status;
|
|
196
|
+
}
|
|
197
|
+
if (lastError.data) {
|
|
198
|
+
error.data = lastError.data;
|
|
199
|
+
}
|
|
200
|
+
throw error;
|
|
233
201
|
}
|
|
234
202
|
|
|
235
203
|
/**
|
|
@@ -259,20 +227,17 @@ async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authCon
|
|
|
259
227
|
const maxAttempts = options.maxAttempts || 60;
|
|
260
228
|
|
|
261
229
|
const validatedEnvKey = validateEnvironmentKey(envKey);
|
|
262
|
-
|
|
230
|
+
|
|
231
|
+
// Convert authConfig to format expected by API client
|
|
232
|
+
const pipelineAuthConfig = authConfig.type === 'bearer'
|
|
233
|
+
? { type: 'bearer', token: authConfig.token }
|
|
234
|
+
: { type: 'client-credentials', clientId: authConfig.clientId, clientSecret: authConfig.clientSecret };
|
|
263
235
|
|
|
264
236
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
265
237
|
try {
|
|
266
|
-
const response = await
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
...createAuthHeaders(authConfig)
|
|
270
|
-
},
|
|
271
|
-
timeout: 10000,
|
|
272
|
-
validateStatus: (status) => status < 500
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
if (response.status === 200) {
|
|
238
|
+
const response = await getPipelineDeployment(controllerUrl, validatedEnvKey, deploymentId, pipelineAuthConfig);
|
|
239
|
+
|
|
240
|
+
if (response.success && response.data) {
|
|
276
241
|
// OpenAPI spec: Response structure { success: boolean, data: { status, progress, ... }, timestamp: string }
|
|
277
242
|
const responseData = response.data;
|
|
278
243
|
const deploymentData = responseData.data || responseData;
|
|
@@ -289,11 +254,14 @@ async function pollDeploymentStatus(deploymentId, controllerUrl, envKey, authCon
|
|
|
289
254
|
await new Promise(resolve => setTimeout(resolve, interval));
|
|
290
255
|
}
|
|
291
256
|
} else {
|
|
292
|
-
|
|
257
|
+
if (response.status === 404) {
|
|
258
|
+
throw new Error(`Deployment ${deploymentId} not found`);
|
|
259
|
+
}
|
|
260
|
+
throw new Error(`Status check failed: ${response.formattedError || response.error || 'Unknown error'}`);
|
|
293
261
|
}
|
|
294
262
|
} catch (error) {
|
|
295
|
-
if (error.
|
|
296
|
-
throw
|
|
263
|
+
if (error.message && error.message.includes('not found')) {
|
|
264
|
+
throw error;
|
|
297
265
|
}
|
|
298
266
|
throw error;
|
|
299
267
|
}
|
|
@@ -14,7 +14,8 @@ const logger = require('./utils/logger');
|
|
|
14
14
|
const config = require('./config');
|
|
15
15
|
const { validateControllerUrl, validateEnvironmentKey } = require('./utils/deployment-validation');
|
|
16
16
|
const { getOrRefreshDeviceToken } = require('./utils/token-manager');
|
|
17
|
-
const {
|
|
17
|
+
const { getEnvironmentStatus } = require('./api/environments.api');
|
|
18
|
+
const { deployEnvironment: deployEnvironmentInfra } = require('./api/deployments.api');
|
|
18
19
|
const { handleDeploymentErrors } = require('./utils/deployment-errors');
|
|
19
20
|
const auditLogger = require('./audit-logger');
|
|
20
21
|
|
|
@@ -93,32 +94,23 @@ async function sendEnvironmentDeployment(controllerUrl, envKey, authConfig, opti
|
|
|
93
94
|
deploymentRequest.description += ` (config: ${options.config})`;
|
|
94
95
|
}
|
|
95
96
|
|
|
96
|
-
// API endpoint: POST /api/v1/environments/{env}/deploy
|
|
97
|
-
// Alternative: POST /api/v1/environments/deploy with environment in body
|
|
98
|
-
const endpoint = `${validatedUrl}/api/v1/environments/${validatedEnvKey}/deploy`;
|
|
99
|
-
|
|
100
97
|
// Log deployment attempt for audit
|
|
101
98
|
await auditLogger.logDeploymentAttempt(validatedEnvKey, validatedUrl, options);
|
|
102
99
|
|
|
103
100
|
try {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
method: 'POST',
|
|
108
|
-
body: JSON.stringify(deploymentRequest)
|
|
109
|
-
},
|
|
110
|
-
authConfig.token
|
|
111
|
-
);
|
|
101
|
+
// Use centralized API client
|
|
102
|
+
const apiAuthConfig = { type: 'bearer', token: authConfig.token };
|
|
103
|
+
const response = await deployEnvironmentInfra(validatedUrl, validatedEnvKey, apiAuthConfig, deploymentRequest);
|
|
112
104
|
|
|
113
105
|
if (!response.success) {
|
|
114
106
|
const error = new Error(response.formattedError || response.error || 'Environment deployment failed');
|
|
115
107
|
error.status = response.status;
|
|
116
|
-
error.data = response.errorData;
|
|
108
|
+
error.data = response.errorData || response.data;
|
|
117
109
|
throw error;
|
|
118
110
|
}
|
|
119
111
|
|
|
120
112
|
// Handle response structure
|
|
121
|
-
const responseData = response.data || {};
|
|
113
|
+
const responseData = response.data.data || response.data || {};
|
|
122
114
|
return {
|
|
123
115
|
success: true,
|
|
124
116
|
environment: validatedEnvKey,
|
|
@@ -150,25 +142,22 @@ async function pollEnvironmentStatus(deploymentId, controllerUrl, envKey, authCo
|
|
|
150
142
|
|
|
151
143
|
const pollInterval = options.pollInterval || 5000;
|
|
152
144
|
const maxAttempts = options.maxAttempts || 60;
|
|
153
|
-
const statusEndpoint = `${validatedUrl}/api/v1/environments/${validatedEnvKey}/status`;
|
|
154
145
|
|
|
155
146
|
logger.log(chalk.blue(`ā³ Polling environment status (${pollInterval}ms intervals)...`));
|
|
156
147
|
|
|
148
|
+
// Use centralized API client
|
|
149
|
+
const apiAuthConfig = { type: 'bearer', token: authConfig.token };
|
|
150
|
+
|
|
157
151
|
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
158
152
|
try {
|
|
159
153
|
await new Promise(resolve => setTimeout(resolve, pollInterval));
|
|
160
154
|
|
|
161
|
-
const response = await
|
|
162
|
-
statusEndpoint,
|
|
163
|
-
{
|
|
164
|
-
method: 'GET'
|
|
165
|
-
},
|
|
166
|
-
authConfig.token
|
|
167
|
-
);
|
|
155
|
+
const response = await getEnvironmentStatus(validatedUrl, validatedEnvKey, apiAuthConfig);
|
|
168
156
|
|
|
169
157
|
if (response.success && response.data) {
|
|
170
|
-
const
|
|
171
|
-
const
|
|
158
|
+
const responseData = response.data.data || response.data;
|
|
159
|
+
const status = responseData.status || responseData.ready;
|
|
160
|
+
const isReady = status === 'ready' || status === 'completed' || responseData.ready === true;
|
|
172
161
|
|
|
173
162
|
if (isReady) {
|
|
174
163
|
return {
|
|
@@ -181,7 +170,7 @@ async function pollEnvironmentStatus(deploymentId, controllerUrl, envKey, authCo
|
|
|
181
170
|
|
|
182
171
|
// Check for terminal failure states
|
|
183
172
|
if (status === 'failed' || status === 'error') {
|
|
184
|
-
throw new Error(`Environment deployment failed: ${
|
|
173
|
+
throw new Error(`Environment deployment failed: ${responseData.message || 'Unknown error'}`);
|
|
185
174
|
}
|
|
186
175
|
}
|
|
187
176
|
} catch (error) {
|
|
@@ -14,7 +14,13 @@ const fsSync = require('fs');
|
|
|
14
14
|
const path = require('path');
|
|
15
15
|
const yaml = require('js-yaml');
|
|
16
16
|
const chalk = require('chalk');
|
|
17
|
-
const {
|
|
17
|
+
const {
|
|
18
|
+
deployExternalSystemViaPipeline,
|
|
19
|
+
deployDatasourceViaPipeline,
|
|
20
|
+
uploadApplicationViaPipeline,
|
|
21
|
+
validateUploadViaPipeline,
|
|
22
|
+
publishUploadViaPipeline
|
|
23
|
+
} = require('./api/pipeline.api');
|
|
18
24
|
const { getDeploymentAuth } = require('./utils/token-manager');
|
|
19
25
|
const { getConfig } = require('./config');
|
|
20
26
|
const logger = require('./utils/logger');
|
|
@@ -155,14 +161,7 @@ async function buildExternalSystem(appName, options = {}) {
|
|
|
155
161
|
const systemContent = await fs.readFile(systemFiles[0], 'utf8');
|
|
156
162
|
const systemJson = JSON.parse(systemContent);
|
|
157
163
|
|
|
158
|
-
const systemResponse = await
|
|
159
|
-
`${dataplaneUrl}/api/v1/pipeline/deploy`,
|
|
160
|
-
{
|
|
161
|
-
method: 'POST',
|
|
162
|
-
body: JSON.stringify(systemJson)
|
|
163
|
-
},
|
|
164
|
-
authConfig.token
|
|
165
|
-
);
|
|
164
|
+
const systemResponse = await deployExternalSystemViaPipeline(dataplaneUrl, authConfig, systemJson);
|
|
166
165
|
|
|
167
166
|
if (!systemResponse.success) {
|
|
168
167
|
throw new Error(`Failed to deploy external system: ${systemResponse.error || systemResponse.formattedError}`);
|
|
@@ -178,14 +177,7 @@ async function buildExternalSystem(appName, options = {}) {
|
|
|
178
177
|
const datasourceContent = await fs.readFile(datasourceFile, 'utf8');
|
|
179
178
|
const datasourceJson = JSON.parse(datasourceContent);
|
|
180
179
|
|
|
181
|
-
const datasourceResponse = await
|
|
182
|
-
`${dataplaneUrl}/api/v1/pipeline/${systemKey}/deploy`,
|
|
183
|
-
{
|
|
184
|
-
method: 'POST',
|
|
185
|
-
body: JSON.stringify(datasourceJson)
|
|
186
|
-
},
|
|
187
|
-
authConfig.token
|
|
188
|
-
);
|
|
180
|
+
const datasourceResponse = await deployDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceJson);
|
|
189
181
|
|
|
190
182
|
if (!datasourceResponse.success) {
|
|
191
183
|
throw new Error(`Failed to deploy datasource ${datasourceName}: ${datasourceResponse.error || datasourceResponse.formattedError}`);
|
|
@@ -245,14 +237,7 @@ async function deployExternalSystem(appName, options = {}) {
|
|
|
245
237
|
|
|
246
238
|
// Step 1: Upload application
|
|
247
239
|
logger.log(chalk.blue('š¤ Uploading application configuration...'));
|
|
248
|
-
const uploadResponse = await
|
|
249
|
-
`${dataplaneUrl}/api/v1/pipeline/upload`,
|
|
250
|
-
{
|
|
251
|
-
method: 'POST',
|
|
252
|
-
body: JSON.stringify(applicationSchema)
|
|
253
|
-
},
|
|
254
|
-
authConfig.token
|
|
255
|
-
);
|
|
240
|
+
const uploadResponse = await uploadApplicationViaPipeline(dataplaneUrl, authConfig, applicationSchema);
|
|
256
241
|
|
|
257
242
|
if (!uploadResponse.success || !uploadResponse.data) {
|
|
258
243
|
throw new Error(`Failed to upload application: ${uploadResponse.error || uploadResponse.formattedError || 'Unknown error'}`);
|
|
@@ -270,13 +255,7 @@ async function deployExternalSystem(appName, options = {}) {
|
|
|
270
255
|
// Step 2: Validate upload (optional, can be skipped)
|
|
271
256
|
if (!options.skipValidation) {
|
|
272
257
|
logger.log(chalk.blue('š Validating upload...'));
|
|
273
|
-
const validateResponse = await
|
|
274
|
-
`${dataplaneUrl}/api/v1/pipeline/upload/${uploadId}/validate`,
|
|
275
|
-
{
|
|
276
|
-
method: 'POST'
|
|
277
|
-
},
|
|
278
|
-
authConfig.token
|
|
279
|
-
);
|
|
258
|
+
const validateResponse = await validateUploadViaPipeline(dataplaneUrl, uploadId, authConfig);
|
|
280
259
|
|
|
281
260
|
if (!validateResponse.success || !validateResponse.data) {
|
|
282
261
|
throw new Error(`Validation failed: ${validateResponse.error || validateResponse.formattedError || 'Unknown error'}`);
|
|
@@ -308,13 +287,7 @@ async function deployExternalSystem(appName, options = {}) {
|
|
|
308
287
|
const generateMcpContract = options.generateMcpContract !== false; // Default to true
|
|
309
288
|
logger.log(chalk.blue(`š¢ Publishing application (MCP contract: ${generateMcpContract ? 'enabled' : 'disabled'})...`));
|
|
310
289
|
|
|
311
|
-
const publishResponse = await
|
|
312
|
-
`${dataplaneUrl}/api/v1/pipeline/upload/${uploadId}/publish?generateMcpContract=${generateMcpContract}`,
|
|
313
|
-
{
|
|
314
|
-
method: 'POST'
|
|
315
|
-
},
|
|
316
|
-
authConfig.token
|
|
317
|
-
);
|
|
290
|
+
const publishResponse = await publishUploadViaPipeline(dataplaneUrl, uploadId, authConfig, { generateMcpContract });
|
|
318
291
|
|
|
319
292
|
if (!publishResponse.success || !publishResponse.data) {
|
|
320
293
|
throw new Error(`Failed to publish application: ${publishResponse.error || publishResponse.formattedError || 'Unknown error'}`);
|
|
@@ -14,7 +14,7 @@ const path = require('path');
|
|
|
14
14
|
const os = require('os');
|
|
15
15
|
const yaml = require('js-yaml');
|
|
16
16
|
const chalk = require('chalk');
|
|
17
|
-
const {
|
|
17
|
+
const { getExternalSystemConfig } = require('./api/external-systems.api');
|
|
18
18
|
const { getDeploymentAuth } = require('./utils/token-manager');
|
|
19
19
|
const { getDataplaneUrl } = require('./datasource-deploy');
|
|
20
20
|
const { getConfig } = require('./config');
|
|
@@ -284,14 +284,12 @@ async function downloadExternalSystem(systemKey, options = {}) {
|
|
|
284
284
|
const dataplaneUrl = await getDataplaneUrl(controllerUrl, systemKey, environment, authConfig);
|
|
285
285
|
logger.log(chalk.green(`ā Dataplane URL: ${dataplaneUrl}`));
|
|
286
286
|
|
|
287
|
-
// Download system configuration
|
|
288
|
-
|
|
289
|
-
const downloadEndpoint = `${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`;
|
|
290
|
-
logger.log(chalk.blue(`š” Downloading from: ${downloadEndpoint}`));
|
|
287
|
+
// Download system configuration using centralized API client
|
|
288
|
+
logger.log(chalk.blue(`š” Downloading system configuration: ${systemKey}`));
|
|
291
289
|
|
|
292
290
|
if (options.dryRun) {
|
|
293
291
|
logger.log(chalk.yellow('š Dry run mode - would download from:'));
|
|
294
|
-
logger.log(chalk.gray(` ${
|
|
292
|
+
logger.log(chalk.gray(` ${dataplaneUrl}/api/v1/external/systems/${systemKey}/config`));
|
|
295
293
|
logger.log(chalk.yellow('\nWould create:'));
|
|
296
294
|
logger.log(chalk.gray(` integration/${systemKey}/`));
|
|
297
295
|
logger.log(chalk.gray(` integration/${systemKey}/variables.yaml`));
|
|
@@ -301,13 +299,7 @@ async function downloadExternalSystem(systemKey, options = {}) {
|
|
|
301
299
|
return;
|
|
302
300
|
}
|
|
303
301
|
|
|
304
|
-
const response = await
|
|
305
|
-
downloadEndpoint,
|
|
306
|
-
{
|
|
307
|
-
method: 'GET'
|
|
308
|
-
},
|
|
309
|
-
authConfig.token
|
|
310
|
-
);
|
|
302
|
+
const response = await getExternalSystemConfig(dataplaneUrl, systemKey, authConfig);
|
|
311
303
|
|
|
312
304
|
if (!response.success || !response.data) {
|
|
313
305
|
throw new Error(`Failed to download system configuration: ${response.error || response.formattedError || 'Unknown error'}`);
|