feat: complete Epic 1 production foundation
This commit is contained in:
+111
-10
@@ -1,3 +1,25 @@
|
||||
var wizardOperationLoadGeneration = 0;
|
||||
var wizardLifecycleActionBusy = false;
|
||||
var wizardTestConfirmationToken = null;
|
||||
|
||||
function operationErrorCode(error) {
|
||||
return error && error.payload && error.payload.error
|
||||
? error.payload.error.code
|
||||
: '';
|
||||
}
|
||||
|
||||
function showStaleOperationError(error) {
|
||||
if (operationErrorCode(error) !== 'operation_stale_version'
|
||||
&& operationErrorCode(error) !== 'operation_precondition_required') {
|
||||
return false;
|
||||
}
|
||||
showWizardLiveStatus(tKey('wizard.stale.title'), tKey('wizard.stale.body'), true);
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(tKey('wizard.stale.body'), tKey('wizard.stale.title'));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function buildToolDescription() {
|
||||
var snapshot = wizardCurrentVersion && wizardCurrentVersion.snapshot
|
||||
? wizardCurrentVersion.snapshot
|
||||
@@ -98,6 +120,7 @@ async function saveOperation(stayOnPage) {
|
||||
try {
|
||||
await persistCurrentDraft(stayOnPage);
|
||||
} catch (error) {
|
||||
if (showStaleOperationError(error)) return;
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || tKey('wizard.error.save'), tKey('wizard.error.save_title'));
|
||||
}
|
||||
@@ -106,14 +129,23 @@ async function saveOperation(stayOnPage) {
|
||||
|
||||
async function loadOperationForEdit() {
|
||||
if (!wizardWorkspaceId || !wizardEditId) return;
|
||||
var detail = await window.CrankApi.getOperation(wizardWorkspaceId, wizardEditId);
|
||||
var generation = ++wizardOperationLoadGeneration;
|
||||
var requestedWorkspaceId = wizardWorkspaceId;
|
||||
var requestedOperationId = wizardEditId;
|
||||
var detail = await window.CrankApi.getOperation(requestedWorkspaceId, requestedOperationId);
|
||||
if (generation !== wizardOperationLoadGeneration
|
||||
|| requestedWorkspaceId !== wizardWorkspaceId
|
||||
|| requestedOperationId !== wizardEditId) return;
|
||||
wizardProtocol = detail.protocol || 'rest';
|
||||
await loadWizardPanels([3]);
|
||||
var draftVersion = await window.CrankApi.getOperationVersion(
|
||||
wizardWorkspaceId,
|
||||
wizardEditId,
|
||||
requestedWorkspaceId,
|
||||
requestedOperationId,
|
||||
detail.draft_version_ref.version
|
||||
);
|
||||
if (generation !== wizardOperationLoadGeneration
|
||||
|| requestedWorkspaceId !== wizardWorkspaceId
|
||||
|| requestedOperationId !== wizardEditId) return;
|
||||
wizardCurrentOperation = detail;
|
||||
wizardCurrentVersion = draftVersion;
|
||||
prefillWizardFromEdit(detail, draftVersion);
|
||||
@@ -150,6 +182,8 @@ function bindWizardLiveActions() {
|
||||
bindLiveAction('wizard-run-test', tKey('wizard.busy.test'), runWizardTest);
|
||||
bindLiveAction('wizard-run-quality', tKey('wizard.busy.quality'), analyzeWizardQuality);
|
||||
bindClick('wizard-copy-test-response', copyTestResponseToOutputSample);
|
||||
bindClick('wizard-copy-request-id', function() { copyCorrelationId('wizard-test-request-id'); });
|
||||
bindClick('wizard-copy-trace-id', function() { copyCorrelationId('wizard-test-trace-id'); });
|
||||
bindClick('wizard-copy-agent-preview', copyAgentFacingPreview);
|
||||
bindLiveAction('wizard-export-yaml', tKey('wizard.busy.export_yaml'), exportWizardYaml);
|
||||
bindLiveAction('wizard-import-yaml', tKey('wizard.busy.import_yaml'), importWizardYaml);
|
||||
@@ -180,6 +214,7 @@ function bindClick(id, handler) {
|
||||
function bindLiveAction(id, busyLabel, handler) {
|
||||
var element = document.getElementById(id);
|
||||
if (!element) return;
|
||||
element.dataset.lifecycleAction = 'true';
|
||||
element.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
runWizardLiveAction(element, busyLabel, handler);
|
||||
@@ -233,11 +268,17 @@ function bindApprovalPolicyControls() {
|
||||
}
|
||||
|
||||
async function runWizardLiveAction(button, busyLabel, handler) {
|
||||
if (!button || button.dataset.busy === 'true') {
|
||||
if (!button || button.dataset.busy === 'true' || wizardLifecycleActionBusy) {
|
||||
return;
|
||||
}
|
||||
|
||||
var originalLabel = button.textContent;
|
||||
wizardLifecycleActionBusy = true;
|
||||
var lifecycleButtons = Array.from(document.querySelectorAll('[data-lifecycle-action="true"]'));
|
||||
lifecycleButtons.forEach(function(element) {
|
||||
element.disabled = true;
|
||||
element.setAttribute('aria-busy', 'true');
|
||||
});
|
||||
button.dataset.busy = 'true';
|
||||
button.disabled = true;
|
||||
button.classList.add('is-busy');
|
||||
@@ -246,6 +287,7 @@ async function runWizardLiveAction(button, busyLabel, handler) {
|
||||
try {
|
||||
await handler();
|
||||
} catch (error) {
|
||||
if (showStaleOperationError(error)) return;
|
||||
showWizardLiveStatus(
|
||||
tKey('wizard.live.failed_title'),
|
||||
error && error.message ? error.message : tKey('wizard.live.failed_body'),
|
||||
@@ -258,8 +300,12 @@ async function runWizardLiveAction(button, busyLabel, handler) {
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
wizardLifecycleActionBusy = false;
|
||||
lifecycleButtons.forEach(function(element) {
|
||||
element.disabled = false;
|
||||
element.removeAttribute('aria-busy');
|
||||
});
|
||||
button.dataset.busy = 'false';
|
||||
button.disabled = false;
|
||||
button.classList.remove('is-busy');
|
||||
button.textContent = originalLabel;
|
||||
}
|
||||
@@ -515,6 +561,15 @@ function copyAgentFacingPreview() {
|
||||
}
|
||||
}
|
||||
|
||||
function copyCorrelationId(elementId) {
|
||||
var element = document.getElementById(elementId);
|
||||
var value = element ? element.textContent : '';
|
||||
if (!value || !navigator.clipboard || !navigator.clipboard.writeText) return;
|
||||
navigator.clipboard.writeText(value).then(function() {
|
||||
showWizardLiveStatus(tKey('wizard.test.id_copied'), tKey('wizard.test.id_copied_body'));
|
||||
});
|
||||
}
|
||||
|
||||
function severityLabel(severity) {
|
||||
if (severity === 'error') return tKey('wizard.quality.severity_error');
|
||||
if (severity === 'warning') return tKey('wizard.quality.severity_warning');
|
||||
@@ -637,7 +692,7 @@ function renderImportQualityFindings(versionDocument) {
|
||||
|
||||
var empty = document.getElementById('wizard-quality-empty');
|
||||
if (empty) {
|
||||
empty.textContent = 'Рекомендации из OpenAPI import. Запустите проверку качества, чтобы пересчитать их по текущему черновику.';
|
||||
empty.textContent = tKey('wizard.quality.import_findings_hint');
|
||||
empty.hidden = false;
|
||||
}
|
||||
}
|
||||
@@ -679,7 +734,12 @@ async function importWizardYaml() {
|
||||
showWizardLiveStatus(tKey('wizard.yaml.none'), tKey('wizard.yaml.none_body'), true);
|
||||
return;
|
||||
}
|
||||
var imported = await window.CrankApi.importOperation(wizardWorkspaceId, yamlDocument, 'upsert');
|
||||
var imported = await window.CrankApi.importOperation(
|
||||
wizardWorkspaceId,
|
||||
yamlDocument,
|
||||
'upsert',
|
||||
wizardEditId
|
||||
);
|
||||
if (Array.isArray(imported.warnings) && imported.warnings.length > 0) {
|
||||
sessionStorage.setItem('crank_import_guidance', JSON.stringify(imported.warnings));
|
||||
}
|
||||
@@ -688,6 +748,7 @@ async function importWizardYaml() {
|
||||
}
|
||||
|
||||
async function publishWizardOperation() {
|
||||
if (!confirm(tKey('wizard.publish.confirm'))) return;
|
||||
var report = await analyzeWizardQuality();
|
||||
if (report.blocking) {
|
||||
throw new Error(tKey('wizard.quality.blocking_error'));
|
||||
@@ -703,6 +764,7 @@ async function publishWizardOperation() {
|
||||
tKey('wizard.publish.done'),
|
||||
tfKey('wizard.publish.done_body', { version: published.published_version })
|
||||
);
|
||||
if (window.CrankOnboarding) window.CrankOnboarding.signalRefresh();
|
||||
}
|
||||
|
||||
function handleImportYamlFileSelection(event) {
|
||||
@@ -724,6 +786,17 @@ function describeWizardTestResult(result) {
|
||||
};
|
||||
}
|
||||
|
||||
function localizeWizardTestErrors(errors) {
|
||||
return (Array.isArray(errors) ? errors : []).map(function(error) {
|
||||
if (!error || !error.code) return error;
|
||||
var key = 'execution.error.' + error.code;
|
||||
var localized = tKey(key);
|
||||
return Object.assign({}, error, {
|
||||
message: localized === key ? error.message : localized,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function uploadInputSampleFromWizard() {
|
||||
await persistCurrentDraft(true);
|
||||
await window.CrankApi.uploadInputSample(
|
||||
@@ -759,21 +832,49 @@ async function generateDraftFromWizard() {
|
||||
}
|
||||
|
||||
async function runWizardTest() {
|
||||
var generation = wizardOperationLoadGeneration;
|
||||
var requestedWorkspaceId = wizardWorkspaceId;
|
||||
var requestedOperationId = wizardEditId;
|
||||
await persistCurrentDraft(true);
|
||||
var result = await window.CrankApi.runOperationTest(wizardWorkspaceId, wizardEditId, {
|
||||
if (generation !== wizardOperationLoadGeneration
|
||||
|| requestedWorkspaceId !== wizardWorkspaceId
|
||||
|| requestedOperationId !== wizardEditId) return;
|
||||
var result = await window.CrankApi.runOperationTest(requestedWorkspaceId, requestedOperationId, {
|
||||
version: currentDraftVersion(),
|
||||
input: parseStructuredText(textValue('wizard-test-input')),
|
||||
confirmation_token: wizardTestConfirmationToken,
|
||||
locale: localStorage.getItem('crank_lang') || 'en',
|
||||
});
|
||||
if (generation !== wizardOperationLoadGeneration
|
||||
|| requestedWorkspaceId !== wizardWorkspaceId
|
||||
|| requestedOperationId !== wizardEditId) return;
|
||||
var confirmationError = Array.isArray(result.errors)
|
||||
? result.errors.find(function(error) { return error && error.code === 'confirmation_required'; })
|
||||
: null;
|
||||
wizardTestConfirmationToken = confirmationError && confirmationError.context
|
||||
? confirmationError.context.confirmation_token || null
|
||||
: null;
|
||||
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 : []);
|
||||
setTextareaValue('wizard-test-errors', localizeWizardTestErrors(result.errors));
|
||||
var status = describeWizardTestResult(result);
|
||||
var requestId = document.getElementById('wizard-test-request-id');
|
||||
var traceId = document.getElementById('wizard-test-trace-id');
|
||||
var correlationRoot = document.getElementById('wizard-test-correlation');
|
||||
if (requestId) requestId.textContent = result.request_id || '';
|
||||
if (traceId) traceId.textContent = result.trace_id || '';
|
||||
if (correlationRoot) correlationRoot.hidden = !(result.request_id && result.trace_id);
|
||||
var correlation = tfKey('wizard.test.correlation', {
|
||||
requestId: result.request_id || '—',
|
||||
traceId: result.trace_id || '—'
|
||||
});
|
||||
showWizardLiveStatus(
|
||||
status.title,
|
||||
status.body,
|
||||
status.body + '\n' + correlation,
|
||||
status.isError
|
||||
);
|
||||
if (window.CrankOnboarding) window.CrankOnboarding.signalRefresh();
|
||||
}
|
||||
|
||||
function copyTestResponseToOutputSample() {
|
||||
|
||||
Reference in New Issue
Block a user