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;