/** * @prettier */ import { createSelector } from "reselect" import { Map } from "immutable" import win from "core/window" export default function downloadUrlPlugin(toolbox) { let { fn } = toolbox const actions = { download: (url) => ({ errActions, specSelectors, specActions, getConfigs }) => { let { fetch } = fn const config = getConfigs() url = url || specSelectors.url() specActions.updateLoadingStatus("loading") errActions.clear({ source: "fetch" }) fetch({ url, loadSpec: true, requestInterceptor: config.requestInterceptor || ((a) => a), responseInterceptor: config.responseInterceptor || ((a) => a), credentials: "same-origin", headers: { Accept: "application/json,*/*", }, }).then(next, next) function next(res) { if (res instanceof Error || res.status >= 400) { specActions.updateLoadingStatus("failed") errActions.newThrownErr( Object.assign( new Error((res.message || res.statusText) + " " + url), { source: "fetch" } ) ) // Check if the failure was possibly due to CORS or mixed content if (!res.status && res instanceof Error) checkPossibleFailReasons() return } specActions.updateLoadingStatus("success") specActions.updateSpec(res.text) if (specSelectors.url() !== url) { specActions.updateUrl(url) } } function checkPossibleFailReasons() { try { let specUrl if ("URL" in win) { specUrl = new URL(url) } else { // legacy browser, use to parse the URL specUrl = document.createElement("a") specUrl.href = url } if ( specUrl.protocol !== "https:" && win.location.protocol === "https:" ) { const error = Object.assign( new Error( `Possible mixed-content issue? The page was loaded over https:// but a ${specUrl.protocol}// URL was specified. Check that you are not attempting to load mixed content.` ), { source: "fetch" } ) errActions.newThrownErr(error) return } if (specUrl.origin !== win.location.origin) { const error = Object.assign( new Error( `Possible cross-origin (CORS) issue? The URL origin (${specUrl.origin}) does not match the page (${win.location.origin}). Check the server returns the correct 'Access-Control-Allow-*' headers.` ), { source: "fetch" } ) errActions.newThrownErr(error) } } catch (e) { return } } }, updateLoadingStatus: (status) => { let enums = [null, "loading", "failed", "success", "failedConfig"] if (enums.indexOf(status) === -1) { console.error(`Error: ${status} is not one of ${JSON.stringify(enums)}`) } return { type: "spec_update_loading_status", payload: status, } }, } let reducers = { spec_update_loading_status: (state, action) => { return typeof action.payload === "string" ? state.set("loadingStatus", action.payload) : state }, } let selectors = { loadingStatus: createSelector( (state) => { return state || Map() }, (spec) => spec.get("loadingStatus") || null ), } return { statePlugins: { spec: { actions, reducers, selectors }, }, } }