Home Reference Source

src/providers/storage/xhr.js

import NativePromise from 'native-promise-only';
import _trim from 'lodash/trim';
export const setXhrHeaders = (formio, xhr) => {
  const { headers } = formio.options;
  if (headers) {
    const ValidHeaders = {
      'Content-Disposition': true,
      'Authorization': true,
    };

    for (const header in headers) {
      if (ValidHeaders[header]) {
        xhr.setRequestHeader(header, headers[header]);
      }
    }
  }
};
const XHR = {
  trim(text) {
    return _trim(text, '/');
  },
  path(items) {
    return items.filter(item => !!item).map(XHR.trim).join('/');
  },
  async upload(formio, type, xhrCallback, file, fileName, dir, progressCallback, groupPermissions, groupId, abortCallback, multipartOptions) {
    // make request to Form.io server
    const token = formio.getToken();
    let response;
    try {
      response = await fetch(`${formio.formUrl}/storage/${type}`, {
        method: 'POST',
        headers: {
          'Accept': 'application/json',
          'Content-Type': 'application/json; charset=UTF-8',
          ...(token ? { 'x-jwt-token': token } : {}),
        },
        body: JSON.stringify({
          name: XHR.path([dir, fileName]),
          size: file.size,
          type: file.type,
          groupPermissions,
          groupId,
          multipart: multipartOptions
        })
      });
    }
    catch (err) {
      // only throws on network errors
      err.networkError = true;
      throw err;
    }
    if (!response.ok) {
      if (response.status === 504) {
        const error = new Error('Network request failed');
        error.networkError = true;
        throw error;
      }

      const message = await response.text();
      throw new Error(message || 'Unable to sign file.');
    }
    const serverResponse = await response.json();
    return await XHR.makeXhrRequest(formio, xhrCallback, serverResponse, progressCallback, abortCallback);
  },
  makeXhrRequest(formio, xhrCallback, serverResponse, progressCallback, abortCallback) {
    return new NativePromise((resolve, reject) => {
      // Send the file with data.
      let xhr = new XMLHttpRequest();
      xhr.openAndSetHeaders = (...params) => {
        xhr.open(...params);
        setXhrHeaders(formio, xhr);
      };
      NativePromise.resolve(xhrCallback(xhr, serverResponse, abortCallback)).then((payload) => {
        // if payload is nullish we can assume the provider took care of the entire upload process
        if (!payload) {
          xhr = null;
          return resolve(serverResponse);
        }
        // Fire on network error.
        xhr.onerror = (err) => {
          err.networkError = true;
          reject(err);
        };

        // Fire on network abort.
        xhr.onabort = (err) => {
          err.networkError = true;
          reject(err);
        };

        // Set the onabort error callback.
        xhr.onabort = reject;

        if (typeof progressCallback === 'function') {
          xhr.upload.onprogress = progressCallback;
        }

        if (typeof abortCallback === 'function') {
          abortCallback(() => xhr.abort());
        }
        // Fired when the response has made it back from the server.
        xhr.onload = () => {
          if (xhr.status >= 200 && xhr.status < 300) {
            resolve(serverResponse);
          }
          else if (xhr.status === 504) {
            const error = new Error('Network request failed');
            error.networkError = true;
            reject(error);
          }
          else {
            reject(xhr.response || 'Unable to upload file');
          }
        };

        // Get the request and send it to the server.
        xhr.send(payload);
      }).catch(reject);
    });
  }
};

export default XHR;