export default async function callApi()

in superset-frontend/packages/superset-ui-core/src/connection/callApi/callApi.ts [62:181]


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,
}: CallApi): Promise<Response> {
  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: Cache | null = 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') as string;
        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: FormData = new FormData();
        Object.keys(payload).forEach(key => {
          const value = (payload as JsonObject)[key] as JsonValue;
          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);
}