@sap-ux/store
Version:
NPM module for storing persistent data
239 lines • 8.58 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.basedir = void 0;
exports.getFilesystemWatcherFor = getFilesystemWatcherFor;
exports.getFilesystemStore = getFilesystemStore;
const path_1 = __importDefault(require("path"));
const fs_1 = __importStar(require("fs"));
const pluralize_1 = require("pluralize");
const utils_1 = require("../utils");
const os_1 = __importDefault(require("os"));
const basedir = ({ baseDirectory } = {}) => {
if (!baseDirectory) {
return (0, utils_1.getFioriToolsDirectory)();
}
else if (path_1.default.isAbsolute(baseDirectory)) {
return baseDirectory;
}
else {
return path_1.default.join(os_1.default.homedir(), baseDirectory);
}
};
exports.basedir = basedir;
class FilesystemStore {
logger;
storeDirectory;
constructor(logger, options = {}) {
this.logger = logger;
this.storeDirectory = (0, exports.basedir)(options);
}
async read({ entityName, id }) {
const name = toPersistenceName(entityName);
if (!name) {
this.logger.debug('read: Entity Type is falsy - ' + entityName);
return Promise.resolve(undefined);
}
const { entities, error } = this._readAll(name);
if (error) {
if (error.code === 'ENOENT') {
return Promise.resolve(undefined);
}
else {
throw error;
}
}
if (!entities) {
this.logger.debug(`read: After parsing, entities is falsy. Entity: ${name}, parsed value: ${entities}`);
return undefined;
}
return Promise.resolve(entities[id]);
}
async getAll({ entityName }) {
const name = toPersistenceName(entityName);
if (!name) {
this.logger.debug('read: Entity Type is falsy - ' + entityName);
return Promise.resolve([]);
}
const { entities, error } = this._readAll(name);
if (error) {
if (error.code === 'ENOENT') {
return Promise.resolve([]);
}
else {
throw error;
}
}
if (!entities) {
this.logger.debug(`read: After parsing, entities is falsy. Entity: ${name}, parsed value: ${entities}`);
return Promise.resolve([]);
}
return Promise.resolve(Object.values(entities));
}
async readAll({ entityName }) {
const name = toPersistenceName(entityName);
if (!name) {
this.logger.debug('read: Entity Type is falsy - ' + entityName);
return Promise.resolve({});
}
const { entities, error } = this._readAll(name);
if (error) {
if (error.code === 'ENOENT') {
return Promise.resolve({});
}
else {
throw error;
}
}
if (!entities) {
this.logger.debug(`read: After parsing, entities is falsy. Entity: ${name}, parsed value: ${entities}`);
return Promise.resolve({});
}
return Promise.resolve(entities);
}
async write({ entityName, id, entity }) {
const name = toPersistenceName(entityName);
if (!name) {
this.logger.debug('write: Entity is falsy - ' + name);
return Promise.resolve(undefined);
}
const { entities = {}, error } = this._readAll(name);
if (error && error.code !== 'ENOENT') {
throw error;
}
entities[id] = entity;
this.writeToFile(name, entities);
return Promise.resolve(entity);
}
async del({ entityName, id }) {
const name = toPersistenceName(entityName);
if (!name) {
this.logger.debug('delete: Entity is falsy - ' + name);
return Promise.resolve(false);
}
const { entities = {}, error } = this._readAll(name);
if (error) {
if (error.code !== 'ENOENT') {
throw error;
}
}
const exists = !!entities[id];
if (exists) {
this.logger.debug(`delete: entity found for id - ${id}. Deleting`);
delete entities[id];
this.writeToFile(name, entities);
return Promise.resolve(true);
}
else {
this.logger.debug('delete: entity not found');
return Promise.resolve(false);
}
}
_readAll(entityName) {
let rawContents;
try {
const configPath = path_1.default.join(this.storeDirectory, `${entityName}.json`);
if ((0, fs_1.existsSync)(configPath)) {
rawContents = (0, fs_1.readFileSync)(configPath).toString().trim();
}
else {
rawContents = '';
}
}
catch (e) {
const err = (0, utils_1.errorInstance)(e);
this.logger.debug(err.message);
return { error: err };
}
if (!rawContents) {
return { entities: undefined };
}
let entities;
try {
entities = JSON.parse(rawContents)?.[entityName];
}
catch (e) {
return { error: (0, utils_1.errorInstance)(e) };
}
return { entities };
}
writeToFile(entityName, entities) {
const data = JSON.stringify({ [entityName]: entities }, null, 2);
const filename = getEntityFileName(entityName);
try {
(0, fs_1.writeFileSync)(path_1.default.join(this.storeDirectory, filename), data);
}
catch (e) {
const err = (0, utils_1.errorInstance)(e);
if (err?.code === 'ENOENT') {
this.logger.debug(`Base directory [${this.storeDirectory}] does not exist, trying to create it`);
(0, fs_1.mkdirSync)(this.storeDirectory, { recursive: true });
(0, fs_1.writeFileSync)(path_1.default.join(this.storeDirectory, filename), data);
}
else {
throw e;
}
}
}
}
/**
* Trims, lowercases and returns plural if a non-empty string
*
* @param s
*/
function toPersistenceName(s) {
const t = s?.trim().toLowerCase();
return t && (0, pluralize_1.plural)(t);
}
function getEntityFileName(entityName) {
return toPersistenceName(entityName) + '.json';
}
/** Return an FSWatcher for a given entity name
* The client is responsible for disposing of the FSWatcher
*/
function getFilesystemWatcherFor(entityName, callback, options = {}) {
const watchPath = path_1.default.join((0, exports.basedir)(options), getEntityFileName(entityName));
if ((0, fs_1.existsSync)(watchPath)) {
return fs_1.default.watch(watchPath, undefined, () => {
callback(entityName);
});
}
else {
console.warn(`File Not Found: ${watchPath}`);
return undefined;
}
}
/**
* Filesystem store. The entity is stored in JSON format (don't depend on the format, this could change).
* The entity is stored in a file named with the plural form of the entity name in the base directory. Again, this is an
* implementation detail, please don't depend on it.
*/
function getFilesystemStore(logger, options) {
return new FilesystemStore(logger, options);
}
//# sourceMappingURL=filesystem.js.map