@aegis-framework/artemis
Version:
Aegis Framework Javascript Library
225 lines (210 loc) • 6.4 kB
JavaScript
/**
* ==============================
* Remote Storage Adapter
* ==============================
*/
import { Request } from './../Request';
/**
* The Remote Storage Adapter provides the Space Class the ability to interact
* with a server in order to handle data persistance. The server's implementation
* is up to the developer but it will need to respond to this adapter's request
* formatting. This adapter uses the Request class to perfom its tasks.
*
* @class
*/
export class RemoteStorage {
/**
* Create a new Remote Storage. This adapter requires an endpoint url where
* it will make the requests. If a store is defined, the request will be made
* using the store as an URL, for example, let's assume the following endpoint:
*
* https://example.com/api/v1/
*
* If no store is defined, then the requests will be made to that simple route,
* with a store definition, requests will be made to:
*
* https://example.com/api/v1/{myStore}/
*
* The key of each item in this store represents another part of the request
*
* https://example.com/api/v1/{key}/
*
* Or:
*
* https://example.com/api/v1/{myStore}/{key}/
*
* This adapter just as the IndexedDB, works with JSON objects instead of string or
* numeric values.
*
* @constructor
* @param {object} [configuration={name = '', version = '', store = '', endpoint = '', props = {}] - Configuration Object for the Adapter
* @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
* @param {string} configuration.endpoint - Endpoint URL where the requests will be made
* @param {string} configuration.props - Properties object to use for the fetch requests
*/
constructor ({name = '', version = '', store = '', endpoint = '', props = {}}) {
this.name = name;
this.version = version;
this.store = store;
this.endpoint = `${endpoint}${store}/`;
this.props = props;
}
/**
* Open the Storage Object
*
* @return {Promise<RemoteStorage>}
*/
open () {
if (typeof this.storage === 'undefined') {
this.storage = Request;
}
return Promise.resolve (this);
}
/**
* Store a key-value pair. This function sends a POST request to the server
*
* @param {string} key - Key with which this value will be saved
* @param {Object} value - Value to save
* @return {Promise<Response>}
*/
set (key, value) {
return this.open ().then (() => {
return this.storage.post (this.endpoint + key, value, this.props)
.then ((response) => response.json ())
.then ((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. This function sends a PUT request to the server.
*
* @param {string} key - Key with which this value will be saved
* @param {Object} value - Value to save
* @return {Promise<Object>}
*/
update (key, value) {
return this.get (key).then ((currentValue) => {
return this.storage.put (this.endpoint + key, Object.assign ({}, currentValue, value), this.props)
.then ((response) => response.json ())
.then ((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>} - Resolves to the retreived value or its rejected
* if it doesn't exist
*/
get (key) {
return this.open ().then (() => {
return this.storage.json (this.endpoint + key, {}, this.props);
});
}
/**
* Retrieves all the values in the space in a key-value JSON object
*
* @return {Promise<Object>} - Resolves to the retreived values
*/
getAll () {
return this.open ().then (() => {
return this.storage.json (this.endpoint, {}, this.props);
});
}
/**
* Check if a space contains a given key.
*
* @param {string} key - Key to look for.
* @return {Promise} Promise gets resolved if it exists and rejected if it
* doesn't
*/
contains (key) {
return this.keys ().then ((keys) => {
if (keys.includes (key)) {
Promise.resolve ();
} else {
return Promise.reject ();
}
});
}
/**
* Upgrading the Storage must be done on the server side, therefore this function
* always gets rejected.
*
* @returns {Promise}
*/
upgrade () {
return Promise.reject ();
}
/**
* Renaming the Storage must be done on the server side, therefore this function
* always gets rejected.
*
* @returns {Promise}
*/
rename () {
return Promise.reject ();
}
/**
* Getting a key by its index is not possible in this adapter, therefore this
* function always gets rejected.
*
* @return {Promise} - Promise Rejection
*/
key () {
return Promise.reject ();
}
/**
* Return all keys stored in the space. This makes a GET request to the full
* endpoint (the URL of the endpoint and store name) with a keys query
* parameter:
*
* https://example.com/api/v1/?keys=true
*
* Or:
*
* https://example.com/api/v1/{myStore}/?keys=true
*
* @return {Promise<string[]>} - Array of keys
*/
keys () {
return this.open ().then (() => {
return this.storage.json (this.endpoint, {keys: true}, this.props);
});
}
/**
* Delete a value from the space given it's key. This function sends a DELETE
* request to the server.
*
* @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.open ().then (() => {
return this.storage.delete (this.endpoint + key, {}, this.props)
.then ((response) => response.json ())
.then ((value) => {
return Promise.resolve (key,value);
});
});
}
/**
* Clear the entire space. This function sends a DELETE request to the server.
* The difference between a clear () and remove () operation is that the clear
* operation does not uses a key in the URL where the request is made.
*
* @return {Promise} - Result of the clear operation
*/
clear () {
return this.open ().then (() => {
return this.storage.delete (this.endpoint, {}, this.props);
});
}
}