pip-services4-persistence-node
Version:
Persistence Components for Pip.Services in Node.js / ES2017
337 lines • 13.3 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MemoryPersistence = void 0;
const pip_services4_data_node_1 = require("pip-services4-data-node");
const pip_services4_data_node_2 = require("pip-services4-data-node");
const pip_services4_observability_node_1 = require("pip-services4-observability-node");
/**
* Abstract persistence component that stores data in memory.
*
* This is the most basic persistence component that is only
* able to store data items of any type. Specific CRUD operations
* over the data items must be implemented in child classes by
* accessing <code>this._items</code> property and calling [[save]] method.
*
* The component supports loading and saving items from another data source.
* That allows to use it as a base class for file and other types
* of persistence components that cache all data in memory.
*
* ### Configuration parameters ###
*
* - options:
* - max_page_size: Maximum number of items returned in a single page (default: 100)
*
* ### References ###
*
* - <code>\*:logger:\*:\*:1.0</code> (optional) [[https://pip-services4-node.github.io/pip-services4-components-node/interfaces/log.ilogger.html ILogger]] components to pass log messages
*
* ### Example ###
*
* class MyMemoryPersistence extends MemoryPersistence<MyData> {
*
* public async getByName(context: IContext, name: string): Promise<MyData> {
* let item = this._items.find((d) => d.name == name);
* return item;
* });
*
* public set(correlatonId: string, item: MyData): Promise<MyData> {
* this._items = this._items.find((d) => d.name != name);
* this._items.push(item);
* await this.save(context);
* return item;
* }
*
* }
*
* let persistence = new MyMemoryPersistence();
*
* let item = await persistence.set("123", { name: "ABC" });
* item = await persistence.getByName("123", "ABC");
* console.log(item); // Result: { name: "ABC" }
*/
class MemoryPersistence {
/**
* Creates a new instance of the persistence.
*
* @param loader (optional) a loader to load items from external datasource.
* @param saver (optional) a saver to save items to external datasource.
*/
constructor(loader, saver) {
this._logger = new pip_services4_observability_node_1.CompositeLogger();
this._items = [];
this._opened = false;
this._maxPageSize = 100;
this._loader = loader;
this._saver = saver;
}
/**
* Configures component by passing configuration parameters.
*
* @param config configuration parameters to be set.
*/
configure(config) {
this._maxPageSize = config.getAsIntegerWithDefault("options.max_page_size", this._maxPageSize);
}
/**
* Sets references to dependent components.
*
* @param references references to locate the component dependencies.
*/
setReferences(references) {
this._logger.setReferences(references);
}
/**
* Checks if the component is opened.
*
* @returns true if the component has been opened and false otherwise.
*/
isOpen() {
return this._opened;
}
/**
* Opens the component.
*
* @param context (optional) execution context to trace execution through call chain.
*/
open(context) {
return __awaiter(this, void 0, void 0, function* () {
yield this.load(context);
this._opened = true;
});
}
load(context) {
return __awaiter(this, void 0, void 0, function* () {
if (this._loader == null) {
return null;
}
this._items = yield this._loader.load(context);
this._logger.trace(context, "Loaded %d items", this._items.length);
});
}
/**
* Closes component and frees used resources.
*
* @param context (optional) execution context to trace execution through call chain.
*/
close(context) {
return __awaiter(this, void 0, void 0, function* () {
yield this.save(context);
this._opened = false;
});
}
/**
* Saves items to external data source using configured saver component.
*
* @param context (optional) a context to trace execution through call chain.
*/
save(context) {
return __awaiter(this, void 0, void 0, function* () {
if (this._saver == null) {
return;
}
yield this._saver.save(context, this._items);
this._logger.trace(context, "Saved %d items", this._items.length);
});
}
/**
* Clears component state.
*
* @param context (optional) execution context to trace execution through call chain.
*/
clear(context) {
return __awaiter(this, void 0, void 0, function* () {
this._items = [];
this._logger.trace(context, "Cleared items");
yield this.save(context);
});
}
/**
* Gets a page of data items retrieved by a given filter and sorted according to sort parameters.
*
* This method shall be called by a public getPageByFilter method from child class that
* receives FilterParams and converts them into a filter function.
*
* @param context (optional) a context to trace execution through call chain.
* @param filter (optional) a filter function to filter items
* @param paging (optional) paging parameters
* @param sort (optional) sorting parameters
* @param select (optional) projection parameters (not used yet)
* @returns a requested page with data items.
*/
getPageByFilter(context, filter,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
paging, sort, select) {
return __awaiter(this, void 0, void 0, function* () {
let items = this._items;
// Filter and sort
if (typeof filter === "function") {
items = items.filter(filter);
}
if (typeof sort === "function") {
items = items.sort((a, b) => {
const sa = sort(a);
const sb = sort(b);
if (sa < sb)
return -1;
if (sa > sb)
return 1;
return 0;
});
}
// Extract a page
paging = paging != null ? paging : new pip_services4_data_node_1.PagingParams();
const skip = paging.getSkip(-1);
const take = paging.getTake(this._maxPageSize);
let total = null;
if (paging.total) {
total = items.length;
}
if (skip > 0) {
items = items.slice(skip);
}
items = items.slice(0, take);
this._logger.trace(context, "Retrieved %d items", items.length);
return new pip_services4_data_node_2.DataPage(items, total);
});
}
/**
* Gets a number of items retrieved by a given filter.
*
* This method shall be called by a public getCountByFilter method from child class that
* receives FilterParams and converts them into a filter function.
*
* @param context (optional) a context to trace execution through call chain.
* @param filter (optional) a filter function to filter items
* @returns a number of data items that satisfy the filter.
*/
getCountByFilter(context, filter) {
return __awaiter(this, void 0, void 0, function* () {
let items = this._items;
// Filter and sort
if (typeof filter === "function") {
items = items.filter(filter);
}
this._logger.trace(context, "Counted %d items", items.length);
return items.length;
});
}
/**
* Gets a list of data items retrieved by a given filter and sorted according to sort parameters.
*
* This method shall be called by a public getListByFilter method from child class that
* receives FilterParams and converts them into a filter function.
*
* @param context (optional) transaction id to trace execution through call chain.
* @param filter (optional) a filter function to filter items
* @param paging (optional) paging parameters
* @param sort (optional) sorting parameters
* @param select (optional) projection parameters (not used yet)
* @returns a list with found data items.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getListByFilter(context, filter, sort, select) {
return __awaiter(this, void 0, void 0, function* () {
let items = this._items;
// Apply filter
if (typeof filter === "function") {
items = items.filter(filter);
}
// Apply sorting
if (typeof sort === "function") {
items = items.sort((a, b) => {
const sa = sort(a);
const sb = sort(b);
if (sa < sb)
return -1;
if (sa > sb)
return 1;
return 0;
});
}
this._logger.trace(context, "Retrieved %d items", items.length);
return items;
});
}
/**
* Gets a random item from items that match to a given filter.
*
* This method shall be called by a public getOneRandom method from child class that
* receives FilterParams and converts them into a filter function.
*
* @param context (optional) a context to trace execution through call chain.
* @param filter (optional) a filter function to filter items.
* @returns a random data item.
*/
getOneRandom(context, filter) {
return __awaiter(this, void 0, void 0, function* () {
let items = this._items;
// Apply filter
if (typeof filter === "function") {
items = items.filter(filter);
}
const index = Math.trunc(items.length * Math.random());
const item = items.length > 0 ? items[index] : null;
if (item != null) {
this._logger.trace(context, "Retrieved a random item");
}
else {
this._logger.trace(context, "Nothing to return as random item");
}
return item;
});
}
/**
* Creates a data item.
*
* @param context (optional) transaction id to trace execution through call chain.
* @param item an item to be created.
* @returns a created data item
*/
create(context, item) {
return __awaiter(this, void 0, void 0, function* () {
// Clone the object
item = Object.assign({}, item);
this._items.push(item);
this._logger.trace(context, "Created item %s", item['id']);
yield this.save(context);
return item;
});
}
/**
* Deletes data items that match to a given filter.
*
* This method shall be called by a public deleteByFilter method from child class that
* receives FilterParams and converts them into a filter function.
*
* @param context (optional) a context to trace execution through call chain.
* @param filter (optional) a filter function to filter items.
*/
deleteByFilter(context, filter) {
return __awaiter(this, void 0, void 0, function* () {
let deleted = 0;
for (let index = this._items.length - 1; index >= 0; index--) {
const item = this._items[index];
if (filter(item)) {
this._items.splice(index, 1);
deleted++;
}
}
if (deleted == 0) {
return;
}
this._logger.trace(context, "Deleted %s items", deleted);
yield this.save(context);
});
}
}
exports.MemoryPersistence = MemoryPersistence;
//# sourceMappingURL=MemoryPersistence.js.map