@aegis-framework/artemis
Version:
Aegis Framework Javascript Library
334 lines (305 loc) • 10.1 kB
JavaScript
/**
* ==============================
* Space
* ==============================
*/
import { LocalStorage } from './SpaceAdapter/LocalStorage';
import { SessionStorage } from './SpaceAdapter/SessionStorage';
import { IndexedDB } from './SpaceAdapter/IndexedDB';
import { RemoteStorage } from './SpaceAdapter/RemoteStorage';
/**
* List of Adapters Available
*/
export const SpaceAdapter = {
LocalStorage,
SessionStorage,
IndexedDB,
RemoteStorage
};
/**
* Space provides a simple wrapper for different Storage APIs. It aims to
* provide data independence through storage namespaces and versioning, allowing
* transparent data formatting and content modifications through versions.
*
* While this class documentation provides some information, specific details may
* be addressed on the documentation of each adapter.
*
* @class
*/
export class Space {
/**
* Create a new Space Object. If no name and version is defined, the global LocalSpace space is used.
*
* @constructor
* @param {SpaceAdapter} [adapter = SpaceAdapter.LocalStorage] - Space Adapter
* to use. Currently LocalStorage, SessionStorage, IndexedDB and Server are
* available
* @param {Object} [configuration = {}] - Configuration object for the space.
* This configuration may change depending on the adapter used, however all
* adapters have support for a name, version and store properties.
* @param {string} configuration.name - Name of the Space
* @param {string} configuration.version - Version of the Space in Semantic versioning syntax
* @param {string} configuration.store - Name of the Object Store to use
*/
constructor (adapter = SpaceAdapter.LocalStorage, configuration = {}) {
// Assign the provided configuration to the default one
this._configuration = Object.assign ({}, {name: '', version: '', store: ''}, configuration);
// Set up the adapter instance to use
this.adapter = new adapter (this._configuration);
// This object stores all the callbacks the user can define for the
// space operations
this.callbacks = {
'create': [],
'update': [],
'delete': []
};
// A transformation is an object that can contain a set and get functions
// every transformation will be applied to the retrieved value or to the
// value before storing it.
this.transformations = {
};
}
/**
* Modify the space configuration, it will also be passed down to the adapter
* using its configuration () function.
*
* @param {object} - Configuration object to set up
* @return {object} - Configuration object if no param was passed
*/
configuration (object = null) {
if (object !== null) {
this._configuration = Object.assign ({}, this._configuration, object);
this.adapter.configuration (object);
} else {
return this._configuration;
}
}
/**
* Open the Storage Object to be used depending on the SpaceAdapter
*
* @return {Promise}
*/
open () {
return this.adapter.open ().then (() => {
return Promise.resolve (this);
});
}
/**
* Store a key-value pair
*
* @param {string} key - Key with which this value will be saved
* @param {Object|string|Number} - Value to save
* @return {Promise<{key, value}>}
*/
set (key, value) {
// Apply all set transformations to the value
for (const id of Object.keys (this.transformations)) {
if (typeof this.transformations[id].set === 'function') {
value = this.transformations[id].set.call (null, key, value);
}
}
return this.adapter.set (key, value).then (({key, value}) => {
for (const callback of this.callbacks.create) {
callback.call (null, key, value);
}
return Promise.resolve ({key, value});
});
}
/**
* Update a key-value pair. In difference with the set () method, the update
* method will use an Object.assign () in the case of objects so no value is
* lost.
*
* @param {string} key - Key with which this value will be saved
* @param {Object|string|Number} - Value to save
* @return {Promise<{key, value}>}
*/
update (key, value) {
// Apply all set transformations to the value
for (const id of Object.keys (this.transformations)) {
if (typeof this.transformations[id].set === 'function') {
value = this.transformations[id].set.call (null, key, value);
}
}
return this.adapter.update (key, value).then (({key, value}) => {
for (const callback of this.callbacks.update) {
callback.call (null, key, value);
}
return Promise.resolve ({key, value});
});
}
/**
* Retrieves a value from storage given it's key
*
* @param {string} - Key with which the value was saved
* @return {Promise<Object>|Promise<string>|Promise<Number>} - Resolves to the retreived value
* or its rejected if it doesn't exist.
*/
get (key) {
return this.adapter.get (key).then ((value) => {
// Apply all get transformations to the value
for (const id of Object.keys (this.transformations)) {
if (typeof this.transformations[id].get === 'function') {
value = this.transformations[id].get.call (null, key, value);
}
}
return value;
});
}
/**
* Retrieves all the values in the space in a key-value JSON object
*
* @return {Promise<Object>} - Resolves to the retreived values
*/
getAll () {
return this.adapter.getAll ().then ((values) => {
// Apply all get transformations to the value
for (const key of Object.keys (values)) {
for (const id of Object.keys (this.transformations)) {
if (typeof this.transformations[id].get === 'function') {
values[key] = this.transformations[id].get.call (null, key, values[key]);
}
}
}
return values;
});
}
/**
* Iterate over every value in the space
*
* @param {function (key, value)} callback - A callback function receiving the
* key and value of a value. Must return a callback
* @return {Promise} - Resolves when all callbacks have been resolved.
*/
each (callback) {
return this.getAll ().then ((values) => {
const promises = [];
for (const i of Object.keys (values)) {
promises.push (callback.call (this, i, values[i]));
}
return Promise.all (promises);
});
}
/**
* Check if a space contains a given key. Not all adapters may give this information
*
* @param {string} key - Key to look for.
* @return {Promise} - Promise gets resolved if it exists and rejected if
* doesn't
*/
contains (key) {
return this.adapter.contains (key);
}
/**
* Upgrade a Space Version. Not all adapters may provide this functionality
*
* @param oldVersion {string} - The version of the storage to be upgraded
* @param newVersion {string} - The version to be upgraded to
* @param callback {function} - Function to transform the old stored values to the new version's format
*
* @returns {Promise} - Result of the upgrade operation
*/
upgrade (oldVersion, newVersion, callback) {
return this.adapter.upgrade (oldVersion, newVersion, callback).then (() => {
return Promise.resolve (this);
});
}
/**
* Rename a Space. Not all adapters may provide this functionality
*
* @param {string} name - New name to be used.
* @returns {Promise} Result of the rename operation
*/
rename (name) {
return this.adapter.rename (name);
}
/**
* Add a callback function to be run every time a value is created.
*
* @param {function (key, value)} callback - Callback Function. Key and Value pair will be sent as parameters when run.
*/
onCreate (callback) {
this.callbacks.create.push (callback);
}
/**
* Add a callback function to be run every time a value is updated.
*
* @param {function (key, value)} callback - Callback Function. Key and Value pair will be sent as parameters when run.
*/
onUpdate (callback) {
this.callbacks.update.push (callback);
}
/**
* Add a callback function to be run every time a value is deleted.
*
* @param {function (key, value)} callback - Callback Function. Key and Value pair will be sent as parameters when run.
*/
onDelete (callback) {
this.callbacks.delete.push (callback);
}
/**
* Add a transformation function to the space.
*
* @param {string} id - Unique transformation name or identifier
* @param {function (key, value)|null} get - Transformation function to apply to the content before
* returning the value when using the get () function .
* @param {function (key, value)|null} set - Transformation function to apply to the content before
* saving it when using the set () function befo.
*/
addTransformation ({id, get, set}) {
this.transformations[id] = {
id,
get,
set
};
}
/**
* Remove a transformation function given its id
*
* @param {string} id - Name or identifier of the transformation to remove
*/
removeTransformation (id) {
delete this.transformations[id];
}
/**
* Get the key that corresponds to a given index in the storage. Not all adapters may provide this functionality
*
* @param {Number} index - Index to get the key from
* @param {boolean} [full = false] - Whether to return the full key name including space id or just the key name
* @return {Promise<string>} - Resolves to the key's name
*/
key (index, full = false) {
return this.adapter.key (index, full);
}
/**
* Return all keys stored in the space. Not all adapters may provide this functionality
*
* @param {boolean} [full = false] - Whether to return the full key name including space id or just the key name
* @return {Promise<string[]>} - Array of keys
*/
keys (full = false) {
return this.adapter.keys (full);
}
/**
* Delete a value from the space given it's key
*
* @param {string} key - Key of the item to delete
* @return {Promise<key, value>} - Resolves to the key and value of the deleted object
*/
remove (key) {
return this.adapter.remove (key).then ((value) => {
// Run the callback for deletions
for (const callback of this.callbacks.delete) {
callback.call (null, key, value);
}
});
}
/**
* Clear the entire space
*
* @return {Promise} - Result of the clear operation
*/
clear () {
return this.adapter.clear ();
}
}