const FsUtil = require('./file');
const Type = require('./type');
/**
* Singleton that provides functions persist data.
* Data is persisted in in files defined inside the data directory.
* Fetches all different files of the data directory into a single object.
*
* @author Maksim Sandybekov
* @date 7.5.20
* @version 1.0
*/
class Storage {
static instance = null;
// Initialize storage with default values
constructor() {
this.fileWriter = new FsUtil();
this.storage = this.fileWriter.readData();
}
/**
* Singleton method. Create and keep only a single object of the storage around.
*/
static getInstance() {
if (!Storage.instance) {
Storage.instance = new Storage();
}
return Storage.instance;
}
/**
* Adding values to data files. Creating the if they are not existent.
*
* @example
* add("queue.users", 22); // store {"users": 22} inside a file named queue.json
*
* @example
* add("queue.1231.servername", "Test Server"); // store {"1231": {"servername": "Test Server"}} inside a file named queue.json
*
* @param {string} key Single or multi path segment separated by dots (Examples: 'key', 'key.nested_key', ...)
* @param {*} value The value to save under given key
* @return {Object} the javascript object that was saved
*/
set(key, value) {
// Only allow string to be used as keys
if (!Type.isString(key)) {
throw new Error(`Parameter {Key} is not a string.`);
}
let subKeys = key.split('.').filter(sKey => sKey != '');
if (subKeys.length <= 0) {
return false;
}
let fileKey = subKeys.shift();
let fileContent = this.storage[fileKey];
// There no keys after document key selection
if (subKeys.length <= 0) {
this.storage[fileKey] = value;
this.fileWriter.writeData(fileKey, value);
return true;
}
// Iterate over sub-keys
let result = fileContent;
for (let i = 0; i < subKeys.length; i++) {
// Check all sub-keys except the last one
if (i != subKeys.length-1 && result[subKeys[i]] === undefined) {
result[subKeys[i]] = {};
}
// Last key reached, update value
if (i == subKeys.length - 1) {
result[subKeys[i]] = value;
break;
}
result = result[subKeys[i]] || null;
}
this.storage[fileKey] = fileContent;
this.fileWriter.writeData(fileKey, fileContent);
return true;
}
/**
* Searches for a value under given key.
*
* @example
* get("queue.member"); // Returns the value saved under given key or undefined
*
* @example
* get("queue"); // Returns whole content of queue.json file
*
* @param {string} key Single/Multi-path segement separated by dots. (Example: 'key', 'key.nested_key', 'users.count', ...)
* @return {null | *} The value under the given path or null if nothing was found.
*/
get(key) {
// Only allow strings to be used as keys
if (!Type.isString(key)) {
throw new Error(`Parameter {Key} is not a string.`);
}
let subKeys = key.split('.').filter(sKey => sKey != '');
if (subKeys.length <= 0) {
return null;
}
// Recursivly choose keys from storage
let result = this.storage;
for (let i = 0; i < subKeys.length; i++) {
if (result[subKeys[i]] === undefined) {
return null;
}
result = result[subKeys[i]];
}
return result;
}
}
module.exports = Storage;