ui: extract wizard model helpers
This commit is contained in:
@@ -0,0 +1,528 @@
|
|||||||
|
function inferSchemaKind(typeName) {
|
||||||
|
if (typeName === 'object') return 'object';
|
||||||
|
if (typeName === 'array') return 'array';
|
||||||
|
if (typeName === 'string') return 'string';
|
||||||
|
if (typeName === 'integer') return 'integer';
|
||||||
|
if (typeName === 'number') return 'number';
|
||||||
|
if (typeName === 'boolean') return 'boolean';
|
||||||
|
if (typeName === 'null') return 'null';
|
||||||
|
return 'string';
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertJsonSchemaToCrankSchema(node, requiredNames) {
|
||||||
|
var schemaNode = node || {};
|
||||||
|
var typeName = Array.isArray(schemaNode.type)
|
||||||
|
? schemaNode.type.find(function(value) { return value !== 'null'; }) || 'string'
|
||||||
|
: schemaNode.type || (schemaNode.properties ? 'object' : 'string');
|
||||||
|
var nullable = Array.isArray(schemaNode.type) && schemaNode.type.indexOf('null') >= 0;
|
||||||
|
var kind = schemaNode.enum ? 'enum' : inferSchemaKind(typeName);
|
||||||
|
var schema = {
|
||||||
|
type: kind,
|
||||||
|
description: schemaNode.description || null,
|
||||||
|
required: false,
|
||||||
|
nullable: nullable,
|
||||||
|
default_value: Object.prototype.hasOwnProperty.call(schemaNode, 'default') ? schemaNode.default : null,
|
||||||
|
fields: {},
|
||||||
|
items: null,
|
||||||
|
enum_values: schemaNode.enum || [],
|
||||||
|
variants: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
if (kind === 'object') {
|
||||||
|
var requiredSet = new Set(schemaNode.required || requiredNames || []);
|
||||||
|
Object.keys(schemaNode.properties || {}).forEach(function(fieldName) {
|
||||||
|
var field = convertJsonSchemaToCrankSchema(schemaNode.properties[fieldName], []);
|
||||||
|
field.required = requiredSet.has(fieldName);
|
||||||
|
schema.fields[fieldName] = field;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (kind === 'array' && schemaNode.items) {
|
||||||
|
schema.items = convertJsonSchemaToCrankSchema(schemaNode.items, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
return schema;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mappingRootForProtocol(protocol) {
|
||||||
|
if (protocol === 'graphql') return '$.request.variables';
|
||||||
|
if (protocol === 'grpc') return '$.request.message';
|
||||||
|
if (protocol === 'soap') return '$.request.body';
|
||||||
|
if (protocol === 'websocket') return '$.request.body';
|
||||||
|
return '$.request.body';
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeInputSource(path) {
|
||||||
|
if (!path) return '$.input';
|
||||||
|
if (path === '$') return '$.input';
|
||||||
|
if (path.indexOf('$.input') === 0) return path;
|
||||||
|
return '$.input.' + path.replace(/^\$\./, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeOutputSource(path) {
|
||||||
|
if (!path) return '$.response.body';
|
||||||
|
if (path.indexOf('$.') === 0) return path;
|
||||||
|
return '$.' + path.replace(/^\$\./, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildMappingSet(rawValue, mode) {
|
||||||
|
var value = rawValue || {};
|
||||||
|
var root = mappingRootForProtocol(wizardProtocol);
|
||||||
|
var rules = [];
|
||||||
|
|
||||||
|
Object.keys(value).forEach(function(target) {
|
||||||
|
var source = value[target];
|
||||||
|
if (mode === 'input') {
|
||||||
|
rules.push({
|
||||||
|
source: normalizeInputSource(source),
|
||||||
|
target: root + '.' + target,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
rules.push({
|
||||||
|
source: normalizeOutputSource(source),
|
||||||
|
target: '$.output.' + target,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return { rules: rules };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseExecutionConfig(text) {
|
||||||
|
var value = parseStructuredText(text);
|
||||||
|
var retry = value.retry || {};
|
||||||
|
var headers = value.headers && typeof value.headers === 'object' ? value.headers : {};
|
||||||
|
var selectedUpstream = currentUpstream();
|
||||||
|
if (!selectedUpstream && wizardMode !== 'edit') {
|
||||||
|
throw new Error(tKey('wizard.error.save_upstream_first'));
|
||||||
|
}
|
||||||
|
var authProfileRef = selectedUpstream && selectedUpstream.authProfileId
|
||||||
|
? selectedUpstream.authProfileId
|
||||||
|
: (value.auth && value.auth.profile ? value.auth.profile : null);
|
||||||
|
var config = {
|
||||||
|
timeout_ms: Number(value.timeout_ms || 10000),
|
||||||
|
retry_policy: retry && retry.max_attempts ? { max_attempts: Number(retry.max_attempts) } : null,
|
||||||
|
auth_profile_ref: authProfileRef,
|
||||||
|
headers: headers,
|
||||||
|
protocol_options: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (wizardProtocol === 'grpc') {
|
||||||
|
var useTls = false;
|
||||||
|
if (value.protocol_options && value.protocol_options.grpc) {
|
||||||
|
useTls = !!value.protocol_options.grpc.use_tls;
|
||||||
|
} else if (value.tls) {
|
||||||
|
useTls = !!value.tls.verify;
|
||||||
|
}
|
||||||
|
config.protocol_options = { grpc: { use_tls: useTls } };
|
||||||
|
} else if (wizardProtocol === 'websocket') {
|
||||||
|
var websocketProtocolOptions = value.protocol_options && value.protocol_options.websocket
|
||||||
|
? value.protocol_options.websocket
|
||||||
|
: {};
|
||||||
|
var heartbeatInterval = websocketProtocolOptions.heartbeat_interval_ms != null
|
||||||
|
? Number(websocketProtocolOptions.heartbeat_interval_ms)
|
||||||
|
: numericValueOrNull('websocket-heartbeat-interval-ms');
|
||||||
|
var reconnectAttempts = websocketProtocolOptions.reconnect_max_attempts != null
|
||||||
|
? Number(websocketProtocolOptions.reconnect_max_attempts)
|
||||||
|
: numericValueOrNull('websocket-reconnect-max-attempts');
|
||||||
|
var reconnectBackoff = websocketProtocolOptions.reconnect_backoff_ms != null
|
||||||
|
? Number(websocketProtocolOptions.reconnect_backoff_ms)
|
||||||
|
: numericValueOrNull('websocket-reconnect-backoff-ms');
|
||||||
|
var runtimeOptions = {};
|
||||||
|
if (Number.isFinite(heartbeatInterval) && heartbeatInterval > 0) {
|
||||||
|
runtimeOptions.heartbeat_interval_ms = heartbeatInterval;
|
||||||
|
}
|
||||||
|
if (Number.isFinite(reconnectAttempts) && reconnectAttempts >= 0) {
|
||||||
|
runtimeOptions.reconnect_max_attempts = reconnectAttempts;
|
||||||
|
}
|
||||||
|
if (Number.isFinite(reconnectBackoff) && reconnectBackoff >= 0) {
|
||||||
|
runtimeOptions.reconnect_backoff_ms = reconnectBackoff;
|
||||||
|
}
|
||||||
|
if (Object.keys(runtimeOptions).length) {
|
||||||
|
config.protocol_options = { websocket: runtimeOptions };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
config.streaming = collectStreamingConfig();
|
||||||
|
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
|
function currentUpstream() {
|
||||||
|
var selected = upstreams.find(function(item) { return item.id === selectedUpstreamId; });
|
||||||
|
if (selected) return selected;
|
||||||
|
|
||||||
|
var customName = textValue('new-upstream-name');
|
||||||
|
var customUrl = textValue('new-upstream-url');
|
||||||
|
if (!customUrl && wizardProtocol === 'soap') {
|
||||||
|
customUrl = textValue('soap-endpoint-override');
|
||||||
|
}
|
||||||
|
if (!customUrl) return null;
|
||||||
|
|
||||||
|
var authMode = textValue('new-upstream-auth-mode') || 'none';
|
||||||
|
var authProfileId = authMode === 'existing' ? textValue('new-upstream-auth-profile') : null;
|
||||||
|
var authProfile = authProfileById(authProfileId);
|
||||||
|
return {
|
||||||
|
id: 'custom',
|
||||||
|
name: customName || 'custom-upstream',
|
||||||
|
url: customUrl,
|
||||||
|
staticHeaders: textValue('new-upstream-static-headers') || '{\n}',
|
||||||
|
authMode: authMode,
|
||||||
|
authProfileId: authProfileId || null,
|
||||||
|
authProfileName: authProfile ? authProfile.name : '',
|
||||||
|
authKind: authProfile ? authProfile.kind : null,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function joinUrl(baseUrl, path) {
|
||||||
|
if (!baseUrl) return path || '';
|
||||||
|
if (!path) return baseUrl;
|
||||||
|
return baseUrl.replace(/\/+$/, '') + '/' + path.replace(/^\/+/, '');
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectedGraphqlOperationType() {
|
||||||
|
var active = document.querySelector('.gql-type-card.active');
|
||||||
|
return active ? active.dataset.gqlType : 'query';
|
||||||
|
}
|
||||||
|
|
||||||
|
function graphqlTopLevelField(queryText) {
|
||||||
|
var match = queryText.replace(/#[^\n]*/g, '').match(/\{\s*([A-Za-z_][A-Za-z0-9_]*)/);
|
||||||
|
return match ? match[1] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function graphqlOperationName(queryText) {
|
||||||
|
var match = queryText.match(/\b(query|mutation)\s+([A-Za-z_][A-Za-z0-9_]*)/);
|
||||||
|
return match ? match[2] : textValue('tool-name');
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseGrpcRoute(route) {
|
||||||
|
var normalized = route.replace(/^\/+/, '');
|
||||||
|
var parts = normalized.split('/');
|
||||||
|
if (parts.length !== 2) {
|
||||||
|
return {
|
||||||
|
package: '',
|
||||||
|
service: '',
|
||||||
|
method: '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var servicePart = parts[0];
|
||||||
|
var method = parts[1];
|
||||||
|
var serviceSegments = servicePart.split('.');
|
||||||
|
var service = serviceSegments.pop() || '';
|
||||||
|
var packageName = serviceSegments.join('.');
|
||||||
|
|
||||||
|
return {
|
||||||
|
package: packageName,
|
||||||
|
service: service,
|
||||||
|
method: method,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseHeaderMap(text) {
|
||||||
|
if (!text || !text.trim()) return {};
|
||||||
|
var value = parseStructuredText(text);
|
||||||
|
return value && typeof value === 'object' ? value : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
function numericValueOrNull(id) {
|
||||||
|
var value = textValue(id);
|
||||||
|
if (!value) return null;
|
||||||
|
var parsed = Number(value);
|
||||||
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringValueOrNull(id) {
|
||||||
|
var value = textValue(id);
|
||||||
|
return value ? value : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function textareaLines(id) {
|
||||||
|
var value = textValue(id);
|
||||||
|
if (!value) return [];
|
||||||
|
return value
|
||||||
|
.split(/\r?\n/)
|
||||||
|
.map(function(line) { return line.trim(); })
|
||||||
|
.filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildTarget() {
|
||||||
|
var upstream = currentUpstream();
|
||||||
|
if (!upstream || !upstream.url) {
|
||||||
|
throw new Error(tKey('wizard.error.select_upstream'));
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathTemplate = textValue('endpoint-path') || '/';
|
||||||
|
|
||||||
|
if (wizardProtocol === 'rest') {
|
||||||
|
var activeMethod = document.querySelector('.method-card.active');
|
||||||
|
return {
|
||||||
|
kind: 'rest',
|
||||||
|
base_url: upstream.url,
|
||||||
|
method: activeMethod ? activeMethod.dataset.method : 'POST',
|
||||||
|
path_template: pathTemplate,
|
||||||
|
static_headers: parseHeaderMap(upstream.staticHeaders),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wizardProtocol === 'graphql') {
|
||||||
|
var queryTemplate = textValue('gql-query-editor');
|
||||||
|
var topField = graphqlTopLevelField(queryTemplate);
|
||||||
|
return {
|
||||||
|
kind: 'graphql',
|
||||||
|
endpoint: joinUrl(upstream.url, pathTemplate),
|
||||||
|
operation_type: selectedGraphqlOperationType(),
|
||||||
|
operation_name: graphqlOperationName(queryTemplate),
|
||||||
|
query_template: queryTemplate,
|
||||||
|
response_path: topField ? '$.response.body.data.' + topField : '$.response.body.data',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wizardProtocol === 'soap') {
|
||||||
|
var endpointOverride = textValue('soap-endpoint-override') || upstream.url;
|
||||||
|
var existingWsdlRef = wizardCurrentVersion && wizardCurrentVersion.target && wizardCurrentVersion.target.kind === 'soap'
|
||||||
|
? wizardCurrentVersion.target.wsdl_ref
|
||||||
|
: 'sample_wsdl_pending';
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: 'soap',
|
||||||
|
wsdl_ref: existingWsdlRef,
|
||||||
|
service_name: textValue('soap-service-name') || 'Service',
|
||||||
|
port_name: textValue('soap-port-name') || 'Port',
|
||||||
|
operation_name: textValue('soap-operation-name') || 'Operation',
|
||||||
|
endpoint_override: endpointOverride || null,
|
||||||
|
soap_version: textValue('soap-version') || 'soap_11',
|
||||||
|
soap_action: textValue('soap-action') || null,
|
||||||
|
binding_style: textValue('soap-binding-style') || 'document_literal',
|
||||||
|
headers: [],
|
||||||
|
fault_contract: null,
|
||||||
|
metadata: { input_part_names: [], output_part_names: [], namespaces: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wizardProtocol === 'websocket') {
|
||||||
|
return {
|
||||||
|
kind: 'websocket',
|
||||||
|
url: joinUrl(upstream.url, textValue('websocket-path')),
|
||||||
|
subprotocols: textareaLines('websocket-subprotocols'),
|
||||||
|
subscribe_message_template: stringValueOrNull('websocket-subscribe-template')
|
||||||
|
? parseStructuredText(textValue('websocket-subscribe-template'))
|
||||||
|
: null,
|
||||||
|
unsubscribe_message_template: stringValueOrNull('websocket-unsubscribe-template')
|
||||||
|
? parseStructuredText(textValue('websocket-unsubscribe-template'))
|
||||||
|
: null,
|
||||||
|
static_headers: parseHeaderMap(upstream.staticHeaders),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
var route = textValue('grpc-manual-route') || (document.getElementById('grpc-path-value') ? document.getElementById('grpc-path-value').textContent.trim() : '');
|
||||||
|
var descriptorRef = wizardCurrentVersion && wizardCurrentVersion.target && wizardCurrentVersion.target.kind === 'grpc'
|
||||||
|
? wizardCurrentVersion.target.descriptor_ref
|
||||||
|
: 'desc_pending';
|
||||||
|
var descriptorSetB64 = wizardCurrentVersion && wizardCurrentVersion.target && wizardCurrentVersion.target.kind === 'grpc'
|
||||||
|
? (wizardCurrentVersion.target.descriptor_set_b64 || '')
|
||||||
|
: '';
|
||||||
|
var parsedRoute = parseGrpcRoute(route);
|
||||||
|
|
||||||
|
return {
|
||||||
|
kind: 'grpc',
|
||||||
|
server_addr: upstream.url,
|
||||||
|
package: parsedRoute.package,
|
||||||
|
service: parsedRoute.service,
|
||||||
|
method: parsedRoute.method,
|
||||||
|
descriptor_ref: descriptorRef,
|
||||||
|
descriptor_set_b64: descriptorSetB64,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function crankSchemaToJsonSchema(schema) {
|
||||||
|
var result = {};
|
||||||
|
var typeName = schema.type === 'enum' ? 'string' : schema.type;
|
||||||
|
result.type = schema.nullable ? [typeName, 'null'] : typeName;
|
||||||
|
if (schema.description) result.description = schema.description;
|
||||||
|
if (schema.default_value !== null && schema.default_value !== undefined) result.default = schema.default_value;
|
||||||
|
if (schema.enum_values && schema.enum_values.length) result.enum = schema.enum_values.slice();
|
||||||
|
if (schema.type === 'object') {
|
||||||
|
result.type = schema.nullable ? ['object', 'null'] : 'object';
|
||||||
|
result.properties = {};
|
||||||
|
var required = [];
|
||||||
|
Object.keys(schema.fields || {}).forEach(function(fieldName) {
|
||||||
|
var field = schema.fields[fieldName];
|
||||||
|
result.properties[fieldName] = crankSchemaToJsonSchema(field);
|
||||||
|
if (field.required) required.push(fieldName);
|
||||||
|
});
|
||||||
|
if (required.length) result.required = required;
|
||||||
|
result.additionalProperties = false;
|
||||||
|
}
|
||||||
|
if (schema.type === 'array' && schema.items) {
|
||||||
|
result.items = crankSchemaToJsonSchema(schema.items);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function mappingSetToEditorValue(mappingSet, mode, protocol) {
|
||||||
|
var rules = mappingSet && mappingSet.rules ? mappingSet.rules : [];
|
||||||
|
var object = {};
|
||||||
|
rules.forEach(function(rule) {
|
||||||
|
if (mode === 'input') {
|
||||||
|
var key = rule.target.replace(mappingRootForProtocol(protocol) + '.', '');
|
||||||
|
object[key] = rule.source.replace('$.mcp.', '$.input.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var outputKey = rule.target.replace('$.output.', '');
|
||||||
|
object[outputKey] = rule.source;
|
||||||
|
});
|
||||||
|
return window.jsyaml ? window.jsyaml.dump(object, { lineWidth: -1 }) : JSON.stringify(object, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function executionConfigToEditorValue(config) {
|
||||||
|
var value = {
|
||||||
|
timeout_ms: config.timeout_ms,
|
||||||
|
};
|
||||||
|
if (config.retry_policy) {
|
||||||
|
value.retry = { max_attempts: config.retry_policy.max_attempts };
|
||||||
|
}
|
||||||
|
if (config.auth_profile_ref) {
|
||||||
|
value.auth = { profile: config.auth_profile_ref };
|
||||||
|
}
|
||||||
|
if (config.headers && Object.keys(config.headers).length) {
|
||||||
|
value.headers = config.headers;
|
||||||
|
}
|
||||||
|
if (config.protocol_options && config.protocol_options.grpc) {
|
||||||
|
value.tls = { verify: !!config.protocol_options.grpc.use_tls };
|
||||||
|
}
|
||||||
|
if (config.protocol_options && config.protocol_options.websocket) {
|
||||||
|
value.protocol_options = value.protocol_options || {};
|
||||||
|
value.protocol_options.websocket = {};
|
||||||
|
if (config.protocol_options.websocket.heartbeat_interval_ms != null) {
|
||||||
|
value.protocol_options.websocket.heartbeat_interval_ms = config.protocol_options.websocket.heartbeat_interval_ms;
|
||||||
|
}
|
||||||
|
if (config.protocol_options.websocket.reconnect_max_attempts != null) {
|
||||||
|
value.protocol_options.websocket.reconnect_max_attempts = config.protocol_options.websocket.reconnect_max_attempts;
|
||||||
|
}
|
||||||
|
if (config.protocol_options.websocket.reconnect_backoff_ms != null) {
|
||||||
|
value.protocol_options.websocket.reconnect_backoff_ms = config.protocol_options.websocket.reconnect_backoff_ms;
|
||||||
|
}
|
||||||
|
if (!Object.keys(value.protocol_options.websocket).length) {
|
||||||
|
delete value.protocol_options.websocket;
|
||||||
|
}
|
||||||
|
if (!Object.keys(value.protocol_options).length) {
|
||||||
|
delete value.protocol_options;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return window.jsyaml ? window.jsyaml.dump(value, { lineWidth: -1 }) : JSON.stringify(value, null, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prefillUpstream(baseUrl, headers, authProfileRef) {
|
||||||
|
var known = upstreams.find(function(item) {
|
||||||
|
return item.url === baseUrl && (item.authProfileId || null) === (authProfileRef || null);
|
||||||
|
});
|
||||||
|
if (known) {
|
||||||
|
pickUpstream(known.id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedUpstreamId = null;
|
||||||
|
var trigger = document.getElementById('upstream-new-trigger');
|
||||||
|
var form = document.getElementById('upstream-new-form');
|
||||||
|
if (trigger) trigger.classList.add('active');
|
||||||
|
if (form) form.hidden = false;
|
||||||
|
setValue('new-upstream-name', 'imported-upstream');
|
||||||
|
setValue('new-upstream-url', baseUrl);
|
||||||
|
setValue('new-upstream-static-headers', headers && Object.keys(headers).length ? JSON.stringify(headers, null, 2) : '{\n}');
|
||||||
|
setValue('new-upstream-auth-mode', authProfileRef ? 'existing' : 'none');
|
||||||
|
setValue('new-upstream-auth-profile', authProfileRef || '');
|
||||||
|
updateUpstreamAuthUi();
|
||||||
|
|
||||||
|
var valueEl = document.getElementById('upstream-combobox-value');
|
||||||
|
if (valueEl) {
|
||||||
|
valueEl.innerHTML = '';
|
||||||
|
valueEl.appendChild(buildUpstreamTriggerContent('imported-upstream', baseUrl));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function prefillWizardFromEdit(detail, versionDocument) {
|
||||||
|
if (!detail || !versionDocument) return;
|
||||||
|
|
||||||
|
setEditModePresentation();
|
||||||
|
selectProtocol(detail.protocol);
|
||||||
|
|
||||||
|
var target = versionDocument.target || {};
|
||||||
|
if (target.kind === 'rest') {
|
||||||
|
prefillUpstream(target.base_url, target.static_headers, versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null);
|
||||||
|
setValue('endpoint-path', target.path_template);
|
||||||
|
document.querySelectorAll('.method-card').forEach(function(card) {
|
||||||
|
card.classList.toggle('active', card.dataset.method === target.method);
|
||||||
|
});
|
||||||
|
} else if (target.kind === 'graphql') {
|
||||||
|
var endpoint = target.endpoint || '';
|
||||||
|
var parsedEndpoint = new URL(endpoint, window.location.origin);
|
||||||
|
prefillUpstream(parsedEndpoint.origin, {}, versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null);
|
||||||
|
setValue('endpoint-path', parsedEndpoint.pathname + parsedEndpoint.search);
|
||||||
|
setValue('gql-query-editor', target.query_template);
|
||||||
|
document.querySelectorAll('.gql-type-card').forEach(function(card) {
|
||||||
|
card.classList.toggle('active', card.dataset.gqlType === target.operation_type);
|
||||||
|
});
|
||||||
|
} else if (target.kind === 'grpc') {
|
||||||
|
prefillUpstream(target.server_addr, {}, versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null);
|
||||||
|
var route = '/' + (target.package ? target.package + '.' : '') + target.service + '/' + target.method;
|
||||||
|
setValue('grpc-manual-route', route);
|
||||||
|
setValue('grpc-manual-req-type', '');
|
||||||
|
setValue('grpc-manual-res-type', '');
|
||||||
|
grpcManualRouteInput(route);
|
||||||
|
grpcManualTypeInput();
|
||||||
|
} else if (target.kind === 'soap') {
|
||||||
|
prefillUpstream(
|
||||||
|
target.endpoint_override || '',
|
||||||
|
{},
|
||||||
|
versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null
|
||||||
|
);
|
||||||
|
setValue('soap-service-name', target.service_name || '');
|
||||||
|
setValue('soap-port-name', target.port_name || '');
|
||||||
|
setValue('soap-operation-name', target.operation_name || '');
|
||||||
|
setValue('soap-endpoint-override', target.endpoint_override || '');
|
||||||
|
setValue('soap-action', target.soap_action || '');
|
||||||
|
setValue('soap-version', target.soap_version || 'soap_11');
|
||||||
|
setValue('soap-binding-style', target.binding_style || 'document_literal');
|
||||||
|
} else if (target.kind === 'websocket') {
|
||||||
|
prefillUpstream(
|
||||||
|
target.url,
|
||||||
|
target.static_headers || {},
|
||||||
|
versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null
|
||||||
|
);
|
||||||
|
setValue('websocket-path', '');
|
||||||
|
setValue('websocket-subprotocols', (target.subprotocols || []).join('\n'));
|
||||||
|
setValue(
|
||||||
|
'websocket-subscribe-template',
|
||||||
|
target.subscribe_message_template ? JSON.stringify(target.subscribe_message_template, null, 2) : ''
|
||||||
|
);
|
||||||
|
setValue(
|
||||||
|
'websocket-unsubscribe-template',
|
||||||
|
target.unsubscribe_message_template ? JSON.stringify(target.unsubscribe_message_template, null, 2) : ''
|
||||||
|
);
|
||||||
|
var websocketOptions = versionDocument.execution_config
|
||||||
|
&& versionDocument.execution_config.protocol_options
|
||||||
|
&& versionDocument.execution_config.protocol_options.websocket
|
||||||
|
? versionDocument.execution_config.protocol_options.websocket
|
||||||
|
: null;
|
||||||
|
setValue('websocket-heartbeat-interval-ms', websocketOptions && websocketOptions.heartbeat_interval_ms ? websocketOptions.heartbeat_interval_ms : '');
|
||||||
|
setValue('websocket-reconnect-max-attempts', websocketOptions && websocketOptions.reconnect_max_attempts != null ? websocketOptions.reconnect_max_attempts : '');
|
||||||
|
setValue('websocket-reconnect-backoff-ms', websocketOptions && websocketOptions.reconnect_backoff_ms ? websocketOptions.reconnect_backoff_ms : '');
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue('tool-name', detail.name);
|
||||||
|
setValue('tool-display-name', detail.display_name);
|
||||||
|
setValue('tool-title', versionDocument.tool_description ? versionDocument.tool_description.title : detail.display_name);
|
||||||
|
setValue('tool-description', versionDocument.tool_description ? versionDocument.tool_description.description : '');
|
||||||
|
setValue('tool-input-schema', JSON.stringify(crankSchemaToJsonSchema(versionDocument.input_schema), null, 2));
|
||||||
|
setValue('tool-output-schema', JSON.stringify(crankSchemaToJsonSchema(versionDocument.output_schema), null, 2));
|
||||||
|
setValue('tool-input-mapping', mappingSetToEditorValue(versionDocument.input_mapping, 'input', detail.protocol));
|
||||||
|
setValue('tool-output-mapping', mappingSetToEditorValue(versionDocument.output_mapping, 'output', detail.protocol));
|
||||||
|
setValue('tool-exec-config', executionConfigToEditorValue(versionDocument.execution_config));
|
||||||
|
applyStreamingConfig(versionDocument.execution_config ? versionDocument.execution_config.streaming : null);
|
||||||
|
|
||||||
|
var toolNameInput = document.getElementById('tool-name');
|
||||||
|
if (toolNameInput) toolNameInput.disabled = true;
|
||||||
|
updateWizardProtocolVisibility();
|
||||||
|
}
|
||||||
@@ -262,359 +262,6 @@ function parseStructuredText(text) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function inferSchemaKind(typeName) {
|
|
||||||
if (typeName === 'object') return 'object';
|
|
||||||
if (typeName === 'array') return 'array';
|
|
||||||
if (typeName === 'string') return 'string';
|
|
||||||
if (typeName === 'integer') return 'integer';
|
|
||||||
if (typeName === 'number') return 'number';
|
|
||||||
if (typeName === 'boolean') return 'boolean';
|
|
||||||
if (typeName === 'null') return 'null';
|
|
||||||
return 'string';
|
|
||||||
}
|
|
||||||
|
|
||||||
function convertJsonSchemaToCrankSchema(node, requiredNames) {
|
|
||||||
var schemaNode = node || {};
|
|
||||||
var typeName = Array.isArray(schemaNode.type)
|
|
||||||
? schemaNode.type.find(function(value) { return value !== 'null'; }) || 'string'
|
|
||||||
: schemaNode.type || (schemaNode.properties ? 'object' : 'string');
|
|
||||||
var nullable = Array.isArray(schemaNode.type) && schemaNode.type.indexOf('null') >= 0;
|
|
||||||
var kind = schemaNode.enum ? 'enum' : inferSchemaKind(typeName);
|
|
||||||
var schema = {
|
|
||||||
type: kind,
|
|
||||||
description: schemaNode.description || null,
|
|
||||||
required: false,
|
|
||||||
nullable: nullable,
|
|
||||||
default_value: Object.prototype.hasOwnProperty.call(schemaNode, 'default') ? schemaNode.default : null,
|
|
||||||
fields: {},
|
|
||||||
items: null,
|
|
||||||
enum_values: schemaNode.enum || [],
|
|
||||||
variants: [],
|
|
||||||
};
|
|
||||||
|
|
||||||
if (kind === 'object') {
|
|
||||||
var requiredSet = new Set(schemaNode.required || requiredNames || []);
|
|
||||||
Object.keys(schemaNode.properties || {}).forEach(function(fieldName) {
|
|
||||||
var field = convertJsonSchemaToCrankSchema(schemaNode.properties[fieldName], []);
|
|
||||||
field.required = requiredSet.has(fieldName);
|
|
||||||
schema.fields[fieldName] = field;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (kind === 'array' && schemaNode.items) {
|
|
||||||
schema.items = convertJsonSchemaToCrankSchema(schemaNode.items, []);
|
|
||||||
}
|
|
||||||
|
|
||||||
return schema;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mappingRootForProtocol(protocol) {
|
|
||||||
if (protocol === 'graphql') return '$.request.variables';
|
|
||||||
if (protocol === 'grpc') return '$.request.grpc';
|
|
||||||
var activeMethod = document.querySelector('.method-card.active');
|
|
||||||
var method = activeMethod ? activeMethod.dataset.method : 'POST';
|
|
||||||
return method === 'GET' || method === 'DELETE' ? '$.request.query' : '$.request.body';
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeInputSource(path) {
|
|
||||||
if (typeof path !== 'string') return '$.mcp';
|
|
||||||
if (path.indexOf('$.input.') === 0) return '$.mcp.' + path.slice('$.input.'.length);
|
|
||||||
if (path === '$.input') return '$.mcp';
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function normalizeOutputSource(path) {
|
|
||||||
if (typeof path !== 'string') return '$.response.data';
|
|
||||||
return path;
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildMappingSet(rawValue, mode) {
|
|
||||||
if (rawValue && Array.isArray(rawValue.rules)) {
|
|
||||||
return { rules: rawValue.rules };
|
|
||||||
}
|
|
||||||
|
|
||||||
var rules = [];
|
|
||||||
if (!rawValue || typeof rawValue !== 'object') {
|
|
||||||
return { rules: rules };
|
|
||||||
}
|
|
||||||
|
|
||||||
var root = mappingRootForProtocol(wizardProtocol);
|
|
||||||
|
|
||||||
Object.keys(rawValue).forEach(function(key) {
|
|
||||||
var source = rawValue[key];
|
|
||||||
if (typeof source !== 'string') return;
|
|
||||||
if (mode === 'input') {
|
|
||||||
rules.push({
|
|
||||||
source: normalizeInputSource(source),
|
|
||||||
target: root + '.' + key,
|
|
||||||
required: false,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
rules.push({
|
|
||||||
source: normalizeOutputSource(source),
|
|
||||||
target: '$.output.' + key,
|
|
||||||
required: false,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return { rules: rules };
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseExecutionConfig(text) {
|
|
||||||
if (window.CrankStreamingForm && typeof window.CrankStreamingForm.validateStreamingConfig === 'function') {
|
|
||||||
var validation = window.CrankStreamingForm.validateStreamingConfig([currentProtocolCapabilities()]);
|
|
||||||
if (!validation.valid) {
|
|
||||||
throw new Error(validation.errors[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var value = parseStructuredText(text);
|
|
||||||
var retry = value.retry || value.retry_policy || null;
|
|
||||||
var headers = value.headers && typeof value.headers === 'object' ? value.headers : {};
|
|
||||||
var selectedUpstream = currentUpstream();
|
|
||||||
if (selectedUpstream && selectedUpstream.authMode === 'create') {
|
|
||||||
throw new Error(tKey('wizard.error.save_upstream_first'));
|
|
||||||
}
|
|
||||||
var authProfileRef = selectedUpstream && selectedUpstream.authProfileId
|
|
||||||
? selectedUpstream.authProfileId
|
|
||||||
: (value.auth && value.auth.profile ? value.auth.profile : null);
|
|
||||||
var config = {
|
|
||||||
timeout_ms: Number(value.timeout_ms || 10000),
|
|
||||||
retry_policy: retry && retry.max_attempts ? { max_attempts: Number(retry.max_attempts) } : null,
|
|
||||||
auth_profile_ref: authProfileRef,
|
|
||||||
headers: headers,
|
|
||||||
protocol_options: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (wizardProtocol === 'grpc') {
|
|
||||||
var useTls = false;
|
|
||||||
if (value.protocol_options && value.protocol_options.grpc) {
|
|
||||||
useTls = !!value.protocol_options.grpc.use_tls;
|
|
||||||
} else if (value.tls) {
|
|
||||||
useTls = !!value.tls.verify;
|
|
||||||
}
|
|
||||||
config.protocol_options = { grpc: { use_tls: useTls } };
|
|
||||||
} else if (wizardProtocol === 'websocket') {
|
|
||||||
var websocketProtocolOptions = value.protocol_options && value.protocol_options.websocket
|
|
||||||
? value.protocol_options.websocket
|
|
||||||
: {};
|
|
||||||
var heartbeatInterval = websocketProtocolOptions.heartbeat_interval_ms != null
|
|
||||||
? Number(websocketProtocolOptions.heartbeat_interval_ms)
|
|
||||||
: numericValueOrNull('websocket-heartbeat-interval-ms');
|
|
||||||
var reconnectAttempts = websocketProtocolOptions.reconnect_max_attempts != null
|
|
||||||
? Number(websocketProtocolOptions.reconnect_max_attempts)
|
|
||||||
: numericValueOrNull('websocket-reconnect-max-attempts');
|
|
||||||
var reconnectBackoff = websocketProtocolOptions.reconnect_backoff_ms != null
|
|
||||||
? Number(websocketProtocolOptions.reconnect_backoff_ms)
|
|
||||||
: numericValueOrNull('websocket-reconnect-backoff-ms');
|
|
||||||
var runtimeOptions = {};
|
|
||||||
if (Number.isFinite(heartbeatInterval) && heartbeatInterval > 0) {
|
|
||||||
runtimeOptions.heartbeat_interval_ms = heartbeatInterval;
|
|
||||||
}
|
|
||||||
if (Number.isFinite(reconnectAttempts) && reconnectAttempts >= 0) {
|
|
||||||
runtimeOptions.reconnect_max_attempts = reconnectAttempts;
|
|
||||||
}
|
|
||||||
if (Number.isFinite(reconnectBackoff) && reconnectBackoff >= 0) {
|
|
||||||
runtimeOptions.reconnect_backoff_ms = reconnectBackoff;
|
|
||||||
}
|
|
||||||
if (Object.keys(runtimeOptions).length) {
|
|
||||||
config.protocol_options = { websocket: runtimeOptions };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.streaming = collectStreamingConfig();
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
function currentUpstream() {
|
|
||||||
var selected = upstreams.find(function(item) { return item.id === selectedUpstreamId; });
|
|
||||||
if (selected) return selected;
|
|
||||||
|
|
||||||
var customName = textValue('new-upstream-name');
|
|
||||||
var customUrl = textValue('new-upstream-url');
|
|
||||||
if (!customUrl && wizardProtocol === 'soap') {
|
|
||||||
customUrl = textValue('soap-endpoint-override');
|
|
||||||
}
|
|
||||||
if (!customUrl) return null;
|
|
||||||
|
|
||||||
var authMode = textValue('new-upstream-auth-mode') || 'none';
|
|
||||||
var authProfileId = authMode === 'existing' ? textValue('new-upstream-auth-profile') : null;
|
|
||||||
var authProfile = authProfileById(authProfileId);
|
|
||||||
return {
|
|
||||||
id: 'custom',
|
|
||||||
name: customName || 'custom-upstream',
|
|
||||||
url: customUrl,
|
|
||||||
staticHeaders: textValue('new-upstream-static-headers') || '{\n}',
|
|
||||||
authMode: authMode,
|
|
||||||
authProfileId: authProfileId || null,
|
|
||||||
authProfileName: authProfile ? authProfile.name : '',
|
|
||||||
authKind: authProfile ? authProfile.kind : null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function joinUrl(baseUrl, path) {
|
|
||||||
if (!baseUrl) return path || '';
|
|
||||||
if (!path) return baseUrl;
|
|
||||||
return baseUrl.replace(/\/+$/, '') + '/' + path.replace(/^\/+/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectedGraphqlOperationType() {
|
|
||||||
var active = document.querySelector('.gql-type-card.active');
|
|
||||||
return active ? active.dataset.gqlType : 'query';
|
|
||||||
}
|
|
||||||
|
|
||||||
function graphqlTopLevelField(queryText) {
|
|
||||||
var match = queryText.replace(/#[^\n]*/g, '').match(/\{\s*([A-Za-z_][A-Za-z0-9_]*)/);
|
|
||||||
return match ? match[1] : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function graphqlOperationName(queryText) {
|
|
||||||
var match = queryText.match(/\b(query|mutation)\s+([A-Za-z_][A-Za-z0-9_]*)/);
|
|
||||||
return match ? match[2] : textValue('tool-name');
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildTarget() {
|
|
||||||
var upstream = currentUpstream();
|
|
||||||
if (!upstream || !upstream.url) {
|
|
||||||
throw new Error(tKey('wizard.error.select_upstream'));
|
|
||||||
}
|
|
||||||
|
|
||||||
var pathTemplate = textValue('endpoint-path') || '/';
|
|
||||||
|
|
||||||
if (wizardProtocol === 'rest') {
|
|
||||||
var activeMethod = document.querySelector('.method-card.active');
|
|
||||||
return {
|
|
||||||
kind: 'rest',
|
|
||||||
base_url: upstream.url,
|
|
||||||
method: activeMethod ? activeMethod.dataset.method : 'POST',
|
|
||||||
path_template: pathTemplate,
|
|
||||||
static_headers: parseHeaderMap(upstream.staticHeaders),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wizardProtocol === 'graphql') {
|
|
||||||
var queryTemplate = textValue('gql-query-editor');
|
|
||||||
var topField = graphqlTopLevelField(queryTemplate);
|
|
||||||
return {
|
|
||||||
kind: 'graphql',
|
|
||||||
endpoint: joinUrl(upstream.url, pathTemplate),
|
|
||||||
operation_type: selectedGraphqlOperationType(),
|
|
||||||
operation_name: graphqlOperationName(queryTemplate),
|
|
||||||
query_template: queryTemplate,
|
|
||||||
response_path: topField ? '$.response.body.data.' + topField : '$.response.body.data',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wizardProtocol === 'soap') {
|
|
||||||
var endpointOverride = textValue('soap-endpoint-override') || upstream.url;
|
|
||||||
var existingWsdlRef = wizardCurrentVersion && wizardCurrentVersion.target && wizardCurrentVersion.target.kind === 'soap'
|
|
||||||
? wizardCurrentVersion.target.wsdl_ref
|
|
||||||
: 'sample_wsdl_pending';
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind: 'soap',
|
|
||||||
wsdl_ref: existingWsdlRef,
|
|
||||||
service_name: textValue('soap-service-name') || 'Service',
|
|
||||||
port_name: textValue('soap-port-name') || 'Port',
|
|
||||||
operation_name: textValue('soap-operation-name') || 'Operation',
|
|
||||||
endpoint_override: endpointOverride || null,
|
|
||||||
soap_version: textValue('soap-version') || 'soap_11',
|
|
||||||
soap_action: textValue('soap-action') || null,
|
|
||||||
binding_style: textValue('soap-binding-style') || 'document_literal',
|
|
||||||
headers: [],
|
|
||||||
fault_contract: null,
|
|
||||||
metadata: { input_part_names: [], output_part_names: [], namespaces: [] },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wizardProtocol === 'websocket') {
|
|
||||||
return {
|
|
||||||
kind: 'websocket',
|
|
||||||
url: joinUrl(upstream.url, textValue('websocket-path')),
|
|
||||||
subprotocols: textareaLines('websocket-subprotocols'),
|
|
||||||
subscribe_message_template: stringValueOrNull('websocket-subscribe-template')
|
|
||||||
? parseStructuredText(textValue('websocket-subscribe-template'))
|
|
||||||
: null,
|
|
||||||
unsubscribe_message_template: stringValueOrNull('websocket-unsubscribe-template')
|
|
||||||
? parseStructuredText(textValue('websocket-unsubscribe-template'))
|
|
||||||
: null,
|
|
||||||
static_headers: parseHeaderMap(upstream.staticHeaders),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var route = textValue('grpc-manual-route') || (document.getElementById('grpc-path-value') ? document.getElementById('grpc-path-value').textContent.trim() : '');
|
|
||||||
var descriptorRef = wizardCurrentVersion && wizardCurrentVersion.target && wizardCurrentVersion.target.kind === 'grpc'
|
|
||||||
? wizardCurrentVersion.target.descriptor_ref
|
|
||||||
: 'desc_pending';
|
|
||||||
var descriptorSetB64 = wizardCurrentVersion && wizardCurrentVersion.target && wizardCurrentVersion.target.kind === 'grpc'
|
|
||||||
? (wizardCurrentVersion.target.descriptor_set_b64 || '')
|
|
||||||
: '';
|
|
||||||
var parsedRoute = parseGrpcRoute(route);
|
|
||||||
|
|
||||||
return {
|
|
||||||
kind: 'grpc',
|
|
||||||
server_addr: upstream.url,
|
|
||||||
package: parsedRoute.package,
|
|
||||||
service: parsedRoute.service,
|
|
||||||
method: parsedRoute.method,
|
|
||||||
descriptor_ref: descriptorRef,
|
|
||||||
descriptor_set_b64: descriptorSetB64,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseGrpcRoute(route) {
|
|
||||||
var normalized = route.replace(/^\/+/, '');
|
|
||||||
var parts = normalized.split('/');
|
|
||||||
if (parts.length !== 2) {
|
|
||||||
return {
|
|
||||||
package: '',
|
|
||||||
service: '',
|
|
||||||
method: '',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var servicePart = parts[0];
|
|
||||||
var method = parts[1];
|
|
||||||
var serviceSegments = servicePart.split('.');
|
|
||||||
var service = serviceSegments.pop() || '';
|
|
||||||
var packageName = serviceSegments.join('.');
|
|
||||||
|
|
||||||
return {
|
|
||||||
package: packageName,
|
|
||||||
service: service,
|
|
||||||
method: method,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function parseHeaderMap(text) {
|
|
||||||
if (!text || !text.trim()) return {};
|
|
||||||
var value = parseStructuredText(text);
|
|
||||||
return value && typeof value === 'object' ? value : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
function numericValueOrNull(id) {
|
|
||||||
var value = textValue(id);
|
|
||||||
if (!value) return null;
|
|
||||||
var parsed = Number(value);
|
|
||||||
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function stringValueOrNull(id) {
|
|
||||||
var value = textValue(id);
|
|
||||||
return value ? value : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function textareaLines(id) {
|
|
||||||
var value = textValue(id);
|
|
||||||
if (!value) return [];
|
|
||||||
return value
|
|
||||||
.split(/\r?\n/)
|
|
||||||
.map(function(line) { return line.trim(); })
|
|
||||||
.filter(Boolean);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildToolDescription() {
|
function buildToolDescription() {
|
||||||
return {
|
return {
|
||||||
title: textValue('tool-title') || textValue('tool-display-name') || textValue('tool-name'),
|
title: textValue('tool-title') || textValue('tool-display-name') || textValue('tool-name'),
|
||||||
@@ -706,197 +353,6 @@ function selectProtocol(protocol) {
|
|||||||
updateWizardProtocolVisibility();
|
updateWizardProtocolVisibility();
|
||||||
}
|
}
|
||||||
|
|
||||||
function prefillUpstream(baseUrl, headers, authProfileRef) {
|
|
||||||
var known = upstreams.find(function(item) {
|
|
||||||
return item.url === baseUrl && (item.authProfileId || null) === (authProfileRef || null);
|
|
||||||
});
|
|
||||||
if (known) {
|
|
||||||
pickUpstream(known.id);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
selectedUpstreamId = null;
|
|
||||||
var trigger = document.getElementById('upstream-new-trigger');
|
|
||||||
var form = document.getElementById('upstream-new-form');
|
|
||||||
if (trigger) trigger.classList.add('active');
|
|
||||||
if (form) form.hidden = false;
|
|
||||||
setValue('new-upstream-name', 'imported-upstream');
|
|
||||||
setValue('new-upstream-url', baseUrl);
|
|
||||||
setValue('new-upstream-static-headers', headers && Object.keys(headers).length ? JSON.stringify(headers, null, 2) : '{\n}');
|
|
||||||
setValue('new-upstream-auth-mode', authProfileRef ? 'existing' : 'none');
|
|
||||||
setValue('new-upstream-auth-profile', authProfileRef || '');
|
|
||||||
updateUpstreamAuthUi();
|
|
||||||
|
|
||||||
var valueEl = document.getElementById('upstream-combobox-value');
|
|
||||||
if (valueEl) {
|
|
||||||
valueEl.innerHTML = '';
|
|
||||||
valueEl.appendChild(buildUpstreamTriggerContent('imported-upstream', baseUrl));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function crankSchemaToJsonSchema(schema) {
|
|
||||||
var result = {};
|
|
||||||
var typeName = schema.type === 'enum' ? 'string' : schema.type;
|
|
||||||
result.type = schema.nullable ? [typeName, 'null'] : typeName;
|
|
||||||
if (schema.description) result.description = schema.description;
|
|
||||||
if (schema.default_value !== null && schema.default_value !== undefined) result.default = schema.default_value;
|
|
||||||
if (schema.enum_values && schema.enum_values.length) result.enum = schema.enum_values.slice();
|
|
||||||
if (schema.type === 'object') {
|
|
||||||
result.type = schema.nullable ? ['object', 'null'] : 'object';
|
|
||||||
result.properties = {};
|
|
||||||
var required = [];
|
|
||||||
Object.keys(schema.fields || {}).forEach(function(fieldName) {
|
|
||||||
var field = schema.fields[fieldName];
|
|
||||||
result.properties[fieldName] = crankSchemaToJsonSchema(field);
|
|
||||||
if (field.required) required.push(fieldName);
|
|
||||||
});
|
|
||||||
if (required.length) result.required = required;
|
|
||||||
result.additionalProperties = false;
|
|
||||||
}
|
|
||||||
if (schema.type === 'array' && schema.items) {
|
|
||||||
result.items = crankSchemaToJsonSchema(schema.items);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function mappingSetToEditorValue(mappingSet, mode, protocol) {
|
|
||||||
var rules = mappingSet && mappingSet.rules ? mappingSet.rules : [];
|
|
||||||
var object = {};
|
|
||||||
rules.forEach(function(rule) {
|
|
||||||
if (mode === 'input') {
|
|
||||||
var key = rule.target.replace(mappingRootForProtocol(protocol) + '.', '');
|
|
||||||
object[key] = rule.source.replace('$.mcp.', '$.input.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var outputKey = rule.target.replace('$.output.', '');
|
|
||||||
object[outputKey] = rule.source;
|
|
||||||
});
|
|
||||||
return window.jsyaml ? window.jsyaml.dump(object, { lineWidth: -1 }) : JSON.stringify(object, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function executionConfigToEditorValue(config) {
|
|
||||||
var value = {
|
|
||||||
timeout_ms: config.timeout_ms,
|
|
||||||
};
|
|
||||||
if (config.retry_policy) {
|
|
||||||
value.retry = { max_attempts: config.retry_policy.max_attempts };
|
|
||||||
}
|
|
||||||
if (config.auth_profile_ref) {
|
|
||||||
value.auth = { profile: config.auth_profile_ref };
|
|
||||||
}
|
|
||||||
if (config.headers && Object.keys(config.headers).length) {
|
|
||||||
value.headers = config.headers;
|
|
||||||
}
|
|
||||||
if (config.protocol_options && config.protocol_options.grpc) {
|
|
||||||
value.tls = { verify: !!config.protocol_options.grpc.use_tls };
|
|
||||||
}
|
|
||||||
if (config.protocol_options && config.protocol_options.websocket) {
|
|
||||||
value.protocol_options = value.protocol_options || {};
|
|
||||||
value.protocol_options.websocket = {};
|
|
||||||
if (config.protocol_options.websocket.heartbeat_interval_ms != null) {
|
|
||||||
value.protocol_options.websocket.heartbeat_interval_ms = config.protocol_options.websocket.heartbeat_interval_ms;
|
|
||||||
}
|
|
||||||
if (config.protocol_options.websocket.reconnect_max_attempts != null) {
|
|
||||||
value.protocol_options.websocket.reconnect_max_attempts = config.protocol_options.websocket.reconnect_max_attempts;
|
|
||||||
}
|
|
||||||
if (config.protocol_options.websocket.reconnect_backoff_ms != null) {
|
|
||||||
value.protocol_options.websocket.reconnect_backoff_ms = config.protocol_options.websocket.reconnect_backoff_ms;
|
|
||||||
}
|
|
||||||
if (!Object.keys(value.protocol_options.websocket).length) {
|
|
||||||
delete value.protocol_options.websocket;
|
|
||||||
}
|
|
||||||
if (!Object.keys(value.protocol_options).length) {
|
|
||||||
delete value.protocol_options;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return window.jsyaml ? window.jsyaml.dump(value, { lineWidth: -1 }) : JSON.stringify(value, null, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function prefillWizardFromEdit(detail, versionDocument) {
|
|
||||||
if (!detail || !versionDocument) return;
|
|
||||||
|
|
||||||
setEditModePresentation();
|
|
||||||
selectProtocol(detail.protocol);
|
|
||||||
|
|
||||||
var target = versionDocument.target || {};
|
|
||||||
if (target.kind === 'rest') {
|
|
||||||
prefillUpstream(target.base_url, target.static_headers, versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null);
|
|
||||||
setValue('endpoint-path', target.path_template);
|
|
||||||
document.querySelectorAll('.method-card').forEach(function(card) {
|
|
||||||
card.classList.toggle('active', card.dataset.method === target.method);
|
|
||||||
});
|
|
||||||
} else if (target.kind === 'graphql') {
|
|
||||||
var endpoint = target.endpoint || '';
|
|
||||||
var parsedEndpoint = new URL(endpoint, window.location.origin);
|
|
||||||
prefillUpstream(parsedEndpoint.origin, {}, versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null);
|
|
||||||
setValue('endpoint-path', parsedEndpoint.pathname + parsedEndpoint.search);
|
|
||||||
setValue('gql-query-editor', target.query_template);
|
|
||||||
document.querySelectorAll('.gql-type-card').forEach(function(card) {
|
|
||||||
card.classList.toggle('active', card.dataset.gqlType === target.operation_type);
|
|
||||||
});
|
|
||||||
} else if (target.kind === 'grpc') {
|
|
||||||
prefillUpstream(target.server_addr, {}, versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null);
|
|
||||||
var route = '/' + (target.package ? target.package + '.' : '') + target.service + '/' + target.method;
|
|
||||||
setValue('grpc-manual-route', route);
|
|
||||||
setValue('grpc-manual-req-type', '');
|
|
||||||
setValue('grpc-manual-res-type', '');
|
|
||||||
grpcManualRouteInput(route);
|
|
||||||
grpcManualTypeInput();
|
|
||||||
} else if (target.kind === 'soap') {
|
|
||||||
prefillUpstream(
|
|
||||||
target.endpoint_override || '',
|
|
||||||
{},
|
|
||||||
versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null
|
|
||||||
);
|
|
||||||
setValue('soap-service-name', target.service_name || '');
|
|
||||||
setValue('soap-port-name', target.port_name || '');
|
|
||||||
setValue('soap-operation-name', target.operation_name || '');
|
|
||||||
setValue('soap-endpoint-override', target.endpoint_override || '');
|
|
||||||
setValue('soap-action', target.soap_action || '');
|
|
||||||
setValue('soap-version', target.soap_version || 'soap_11');
|
|
||||||
setValue('soap-binding-style', target.binding_style || 'document_literal');
|
|
||||||
} else if (target.kind === 'websocket') {
|
|
||||||
prefillUpstream(
|
|
||||||
target.url,
|
|
||||||
target.static_headers || {},
|
|
||||||
versionDocument.execution_config ? versionDocument.execution_config.auth_profile_ref : null
|
|
||||||
);
|
|
||||||
setValue('websocket-path', '');
|
|
||||||
setValue('websocket-subprotocols', (target.subprotocols || []).join('\n'));
|
|
||||||
setValue(
|
|
||||||
'websocket-subscribe-template',
|
|
||||||
target.subscribe_message_template ? JSON.stringify(target.subscribe_message_template, null, 2) : ''
|
|
||||||
);
|
|
||||||
setValue(
|
|
||||||
'websocket-unsubscribe-template',
|
|
||||||
target.unsubscribe_message_template ? JSON.stringify(target.unsubscribe_message_template, null, 2) : ''
|
|
||||||
);
|
|
||||||
var websocketOptions = versionDocument.execution_config
|
|
||||||
&& versionDocument.execution_config.protocol_options
|
|
||||||
&& versionDocument.execution_config.protocol_options.websocket
|
|
||||||
? versionDocument.execution_config.protocol_options.websocket
|
|
||||||
: null;
|
|
||||||
setValue('websocket-heartbeat-interval-ms', websocketOptions && websocketOptions.heartbeat_interval_ms ? websocketOptions.heartbeat_interval_ms : '');
|
|
||||||
setValue('websocket-reconnect-max-attempts', websocketOptions && websocketOptions.reconnect_max_attempts != null ? websocketOptions.reconnect_max_attempts : '');
|
|
||||||
setValue('websocket-reconnect-backoff-ms', websocketOptions && websocketOptions.reconnect_backoff_ms ? websocketOptions.reconnect_backoff_ms : '');
|
|
||||||
}
|
|
||||||
|
|
||||||
setValue('tool-name', detail.name);
|
|
||||||
setValue('tool-display-name', detail.display_name);
|
|
||||||
setValue('tool-title', versionDocument.tool_description ? versionDocument.tool_description.title : detail.display_name);
|
|
||||||
setValue('tool-description', versionDocument.tool_description ? versionDocument.tool_description.description : '');
|
|
||||||
setValue('tool-input-schema', JSON.stringify(crankSchemaToJsonSchema(versionDocument.input_schema), null, 2));
|
|
||||||
setValue('tool-output-schema', JSON.stringify(crankSchemaToJsonSchema(versionDocument.output_schema), null, 2));
|
|
||||||
setValue('tool-input-mapping', mappingSetToEditorValue(versionDocument.input_mapping, 'input', detail.protocol));
|
|
||||||
setValue('tool-output-mapping', mappingSetToEditorValue(versionDocument.output_mapping, 'output', detail.protocol));
|
|
||||||
setValue('tool-exec-config', executionConfigToEditorValue(versionDocument.execution_config));
|
|
||||||
applyStreamingConfig(versionDocument.execution_config ? versionDocument.execution_config.streaming : null);
|
|
||||||
|
|
||||||
var toolNameInput = document.getElementById('tool-name');
|
|
||||||
if (toolNameInput) toolNameInput.disabled = true;
|
|
||||||
updateWizardProtocolVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
function bindWizardLiveActions() {
|
function bindWizardLiveActions() {
|
||||||
bindLiveAction('wizard-upload-input-sample', tKey('wizard.busy.input_sample'), uploadInputSampleFromWizard);
|
bindLiveAction('wizard-upload-input-sample', tKey('wizard.busy.input_sample'), uploadInputSampleFromWizard);
|
||||||
bindLiveAction('wizard-upload-output-sample', tKey('wizard.busy.output_sample'), uploadOutputSampleFromWizard);
|
bindLiveAction('wizard-upload-output-sample', tKey('wizard.busy.output_sample'), uploadOutputSampleFromWizard);
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ const BUNDLES = {
|
|||||||
'js/wizard-grpc.js',
|
'js/wizard-grpc.js',
|
||||||
'js/wizard-artifacts.js',
|
'js/wizard-artifacts.js',
|
||||||
'js/wizard-upstreams.js',
|
'js/wizard-upstreams.js',
|
||||||
|
'js/wizard-model.js',
|
||||||
'js/wizard.js',
|
'js/wizard.js',
|
||||||
'js/nav.js',
|
'js/nav.js',
|
||||||
],
|
],
|
||||||
|
|||||||
Reference in New Issue
Block a user