diff --git a/apps/ui/js/wizard-live.js b/apps/ui/js/wizard-live.js new file mode 100644 index 0000000..ab7e703 --- /dev/null +++ b/apps/ui/js/wizard-live.js @@ -0,0 +1,500 @@ +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 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')); +} + +window.CrankWizardLive = { + saveOperation: saveOperation, + loadOperationForEdit: loadOperationForEdit, + bindWizardLiveActions: bindWizardLiveActions, + updateWizardProtocolVisibility: updateWizardProtocolVisibility, +}; diff --git a/apps/ui/js/wizard.js b/apps/ui/js/wizard.js index d557a16..20d823d 100644 --- a/apps/ui/js/wizard.js +++ b/apps/ui/js/wizard.js @@ -16,6 +16,10 @@ var step3PanelId = window.CrankWizardShell.step3PanelId; var bindStreamingConfigControls = window.CrankStreamingForm.bindStreamingConfigControls; var collectStreamingConfig = window.CrankStreamingForm.collectStreamingConfig; var applyStreamingConfig = window.CrankStreamingForm.applyStreamingConfig; +var saveOperation = window.CrankWizardLive.saveOperation; +var loadOperationForEdit = window.CrankWizardLive.loadOperationForEdit; +var bindWizardLiveActions = window.CrankWizardLive.bindWizardLiveActions; +var updateWizardProtocolVisibility = window.CrankWizardLive.updateWizardProtocolVisibility; var currentStep = window.currentStep; var wizardProtocol = window.wizardProtocol; var wizardMode = window.wizardMode; @@ -261,497 +265,3 @@ function parseStructuredText(text) { return window.jsyaml.load(text) || {}; } } - -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 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')); -} diff --git a/apps/ui/scripts/build.js b/apps/ui/scripts/build.js index 277ba34..ea88de7 100644 --- a/apps/ui/scripts/build.js +++ b/apps/ui/scripts/build.js @@ -106,6 +106,7 @@ const BUNDLES = { 'js/wizard-artifacts.js', 'js/wizard-upstreams.js', 'js/wizard-model.js', + 'js/wizard-live.js', 'js/wizard.js', 'js/nav.js', ],