@axway/axway-central-cli
Version:
Manage APIs, services and publish to the Amplify Marketplace
146 lines (139 loc) • 6.14 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.CacheController = void 0;
var _dayjs = _interopRequireDefault(require("dayjs"));
var _fsExtra = require("fs-extra");
var _isEmpty = _interopRequireDefault(require("lodash/isEmpty"));
var _nodeCache = _interopRequireDefault(require("node-cache"));
var _os = require("os");
var _path = _interopRequireDefault(require("path"));
var _snooplogg = _interopRequireDefault(require("snooplogg"));
var _types = require("./types");
var _utils = require("./utils");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; }
function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
const {
log
} = (0, _snooplogg.default)('central: CacheController');
/**
* Note: this file intentionally exporting only a single instance of CacheController,
* since its possible to face a race condition when multiple instances will try to read/write file at the same time
* Please do not use this class directly or rework the logic before.
*/
class CacheControllerClass {
constructor() {
_defineProperty(this, "cacheFilePath", _path.default.join((0, _os.homedir)(), '.axway', 'central', 'cache.json'));
_defineProperty(this, "cache", new _nodeCache.default());
// note: init cache fire only once since using only a single instance of the class, remove if this will change
this.initCacheFile();
this.readFromFile();
}
/**
* Inits and validate cache file, should run once before using this class in the code (initialized in cli.ts currently)
* An empty JSON file will be created if it is not exists of the file size is more than some value.
*/
initCacheFile() {
try {
if ((0, _fsExtra.pathExistsSync)(this.cacheFilePath)) {
log(`init, cache file found at ${this.cacheFilePath}`);
const stats = (0, _fsExtra.lstatSync)(this.cacheFilePath);
log(`init, cache file size: ${Math.round(stats.size / 1000)} kb`);
if (stats.size >= _types.MAX_CACHE_FILE_SIZE) {
// validating the size
log(`init, cache size is exceeding the max allowed size of ${Math.round(_types.MAX_CACHE_FILE_SIZE / 1000)} kb, resetting the file`);
(0, _fsExtra.outputJsonSync)(this.cacheFilePath, {});
} else if (!(0, _utils.isValidJson)((0, _fsExtra.readFileSync)(this.cacheFilePath, 'utf8'))) {
// validating the content
log('init, cache content is invalid, resetting the file ');
(0, _fsExtra.outputJsonSync)(this.cacheFilePath, {});
}
} else {
log(`init, cache file not found, creating an empty one at ${this.cacheFilePath}`);
(0, _fsExtra.outputJsonSync)(this.cacheFilePath, {});
}
} catch (e) {
log(`cannot initialize cache file`, e);
}
}
/**
* Set the key in memory cache.
* @param key cache key to set
* @param value value to set, note that setting "undefined" value will result in "null" value stored
* @returns CacheController instance
*/
set(key, value) {
this.cache.set(key, value);
return this;
}
/**
* Returns the key value from the memory cache.
* @param key key to get
* @returns key value
*/
get(key) {
return this.cache.get(key);
}
/**
* Load stored cache from the file into memory and checks its timestamp.
* If the timestamp is more than X days old it will reset the file without any changes to cache.
* Note: using this method before writeToFile() will override keys in memory cache with the same name.
* @returns CacheController instance
*/
readFromFile() {
try {
log('reading cache from the file');
const jsonData = (0, _fsExtra.readFileSync)(this.cacheFilePath, 'utf8');
const storedCache = JSON.parse(jsonData);
// validate values stored in the cache, reset the content of the file if its not empty already.
if (storedCache.data && storedCache.metadata && storedCache.metadata.modifyTimestamp && (0, _dayjs.default)().diff(storedCache.metadata.modifyTimestamp, 'milliseconds') < _types.CACHE_FILE_TTL_MILLISECONDS) {
for (const [key, val] of Object.entries(storedCache.data)) {
if (storedCache.data.hasOwnProperty(key)) {
this.cache.set(key, val);
}
}
} else if (!(0, _isEmpty.default)(storedCache)) {
log('timestamp or content is not valid and file is not empty, resetting the cache file');
(0, _fsExtra.outputJsonSync)(this.cacheFilePath, {});
}
} catch (e) {
log('cannot read cache from the file', e);
} finally {
return this;
}
}
/**
* Writes current set of keys to the json file with following structure:
* {
* metadata: {
* modifyTimestamp: current timestamp, used on read for TTL validation
* schemaVersion: indicates the version of cache file structure, can be used later on if changing it.
* },
* data: {} key-value cache data
* }
* @returns CacheController instance
*/
writeToFile() {
try {
log('writing cache to the file');
const keys = this.cache.keys();
const cachedData = this.cache.mget(keys);
const dataToStore = {
metadata: {
modifyTimestamp: Date.now(),
schemaVersion: '1'
},
data: cachedData
};
(0, _utils.writeToFile)(this.cacheFilePath, JSON.stringify(dataToStore));
} catch (e) {
log('cannot write cache to the file', e);
} finally {
return this;
}
}
}
const CacheController = exports.CacheController = new CacheControllerClass();