ajax/ajax.js

/**
 * @file Ajax Utility functions
 * @category Ajax Utils
 * @module AjaxUtils
 */

/**
 * Make HTTP Fetch API call returning the JSON in the promise resolve
 * @function fetchApi
 * @param {string} httpMethod - GET, POST
 * @param {string} url - URL
 * @param {object} payload - body
 * @param {object} headers - HTTP Headers
 * @returns {object} promise
 */
export const fetchApi = (httpMethod, url, payload, headers) => {
  let httpHeaders = new Headers({
    "Content-Type": "application/json",
    Accept: "application/json",
  });
  if (headers) {
    headers.forEach((value, key) => {
      httpHeaders.append(key, value);
    });
  }
  return new Promise((resolve, reject) => {
    let options = { method: httpMethod, headers: httpHeaders };
    if ((httpMethod === "POST" || httpMethod === "PATCH") && payload) {
      options = { ...options, body: JSON.stringify(payload) };
    }

    fetch(url, options)
      .then((response) => {
        if (response) {
          if (response.ok) {
            return response.json();
          }
          return response.json().then((error) => {
            console.log(error);
            reject(error);
          });
        }
      })
      .then((jsonData) => {
        console.log(`${httpMethod}: ${url}`, jsonData);

        resolve(jsonData);
      })
      .catch((error) => {
        console.log(`${httpMethod}: ${url}`, error);
        reject(error);
      });
  });
};

/**
 * This callback is displayed as a global member.
 * @callback interceptAjaxResponseCallback
 * @param {string} responseURL - Ajax Response URL
 * @param {string} responseText - Ajax response Text
 */

/**
 * Intercepts AJAX requests by overwriting the XMLHttpRequest prototype.
 * @function interceptAjaxResponse
 * @param {string} pageUrl - The page URL that this function is required on. This is important as the function should be restricted as much as possible
 * @param {string} responseURL - The specific AJAX response URL the data is required from
 * @param {boolean} pageURLsubstringMatch - If true - the pageURL can be matched as a substring. If false, the pageURL is matched as an absolute match
 * @param {boolean} responseURLsubstringMatch - If true - the responseURL can be matched as a substring. If false, the responseURL is matched as an absolute match
 * @param {interceptAjaxResponseCallback} cb - callback. returning the RepsonseURL and the RepsonseText
 */
export const interceptAjaxResponse = (
  pageUrl,
  responseURL,
  pageURLsubstringMatch,
  responseURLsubstringMatch,
  cb
) => {
  // Check if the URL is a match
  if (pageURLsubstringMatch) {
    if (document.location.href.indexOf(pageUrl) === -1) {
      return;
    }
  } else {
    if (document.location.href !== pageUrl) {
      return;
    }
  }

  // Update the AJAX prototype
  const send = XMLHttpRequest.prototype.send;

  XMLHttpRequest.prototype.send = function () {
    this.addEventListener("load", function () {
      try {
        if (this && this.responseURL && this.responseText) {
          // Look for responseURL before changing code
          if (responseURLsubstringMatch) {
            if (this.responseURL.indexOf(responseURL) > -1) {
              const responseURL = this.responseURL;
              const responseText = this.responseText;
              cb(responseURL, responseText);
            }
          } else {
            if (this.responseURL === responseURL) {
              const responseURL = this.responseURL;
              const responseText = this.responseText;
              cb(responseURL, responseText);
            }
          }
        }
      } catch (error) {
        console.log("interceptAjaxResponse Error: " + error);
      }
    });

    return send.apply(this, arguments);
  };
};

/**
 * Generate UUID for creating AJAX requests
 * @function getUUID
 * @returns {string} returns random 16 char length string
 */
export const getUUID = () => {
  return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) =>
    (
      c ^
      (crypto.getRandomValues(new Uint8Array(1))[0] & (15 >> (c / 4)))
    ).toString(16)
  );
};