@adminide-stack/form-builder-core 5.1.4-alpha.82 → 5.1.6-alpha.1
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/LICENSE +30 -21
- package/lib/config/__tests__/connectorFieldTokenResolver.test.d.ts +2 -0
- package/lib/config/__tests__/connectorFieldTokenResolver.test.d.ts.map +1 -0
- package/lib/config/cdnConfig.d.ts +17 -0
- package/lib/config/cdnConfig.d.ts.map +1 -0
- package/lib/config/cdnConfig.js +29 -0
- package/lib/config/cdnConfig.js.map +1 -0
- package/lib/config/configTypes.d.ts +220 -0
- package/lib/config/configTypes.d.ts.map +1 -0
- package/lib/config/configTypes.js +6 -0
- package/lib/config/configTypes.js.map +1 -0
- package/lib/config/connectorFieldTokenResolver.d.ts +11 -0
- package/lib/config/connectorFieldTokenResolver.d.ts.map +1 -0
- package/lib/config/connectorFieldTokenResolver.js +95 -0
- package/lib/config/connectorFieldTokenResolver.js.map +1 -0
- package/lib/config/connectorsConfig.d.ts +3 -0
- package/lib/config/connectorsConfig.d.ts.map +1 -0
- package/lib/config/connectorsConfig.js +44533 -0
- package/lib/config/connectorsConfig.js.map +1 -0
- package/lib/config/connectorsConfig2.d.ts +11 -0
- package/lib/config/connectorsConfig2.d.ts.map +1 -0
- package/lib/config/llmProviderConfig.d.ts +3 -0
- package/lib/config/llmProviderConfig.d.ts.map +1 -0
- package/lib/config/llmProviderConfig.js +457 -0
- package/lib/config/llmProviderConfig.js.map +1 -0
- package/lib/config/mcpConnectorConfig.d.ts +3 -0
- package/lib/config/mcpConnectorConfig.d.ts.map +1 -0
- package/lib/config/mcpConnectorConfig.js +961 -0
- package/lib/config/mcpConnectorConfig.js.map +1 -0
- package/lib/index.d.ts +11 -2
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -1
- package/lib/inngest/generateFunctionCode.js +1 -1
- package/lib/utils/__tests__/deepMergeFormSteps.test.d.ts +2 -0
- package/lib/utils/__tests__/deepMergeFormSteps.test.d.ts.map +1 -0
- package/lib/utils/authMetaDataSchema.d.ts +199 -0
- package/lib/utils/authMetaDataSchema.d.ts.map +1 -0
- package/lib/utils/authMetaDataSchema.js +1601 -0
- package/lib/utils/authMetaDataSchema.js.map +1 -0
- package/lib/utils/deepMergeFormSteps.d.ts +15 -0
- package/lib/utils/deepMergeFormSteps.d.ts.map +1 -0
- package/lib/utils/deepMergeFormSteps.js +64 -0
- package/lib/utils/deepMergeFormSteps.js.map +1 -0
- package/lib/utils/schemaHelpers.d.ts +85 -0
- package/lib/utils/schemaHelpers.d.ts.map +1 -0
- package/lib/utils/schemaHelpers.js +473 -0
- package/lib/utils/schemaHelpers.js.map +1 -0
- package/package.json +3 -2
- package/CHANGELOG.md +0 -60
- package/rollup.config.mjs +0 -33
- package/src/index.ts +0 -5
- package/src/inngest/MONACO_INTEGRATION_EXAMPLE.md +0 -406
- package/src/inngest/README_AUTOCOMPLETE.md +0 -380
- package/src/inngest/generateFunctionCode.ts +0 -248
- package/src/inngest/interfaces/index.ts +0 -1
- package/src/inngest/interfaces/types.ts +0 -63
- package/src/inngest/monacoAutocompleteIntegration.ts +0 -432
- package/src/inngest/stepGenerator.ts +0 -538
- package/src/utils/json.ts +0 -50
- package/tsconfig.json +0 -14
|
@@ -0,0 +1,1601 @@
|
|
|
1
|
+
import {CONTRIBUTION_POINTS,ContributeDefaultValueSourceType}from'common';import {resolveConnectorFieldTokensInValue}from'../config/connectorFieldTokenResolver.js';import {parseSampleNodes,flattenSchemas}from'./schemaHelpers.js';const parseScopes = raw => raw.split(',').map(s => s.trim()).filter(Boolean);
|
|
2
|
+
const parseEnumValues = raw => {
|
|
3
|
+
if (Array.isArray(raw)) return raw;
|
|
4
|
+
if (raw === null || raw === undefined) return [];
|
|
5
|
+
if (typeof raw !== 'string') return [];
|
|
6
|
+
if (!raw.trim()) return [];
|
|
7
|
+
try {
|
|
8
|
+
const parsed = JSON.parse(raw);
|
|
9
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
10
|
+
} catch (_) {
|
|
11
|
+
return raw.split('\n').map(line => line.trim()).filter(Boolean).map(line => {
|
|
12
|
+
const [value, label] = line.split(':');
|
|
13
|
+
return {
|
|
14
|
+
value: (value || '').trim(),
|
|
15
|
+
label: (label || value || '').trim()
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
const toEnvKey = value => (value || '').replace(/([a-z0-9])([A-Z])/g, '$1_$2').replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2').replace(/[^a-zA-Z0-9]+/g, '_').replace(/^_+|_+$/g, '').toUpperCase();
|
|
21
|
+
const mapProviderFields = (fields, providerId) => fields.map(field => ({
|
|
22
|
+
name: field.name,
|
|
23
|
+
label: field.label,
|
|
24
|
+
type: field.type,
|
|
25
|
+
placeholder: field.placeholder,
|
|
26
|
+
description: field.description,
|
|
27
|
+
required: field.required,
|
|
28
|
+
defaultValue: field.defaultValue ?? '',
|
|
29
|
+
isConfigField: field.isConfigField,
|
|
30
|
+
isSecret: field.isSecret,
|
|
31
|
+
secretSource: field.isSecret ? field.secretSource || {
|
|
32
|
+
type: 'vault',
|
|
33
|
+
secretType: field.secretType,
|
|
34
|
+
secretKey: field.secretKey || (providerId ? `${toEnvKey(providerId)}_${toEnvKey(field.name)}` : toEnvKey(field.name))
|
|
35
|
+
} : null,
|
|
36
|
+
uiWidget: field.uiWidget,
|
|
37
|
+
enumValues: parseEnumValues(field.enumValues),
|
|
38
|
+
readOnly: field.readOnly,
|
|
39
|
+
iconType: field.iconType,
|
|
40
|
+
genericType: field.genericType || undefined,
|
|
41
|
+
placement: field.placement || undefined,
|
|
42
|
+
placementKey: field.placementKey || undefined,
|
|
43
|
+
scope: field.scope || undefined
|
|
44
|
+
}));
|
|
45
|
+
const buildProviderConfig = (providerConfig, connectorType) => {
|
|
46
|
+
const base = {
|
|
47
|
+
id: providerConfig.id,
|
|
48
|
+
title: providerConfig.title,
|
|
49
|
+
description: providerConfig.description,
|
|
50
|
+
icon: providerConfig.icon || {
|
|
51
|
+
name: providerConfig.iconName,
|
|
52
|
+
style: providerConfig.iconFontSize ? {
|
|
53
|
+
fontSize: providerConfig.iconFontSize
|
|
54
|
+
} : undefined
|
|
55
|
+
},
|
|
56
|
+
logo: providerConfig.logo || undefined,
|
|
57
|
+
color: providerConfig.color || undefined,
|
|
58
|
+
enabled: providerConfig.enabled,
|
|
59
|
+
scopes: Array.isArray(providerConfig.scopes) ? providerConfig.scopes : parseScopes(providerConfig.scopes),
|
|
60
|
+
authMethods: providerConfig.authMethods.length > 0 ? providerConfig.authMethods : ['api_key'],
|
|
61
|
+
categories: providerConfig.categories || [providerConfig.category],
|
|
62
|
+
fields: mapProviderFields(providerConfig.fields, providerConfig.id)
|
|
63
|
+
};
|
|
64
|
+
if (connectorType === 'mcp') {
|
|
65
|
+
return {
|
|
66
|
+
...base,
|
|
67
|
+
providerUrl: providerConfig.providerUrl || undefined,
|
|
68
|
+
mcpUrl: providerConfig.mcpUrl
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
...base,
|
|
73
|
+
providerUrl: providerConfig.providerUrl,
|
|
74
|
+
useCustomOAuthEndpoints: providerConfig.useCustomOAuthEndpoints,
|
|
75
|
+
usePKCE: providerConfig.usePKCE,
|
|
76
|
+
clientAuthMethod: providerConfig.clientAuthMethod,
|
|
77
|
+
authorizationUrl: providerConfig.authorizationUrl,
|
|
78
|
+
tokenUrl: providerConfig.tokenUrl,
|
|
79
|
+
revocationUrl: providerConfig.revocationUrl,
|
|
80
|
+
useBasicAuth: providerConfig.useBasicAuth
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const buildAppSeed = p => {
|
|
84
|
+
const authMethods = p.authMethods ? [...p.authMethods] : ['oauth2'];
|
|
85
|
+
return {
|
|
86
|
+
key: `${p.id}:app`,
|
|
87
|
+
providerId: p.id,
|
|
88
|
+
title: p.title,
|
|
89
|
+
description: p.description || '',
|
|
90
|
+
scopes: p.scopes || [],
|
|
91
|
+
providerUrl: p.providerUrl || '',
|
|
92
|
+
useCustomOAuthEndpoints: p.useCustomOAuthEndpoints,
|
|
93
|
+
usePKCE: p.usePKCE,
|
|
94
|
+
clientAuthMethod: p.clientAuthMethod,
|
|
95
|
+
authorizationUrl: p.authorizationUrl,
|
|
96
|
+
tokenUrl: p.tokenUrl,
|
|
97
|
+
revocationUrl: p.revocationUrl,
|
|
98
|
+
useBasicAuth: p.useBasicAuth,
|
|
99
|
+
mcpUrl: p.mcpUrl || '',
|
|
100
|
+
connectorType: 'app',
|
|
101
|
+
authMethods,
|
|
102
|
+
fields: p.fields || [],
|
|
103
|
+
resources: p.resources || [],
|
|
104
|
+
customButtons: p.customButtons || [],
|
|
105
|
+
formIcon: p.logo || '',
|
|
106
|
+
categories: p.categories
|
|
107
|
+
};
|
|
108
|
+
};
|
|
109
|
+
const buildMcpSeed = p => ({
|
|
110
|
+
key: `${p.id}:mcp`,
|
|
111
|
+
providerId: p.id,
|
|
112
|
+
title: p.title,
|
|
113
|
+
description: p.description || '',
|
|
114
|
+
scopes: p.scopes || [],
|
|
115
|
+
providerUrl: p.mcpUrl || '',
|
|
116
|
+
mcpUrl: p.mcpUrl || '',
|
|
117
|
+
connectorType: 'mcp',
|
|
118
|
+
authMethods: [...p.authMethods],
|
|
119
|
+
fields: p.fields || [],
|
|
120
|
+
resources: [],
|
|
121
|
+
customButtons: [],
|
|
122
|
+
formIcon: p.logo || '',
|
|
123
|
+
categories: p.categories
|
|
124
|
+
});
|
|
125
|
+
function buildProviderSeedFromConfig(provider, connectorType) {
|
|
126
|
+
if (connectorType === 'mcp') {
|
|
127
|
+
return buildMcpSeed(provider);
|
|
128
|
+
}
|
|
129
|
+
return buildAppSeed(provider);
|
|
130
|
+
}
|
|
131
|
+
// Auth seed generation
|
|
132
|
+
const sanitizeFieldKey = value => value.replace(/[^a-zA-Z0-9_]/g, '_');
|
|
133
|
+
const seedIsApiKeyOnly = seed => seed.connectorType !== 'mcp' && !seed.authMethods.includes('oauth2') && !seed.authMethods.includes('bearer_token');
|
|
134
|
+
const STEP_KEY = 'step1';
|
|
135
|
+
const seedTemplateType = seed => seed.connectorType === 'mcp' ? 'mcp' : seed.authMethods.includes('oauth2') ? 'oauth' : 'api';
|
|
136
|
+
function buildResourcesNode(seed) {
|
|
137
|
+
if (seed.resources.length === 0) return null;
|
|
138
|
+
const properties = {};
|
|
139
|
+
for (const originalResource of seed.resources) {
|
|
140
|
+
const resource = resolveConnectorFieldTokensInValue(originalResource, {
|
|
141
|
+
fields: seed.fields,
|
|
142
|
+
basePath: `${STEP_KEY}.oauthConnector`,
|
|
143
|
+
context: `${seed.key}.${originalResource.name}`
|
|
144
|
+
});
|
|
145
|
+
const prefix = resource.name;
|
|
146
|
+
const selectedSource = resource.type === 'jsCode' ? 'js-code' : resource.type;
|
|
147
|
+
properties[`${prefix}.id`] = resource.name;
|
|
148
|
+
properties[`${prefix}.name`] = resource.label;
|
|
149
|
+
properties[`${prefix}.type`] = resource.type === 'rest-api' ? 'apiRequest' : resource.type;
|
|
150
|
+
properties[`${prefix}.selectedSource`] = selectedSource;
|
|
151
|
+
properties[`${prefix}.description`] = '';
|
|
152
|
+
properties[`${prefix}.key`] = resource.name;
|
|
153
|
+
if (resource.type === 'rest-api' && resource.config.restApi) {
|
|
154
|
+
const r = resource.config.restApi;
|
|
155
|
+
const rp = `${prefix}.config.restApi`;
|
|
156
|
+
properties[`${rp}.httpMethod`] = r.httpMethod;
|
|
157
|
+
properties[`${rp}.url`] = r.url;
|
|
158
|
+
properties[`${rp}.urlParams`] = r.urlParams;
|
|
159
|
+
r.headers.forEach((h, i) => {
|
|
160
|
+
properties[`${rp}.headers.${i}.key`] = h.key;
|
|
161
|
+
properties[`${rp}.headers.${i}.value`] = h.value;
|
|
162
|
+
});
|
|
163
|
+
properties[`${rp}.body.format`] = r.body.format;
|
|
164
|
+
properties[`${rp}.body.data`] = r.body.data;
|
|
165
|
+
properties[`${rp}.body.rawData`] = r.body.rawData;
|
|
166
|
+
properties[`${rp}.cookies`] = r.cookies;
|
|
167
|
+
properties[`${rp}.runBehavior`] = r.runBehavior;
|
|
168
|
+
properties[`${rp}.transformResults`] = r.transformResults;
|
|
169
|
+
}
|
|
170
|
+
if (resource.type === 'jsCode' && resource.config.jsCode) {
|
|
171
|
+
const j = resource.config.jsCode;
|
|
172
|
+
const jp = `${prefix}.config.jsCode`;
|
|
173
|
+
properties[`${jp}.query`] = j.query;
|
|
174
|
+
properties[`${jp}.transformResults`] = j.transformResults;
|
|
175
|
+
}
|
|
176
|
+
if (resource.type === 'graphql' && resource.config.graphql) {
|
|
177
|
+
const g = resource.config.graphql;
|
|
178
|
+
const gp = `${prefix}.config.graphql`;
|
|
179
|
+
properties[`${gp}.query`] = g.query;
|
|
180
|
+
properties[`${gp}.useInternalLink`] = g.useInternalLink;
|
|
181
|
+
properties[`${gp}.transformResults`] = g.transformResults;
|
|
182
|
+
properties[`${gp}.variables`] = g.variables;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return {
|
|
186
|
+
type: 'resources',
|
|
187
|
+
properties
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function buildCustomButtonsNode(seed) {
|
|
191
|
+
if (seed.customButtons.length === 0) return null;
|
|
192
|
+
const node = {
|
|
193
|
+
type: 'customButtons'
|
|
194
|
+
};
|
|
195
|
+
seed.customButtons.forEach((btn, idx) => {
|
|
196
|
+
const base = `properties.${STEP_KEY}.${idx}`;
|
|
197
|
+
node[`${base}.id`] = btn.id || `btn-${Math.random().toString(36).substring(2, 15)}`;
|
|
198
|
+
node[`${base}.title`] = btn.title;
|
|
199
|
+
node[`${base}.action`] = btn.action;
|
|
200
|
+
if (btn.resourceId) node[`${base}.resourceId`] = btn.resourceId;
|
|
201
|
+
if (btn.inngestId) node[`${base}.inngestId`] = btn.inngestId;
|
|
202
|
+
});
|
|
203
|
+
return node;
|
|
204
|
+
}
|
|
205
|
+
function buildInngestNode() {
|
|
206
|
+
return {
|
|
207
|
+
type: 'inngest',
|
|
208
|
+
'properties.generatedCode.submit': `async ({ event, step }) => {\nreturn {\n functionId: 'new-function',\n timestamp: new Date().toISOString()\n};\n}`,
|
|
209
|
+
'properties.raw.submit.functionID': 'new-function',
|
|
210
|
+
'properties.raw.submit.functionName': 'New Workflow Function',
|
|
211
|
+
'properties.raw.submit.functionDescription': 'A new Workflow function',
|
|
212
|
+
'properties.raw.submit.enabled': false,
|
|
213
|
+
'properties.raw.submit.displaySubmitButton': false,
|
|
214
|
+
'properties.raw.submit.events.0': 'app.event',
|
|
215
|
+
'properties.raw.submit.metadata.version': '1.0.0',
|
|
216
|
+
'properties.raw.submit.selectedStep': null,
|
|
217
|
+
'properties.raw.submit.activeTab': 'config',
|
|
218
|
+
'properties.raw.submit.editorSettings.theme': 'vs-dark',
|
|
219
|
+
'properties.raw.submit.editorSettings.fontSize': 14,
|
|
220
|
+
'properties.raw.submit.editorSettings.tabSize': 2,
|
|
221
|
+
'properties.raw.submit.editorSettings.wordWrap': true,
|
|
222
|
+
'properties.raw.submit.generatedCode': `async ({ event, step }) => {\nreturn {\n functionId: 'new-function',\n timestamp: new Date().toISOString()\n};\n}`,
|
|
223
|
+
'properties.raw.submit.isCodeManuallyEdited': false,
|
|
224
|
+
'properties.raw.submit.stepEditorStates.activeTab': 'config',
|
|
225
|
+
'properties.raw.submit.version': '1.0.0'
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const seedCategory = seed => seed.categories[0];
|
|
229
|
+
const toVaultPrefix = value => value.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
|
|
230
|
+
const seedAuthMethodDefault = seed => seed.authMethods.includes('oauth2') ? 'oauth' : seed.connectorType === 'mcp' ? 'mcp' : 'api_key';
|
|
231
|
+
/**
|
|
232
|
+
* Build a `genericType -> count` map for the given fields. Used by
|
|
233
|
+
* {@link resolveFieldKey} to detect colliding `genericType` values so that
|
|
234
|
+
* generated config paths (e.g. `oauthConnector.info`) don't clash when
|
|
235
|
+
* multiple fields declare the same generic bucket.
|
|
236
|
+
*/
|
|
237
|
+
const buildGenericTypeCounts = fields => {
|
|
238
|
+
const counts = new Map();
|
|
239
|
+
if (!fields) return counts;
|
|
240
|
+
for (const field of fields) {
|
|
241
|
+
const gt = field?.genericType;
|
|
242
|
+
if (gt) counts.set(gt, (counts.get(gt) || 0) + 1);
|
|
243
|
+
}
|
|
244
|
+
return counts;
|
|
245
|
+
};
|
|
246
|
+
/**
|
|
247
|
+
* Reserved `genericType` value that means "this is a custom config field —
|
|
248
|
+
* route it under `oauthConnector.additionalKeys.<fieldName>`". Use this for
|
|
249
|
+
* any field that doesn't fit a canonical genericType (api_key, base_url,
|
|
250
|
+
* username, subdomain, etc.). Each field gets its own leaf path, so an
|
|
251
|
+
* arbitrary number of additional keys can coexist without collision.
|
|
252
|
+
*/
|
|
253
|
+
const ADDITIONAL_KEYS_GENERIC_TYPE = 'additionalKeys';
|
|
254
|
+
/**
|
|
255
|
+
* Resolve the path-segment for a config field. Rules (first match wins):
|
|
256
|
+
* 1. `genericType === 'additionalKeys'` → `additionalKeys.<field.name>` (namespaced)
|
|
257
|
+
* 2. unique `genericType` → that value
|
|
258
|
+
* 3. otherwise → `field.name` (collision fallback for legacy connectors)
|
|
259
|
+
*/
|
|
260
|
+
const resolveFieldKey = (field, genericTypeCounts) => {
|
|
261
|
+
const gt = field?.genericType;
|
|
262
|
+
if (gt === ADDITIONAL_KEYS_GENERIC_TYPE) return `${ADDITIONAL_KEYS_GENERIC_TYPE}.${field.name}`;
|
|
263
|
+
if (gt && (genericTypeCounts.get(gt) || 0) <= 1) return gt;
|
|
264
|
+
return field.name;
|
|
265
|
+
};
|
|
266
|
+
function buildPreGeneratedFormSteps(seed, namespace) {
|
|
267
|
+
const vaultPrefix = toVaultPrefix(seed.providerId);
|
|
268
|
+
const isApp = seed.connectorType === 'app';
|
|
269
|
+
const isApiKeyOnly = seedIsApiKeyOnly(seed);
|
|
270
|
+
const templateType = seedTemplateType(seed);
|
|
271
|
+
const fullPath = `integrationSettings.settings.${templateType}.${namespace}.oauthConnector`;
|
|
272
|
+
if (isApiKeyOnly) {
|
|
273
|
+
// api_key-only: oauthConnector element with minimal children (so widget renders),
|
|
274
|
+
// plus custom field elements with full secret/config metadata.
|
|
275
|
+
const minimalChildren = [{
|
|
276
|
+
id: 'provider',
|
|
277
|
+
type: 'string',
|
|
278
|
+
label: 'Provider',
|
|
279
|
+
default: seed.providerId,
|
|
280
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
281
|
+
readOnly: true
|
|
282
|
+
}, {
|
|
283
|
+
id: 'providerTitle',
|
|
284
|
+
type: 'string',
|
|
285
|
+
label: 'Provider Title',
|
|
286
|
+
default: seed.title,
|
|
287
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
288
|
+
readOnly: true
|
|
289
|
+
}, {
|
|
290
|
+
id: 'providerUrl',
|
|
291
|
+
type: 'string',
|
|
292
|
+
label: 'Provider URL',
|
|
293
|
+
default: seed.providerUrl,
|
|
294
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
295
|
+
readOnly: true
|
|
296
|
+
}, {
|
|
297
|
+
id: 'description',
|
|
298
|
+
type: 'string',
|
|
299
|
+
label: 'Description',
|
|
300
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
301
|
+
default: seed.description,
|
|
302
|
+
readOnly: true
|
|
303
|
+
}, {
|
|
304
|
+
id: 'connectorType',
|
|
305
|
+
type: 'string',
|
|
306
|
+
label: 'Connector Type',
|
|
307
|
+
default: seed.connectorType,
|
|
308
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
309
|
+
readOnly: true
|
|
310
|
+
}, {
|
|
311
|
+
id: 'authMethod',
|
|
312
|
+
type: 'string',
|
|
313
|
+
label: 'Auth Method',
|
|
314
|
+
default: 'api_key',
|
|
315
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
316
|
+
readOnly: true
|
|
317
|
+
}];
|
|
318
|
+
const minimalUiOverrides = {
|
|
319
|
+
provider: {
|
|
320
|
+
'ui:readonly': true
|
|
321
|
+
},
|
|
322
|
+
providerTitle: {
|
|
323
|
+
'ui:readonly': true
|
|
324
|
+
},
|
|
325
|
+
providerUrl: {
|
|
326
|
+
'ui:readonly': true
|
|
327
|
+
},
|
|
328
|
+
description: {
|
|
329
|
+
'ui:readonly': true
|
|
330
|
+
},
|
|
331
|
+
connectorType: {
|
|
332
|
+
'ui:widget': 'hidden'
|
|
333
|
+
},
|
|
334
|
+
authMethod: {}
|
|
335
|
+
};
|
|
336
|
+
const elements = [{
|
|
337
|
+
id: 'oauthConnector',
|
|
338
|
+
type: 'object',
|
|
339
|
+
label: 'OAuth Connector',
|
|
340
|
+
required: false,
|
|
341
|
+
description: seed.description || 'Connect to external providers',
|
|
342
|
+
connectorType: seed.connectorType,
|
|
343
|
+
widget: 'OAuthConnectorWidget',
|
|
344
|
+
uiField: 'OAuthConnectorField',
|
|
345
|
+
children: minimalChildren,
|
|
346
|
+
_widgetUiOverrides: minimalUiOverrides,
|
|
347
|
+
connectorProvider: seed.providerId,
|
|
348
|
+
connectorProviderTitle: seed.title,
|
|
349
|
+
connectorDescription: seed.description,
|
|
350
|
+
connectorCategory: seedCategory(seed),
|
|
351
|
+
...(seed.connectorType === 'mcp' ? {
|
|
352
|
+
connectorMcpUrl: seed.providerUrl
|
|
353
|
+
} : {
|
|
354
|
+
connectorProviderUrl: seed.providerUrl
|
|
355
|
+
}),
|
|
356
|
+
connectorScopes: seed.scopes,
|
|
357
|
+
useCustomOAuthEndpoints: seed.useCustomOAuthEndpoints ?? false,
|
|
358
|
+
connectorAuthorizationUrl: seed.authorizationUrl || '',
|
|
359
|
+
connectorTokenUrl: seed.tokenUrl || '',
|
|
360
|
+
connectorRevocationUrl: seed.revocationUrl || '',
|
|
361
|
+
useBasicAuth: seed.useBasicAuth ?? false,
|
|
362
|
+
connectorRedirectUri: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
|
|
363
|
+
// connectorAuthMethod — present only for non-oauth connectors (matches reference forms)
|
|
364
|
+
...(!seed.authMethods.includes('oauth2') ? {
|
|
365
|
+
connectorAuthMethod: seedAuthMethodDefault(seed)
|
|
366
|
+
} : {}),
|
|
367
|
+
...(seed.fields.length > 0 ? {
|
|
368
|
+
connectorProviderFields: seed.fields
|
|
369
|
+
} : {}),
|
|
370
|
+
...(seed.resources.length > 0 ? {
|
|
371
|
+
connectorProviderResources: seed.resources
|
|
372
|
+
} : {}),
|
|
373
|
+
...(seed.customButtons.length > 0 ? {
|
|
374
|
+
connectorProviderCustomButtons: seed.customButtons
|
|
375
|
+
} : {}),
|
|
376
|
+
addedFromStepperConfigToggle: true,
|
|
377
|
+
isSchemaElement: true,
|
|
378
|
+
sourceSchemaId: 'configuration',
|
|
379
|
+
fullPath,
|
|
380
|
+
contributionPoint: 'configuration.integrationSettings',
|
|
381
|
+
templateType,
|
|
382
|
+
namespace
|
|
383
|
+
}];
|
|
384
|
+
return [{
|
|
385
|
+
id: STEP_KEY,
|
|
386
|
+
title: 'Step 1',
|
|
387
|
+
elements
|
|
388
|
+
}];
|
|
389
|
+
}
|
|
390
|
+
// Full OAuth children
|
|
391
|
+
const children = [{
|
|
392
|
+
id: 'clientId',
|
|
393
|
+
type: 'string',
|
|
394
|
+
label: 'Client ID',
|
|
395
|
+
description: 'OAuth application client identifier',
|
|
396
|
+
format: 'secret',
|
|
397
|
+
readOnly: true,
|
|
398
|
+
default: '',
|
|
399
|
+
scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
|
|
400
|
+
schemaExtensions: {
|
|
401
|
+
defaultValueSource: {
|
|
402
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
403
|
+
secretType: 'OAUTH_CLIENT',
|
|
404
|
+
secretKey: `${vaultPrefix}_CLIENT_ID`,
|
|
405
|
+
...(isApp ? {
|
|
406
|
+
requiredSignature: true
|
|
407
|
+
} : {})
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
}, {
|
|
411
|
+
id: 'clientSecret',
|
|
412
|
+
type: 'string',
|
|
413
|
+
label: 'Client Secret',
|
|
414
|
+
description: 'OAuth application client secret',
|
|
415
|
+
format: 'secret',
|
|
416
|
+
readOnly: true,
|
|
417
|
+
default: '',
|
|
418
|
+
scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
|
|
419
|
+
schemaExtensions: {
|
|
420
|
+
defaultValueSource: {
|
|
421
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
422
|
+
secretType: 'OAUTH_CLIENT',
|
|
423
|
+
secretKey: `${vaultPrefix}_CLIENT_SECRET`,
|
|
424
|
+
...(isApp ? {
|
|
425
|
+
requiredSignature: true
|
|
426
|
+
} : {})
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}, {
|
|
430
|
+
id: 'apiKey',
|
|
431
|
+
type: 'string',
|
|
432
|
+
label: 'API Key',
|
|
433
|
+
description: 'API Key for MCP integration',
|
|
434
|
+
format: 'secret',
|
|
435
|
+
writeOnly: true,
|
|
436
|
+
default: '',
|
|
437
|
+
scope: 4 /* ConfigurationScope.RESOURCE */,
|
|
438
|
+
schemaExtensions: {
|
|
439
|
+
defaultValueSource: {
|
|
440
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
441
|
+
secretType: 'API_KEY',
|
|
442
|
+
secretKey: `${vaultPrefix}_API_KEY`
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
}, {
|
|
446
|
+
id: 'accessToken',
|
|
447
|
+
type: 'string',
|
|
448
|
+
label: 'Access Token',
|
|
449
|
+
format: 'secret',
|
|
450
|
+
writeOnly: true,
|
|
451
|
+
default: '',
|
|
452
|
+
readOnly: true,
|
|
453
|
+
scope: 1 /* ConfigurationScope.APPLICATION */,
|
|
454
|
+
schemaExtensions: {
|
|
455
|
+
defaultValueSource: {
|
|
456
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
457
|
+
secretType: 'OAUTH_CLIENT',
|
|
458
|
+
secretKey: `${vaultPrefix}_ACCESS_TOKEN`
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
}, {
|
|
462
|
+
id: 'refreshToken',
|
|
463
|
+
type: 'string',
|
|
464
|
+
label: 'Refresh Token',
|
|
465
|
+
format: 'secret',
|
|
466
|
+
writeOnly: true,
|
|
467
|
+
default: '',
|
|
468
|
+
readOnly: true,
|
|
469
|
+
scope: 1 /* ConfigurationScope.APPLICATION */,
|
|
470
|
+
schemaExtensions: {
|
|
471
|
+
defaultValueSource: {
|
|
472
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
473
|
+
secretType: 'OAUTH_CLIENT',
|
|
474
|
+
secretKey: `${vaultPrefix}_REFRESH_TOKEN`
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}, {
|
|
478
|
+
id: 'expiresAt',
|
|
479
|
+
type: 'number',
|
|
480
|
+
label: 'Expires At',
|
|
481
|
+
description: 'Access token expiry as Unix timestamp in milliseconds; 0 if unset',
|
|
482
|
+
default: 0,
|
|
483
|
+
readOnly: true,
|
|
484
|
+
scope: 1 /* ConfigurationScope.APPLICATION */
|
|
485
|
+
}, {
|
|
486
|
+
id: 'urlTemplateKeyValues',
|
|
487
|
+
type: 'string',
|
|
488
|
+
label: 'URL Template Key Values',
|
|
489
|
+
description: 'Stringified JSON of resolved values for URL template placeholders (e.g. {"zoho-region":"eu"})',
|
|
490
|
+
default: '{}',
|
|
491
|
+
readOnly: true,
|
|
492
|
+
scope: 1 /* ConfigurationScope.APPLICATION */
|
|
493
|
+
}, {
|
|
494
|
+
id: 'authRequestMeta',
|
|
495
|
+
type: 'string',
|
|
496
|
+
label: 'Auth Request Metadata',
|
|
497
|
+
description: 'Serialized OAuth authorization-request metadata (e.g. PKCE verifier, state) for session continuity',
|
|
498
|
+
default: '',
|
|
499
|
+
readOnly: true,
|
|
500
|
+
scope: 1 /* ConfigurationScope.APPLICATION */
|
|
501
|
+
}, {
|
|
502
|
+
id: 'provider',
|
|
503
|
+
type: 'string',
|
|
504
|
+
label: 'Provider',
|
|
505
|
+
default: seed.providerId,
|
|
506
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
507
|
+
readOnly: true
|
|
508
|
+
}, {
|
|
509
|
+
id: 'providerTitle',
|
|
510
|
+
type: 'string',
|
|
511
|
+
label: 'Provider Title',
|
|
512
|
+
default: seed.title,
|
|
513
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
514
|
+
readOnly: true
|
|
515
|
+
}, {
|
|
516
|
+
id: 'providerUrl',
|
|
517
|
+
type: 'string',
|
|
518
|
+
label: 'Provider URL',
|
|
519
|
+
default: seed.providerUrl,
|
|
520
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
521
|
+
readOnly: true
|
|
522
|
+
}, {
|
|
523
|
+
id: 'useCustomOAuthEndpoints',
|
|
524
|
+
type: 'boolean',
|
|
525
|
+
label: 'Use Custom OAuth Endpoints',
|
|
526
|
+
default: seed.useCustomOAuthEndpoints ?? false,
|
|
527
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
528
|
+
readOnly: true
|
|
529
|
+
}, {
|
|
530
|
+
id: 'authorizationUrl',
|
|
531
|
+
type: 'string',
|
|
532
|
+
label: 'Authorization URL',
|
|
533
|
+
default: seed.authorizationUrl || '',
|
|
534
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
535
|
+
readOnly: true
|
|
536
|
+
}, {
|
|
537
|
+
id: 'tokenUrl',
|
|
538
|
+
type: 'string',
|
|
539
|
+
label: 'Token URL',
|
|
540
|
+
default: seed.tokenUrl || '',
|
|
541
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
542
|
+
readOnly: true
|
|
543
|
+
}, {
|
|
544
|
+
id: 'revocationUrl',
|
|
545
|
+
type: 'string',
|
|
546
|
+
label: 'Revocation URL',
|
|
547
|
+
default: seed.revocationUrl || '',
|
|
548
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
549
|
+
readOnly: true
|
|
550
|
+
}, {
|
|
551
|
+
id: 'useBasicAuth',
|
|
552
|
+
type: 'boolean',
|
|
553
|
+
label: 'Use Basic Auth',
|
|
554
|
+
default: seed.useBasicAuth ?? false,
|
|
555
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
556
|
+
readOnly: true
|
|
557
|
+
}, {
|
|
558
|
+
id: 'redirectUri',
|
|
559
|
+
type: 'string',
|
|
560
|
+
label: 'Redirect URI',
|
|
561
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
562
|
+
default: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
|
|
563
|
+
readOnly: true,
|
|
564
|
+
defaultValueSource: {
|
|
565
|
+
type: ContributeDefaultValueSourceType.Environment
|
|
566
|
+
}
|
|
567
|
+
}, {
|
|
568
|
+
id: 'scopes',
|
|
569
|
+
type: 'array',
|
|
570
|
+
label: 'Scopes',
|
|
571
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
572
|
+
default: seed.scopes,
|
|
573
|
+
readOnly: true
|
|
574
|
+
}, {
|
|
575
|
+
id: 'description',
|
|
576
|
+
type: 'string',
|
|
577
|
+
label: 'Description',
|
|
578
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
579
|
+
default: seed.description,
|
|
580
|
+
readOnly: true
|
|
581
|
+
}, {
|
|
582
|
+
id: 'connectorType',
|
|
583
|
+
type: 'string',
|
|
584
|
+
label: 'Connector Type',
|
|
585
|
+
default: seed.connectorType,
|
|
586
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
587
|
+
readOnly: true
|
|
588
|
+
}, {
|
|
589
|
+
id: 'mcpUrl',
|
|
590
|
+
type: 'string',
|
|
591
|
+
label: 'MCP URL',
|
|
592
|
+
default: seed.mcpUrl || '',
|
|
593
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
594
|
+
readOnly: true
|
|
595
|
+
}, {
|
|
596
|
+
id: 'authMethod',
|
|
597
|
+
type: 'string',
|
|
598
|
+
label: 'Auth Method',
|
|
599
|
+
default: seedAuthMethodDefault(seed),
|
|
600
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
601
|
+
readOnly: true
|
|
602
|
+
}, {
|
|
603
|
+
id: 'mcpScopes',
|
|
604
|
+
type: 'array',
|
|
605
|
+
label: 'MCP Scopes',
|
|
606
|
+
default: seed.connectorType === 'mcp' ? seed.scopes : [],
|
|
607
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
608
|
+
readOnly: true
|
|
609
|
+
}, {
|
|
610
|
+
id: 'hideCredentials',
|
|
611
|
+
type: 'boolean',
|
|
612
|
+
label: 'Hide Credentials',
|
|
613
|
+
default: false,
|
|
614
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
615
|
+
}, {
|
|
616
|
+
id: 'isConnected',
|
|
617
|
+
type: 'boolean',
|
|
618
|
+
label: 'Connected',
|
|
619
|
+
default: false,
|
|
620
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
621
|
+
readOnly: true
|
|
622
|
+
}];
|
|
623
|
+
const widgetUiOverrides = {
|
|
624
|
+
clientId: {
|
|
625
|
+
'ui:widget': 'hidden',
|
|
626
|
+
'ui:placeholder': 'Enter OAuth Client ID'
|
|
627
|
+
},
|
|
628
|
+
clientSecret: {
|
|
629
|
+
'ui:widget': 'hidden',
|
|
630
|
+
'ui:placeholder': 'Enter OAuth Client Secret'
|
|
631
|
+
},
|
|
632
|
+
apiKey: {
|
|
633
|
+
'ui:widget': seed.connectorType === 'mcp' || !seed.authMethods.includes('oauth2') ? 'password' : 'hidden',
|
|
634
|
+
'ui:placeholder': 'Enter API Key'
|
|
635
|
+
},
|
|
636
|
+
accessToken: {
|
|
637
|
+
'ui:widget': 'hidden'
|
|
638
|
+
},
|
|
639
|
+
refreshToken: {
|
|
640
|
+
'ui:widget': 'hidden'
|
|
641
|
+
},
|
|
642
|
+
expiresAt: {
|
|
643
|
+
'ui:widget': 'hidden'
|
|
644
|
+
},
|
|
645
|
+
urlTemplateKeyValues: {
|
|
646
|
+
'ui:widget': 'hidden'
|
|
647
|
+
},
|
|
648
|
+
authRequestMeta: {
|
|
649
|
+
'ui:widget': 'hidden'
|
|
650
|
+
},
|
|
651
|
+
provider: {
|
|
652
|
+
'ui:readonly': true
|
|
653
|
+
},
|
|
654
|
+
providerTitle: {
|
|
655
|
+
'ui:readonly': true
|
|
656
|
+
},
|
|
657
|
+
providerUrl: {
|
|
658
|
+
'ui:readonly': true
|
|
659
|
+
},
|
|
660
|
+
useCustomOAuthEndpoints: {
|
|
661
|
+
'ui:readonly': true
|
|
662
|
+
},
|
|
663
|
+
authorizationUrl: {
|
|
664
|
+
'ui:readonly': true
|
|
665
|
+
},
|
|
666
|
+
tokenUrl: {
|
|
667
|
+
'ui:readonly': true
|
|
668
|
+
},
|
|
669
|
+
revocationUrl: {
|
|
670
|
+
'ui:readonly': true
|
|
671
|
+
},
|
|
672
|
+
redirectUri: {
|
|
673
|
+
'ui:readonly': true
|
|
674
|
+
},
|
|
675
|
+
scopes: {
|
|
676
|
+
'ui:readonly': true,
|
|
677
|
+
'ui:widget': 'chips'
|
|
678
|
+
},
|
|
679
|
+
description: {
|
|
680
|
+
'ui:readonly': true
|
|
681
|
+
},
|
|
682
|
+
connectorType: {
|
|
683
|
+
'ui:widget': 'hidden'
|
|
684
|
+
},
|
|
685
|
+
mcpUrl: {
|
|
686
|
+
'ui:readonly': true
|
|
687
|
+
},
|
|
688
|
+
authMethod: {},
|
|
689
|
+
mcpScopes: {
|
|
690
|
+
'ui:readonly': true,
|
|
691
|
+
'ui:widget': 'chips'
|
|
692
|
+
},
|
|
693
|
+
hideCredentials: {
|
|
694
|
+
'ui:widget': 'hidden'
|
|
695
|
+
},
|
|
696
|
+
isConnected: {
|
|
697
|
+
'ui:widget': 'hidden'
|
|
698
|
+
}
|
|
699
|
+
};
|
|
700
|
+
return [{
|
|
701
|
+
id: STEP_KEY,
|
|
702
|
+
title: 'Step 1',
|
|
703
|
+
elements: [{
|
|
704
|
+
id: 'oauthConnector',
|
|
705
|
+
type: 'object',
|
|
706
|
+
label: 'OAuth Connector',
|
|
707
|
+
required: false,
|
|
708
|
+
description: seed.description || 'Connect to external OAuth providers',
|
|
709
|
+
connectorType: seed.connectorType,
|
|
710
|
+
widget: 'OAuthConnectorWidget',
|
|
711
|
+
uiField: 'OAuthConnectorField',
|
|
712
|
+
children,
|
|
713
|
+
_widgetUiOverrides: widgetUiOverrides,
|
|
714
|
+
connectorProvider: seed.providerId,
|
|
715
|
+
connectorProviderTitle: seed.title,
|
|
716
|
+
connectorDescription: seed.description,
|
|
717
|
+
connectorCategory: seedCategory(seed),
|
|
718
|
+
...(seed.connectorType === 'mcp' ? {
|
|
719
|
+
connectorMcpUrl: seed.providerUrl
|
|
720
|
+
} : {
|
|
721
|
+
connectorProviderUrl: seed.providerUrl
|
|
722
|
+
}),
|
|
723
|
+
connectorScopes: seed.scopes,
|
|
724
|
+
useCustomOAuthEndpoints: seed.useCustomOAuthEndpoints ?? false,
|
|
725
|
+
connectorAuthorizationUrl: seed.authorizationUrl || '',
|
|
726
|
+
connectorTokenUrl: seed.tokenUrl || '',
|
|
727
|
+
connectorRevocationUrl: seed.revocationUrl || '',
|
|
728
|
+
useBasicAuth: seed.useBasicAuth ?? false,
|
|
729
|
+
connectorRedirectUri: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
|
|
730
|
+
// connectorAuthMethod — present only for non-oauth connectors (matches reference forms)
|
|
731
|
+
...(!seed.authMethods.includes('oauth2') ? {
|
|
732
|
+
connectorAuthMethod: seedAuthMethodDefault(seed)
|
|
733
|
+
} : {}),
|
|
734
|
+
...(seed.fields.length > 0 ? {
|
|
735
|
+
connectorProviderFields: seed.fields
|
|
736
|
+
} : {}),
|
|
737
|
+
...(seed.resources.length > 0 ? {
|
|
738
|
+
connectorProviderResources: seed.resources
|
|
739
|
+
} : {}),
|
|
740
|
+
...(seed.customButtons.length > 0 ? {
|
|
741
|
+
connectorProviderCustomButtons: seed.customButtons
|
|
742
|
+
} : {}),
|
|
743
|
+
addedFromStepperConfigToggle: true,
|
|
744
|
+
isSchemaElement: true,
|
|
745
|
+
sourceSchemaId: 'configuration',
|
|
746
|
+
fullPath,
|
|
747
|
+
contributionPoint: 'configuration.integrationSettings',
|
|
748
|
+
templateType,
|
|
749
|
+
namespace
|
|
750
|
+
}]
|
|
751
|
+
}];
|
|
752
|
+
}
|
|
753
|
+
const buildCategoryKeyLookup = () => {
|
|
754
|
+
const lookup = new Map();
|
|
755
|
+
const prefix = 'uiLayout.credential.templates.';
|
|
756
|
+
const suffix = '.value';
|
|
757
|
+
for (const [key, value] of Object.entries(CONTRIBUTION_POINTS)) {
|
|
758
|
+
if (key.startsWith(prefix) && key.endsWith(suffix)) {
|
|
759
|
+
lookup.set(value, key.slice(prefix.length, -suffix.length));
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return lookup;
|
|
763
|
+
};
|
|
764
|
+
const CATEGORY_KEY_LOOKUP = buildCategoryKeyLookup();
|
|
765
|
+
const categoryToTemplateTypeKey = category => {
|
|
766
|
+
const key = CATEGORY_KEY_LOOKUP.get(category);
|
|
767
|
+
if (!key) {
|
|
768
|
+
throw new Error(`No contribution-point template type found for category "${category}"`);
|
|
769
|
+
}
|
|
770
|
+
return key;
|
|
771
|
+
};
|
|
772
|
+
function buildOAuthConfigurationContribution(seedsWithNamespaces) {
|
|
773
|
+
const formProperties = {
|
|
774
|
+
required: []
|
|
775
|
+
};
|
|
776
|
+
const formUIProperties = {};
|
|
777
|
+
const metaPaths = new Set();
|
|
778
|
+
for (const {
|
|
779
|
+
seed,
|
|
780
|
+
namespace
|
|
781
|
+
} of seedsWithNamespaces) {
|
|
782
|
+
const vaultPrefix = toVaultPrefix(seed.providerId);
|
|
783
|
+
const isApp = seed.connectorType === 'app';
|
|
784
|
+
const isApiKeyOnly = seedIsApiKeyOnly(seed);
|
|
785
|
+
const templateType = seedTemplateType(seed);
|
|
786
|
+
const basePath = `integrationSettings.settings.${templateType}.${namespace}.oauthConnector`;
|
|
787
|
+
// Helper to add a config field under the oauthConnector basePath
|
|
788
|
+
const addField = (fieldName, def, uiDef) => {
|
|
789
|
+
formProperties[`${basePath}.${fieldName}`] = {
|
|
790
|
+
...def,
|
|
791
|
+
fieldMeta: {
|
|
792
|
+
addedFromStepperConfigToggle: true
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
if (uiDef && Object.keys(uiDef).length > 0) {
|
|
796
|
+
formUIProperties[`${basePath}.${fieldName}`] = uiDef;
|
|
797
|
+
}
|
|
798
|
+
};
|
|
799
|
+
// Register oauthConnector path + all parents for meta entries (always, so widget renders)
|
|
800
|
+
const baseParts = basePath.split('.');
|
|
801
|
+
for (let i = 1; i < baseParts.length; i++) {
|
|
802
|
+
metaPaths.add(baseParts.slice(0, i).join('.'));
|
|
803
|
+
}
|
|
804
|
+
metaPaths.add(basePath);
|
|
805
|
+
// Root OAuthConnector UI entry (always, so widget renders)
|
|
806
|
+
// Include ui:options so providerFields / authMethods survive the $ref resolution
|
|
807
|
+
const uiOptions = {
|
|
808
|
+
provider: seed.providerId,
|
|
809
|
+
connectorType: seed.connectorType,
|
|
810
|
+
defaultAuthMethod: seedAuthMethodDefault(seed)
|
|
811
|
+
};
|
|
812
|
+
if (seed.authMethods.length > 0) uiOptions.authMethods = seed.authMethods;
|
|
813
|
+
if (seed.clientAuthMethod) uiOptions.defaultClientAuthMethod = seed.clientAuthMethod;
|
|
814
|
+
if (seed.usePKCE !== undefined) uiOptions.usePKCE = seed.usePKCE;
|
|
815
|
+
uiOptions.category = seedCategory(seed);
|
|
816
|
+
if (seed.fields.length > 0) {
|
|
817
|
+
const genericTypeCounts = buildGenericTypeCounts(seed.fields);
|
|
818
|
+
uiOptions.providerFields = seed.fields.map(field => field.isConfigField ? {
|
|
819
|
+
...field,
|
|
820
|
+
keyReference: `${basePath}.${resolveFieldKey(field, genericTypeCounts)}`,
|
|
821
|
+
schemaReference: 'configuration'
|
|
822
|
+
} : field);
|
|
823
|
+
}
|
|
824
|
+
if (seed.resources.length > 0) uiOptions.providerResources = seed.resources;
|
|
825
|
+
if (seed.customButtons.length > 0) uiOptions.providerCustomButtons = seed.customButtons;
|
|
826
|
+
formUIProperties[basePath] = {
|
|
827
|
+
'ui:widget': 'OAuthConnectorWidget',
|
|
828
|
+
'ui:field': 'OAuthConnectorField',
|
|
829
|
+
'ui:options': uiOptions
|
|
830
|
+
};
|
|
831
|
+
// ── OAuth sub-fields — only for non-api_key-only connectors ─────
|
|
832
|
+
if (!isApiKeyOnly) {
|
|
833
|
+
addField('clientId', {
|
|
834
|
+
type: 'string',
|
|
835
|
+
title: 'Client ID',
|
|
836
|
+
description: 'OAuth application client identifier',
|
|
837
|
+
format: 'secret',
|
|
838
|
+
default: '',
|
|
839
|
+
readOnly: true,
|
|
840
|
+
scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
|
|
841
|
+
defaultValueSource: {
|
|
842
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
843
|
+
secretType: 'OAUTH_CLIENT',
|
|
844
|
+
secretKey: `${vaultPrefix}_CLIENT_ID`,
|
|
845
|
+
...(isApp ? {
|
|
846
|
+
requiredSignature: true
|
|
847
|
+
} : {})
|
|
848
|
+
}
|
|
849
|
+
}, {
|
|
850
|
+
'ui:widget': 'hidden',
|
|
851
|
+
'ui:description': 'OAuth application client identifier',
|
|
852
|
+
'ui:placeholder': 'Enter OAuth Client ID'
|
|
853
|
+
});
|
|
854
|
+
addField('clientSecret', {
|
|
855
|
+
type: 'string',
|
|
856
|
+
title: 'Client Secret',
|
|
857
|
+
description: 'OAuth application client secret',
|
|
858
|
+
format: 'secret',
|
|
859
|
+
default: '',
|
|
860
|
+
readOnly: true,
|
|
861
|
+
scope: isApp ? 6 /* ConfigurationScope.MACHINE_OVERRIDABLE */ : 1 /* ConfigurationScope.APPLICATION */,
|
|
862
|
+
defaultValueSource: {
|
|
863
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
864
|
+
secretType: 'OAUTH_CLIENT',
|
|
865
|
+
secretKey: `${vaultPrefix}_CLIENT_SECRET`,
|
|
866
|
+
...(isApp ? {
|
|
867
|
+
requiredSignature: true
|
|
868
|
+
} : {})
|
|
869
|
+
}
|
|
870
|
+
}, {
|
|
871
|
+
'ui:widget': 'hidden',
|
|
872
|
+
'ui:description': 'OAuth application client secret',
|
|
873
|
+
'ui:placeholder': 'Enter OAuth Client Secret'
|
|
874
|
+
});
|
|
875
|
+
addField('apiKey', {
|
|
876
|
+
type: 'string',
|
|
877
|
+
title: 'API Key',
|
|
878
|
+
description: 'API Key for MCP integration',
|
|
879
|
+
format: 'secret',
|
|
880
|
+
default: '',
|
|
881
|
+
writeOnly: true,
|
|
882
|
+
scope: 4 /* ConfigurationScope.RESOURCE */,
|
|
883
|
+
defaultValueSource: {
|
|
884
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
885
|
+
secretType: 'API_KEY',
|
|
886
|
+
secretKey: `${vaultPrefix}_API_KEY`
|
|
887
|
+
}
|
|
888
|
+
}, {
|
|
889
|
+
'ui:widget': !seed.authMethods.includes('oauth2') ? 'password' : 'hidden',
|
|
890
|
+
'ui:description': 'API Key for MCP integration',
|
|
891
|
+
'ui:placeholder': 'Enter API Key'
|
|
892
|
+
});
|
|
893
|
+
addField('accessToken', {
|
|
894
|
+
type: 'string',
|
|
895
|
+
title: 'Access Token',
|
|
896
|
+
description: '',
|
|
897
|
+
format: 'secret',
|
|
898
|
+
default: '',
|
|
899
|
+
readOnly: true,
|
|
900
|
+
writeOnly: true,
|
|
901
|
+
scope: 1 /* ConfigurationScope.APPLICATION */,
|
|
902
|
+
defaultValueSource: {
|
|
903
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
904
|
+
secretType: 'OAUTH_CLIENT',
|
|
905
|
+
secretKey: `${vaultPrefix}_ACCESS_TOKEN`
|
|
906
|
+
}
|
|
907
|
+
}, {
|
|
908
|
+
'ui:widget': 'hidden'
|
|
909
|
+
});
|
|
910
|
+
addField('refreshToken', {
|
|
911
|
+
type: 'string',
|
|
912
|
+
title: 'Refresh Token',
|
|
913
|
+
description: '',
|
|
914
|
+
format: 'secret',
|
|
915
|
+
default: '',
|
|
916
|
+
readOnly: true,
|
|
917
|
+
writeOnly: true,
|
|
918
|
+
scope: 1 /* ConfigurationScope.APPLICATION */,
|
|
919
|
+
defaultValueSource: {
|
|
920
|
+
type: ContributeDefaultValueSourceType.Vault,
|
|
921
|
+
secretType: 'OAUTH_CLIENT',
|
|
922
|
+
secretKey: `${vaultPrefix}_REFRESH_TOKEN`
|
|
923
|
+
}
|
|
924
|
+
}, {
|
|
925
|
+
'ui:widget': 'hidden'
|
|
926
|
+
});
|
|
927
|
+
addField('expiresAt', {
|
|
928
|
+
type: 'number',
|
|
929
|
+
title: 'Expires At',
|
|
930
|
+
description: 'Access token expiry as Unix timestamp in milliseconds; 0 if unset',
|
|
931
|
+
default: 0,
|
|
932
|
+
readOnly: true,
|
|
933
|
+
scope: 1 /* ConfigurationScope.APPLICATION */
|
|
934
|
+
}, {
|
|
935
|
+
'ui:widget': 'hidden'
|
|
936
|
+
});
|
|
937
|
+
addField('urlTemplateKeyValues', {
|
|
938
|
+
type: 'string',
|
|
939
|
+
title: 'URL Template Key Values',
|
|
940
|
+
description: 'Stringified JSON of resolved values for URL template placeholders (e.g. {"zoho-region":"eu"})',
|
|
941
|
+
default: '{}',
|
|
942
|
+
readOnly: true,
|
|
943
|
+
scope: 1 /* ConfigurationScope.APPLICATION */
|
|
944
|
+
}, {
|
|
945
|
+
'ui:widget': 'hidden'
|
|
946
|
+
});
|
|
947
|
+
addField('authRequestMeta', {
|
|
948
|
+
type: 'string',
|
|
949
|
+
title: 'Auth Request Metadata',
|
|
950
|
+
description: 'Serialized OAuth authorization-request metadata (e.g. PKCE verifier, state) for session continuity',
|
|
951
|
+
default: '',
|
|
952
|
+
readOnly: true,
|
|
953
|
+
scope: 1 /* ConfigurationScope.APPLICATION */
|
|
954
|
+
}, {
|
|
955
|
+
'ui:widget': 'hidden'
|
|
956
|
+
});
|
|
957
|
+
addField('provider', {
|
|
958
|
+
type: 'string',
|
|
959
|
+
title: 'Provider',
|
|
960
|
+
default: seed.providerId,
|
|
961
|
+
readOnly: true,
|
|
962
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
963
|
+
}, {
|
|
964
|
+
'ui:readonly': true
|
|
965
|
+
});
|
|
966
|
+
addField('providerTitle', {
|
|
967
|
+
type: 'string',
|
|
968
|
+
title: 'Provider Title',
|
|
969
|
+
default: seed.title,
|
|
970
|
+
readOnly: true,
|
|
971
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
972
|
+
}, {
|
|
973
|
+
'ui:readonly': true
|
|
974
|
+
});
|
|
975
|
+
addField('providerUrl', {
|
|
976
|
+
type: 'string',
|
|
977
|
+
title: 'Provider URL',
|
|
978
|
+
default: seed.providerUrl,
|
|
979
|
+
readOnly: true,
|
|
980
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
981
|
+
}, {
|
|
982
|
+
'ui:readonly': true
|
|
983
|
+
});
|
|
984
|
+
addField('useCustomOAuthEndpoints', {
|
|
985
|
+
type: 'boolean',
|
|
986
|
+
title: 'Use Custom OAuth Endpoints',
|
|
987
|
+
default: seed.useCustomOAuthEndpoints ?? false,
|
|
988
|
+
readOnly: true,
|
|
989
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
990
|
+
}, {
|
|
991
|
+
'ui:readonly': true
|
|
992
|
+
});
|
|
993
|
+
addField('authorizationUrl', {
|
|
994
|
+
type: 'string',
|
|
995
|
+
title: 'Authorization URL',
|
|
996
|
+
default: seed.authorizationUrl || '',
|
|
997
|
+
readOnly: true,
|
|
998
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
999
|
+
}, {
|
|
1000
|
+
'ui:readonly': true
|
|
1001
|
+
});
|
|
1002
|
+
addField('tokenUrl', {
|
|
1003
|
+
type: 'string',
|
|
1004
|
+
title: 'Token URL',
|
|
1005
|
+
default: seed.tokenUrl || '',
|
|
1006
|
+
readOnly: true,
|
|
1007
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1008
|
+
}, {
|
|
1009
|
+
'ui:readonly': true
|
|
1010
|
+
});
|
|
1011
|
+
addField('revocationUrl', {
|
|
1012
|
+
type: 'string',
|
|
1013
|
+
title: 'Revocation URL',
|
|
1014
|
+
default: seed.revocationUrl || '',
|
|
1015
|
+
readOnly: true,
|
|
1016
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1017
|
+
}, {
|
|
1018
|
+
'ui:readonly': true
|
|
1019
|
+
});
|
|
1020
|
+
addField('useBasicAuth', {
|
|
1021
|
+
type: 'boolean',
|
|
1022
|
+
title: 'Use Basic Auth',
|
|
1023
|
+
default: seed.useBasicAuth ?? false,
|
|
1024
|
+
readOnly: true,
|
|
1025
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1026
|
+
}, {
|
|
1027
|
+
'ui:widget': 'hidden'
|
|
1028
|
+
});
|
|
1029
|
+
if (seed.clientAuthMethod) {
|
|
1030
|
+
addField('clientAuthMethod', {
|
|
1031
|
+
type: 'string',
|
|
1032
|
+
title: 'Client Auth Method',
|
|
1033
|
+
default: seed.clientAuthMethod,
|
|
1034
|
+
readOnly: true,
|
|
1035
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1036
|
+
}, {
|
|
1037
|
+
'ui:widget': 'hidden'
|
|
1038
|
+
});
|
|
1039
|
+
}
|
|
1040
|
+
addField('redirectUri', {
|
|
1041
|
+
type: 'string',
|
|
1042
|
+
title: 'Redirect URI',
|
|
1043
|
+
default: '{{CLIENT_URL}}/{{CONNECTOR_ENDPOINT}}',
|
|
1044
|
+
readOnly: true,
|
|
1045
|
+
scope: 2 /* ConfigurationScope.MACHINE */,
|
|
1046
|
+
defaultValueSource: {
|
|
1047
|
+
type: ContributeDefaultValueSourceType.Environment
|
|
1048
|
+
}
|
|
1049
|
+
}, {
|
|
1050
|
+
'ui:readonly': true
|
|
1051
|
+
});
|
|
1052
|
+
addField('scopes', {
|
|
1053
|
+
type: 'string',
|
|
1054
|
+
title: 'Scopes',
|
|
1055
|
+
default: seed.scopes,
|
|
1056
|
+
readOnly: true,
|
|
1057
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1058
|
+
}, {
|
|
1059
|
+
'ui:readonly': true,
|
|
1060
|
+
'ui:widget': 'chips'
|
|
1061
|
+
});
|
|
1062
|
+
addField('description', {
|
|
1063
|
+
type: 'string',
|
|
1064
|
+
title: 'Description',
|
|
1065
|
+
default: seed.description,
|
|
1066
|
+
readOnly: true,
|
|
1067
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1068
|
+
}, {
|
|
1069
|
+
'ui:readonly': true
|
|
1070
|
+
});
|
|
1071
|
+
addField('connectorType', {
|
|
1072
|
+
type: 'string',
|
|
1073
|
+
title: 'Connector Type',
|
|
1074
|
+
default: seed.connectorType,
|
|
1075
|
+
readOnly: true,
|
|
1076
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1077
|
+
}, {
|
|
1078
|
+
'ui:widget': 'hidden'
|
|
1079
|
+
});
|
|
1080
|
+
addField('mcpUrl', {
|
|
1081
|
+
type: 'string',
|
|
1082
|
+
title: 'MCP URL',
|
|
1083
|
+
default: seed.mcpUrl || '',
|
|
1084
|
+
readOnly: true,
|
|
1085
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1086
|
+
}, {
|
|
1087
|
+
'ui:readonly': true
|
|
1088
|
+
});
|
|
1089
|
+
addField('authMethod', {
|
|
1090
|
+
type: 'string',
|
|
1091
|
+
title: 'Auth Method',
|
|
1092
|
+
default: seedAuthMethodDefault(seed),
|
|
1093
|
+
readOnly: true,
|
|
1094
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1095
|
+
}, {});
|
|
1096
|
+
addField('mcpScopes', {
|
|
1097
|
+
type: 'string',
|
|
1098
|
+
title: 'MCP Scopes',
|
|
1099
|
+
default: seed.connectorType === 'mcp' ? seed.scopes : [],
|
|
1100
|
+
readOnly: true,
|
|
1101
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1102
|
+
}, {
|
|
1103
|
+
'ui:readonly': true,
|
|
1104
|
+
'ui:widget': 'chips'
|
|
1105
|
+
});
|
|
1106
|
+
addField('hideCredentials', {
|
|
1107
|
+
type: 'string',
|
|
1108
|
+
title: 'Hide Credentials',
|
|
1109
|
+
default: false,
|
|
1110
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1111
|
+
}, {
|
|
1112
|
+
'ui:widget': 'hidden'
|
|
1113
|
+
});
|
|
1114
|
+
addField('isConnected', {
|
|
1115
|
+
type: 'string',
|
|
1116
|
+
title: 'Connected',
|
|
1117
|
+
default: false,
|
|
1118
|
+
readOnly: true,
|
|
1119
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1120
|
+
}, {
|
|
1121
|
+
'ui:widget': 'hidden'
|
|
1122
|
+
});
|
|
1123
|
+
} else {
|
|
1124
|
+
// api_key-only: still need minimal sub-fields so the widget can render
|
|
1125
|
+
addField('provider', {
|
|
1126
|
+
type: 'string',
|
|
1127
|
+
title: 'Provider',
|
|
1128
|
+
default: seed.providerId,
|
|
1129
|
+
readOnly: true,
|
|
1130
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1131
|
+
}, {
|
|
1132
|
+
'ui:readonly': true
|
|
1133
|
+
});
|
|
1134
|
+
addField('providerTitle', {
|
|
1135
|
+
type: 'string',
|
|
1136
|
+
title: 'Provider Title',
|
|
1137
|
+
default: seed.title,
|
|
1138
|
+
readOnly: true,
|
|
1139
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1140
|
+
}, {
|
|
1141
|
+
'ui:readonly': true
|
|
1142
|
+
});
|
|
1143
|
+
addField('providerUrl', {
|
|
1144
|
+
type: 'string',
|
|
1145
|
+
title: 'Provider URL',
|
|
1146
|
+
default: seed.providerUrl,
|
|
1147
|
+
readOnly: true,
|
|
1148
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1149
|
+
}, {
|
|
1150
|
+
'ui:readonly': true
|
|
1151
|
+
});
|
|
1152
|
+
addField('description', {
|
|
1153
|
+
type: 'string',
|
|
1154
|
+
title: 'Description',
|
|
1155
|
+
default: seed.description,
|
|
1156
|
+
readOnly: true,
|
|
1157
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1158
|
+
}, {
|
|
1159
|
+
'ui:readonly': true
|
|
1160
|
+
});
|
|
1161
|
+
addField('connectorType', {
|
|
1162
|
+
type: 'string',
|
|
1163
|
+
title: 'Connector Type',
|
|
1164
|
+
default: seed.connectorType,
|
|
1165
|
+
readOnly: true,
|
|
1166
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1167
|
+
}, {
|
|
1168
|
+
'ui:widget': 'hidden'
|
|
1169
|
+
});
|
|
1170
|
+
addField('authMethod', {
|
|
1171
|
+
type: 'string',
|
|
1172
|
+
title: 'Auth Method',
|
|
1173
|
+
default: 'api_key',
|
|
1174
|
+
readOnly: true,
|
|
1175
|
+
scope: 2 /* ConfigurationScope.MACHINE */
|
|
1176
|
+
}, {});
|
|
1177
|
+
}
|
|
1178
|
+
// Custom isConfigField fields should also be written into configuration schema.
|
|
1179
|
+
const customFieldGenericCounts = buildGenericTypeCounts(seed.fields);
|
|
1180
|
+
for (const field of seed.fields) {
|
|
1181
|
+
if (!field.isConfigField) continue;
|
|
1182
|
+
const fieldPath = `${basePath}.${resolveFieldKey(field, customFieldGenericCounts)}`;
|
|
1183
|
+
const fieldDef = {
|
|
1184
|
+
type: field.type,
|
|
1185
|
+
title: field.label,
|
|
1186
|
+
description: field.description || '',
|
|
1187
|
+
fieldMeta: {
|
|
1188
|
+
addedFromStepperConfigToggle: true
|
|
1189
|
+
},
|
|
1190
|
+
required: field.required,
|
|
1191
|
+
scope: field.scope || 4 /* ConfigurationScope.RESOURCE */
|
|
1192
|
+
};
|
|
1193
|
+
if (field.isSecret && field.secretSource) {
|
|
1194
|
+
fieldDef.format = 'secret';
|
|
1195
|
+
fieldDef.writeOnly = true;
|
|
1196
|
+
fieldDef['x-env'] = {
|
|
1197
|
+
isEnvironmentVariable: true,
|
|
1198
|
+
vaultKeyName: field.secretSource.secretKey,
|
|
1199
|
+
secretType: field.secretSource.secretType
|
|
1200
|
+
};
|
|
1201
|
+
fieldDef.defaultValueSource = {
|
|
1202
|
+
type: field.secretSource.type,
|
|
1203
|
+
secretType: field.secretSource.secretType,
|
|
1204
|
+
secretKey: field.secretSource.secretKey
|
|
1205
|
+
};
|
|
1206
|
+
fieldDef.isEnvironmentVariable = true;
|
|
1207
|
+
fieldDef.secretType = field.secretSource.secretType;
|
|
1208
|
+
fieldDef.vaultKeyName = field.secretSource.secretKey;
|
|
1209
|
+
if (field.placeholder) fieldDef.placeholder = field.placeholder;
|
|
1210
|
+
}
|
|
1211
|
+
formProperties[fieldPath] = fieldDef;
|
|
1212
|
+
const uiEntry = {};
|
|
1213
|
+
if (field.uiWidget && field.uiWidget !== 'text') {
|
|
1214
|
+
uiEntry['ui:widget'] = field.uiWidget;
|
|
1215
|
+
}
|
|
1216
|
+
if (field.placeholder) {
|
|
1217
|
+
uiEntry['ui:placeholder'] = field.placeholder;
|
|
1218
|
+
}
|
|
1219
|
+
if (Object.keys(uiEntry).length > 0) {
|
|
1220
|
+
formUIProperties[fieldPath] = uiEntry;
|
|
1221
|
+
}
|
|
1222
|
+
// Ensure meta paths include this custom field path.
|
|
1223
|
+
const parts = fieldPath.split('.');
|
|
1224
|
+
for (let i = 1; i < parts.length; i++) {
|
|
1225
|
+
metaPaths.add(parts.slice(0, i).join('.'));
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
// Build meta entries for all parent paths
|
|
1230
|
+
const sortedMeta = Array.from(metaPaths).sort();
|
|
1231
|
+
for (const path of sortedMeta) {
|
|
1232
|
+
const parts = path.split('.');
|
|
1233
|
+
formProperties[`${path}.meta`] = {
|
|
1234
|
+
title: parts[parts.length - 1],
|
|
1235
|
+
description: '',
|
|
1236
|
+
type: 'object',
|
|
1237
|
+
position: 0
|
|
1238
|
+
};
|
|
1239
|
+
formProperties[`${path}.required`] = [];
|
|
1240
|
+
}
|
|
1241
|
+
return [{
|
|
1242
|
+
type: 'form-metadata',
|
|
1243
|
+
properties: {
|
|
1244
|
+
schemaId: 'configuration'
|
|
1245
|
+
}
|
|
1246
|
+
}, {
|
|
1247
|
+
type: 'form',
|
|
1248
|
+
properties: formProperties
|
|
1249
|
+
}, {
|
|
1250
|
+
type: 'formUI',
|
|
1251
|
+
properties: formUIProperties
|
|
1252
|
+
}];
|
|
1253
|
+
}
|
|
1254
|
+
function generateFromSeeds(seeds, title, formIcon, formExtensionId) {
|
|
1255
|
+
const allNodes = [];
|
|
1256
|
+
const mergedUiLayoutTemplates = {};
|
|
1257
|
+
const mergedUiLayoutUi = {};
|
|
1258
|
+
let uiLayoutFormMetadataNode = null;
|
|
1259
|
+
const seedsWithNamespaces = [];
|
|
1260
|
+
seeds.forEach((seed, idx) => {
|
|
1261
|
+
const generatedFormId = `${sanitizeFieldKey(seed.providerId)}-${seed.connectorType}-${Math.random().toString(36).substring(2, 10)}`;
|
|
1262
|
+
const providerTitle = `${seed.title} (${seed.connectorType.toUpperCase()})`;
|
|
1263
|
+
const namespace = generatedFormId;
|
|
1264
|
+
seedsWithNamespaces.push({
|
|
1265
|
+
seed,
|
|
1266
|
+
namespace
|
|
1267
|
+
});
|
|
1268
|
+
// ── Stepper-form: minimal form node with $ref to configuration ──
|
|
1269
|
+
seedIsApiKeyOnly(seed);
|
|
1270
|
+
const formProperties = {
|
|
1271
|
+
required: [],
|
|
1272
|
+
[`${STEP_KEY}.meta`]: {
|
|
1273
|
+
title: 'Step 1',
|
|
1274
|
+
description: '',
|
|
1275
|
+
type: 'object',
|
|
1276
|
+
position: 0
|
|
1277
|
+
},
|
|
1278
|
+
[`${STEP_KEY}.required`]: []
|
|
1279
|
+
};
|
|
1280
|
+
// oauthConnector $ref — always present so the widget renders for all forms
|
|
1281
|
+
formProperties[`${STEP_KEY}.oauthConnector`] = {
|
|
1282
|
+
$ref: `configuration#/properties/integrationSettings.settings.${seedTemplateType(seed)}.${namespace}.oauthConnector`
|
|
1283
|
+
};
|
|
1284
|
+
// Custom isConfigField fields are rendered inside the OAuthConnectorWidget
|
|
1285
|
+
// via connectorProviderFields — no standalone $refs needed in the stepper form.
|
|
1286
|
+
const formNode = {
|
|
1287
|
+
type: 'form',
|
|
1288
|
+
properties: formProperties
|
|
1289
|
+
};
|
|
1290
|
+
const formUINode = {
|
|
1291
|
+
type: 'formUI',
|
|
1292
|
+
properties: {
|
|
1293
|
+
[STEP_KEY]: {
|
|
1294
|
+
'ui:options': {}
|
|
1295
|
+
}
|
|
1296
|
+
}
|
|
1297
|
+
};
|
|
1298
|
+
const definitionsNode = {
|
|
1299
|
+
type: 'definitions',
|
|
1300
|
+
properties: {}
|
|
1301
|
+
};
|
|
1302
|
+
const connectionsNode = {
|
|
1303
|
+
type: 'connections',
|
|
1304
|
+
properties: {
|
|
1305
|
+
type: 'connections',
|
|
1306
|
+
'properties.steps.0': STEP_KEY,
|
|
1307
|
+
[`properties.transitions.${STEP_KEY}`]: {}
|
|
1308
|
+
}
|
|
1309
|
+
};
|
|
1310
|
+
const buttonsNode = buildCustomButtonsNode(seed);
|
|
1311
|
+
const resourcesNode = buildResourcesNode(seed);
|
|
1312
|
+
const inngestNode = buildInngestNode();
|
|
1313
|
+
const formDataNode = {
|
|
1314
|
+
type: 'formData',
|
|
1315
|
+
properties: {}
|
|
1316
|
+
};
|
|
1317
|
+
// form-metadata goes LAST (matches manual GUI pattern)
|
|
1318
|
+
const formMetadataNode = {
|
|
1319
|
+
type: 'form-metadata',
|
|
1320
|
+
properties: {
|
|
1321
|
+
title: providerTitle,
|
|
1322
|
+
formIcon: seed.formIcon || '',
|
|
1323
|
+
id: generatedFormId,
|
|
1324
|
+
category: seedCategory(seed),
|
|
1325
|
+
preGeneratedFormSteps: buildPreGeneratedFormSteps(seed, namespace)
|
|
1326
|
+
}
|
|
1327
|
+
};
|
|
1328
|
+
// Emit nodes: form-metadata FIRST (parseSampleNodes splits groups at form-metadata boundaries)
|
|
1329
|
+
allNodes.push(formMetadataNode, formNode, formUINode);
|
|
1330
|
+
allNodes.push(definitionsNode, connectionsNode);
|
|
1331
|
+
allNodes.push(buttonsNode || {
|
|
1332
|
+
type: 'customButtons',
|
|
1333
|
+
properties: {}
|
|
1334
|
+
});
|
|
1335
|
+
allNodes.push(resourcesNode || {
|
|
1336
|
+
type: 'resources',
|
|
1337
|
+
properties: {}
|
|
1338
|
+
});
|
|
1339
|
+
allNodes.push(inngestNode);
|
|
1340
|
+
allNodes.push(formDataNode);
|
|
1341
|
+
// Build merged uiLayout (single record)
|
|
1342
|
+
// Only build the metadata node once
|
|
1343
|
+
if (!uiLayoutFormMetadataNode) {
|
|
1344
|
+
uiLayoutFormMetadataNode = {
|
|
1345
|
+
type: 'form-metadata',
|
|
1346
|
+
properties: {
|
|
1347
|
+
id: 'uilayout-oauth',
|
|
1348
|
+
title: 'uiLayout',
|
|
1349
|
+
schemaId: 'uiLayout',
|
|
1350
|
+
contributionPoint: 'credential',
|
|
1351
|
+
namespace: 'templates'
|
|
1352
|
+
}
|
|
1353
|
+
};
|
|
1354
|
+
}
|
|
1355
|
+
// Add to merged templates — key must match formId (same pattern as commonApiSample)
|
|
1356
|
+
const hasDualAuth = seed.connectorType === 'app' && seed.authMethods.includes('api_key') && seed.authMethods.includes('oauth2');
|
|
1357
|
+
const templateProperties = {
|
|
1358
|
+
templateName: {
|
|
1359
|
+
type: 'string',
|
|
1360
|
+
title: 'templateName',
|
|
1361
|
+
default: providerTitle,
|
|
1362
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1363
|
+
},
|
|
1364
|
+
projectType: {
|
|
1365
|
+
type: 'string',
|
|
1366
|
+
title: 'projectType',
|
|
1367
|
+
default: seed.connectorType === 'mcp' ? 'MCP' : !seed.authMethods.includes('oauth2') && (seed.authMethods.includes('api_key') || seed.authMethods.includes('bearer_token')) ? 'API' : 'OAuth',
|
|
1368
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1369
|
+
},
|
|
1370
|
+
description: {
|
|
1371
|
+
type: 'string',
|
|
1372
|
+
title: 'description',
|
|
1373
|
+
default: seed.description || `${seed.title} connector template`,
|
|
1374
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1375
|
+
},
|
|
1376
|
+
tags: {
|
|
1377
|
+
type: 'array',
|
|
1378
|
+
title: 'tags',
|
|
1379
|
+
items: {
|
|
1380
|
+
type: 'string'
|
|
1381
|
+
},
|
|
1382
|
+
uniqueItems: true,
|
|
1383
|
+
default: [seed.providerId, seed.connectorType, 'connector'],
|
|
1384
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1385
|
+
},
|
|
1386
|
+
keywords: {
|
|
1387
|
+
type: 'array',
|
|
1388
|
+
title: 'keywords',
|
|
1389
|
+
items: {
|
|
1390
|
+
type: 'string'
|
|
1391
|
+
},
|
|
1392
|
+
uniqueItems: true,
|
|
1393
|
+
default: seed.connectorType === 'mcp' ? [seed.providerId, 'mcp', seed.authMethods.includes('oauth2') ? 'oauth' : 'api-key'] : !seed.authMethods.includes('oauth2') && seed.authMethods.includes('api_key') ? [seed.providerId, 'api', 'api-key'] : [seed.providerId, seed.connectorType, 'oauth widget'],
|
|
1394
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1395
|
+
},
|
|
1396
|
+
formId: {
|
|
1397
|
+
type: 'string',
|
|
1398
|
+
title: 'formId',
|
|
1399
|
+
default: generatedFormId,
|
|
1400
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1401
|
+
},
|
|
1402
|
+
formExtensionId: {
|
|
1403
|
+
type: 'string',
|
|
1404
|
+
title: 'formExtensionId',
|
|
1405
|
+
default: formExtensionId,
|
|
1406
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1407
|
+
},
|
|
1408
|
+
formIcon: {
|
|
1409
|
+
type: 'string',
|
|
1410
|
+
title: 'formIcon',
|
|
1411
|
+
default: seed.formIcon || '',
|
|
1412
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1413
|
+
}
|
|
1414
|
+
};
|
|
1415
|
+
if (hasDualAuth) {
|
|
1416
|
+
templateProperties.apiKeyEnabled = {
|
|
1417
|
+
type: 'boolean',
|
|
1418
|
+
title: 'apiKeyEnabled',
|
|
1419
|
+
default: true,
|
|
1420
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1421
|
+
};
|
|
1422
|
+
templateProperties.oAuthEnabled = {
|
|
1423
|
+
type: 'boolean',
|
|
1424
|
+
title: 'oAuthEnabled',
|
|
1425
|
+
default: false,
|
|
1426
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1427
|
+
};
|
|
1428
|
+
} else {
|
|
1429
|
+
// If connectorType is 'app' and only oauth2 is present, enabled should be false
|
|
1430
|
+
const isAppOnlyOauth = seed.connectorType === 'app' && seed.authMethods.length === 1 && seed.authMethods[0] === 'oauth2';
|
|
1431
|
+
templateProperties.enabled = {
|
|
1432
|
+
type: 'boolean',
|
|
1433
|
+
title: 'enabled',
|
|
1434
|
+
default: !isAppOnlyOauth,
|
|
1435
|
+
scope: 3 /* ConfigurationScope.WINDOW */
|
|
1436
|
+
};
|
|
1437
|
+
}
|
|
1438
|
+
mergedUiLayoutTemplates[generatedFormId] = {
|
|
1439
|
+
type: 'object',
|
|
1440
|
+
title: providerTitle,
|
|
1441
|
+
properties: templateProperties,
|
|
1442
|
+
required: []
|
|
1443
|
+
};
|
|
1444
|
+
const templateUi = {
|
|
1445
|
+
templateName: {
|
|
1446
|
+
'ui:widget': 'text'
|
|
1447
|
+
},
|
|
1448
|
+
projectType: {
|
|
1449
|
+
'ui:widget': 'text'
|
|
1450
|
+
},
|
|
1451
|
+
description: {
|
|
1452
|
+
'ui:widget': 'textarea',
|
|
1453
|
+
'ui:options': {
|
|
1454
|
+
rows: 2
|
|
1455
|
+
}
|
|
1456
|
+
},
|
|
1457
|
+
tags: {
|
|
1458
|
+
'ui:widget': 'chips',
|
|
1459
|
+
'ui:options': {
|
|
1460
|
+
label: false
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
keywords: {
|
|
1464
|
+
'ui:widget': 'chips',
|
|
1465
|
+
'ui:options': {
|
|
1466
|
+
label: false
|
|
1467
|
+
}
|
|
1468
|
+
},
|
|
1469
|
+
formId: {
|
|
1470
|
+
'ui:readonly': true
|
|
1471
|
+
},
|
|
1472
|
+
formExtensionId: {
|
|
1473
|
+
'ui:readonly': true
|
|
1474
|
+
},
|
|
1475
|
+
formIcon: {
|
|
1476
|
+
'ui:widget': 'text'
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
if (hasDualAuth) {
|
|
1480
|
+
templateUi.apiKeyEnabled = {
|
|
1481
|
+
'ui:widget': 'switch'
|
|
1482
|
+
};
|
|
1483
|
+
templateUi.oAuthEnabled = {
|
|
1484
|
+
'ui:widget': 'switch'
|
|
1485
|
+
};
|
|
1486
|
+
} else {
|
|
1487
|
+
templateUi.enabled = {
|
|
1488
|
+
'ui:widget': 'switch'
|
|
1489
|
+
};
|
|
1490
|
+
}
|
|
1491
|
+
mergedUiLayoutUi[generatedFormId] = templateUi;
|
|
1492
|
+
});
|
|
1493
|
+
// Compose a single flattened uiLayout schema record for all providers, with '{credentialPath}.{category}.' prefix
|
|
1494
|
+
// Group by connector category so each provider lands under its actual category key
|
|
1495
|
+
const credentialBasePath = CONTRIBUTION_POINTS['uiLayout.credential.path'];
|
|
1496
|
+
const prefixedTemplates = {};
|
|
1497
|
+
const prefixedUi = {};
|
|
1498
|
+
// Build a namespace→category lookup from seeds using contribution-point template type keys
|
|
1499
|
+
const nsToCategoryMap = new Map();
|
|
1500
|
+
seedsWithNamespaces.forEach(({
|
|
1501
|
+
seed,
|
|
1502
|
+
namespace
|
|
1503
|
+
}) => {
|
|
1504
|
+
nsToCategoryMap.set(namespace, categoryToTemplateTypeKey(seedCategory(seed)));
|
|
1505
|
+
});
|
|
1506
|
+
Object.keys(mergedUiLayoutTemplates).forEach(key => {
|
|
1507
|
+
const categoryKey = nsToCategoryMap.get(key);
|
|
1508
|
+
if (!categoryKey) throw new Error(`Missing category mapping for namespace "${key}"`);
|
|
1509
|
+
prefixedTemplates[`${credentialBasePath}.${categoryKey}.${key}`] = mergedUiLayoutTemplates[key];
|
|
1510
|
+
});
|
|
1511
|
+
Object.keys(mergedUiLayoutUi).forEach(key => {
|
|
1512
|
+
const categoryKey = nsToCategoryMap.get(key);
|
|
1513
|
+
if (!categoryKey) throw new Error(`Missing category mapping for namespace "${key}"`);
|
|
1514
|
+
prefixedUi[`${credentialBasePath}.${categoryKey}.${key}`] = mergedUiLayoutUi[key];
|
|
1515
|
+
});
|
|
1516
|
+
const uiLayoutSchema = {
|
|
1517
|
+
type: 'object',
|
|
1518
|
+
properties: {
|
|
1519
|
+
...prefixedTemplates
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
const uiLayoutUiSchema = {
|
|
1523
|
+
...prefixedUi
|
|
1524
|
+
};
|
|
1525
|
+
const flattenedUiLayout = flattenSchemas({
|
|
1526
|
+
formSchema: uiLayoutSchema,
|
|
1527
|
+
uiSchema: uiLayoutUiSchema
|
|
1528
|
+
});
|
|
1529
|
+
const uiLayoutFormNode = {
|
|
1530
|
+
type: 'form',
|
|
1531
|
+
properties: flattenedUiLayout.formSchema || {}
|
|
1532
|
+
};
|
|
1533
|
+
const uiLayoutFormUINode = {
|
|
1534
|
+
type: 'formUI',
|
|
1535
|
+
properties: flattenedUiLayout.uiSchema || {}
|
|
1536
|
+
};
|
|
1537
|
+
// Merged configuration contribution (ALWAYS emitted)
|
|
1538
|
+
allNodes.push(...buildOAuthConfigurationContribution(seedsWithNamespaces));
|
|
1539
|
+
if (!uiLayoutFormMetadataNode) return allNodes;
|
|
1540
|
+
return [...allNodes, uiLayoutFormMetadataNode, uiLayoutFormNode, uiLayoutFormUINode];
|
|
1541
|
+
}
|
|
1542
|
+
function createOAuthSeedGenerator(seeds) {
|
|
1543
|
+
return (title = '', formIcon = '', formExtensionId = '') => generateFromSeeds(seeds, title, formIcon, formExtensionId);
|
|
1544
|
+
}
|
|
1545
|
+
// create provider refs
|
|
1546
|
+
const findRefPath = node => {
|
|
1547
|
+
if (!node || typeof node !== 'object') return null;
|
|
1548
|
+
if (typeof node.$ref === 'string') return node.$ref;
|
|
1549
|
+
for (const value of Object.values(node)) {
|
|
1550
|
+
if (Array.isArray(value)) {
|
|
1551
|
+
for (const entry of value) {
|
|
1552
|
+
const found = findRefPath(entry);
|
|
1553
|
+
if (found) return found;
|
|
1554
|
+
}
|
|
1555
|
+
} else if (value && typeof value === 'object') {
|
|
1556
|
+
const found = findRefPath(value);
|
|
1557
|
+
if (found) return found;
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
return null;
|
|
1561
|
+
};
|
|
1562
|
+
const buildProviderWithRefs = (provider, sampleNodes) => {
|
|
1563
|
+
const refPath = findRefPath({
|
|
1564
|
+
nodes: sampleNodes
|
|
1565
|
+
});
|
|
1566
|
+
const refMatch = refPath && /integrationSettings\.settings\.([^.]+)\.([^.]+)\.oauthConnector/.exec(refPath);
|
|
1567
|
+
const templateType = refMatch?.[1];
|
|
1568
|
+
const namespace = refMatch?.[2];
|
|
1569
|
+
const basePath = templateType && namespace ? `integrationSettings.settings.${templateType}.${namespace}.oauthConnector` : null;
|
|
1570
|
+
if (!basePath) return provider;
|
|
1571
|
+
const providerFieldGenericCounts = buildGenericTypeCounts(provider.fields);
|
|
1572
|
+
return {
|
|
1573
|
+
...provider,
|
|
1574
|
+
fields: provider.fields.map(field => {
|
|
1575
|
+
if (!field.isConfigField) return field;
|
|
1576
|
+
return {
|
|
1577
|
+
...field,
|
|
1578
|
+
keyReference: `${basePath}.${resolveFieldKey(field, providerFieldGenericCounts)}`,
|
|
1579
|
+
schemaReference: 'configuration'
|
|
1580
|
+
};
|
|
1581
|
+
})
|
|
1582
|
+
};
|
|
1583
|
+
};
|
|
1584
|
+
const buildMetadataSchema = sampleNodes => {
|
|
1585
|
+
const parsed = parseSampleNodes(sampleNodes, 'stepper-form', 'Multi Form');
|
|
1586
|
+
const mainNodes = parsed.mainMetadataNode ? [parsed.mainMetadataNode, ...parsed.mainNodes] : parsed.mainNodes;
|
|
1587
|
+
return [{
|
|
1588
|
+
schemaId: 'stepper-form',
|
|
1589
|
+
formId: parsed.mainMetadataNode?.properties?.id || '',
|
|
1590
|
+
integrationConfigurationId: null,
|
|
1591
|
+
type: 'Multi Form',
|
|
1592
|
+
configurationNodes: mainNodes
|
|
1593
|
+
}, ...parsed.additionalSchemas.map(entry => ({
|
|
1594
|
+
schemaId: entry.schemaId,
|
|
1595
|
+
formId: entry.formId || '',
|
|
1596
|
+
integrationConfigurationId: null,
|
|
1597
|
+
type: entry.type || 'Contribution',
|
|
1598
|
+
configurationNodes: entry.configurationNodes,
|
|
1599
|
+
contributes: {}
|
|
1600
|
+
}))];
|
|
1601
|
+
};export{buildMetadataSchema,buildProviderConfig,buildProviderSeedFromConfig,buildProviderWithRefs,createOAuthSeedGenerator};//# sourceMappingURL=authMetaDataSchema.js.map
|