PrototypeEntry.js

import Entry from './Entry.js';
import EntryInfo from './EntryInfo.js';
import terms from './terms.js';

const NEW_ID_PLACEHOLDER = '_newId';
/**
 * A PrototypeEntry is used to create new entries by collecting information about the initial
 * state of the entry to send along to the repository upon creation.
 *
 * All access and utility methods from Entry is just inherited. Some methods have been moved over
 * from EntryInformation to allow easier method chaining. Finally some information cannot be
 * changed in an entry, e.g. the entry, graph and resource types, but are crucial before creation.
 * Hence, some methods have been introduced to cover for this need.
 *
 * @exports store/PrototypeEntry
 */
export default class PrototypeEntry extends Entry {
  /**
   * @param {Context} context where this prototypeEntry belongs.
   * @param {string} id - entry identifier, if not unique in the context the subsequent commit
   * will fail.
   */
  constructor(context, id = NEW_ID_PLACEHOLDER) {
    const cru = context.getResourceURI();
    const entryInfo = new EntryInfo(`${cru}/entry/${id}`, null, context.getEntryStore());
    if (context.getId() === '_contexts') {
      entryInfo._resourceURI = context.getEntryStore().getBaseURI() + id;
    } else {
      entryInfo._resourceURI = `${cru}/resource/${id}`;
    }
    const oldSetResourceURI = entryInfo.setResourceURI.bind(entryInfo);
    entryInfo.setResourceURI = (uri) => {
      this._resourceURI = uri;
      oldSetResourceURI(uri);
      return this;
    };

    entryInfo.getResourceURI = () => this._resourceURI || entryInfo._resourceURI;

    super(context, entryInfo); // Call the super constructor.
    if (id !== NEW_ID_PLACEHOLDER) {
      this.specificId = id;
    }
  }

  /**
   * Direct access method for the resource instance for prorotypeEntries.
   * @returns {Resource}
   */
  getResource() {
    return this._resource;
  }

  /**
   * Exposes the {@link EntryInfo#setACL setACL} method from {@link EntryInfo}
   * on PrototypeEntry
   * and makes it chainable.
   *
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setACL() {
    EntryInfo.prototype.setACL.apply(this._entryInfo, arguments);
    return this;
  }

  /**
   * Exposes the {@link EntryInfo#setResourceURI setResourceURI} method from
   * {@link EntryInfo} on this class
   * and makes it chainable.
   *
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setResourceURI(uri) {
    this._entryInfo.setResourceURI(uri);
    return this;
  }

  /**
   * Exposes the {@link EntryInfo#setExternalMetadataURI setExternalMetadataURI} method
   * from {@link EntryInfo} on this class
   * and makes it chainable.
   *
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setExternalMetadataURI() {
    EntryInfo.prototype.setExternalMetadataURI.apply(this._entryInfo, arguments);
    return this;
  }

  /**
   * Makes it possible to change the EntryType (which is not allowed on existing entries).
   *
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setEntryType(et) {
    const uri = terms.invEntryType[et];
    if (uri) {
      this._entryInfo.getGraph().create(this._entryInfo.getEntryURI(), terms.rdf.type, {
        type: 'uri',
        value: uri,
      });
    }
    return this;
  }

  /**
   * Makes it possible to change the GraphType (which is not allowed on existing entries).
   *
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setGraphType(gt) {
    this._gt = gt;
    const uri = terms.invGraphType[gt];
    if (uri) {
      this._entryInfo.getGraph().create(this._entryInfo.getResourceURI(), terms.rdf.type, {
        type: 'uri',
        value: uri,
      });
    }
    return this;
  }

  /**
   * Makes it possible to change the ResourceType (which is not allowed on existing entries).
   *
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setResourceType(rt) {
    const uri = terms.invResourceType[rt];
    if (uri) {
      this._entryInfo.getGraph().create(this._entryInfo.getResourceURI(), terms.rdf.type, {
        type: 'uri',
        value: uri,
      });
    }
    return this;
  }

  /**
   * When creating new entries a single parent list can be specified, hence we need a way to set
   * it in PrototypeEntry.
   *
   * @param {Entry} parentListEntry
   * @returns {PrototypeEntry} - to allow the method call to be chained.
   */
  setParentList(parentListEntry) {
    this.parentListEntry = parentListEntry;
    return this;
  }

  /**
   * Get the parent list (as an entry) for this PrototypeEntry.
   * @returns {Entry}
   */
  getParentList() {
    return this.parentListEntry;
  }

  /**
   * Get the suggested entry id for this PrototypeEntry
   * @returns {string}
   */
  getSpecificId() {
    return this.specificId;
  }

  /**
   * Allowed as a way to save metadata for an
   * entry that is assumed to exist with a given entry id.
   * @override
   */
  commitMetadata() {
    if (!this.specificId) {
      throw new Error('The entryId must have been specified for allowing metadata to be saved.');
    }
    const es = this.getEntryStore();

    return es.handleAsync(es.getREST().put(this.getEntryInfo().getMetadataURI(),
      JSON.stringify(this.getMetadata().exportRDFJSON())), 'commitMetadata');
  }

  /**
   * Allowed as a way to save cached external metadata for an entry that is assumed to
   * exist with a given entry id.
   * @override
   */
  commitCachedExternalMetadata() {
    if (!this.specificId) {
      throw new Error('The entryId must have been specified for allowing cached external metadata to be saved.');
    }
    const es = this.getEntryStore();
    return es.handleAsync(es.getREST().put(this.getEntryInfo().getCachedExternalMetadataURI(),
      JSON.stringify(this._cachedExternalMetadata.exportRDFJSON())), 'commitCachedExternalMetadata');
  }

  /**
   * @deprecated use {@link PrototypeEntry#commit commit} instead.
   * @returns {Promise.<Entry>}
   */
  create() {
    return this._context.getEntryStore().createEntry(this);
  }

  /**
   * Create a new entry according to the information specified in the prototype entry.
   *
   * @returns {Promise.<Entry>}
   * @see EntryStore#createEntry
   */
  commit() {
    return this._context.getEntryStore().createEntry(this);
  }
};