UNPKG

@superset-ui/core

Version:
145 lines 6.19 kB
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import 'whatwg-fetch'; import fetchRetry from 'fetch-retry'; import { CACHE_AVAILABLE, CACHE_KEY, HTTP_STATUS_NOT_MODIFIED, HTTP_STATUS_OK, } from '../constants'; function tryParsePayload(payload) { try { return typeof payload === 'string' ? JSON.parse(payload) : payload; } catch (error) { throw new Error(`Invalid payload:\n\n${payload}`); } } /** * Try appending search params to an URL if needed. */ function getFullUrl(partialUrl, params) { if (params) { const url = new URL(partialUrl, window.location.href); const search = params instanceof URLSearchParams ? params : new URLSearchParams(params); // will completely override any existing search params url.search = search.toString(); return url.href; } return partialUrl; } /** * Fetch an API response and returns the corresponding json. * * @param {Payload} postPayload payload to send as FormData in a post form * @param {Payload} jsonPayload json payload to post, will automatically add Content-Type header * @param {string} stringify whether to stringify field values when post as formData */ export default async function callApi({ body, cache = 'default', credentials = 'same-origin', fetchRetryOptions, headers, method = 'GET', mode = 'same-origin', postPayload, jsonPayload, redirect = 'follow', signal, stringify = true, url: url_, searchParams, }) { const fetchWithRetry = fetchRetry(fetch, fetchRetryOptions); const url = `${getFullUrl(url_, searchParams)}`; const request = { body, cache, credentials, headers, method, mode, redirect, signal, }; if (method === 'GET' && cache !== 'no-store' && cache !== 'reload' && CACHE_AVAILABLE && window.location?.protocol === 'https:') { let supersetCache = null; try { supersetCache = await caches.open(CACHE_KEY); const cachedResponse = await supersetCache.match(url); if (cachedResponse) { // if we have a cached response, send its ETag in the // `If-None-Match` header in a conditional request const etag = cachedResponse.headers.get('Etag'); request.headers = { ...request.headers, 'If-None-Match': etag }; } } catch { // If superset is in an iframe and third-party cookies are disabled, caches.open throws } const response = await fetchWithRetry(url, request); if (supersetCache && response.status === HTTP_STATUS_NOT_MODIFIED) { const cachedFullResponse = await supersetCache.match(url); if (cachedFullResponse) { return cachedFullResponse.clone(); } throw new Error('Received 304 but no content is cached!'); } if (supersetCache && response.status === HTTP_STATUS_OK && response.headers.get('Etag')) { supersetCache.delete(url); supersetCache.put(url, response.clone()); } return response; } if (method === 'POST' || method === 'PATCH' || method === 'PUT') { if (postPayload && jsonPayload) { throw new Error('Please provide only one of jsonPayload or postPayload'); } if (postPayload instanceof FormData) { request.body = postPayload; } else if (postPayload) { const payload = tryParsePayload(postPayload); if (payload && typeof payload === 'object') { // using FormData has the effect that Content-Type header is set to `multipart/form-data`, // not e.g., 'application/x-www-form-urlencoded' const formData = new FormData(); Object.keys(payload).forEach(key => { const value = payload[key]; if (typeof value !== 'undefined') { let valueString; try { // We have seen instances where casting to String() throws error // This check allows all valid attributes to be appended to the formData // while logging error to console for any attribute that fails the cast to String valueString = stringify ? JSON.stringify(value) : String(value); } catch (e) { // eslint-disable-next-line no-console console.error(`Unable to convert attribute '${key}' to a String(). '${key}' was not added to the formData in request.body for call to ${url}`, value, e); } if (valueString !== undefined) { formData.append(key, valueString); } } }); request.body = formData; } } if (jsonPayload !== undefined) { request.body = JSON.stringify(jsonPayload); request.headers = { ...request.headers, 'Content-Type': 'application/json', }; } } return fetchWithRetry(url, request); } //# sourceMappingURL=callApi.js.map