Release 2.16.1
This commit is contained in:
1 parent
3514d514a3
commit
8ce8228091
25 files changed
+1307
-613
No files matched your search
@@ -12,9 +12,188 @@
|
||||
var panels = Array.prototype.slice.call(form.querySelectorAll('[data-api-source-auth-panel]'));
|
||||
var bearerTokenInput = form.querySelector('[data-api-source-bearer-token-input]');
|
||||
var bearerTokenToggle = form.querySelector('[data-api-source-bearer-token-toggle]');
|
||||
var requestMethodSelect = form.querySelector('[data-api-source-request-method]');
|
||||
var requestSection = form.querySelector('[data-api-source-request-section]');
|
||||
var requestBodyInput = form.querySelector('[data-api-source-request-body]');
|
||||
var testButton = form.querySelector('[data-api-auth-test-url]') || form.querySelector('[data-api-source-test-url]');
|
||||
var lastTestButton = testButton;
|
||||
var isTesting = false;
|
||||
var requestList = form.querySelector('[data-api-request-list]');
|
||||
var requestTemplate = form.querySelector('[data-api-request-template]');
|
||||
var addRequestButton = form.querySelector('[data-api-request-add]');
|
||||
var diagnostics = form.querySelector('[data-api-source-diagnostics]');
|
||||
var testStatus = form.querySelector('[data-api-source-test-status]');
|
||||
var testResults = form.querySelector('[data-api-source-test-results]');
|
||||
var copyReportButton = form.querySelector('[data-api-source-copy-report]');
|
||||
var dismissDiagnosticsButton = form.querySelector('[data-api-source-dismiss-diagnostics]');
|
||||
var diagnosticReport = null;
|
||||
|
||||
function addText(parent, tag, text, className) {
|
||||
var element = document.createElement(tag);
|
||||
element.textContent = text;
|
||||
if (className) element.className = className;
|
||||
parent.appendChild(element);
|
||||
return element;
|
||||
}
|
||||
|
||||
function addPreview(parent, title, text) {
|
||||
var details = document.createElement('details');
|
||||
details.className = 'mb-3';
|
||||
addText(details, 'summary', title);
|
||||
var preview = addText(details, 'pre', text, 'small bg-body-tertiary border rounded p-2 mt-2 mb-0');
|
||||
preview.style.cssText = 'white-space:pre-wrap;overflow-wrap:anywhere;max-height:24rem;overflow:auto;';
|
||||
parent.appendChild(details);
|
||||
}
|
||||
|
||||
function showReport(report) {
|
||||
testResults.replaceChildren();
|
||||
testStatus.textContent = report.ok ? 'Connection test passed. No changes saved.' : 'Connection test failed. No changes saved.';
|
||||
testStatus.className = report.ok ? 'mb-2 text-success' : 'mb-2 text-danger';
|
||||
if (report.error) addText(testResults, 'p', report.error, 'text-break');
|
||||
(report.steps || []).forEach(function (step) {
|
||||
var block = document.createElement('div');
|
||||
block.className = 'border-top py-2';
|
||||
addText(block, 'h4', step.stage + ': ' + (step.skipped ? 'Not required' : step.ok ? 'Passed' : 'Failed'), 'h6');
|
||||
var metadata = [step.method, step.url, step.status ? 'HTTP ' + step.status : '', step.contentType, step.durationMs !== undefined ? step.durationMs + ' ms' : ''].filter(Boolean);
|
||||
if (metadata.length) addText(block, 'p', metadata.join(' | '), 'small text-break mb-1');
|
||||
if (step.error || step.message) addText(block, 'p', step.error || step.message, 'text-break mb-1');
|
||||
if (step.response) addPreview(block, 'Sanitised response', step.response);
|
||||
testResults.appendChild(block);
|
||||
});
|
||||
if (report.preview) addPreview(testResults, 'JSON preview (up to 12,000 characters)', report.preview);
|
||||
if (report.paths && report.paths.length) {
|
||||
addPreview(testResults, 'Available field paths (sample)', report.paths.join('\n'));
|
||||
}
|
||||
}
|
||||
|
||||
function readRequest(card) {
|
||||
var request = { id: card.getAttribute('data-request-id') || null };
|
||||
Array.prototype.forEach.call(card.querySelectorAll('[data-request-field]'), function (input) {
|
||||
request[input.getAttribute('data-request-field')] = input.type === 'checkbox' ? input.checked : input.value;
|
||||
});
|
||||
return request;
|
||||
}
|
||||
|
||||
function serializeRequests() {
|
||||
return Array.prototype.map.call(requestList.querySelectorAll('[data-api-request-card]'), readRequest);
|
||||
}
|
||||
|
||||
function updateRequestCard(card) {
|
||||
var fields = readRequest(card);
|
||||
card.querySelector('[data-api-request-title]').textContent = 'Request - ' + (fields.name || 'Unnamed request');
|
||||
card.querySelector('[data-api-request-body-section]').hidden = fields.request_method !== 'POST';
|
||||
}
|
||||
|
||||
function updateRequestFromEvent(event) {
|
||||
var card = event.target.closest('[data-api-request-card]');
|
||||
if (card) updateRequestCard(card);
|
||||
}
|
||||
|
||||
if (requestList) {
|
||||
form.addEventListener('formdata', function (event) {
|
||||
event.formData.set('requests_json', JSON.stringify(serializeRequests()));
|
||||
});
|
||||
requestList.addEventListener('input', updateRequestFromEvent);
|
||||
requestList.addEventListener('change', updateRequestFromEvent);
|
||||
requestList.addEventListener('click', function (event) {
|
||||
var remove = event.target.closest('[data-api-request-remove]');
|
||||
if (remove && !remove.disabled) {
|
||||
var card = remove.closest('[data-api-request-card]');
|
||||
if (requestList.querySelectorAll('[data-api-request-card]').length === 1) return;
|
||||
var requestName = card.querySelector('[data-request-field="name"]').value || 'Unnamed request';
|
||||
if (!window.confirm('Delete request "' + requestName + '"? This takes effect when you save the API source.')) return;
|
||||
if (lastTestButton && lastTestButton.closest && lastTestButton.closest('[data-api-request-card]') === card) {
|
||||
diagnostics.hidden = true;
|
||||
form.appendChild(diagnostics);
|
||||
lastTestButton = testButton;
|
||||
}
|
||||
card.remove();
|
||||
addRequestButton.focus();
|
||||
}
|
||||
var button = event.target.closest('[data-api-source-test-url]');
|
||||
if (button && button !== testButton) testConnection({ currentTarget: button });
|
||||
updateRemoveButtons();
|
||||
});
|
||||
addRequestButton.addEventListener('click', function () {
|
||||
if (requestList.querySelectorAll('[data-api-request-card]').length >= 50) return;
|
||||
requestList.appendChild(requestTemplate.content.cloneNode(true));
|
||||
var cards = requestList.querySelectorAll('[data-api-request-card]');
|
||||
var input = cards[cards.length - 1].querySelector('[data-request-field="name"]');
|
||||
var names = Array.prototype.map.call(cards, function (card) { return card.querySelector('[data-request-field="name"]').value.toLowerCase(); });
|
||||
var suffix = cards.length;
|
||||
while (names.indexOf('request ' + suffix) !== -1) suffix += 1;
|
||||
input.value = 'Request ' + suffix;
|
||||
updateRequestCard(cards[cards.length - 1]);
|
||||
updateRemoveButtons();
|
||||
input.focus();
|
||||
input.select();
|
||||
});
|
||||
updateRemoveButtons();
|
||||
}
|
||||
|
||||
function updateRemoveButtons() {
|
||||
if (!requestList) return;
|
||||
var cards = requestList.querySelectorAll('[data-api-request-card]');
|
||||
addRequestButton.disabled = cards.length >= 50;
|
||||
Array.prototype.forEach.call(cards, function (card) {
|
||||
var button = card.querySelector('[data-api-request-remove]');
|
||||
if (!button.hasAttribute('data-request-protected')) {
|
||||
button.disabled = cards.length <= 1;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function testConnection(event) {
|
||||
if (isTesting) return;
|
||||
isTesting = true;
|
||||
var activeButton = event && event.currentTarget || testButton;
|
||||
lastTestButton = activeButton;
|
||||
var card = activeButton.closest ? activeButton.closest('[data-api-request-card]') : null;
|
||||
var anchor = card || (authCard && !authCard.hidden ? authCard : activeButton.closest && activeButton.closest('.card'));
|
||||
if (anchor) anchor.after(diagnostics);
|
||||
diagnostics.hidden = false;
|
||||
diagnostics.setAttribute('aria-busy', 'true');
|
||||
activeButton.disabled = true;
|
||||
copyReportButton.disabled = true;
|
||||
diagnosticReport = null;
|
||||
testResults.replaceChildren();
|
||||
testStatus.className = 'mb-2';
|
||||
testStatus.textContent = 'Testing connection...';
|
||||
try {
|
||||
var payload = Object.fromEntries(new FormData(form));
|
||||
if (card) Object.assign(payload, readRequest(card));
|
||||
var response = await fetch(activeButton.getAttribute('data-api-auth-test-url') || activeButton.getAttribute('data-api-source-test-url'), {
|
||||
method: 'POST', credentials: 'same-origin',
|
||||
headers: { 'Content-Type': 'application/json', 'X-Requested-With': 'XMLHttpRequest' },
|
||||
body: JSON.stringify(payload)
|
||||
});
|
||||
if (!(response.headers.get('content-type') || '').includes('application/json')) {
|
||||
throw new Error('Connection test unavailable (HTTP ' + response.status + '). Check your session and permissions.');
|
||||
}
|
||||
diagnosticReport = await response.json();
|
||||
showReport(diagnosticReport);
|
||||
copyReportButton.disabled = false;
|
||||
} catch (error) {
|
||||
testStatus.className = 'mb-2 text-danger';
|
||||
testStatus.textContent = error.message || 'Unable to run connection test.';
|
||||
} finally {
|
||||
activeButton.disabled = false;
|
||||
isTesting = false;
|
||||
diagnostics.setAttribute('aria-busy', 'false');
|
||||
}
|
||||
}
|
||||
|
||||
if (testButton) testButton.addEventListener('click', testConnection);
|
||||
if (dismissDiagnosticsButton) dismissDiagnosticsButton.addEventListener('click', function () {
|
||||
diagnostics.hidden = true;
|
||||
if (lastTestButton && lastTestButton.isConnected !== false) lastTestButton.focus();
|
||||
});
|
||||
if (copyReportButton) copyReportButton.addEventListener('click', async function () {
|
||||
if (!diagnosticReport) return;
|
||||
try {
|
||||
await navigator.clipboard.writeText(JSON.stringify(diagnosticReport, null, 2));
|
||||
testStatus.textContent = 'Diagnostic report copied.';
|
||||
} catch (_error) {
|
||||
testStatus.textContent = 'Clipboard unavailable. Select the diagnostic results to copy them.';
|
||||
}
|
||||
});
|
||||
|
||||
function updateBearerTokenToggle() {
|
||||
if (!bearerTokenInput || !bearerTokenToggle) {
|
||||
@@ -61,30 +240,16 @@
|
||||
});
|
||||
}
|
||||
|
||||
function updateRequestBodyVisibility() {
|
||||
if (!requestMethodSelect || !requestSection || !requestBodyInput) {
|
||||
return;
|
||||
}
|
||||
|
||||
var isPost = String(requestMethodSelect.value || 'GET').toUpperCase() === 'POST';
|
||||
requestSection.hidden = !isPost;
|
||||
}
|
||||
|
||||
if (methodSelect) {
|
||||
methodSelect.addEventListener('change', function () {
|
||||
updatePanels(true);
|
||||
});
|
||||
}
|
||||
|
||||
if (requestMethodSelect) {
|
||||
requestMethodSelect.addEventListener('change', updateRequestBodyVisibility);
|
||||
}
|
||||
|
||||
if (bearerTokenToggle && bearerTokenInput) {
|
||||
bearerTokenToggle.addEventListener('click', toggleBearerTokenVisibility);
|
||||
updateBearerTokenToggle();
|
||||
}
|
||||
|
||||
updatePanels(false);
|
||||
updateRequestBodyVisibility();
|
||||
}());
|
||||
Reference in new issue
Block a user