File.js

import xmldom from '@xmldom/xmldom';
import factory from './factory.js';
import Resource from './Resource.js';
import { isBrowser } from './utils.js';

/**
 * File resources are resources located in the Entrystore repository that have a graph type of
 * none, e.g. none of the special cases for which there are special treatment in EntryStore.
 *
 * @exports store/File
 */
export default class FileResource extends Resource {
  constructor(...args) {
    super(...args);
  }

  /**
   * Pushes a file to the server for this resource.
   * In a browser environment a file is represented via an input tag which references
   * the file to be uploaded via its value attribute. E.g.:
   *
   *       <input type="file" name="uploadFile"/>
   *
   * During the uploading process the input tag will be moved temporarily in the DOM tree,
   * it will be restored to its original position afterwards (both upon success and failure).
   *
   * In a nodejs environments the file is leveraged as a stream, i.e.:
   * res.putFile(fs.createReadStream('file.txt'));
   *
   * @param {node|fileHandle} data - input tag or file handle that corresponds to a file.
   * @param {string} format - indicates the mimetype of the data
   * @todo fix-if-modified-since
   * @returns {Promise}
   */
  async putFile(data, format) {
    let url;
    // noinspection AmdModulesDependencies
    if (isBrowser() && data instanceof Node) {
      if (data.name == null || data.name === '') {
        throw new Error('Failure, cannot upload resource from input element unless a name' +
          ' attribute is provided.');
      }
      url = factory.getPutFileURI(this.getResourceURI());
    } else {
      url = this.getResourceURI();
    }
    const es = this.getEntryStore();
    const entry = await this.getEntry();
    const promise = es.getREST().putFile(url, data, format);
    es.handleAsync(promise, 'putFile');
    const response = await promise;
    entry.getEntryInfo().setModificationDate(response.header['last-modified']);
    entry.setRefreshNeeded();
    return response;
  }

  /**
   * Pushes data to the server for this resource.
   *
   * @param {string} data - to be stored as a file.
   * @param {string} format - the format of the data as a mimetype.
   * @returns {Promise}
   */
  async put(data, format = 'application/json') {
    const es = this.getEntryStore();
    const entry = await this.getEntry();
    const promise = es.getREST().put(this.getResourceURI(), data, null, format);
    es.handleAsync(promise, 'putFile');
    const response = await promise;
    entry.getEntryInfo().setModificationDate(response.header['last-modified']);
    entry.setRefreshNeeded();
    return response;
  }

  /**
   * Pushes text content to the server for this resource.
   *
   * @param {string} text - to be stored as a file, format will be set to text/plain.
   * @returns {Promise}
   */
  putText(text) {
    return this.put(text, 'text/plain');
  }

  /**
   * Pushes JSON content to the server for this resource.
   *
   * @param {Object} obj - to be stored as a json object, format will be set to application/json.
   * @returns {Promise}
   */
  putJSON(obj) {
    return this.put(JSON.stringify(obj));
  }

  /**
   * Pushes xml content to the server for this resource.
   *
   * @param {string|Document} xml - to be stored as a xml object, format will be set to
   * application/json.
   * @returns {Promise}
   * @todo not finished or tested
   */
  putXML(xml) {
    let _xml = xml;
    const XMLSerializer = isBrowser() ? window.XMLSerializer : xmldom.XMLSerializer;

    if (!isBrowser() || _xml instanceof Document) {
      try {
        // Gecko- and Webkit-based browsers (Firefox, Chrome), Opera.
        _xml = (new XMLSerializer()).serializeToString(_xml);
      } catch (e) {
        try {
          // Internet Explorer.
          _xml = _xml.xml;
        } catch (ee) {
          throw new Error('Xmlserializer not supported');
        }
      }
    } else if ((typeof _xml) !== "string") {
      throw new Error('Unsupported format of parameter xml to method putAsXML');
    }
    return this.put(_xml, 'text/xml');
  }

  /**
   * @returns {Promise} which format the resource is returned in the promise (string, json or
   * xml) depends on what is specified in the mimetype. Xml is only returned in a browser
   * environment, if not in a browser a string is returned.
   * @param writableStream a writable stream, e.g. in nodejs it can be created via fs.createWriteStream('./output');
   */
  get(writableStream) {
    const format = this.getEntry(true).getEntryInfo().getFormat();
    const es = this.getEntryStore();
    return es.handleAsync(es.getREST().get(this.getResourceURI(), format, undefined, writableStream), 'getFile');
  }

  /**
   * @returns {Promise.<String>} ignores what is specified in the mimetype and returns the resource
   * as a string in the promise.
   */
  getText() {
    const es = this.getEntryStore();
    return es.handleAsync(this.getEntryStore().getREST().get(this.getResourceURI(), 'text/plain', true), 'getFile');
  }

  /**
   * @returns {Promise.<Object>} ignores what is specified in the mimetype and returns the resource
   * as a javascript object in the promise.
   */
  getJSON() {
    const es = this.getEntryStore();
    return es.handleAsync(es.getREST().get(this.getResourceURI(), 'application/json'), 'getFile');
  }

  /**
   * @returns {Promise} ignores what is specified in the mimetype and returns the resource
   * in the promise as a XML Document or a string (depending on if you are in browser or not).
   */
  getXML() {
    const es = this.getEntryStore();
    return es.handleAsync(es.getREST().get(this.getResourceURI(), 'text/xml'), 'getFile');
  }
}