From 786447a852e734232e75d79c2257a1a16ab42aab Mon Sep 17 00:00:00 2001 From: MattiasSp Date: Fri, 27 Sep 2024 11:17:01 +0200 Subject: [PATCH] New requestMethod `GET` to get and show response --- scss/_infowindow.scss | 4 +++ src/infowindow.js | 16 ++++++++- src/infowindow_exporthandler.js | 61 +++++++++++++++++++++++++++++++-- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/scss/_infowindow.scss b/scss/_infowindow.scss index 3325a9ec3..12a1ce376 100644 --- a/scss/_infowindow.scss +++ b/scss/_infowindow.scss @@ -239,6 +239,10 @@ text-align: left; } +.export-response-container { + margin: 0.5em 0.5em 0.5em 3.6em; +} + .toaster { border-radius: 0.5em; box-shadow: 0 0 8px #888; diff --git a/src/infowindow.js b/src/infowindow.js index 8b3210841..7d684cd5c 100644 --- a/src/infowindow.js +++ b/src/infowindow.js @@ -10,6 +10,7 @@ let exportContainer; let groupFooterContainer; let sublists; let subexports; +let subexportResponses; let urvalElements; let footerContainers; let expandableContents; @@ -203,6 +204,8 @@ function showSelectedList(selectionGroup) { } const subexportToAppend = subexports.get(selectionGroup); exportContainer.appendChild(subexportToAppend); + const subexportResponseToAppend = subexportResponses.get(selectionGroup); + exportContainer.appendChild(subexportResponseToAppend); selectionManager.clearHighlightedFeatures(); selectionManager.refreshAllLayers(); urvalElements.forEach((value, key) => { @@ -238,8 +241,18 @@ function createUrvalElement(selectionGroup, selectionGroupTitle) { const footerContainer = document.createElement('div'); footerContainers.set(selectionGroup, footerContainer); - const subexportComponent = createSubexportComponent({ selectionGroup, viewer, exportOptions }); + // Updates the response content for the given selectionGroup + const responseHandler = function responseHandler(responseSelectionGroup, text) { + const responseContainer = subexportResponses.get(responseSelectionGroup); + responseContainer.innerHTML = text; + }; + + const subexportComponent = createSubexportComponent({ selectionGroup, viewer, exportOptions, responseHandler }); subexports.set(selectionGroup, subexportComponent); + + const subexportResponseComponent = document.createElement('div'); + subexportResponseComponent.classList.add('export-response-container'); + subexportResponses.set(selectionGroup, subexportResponseComponent); } function createExpandableContent(listElementContentContainer, content, elementId) { @@ -427,6 +440,7 @@ function init(options) { exportOptions = infowindowOptions.export || {}; sublists = new Map(); subexports = new Map(); + subexportResponses = new Map(); urvalElements = new Map(); expandableContents = new Map(); footerContainers = new Map(); diff --git a/src/infowindow_exporthandler.js b/src/infowindow_exporthandler.js index e04d64530..6a260aba0 100644 --- a/src/infowindow_exporthandler.js +++ b/src/infowindow_exporthandler.js @@ -52,6 +52,45 @@ export function simpleExportHandler(simpleExportUrl, activeLayer, selectedItems, }); } +/** + * Makes a HEAD request to find out what the content type of the response will be, so that the + * response can be handled accordingly. Non-images will be blocked by CORS restrictions unless + * the server/API is set to allow the client's origin. + * @param {string} url The url to fetch + * @returns {Promise} HTML content containing the response + */ +async function fetchByContentTypes(url) { + try { + // Perform the HEAD request to check response content type + const headResponse = await fetch(url, { method: 'HEAD' }); + if (!headResponse.ok) { + throw new Error(`HEAD request failed with status: ${headResponse.status}`); + } + // Get the content-type header + const contentType = headResponse.headers.get('Content-Type'); + + // Generate content to display based on Content-Type + if (contentType.startsWith('image/')) { + return ``; + } else if (contentType.startsWith('text/plain') || contentType.startsWith('application/json')) { + const getResponse = await fetch(url, { method: 'GET' }); + if (!getResponse.ok) { + throw new Error(`GET request failed with status: ${getResponse.status}`); + } + const responseText = await getResponse.text(); + if (contentType.startsWith('text/plain')) { + return `${responseText}`; + } else if (contentType.startsWith('application/json')) { + return `
${responseText}
`; + } + } + return 'Unsupported response Content-Type'; + } catch (err) { + console.error(err); + throw err; + } +} + export function layerSpecificExportHandler(url, requestMethod, urlParameters, activeLayer, selectedItems, attributesToSendToExport, exportedFileName) { if (!url) { throw new Error('Export URL is not specified.'); @@ -108,6 +147,8 @@ export function layerSpecificExportHandler(url, requestMethod, urlParameters, ac if (requestMethod === 'OPEN') { return window.open(requestUrl, '_blank') ? Promise.resolve() : Promise.reject(); + } else if (requestMethod === 'GET') { + return fetchByContentTypes(requestUrl); } // eslint-disable-next-line consistent-return return fetch(requestUrl, { @@ -272,10 +313,12 @@ function createExportButtons( urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, + displayExportResponsePerLayer, selectionGroup, activeLayer, selectionManager, - exportOptions + exportOptions, + responseHandler ) { const roundButton = obj.button.roundButton || false; const buttonText = obj.button.buttonText || defaultText; @@ -284,6 +327,12 @@ function createExportButtons( const urlParameters = obj.urlParameters || urlParametersPerLayer; const attributesToSendToExport = obj.attributesToSendToExport || attributesToSendToExportPerLayer; const exportedFileName = obj.exportedFileName || exportedFileNamePerLayer; + const displayExportResponse = obj.displayExportResponse || displayExportResponsePerLayer || false; + // No point in creating a button that will make unnecessary GET requests + if (requestMethod === 'GET' && !displayExportResponse) { + console.warn('Skipping button for GET request with nothing to display.'); + return ''; + } const exportBtn = roundButton ? createCustomExportButton( obj.button.roundButtonIcon || defaultIcon, @@ -319,6 +368,9 @@ function createExportButtons( default: break; } + if (requestMethod === 'GET') { + responseHandler(selectionGroup, data); + } } btn.loadStop(); }) @@ -331,7 +383,7 @@ function createExportButtons( return exportBtn; } -export function createSubexportComponent({ selectionGroup, viewer, exportOptions }) { +export function createSubexportComponent({ selectionGroup, viewer, exportOptions, responseHandler }) { viewerId = viewer.getId(); const selectionManager = viewer.getSelectionManager(); // OBS! selectionGroup corresponds to a layer with the same name in most cases, but in case of a group layer it can contain selected items from all the layers in that GroupLayer. @@ -352,6 +404,7 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions const urlParametersPerLayer = layerSpecificExportOptions.urlParameters; const attributesToSendToExportPerLayer = layerSpecificExportOptions.attributesToSendToExport; const exportedFileNamePerLayer = layerSpecificExportOptions.exportedFileName; + const displayExportResponsePerLayer = layerSpecificExportOptions.displayExportResponse; exportUrls.sort((exportUrl) => (exportUrl.button.roundButton ? -1 : 1)) .forEach((obj) => { @@ -361,10 +414,12 @@ export function createSubexportComponent({ selectionGroup, viewer, exportOptions urlParametersPerLayer, attributesToSendToExportPerLayer, exportedFileNamePerLayer, + displayExportResponsePerLayer, selectionGroup, activeLayer, selectionManager, - exportOptions + exportOptions, + responseHandler ); subexportContainer.appendChild(button); });