@aifabrix/builder 2.41.0 → 2.42.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/docs-rules.mdc +30 -0
- package/README.md +1 -1
- package/integration/hubspot/README.md +8 -4
- package/integration/hubspot/application.json +54 -0
- package/integration/hubspot/create-hubspot.js +9 -136
- package/integration/hubspot/env.template +3 -4
- package/integration/hubspot/hubspot-datasource-company.json +343 -5
- package/integration/hubspot/hubspot-datasource-contact.json +413 -5
- package/integration/hubspot/hubspot-datasource-deal.json +341 -4
- package/integration/hubspot/hubspot-datasource-users.json +116 -0
- package/integration/hubspot/hubspot-deploy.json +1250 -108
- package/integration/hubspot/hubspot-system.json +15 -32
- package/integration/hubspot/test-dataplane-down-tests.js +17 -16
- package/integration/hubspot/test-dataplane-down.js +2 -2
- package/jest.config.manual.js +2 -1
- package/lib/api/external-test.api.js +111 -0
- package/lib/api/index.js +42 -19
- package/lib/api/pipeline.api.js +66 -120
- package/lib/api/types/pipeline.types.js +37 -0
- package/lib/api/wizard-platform.api.js +61 -0
- package/lib/api/wizard.api.js +34 -1
- package/lib/app/config.js +23 -11
- package/lib/app/index.js +3 -1
- package/lib/app/prompts.js +44 -29
- package/lib/app/readme.js +8 -3
- package/lib/app/run-env-compose.js +64 -1
- package/lib/app/run-helpers.js +1 -1
- package/lib/app/show-display.js +1 -1
- package/lib/cli/setup-app.js +42 -11
- package/lib/cli/setup-credential-deployment.js +31 -6
- package/lib/cli/setup-dev.js +27 -0
- package/lib/cli/setup-environment.js +12 -4
- package/lib/cli/setup-external-system.js +19 -4
- package/lib/cli/setup-infra.js +54 -14
- package/lib/cli/setup-utility.js +117 -21
- package/lib/commands/credential-env.js +162 -0
- package/lib/commands/credential-list.js +17 -22
- package/lib/commands/credential-push.js +96 -0
- package/lib/commands/datasource.js +77 -6
- package/lib/commands/dev-init.js +39 -1
- package/lib/commands/repair-auth-config.js +99 -0
- package/lib/commands/repair-datasource-keys.js +208 -0
- package/lib/commands/repair-datasource.js +235 -0
- package/lib/commands/repair-env-template.js +348 -0
- package/lib/commands/repair-internal.js +85 -0
- package/lib/commands/repair-rbac.js +158 -0
- package/lib/commands/repair.js +507 -0
- package/lib/commands/test-e2e-external.js +165 -0
- package/lib/commands/upload.js +71 -40
- package/lib/commands/wizard-core-helpers.js +226 -4
- package/lib/commands/wizard-core.js +67 -29
- package/lib/commands/wizard-dataplane.js +1 -1
- package/lib/commands/wizard-entity-selection.js +43 -0
- package/lib/commands/wizard-headless.js +44 -5
- package/lib/commands/wizard-helpers.js +7 -3
- package/lib/commands/wizard.js +86 -64
- package/lib/core/config.js +7 -1
- package/lib/core/secrets.js +33 -12
- package/lib/datasource/deploy.js +12 -3
- package/lib/datasource/test-e2e.js +219 -0
- package/lib/datasource/test-integration.js +154 -0
- package/lib/deployment/deployer.js +7 -5
- package/lib/external-system/download.js +182 -204
- package/lib/external-system/generator.js +204 -56
- package/lib/external-system/test-execution.js +2 -1
- package/lib/external-system/test-system-level.js +73 -0
- package/lib/external-system/test.js +51 -18
- package/lib/generator/external-controller-manifest.js +29 -2
- package/lib/generator/external-schema-utils.js +1 -1
- package/lib/generator/external.js +10 -3
- package/lib/generator/index.js +4 -1
- package/lib/generator/split-readme.js +1 -0
- package/lib/generator/split-variables.js +7 -1
- package/lib/generator/split.js +194 -54
- package/lib/generator/wizard-prompts-secondary.js +294 -0
- package/lib/generator/wizard-prompts.js +105 -106
- package/lib/generator/wizard-readme.js +88 -0
- package/lib/generator/wizard.js +147 -158
- package/lib/infrastructure/compose.js +11 -1
- package/lib/infrastructure/index.js +11 -3
- package/lib/infrastructure/services.js +22 -11
- package/lib/schema/application-schema.json +8 -5
- package/lib/schema/external-datasource.schema.json +49 -26
- package/lib/schema/external-system.schema.json +82 -6
- package/lib/schema/wizard-config.schema.json +16 -0
- package/lib/utils/api.js +38 -10
- package/lib/utils/auth-headers.js +8 -7
- package/lib/utils/compose-generator.js +1 -1
- package/lib/utils/compose-handlebars-helpers.js +11 -0
- package/lib/utils/config-format-preference.js +51 -0
- package/lib/utils/config-format.js +36 -0
- package/lib/utils/configuration-env-resolver.js +179 -0
- package/lib/utils/credential-display.js +83 -0
- package/lib/utils/credential-secrets-env.js +115 -25
- package/lib/utils/dataplane-pipeline-warning.js +28 -0
- package/lib/utils/deployment-validation-helpers.js +4 -4
- package/lib/utils/dev-ca-install.js +139 -0
- package/lib/utils/env-copy.js +23 -3
- package/lib/utils/error-formatters/http-status-errors.js +0 -1
- package/lib/utils/error-formatters/permission-errors.js +0 -1
- package/lib/utils/error-formatters/validation-errors.js +0 -1
- package/lib/utils/external-readme.js +56 -29
- package/lib/utils/external-system-display.js +59 -1
- package/lib/utils/external-system-test-helpers.js +21 -8
- package/lib/utils/external-system-validators.js +3 -0
- package/lib/utils/file-upload.js +20 -50
- package/lib/utils/help-builder.js +1 -0
- package/lib/utils/infra-status.js +50 -44
- package/lib/utils/local-secrets.js +5 -5
- package/lib/utils/paths.js +85 -4
- package/lib/utils/secrets-canonical.js +93 -0
- package/lib/utils/secrets-generator.js +20 -0
- package/lib/utils/secrets-helpers.js +75 -89
- package/lib/utils/test-log-writer.js +56 -0
- package/lib/utils/token-manager.js +24 -32
- package/lib/validation/env-template-auth.js +157 -0
- package/lib/validation/env-template-kv.js +41 -0
- package/lib/validation/external-manifest-validator.js +25 -0
- package/lib/validation/external-system-auth-rules.js +86 -0
- package/lib/validation/validate-batch.js +149 -0
- package/lib/validation/validate-datasource-keys-api.js +33 -0
- package/lib/validation/validate-display.js +94 -16
- package/lib/validation/validate.js +25 -12
- package/lib/validation/validator.js +7 -9
- package/lib/validation/wizard-datasource-validation.js +50 -0
- package/package.json +7 -2
- package/templates/applications/dataplane/application.yaml +1 -1
- package/templates/applications/dataplane/env.template +5 -5
- package/templates/applications/dataplane/rbac.yaml +2 -2
- package/templates/applications/miso-controller/env.template +1 -1
- package/templates/external-system/README.md.hbs +65 -25
- package/templates/external-system/deploy.js.hbs +4 -2
- package/templates/external-system/external-datasource.yaml.hbs +217 -0
- package/templates/external-system/external-system.json.hbs +1 -18
- package/templates/infra/compose.yaml.hbs +6 -0
- package/templates/python/docker-compose.hbs +4 -4
- package/templates/typescript/docker-compose.hbs +4 -4
- package/integration/hubspot/application.yaml +0 -37
|
@@ -5,41 +5,18 @@
|
|
|
5
5
|
"type": "openapi",
|
|
6
6
|
"enabled": true,
|
|
7
7
|
"authentication": {
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
"tokenUrl": "
|
|
12
|
-
"
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
"crm.objects.contacts.read",
|
|
18
|
-
"crm.objects.contacts.write",
|
|
19
|
-
"crm.objects.deals.read",
|
|
20
|
-
"crm.objects.deals.write"
|
|
21
|
-
]
|
|
8
|
+
"method": "oauth2",
|
|
9
|
+
"variables": {
|
|
10
|
+
"baseUrl": "https://api.hubapi.com",
|
|
11
|
+
"tokenUrl": "https://api.hubapi.com/oauth/v1/token",
|
|
12
|
+
"scope": "crm.objects.companies.read crm.objects.companies.write crm.objects.contacts.read crm.objects.contacts.write crm.objects.deals.read crm.objects.deals.write"
|
|
13
|
+
},
|
|
14
|
+
"security": {
|
|
15
|
+
"clientId": "kv://hubspot/clientid",
|
|
16
|
+
"clientSecret": "kv://hubspot/clientsecret"
|
|
22
17
|
}
|
|
23
18
|
},
|
|
24
19
|
"configuration": [
|
|
25
|
-
{
|
|
26
|
-
"name": "CLIENTID",
|
|
27
|
-
"value": "hubspot-clientidKeyVault",
|
|
28
|
-
"location": "keyvault",
|
|
29
|
-
"required": true
|
|
30
|
-
},
|
|
31
|
-
{
|
|
32
|
-
"name": "CLIENTSECRET",
|
|
33
|
-
"value": "hubspot-clientsecretKeyVault",
|
|
34
|
-
"location": "keyvault",
|
|
35
|
-
"required": true
|
|
36
|
-
},
|
|
37
|
-
{
|
|
38
|
-
"name": "TOKENURL",
|
|
39
|
-
"value": "https://api.hubapi.com/oauth/v1/token",
|
|
40
|
-
"location": "variable",
|
|
41
|
-
"required": true
|
|
42
|
-
},
|
|
43
20
|
{
|
|
44
21
|
"name": "HUBSPOT_API_VERSION",
|
|
45
22
|
"value": "v3",
|
|
@@ -86,5 +63,11 @@
|
|
|
86
63
|
"sales",
|
|
87
64
|
"marketing",
|
|
88
65
|
"hubspot"
|
|
66
|
+
],
|
|
67
|
+
"dataSources": [
|
|
68
|
+
"hubspot-company",
|
|
69
|
+
"hubspot-contact",
|
|
70
|
+
"hubspot-deal",
|
|
71
|
+
"hubspot-users-datasource"
|
|
89
72
|
]
|
|
90
73
|
}
|
|
@@ -194,27 +194,27 @@ async function createTestDatasource(datasourcePath) {
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
/**
|
|
197
|
-
* Builds datasource
|
|
198
|
-
* @function
|
|
197
|
+
* Builds datasource upload command arguments
|
|
198
|
+
* @function buildDatasourceUploadArgs
|
|
199
199
|
* @param {string} datasourcePath - Path to datasource file
|
|
200
200
|
* @returns {string[]} Command arguments
|
|
201
201
|
*/
|
|
202
|
-
function
|
|
202
|
+
function buildDatasourceUploadArgs(datasourcePath) {
|
|
203
203
|
return [
|
|
204
204
|
'bin/aifabrix.js',
|
|
205
205
|
'datasource',
|
|
206
|
-
'
|
|
206
|
+
'upload',
|
|
207
207
|
'test-app',
|
|
208
208
|
datasourcePath
|
|
209
209
|
];
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
/**
|
|
213
|
-
* Gets expected error patterns for datasource
|
|
214
|
-
* @function
|
|
213
|
+
* Gets expected error patterns for datasource upload
|
|
214
|
+
* @function getDatasourceUploadErrorPatterns
|
|
215
215
|
* @returns {string[]} Expected error patterns
|
|
216
216
|
*/
|
|
217
|
-
function
|
|
217
|
+
function getDatasourceUploadErrorPatterns() {
|
|
218
218
|
return [
|
|
219
219
|
'failed to connect',
|
|
220
220
|
'connection refused',
|
|
@@ -224,24 +224,25 @@ function getDatasourceDeployErrorPatterns() {
|
|
|
224
224
|
'timeout',
|
|
225
225
|
'unreachable',
|
|
226
226
|
'failed to publish',
|
|
227
|
+
'upload failed',
|
|
227
228
|
'deployment failed'
|
|
228
229
|
];
|
|
229
230
|
}
|
|
230
231
|
|
|
231
232
|
/**
|
|
232
|
-
* Test datasource
|
|
233
|
+
* Test datasource upload command with invalid dataplane
|
|
233
234
|
* @async
|
|
234
|
-
* @function
|
|
235
|
+
* @function testDatasourceUpload
|
|
235
236
|
* @returns {Promise<Object>} Test result
|
|
236
237
|
*/
|
|
237
|
-
async function
|
|
238
|
-
logInfo('\n🚀 Testing: datasource
|
|
238
|
+
async function testDatasourceUpload() {
|
|
239
|
+
logInfo('\n🚀 Testing: datasource upload command');
|
|
239
240
|
|
|
240
241
|
const datasourcePath = path.join(process.cwd(), 'integration', 'test-datasource.json');
|
|
241
242
|
|
|
242
243
|
try {
|
|
243
244
|
await createTestDatasource(datasourcePath);
|
|
244
|
-
const args =
|
|
245
|
+
const args = buildDatasourceUploadArgs(datasourcePath);
|
|
245
246
|
const result = await runCommand('node', args);
|
|
246
247
|
const output = `${result.stdout}\n${result.stderr}`;
|
|
247
248
|
|
|
@@ -252,18 +253,18 @@ async function testDatasourceDeploy() {
|
|
|
252
253
|
// Ignore cleanup errors
|
|
253
254
|
}
|
|
254
255
|
|
|
255
|
-
const expectedPatterns =
|
|
256
|
+
const expectedPatterns = getDatasourceUploadErrorPatterns();
|
|
256
257
|
const isValid = !result.success && validateError(output, expectedPatterns);
|
|
257
258
|
|
|
258
259
|
return {
|
|
259
|
-
name: 'datasource
|
|
260
|
+
name: 'datasource upload',
|
|
260
261
|
success: isValid,
|
|
261
262
|
output,
|
|
262
263
|
expectedPatterns
|
|
263
264
|
};
|
|
264
265
|
} catch (error) {
|
|
265
266
|
return {
|
|
266
|
-
name: 'datasource
|
|
267
|
+
name: 'datasource upload',
|
|
267
268
|
success: false,
|
|
268
269
|
output: error.message,
|
|
269
270
|
error: error.message
|
|
@@ -385,7 +386,7 @@ module.exports = {
|
|
|
385
386
|
testWizard,
|
|
386
387
|
testDownload,
|
|
387
388
|
testDelete,
|
|
388
|
-
|
|
389
|
+
testDatasourceUpload,
|
|
389
390
|
testIntegration,
|
|
390
391
|
testDataplaneDiscovery
|
|
391
392
|
};
|
|
@@ -24,7 +24,7 @@ const {
|
|
|
24
24
|
testWizard,
|
|
25
25
|
testDownload,
|
|
26
26
|
testDelete,
|
|
27
|
-
|
|
27
|
+
testDatasourceUpload,
|
|
28
28
|
testIntegration,
|
|
29
29
|
testDataplaneDiscovery
|
|
30
30
|
} = require('./test-dataplane-down-tests');
|
|
@@ -139,7 +139,7 @@ async function runTests() {
|
|
|
139
139
|
testWizard,
|
|
140
140
|
testDownload,
|
|
141
141
|
testDelete,
|
|
142
|
-
|
|
142
|
+
testDatasourceUpload,
|
|
143
143
|
testIntegration,
|
|
144
144
|
testDataplaneDiscovery
|
|
145
145
|
];
|
package/jest.config.manual.js
CHANGED
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
const baseProject = require('./jest.config').projects[0];
|
|
11
11
|
|
|
12
12
|
module.exports = {
|
|
13
|
+
// Top-level so Jest actually applies it (project-level testTimeout is ignored in some Jest versions)
|
|
14
|
+
testTimeout: 60000,
|
|
13
15
|
projects: [
|
|
14
16
|
{
|
|
15
17
|
...baseProject,
|
|
@@ -22,7 +24,6 @@ module.exports = {
|
|
|
22
24
|
'\\\\node_modules\\\\'
|
|
23
25
|
],
|
|
24
26
|
setupFilesAfterEnv: ['<rootDir>/tests/manual/setup.js'],
|
|
25
|
-
testTimeout: 60000,
|
|
26
27
|
maxWorkers: 1
|
|
27
28
|
}
|
|
28
29
|
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview External test API - dataplane external endpoints (test, test-e2e)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { ApiClient } = require('./index');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Run E2E test for one datasource (config, credential, sync, data, CIP) via dataplane external API.
|
|
11
|
+
* Requires Bearer token or API key; client credentials are not accepted.
|
|
12
|
+
* When asyncRun is true, POST returns 202 with { testRunId, status, startedAt }; caller must poll
|
|
13
|
+
* getE2ETestRun until status is 'completed' or 'failed'. When asyncRun is false, POST returns 200
|
|
14
|
+
* with sync body { steps, success, error?, ... }.
|
|
15
|
+
*
|
|
16
|
+
* @requiresPermission {Dataplane} external-data-source:read
|
|
17
|
+
* @async
|
|
18
|
+
* @function testDatasourceE2E
|
|
19
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
20
|
+
* @param {string} sourceIdOrKey - Source ID or datasource key (e.g. hubspot-test-v4-contacts)
|
|
21
|
+
* @param {Object} authConfig - Authentication configuration (must have token or apiKey; client creds rejected)
|
|
22
|
+
* @param {Object} [body] - Optional request body (e.g. includeDebug, testCrud, recordId, cleanup, primaryKeyValue)
|
|
23
|
+
* @param {Object} [options] - Optional options
|
|
24
|
+
* @param {boolean} [options.asyncRun] - If true, request async run (query param asyncRun=true); response may be 202 with testRunId
|
|
25
|
+
* @returns {Promise<Object>} Response with success, data (sync: steps/success/error; async start: testRunId/status/startedAt), status
|
|
26
|
+
* @throws {Error} If auth lacks Bearer/API_KEY or if test fails
|
|
27
|
+
*/
|
|
28
|
+
async function testDatasourceE2E(dataplaneUrl, sourceIdOrKey, authConfig, body = {}, options = {}) {
|
|
29
|
+
if (!authConfig.token && !authConfig.apiKey) {
|
|
30
|
+
throw new Error(
|
|
31
|
+
'E2E tests require Bearer token or API key. Run \'aifabrix login\' or configure API key. ' +
|
|
32
|
+
'Client credentials are not supported for external test endpoints.'
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
36
|
+
const postOptions = { body };
|
|
37
|
+
if (options.asyncRun === true) {
|
|
38
|
+
postOptions.params = { asyncRun: 'true' };
|
|
39
|
+
}
|
|
40
|
+
return await client.post(`/api/v1/external/${encodeURIComponent(sourceIdOrKey)}/test-e2e`, postOptions);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Poll E2E test run status. Call after testDatasourceE2E with asyncRun true when response has testRunId.
|
|
45
|
+
* Same auth as E2E (Bearer or API key).
|
|
46
|
+
*
|
|
47
|
+
* @requiresPermission {Dataplane} external-data-source:read
|
|
48
|
+
* @async
|
|
49
|
+
* @function getE2ETestRun
|
|
50
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
51
|
+
* @param {string} sourceIdOrKey - Source ID or datasource key
|
|
52
|
+
* @param {string} testRunId - Test run ID from async start response
|
|
53
|
+
* @param {Object} authConfig - Authentication configuration (must have token or apiKey)
|
|
54
|
+
* @returns {Promise<Object>} Poll response: { status, completedActions?, steps?, success?, error?, durationSeconds?, debug? }
|
|
55
|
+
* @throws {Error} If auth lacks Bearer/API_KEY, or if run not found/expired (404)
|
|
56
|
+
*/
|
|
57
|
+
async function getE2ETestRun(dataplaneUrl, sourceIdOrKey, testRunId, authConfig) {
|
|
58
|
+
if (!authConfig.token && !authConfig.apiKey) {
|
|
59
|
+
throw new Error(
|
|
60
|
+
'E2E poll requires Bearer token or API key. Run \'aifabrix login\' or configure API key.'
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
if (!testRunId || typeof testRunId !== 'string') {
|
|
64
|
+
throw new Error('testRunId is required for E2E poll');
|
|
65
|
+
}
|
|
66
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
67
|
+
const response = await client.get(
|
|
68
|
+
`/api/v1/external/${encodeURIComponent(sourceIdOrKey)}/test-e2e/${encodeURIComponent(testRunId)}`
|
|
69
|
+
);
|
|
70
|
+
if (!response.success) {
|
|
71
|
+
if (response.status === 404) {
|
|
72
|
+
throw new Error(
|
|
73
|
+
`E2E test run not found or expired (run ID: ${testRunId}). The run may have been purged or the ID is invalid.`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
throw new Error(response.formattedError || response.error || 'E2E poll failed');
|
|
77
|
+
}
|
|
78
|
+
return response.data || response;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Run config test for one datasource via dataplane external API.
|
|
83
|
+
* Requires Bearer token or API key; client credentials are not accepted.
|
|
84
|
+
*
|
|
85
|
+
* @requiresPermission {Dataplane} external-data-source:read
|
|
86
|
+
* @async
|
|
87
|
+
* @function testDatasourceConfig
|
|
88
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
89
|
+
* @param {string} sourceIdOrKey - Source ID or datasource key
|
|
90
|
+
* @param {Object} authConfig - Authentication configuration
|
|
91
|
+
* @param {Object} [body] - Optional request body
|
|
92
|
+
* @returns {Promise<Object>} Config test response
|
|
93
|
+
* @throws {Error} If auth lacks Bearer/API_KEY or if test fails
|
|
94
|
+
*/
|
|
95
|
+
async function testDatasourceConfig(dataplaneUrl, sourceIdOrKey, authConfig, body = {}) {
|
|
96
|
+
if (!authConfig.token && !authConfig.apiKey) {
|
|
97
|
+
throw new Error(
|
|
98
|
+
'External config tests require Bearer token or API key. Run \'aifabrix login\' or configure API key.'
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
102
|
+
return await client.post(`/api/v1/external/${encodeURIComponent(sourceIdOrKey)}/test`, {
|
|
103
|
+
body
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
module.exports = {
|
|
108
|
+
testDatasourceE2E,
|
|
109
|
+
getE2ETestRun,
|
|
110
|
+
testDatasourceConfig
|
|
111
|
+
};
|
package/lib/api/index.js
CHANGED
|
@@ -13,13 +13,12 @@ const { makeApiCall, authenticatedApiCall } = require('../utils/api');
|
|
|
13
13
|
*/
|
|
14
14
|
class ApiClient {
|
|
15
15
|
/**
|
|
16
|
-
* Create an API client instance
|
|
16
|
+
* Create an API client instance.
|
|
17
|
+
* App endpoints receive token-only auth (Bearer). x-client-id/x-client-secret are not sent to app endpoints.
|
|
17
18
|
* @param {string} baseUrl - Base URL for the API (controller URL)
|
|
18
19
|
* @param {Object} [authConfig] - Authentication configuration
|
|
19
|
-
* @param {string} [authConfig.type] - Auth type ('bearer' | 'client-
|
|
20
|
+
* @param {string} [authConfig.type] - Auth type ('bearer' | 'client-token')
|
|
20
21
|
* @param {string} [authConfig.token] - Bearer token
|
|
21
|
-
* @param {string} [authConfig.clientId] - Client ID
|
|
22
|
-
* @param {string} [authConfig.clientSecret] - Client secret
|
|
23
22
|
*/
|
|
24
23
|
constructor(baseUrl, authConfig = {}) {
|
|
25
24
|
if (baseUrl === null || baseUrl === undefined || typeof baseUrl !== 'string') {
|
|
@@ -48,26 +47,23 @@ class ApiClient {
|
|
|
48
47
|
* Build request headers with authentication
|
|
49
48
|
* @private
|
|
50
49
|
* @param {Object} [additionalHeaders] - Additional headers to include
|
|
50
|
+
* @param {Object} [opts] - Options
|
|
51
|
+
* @param {boolean} [opts.skipContentType] - If true, do not set Content-Type (e.g. for FormData when boundary is set by fetch)
|
|
51
52
|
* @returns {Object} Request headers
|
|
52
53
|
*/
|
|
53
|
-
_buildHeaders(additionalHeaders = {}) {
|
|
54
|
-
const headers = {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
}
|
|
54
|
+
_buildHeaders(additionalHeaders = {}, opts = {}) {
|
|
55
|
+
const headers = { ...additionalHeaders };
|
|
56
|
+
if (!opts.skipContentType) {
|
|
57
|
+
headers['Content-Type'] = 'application/json';
|
|
58
|
+
}
|
|
58
59
|
|
|
59
|
-
//
|
|
60
|
-
if (this.authConfig.
|
|
61
|
-
if (this.authConfig.token) {
|
|
60
|
+
// User token (bearer) → Authorization: Bearer; application token (client-token) → x-client-token
|
|
61
|
+
if (this.authConfig.token) {
|
|
62
|
+
if (this.authConfig.type === 'client-token') {
|
|
63
|
+
headers['x-client-token'] = this.authConfig.token;
|
|
64
|
+
} else {
|
|
62
65
|
headers['Authorization'] = `Bearer ${this.authConfig.token}`;
|
|
63
66
|
}
|
|
64
|
-
} else if (this.authConfig.type === 'client-credentials') {
|
|
65
|
-
if (this.authConfig.clientId) {
|
|
66
|
-
headers['x-client-id'] = this.authConfig.clientId;
|
|
67
|
-
}
|
|
68
|
-
if (this.authConfig.clientSecret) {
|
|
69
|
-
headers['x-client-secret'] = this.authConfig.clientSecret;
|
|
70
|
-
}
|
|
71
67
|
}
|
|
72
68
|
|
|
73
69
|
return headers;
|
|
@@ -153,6 +149,33 @@ class ApiClient {
|
|
|
153
149
|
return await makeApiCall(url, requestOptions);
|
|
154
150
|
}
|
|
155
151
|
|
|
152
|
+
/**
|
|
153
|
+
* POST multipart/form-data (e.g. file upload). Uses same auth as other methods; does not set Content-Type so fetch sets boundary.
|
|
154
|
+
* @async
|
|
155
|
+
* @param {string} endpoint - API endpoint path
|
|
156
|
+
* @param {FormData} formData - FormData body
|
|
157
|
+
* @param {Object} [options] - Request options
|
|
158
|
+
* @param {Object} [options.headers] - Additional headers
|
|
159
|
+
* @returns {Promise<Object>} API response
|
|
160
|
+
*/
|
|
161
|
+
async postFormData(endpoint, formData, options = {}) {
|
|
162
|
+
const url = this._buildUrl(endpoint);
|
|
163
|
+
const headers = this._buildHeaders(options.headers || {}, { skipContentType: true });
|
|
164
|
+
|
|
165
|
+
const requestOptions = {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
headers,
|
|
168
|
+
body: formData
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const hasToken = this.authConfig.type === 'bearer' || this.authConfig.type === 'client-token';
|
|
172
|
+
if (hasToken && this.authConfig.token) {
|
|
173
|
+
return await authenticatedApiCall(url, requestOptions, this.authConfig);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return await makeApiCall(url, requestOptions);
|
|
177
|
+
}
|
|
178
|
+
|
|
156
179
|
/**
|
|
157
180
|
* Make a PATCH request
|
|
158
181
|
* @async
|
package/lib/api/pipeline.api.js
CHANGED
|
@@ -14,7 +14,7 @@ const { ApiClient } = require('./index');
|
|
|
14
14
|
* @function validatePipeline
|
|
15
15
|
* @param {string} controllerUrl - Controller base URL
|
|
16
16
|
* @param {string} envKey - Environment key
|
|
17
|
-
* @param {Object} authConfig - Authentication configuration (
|
|
17
|
+
* @param {Object} authConfig - Authentication configuration (Bearer token only for app endpoints)
|
|
18
18
|
* @param {Object} validationData - Validation data
|
|
19
19
|
* @param {string} validationData.clientId - Client ID for application authentication
|
|
20
20
|
* @param {string} validationData.repositoryUrl - Repository URL for validation
|
|
@@ -37,7 +37,7 @@ async function validatePipeline(controllerUrl, envKey, authConfig, validationDat
|
|
|
37
37
|
* @function deployPipeline
|
|
38
38
|
* @param {string} controllerUrl - Controller base URL
|
|
39
39
|
* @param {string} envKey - Environment key
|
|
40
|
-
* @param {Object} authConfig - Authentication configuration (
|
|
40
|
+
* @param {Object} authConfig - Authentication configuration (Bearer token only for app endpoints)
|
|
41
41
|
* @param {Object} deployData - Deployment data
|
|
42
42
|
* @param {string} deployData.validateToken - One-time deployment token from /validate endpoint
|
|
43
43
|
* @param {string} deployData.imageTag - Container image tag to deploy
|
|
@@ -60,7 +60,7 @@ async function deployPipeline(controllerUrl, envKey, authConfig, deployData) {
|
|
|
60
60
|
* @param {string} controllerUrl - Controller base URL
|
|
61
61
|
* @param {string} envKey - Environment key
|
|
62
62
|
* @param {string} deploymentId - Deployment ID
|
|
63
|
-
* @param {Object} authConfig - Authentication configuration (
|
|
63
|
+
* @param {Object} authConfig - Authentication configuration (Bearer token only for app endpoints)
|
|
64
64
|
* @returns {Promise<Object>} Minimal deployment status response
|
|
65
65
|
* @throws {Error} If request fails
|
|
66
66
|
*/
|
|
@@ -85,31 +85,9 @@ async function getPipelineHealth(controllerUrl, envKey) {
|
|
|
85
85
|
return await client.get(`/api/v1/pipeline/${envKey}/health`);
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
/**
|
|
89
|
-
* Publish one external system via dataplane pipeline endpoint
|
|
90
|
-
* POST /api/v1/pipeline/publish (Dataplane OpenAPI operationId: publishExternalSystemViaPipeline)
|
|
91
|
-
* Request body: external system JSON (external-system.schema.json). Optional field in body:
|
|
92
|
-
* generateMcpContract (boolean, default true). Optional: generateOpenApiContract (boolean).
|
|
93
|
-
* Do not use query parameters for MCP; use the field in the body only.
|
|
94
|
-
* @requiresPermission {Dataplane} external-system:publish. Auth: OAuth2 (Bearer) or API_KEY only; client id/secret are not accepted.
|
|
95
|
-
* @async
|
|
96
|
-
* @function publishSystemViaPipeline
|
|
97
|
-
* @param {string} dataplaneUrl - Dataplane base URL
|
|
98
|
-
* @param {Object} authConfig - Authentication configuration (must include token for Bearer; client id/secret rejected)
|
|
99
|
-
* @param {Object} systemConfig - External system configuration (conforms to external-system.schema.json)
|
|
100
|
-
* @returns {Promise<Object>} Published external system response
|
|
101
|
-
* @throws {Error} If publish fails
|
|
102
|
-
*/
|
|
103
|
-
async function publishSystemViaPipeline(dataplaneUrl, authConfig, systemConfig) {
|
|
104
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
105
|
-
return await client.post('/api/v1/pipeline/publish', {
|
|
106
|
-
body: systemConfig
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
88
|
/**
|
|
111
89
|
* Publish datasource via dataplane pipeline endpoint
|
|
112
|
-
* POST /api/v1/pipeline/{systemKey}/
|
|
90
|
+
* POST /api/v1/pipeline/{systemKey}/upload (Dataplane: renamed from /publish)
|
|
113
91
|
* No generateMcpContract for this endpoint; dataplane always uses default (MCP generated).
|
|
114
92
|
* @requiresPermission {Dataplane} external-system:publish. Auth: OAuth2 (Bearer) or API_KEY only; client id/secret are not accepted.
|
|
115
93
|
* @async
|
|
@@ -123,15 +101,62 @@ async function publishSystemViaPipeline(dataplaneUrl, authConfig, systemConfig)
|
|
|
123
101
|
*/
|
|
124
102
|
async function publishDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceConfig) {
|
|
125
103
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
126
|
-
return await client.post(`/api/v1/pipeline/${systemKey}/
|
|
104
|
+
return await client.post(`/api/v1/pipeline/${systemKey}/upload`, {
|
|
127
105
|
body: datasourceConfig
|
|
128
106
|
});
|
|
129
107
|
}
|
|
130
108
|
|
|
109
|
+
/**
|
|
110
|
+
* Validate pipeline config against Dataplane (dry-run; no publish).
|
|
111
|
+
* POST /api/v1/pipeline/validate
|
|
112
|
+
* @requiresPermission {Dataplane} external-system:read or external-system:publish
|
|
113
|
+
* @async
|
|
114
|
+
* @function validatePipelineConfig
|
|
115
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
116
|
+
* @param {Object} authConfig - Authentication configuration
|
|
117
|
+
* @param {Object} params - Request params
|
|
118
|
+
* @param {Object} params.config - Full config: { version, application, dataSources }
|
|
119
|
+
* @returns {Promise<Object>} { isValid, errors, warnings }
|
|
120
|
+
* @throws {Error} If request fails
|
|
121
|
+
*/
|
|
122
|
+
async function validatePipelineConfig(dataplaneUrl, authConfig, { config }) {
|
|
123
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
124
|
+
return await client.post('/api/v1/pipeline/validate', {
|
|
125
|
+
body: { config }
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Test external system (all datasources) via dataplane pipeline endpoint
|
|
131
|
+
* POST /api/v1/pipeline/{systemKey}/test
|
|
132
|
+
* @requiresPermission {Dataplane} external-system:publish. Auth: Bearer or x-client-token only.
|
|
133
|
+
* @async
|
|
134
|
+
* @function testSystemViaPipeline
|
|
135
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
136
|
+
* @param {string} systemKey - System key
|
|
137
|
+
* @param {Object} authConfig - Authentication configuration (Bearer token)
|
|
138
|
+
* @param {Object} testData - Test data
|
|
139
|
+
* @param {Object} [testData.payloadTemplate] - Optional payload template
|
|
140
|
+
* @param {boolean} [testData.includeDebug] - Include debug output in response
|
|
141
|
+
* @param {Object} [options] - Request options
|
|
142
|
+
* @param {number} [options.timeout] - Request timeout in milliseconds
|
|
143
|
+
* @returns {Promise<Object>} Test response
|
|
144
|
+
* @throws {Error} If test fails
|
|
145
|
+
*/
|
|
146
|
+
async function testSystemViaPipeline(dataplaneUrl, systemKey, authConfig, testData = {}, options = {}) {
|
|
147
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
148
|
+
const requestOptions = { body: testData };
|
|
149
|
+
if (options.timeout) {
|
|
150
|
+
requestOptions.timeout = options.timeout;
|
|
151
|
+
}
|
|
152
|
+
return await client.post(`/api/v1/pipeline/${systemKey}/test`, requestOptions);
|
|
153
|
+
}
|
|
154
|
+
|
|
131
155
|
/**
|
|
132
156
|
* Test datasource via dataplane pipeline endpoint
|
|
133
157
|
* POST /api/v1/pipeline/{systemKey}/{datasourceKey}/test (Dataplane OpenAPI operationId: testExternalDataSourceViaPipeline)
|
|
134
|
-
*
|
|
158
|
+
* Supports client credentials for CI/CD.
|
|
159
|
+
* @requiresPermission {Dataplane} external-system:publish or external-data-source:read. Auth: Bearer or x-client-token only.
|
|
135
160
|
* @async
|
|
136
161
|
* @function testDatasourceViaPipeline
|
|
137
162
|
* @param {Object} params - Function parameters
|
|
@@ -159,116 +184,37 @@ async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKe
|
|
|
159
184
|
}
|
|
160
185
|
|
|
161
186
|
/**
|
|
162
|
-
*
|
|
163
|
-
* POST /api/v1/pipeline/
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
* @function deployExternalSystemViaPipeline
|
|
167
|
-
* @param {string} dataplaneUrl - Dataplane base URL
|
|
168
|
-
* @param {Object} authConfig - Authentication configuration
|
|
169
|
-
* @param {Object} systemConfig - External system configuration to deploy
|
|
170
|
-
* @returns {Promise<Object>} Deployment response
|
|
171
|
-
* @throws {Error} If deployment fails
|
|
172
|
-
*/
|
|
173
|
-
async function deployExternalSystemViaPipeline(dataplaneUrl, authConfig, systemConfig) {
|
|
174
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
175
|
-
return await client.post('/api/v1/pipeline/deploy', {
|
|
176
|
-
body: systemConfig
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Deploy datasource via dataplane pipeline endpoint
|
|
182
|
-
* POST /api/v1/pipeline/{systemKey}/deploy
|
|
183
|
-
* @requiresPermission {Dataplane} external-system:publish. Auth: OAuth2 (Bearer) or API_KEY only; client id/secret are not accepted.
|
|
184
|
-
* @async
|
|
185
|
-
* @function deployDatasourceViaPipeline
|
|
186
|
-
* @param {string} dataplaneUrl - Dataplane base URL
|
|
187
|
-
* @param {string} systemKey - System key
|
|
188
|
-
* @param {Object} authConfig - Authentication configuration
|
|
189
|
-
* @param {Object} datasourceConfig - Datasource configuration to deploy
|
|
190
|
-
* @returns {Promise<Object>} Deployment response
|
|
191
|
-
* @throws {Error} If deployment fails
|
|
192
|
-
*/
|
|
193
|
-
async function deployDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig, datasourceConfig) {
|
|
194
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
195
|
-
return await client.post(`/api/v1/pipeline/${systemKey}/deploy`, {
|
|
196
|
-
body: datasourceConfig
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* Upload application configuration via dataplane pipeline endpoint
|
|
202
|
-
* POST /api/v1/pipeline/upload (Dataplane OpenAPI operationId: uploadApplication)
|
|
203
|
-
* Body: { version, application, dataSources }. Include application.generateMcpContract
|
|
204
|
-
* and/or application.generateOpenApiContract to control contract generation when
|
|
205
|
-
* publishing this upload (publish reads from stored config; no query param on publish).
|
|
187
|
+
* Upload application configuration via dataplane pipeline endpoint (single call: upload → validate → publish → controller register).
|
|
188
|
+
* POST /api/v1/pipeline/upload
|
|
189
|
+
* Body: { version, application, dataSources, status }. status "draft" (default) or "published".
|
|
190
|
+
* Include application.generateMcpContract and/or application.generateOpenApiContract to control contract generation.
|
|
206
191
|
* @requiresPermission {Dataplane} external-system:publish. Auth: OAuth2 (Bearer) or API_KEY only; client id/secret are not accepted.
|
|
207
192
|
* @async
|
|
208
193
|
* @function uploadApplicationViaPipeline
|
|
209
194
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
210
195
|
* @param {Object} authConfig - Authentication configuration (must include token for Bearer; client id/secret rejected)
|
|
211
|
-
* @param {Object}
|
|
212
|
-
* @
|
|
196
|
+
* @param {Object} payload - { version, application, dataSources }; optional status (default "draft")
|
|
197
|
+
* @param {string} [payload.status="draft"] - "draft" or "published"; Builder uses "draft"
|
|
198
|
+
* @returns {Promise<Object>} Publication result (system, datasources, warnings); no uploadId
|
|
213
199
|
* @throws {Error} If upload fails
|
|
214
200
|
*/
|
|
215
|
-
async function uploadApplicationViaPipeline(dataplaneUrl, authConfig,
|
|
201
|
+
async function uploadApplicationViaPipeline(dataplaneUrl, authConfig, payload) {
|
|
202
|
+
const body = { ...payload, status: payload.status ?? 'draft' };
|
|
216
203
|
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
217
204
|
return await client.post('/api/v1/pipeline/upload', {
|
|
218
|
-
body
|
|
205
|
+
body
|
|
219
206
|
});
|
|
220
207
|
}
|
|
221
208
|
|
|
222
|
-
/**
|
|
223
|
-
* Validate upload via dataplane pipeline endpoint
|
|
224
|
-
* POST /api/v1/pipeline/upload/{uploadId}/validate (Dataplane OpenAPI operationId: validateApplication)
|
|
225
|
-
* @requiresPermission {Dataplane} external-system:publish. Auth: OAuth2 (Bearer) or API_KEY only; client id/secret are not accepted.
|
|
226
|
-
* @async
|
|
227
|
-
* @function validateUploadViaPipeline
|
|
228
|
-
* @param {string} dataplaneUrl - Dataplane base URL
|
|
229
|
-
* @param {string} uploadId - Upload ID
|
|
230
|
-
* @param {Object} authConfig - Authentication configuration
|
|
231
|
-
* @returns {Promise<Object>} Validation response with changes and summary
|
|
232
|
-
* @throws {Error} If validation fails
|
|
233
|
-
*/
|
|
234
|
-
async function validateUploadViaPipeline(dataplaneUrl, uploadId, authConfig) {
|
|
235
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
236
|
-
return await client.post(`/api/v1/pipeline/upload/${uploadId}/validate`);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
/**
|
|
240
|
-
* Publish upload via dataplane pipeline endpoint
|
|
241
|
-
* POST /api/v1/pipeline/upload/{uploadId}/publish (Dataplane OpenAPI operationId: publishApplication)
|
|
242
|
-
* No body or query parameters. MCP/OpenAPI generation is taken from the application config
|
|
243
|
-
* that was uploaded (application.generateMcpContract, application.generateOpenApiContract).
|
|
244
|
-
* To control MCP/OpenAPI, include those fields in the application object when calling
|
|
245
|
-
* uploadApplicationViaPipeline.
|
|
246
|
-
* @requiresPermission {Dataplane} external-system:publish. Auth: OAuth2 (Bearer) or API_KEY only; client id/secret are not accepted.
|
|
247
|
-
* @async
|
|
248
|
-
* @function publishUploadViaPipeline
|
|
249
|
-
* @param {string} dataplaneUrl - Dataplane base URL
|
|
250
|
-
* @param {string} uploadId - Upload ID
|
|
251
|
-
* @param {Object} authConfig - Authentication configuration (must include token for Bearer; client id/secret rejected)
|
|
252
|
-
* @returns {Promise<Object>} Publish response
|
|
253
|
-
* @throws {Error} If publish fails
|
|
254
|
-
*/
|
|
255
|
-
async function publishUploadViaPipeline(dataplaneUrl, uploadId, authConfig) {
|
|
256
|
-
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
257
|
-
return await client.post(`/api/v1/pipeline/upload/${uploadId}/publish`);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
209
|
module.exports = {
|
|
261
210
|
validatePipeline,
|
|
262
211
|
deployPipeline,
|
|
263
212
|
getPipelineDeployment,
|
|
264
213
|
getPipelineHealth,
|
|
265
|
-
publishSystemViaPipeline,
|
|
266
214
|
publishDatasourceViaPipeline,
|
|
215
|
+
validatePipelineConfig,
|
|
216
|
+
testSystemViaPipeline,
|
|
267
217
|
testDatasourceViaPipeline,
|
|
268
|
-
|
|
269
|
-
deployDatasourceViaPipeline,
|
|
270
|
-
uploadApplicationViaPipeline,
|
|
271
|
-
validateUploadViaPipeline,
|
|
272
|
-
publishUploadViaPipeline
|
|
218
|
+
uploadApplicationViaPipeline
|
|
273
219
|
};
|
|
274
220
|
|