@aifabrix/builder 2.37.9 ā 2.39.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 +3 -0
- package/README.md +19 -0
- package/integration/hubspot/hubspot-deploy.json +1 -5
- package/integration/hubspot/hubspot-system.json +0 -3
- package/lib/api/applications.api.js +29 -1
- package/lib/api/auth.api.js +14 -0
- package/lib/api/credentials.api.js +34 -0
- package/lib/api/datasources-core.api.js +16 -1
- package/lib/api/datasources-extended.api.js +18 -1
- package/lib/api/deployments.api.js +32 -0
- package/lib/api/environments.api.js +11 -0
- package/lib/api/external-systems.api.js +16 -1
- package/lib/api/pipeline.api.js +12 -4
- package/lib/api/service-users.api.js +41 -0
- package/lib/api/types/applications.types.js +1 -1
- package/lib/api/types/deployments.types.js +1 -1
- package/lib/api/types/pipeline.types.js +1 -1
- package/lib/api/types/service-users.types.js +24 -0
- package/lib/api/wizard.api.js +40 -1
- package/lib/app/deploy.js +86 -21
- package/lib/app/rotate-secret.js +3 -1
- package/lib/app/run-helpers.js +35 -2
- package/lib/app/show-display.js +30 -11
- package/lib/app/show.js +34 -8
- package/lib/cli/index.js +4 -0
- package/lib/cli/setup-app.js +40 -0
- package/lib/cli/setup-credential-deployment.js +72 -0
- package/lib/cli/setup-infra.js +3 -3
- package/lib/cli/setup-service-user.js +52 -0
- package/lib/cli/setup-utility.js +1 -25
- package/lib/commands/app-down.js +80 -0
- package/lib/commands/app-logs.js +146 -0
- package/lib/commands/app.js +24 -1
- package/lib/commands/credential-list.js +104 -0
- package/lib/commands/deployment-list.js +184 -0
- package/lib/commands/service-user.js +193 -0
- package/lib/commands/up-common.js +74 -5
- package/lib/commands/up-dataplane.js +13 -7
- package/lib/commands/up-miso.js +17 -24
- package/lib/core/templates.js +2 -2
- package/lib/external-system/deploy.js +79 -15
- package/lib/generator/builders.js +8 -27
- package/lib/generator/external-controller-manifest.js +5 -4
- package/lib/generator/index.js +16 -14
- package/lib/generator/split.js +1 -0
- package/lib/generator/wizard.js +4 -1
- package/lib/schema/application-schema.json +6 -14
- package/lib/schema/deployment-rules.yaml +121 -0
- package/lib/schema/external-system.schema.json +0 -16
- package/lib/utils/app-register-config.js +10 -12
- package/lib/utils/app-run-containers.js +2 -1
- package/lib/utils/compose-generator.js +2 -1
- package/lib/utils/deployment-errors.js +10 -0
- package/lib/utils/environment-checker.js +25 -6
- package/lib/utils/help-builder.js +0 -1
- package/lib/utils/image-version.js +209 -0
- package/lib/utils/schema-loader.js +1 -1
- package/lib/utils/variable-transformer.js +7 -33
- package/lib/validation/external-manifest-validator.js +1 -1
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +1 -3
- package/templates/applications/dataplane/Dockerfile +2 -2
- package/templates/applications/dataplane/README.md +20 -6
- package/templates/applications/dataplane/env.template +31 -2
- package/templates/applications/dataplane/rbac.yaml +1 -1
- package/templates/applications/dataplane/variables.yaml +7 -4
- package/templates/applications/keycloak/Dockerfile +3 -3
- package/templates/applications/keycloak/README.md +14 -4
- package/templates/applications/keycloak/env.template +17 -2
- package/templates/applications/keycloak/variables.yaml +2 -1
- package/templates/applications/miso-controller/README.md +1 -3
- package/templates/applications/miso-controller/env.template +85 -25
- package/templates/applications/miso-controller/rbac.yaml +15 -0
- package/templates/applications/miso-controller/variables.yaml +24 -23
|
@@ -9,6 +9,7 @@ const { ApiClient } = require('./index');
|
|
|
9
9
|
/**
|
|
10
10
|
* List external systems
|
|
11
11
|
* GET /api/v1/external/systems
|
|
12
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
12
13
|
* @async
|
|
13
14
|
* @function listExternalSystems
|
|
14
15
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -32,6 +33,7 @@ async function listExternalSystems(dataplaneUrl, authConfig, options = {}) {
|
|
|
32
33
|
/**
|
|
33
34
|
* Create external system
|
|
34
35
|
* POST /api/v1/external/systems
|
|
36
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
35
37
|
* @async
|
|
36
38
|
* @function createExternalSystem
|
|
37
39
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -50,12 +52,16 @@ async function createExternalSystem(dataplaneUrl, authConfig, systemData) {
|
|
|
50
52
|
/**
|
|
51
53
|
* Get external system details
|
|
52
54
|
* GET /api/v1/external/systems/{systemIdOrKey}
|
|
55
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
53
56
|
* @async
|
|
54
57
|
* @function getExternalSystem
|
|
55
58
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
56
59
|
* @param {string} systemIdOrKey - System ID or key
|
|
57
60
|
* @param {Object} authConfig - Authentication configuration
|
|
58
|
-
* @returns {Promise<Object>} External system details response
|
|
61
|
+
* @returns {Promise<Object>} External system details response. May include optional fields:
|
|
62
|
+
* - {string} [mcpServerUrl] - Full URL of external system MCP server when configured
|
|
63
|
+
* - {string} [apiDocumentUrl] - Full URL of API document (OpenAPI spec) when configured
|
|
64
|
+
* - {string} [openApiDocsPageUrl] - Full URL of dataplane API docs page when showOpenApiDocs is true
|
|
59
65
|
* @throws {Error} If request fails
|
|
60
66
|
*/
|
|
61
67
|
async function getExternalSystem(dataplaneUrl, systemIdOrKey, authConfig) {
|
|
@@ -66,6 +72,7 @@ async function getExternalSystem(dataplaneUrl, systemIdOrKey, authConfig) {
|
|
|
66
72
|
/**
|
|
67
73
|
* Update external system
|
|
68
74
|
* PUT /api/v1/external/systems/{systemIdOrKey}
|
|
75
|
+
* @requiresPermission {Dataplane} external-system:update
|
|
69
76
|
* @async
|
|
70
77
|
* @function updateExternalSystem
|
|
71
78
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -85,6 +92,7 @@ async function updateExternalSystem(dataplaneUrl, systemIdOrKey, authConfig, upd
|
|
|
85
92
|
/**
|
|
86
93
|
* Delete external system (soft delete)
|
|
87
94
|
* DELETE /api/v1/external/systems/{systemIdOrKey}
|
|
95
|
+
* @requiresPermission {Dataplane} external-system:delete
|
|
88
96
|
* @async
|
|
89
97
|
* @function deleteExternalSystem
|
|
90
98
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -101,6 +109,7 @@ async function deleteExternalSystem(dataplaneUrl, systemIdOrKey, authConfig) {
|
|
|
101
109
|
/**
|
|
102
110
|
* Get full config with application schema and dataSources
|
|
103
111
|
* GET /api/v1/external/systems/{systemIdOrKey}/config
|
|
112
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
104
113
|
* @async
|
|
105
114
|
* @function getExternalSystemConfig
|
|
106
115
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -117,6 +126,7 @@ async function getExternalSystemConfig(dataplaneUrl, systemIdOrKey, authConfig)
|
|
|
117
126
|
/**
|
|
118
127
|
* Create external system from integration template
|
|
119
128
|
* POST /api/v1/external/systems/from-template
|
|
129
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
120
130
|
* @async
|
|
121
131
|
* @function createFromTemplate
|
|
122
132
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -138,6 +148,7 @@ async function createFromTemplate(dataplaneUrl, authConfig, templateData) {
|
|
|
138
148
|
/**
|
|
139
149
|
* List OpenAPI files for system
|
|
140
150
|
* GET /api/v1/external/systems/{systemIdOrKey}/openapi-files
|
|
151
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
141
152
|
* @async
|
|
142
153
|
* @function listOpenAPIFiles
|
|
143
154
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -157,6 +168,7 @@ async function listOpenAPIFiles(dataplaneUrl, systemIdOrKey, authConfig, options
|
|
|
157
168
|
/**
|
|
158
169
|
* List OpenAPI endpoints for system
|
|
159
170
|
* GET /api/v1/external/systems/{systemIdOrKey}/openapi-endpoints
|
|
171
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
160
172
|
* @async
|
|
161
173
|
* @function listOpenAPIEndpoints
|
|
162
174
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -176,6 +188,7 @@ async function listOpenAPIEndpoints(dataplaneUrl, systemIdOrKey, authConfig, opt
|
|
|
176
188
|
/**
|
|
177
189
|
* Publish external system
|
|
178
190
|
* POST /api/v1/external/systems/{systemIdOrKey}/publish
|
|
191
|
+
* @requiresPermission {Dataplane} external-system:update (or publish scope per dataplane spec)
|
|
179
192
|
* @async
|
|
180
193
|
* @function publishExternalSystem
|
|
181
194
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -196,6 +209,7 @@ async function publishExternalSystem(dataplaneUrl, systemIdOrKey, authConfig, pu
|
|
|
196
209
|
/**
|
|
197
210
|
* Rollback external system to version
|
|
198
211
|
* POST /api/v1/external/systems/{systemIdOrKey}/rollback
|
|
212
|
+
* @requiresPermission {Dataplane} external-system:update
|
|
199
213
|
* @async
|
|
200
214
|
* @function rollbackExternalSystem
|
|
201
215
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -216,6 +230,7 @@ async function rollbackExternalSystem(dataplaneUrl, systemIdOrKey, authConfig, r
|
|
|
216
230
|
/**
|
|
217
231
|
* Save external system as integration template
|
|
218
232
|
* POST /api/v1/external/systems/{systemIdOrKey}/save-template
|
|
233
|
+
* @requiresPermission {Dataplane} external-system:update
|
|
219
234
|
* @async
|
|
220
235
|
* @function saveAsTemplate
|
|
221
236
|
* @param {string} dataplaneUrl - Dataplane base URL
|
package/lib/api/pipeline.api.js
CHANGED
|
@@ -9,6 +9,7 @@ const { ApiClient } = require('./index');
|
|
|
9
9
|
/**
|
|
10
10
|
* Validate deployment configuration
|
|
11
11
|
* POST /api/v1/pipeline/{envKey}/validate
|
|
12
|
+
* @requiresPermission {Controller} applications:deploy
|
|
12
13
|
* @async
|
|
13
14
|
* @function validatePipeline
|
|
14
15
|
* @param {string} controllerUrl - Controller base URL
|
|
@@ -31,6 +32,7 @@ async function validatePipeline(controllerUrl, envKey, authConfig, validationDat
|
|
|
31
32
|
/**
|
|
32
33
|
* Deploy application using validateToken
|
|
33
34
|
* POST /api/v1/pipeline/{envKey}/deploy
|
|
35
|
+
* @requiresPermission {Controller} applications:deploy
|
|
34
36
|
* @async
|
|
35
37
|
* @function deployPipeline
|
|
36
38
|
* @param {string} controllerUrl - Controller base URL
|
|
@@ -52,6 +54,7 @@ async function deployPipeline(controllerUrl, envKey, authConfig, deployData) {
|
|
|
52
54
|
/**
|
|
53
55
|
* Get deployment status for CI/CD
|
|
54
56
|
* GET /api/v1/pipeline/{envKey}/deployments/{deploymentId}
|
|
57
|
+
* @requiresPermission {Controller} applications:deploy
|
|
55
58
|
* @async
|
|
56
59
|
* @function getPipelineDeployment
|
|
57
60
|
* @param {string} controllerUrl - Controller base URL
|
|
@@ -69,6 +72,7 @@ async function getPipelineDeployment(controllerUrl, envKey, deploymentId, authCo
|
|
|
69
72
|
/**
|
|
70
73
|
* Pipeline health check
|
|
71
74
|
* GET /api/v1/pipeline/{envKey}/health
|
|
75
|
+
* @requiresPermission {Controller} Public (no auth required)
|
|
72
76
|
* @async
|
|
73
77
|
* @function getPipelineHealth
|
|
74
78
|
* @param {string} controllerUrl - Controller base URL
|
|
@@ -87,7 +91,7 @@ async function getPipelineHealth(controllerUrl, envKey) {
|
|
|
87
91
|
* Request body: external system JSON (external-system.schema.json). Optional field in body:
|
|
88
92
|
* generateMcpContract (boolean, default true). Optional: generateOpenApiContract (boolean).
|
|
89
93
|
* Do not use query parameters for MCP; use the field in the body only.
|
|
90
|
-
*
|
|
94
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
91
95
|
* @async
|
|
92
96
|
* @function publishSystemViaPipeline
|
|
93
97
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -107,7 +111,7 @@ async function publishSystemViaPipeline(dataplaneUrl, authConfig, systemConfig)
|
|
|
107
111
|
* Publish datasource via dataplane pipeline endpoint
|
|
108
112
|
* POST /api/v1/pipeline/{systemKey}/publish
|
|
109
113
|
* No generateMcpContract for this endpoint; dataplane always uses default (MCP generated).
|
|
110
|
-
*
|
|
114
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
111
115
|
* @async
|
|
112
116
|
* @function publishDatasourceViaPipeline
|
|
113
117
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -127,6 +131,7 @@ async function publishDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig,
|
|
|
127
131
|
/**
|
|
128
132
|
* Test datasource via dataplane pipeline endpoint
|
|
129
133
|
* POST /api/v1/pipeline/{systemKey}/{datasourceKey}/test
|
|
134
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
130
135
|
* @async
|
|
131
136
|
* @function testDatasourceViaPipeline
|
|
132
137
|
* @param {Object} params - Function parameters
|
|
@@ -156,6 +161,7 @@ async function testDatasourceViaPipeline({ dataplaneUrl, systemKey, datasourceKe
|
|
|
156
161
|
/**
|
|
157
162
|
* Deploy external system via dataplane pipeline endpoint
|
|
158
163
|
* POST /api/v1/pipeline/deploy
|
|
164
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
159
165
|
* @async
|
|
160
166
|
* @function deployExternalSystemViaPipeline
|
|
161
167
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -174,6 +180,7 @@ async function deployExternalSystemViaPipeline(dataplaneUrl, authConfig, systemC
|
|
|
174
180
|
/**
|
|
175
181
|
* Deploy datasource via dataplane pipeline endpoint
|
|
176
182
|
* POST /api/v1/pipeline/{systemKey}/deploy
|
|
183
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
177
184
|
* @async
|
|
178
185
|
* @function deployDatasourceViaPipeline
|
|
179
186
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -196,7 +203,7 @@ async function deployDatasourceViaPipeline(dataplaneUrl, systemKey, authConfig,
|
|
|
196
203
|
* Body: { version, application, dataSources }. Include application.generateMcpContract
|
|
197
204
|
* and/or application.generateOpenApiContract to control contract generation when
|
|
198
205
|
* publishing this upload (publish reads from stored config; no query param on publish).
|
|
199
|
-
*
|
|
206
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
200
207
|
* @async
|
|
201
208
|
* @function uploadApplicationViaPipeline
|
|
202
209
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -215,6 +222,7 @@ async function uploadApplicationViaPipeline(dataplaneUrl, authConfig, applicatio
|
|
|
215
222
|
/**
|
|
216
223
|
* Validate upload via dataplane pipeline endpoint
|
|
217
224
|
* POST /api/v1/pipeline/upload/{uploadId}/validate
|
|
225
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
218
226
|
* @async
|
|
219
227
|
* @function validateUploadViaPipeline
|
|
220
228
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -235,7 +243,7 @@ async function validateUploadViaPipeline(dataplaneUrl, uploadId, authConfig) {
|
|
|
235
243
|
* that was uploaded (application.generateMcpContract, application.generateOpenApiContract).
|
|
236
244
|
* To control MCP/OpenAPI, include those fields in the application object when calling
|
|
237
245
|
* uploadApplicationViaPipeline.
|
|
238
|
-
*
|
|
246
|
+
* @requiresPermission {Dataplane} Authenticated (oauth2: [])
|
|
239
247
|
* @async
|
|
240
248
|
* @function publishUploadViaPipeline
|
|
241
249
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service users API functions (create service user with one-time secret)
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const { ApiClient } = require('./index');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Create a service user (username, email, redirectUris, groupIds); return one-time secret
|
|
11
|
+
* POST /api/v1/service-users
|
|
12
|
+
* @requiresPermission {Controller} service-user:create
|
|
13
|
+
* @async
|
|
14
|
+
* @function createServiceUser
|
|
15
|
+
* @param {string} controllerUrl - Controller base URL
|
|
16
|
+
* @param {Object} authConfig - Authentication configuration (bearer or client-credentials)
|
|
17
|
+
* @param {Object} body - Request body
|
|
18
|
+
* @param {string} body.username - Username (required)
|
|
19
|
+
* @param {string} body.email - Email (required)
|
|
20
|
+
* @param {string[]} body.redirectUris - Redirect URIs for OAuth2 (required, min 1)
|
|
21
|
+
* @param {string[]} body.groupNames - Group names (required, e.g. AI-Fabrix-Developers)
|
|
22
|
+
* @param {string} [body.description] - Optional description
|
|
23
|
+
* @returns {Promise<Object>} Response with clientId and one-time clientSecret
|
|
24
|
+
* @throws {Error} If request fails (400/401/403 or network)
|
|
25
|
+
*/
|
|
26
|
+
async function createServiceUser(controllerUrl, authConfig, body) {
|
|
27
|
+
const client = new ApiClient(controllerUrl, authConfig);
|
|
28
|
+
return await client.post('/api/v1/service-users', {
|
|
29
|
+
body: {
|
|
30
|
+
username: body.username,
|
|
31
|
+
email: body.email,
|
|
32
|
+
redirectUris: body.redirectUris,
|
|
33
|
+
groupNames: body.groupNames,
|
|
34
|
+
description: body.description
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
module.exports = {
|
|
40
|
+
createServiceUser
|
|
41
|
+
};
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @property {string} displayName - Human-readable application name
|
|
31
31
|
* @property {string} description - Application description
|
|
32
32
|
* @property {string} type - Azure application type ('webapp' | 'functionapp' | 'api' | 'service' | 'external')
|
|
33
|
-
* @property {string} deploymentKey - SHA256 hash of deployment manifest
|
|
33
|
+
* @property {string} [deploymentKey] - SHA256 hash of deployment manifest (Controller adds internally)
|
|
34
34
|
* @property {string} [image] - Container image reference
|
|
35
35
|
* @property {string} [registryMode] - Registry mode ('acr' | 'external' | 'public')
|
|
36
36
|
* @property {number} [port] - Application port number
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
* @property {string} displayName - Human-readable application name
|
|
31
31
|
* @property {string} description - Application description
|
|
32
32
|
* @property {string} type - Azure application type
|
|
33
|
-
* @property {string} deploymentKey - SHA256 hash of deployment manifest
|
|
33
|
+
* @property {string} [deploymentKey] - SHA256 hash of deployment manifest (Controller adds internally)
|
|
34
34
|
* @property {*} [additionalProperties] - Additional configuration properties
|
|
35
35
|
*/
|
|
36
36
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* @property {string} displayName - Human-readable application name
|
|
12
12
|
* @property {string} description - Application description
|
|
13
13
|
* @property {string} type - Azure application type
|
|
14
|
-
* @property {string} deploymentKey - SHA256 hash of deployment manifest
|
|
14
|
+
* @property {string} [deploymentKey] - SHA256 hash of deployment manifest (Controller adds internally)
|
|
15
15
|
* @property {*} [additionalProperties] - Additional configuration properties
|
|
16
16
|
*/
|
|
17
17
|
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Service users API type definitions
|
|
3
|
+
* @author AI Fabrix Team
|
|
4
|
+
* @version 2.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Service user create request body (builder sends this to controller)
|
|
9
|
+
* @typedef {Object} ServiceUserCreateRequest
|
|
10
|
+
* @property {string} username - Username (1ā100 chars, e.g. api-client-001)
|
|
11
|
+
* @property {string} email - Email address
|
|
12
|
+
* @property {string[]} redirectUris - Allowed redirect URIs for OAuth2 (min 1)
|
|
13
|
+
* @property {string[]} groupNames - Group names to assign (e.g. AI-Fabrix-Developers)
|
|
14
|
+
* @property {string} [description] - Optional description (max 500 chars)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Service user create response (clientSecret is one-time-only; store at creation time)
|
|
19
|
+
* @typedef {Object} ServiceUserCreateResponse
|
|
20
|
+
* @property {string} clientId - Stable identifier for the service user
|
|
21
|
+
* @property {string} clientSecret - One-time-only secret; not returned by any other endpoint
|
|
22
|
+
* @property {boolean} [success] - Optional wrapper flag
|
|
23
|
+
* @property {string} [createdAt] - Optional creation timestamp (ISO 8601)
|
|
24
|
+
*/
|
package/lib/api/wizard.api.js
CHANGED
|
@@ -10,6 +10,7 @@ const { uploadFile } = require('../utils/file-upload');
|
|
|
10
10
|
/**
|
|
11
11
|
* Create wizard session
|
|
12
12
|
* POST /api/v1/wizard/sessions
|
|
13
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
13
14
|
* @async
|
|
14
15
|
* @function createWizardSession
|
|
15
16
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -33,6 +34,7 @@ async function createWizardSession(dataplaneUrl, authConfig, mode, systemIdOrKey
|
|
|
33
34
|
/**
|
|
34
35
|
* Get wizard session
|
|
35
36
|
* GET /api/v1/wizard/sessions/{sessionId}
|
|
37
|
+
* @requiresPermission {Dataplane} external-system:read or external-system:create
|
|
36
38
|
* @async
|
|
37
39
|
* @function getWizardSession
|
|
38
40
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -49,6 +51,7 @@ async function getWizardSession(dataplaneUrl, sessionId, authConfig) {
|
|
|
49
51
|
/**
|
|
50
52
|
* Update wizard session
|
|
51
53
|
* PUT /api/v1/wizard/sessions/{sessionId}
|
|
54
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
52
55
|
* @async
|
|
53
56
|
* @function updateWizardSession
|
|
54
57
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -80,6 +83,7 @@ async function updateWizardSession(dataplaneUrl, sessionId, authConfig, updateDa
|
|
|
80
83
|
/**
|
|
81
84
|
* Delete wizard session
|
|
82
85
|
* DELETE /api/v1/wizard/sessions/{sessionId}
|
|
86
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
83
87
|
* @async
|
|
84
88
|
* @function deleteWizardSession
|
|
85
89
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -96,6 +100,7 @@ async function deleteWizardSession(dataplaneUrl, sessionId, authConfig) {
|
|
|
96
100
|
/**
|
|
97
101
|
* Get wizard session progress
|
|
98
102
|
* GET /api/v1/wizard/sessions/{sessionId}/progress
|
|
103
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
99
104
|
* @async
|
|
100
105
|
* @function getWizardProgress
|
|
101
106
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -112,6 +117,7 @@ async function getWizardProgress(dataplaneUrl, sessionId, authConfig) {
|
|
|
112
117
|
/**
|
|
113
118
|
* Parse OpenAPI file or URL
|
|
114
119
|
* POST /api/v1/wizard/parse-openapi
|
|
120
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
115
121
|
* @async
|
|
116
122
|
* @function parseOpenApi
|
|
117
123
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -133,6 +139,7 @@ async function parseOpenApi(dataplaneUrl, authConfig, openApiFilePathOrUrl, isUr
|
|
|
133
139
|
/**
|
|
134
140
|
* Credential selection for wizard
|
|
135
141
|
* POST /api/v1/wizard/credential-selection
|
|
142
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
136
143
|
* @async
|
|
137
144
|
* @function credentialSelection
|
|
138
145
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -154,6 +161,7 @@ async function credentialSelection(dataplaneUrl, authConfig, selectionData) {
|
|
|
154
161
|
/**
|
|
155
162
|
* Detect API type from OpenAPI spec
|
|
156
163
|
* POST /api/v1/wizard/detect-type
|
|
164
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
157
165
|
* @async
|
|
158
166
|
* @function detectType
|
|
159
167
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -172,6 +180,7 @@ async function detectType(dataplaneUrl, authConfig, openapiSpec) {
|
|
|
172
180
|
/**
|
|
173
181
|
* Generate configuration via AI
|
|
174
182
|
* POST /api/v1/wizard/generate-config
|
|
183
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
175
184
|
* @async
|
|
176
185
|
* @function generateConfig
|
|
177
186
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -202,6 +211,7 @@ async function generateConfig(dataplaneUrl, authConfig, config) {
|
|
|
202
211
|
/**
|
|
203
212
|
* Generate configuration via AI (streaming)
|
|
204
213
|
* POST /api/v1/wizard/generate-config-stream
|
|
214
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
205
215
|
* @async
|
|
206
216
|
* @function generateConfigStream
|
|
207
217
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -220,6 +230,7 @@ async function generateConfigStream(dataplaneUrl, authConfig, config) {
|
|
|
220
230
|
/**
|
|
221
231
|
* Validate wizard configuration
|
|
222
232
|
* POST /api/v1/wizard/validate
|
|
233
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
223
234
|
* @async
|
|
224
235
|
* @function validateWizardConfig
|
|
225
236
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -242,6 +253,7 @@ async function validateWizardConfig(dataplaneUrl, authConfig, systemConfig, data
|
|
|
242
253
|
/**
|
|
243
254
|
* Validate all completed wizard steps
|
|
244
255
|
* GET /api/v1/wizard/sessions/{sessionId}/validate
|
|
256
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
245
257
|
* @async
|
|
246
258
|
* @function validateAllSteps
|
|
247
259
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -258,6 +270,7 @@ async function validateAllSteps(dataplaneUrl, sessionId, authConfig) {
|
|
|
258
270
|
/**
|
|
259
271
|
* Validate specific wizard step
|
|
260
272
|
* POST /api/v1/wizard/sessions/{sessionId}/validate-step
|
|
273
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
261
274
|
* @async
|
|
262
275
|
* @function validateStep
|
|
263
276
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -275,6 +288,7 @@ async function validateStep(dataplaneUrl, sessionId, authConfig, stepNumber) {
|
|
|
275
288
|
/**
|
|
276
289
|
* Get configuration preview with summaries
|
|
277
290
|
* GET /api/v1/wizard/preview/{sessionId}
|
|
291
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
278
292
|
* @async
|
|
279
293
|
* @function getPreview
|
|
280
294
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -291,6 +305,7 @@ async function getPreview(dataplaneUrl, sessionId, authConfig) {
|
|
|
291
305
|
/**
|
|
292
306
|
* Test MCP server connection
|
|
293
307
|
* POST /api/v1/wizard/test-mcp-connection
|
|
308
|
+
* @requiresPermission {Dataplane} external-system:create
|
|
294
309
|
* @async
|
|
295
310
|
* @function testMcpConnection
|
|
296
311
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -313,6 +328,7 @@ async function testMcpConnection(dataplaneUrl, authConfig, serverUrl, token) {
|
|
|
313
328
|
/**
|
|
314
329
|
* Get deployment documentation for a system (from dataplane DB only)
|
|
315
330
|
* GET /api/v1/wizard/deployment-docs/{systemKey}
|
|
331
|
+
* @requiresPermission {Dataplane} external-system:read
|
|
316
332
|
* @async
|
|
317
333
|
* @function getDeploymentDocs
|
|
318
334
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -330,6 +346,7 @@ async function getDeploymentDocs(dataplaneUrl, authConfig, systemKey) {
|
|
|
330
346
|
* Generate deployment documentation with variables.yaml and deploy JSON for better quality
|
|
331
347
|
* POST /api/v1/wizard/deployment-docs/{systemKey}
|
|
332
348
|
* Sends deployJson and variablesYaml in the request body so the dataplane can align README with the integration folder.
|
|
349
|
+
* @requiresPermission {Dataplane} external-system:update
|
|
333
350
|
* @async
|
|
334
351
|
* @function postDeploymentDocs
|
|
335
352
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -353,6 +370,7 @@ async function postDeploymentDocs(dataplaneUrl, authConfig, systemKey, body = nu
|
|
|
353
370
|
* GET /api/v1/wizard/platforms
|
|
354
371
|
* Expected response: { platforms: [ { key: string, displayName?: string } ] } or equivalent.
|
|
355
372
|
* On 404 or error, returns empty array (caller should hide "Known platform" choice).
|
|
373
|
+
* @requiresPermission {Dataplane} external-system:read or unauthenticated
|
|
356
374
|
* @async
|
|
357
375
|
* @function getWizardPlatforms
|
|
358
376
|
* @param {string} dataplaneUrl - Dataplane base URL
|
|
@@ -370,6 +388,26 @@ async function getWizardPlatforms(dataplaneUrl, authConfig) {
|
|
|
370
388
|
}
|
|
371
389
|
}
|
|
372
390
|
|
|
391
|
+
/**
|
|
392
|
+
* List credentials for wizard selection (Step 3)
|
|
393
|
+
* GET /api/v1/wizard/credentials
|
|
394
|
+
* @requiresPermission {Dataplane} credential:read or external-system:create
|
|
395
|
+
* @async
|
|
396
|
+
* @function listWizardCredentials
|
|
397
|
+
* @param {string} dataplaneUrl - Dataplane base URL
|
|
398
|
+
* @param {Object} authConfig - Authentication configuration
|
|
399
|
+
* @param {Object} [options] - Query options
|
|
400
|
+
* @param {boolean} [options.activeOnly] - If true, return only active credentials
|
|
401
|
+
* @returns {Promise<Object>} Response with credentials list (e.g. data.credentials or data.items)
|
|
402
|
+
* @throws {Error} If request fails
|
|
403
|
+
*/
|
|
404
|
+
async function listWizardCredentials(dataplaneUrl, authConfig, options = {}) {
|
|
405
|
+
const client = new ApiClient(dataplaneUrl, authConfig);
|
|
406
|
+
return await client.get('/api/v1/wizard/credentials', {
|
|
407
|
+
params: options
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
373
411
|
module.exports = {
|
|
374
412
|
createWizardSession,
|
|
375
413
|
getWizardSession,
|
|
@@ -388,5 +426,6 @@ module.exports = {
|
|
|
388
426
|
testMcpConnection,
|
|
389
427
|
getDeploymentDocs,
|
|
390
428
|
postDeploymentDocs,
|
|
391
|
-
getWizardPlatforms
|
|
429
|
+
getWizardPlatforms,
|
|
430
|
+
listWizardCredentials
|
|
392
431
|
};
|
package/lib/app/deploy.js
CHANGED
|
@@ -17,6 +17,7 @@ const pushUtils = require('../deployment/push');
|
|
|
17
17
|
const logger = require('../utils/logger');
|
|
18
18
|
const { detectAppType, getBuilderPath, getIntegrationPath } = require('../utils/paths');
|
|
19
19
|
const { checkApplicationExists } = require('../utils/app-existence');
|
|
20
|
+
const { getApplicationStatus } = require('../api/applications.api');
|
|
20
21
|
const { loadDeploymentConfig } = require('./deploy-config');
|
|
21
22
|
|
|
22
23
|
/**
|
|
@@ -218,6 +219,26 @@ function displayDeploymentResults(result) {
|
|
|
218
219
|
}
|
|
219
220
|
}
|
|
220
221
|
|
|
222
|
+
/**
|
|
223
|
+
* Fetches app URL from controller application status and displays it.
|
|
224
|
+
* On API failure or missing URL, shows controllerUrl so the user always sees where the app is.
|
|
225
|
+
* @param {string} controllerUrl - Controller base URL (used as fallback)
|
|
226
|
+
* @param {string} envKey - Environment key
|
|
227
|
+
* @param {string} appKey - Application key (manifest.key)
|
|
228
|
+
* @param {Object} authConfig - Auth used for deployment (same as for status)
|
|
229
|
+
*/
|
|
230
|
+
async function displayAppUrlFromController(controllerUrl, envKey, appKey, authConfig) {
|
|
231
|
+
let url = null;
|
|
232
|
+
try {
|
|
233
|
+
const res = await getApplicationStatus(controllerUrl, envKey, appKey, authConfig);
|
|
234
|
+
const body = res?.data;
|
|
235
|
+
url = (body && (body.url || body.data?.url)) || res?.url || null;
|
|
236
|
+
} catch (_) {
|
|
237
|
+
// Use controllerUrl fallback below
|
|
238
|
+
}
|
|
239
|
+
logger.log(chalk.green(` ā App running at ${url || controllerUrl}`));
|
|
240
|
+
}
|
|
241
|
+
|
|
221
242
|
/**
|
|
222
243
|
* Check if app is external and handle external deployment.
|
|
223
244
|
* When options.type === 'external', forces deployment from integration/<app> (no app register needed).
|
|
@@ -272,6 +293,59 @@ function validateImageIsPullable(imageRef, appName) {
|
|
|
272
293
|
}
|
|
273
294
|
}
|
|
274
295
|
|
|
296
|
+
/**
|
|
297
|
+
* Throws an error when deployment status is failed or cancelled
|
|
298
|
+
* @param {string} status - Status value
|
|
299
|
+
* @param {Object} statusObj - Full status object from result
|
|
300
|
+
* @throws {Error}
|
|
301
|
+
*/
|
|
302
|
+
function throwIfDeploymentFailed(status, statusObj) {
|
|
303
|
+
if (status !== 'failed' && status !== 'cancelled') return;
|
|
304
|
+
const msg =
|
|
305
|
+
statusObj.message ||
|
|
306
|
+
statusObj.error ||
|
|
307
|
+
(status === 'cancelled' ? 'Deployment cancelled' : 'Deployment failed');
|
|
308
|
+
const err = new Error(`Deployment ${status}: ${msg}`);
|
|
309
|
+
err.formatted = `Deployment ${status}.\n\n${msg}`;
|
|
310
|
+
err.deploymentStatus = statusObj;
|
|
311
|
+
throw err;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Enhances 401 error with rotate-secret hint when app exists
|
|
316
|
+
* @param {Error} error - Caught error
|
|
317
|
+
* @param {boolean} appExists - Whether app exists in controller
|
|
318
|
+
* @param {string} appName - Application name
|
|
319
|
+
* @param {string} envKey - Environment key
|
|
320
|
+
* @throws {Error} Enhanced or original error
|
|
321
|
+
*/
|
|
322
|
+
function enhanceAuthErrorIfNeeded(error, appExists, appName, envKey) {
|
|
323
|
+
if (!appExists || error.status !== 401 || error.message.includes('rotate-secret')) {
|
|
324
|
+
throw error;
|
|
325
|
+
}
|
|
326
|
+
const enhancedError = new Error(
|
|
327
|
+
`${error.message}\n\nš” The application '${appName}' exists in environment '${envKey}'. ` +
|
|
328
|
+
`To fix invalid credentials, rotate the application secret:\n aifabrix app rotate-secret ${appName}`
|
|
329
|
+
);
|
|
330
|
+
enhancedError.status = 401;
|
|
331
|
+
enhancedError.formatted = error.formatted || enhancedError.message;
|
|
332
|
+
throw enhancedError;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Apply image/registry overrides from options to manifest
|
|
337
|
+
* @param {Object} manifest - Deployment manifest
|
|
338
|
+
* @param {Object} options - Deployment options
|
|
339
|
+
*/
|
|
340
|
+
function applyManifestOverrides(manifest, options) {
|
|
341
|
+
if (options.imageOverride || options.image) {
|
|
342
|
+
manifest.image = options.imageOverride || options.image;
|
|
343
|
+
}
|
|
344
|
+
if (options.registryMode) {
|
|
345
|
+
manifest.registryMode = options.registryMode;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
275
349
|
/**
|
|
276
350
|
* Execute standard application deployment flow
|
|
277
351
|
* @async
|
|
@@ -283,38 +357,29 @@ function validateImageIsPullable(imageRef, appName) {
|
|
|
283
357
|
async function executeStandardDeployment(appName, options) {
|
|
284
358
|
const config = await loadDeploymentConfig(appName, options);
|
|
285
359
|
const controllerUrl = config.controllerUrl || 'unknown';
|
|
286
|
-
|
|
287
|
-
// Check if application exists before deployment
|
|
288
360
|
const appExists = await checkApplicationExists(appName, controllerUrl, config.envKey, config.auth);
|
|
289
361
|
|
|
290
362
|
const { manifest, manifestPath } = await generateAndValidateManifest(appName);
|
|
291
|
-
|
|
292
|
-
manifest.image = options.imageOverride || options.image;
|
|
293
|
-
}
|
|
294
|
-
if (options.registryMode) {
|
|
295
|
-
manifest.registryMode = options.registryMode;
|
|
296
|
-
}
|
|
297
|
-
|
|
363
|
+
applyManifestOverrides(manifest, options);
|
|
298
364
|
validateImageIsPullable(manifest.image, appName);
|
|
299
|
-
|
|
300
365
|
displayDeploymentInfo(manifest, manifestPath);
|
|
301
366
|
|
|
302
367
|
try {
|
|
303
368
|
const result = await executeDeployment(manifest, config);
|
|
304
369
|
displayDeploymentResults(result);
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
370
|
+
const status = result.status?.status;
|
|
371
|
+
throwIfDeploymentFailed(status, result.status || {});
|
|
372
|
+
if (status === 'completed') {
|
|
373
|
+
await displayAppUrlFromController(
|
|
374
|
+
config.controllerUrl,
|
|
375
|
+
config.envKey,
|
|
376
|
+
manifest.key,
|
|
377
|
+
config.auth
|
|
312
378
|
);
|
|
313
|
-
enhancedError.status = 401;
|
|
314
|
-
enhancedError.formatted = error.formatted || enhancedError.message;
|
|
315
|
-
throw enhancedError;
|
|
316
379
|
}
|
|
317
|
-
|
|
380
|
+
return { result, controllerUrl, appExists };
|
|
381
|
+
} catch (error) {
|
|
382
|
+
enhanceAuthErrorIfNeeded(error, appExists, appName, config.envKey);
|
|
318
383
|
}
|
|
319
384
|
}
|
|
320
385
|
|
package/lib/app/rotate-secret.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
const chalk = require('chalk');
|
|
12
|
-
const { getConfig, normalizeControllerUrl, resolveEnvironment } = require('../core/config');
|
|
12
|
+
const { getConfig, normalizeControllerUrl, resolveEnvironment, clearClientToken } = require('../core/config');
|
|
13
13
|
const { resolveControllerUrl } = require('../utils/controller-url');
|
|
14
14
|
const { getOrRefreshDeviceToken } = require('../utils/token-manager');
|
|
15
15
|
const { rotateApplicationSecret } = require('../api/applications.api');
|
|
@@ -324,6 +324,8 @@ async function executeRotation(appKey, actualControllerUrl, environment, token)
|
|
|
324
324
|
const { credentials, message } = validateResponse(response);
|
|
325
325
|
await saveCredentialsLocally(appKey, credentials, actualControllerUrl);
|
|
326
326
|
displayRotationResults(appKey, environment, credentials, actualControllerUrl, message);
|
|
327
|
+
// Clear cached client token so next deploy uses new credentials (avoids "Invalid or expired credentials" when running deploy/up-dataplane immediately after rotate)
|
|
328
|
+
await clearClientToken(environment, appKey);
|
|
327
329
|
}
|
|
328
330
|
|
|
329
331
|
/**
|