@aifabrix/builder 2.10.0 ā 2.11.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/integration/hubspot/README.md +2 -2
- package/integration/hubspot/hubspot-deploy.json +12 -4
- package/lib/app-config.js +39 -8
- package/lib/app-register.js +70 -388
- package/lib/templates.js +8 -3
- package/lib/utils/api-error-handler.js +11 -453
- package/lib/utils/app-register-api.js +71 -0
- package/lib/utils/app-register-auth.js +72 -0
- package/lib/utils/app-register-config.js +205 -0
- package/lib/utils/app-register-display.js +69 -0
- package/lib/utils/app-register-validator.js +143 -0
- package/lib/utils/device-code.js +1 -1
- package/lib/utils/error-formatters/error-parser.js +150 -0
- package/lib/utils/error-formatters/http-status-errors.js +189 -0
- package/lib/utils/error-formatters/network-errors.js +46 -0
- package/lib/utils/error-formatters/permission-errors.js +94 -0
- package/lib/utils/error-formatters/validation-errors.js +133 -0
- package/package.json +1 -1
- package/templates/applications/README.md.hbs +1 -1
|
@@ -48,13 +48,13 @@ aifabrix validate hubspot
|
|
|
48
48
|
|
|
49
49
|
```bash
|
|
50
50
|
# Login to controller
|
|
51
|
-
aifabrix login --controller
|
|
51
|
+
aifabrix login --controller http://localhost:3100 --method device --environment dev
|
|
52
52
|
|
|
53
53
|
# Register application
|
|
54
54
|
aifabrix app register hubspot --environment dev
|
|
55
55
|
|
|
56
56
|
# Deploy entire system
|
|
57
|
-
aifabrix deploy hubspot --controller
|
|
57
|
+
aifabrix deploy hubspot --controller http://localhost:3100 --environment dev
|
|
58
58
|
|
|
59
59
|
# Or deploy individual datasources for testing
|
|
60
60
|
aifabrix datasource deploy hubspot-company --environment dev --file integration/hubspot/hubspot-deploy-company.json
|
|
@@ -58,7 +58,11 @@
|
|
|
58
58
|
"field": "select",
|
|
59
59
|
"label": "HubSpot API Version",
|
|
60
60
|
"placeholder": "Select API version",
|
|
61
|
-
"options": [
|
|
61
|
+
"options": [
|
|
62
|
+
"v1",
|
|
63
|
+
"v2",
|
|
64
|
+
"v3"
|
|
65
|
+
],
|
|
62
66
|
"validation": {
|
|
63
67
|
"required": false
|
|
64
68
|
}
|
|
@@ -86,6 +90,10 @@
|
|
|
86
90
|
"documentKey": "hubspot-v3",
|
|
87
91
|
"autoDiscoverEntities": false
|
|
88
92
|
},
|
|
89
|
-
"tags": [
|
|
90
|
-
|
|
91
|
-
|
|
93
|
+
"tags": [
|
|
94
|
+
"crm",
|
|
95
|
+
"sales",
|
|
96
|
+
"marketing",
|
|
97
|
+
"hubspot"
|
|
98
|
+
]
|
|
99
|
+
}
|
package/lib/app-config.js
CHANGED
|
@@ -46,23 +46,54 @@ async function generateVariablesYamlFile(appPath, appName, config) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* Generates env.template content for external systems based on authentication type
|
|
51
|
+
* @param {Object} config - Application configuration with authType and systemKey
|
|
52
|
+
* @param {string} appName - Application name (used as fallback for systemKey)
|
|
53
|
+
* @returns {string} Environment template content
|
|
54
|
+
*/
|
|
55
|
+
function generateExternalSystemEnvTemplate(config, appName) {
|
|
56
|
+
const systemKey = config.systemKey || appName;
|
|
57
|
+
const authType = config.authType || 'apikey';
|
|
58
|
+
const lines = [
|
|
59
|
+
`# ${systemKey} ${authType.toUpperCase()} Configuration`,
|
|
60
|
+
'# These values are set via the Miso Controller interface or Dataplane portal',
|
|
61
|
+
'# Values are stored in Key Vault automatically by the platform',
|
|
62
|
+
''
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
if (authType === 'oauth2') {
|
|
66
|
+
lines.push('CLIENTID=kv://' + systemKey + '-clientidKeyVault');
|
|
67
|
+
lines.push('CLIENTSECRET=kv://' + systemKey + '-clientsecretKeyVault');
|
|
68
|
+
lines.push('TOKENURL=https://api.example.com/oauth/token');
|
|
69
|
+
lines.push('REDIRECT_URI=kv://' + systemKey + '-redirect-uriKeyVault');
|
|
70
|
+
} else if (authType === 'apikey') {
|
|
71
|
+
lines.push('API_KEY=kv://' + systemKey + '-api-keyKeyVault');
|
|
72
|
+
} else if (authType === 'basic') {
|
|
73
|
+
lines.push('USERNAME=kv://' + systemKey + '-usernameKeyVault');
|
|
74
|
+
lines.push('PASSWORD=kv://' + systemKey + '-passwordKeyVault');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return lines.join('\n');
|
|
78
|
+
}
|
|
79
|
+
|
|
49
80
|
/**
|
|
50
81
|
* Generates env.template file if it doesn't exist
|
|
51
82
|
* @async
|
|
52
83
|
* @param {string} appPath - Path to application directory
|
|
84
|
+
* @param {string} appName - Application name
|
|
53
85
|
* @param {Object} config - Application configuration
|
|
54
86
|
* @param {Object} existingEnv - Existing environment variables
|
|
55
87
|
*/
|
|
56
|
-
async function generateEnvTemplateFile(appPath, config, existingEnv) {
|
|
57
|
-
// Skip env.template for external type
|
|
58
|
-
if (config.type === 'external') {
|
|
59
|
-
return;
|
|
60
|
-
}
|
|
61
|
-
|
|
88
|
+
async function generateEnvTemplateFile(appPath, appName, config, existingEnv) {
|
|
62
89
|
const envTemplatePath = path.join(appPath, 'env.template');
|
|
63
90
|
if (!(await fileExists(envTemplatePath))) {
|
|
64
91
|
let envTemplate;
|
|
65
|
-
|
|
92
|
+
|
|
93
|
+
if (config.type === 'external') {
|
|
94
|
+
// Generate env.template for external systems based on authType
|
|
95
|
+
envTemplate = generateExternalSystemEnvTemplate(config, appName);
|
|
96
|
+
} else if (existingEnv) {
|
|
66
97
|
const envResult = await generateEnvTemplateFromReader(config, existingEnv);
|
|
67
98
|
envTemplate = envResult.template;
|
|
68
99
|
|
|
@@ -155,7 +186,7 @@ async function generateDeployJsonFile(appPath, appName, config) {
|
|
|
155
186
|
async function generateConfigFiles(appPath, appName, config, existingEnv) {
|
|
156
187
|
try {
|
|
157
188
|
await generateVariablesYamlFile(appPath, appName, config);
|
|
158
|
-
await generateEnvTemplateFile(appPath, config, existingEnv);
|
|
189
|
+
await generateEnvTemplateFile(appPath, appName, config, existingEnv);
|
|
159
190
|
await generateRbacYamlFile(appPath, appName, config);
|
|
160
191
|
await generateDeployJsonFile(appPath, appName, config);
|
|
161
192
|
await generateReadmeMdFile(appPath, appName, config);
|
package/lib/app-register.js
CHANGED
|
@@ -8,361 +8,98 @@
|
|
|
8
8
|
* @version 2.0.0
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
const fs = require('fs').promises;
|
|
12
|
-
const path = require('path');
|
|
13
11
|
const chalk = require('chalk');
|
|
14
|
-
const yaml = require('js-yaml');
|
|
15
|
-
const { getConfig } = require('./config');
|
|
16
|
-
const { authenticatedApiCall } = require('./utils/api');
|
|
17
|
-
const { formatApiError } = require('./utils/api-error-handler');
|
|
18
12
|
const logger = require('./utils/logger');
|
|
19
13
|
const { saveLocalSecret, isLocalhost } = require('./utils/local-secrets');
|
|
20
14
|
const { updateEnvTemplate } = require('./utils/env-template');
|
|
21
|
-
const { getOrRefreshDeviceToken } = require('./utils/token-manager');
|
|
22
|
-
const { detectAppType } = require('./utils/paths');
|
|
23
15
|
const { generateEnvFile } = require('./secrets');
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Validation schema for application registration
|
|
35
|
-
*/
|
|
36
|
-
const registerApplicationSchema = {
|
|
37
|
-
environmentId: (val) => {
|
|
38
|
-
if (!val || val.length < 1) {
|
|
39
|
-
throw new Error('Invalid environment ID format');
|
|
40
|
-
}
|
|
41
|
-
return val;
|
|
42
|
-
},
|
|
43
|
-
key: (val) => {
|
|
44
|
-
if (!val || val.length < 1) {
|
|
45
|
-
throw new Error('Application key is required');
|
|
46
|
-
}
|
|
47
|
-
if (val.length > 50) {
|
|
48
|
-
throw new Error('Application key must be at most 50 characters');
|
|
49
|
-
}
|
|
50
|
-
if (!/^[a-z0-9-]+$/.test(val)) {
|
|
51
|
-
throw new Error('Application key must contain only lowercase letters, numbers, and hyphens');
|
|
52
|
-
}
|
|
53
|
-
return val;
|
|
54
|
-
},
|
|
55
|
-
displayName: (val) => {
|
|
56
|
-
if (!val || val.length < 1) {
|
|
57
|
-
throw new Error('Display name is required');
|
|
58
|
-
}
|
|
59
|
-
if (val.length > 100) {
|
|
60
|
-
throw new Error('Display name must be at most 100 characters');
|
|
61
|
-
}
|
|
62
|
-
return val;
|
|
63
|
-
},
|
|
64
|
-
description: (val) => val || undefined,
|
|
65
|
-
configuration: (val) => {
|
|
66
|
-
const validTypes = ['webapp', 'api', 'service', 'functionapp'];
|
|
67
|
-
const validRegistryModes = ['acr', 'external', 'public'];
|
|
68
|
-
|
|
69
|
-
if (!val || !val.type || !validTypes.includes(val.type)) {
|
|
70
|
-
throw new Error('Configuration type must be one of: webapp, api, service, functionapp');
|
|
71
|
-
}
|
|
72
|
-
if (!val.registryMode || !validRegistryModes.includes(val.registryMode)) {
|
|
73
|
-
throw new Error('Registry mode must be one of: acr, external, public');
|
|
74
|
-
}
|
|
75
|
-
if (val.port !== undefined) {
|
|
76
|
-
if (!Number.isInteger(val.port) || val.port < 1 || val.port > 65535) {
|
|
77
|
-
throw new Error('Port must be an integer between 1 and 65535');
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return val;
|
|
81
|
-
}
|
|
82
|
-
};
|
|
16
|
+
const { registerApplicationSchema, validateAppRegistrationData } = require('./utils/app-register-validator');
|
|
17
|
+
const {
|
|
18
|
+
loadVariablesYaml,
|
|
19
|
+
createMinimalAppIfNeeded,
|
|
20
|
+
extractAppConfiguration
|
|
21
|
+
} = require('./utils/app-register-config');
|
|
22
|
+
const { checkAuthentication } = require('./utils/app-register-auth');
|
|
23
|
+
const { callRegisterApi } = require('./utils/app-register-api');
|
|
24
|
+
const { displayRegistrationResults, getEnvironmentPrefix } = require('./utils/app-register-display');
|
|
83
25
|
|
|
84
26
|
/**
|
|
85
|
-
*
|
|
86
|
-
* @
|
|
87
|
-
* @param {string} appKey - Application key
|
|
88
|
-
* @returns {Promise<{variables: Object, created: boolean}>} Variables and creation flag
|
|
89
|
-
*/
|
|
90
|
-
async function loadVariablesYaml(appKey) {
|
|
91
|
-
// Detect app type and get correct path (integration or builder)
|
|
92
|
-
const { appPath } = await detectAppType(appKey);
|
|
93
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
94
|
-
|
|
95
|
-
try {
|
|
96
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf-8');
|
|
97
|
-
return { variables: yaml.load(variablesContent), created: false };
|
|
98
|
-
} catch (error) {
|
|
99
|
-
if (error.code === 'ENOENT') {
|
|
100
|
-
logger.log(chalk.yellow(`ā ļø variables.yaml not found for ${appKey}`));
|
|
101
|
-
logger.log(chalk.yellow('š Creating minimal configuration...\n'));
|
|
102
|
-
return { variables: null, created: true };
|
|
103
|
-
}
|
|
104
|
-
throw new Error(`Failed to read variables.yaml: ${error.message}`);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Create minimal application configuration if needed
|
|
110
|
-
* @async
|
|
111
|
-
* @param {string} appKey - Application key
|
|
27
|
+
* Build registration data payload from app configuration
|
|
28
|
+
* @param {Object} appConfig - Application configuration
|
|
112
29
|
* @param {Object} options - Registration options
|
|
113
|
-
* @returns {
|
|
30
|
+
* @returns {Object} Registration data payload
|
|
114
31
|
*/
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
await createApp(appKey, {
|
|
121
|
-
port: options.port,
|
|
122
|
-
language: 'typescript',
|
|
123
|
-
database: false,
|
|
124
|
-
redis: false,
|
|
125
|
-
storage: false,
|
|
126
|
-
authentication: false
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// Detect app type and get correct path (integration or builder)
|
|
130
|
-
const { appPath } = await detectAppType(appKey);
|
|
131
|
-
const variablesPath = path.join(appPath, 'variables.yaml');
|
|
132
|
-
const variablesContent = await fs.readFile(variablesPath, 'utf-8');
|
|
133
|
-
return yaml.load(variablesContent);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* Extract application configuration from variables.yaml
|
|
138
|
-
* @param {Object} variables - Variables from YAML file
|
|
139
|
-
* @param {string} appKey - Application key
|
|
140
|
-
* @param {Object} options - Registration options
|
|
141
|
-
* @returns {Object} Extracted configuration
|
|
142
|
-
*/
|
|
143
|
-
function extractAppConfiguration(variables, appKey, options) {
|
|
144
|
-
const appKeyFromFile = variables.app?.key || appKey;
|
|
145
|
-
const displayName = variables.app?.name || options.name || appKey;
|
|
146
|
-
const description = variables.app?.description || '';
|
|
147
|
-
|
|
148
|
-
// Handle external type
|
|
149
|
-
if (variables.app?.type === 'external') {
|
|
150
|
-
return {
|
|
151
|
-
appKey: appKeyFromFile,
|
|
152
|
-
displayName,
|
|
153
|
-
description,
|
|
154
|
-
appType: 'external',
|
|
155
|
-
registryMode: 'external',
|
|
156
|
-
port: null, // External systems don't need ports
|
|
157
|
-
language: null // External systems don't need language
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
const appType = variables.build?.language === 'typescript' ? 'webapp' : 'service';
|
|
162
|
-
const registryMode = 'external';
|
|
163
|
-
const port = variables.build?.port || options.port || 3000;
|
|
164
|
-
const language = variables.build?.language || 'typescript';
|
|
165
|
-
|
|
166
|
-
return {
|
|
167
|
-
appKey: appKeyFromFile,
|
|
168
|
-
displayName,
|
|
169
|
-
description,
|
|
170
|
-
appType,
|
|
171
|
-
registryMode,
|
|
172
|
-
port,
|
|
173
|
-
language
|
|
32
|
+
function buildRegistrationData(appConfig, options) {
|
|
33
|
+
const registrationData = {
|
|
34
|
+
key: appConfig.appKey,
|
|
35
|
+
displayName: appConfig.displayName,
|
|
36
|
+
type: appConfig.appType
|
|
174
37
|
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Validate application registration data
|
|
179
|
-
* @async
|
|
180
|
-
* @param {Object} config - Application configuration
|
|
181
|
-
* @param {string} originalAppKey - Original app key for error messages
|
|
182
|
-
* @throws {Error} If validation fails
|
|
183
|
-
*/
|
|
184
|
-
async function validateAppRegistrationData(config, originalAppKey) {
|
|
185
|
-
const missingFields = [];
|
|
186
|
-
if (!config.appKey) missingFields.push('app.key');
|
|
187
|
-
if (!config.displayName) missingFields.push('app.name');
|
|
188
|
-
|
|
189
|
-
if (missingFields.length > 0) {
|
|
190
|
-
logger.error(chalk.red('ā Missing required fields in variables.yaml:'));
|
|
191
|
-
missingFields.forEach(field => logger.error(chalk.red(` - ${field}`)));
|
|
192
|
-
// Detect app type to show correct path
|
|
193
|
-
const { appPath } = await detectAppType(originalAppKey);
|
|
194
|
-
const relativePath = path.relative(process.cwd(), appPath);
|
|
195
|
-
logger.error(chalk.red(`\n Please update ${relativePath}/variables.yaml and try again.`));
|
|
196
|
-
process.exit(1);
|
|
197
|
-
}
|
|
198
38
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
registerApplicationSchema.configuration({
|
|
203
|
-
type: config.appType,
|
|
204
|
-
registryMode: config.registryMode,
|
|
205
|
-
port: config.port
|
|
206
|
-
});
|
|
207
|
-
} catch (error) {
|
|
208
|
-
logger.error(chalk.red(`ā Invalid configuration: ${error.message}`));
|
|
209
|
-
process.exit(1);
|
|
39
|
+
// Add optional fields only if they have values
|
|
40
|
+
if (appConfig.description || options.description) {
|
|
41
|
+
registrationData.description = appConfig.description || options.description;
|
|
210
42
|
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Check if user is authenticated and get token
|
|
215
|
-
* @async
|
|
216
|
-
* @param {string} [controllerUrl] - Optional controller URL from variables.yaml
|
|
217
|
-
* @param {string} [environment] - Optional environment key
|
|
218
|
-
* @returns {Promise<{apiUrl: string, token: string}>} Configuration with API URL and token
|
|
219
|
-
*/
|
|
220
|
-
async function checkAuthentication(controllerUrl, environment) {
|
|
221
|
-
const config = await getConfig();
|
|
222
|
-
|
|
223
|
-
// Try to get controller URL from parameter, config, or device tokens
|
|
224
|
-
let finalControllerUrl = controllerUrl;
|
|
225
|
-
let token = null;
|
|
226
43
|
|
|
227
|
-
//
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
if (
|
|
231
|
-
|
|
232
|
-
finalControllerUrl = deviceToken.controller;
|
|
44
|
+
// Handle external type vs non-external types differently
|
|
45
|
+
if (appConfig.appType === 'external') {
|
|
46
|
+
// For external type: include externalIntegration, exclude registryMode/port/image
|
|
47
|
+
if (appConfig.externalIntegration) {
|
|
48
|
+
registrationData.externalIntegration = appConfig.externalIntegration;
|
|
233
49
|
}
|
|
234
|
-
}
|
|
50
|
+
} else {
|
|
51
|
+
// For non-external types: include registryMode, port, image
|
|
52
|
+
registrationData.registryMode = appConfig.registryMode;
|
|
235
53
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
if (deviceUrls.length > 0) {
|
|
240
|
-
// Use first available device token
|
|
241
|
-
finalControllerUrl = deviceUrls[0];
|
|
242
|
-
const deviceToken = await getOrRefreshDeviceToken(finalControllerUrl);
|
|
243
|
-
if (deviceToken && deviceToken.token) {
|
|
244
|
-
token = deviceToken.token;
|
|
245
|
-
finalControllerUrl = deviceToken.controller;
|
|
246
|
-
}
|
|
54
|
+
// Port is required for non-external types
|
|
55
|
+
if (appConfig.port) {
|
|
56
|
+
registrationData.port = appConfig.port;
|
|
247
57
|
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// If still no token, check for client token (requires environment and app)
|
|
251
|
-
if (!token && environment) {
|
|
252
|
-
// For app register, we don't have an app yet, so client tokens won't work
|
|
253
|
-
// This is expected - device tokens should be used for registration
|
|
254
|
-
}
|
|
255
58
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
59
|
+
// Image is required for non-external types
|
|
60
|
+
if (appConfig.image) {
|
|
61
|
+
registrationData.image = appConfig.image;
|
|
62
|
+
}
|
|
260
63
|
}
|
|
261
64
|
|
|
262
|
-
return
|
|
263
|
-
apiUrl: finalControllerUrl,
|
|
264
|
-
token: token
|
|
265
|
-
};
|
|
65
|
+
return registrationData;
|
|
266
66
|
}
|
|
267
67
|
|
|
268
68
|
/**
|
|
269
|
-
*
|
|
69
|
+
* Save credentials to local secrets if localhost
|
|
270
70
|
* @async
|
|
71
|
+
* @param {Object} responseData - Registration response data
|
|
271
72
|
* @param {string} apiUrl - API URL
|
|
272
|
-
* @param {string} token - Authentication token
|
|
273
|
-
* @param {string} environment - Environment ID
|
|
274
|
-
* @param {Object} registrationData - Registration data
|
|
275
|
-
* @returns {Promise<Object>} API response
|
|
276
73
|
*/
|
|
277
|
-
async function
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
{
|
|
281
|
-
method: 'POST',
|
|
282
|
-
body: JSON.stringify(registrationData)
|
|
283
|
-
},
|
|
284
|
-
token
|
|
285
|
-
);
|
|
286
|
-
|
|
287
|
-
if (!response.success) {
|
|
288
|
-
const formattedError = response.formattedError || formatApiError(response);
|
|
289
|
-
logger.error(formattedError);
|
|
290
|
-
process.exit(1);
|
|
74
|
+
async function saveLocalCredentials(responseData, apiUrl) {
|
|
75
|
+
if (!isLocalhost(apiUrl)) {
|
|
76
|
+
return;
|
|
291
77
|
}
|
|
292
78
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// 1. Direct format: { application: {...}, credentials: {...} }
|
|
297
|
-
// 2. Wrapped format: { success: true, data: { application: {...}, credentials: {...} } }
|
|
298
|
-
const apiResponse = response.data;
|
|
299
|
-
if (apiResponse && apiResponse.data && apiResponse.data.application) {
|
|
300
|
-
// Wrapped format: use apiResponse.data
|
|
301
|
-
return apiResponse.data;
|
|
302
|
-
} else if (apiResponse && apiResponse.application) {
|
|
303
|
-
// Direct format: use apiResponse directly
|
|
304
|
-
return apiResponse;
|
|
305
|
-
}
|
|
306
|
-
// Fallback: return apiResponse as-is (shouldn't happen, but handle gracefully)
|
|
307
|
-
logger.error(chalk.red('ā Invalid response: missing application data'));
|
|
308
|
-
logger.error(chalk.gray('\nFull response for debugging:'));
|
|
309
|
-
logger.error(chalk.gray(JSON.stringify(response, null, 2)));
|
|
310
|
-
process.exit(1);
|
|
311
|
-
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Get environment prefix for GitHub Secrets
|
|
316
|
-
* @param {string} environment - Environment key (e.g., 'dev', 'tst', 'pro', 'miso')
|
|
317
|
-
* @returns {string} Uppercase prefix (e.g., 'DEV', 'TST', 'PRO', 'MISO')
|
|
318
|
-
*/
|
|
319
|
-
function getEnvironmentPrefix(environment) {
|
|
320
|
-
if (!environment) {
|
|
321
|
-
return 'DEV';
|
|
322
|
-
}
|
|
323
|
-
// Convert to uppercase and handle common variations
|
|
324
|
-
const env = environment.toLowerCase();
|
|
325
|
-
if (env === 'dev' || env === 'development') {
|
|
326
|
-
return 'DEV';
|
|
327
|
-
}
|
|
328
|
-
if (env === 'tst' || env === 'test' || env === 'staging') {
|
|
329
|
-
return 'TST';
|
|
330
|
-
}
|
|
331
|
-
if (env === 'pro' || env === 'prod' || env === 'production') {
|
|
332
|
-
return 'PRO';
|
|
333
|
-
}
|
|
334
|
-
// For other environments (e.g., 'miso'), uppercase the entire string
|
|
335
|
-
// Use full string if 4 characters or less, otherwise use first 4 characters
|
|
336
|
-
const upper = environment.toUpperCase();
|
|
337
|
-
return upper.length <= 4 ? upper : upper.substring(0, 4);
|
|
338
|
-
}
|
|
79
|
+
const registeredAppKey = responseData.application.key;
|
|
80
|
+
const clientIdKey = `${registeredAppKey}-client-idKeyVault`;
|
|
81
|
+
const clientSecretKey = `${registeredAppKey}-client-secretKeyVault`;
|
|
339
82
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
* @param {string} apiUrl - API URL
|
|
344
|
-
* @param {string} environment - Environment key
|
|
345
|
-
*/
|
|
346
|
-
function displayRegistrationResults(data, apiUrl, environment) {
|
|
347
|
-
logger.log(chalk.green('ā
Application registered successfully!\n'));
|
|
348
|
-
logger.log(chalk.bold('š Application Details:'));
|
|
349
|
-
logger.log(` ID: ${data.application.id}`);
|
|
350
|
-
logger.log(` Key: ${data.application.key}`);
|
|
351
|
-
logger.log(` Display Name: ${data.application.displayName}\n`);
|
|
83
|
+
try {
|
|
84
|
+
await saveLocalSecret(clientIdKey, responseData.credentials.clientId);
|
|
85
|
+
await saveLocalSecret(clientSecretKey, responseData.credentials.clientSecret);
|
|
352
86
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
logger.log(chalk.yellow(` Client Secret: ${data.credentials.clientSecret}\n`));
|
|
87
|
+
// Update env.template
|
|
88
|
+
await updateEnvTemplate(registeredAppKey, clientIdKey, clientSecretKey, apiUrl);
|
|
356
89
|
|
|
357
|
-
|
|
90
|
+
// Regenerate .env file with updated credentials
|
|
91
|
+
try {
|
|
92
|
+
await generateEnvFile(registeredAppKey, null, 'local');
|
|
93
|
+
logger.log(chalk.green('ā .env file updated with new credentials'));
|
|
94
|
+
} catch (error) {
|
|
95
|
+
logger.warn(chalk.yellow(`ā ļø Could not regenerate .env file: ${error.message}`));
|
|
96
|
+
}
|
|
358
97
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
logger.log(chalk.cyan(` ${envPrefix}_MISO_CLIENTID = ${data.credentials.clientId}`));
|
|
365
|
-
logger.log(chalk.cyan(` ${envPrefix}_MISO_CLIENTSECRET = ${data.credentials.clientSecret}\n`));
|
|
98
|
+
logger.log(chalk.green('\nā Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
99
|
+
logger.log(chalk.green('ā env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.warn(chalk.yellow(`ā ļø Could not save credentials locally: ${error.message}`));
|
|
102
|
+
}
|
|
366
103
|
}
|
|
367
104
|
|
|
368
105
|
/**
|
|
@@ -381,49 +118,21 @@ async function registerApplication(appKey, options) {
|
|
|
381
118
|
|
|
382
119
|
// Load variables.yaml
|
|
383
120
|
const { variables, created } = await loadVariablesYaml(appKey);
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
if (created) {
|
|
388
|
-
finalVariables = await createMinimalAppIfNeeded(appKey, options);
|
|
389
|
-
}
|
|
121
|
+
const finalVariables = created
|
|
122
|
+
? await createMinimalAppIfNeeded(appKey, options)
|
|
123
|
+
: variables;
|
|
390
124
|
|
|
391
|
-
// Extract configuration
|
|
392
|
-
const appConfig = extractAppConfiguration(finalVariables, appKey, options);
|
|
393
|
-
|
|
394
|
-
// Validate configuration (pass original appKey for error messages)
|
|
125
|
+
// Extract and validate configuration
|
|
126
|
+
const appConfig = await extractAppConfiguration(finalVariables, appKey, options);
|
|
395
127
|
await validateAppRegistrationData(appConfig, appKey);
|
|
396
128
|
|
|
397
|
-
//
|
|
129
|
+
// Authenticate and get API configuration
|
|
398
130
|
const controllerUrl = finalVariables?.deployment?.controllerUrl;
|
|
399
|
-
|
|
400
|
-
// Check authentication (try device token first, supports registration flow)
|
|
401
131
|
const authConfig = await checkAuthentication(controllerUrl, options.environment);
|
|
402
|
-
|
|
403
|
-
// Validate environment
|
|
404
132
|
const environment = registerApplicationSchema.environmentId(options.environment);
|
|
405
133
|
|
|
406
|
-
// Prepare registration data to match OpenAPI RegisterApplicationRequest schema
|
|
407
|
-
// Schema: { key, displayName, description?, configuration: { type, registryMode, port?, image? } }
|
|
408
|
-
const registrationData = {
|
|
409
|
-
key: appConfig.appKey,
|
|
410
|
-
displayName: appConfig.displayName,
|
|
411
|
-
configuration: {
|
|
412
|
-
type: appConfig.appType,
|
|
413
|
-
registryMode: appConfig.registryMode
|
|
414
|
-
}
|
|
415
|
-
};
|
|
416
|
-
|
|
417
|
-
// Add optional fields only if they have values
|
|
418
|
-
if (appConfig.description || options.description) {
|
|
419
|
-
registrationData.description = appConfig.description || options.description;
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (appConfig.port) {
|
|
423
|
-
registrationData.configuration.port = appConfig.port;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
134
|
// Register application
|
|
135
|
+
const registrationData = buildRegistrationData(appConfig, options);
|
|
427
136
|
const responseData = await callRegisterApi(
|
|
428
137
|
authConfig.apiUrl,
|
|
429
138
|
authConfig.token,
|
|
@@ -431,35 +140,8 @@ async function registerApplication(appKey, options) {
|
|
|
431
140
|
registrationData
|
|
432
141
|
);
|
|
433
142
|
|
|
434
|
-
// Save credentials
|
|
435
|
-
|
|
436
|
-
const registeredAppKey = responseData.application.key;
|
|
437
|
-
const clientIdKey = `${registeredAppKey}-client-idKeyVault`;
|
|
438
|
-
const clientSecretKey = `${registeredAppKey}-client-secretKeyVault`;
|
|
439
|
-
|
|
440
|
-
try {
|
|
441
|
-
await saveLocalSecret(clientIdKey, responseData.credentials.clientId);
|
|
442
|
-
await saveLocalSecret(clientSecretKey, responseData.credentials.clientSecret);
|
|
443
|
-
|
|
444
|
-
// Update env.template
|
|
445
|
-
await updateEnvTemplate(registeredAppKey, clientIdKey, clientSecretKey, authConfig.apiUrl);
|
|
446
|
-
|
|
447
|
-
// Regenerate .env file with updated credentials
|
|
448
|
-
try {
|
|
449
|
-
await generateEnvFile(registeredAppKey, null, 'local');
|
|
450
|
-
logger.log(chalk.green('ā .env file updated with new credentials'));
|
|
451
|
-
} catch (error) {
|
|
452
|
-
logger.warn(chalk.yellow(`ā ļø Could not regenerate .env file: ${error.message}`));
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
logger.log(chalk.green('\nā Credentials saved to ~/.aifabrix/secrets.local.yaml'));
|
|
456
|
-
logger.log(chalk.green('ā env.template updated with MISO_CLIENTID, MISO_CLIENTSECRET, and MISO_CONTROLLER_URL\n'));
|
|
457
|
-
} catch (error) {
|
|
458
|
-
logger.warn(chalk.yellow(`ā ļø Could not save credentials locally: ${error.message}`));
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
// Display results
|
|
143
|
+
// Save credentials and display results
|
|
144
|
+
await saveLocalCredentials(responseData, authConfig.apiUrl);
|
|
463
145
|
displayRegistrationResults(responseData, authConfig.apiUrl, environment);
|
|
464
146
|
}
|
|
465
147
|
|
package/lib/templates.js
CHANGED
|
@@ -20,11 +20,16 @@ function generateVariablesYaml(appName, config) {
|
|
|
20
20
|
|
|
21
21
|
// For external type, create minimal variables.yaml
|
|
22
22
|
if (appType === 'external') {
|
|
23
|
+
// Use config values if provided, otherwise use defaults consistent with prompts
|
|
24
|
+
const systemKey = config.systemKey || appName;
|
|
25
|
+
const systemDisplayName = config.systemDisplayName || displayName;
|
|
26
|
+
const systemDescription = config.systemDescription || `External system integration for ${appName}`;
|
|
27
|
+
|
|
23
28
|
const variables = {
|
|
24
29
|
app: {
|
|
25
|
-
key:
|
|
26
|
-
displayName:
|
|
27
|
-
description:
|
|
30
|
+
key: systemKey,
|
|
31
|
+
displayName: systemDisplayName,
|
|
32
|
+
description: systemDescription,
|
|
28
33
|
type: 'external'
|
|
29
34
|
},
|
|
30
35
|
deployment: {
|