Manual Reference Source Test

src/utilities/StorageUtility.js

import { validate, isA, isRequired } from './ValidationUtility';

/** @type {Symbol} */
const storageSingleton = Symbol('storageSingleton');
/** @type {Symbol} */
const storageSingletonEnforcer = Symbol('storageSingletonEnforcer');

/**
 * Storage will store and retrieve data.  This is expected to have set and get methods
 * overwritten if necessary.
 *
 * @example
 * import { Storage } from 'clinical6';
 * Storage.set('kittens', 5);
 * Storage.get('kittens');
 */
class StorageUtility {
  /**
   * Constructor
   *
   * @param {Symbol} enforcer
   */
  constructor(enforcer) {
    /** @type {Object} */
    this.data = {};

    if (enforcer !== storageSingletonEnforcer) {
      throw new Error('Cannot construct singleton');
    }
  }

  /**
   * Get the instance of the storage utility
   *
   * @type {Object} - instance
   */
  static get instance() {
    if (!this[storageSingleton]) {
      /** @type {Client} */
      this[storageSingleton] = new StorageUtility(storageSingletonEnforcer);
    }
    return this[storageSingleton];
  }

  /**
   * Clear storage data.
   *
   * @throws {Clinical6Error} - If type is not a string
   * @param  {String} [type]  - Type/Key of item to clear
   * @param  {String} [id]    - The id, if you want to store a specific item
   * @return {Promise<any>}   - Promise to indicate the clear is finished
   *
   * @example
   * import { Storage } from 'clinical6';
   * Storage.clear('kittens');
   * Storage.clear();
   */
  clear(type = undefined, id = undefined) {
    validate('StorageUtility.clear',
      isA({ type }, 'string'));
    return new Promise((resolve) => {
      if (!this.data) {
        this.data = {};
      }
      if (!type) {
        this.data = {}; // clear all
      } else if (this.data[type] && this.data[type][id]) {
        delete this.data[type][id]; // delete item
      } else if (this.data[type]) {
        delete this.data[type]; // delete type
      }
      resolve('removed');
    });
  }

  /**
   * Get storage data.
   *
   * @throws {Clinical6Error}             - If type is empty or not a string
   * @param  {!String}  type              - Type/Key of item to get - should be server's obj type
   * @param  {?Object}  [options]         - Options for getting storage information
   * @param  {String}   [options.id]      - The id, if you want to store a specific item
   * @param  {String}   [options.asArray] - Return as array
   * @return {Promise<any>}               - Promise with the stored data
   *
   * @example
   * import { Storage } from 'clinical6';
   * Storage.get('kittens');
   */
  get(type, options = {}) {
    validate('StorageUtility.get',
      isRequired({ type }),
      isA({ type }, 'string'));
    return new Promise((resolve) => {
      if (!this.data) {
        this.data = {};
      }
      if (!this.data[type]) {
        this.data[type] = {};
      }

      const obj = this.data[type];
      if (options && options.id && obj[options.id]) {
        resolve(obj[options.id]);
      } else if (options && options.asArray) {
        resolve(Object.values(obj));
      } else {
        resolve(obj);
      }
    });
  }

  /**
   * Determines if the type and id exist in storage
   *
   * @param  {String} type  - The key of the storage location
   * @param  {Number} [id]  - The id belonging to the item in question
   * @return {Boolean}      - Whether or not the item exists
   */
  has(type, id = undefined) {
    let _return = false;
    if (id) {
      _return = Boolean(this.data[type] && this.data[type][id]);
    } else {
      _return = Boolean(this.data[type]);
    }
    return _return;
  }

  /**
   * Set storage data.
   *
   * @throws {Clinical6Error}         - If key/value is empty or not a string
   * @param  {!String}  type          - Type/Key of item to store - should be server's obj type
   * @param  {String}   value         - Value of item to store
   * @param  {?Object}  [options]     - Options for setting storage information
   * @param  {String}   [options.id]  - The id, if you want to store a specific item
   * @param  {String}   [options.key] - The key, if you want to store a specific key.  Only used
   *                                    if value is an array
   * @return {Promise<any>}           - Promise with the stored data
   *
   * @example
   * import { Storage } from 'clinical6';
   * Storage.set('kittens', 5);
   */
  set(type, value, options = undefined) {
    validate('StorageUtility.set',
      isRequired({ type, value }),
      isA({ type }, 'string'));
    return new Promise((resolve) => {
      if (!this.data) {
        this.data = {};
      }
      if (!this.data[type]) {
        this.data[type] = {};
      }

      if (options && options.id) {
        this.data[type][options.id] = value;
      } else if (Array.isArray(value)) {
        value.forEach((obj) => {
          const key = (options && options.key) ? obj[options.key] : obj.id;
          if (key !== undefined && key !== null) {
            this.data[type][key] = obj;
          }
        });
      } else {
        this.data[type] = value;
      }
      resolve(value);
    });
  }
}

export default StorageUtility;