1302 lines
49 KiB
JavaScript
1302 lines
49 KiB
JavaScript
function tKey(key) {
|
||
return typeof t === 'function' ? t(key) : key;
|
||
}
|
||
function tfKey(key, vars) {
|
||
return typeof tf === 'function' ? tf(key, vars) : key;
|
||
}
|
||
var TOTAL_STEPS = window.CrankWizardShell.TOTAL_STEPS;
|
||
var loadProtocolCapabilities = window.CrankWizardState.loadProtocolCapabilities;
|
||
var currentProtocolCapabilities = window.CrankWizardState.currentProtocolCapabilities;
|
||
var loadStep = window.CrankWizardShell.loadStep;
|
||
var goToStep = window.CrankWizardShell.goToStep;
|
||
var _doGoToStep = window.CrankWizardShell.doGoToStep;
|
||
var loadWizardPanels = window.CrankWizardShell.loadWizardPanels;
|
||
var bindProtocolCards = window.CrankWizardShell.bindProtocolCards;
|
||
var step3PanelId = window.CrankWizardShell.step3PanelId;
|
||
var bindStreamingConfigControls = window.CrankStreamingForm.bindStreamingConfigControls;
|
||
var collectStreamingConfig = window.CrankStreamingForm.collectStreamingConfig;
|
||
var applyStreamingConfig = window.CrankStreamingForm.applyStreamingConfig;
|
||
var currentStep = window.currentStep;
|
||
var wizardProtocol = window.wizardProtocol;
|
||
var wizardMode = window.wizardMode;
|
||
var wizardEditId = window.wizardEditId;
|
||
var wizardWorkspaceId = window.wizardWorkspaceId;
|
||
var wizardCurrentOperation = window.wizardCurrentOperation;
|
||
var wizardCurrentVersion = window.wizardCurrentVersion;
|
||
var wizardProtoUpload = window.wizardProtoUpload;
|
||
var wizardDescriptorSetUpload = window.wizardDescriptorSetUpload;
|
||
var wizardSoapWsdlUpload = window.wizardSoapWsdlUpload;
|
||
var wizardSoapXsdUpload = window.wizardSoapXsdUpload;
|
||
var wizardTestResponsePreview = window.wizardTestResponsePreview;
|
||
var grpcDescriptorServices = window.grpcDescriptorServices;
|
||
var soapServiceCatalog = window.soapServiceCatalog;
|
||
var wizardProtocolCapabilities = window.wizardProtocolCapabilities;
|
||
var wizardSecrets = window.wizardSecrets;
|
||
var wizardAuthProfiles = window.wizardAuthProfiles;
|
||
var selectedUpstreamId = window.selectedUpstreamId;
|
||
var editingUpstreamId = window.editingUpstreamId;
|
||
var protoParsed = window.protoParsed;
|
||
var selectedRpcMethod = window.selectedRpcMethod;
|
||
|
||
async function initWizardPage() {
|
||
renderSidebarBrand('create');
|
||
document.querySelector('.btn-continue').addEventListener('click', function() {
|
||
if (currentStep < TOTAL_STEPS) {
|
||
goToStep(currentStep + 1);
|
||
} else {
|
||
saveOperation();
|
||
}
|
||
});
|
||
|
||
document.querySelector('.btn-back').addEventListener('click', function() {
|
||
if (!this.disabled && currentStep > 1) goToStep(currentStep - 1);
|
||
});
|
||
|
||
document.querySelectorAll('.step-item').forEach(function(item, i) {
|
||
item.addEventListener('click', function() { goToStep(i + 1); });
|
||
});
|
||
|
||
var backToCatalog = document.getElementById('back-to-catalog');
|
||
if (backToCatalog) {
|
||
backToCatalog.addEventListener('click', function() {
|
||
window.location.href = (window.CrankRoutes && window.CrankRoutes.home) || '/';
|
||
});
|
||
}
|
||
|
||
var closeBtn = document.querySelector('.progress-close');
|
||
if (closeBtn) {
|
||
closeBtn.addEventListener('click', function() {
|
||
window.location.href = (window.CrankRoutes && window.CrankRoutes.home) || '/';
|
||
});
|
||
}
|
||
|
||
var saveDraftBtn = document.querySelector('.btn-save-draft');
|
||
if (saveDraftBtn) {
|
||
saveDraftBtn.addEventListener('click', function() {
|
||
saveOperation(true);
|
||
});
|
||
}
|
||
|
||
await (window.whenWorkspacesReady ? window.whenWorkspacesReady() : Promise.resolve());
|
||
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
|
||
wizardWorkspaceId = workspace ? workspace.id : null;
|
||
await loadProtocolCapabilities();
|
||
|
||
await loadWizardPanels([1, 2, 3, 4, 5]);
|
||
await loadWizardAuthResources();
|
||
bindProtocolCards();
|
||
bindWizardLiveActions();
|
||
bindStreamingConfigControls();
|
||
updateUpstreamAuthUi();
|
||
|
||
var quickSecretModal = document.getElementById('quick-secret-modal');
|
||
var quickSecretClose = document.getElementById('quick-secret-close-btn');
|
||
var quickSecretCancel = document.getElementById('quick-secret-cancel-btn');
|
||
var quickSecretSubmit = document.getElementById('quick-secret-submit-btn');
|
||
if (quickSecretClose) quickSecretClose.addEventListener('click', closeQuickSecretModal);
|
||
if (quickSecretCancel) quickSecretCancel.addEventListener('click', closeQuickSecretModal);
|
||
if (quickSecretModal) {
|
||
quickSecretModal.addEventListener('click', function(event) {
|
||
if (event.target === quickSecretModal) closeQuickSecretModal();
|
||
});
|
||
}
|
||
if (quickSecretSubmit) {
|
||
quickSecretSubmit.addEventListener('click', async function() {
|
||
var button = quickSecretSubmit;
|
||
var original = button.textContent;
|
||
button.disabled = true;
|
||
button.textContent = tKey('wizard.step2.quick_secret_submit');
|
||
try {
|
||
await submitQuickSecret();
|
||
} catch (error) {
|
||
if (window.CrankUi) {
|
||
window.CrankUi.error(
|
||
error.message || tKey('wizard.toast.quick_secret_error_title'),
|
||
tKey('wizard.toast.quick_secret_error_title')
|
||
);
|
||
}
|
||
} finally {
|
||
button.disabled = false;
|
||
button.textContent = original;
|
||
}
|
||
});
|
||
}
|
||
|
||
var params = new URLSearchParams(window.location.search);
|
||
if (params.get('mode') === 'edit' && params.get('operationId')) {
|
||
wizardMode = 'edit';
|
||
wizardEditId = params.get('operationId');
|
||
document.title = 'Crank — ' + tKey('wizard.progress.edit');
|
||
await loadOperationForEdit();
|
||
}
|
||
|
||
updateWizardProtocolVisibility();
|
||
_doGoToStep(1);
|
||
}
|
||
|
||
if (window.CrankDiagnostics && typeof window.CrankDiagnostics.bootstrap === 'function') {
|
||
window.CrankDiagnostics.bootstrap('wizard', initWizardPage);
|
||
} else {
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
void initWizardPage();
|
||
});
|
||
}
|
||
|
||
|
||
/* ── HTTP method picker (Step 4 REST) ── */
|
||
|
||
var METHOD_CALLOUTS = {
|
||
GET: null,
|
||
DELETE: null,
|
||
POST: { icon: 'info', text: { en: { title: 'POST selected — body encoding required.', body: 'In step 4 you will define the JSON schema for the body payload. Crank will automatically serialize MCP tool arguments into the format you specify.' }, ru: { title: 'Выбран POST — требуется кодирование тела запроса.', body: 'На шаге 4 вы зададите JSON-схему для body payload. Crank автоматически сериализует аргументы MCP-инструмента в выбранный формат.' } } },
|
||
PUT: { icon: 'info', text: { en: { title: 'PUT selected — full-resource replacement.', body: 'The request body must contain a complete resource representation. Define the body schema in step 4.' }, ru: { title: 'Выбран PUT — полная замена ресурса.', body: 'Request body должен содержать полное представление ресурса. Задайте body schema на шаге 4.' } } },
|
||
PATCH: { icon: 'info', text: { en: { title: 'PATCH selected — partial update.', body: 'Only include the fields you want to modify in the body schema defined in step 4.' }, ru: { title: 'Выбран PATCH — частичное обновление.', body: 'В body schema на шаге 4 включайте только те поля, которые хотите изменить.' } } },
|
||
};
|
||
|
||
function renderSidebarBrand(mode) {
|
||
var sidebarBrand = document.querySelector('.step-sidebar-brand');
|
||
if (!sidebarBrand) return;
|
||
var language = localStorage.getItem('crank_lang') || 'en';
|
||
var parts;
|
||
if (mode === 'edit') {
|
||
parts = language === 'ru'
|
||
? { prefix: 'Редактировать ', suffix: 'операцию' }
|
||
: { prefix: 'Edit ', suffix: 'operation' };
|
||
} else {
|
||
parts = language === 'ru'
|
||
? { prefix: 'Создать ', suffix: 'операцию' }
|
||
: { prefix: 'Create ', suffix: 'operation' };
|
||
}
|
||
sidebarBrand.dataset.wizardBrand = mode;
|
||
sidebarBrand.textContent = parts.prefix;
|
||
var accent = document.createElement('span');
|
||
accent.textContent = parts.suffix;
|
||
sidebarBrand.appendChild(accent);
|
||
}
|
||
|
||
window.addEventListener('crank:langchange', function() {
|
||
renderSidebarBrand(wizardMode === 'edit' ? 'edit' : 'create');
|
||
});
|
||
|
||
function selectMethod(btn) {
|
||
document.querySelectorAll('.method-card').forEach(function(b) { b.classList.remove('active'); });
|
||
btn.classList.add('active');
|
||
var method = btn.dataset.method;
|
||
var callout = document.getElementById('method-callout-rest');
|
||
if (!callout) return;
|
||
var info = METHOD_CALLOUTS[method];
|
||
if (info) {
|
||
var lang = localStorage.getItem('crank_lang') || 'en';
|
||
var text = typeof info.text === 'string' ? info.text : (info.text[lang] || info.text.en);
|
||
callout.innerHTML = '';
|
||
var icon = buildIconSvg(
|
||
(window.APP_BASE || '') + 'icons/general/info-circle.svg#icon',
|
||
15,
|
||
15
|
||
);
|
||
icon.style.flexShrink = '0';
|
||
icon.style.color = 'var(--accent)';
|
||
callout.appendChild(icon);
|
||
|
||
var content = document.createElement('span');
|
||
var title = document.createElement('strong');
|
||
title.textContent = text.title;
|
||
content.appendChild(title);
|
||
content.appendChild(document.createTextNode(' ' + text.body));
|
||
callout.appendChild(content);
|
||
callout.hidden = false;
|
||
} else {
|
||
callout.hidden = true;
|
||
}
|
||
}
|
||
|
||
/* ── GraphQL operation type picker (Step 4 GraphQL) ── */
|
||
|
||
function selectGqlType(btn) {
|
||
document.querySelectorAll('.gql-type-card').forEach(function(b) { b.classList.remove('active'); });
|
||
btn.classList.add('active');
|
||
// Update query editor placeholder if it's still the default
|
||
var editor = document.getElementById('gql-query-editor');
|
||
if (!editor) return;
|
||
var type = btn.dataset.gqlType;
|
||
var defaults = {
|
||
query: 'query GetContact($id: ID!) {\n contact(id: $id) {\n id\n firstName\n lastName\n email\n company {\n id\n name\n }\n createdAt\n }\n}',
|
||
mutation: 'mutation CreateContact(\n $firstName: String!\n $lastName: String!\n $email: String!\n $company: String\n) {\n createContact(input: {\n firstName: $firstName\n lastName: $lastName\n email: $email\n company: $company\n }) {\n id\n firstName\n lastName\n createdAt\n }\n}'
|
||
};
|
||
if (defaults[type]) editor.value = defaults[type];
|
||
}
|
||
|
||
function escapeHtml(str) {
|
||
return String(str).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/"/g,'"');
|
||
}
|
||
|
||
// Close dropdown on outside click
|
||
document.addEventListener('click', function() {
|
||
if (dropdownOpen) closeUpstreamDropdown();
|
||
});
|
||
|
||
|
||
/* ══════════════════════════════════════════════
|
||
gRPC — source selector
|
||
══════════════════════════════════════════════ */
|
||
|
||
var grpcSource = 'proto'; // 'proto' | 'reflection' | 'manual'
|
||
|
||
function textValue(id) {
|
||
var element = document.getElementById(id);
|
||
return element ? element.value.trim() : '';
|
||
}
|
||
|
||
function setValue(id, value) {
|
||
var element = document.getElementById(id);
|
||
if (element) element.value = value || '';
|
||
}
|
||
|
||
function parseStructuredText(text) {
|
||
if (!text.trim()) return {};
|
||
try {
|
||
return JSON.parse(text);
|
||
} catch (_error) {
|
||
if (!window.jsyaml) throw new Error(tKey('wizard.error.parser_unavailable'));
|
||
return window.jsyaml.load(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() {
|
||
return {
|
||
title: textValue('tool-title') || textValue('tool-display-name') || textValue('tool-name'),
|
||
description: textValue('tool-description'),
|
||
tags: [],
|
||
examples: [],
|
||
};
|
||
}
|
||
|
||
function collectWizardPayload() {
|
||
var name = textValue('tool-name');
|
||
if (!name) throw new Error(tKey('wizard.error.tool_name'));
|
||
|
||
var inputSchemaValue = parseStructuredText(textValue('tool-input-schema'));
|
||
var outputSchemaValue = parseStructuredText(textValue('tool-output-schema'));
|
||
var inputMappingValue = parseStructuredText(textValue('tool-input-mapping'));
|
||
var outputMappingValue = parseStructuredText(textValue('tool-output-mapping'));
|
||
|
||
return {
|
||
name: name,
|
||
display_name: textValue('tool-display-name') || name,
|
||
category: 'general',
|
||
protocol: wizardProtocol,
|
||
target: buildTarget(),
|
||
input_schema: convertJsonSchemaToCrankSchema(inputSchemaValue, []),
|
||
output_schema: convertJsonSchemaToCrankSchema(outputSchemaValue, []),
|
||
input_mapping: buildMappingSet(inputMappingValue, 'input'),
|
||
output_mapping: buildMappingSet(outputMappingValue, 'output'),
|
||
execution_config: parseExecutionConfig(textValue('tool-exec-config')),
|
||
tool_description: buildToolDescription(),
|
||
};
|
||
}
|
||
|
||
async function saveOperation(stayOnPage) {
|
||
try {
|
||
await persistCurrentDraft(stayOnPage);
|
||
} catch (error) {
|
||
if (window.CrankUi) {
|
||
window.CrankUi.error(error.message || tKey('wizard.error.save'), tKey('wizard.error.save_title'));
|
||
}
|
||
}
|
||
}
|
||
|
||
async function loadOperationForEdit() {
|
||
if (!wizardWorkspaceId || !wizardEditId) return;
|
||
var detail = await window.CrankApi.getOperation(wizardWorkspaceId, wizardEditId);
|
||
wizardProtocol = detail.protocol || 'rest';
|
||
await loadWizardPanels([3]);
|
||
var draftVersion = await window.CrankApi.getOperationVersion(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
detail.draft_version_ref.version
|
||
);
|
||
wizardCurrentOperation = detail;
|
||
wizardCurrentVersion = draftVersion;
|
||
prefillWizardFromEdit(detail, draftVersion);
|
||
}
|
||
|
||
function setEditModePresentation() {
|
||
var progressLabel = document.querySelector('.progress-label');
|
||
if (progressLabel) progressLabel.textContent = tKey('wizard.progress.edit');
|
||
renderSidebarBrand('edit');
|
||
}
|
||
|
||
function selectProtocol(protocol) {
|
||
wizardProtocol = protocol || 'rest';
|
||
document.querySelectorAll('.protocol-card').forEach(function(card) {
|
||
var proto = card.dataset.protocol
|
||
|| (card.classList.contains('rest')
|
||
? 'rest'
|
||
: card.classList.contains('graphql')
|
||
? 'graphql'
|
||
: card.classList.contains('grpc')
|
||
? 'grpc'
|
||
: card.classList.contains('websocket')
|
||
? 'websocket'
|
||
: card.classList.contains('soap')
|
||
? 'soap'
|
||
: 'rest');
|
||
var selected = proto === wizardProtocol;
|
||
card.classList.toggle('selected', selected);
|
||
card.setAttribute('aria-checked', selected ? 'true' : 'false');
|
||
});
|
||
var s3name = document.getElementById('sidebar-step-3-name');
|
||
if (s3name) {
|
||
var step3Labels = window.CrankWizardState.step3Labels();
|
||
s3name.textContent = step3Labels[wizardProtocol] || tKey('wizard.step3.rest.label');
|
||
}
|
||
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() {
|
||
bindLiveAction('wizard-upload-input-sample', tKey('wizard.busy.input_sample'), uploadInputSampleFromWizard);
|
||
bindLiveAction('wizard-upload-output-sample', tKey('wizard.busy.output_sample'), uploadOutputSampleFromWizard);
|
||
bindLiveAction('wizard-generate-draft', tKey('wizard.busy.generate'), generateDraftFromWizard);
|
||
bindLiveAction('wizard-run-test', tKey('wizard.busy.test'), runWizardTest);
|
||
bindClick('wizard-copy-test-response', copyTestResponseToOutputSample);
|
||
bindLiveAction('wizard-export-yaml', tKey('wizard.busy.export_yaml'), exportWizardYaml);
|
||
bindLiveAction('wizard-import-yaml', tKey('wizard.busy.import_yaml'), importWizardYaml);
|
||
bindLiveAction('wizard-publish-operation', tKey('wizard.busy.publish'), publishWizardOperation);
|
||
bindClick('wizard-select-descriptor-set', function() {
|
||
var input = document.getElementById('wizard-descriptor-set-input');
|
||
if (input) input.click();
|
||
});
|
||
bindLiveAction('wizard-upload-descriptor-set', tKey('wizard.busy.descriptor'), uploadDescriptorSetAndDiscover);
|
||
bindClick('wizard-select-soap-wsdl', function() {
|
||
var input = document.getElementById('wizard-soap-wsdl-input');
|
||
if (input) input.click();
|
||
});
|
||
bindClick('wizard-select-soap-xsd', function() {
|
||
var input = document.getElementById('wizard-soap-xsd-input');
|
||
if (input) input.click();
|
||
});
|
||
bindLiveAction('wizard-upload-soap-contracts', tKey('wizard.busy.wsdl'), uploadSoapArtifactsAndInspect);
|
||
bindClick('wizard-import-yaml-file-trigger', function() {
|
||
var input = document.getElementById('wizard-import-yaml-file');
|
||
if (input) input.click();
|
||
});
|
||
|
||
var descriptorInput = document.getElementById('wizard-descriptor-set-input');
|
||
if (descriptorInput) descriptorInput.addEventListener('change', handleDescriptorSetSelection);
|
||
var soapWsdlInput = document.getElementById('wizard-soap-wsdl-input');
|
||
if (soapWsdlInput) soapWsdlInput.addEventListener('change', handleSoapWsdlSelection);
|
||
var soapXsdInput = document.getElementById('wizard-soap-xsd-input');
|
||
if (soapXsdInput) soapXsdInput.addEventListener('change', handleSoapXsdSelection);
|
||
|
||
var yamlInput = document.getElementById('wizard-import-yaml-file');
|
||
if (yamlInput) yamlInput.addEventListener('change', handleImportYamlFileSelection);
|
||
}
|
||
|
||
function bindClick(id, handler) {
|
||
var element = document.getElementById(id);
|
||
if (!element) return;
|
||
element.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
handler();
|
||
});
|
||
}
|
||
|
||
function bindLiveAction(id, busyLabel, handler) {
|
||
var element = document.getElementById(id);
|
||
if (!element) return;
|
||
element.addEventListener('click', function(event) {
|
||
event.preventDefault();
|
||
runWizardLiveAction(element, busyLabel, handler);
|
||
});
|
||
}
|
||
|
||
async function runWizardLiveAction(button, busyLabel, handler) {
|
||
if (!button || button.dataset.busy === 'true') {
|
||
return;
|
||
}
|
||
|
||
var originalLabel = button.textContent;
|
||
button.dataset.busy = 'true';
|
||
button.disabled = true;
|
||
button.classList.add('is-busy');
|
||
button.textContent = busyLabel;
|
||
|
||
try {
|
||
await handler();
|
||
} catch (error) {
|
||
showWizardLiveStatus(
|
||
tKey('wizard.live.failed_title'),
|
||
error && error.message ? error.message : tKey('wizard.live.failed_body'),
|
||
true
|
||
);
|
||
if (window.CrankUi) {
|
||
window.CrankUi.error(
|
||
error && error.message ? error.message : tKey('wizard.live.failed_body'),
|
||
tKey('wizard.live.failed_title')
|
||
);
|
||
}
|
||
} finally {
|
||
button.dataset.busy = 'false';
|
||
button.disabled = false;
|
||
button.classList.remove('is-busy');
|
||
button.textContent = originalLabel;
|
||
}
|
||
}
|
||
|
||
function updateWizardProtocolVisibility() {
|
||
var grpcTools = document.getElementById('wizard-grpc-live-tools');
|
||
if (grpcTools) grpcTools.hidden = wizardProtocol !== 'grpc';
|
||
updateStreamingModeOptions();
|
||
updateStreamingConfigVisibility();
|
||
}
|
||
|
||
function showWizardLiveStatus(title, text, isError) {
|
||
var root = document.getElementById('wizard-live-status');
|
||
var titleEl = document.getElementById('wizard-live-status-title');
|
||
var textEl = document.getElementById('wizard-live-status-text');
|
||
if (!root || !titleEl || !textEl) return;
|
||
titleEl.textContent = title;
|
||
textEl.textContent = text;
|
||
root.hidden = false;
|
||
root.classList.toggle('is-error', !!isError);
|
||
root.classList.toggle('is-success', !isError);
|
||
}
|
||
|
||
function currentDraftVersion() {
|
||
if (wizardCurrentOperation && wizardCurrentOperation.draft_version_ref) {
|
||
return wizardCurrentOperation.draft_version_ref.version;
|
||
}
|
||
if (wizardCurrentOperation && wizardCurrentOperation.current_draft_version) {
|
||
return wizardCurrentOperation.current_draft_version;
|
||
}
|
||
if (wizardCurrentVersion && wizardCurrentVersion.version) {
|
||
return wizardCurrentVersion.version;
|
||
}
|
||
return 1;
|
||
}
|
||
|
||
function updateOperationPayload(payload) {
|
||
return {
|
||
display_name: payload.display_name,
|
||
category: payload.category,
|
||
target: payload.target,
|
||
input_schema: payload.input_schema,
|
||
output_schema: payload.output_schema,
|
||
input_mapping: payload.input_mapping,
|
||
output_mapping: payload.output_mapping,
|
||
execution_config: payload.execution_config,
|
||
tool_description: payload.tool_description,
|
||
};
|
||
}
|
||
|
||
async function refreshCurrentOperationState() {
|
||
if (!wizardWorkspaceId || !wizardEditId) return null;
|
||
var detail = await window.CrankApi.getOperation(wizardWorkspaceId, wizardEditId);
|
||
var versionNumber = detail.draft_version_ref
|
||
? detail.draft_version_ref.version
|
||
: detail.current_draft_version;
|
||
var versionDocument = await window.CrankApi.getOperationVersion(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
versionNumber
|
||
);
|
||
wizardCurrentOperation = detail;
|
||
wizardCurrentVersion = versionDocument;
|
||
updateWizardProtocolVisibility();
|
||
return {
|
||
detail: detail,
|
||
version: versionDocument,
|
||
};
|
||
}
|
||
|
||
async function uploadPendingGrpcArtifacts(payload) {
|
||
if (wizardProtocol !== 'grpc' || !wizardEditId) return;
|
||
|
||
var descriptorResponse = null;
|
||
|
||
if (wizardProtoUpload) {
|
||
descriptorResponse = await window.CrankApi.uploadProtoFile(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
new TextEncoder().encode(wizardProtoUpload.content),
|
||
wizardProtoUpload.fileName
|
||
);
|
||
wizardProtoUpload = null;
|
||
}
|
||
|
||
if (wizardDescriptorSetUpload) {
|
||
descriptorResponse = await window.CrankApi.uploadDescriptorSet(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
wizardDescriptorSetUpload.bytes,
|
||
wizardDescriptorSetUpload.fileName
|
||
);
|
||
wizardDescriptorSetUpload = null;
|
||
var descriptorName = document.getElementById('wizard-descriptor-set-name');
|
||
if (descriptorName) descriptorName.textContent = tKey('wizard.descriptor.uploaded');
|
||
}
|
||
|
||
if (!descriptorResponse) return;
|
||
|
||
payload.target.descriptor_ref = descriptorResponse.descriptor_id;
|
||
payload.target.descriptor_set_b64 = '';
|
||
await window.CrankApi.updateOperation(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
updateOperationPayload(payload)
|
||
);
|
||
}
|
||
|
||
async function uploadPendingSoapArtifacts(payload) {
|
||
if (wizardProtocol !== 'soap' || !wizardEditId) return;
|
||
|
||
var wsdlResponse = null;
|
||
if (wizardSoapWsdlUpload) {
|
||
wsdlResponse = await window.CrankApi.uploadWsdlFile(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
wizardSoapWsdlUpload.bytes,
|
||
wizardSoapWsdlUpload.fileName
|
||
);
|
||
wizardSoapWsdlUpload = null;
|
||
var wsdlName = document.getElementById('wizard-soap-wsdl-name');
|
||
if (wsdlName) wsdlName.textContent = tKey('wizard.step3.soap.wsdl_uploaded');
|
||
}
|
||
|
||
if (wizardSoapXsdUpload) {
|
||
await window.CrankApi.uploadXsdFile(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
wizardSoapXsdUpload.bytes,
|
||
wizardSoapXsdUpload.fileName
|
||
);
|
||
wizardSoapXsdUpload = null;
|
||
var xsdName = document.getElementById('wizard-soap-xsd-name');
|
||
if (xsdName) xsdName.textContent = tKey('wizard.step3.soap.xsd_uploaded');
|
||
}
|
||
|
||
if (!wsdlResponse) return;
|
||
|
||
payload.target.wsdl_ref = wsdlResponse.descriptor_id;
|
||
await window.CrankApi.updateOperation(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
updateOperationPayload(payload)
|
||
);
|
||
}
|
||
|
||
async function persistCurrentDraft(stayOnPage) {
|
||
if (!wizardWorkspaceId) {
|
||
throw new Error(tKey('wizard.error.no_workspace'));
|
||
}
|
||
|
||
var payload = collectWizardPayload();
|
||
var response;
|
||
var createdNow = false;
|
||
|
||
if (wizardMode === 'edit' && wizardEditId) {
|
||
response = await window.CrankApi.updateOperation(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
updateOperationPayload(payload)
|
||
);
|
||
} else {
|
||
response = await window.CrankApi.createOperation(wizardWorkspaceId, payload);
|
||
createdNow = true;
|
||
wizardEditId = response.operation_id;
|
||
wizardMode = 'edit';
|
||
history.replaceState(
|
||
{},
|
||
'',
|
||
window.location.pathname + '?mode=edit&operationId=' + encodeURIComponent(wizardEditId)
|
||
);
|
||
setEditModePresentation();
|
||
_doGoToStep(currentStep);
|
||
var toolNameInput = document.getElementById('tool-name');
|
||
if (toolNameInput) toolNameInput.disabled = true;
|
||
}
|
||
|
||
await uploadPendingGrpcArtifacts(payload);
|
||
await uploadPendingSoapArtifacts(payload);
|
||
await refreshCurrentOperationState();
|
||
|
||
if (stayOnPage) {
|
||
var saveDraftBtn = document.querySelector('.btn-save-draft');
|
||
if (saveDraftBtn) {
|
||
var label = saveDraftBtn.textContent;
|
||
saveDraftBtn.textContent = tKey('wizard.save.saved');
|
||
setTimeout(function() {
|
||
saveDraftBtn.textContent = label;
|
||
}, 1400);
|
||
}
|
||
}
|
||
|
||
showWizardLiveStatus(
|
||
createdNow ? tKey('wizard.save.created') : tKey('wizard.save.updated'),
|
||
tKey('wizard.save.body')
|
||
);
|
||
return response;
|
||
}
|
||
|
||
function safeStringify(value) {
|
||
if (value === null || value === undefined) return '';
|
||
if (typeof value === 'string') return value;
|
||
return JSON.stringify(value, null, 2);
|
||
}
|
||
|
||
function setTextareaValue(id, value) {
|
||
var element = document.getElementById(id);
|
||
if (!element) return;
|
||
element.value = safeStringify(value);
|
||
}
|
||
|
||
function describeWizardTestResult(result) {
|
||
if (result.stream_session && result.stream_session.session_id) {
|
||
return {
|
||
title: tKey('wizard.test.session_started'),
|
||
body: tfKey('wizard.test.session_started_body', {
|
||
session_id: result.stream_session.session_id,
|
||
poll_after_ms: result.stream_session.poll_after_ms
|
||
}),
|
||
isError: false,
|
||
};
|
||
}
|
||
|
||
if (result.async_job && result.async_job.job_id) {
|
||
return {
|
||
title: tKey('wizard.test.async_job_started'),
|
||
body: tfKey('wizard.test.async_job_started_body', {
|
||
job_id: result.async_job.job_id
|
||
}),
|
||
isError: false,
|
||
};
|
||
}
|
||
|
||
if (result.window) {
|
||
var bodyParts = [
|
||
tKey('wizard.test.window_completed_body')
|
||
];
|
||
if (result.window.truncated) {
|
||
bodyParts.push(tKey('wizard.test.window_truncated_note'));
|
||
}
|
||
if (result.window.has_more) {
|
||
bodyParts.push(tKey('wizard.test.window_more_note'));
|
||
}
|
||
if (!result.window.window_complete) {
|
||
bodyParts.push(tKey('wizard.test.window_incomplete_note'));
|
||
}
|
||
return {
|
||
title: result.ok ? tKey('wizard.test.window_completed') : tKey('wizard.test.failed'),
|
||
body: bodyParts.join(' '),
|
||
isError: !result.ok,
|
||
};
|
||
}
|
||
|
||
return {
|
||
title: result.ok ? tKey('wizard.test.completed') : tKey('wizard.test.failed'),
|
||
body: result.ok ? tKey('wizard.test.completed_body') : tKey('wizard.test.failed_body'),
|
||
isError: !result.ok,
|
||
};
|
||
}
|
||
|
||
async function uploadInputSampleFromWizard() {
|
||
await persistCurrentDraft(true);
|
||
await window.CrankApi.uploadInputSample(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
parseStructuredText(textValue('wizard-input-sample'))
|
||
);
|
||
showWizardLiveStatus(tKey('wizard.sample.input_saved'), tKey('wizard.sample.input_saved_body'));
|
||
}
|
||
|
||
async function uploadOutputSampleFromWizard() {
|
||
await persistCurrentDraft(true);
|
||
await window.CrankApi.uploadOutputSample(
|
||
wizardWorkspaceId,
|
||
wizardEditId,
|
||
parseStructuredText(textValue('wizard-output-sample'))
|
||
);
|
||
showWizardLiveStatus(tKey('wizard.sample.output_saved'), tKey('wizard.sample.output_saved_body'));
|
||
}
|
||
|
||
async function generateDraftFromWizard() {
|
||
await persistCurrentDraft(true);
|
||
var generated = await window.CrankApi.generateDraft(wizardWorkspaceId, wizardEditId, {});
|
||
setValue('tool-input-schema', JSON.stringify(crankSchemaToJsonSchema(generated.input_schema), null, 2));
|
||
setValue('tool-output-schema', JSON.stringify(crankSchemaToJsonSchema(generated.output_schema), null, 2));
|
||
setValue('tool-input-mapping', mappingSetToEditorValue(generated.input_mapping, 'input', wizardProtocol));
|
||
setValue('tool-output-mapping', mappingSetToEditorValue(generated.output_mapping, 'output', wizardProtocol));
|
||
showWizardLiveStatus(tKey('wizard.sample.generated'), tKey('wizard.sample.generated_body'));
|
||
}
|
||
|
||
async function runWizardTest() {
|
||
await persistCurrentDraft(true);
|
||
var result = await window.CrankApi.runOperationTest(wizardWorkspaceId, wizardEditId, {
|
||
version: currentDraftVersion(),
|
||
input: parseStructuredText(textValue('wizard-test-input')),
|
||
});
|
||
wizardTestResponsePreview = result.response_preview;
|
||
setTextareaValue('wizard-test-request-preview', result.request_preview);
|
||
setTextareaValue('wizard-test-response-preview', result.response_preview);
|
||
setTextareaValue('wizard-test-errors', result.errors && result.errors.length ? result.errors : []);
|
||
var status = describeWizardTestResult(result);
|
||
showWizardLiveStatus(
|
||
status.title,
|
||
status.body,
|
||
status.isError
|
||
);
|
||
}
|
||
|
||
function copyTestResponseToOutputSample() {
|
||
if (!wizardTestResponsePreview) {
|
||
showWizardLiveStatus(tKey('wizard.test.no_response'), tKey('wizard.test.no_response_body'), true);
|
||
return;
|
||
}
|
||
setTextareaValue('wizard-output-sample', wizardTestResponsePreview);
|
||
showWizardLiveStatus(tKey('wizard.test.response_copied'), tKey('wizard.test.response_copied_body'));
|
||
}
|