ns2-front-module-common
Version:
NS2 common module
316 lines • 15.4 kB
JavaScript
import { KeyValueCacheStructure } from "../models/key-value-cache-structure.model";
import { Injectable } from "@angular/core";
import { Md5 } from 'ts-md5/dist/md5';
import { STORAGE_TOKEN_KEY } from "./current-user.service";
/**
* Базовый сервис для key-value хранилищ
*/
var AbstractKeyValueStorage = (function () {
function AbstractKeyValueStorage() {
// Флаг, обозначающий что процесс очистки выполняется
this.maintenanceProcessing = false;
// Список ключей которые нельзя удалять при процедуре очистки
this.maintenanceExceptKeys = [STORAGE_TOKEN_KEY];
}
/**
* Сохранение данных по ключу на указанное время, с учетом переданных тегов.
* При ошибке сохранения запускается метод очистки хранилища (т.к. ошибка могла произойти из-за нехватки места)
* и после очистки сохранение пытается выполниться еще раз.
*
* @param {string} key Ключ, по которому необходимо сохранить данные
* @param {any} value Данные для сохранения
* @param {number} expired Количество секунд в течение которых данные будут храниться
* @param {string[]} tags Массив тегов, которыми необходимо пометить набор данных
* @returns {Promise<T>}
*/
AbstractKeyValueStorage.prototype.set = function (key, value, expired, tags) {
var _this = this;
var item = new KeyValueCacheStructure(expired, tags, value);
return new Promise(function (resolve, reject) {
var res = function () {
resolve(value);
};
_this.setInternal(key, item)
.then(res)
.catch(function () {
_this.maintenance()
.then(function () {
_this.setInternal(key, item).then(res).catch(reject);
})
.catch(reject);
});
});
};
/**
* Получение значения по ключу.
* Если значение существует, но истек срок его хранения - удаляем эту запись и все с ней связанное из хранилища.
*
* @param {string} key Ключ, по которому необходимо получить значение
* @returns {Promise<T>}
*/
AbstractKeyValueStorage.prototype.get = function (key) {
var _this = this;
return new Promise(function (resolve, reject) {
_this._get(_this.prepareKey(key)).then(function (value) {
try {
var item_1 = KeyValueCacheStructure.unserialize(value);
if (item_1.isExpired()) {
_this.removeInternal(key, item_1.tags).then(reject).catch(reject);
}
else {
item_1.hits = item_1.hits + 1;
_this.setInternal(key, item_1).then(function () {
resolve(item_1.value || null);
}).catch(function () {
resolve(item_1.value || null);
});
}
}
catch (err) {
reject(err);
}
}).catch(reject);
});
};
/**
* Удаление данных из хранилища по указанному тегу.
*
* @param {string} tag Имя тега по которому необходимо удалить данные
* @returns {Promise<T>}
*/
AbstractKeyValueStorage.prototype.removeByTag = function (tag) {
var _this = this;
return new Promise(function (resolve, reject) {
var depends = [];
var realTagName = _this.prepareTag(tag);
_this._get(realTagName).then(function (keysListJson) {
var keysList = JSON.parse(keysListJson);
depends = keysList.map(function (keyName) {
return new Promise(function (res, rej) {
var realKeyName = _this.prepareKey(keyName);
_this._get(realKeyName).then(function (value) {
var item = KeyValueCacheStructure.unserialize(value);
_this.removeInternal(keyName, item.tags).then(res).catch(rej);
}).catch(rej);
});
});
var rmDepends = function () {
Promise.all(depends).then(resolve).catch(resolve);
};
_this._remove(realTagName).then(rmDepends).catch(rmDepends);
}).catch(reject);
});
};
/**
* Удаление данных из хранилища по ключу.
*
* @param {string} key Ключ по которым необходимо удалить данные
* @returns {Promise<T>}
*/
AbstractKeyValueStorage.prototype.remove = function (key) {
var _this = this;
return new Promise(function (resolve, reject) {
var realKeyName = _this.prepareKey(key);
_this._get(realKeyName).then(function (jsonItem) {
var item = KeyValueCacheStructure.unserialize(jsonItem);
_this.removeInternal(key, item.tags).then(resolve).catch(reject);
}).catch(reject);
});
};
/**
* Очистка всего хранилища.
*
* @returns {Promise<any>}
*/
AbstractKeyValueStorage.prototype.clear = function () {
return this._clear();
};
/**
* Очистка неиспользуемых/редко используемых данных, освобождение места.
*
* @returns {Promise<any>}
*/
AbstractKeyValueStorage.prototype.maintenance = function () {
var _this = this;
return new Promise(function (resolve, reject) {
if (!_this.maintenanceProcessing) {
_this._getStorageInstance().then(function (storage) {
_this.maintenanceProcessing = true;
var keys = Object.keys(storage);
var CLEAN_RECORDS = Math.floor(keys.length * AbstractKeyValueStorage.CLEAN_RECORDS_PERCENT / 100);
var list2Clean = {};
var toCleanLength = 0;
var minKeyName = '';
var minHits = 0;
var cleanOnlyEmpty = true;
for (var i = 0; i < keys.length; i++) {
var keyName = keys[i];
if (keyName.indexOf(AbstractKeyValueStorage.DATA_PREFIX) === 0) {
var originalName = keyName.substring(AbstractKeyValueStorage.DATA_PREFIX.length), skipKey = (_this.maintenanceExceptKeys.indexOf(originalName) > -1);
var jsonInfo = storage.getItem(keyName);
if ((jsonInfo !== null) && !skipKey) {
var item = KeyValueCacheStructure.unserialize(jsonInfo);
if (toCleanLength < CLEAN_RECORDS) {
list2Clean[keyName] = item.hits;
toCleanLength++;
if ((minKeyName == '') || (minHits > item.hits)) {
minKeyName = keyName;
minHits = item.hits;
}
if (item.hits > 0) {
cleanOnlyEmpty = false;
}
}
else if (cleanOnlyEmpty && (toCleanLength >= CLEAN_RECORDS)) {
break;
}
else if (item.hits < minHits) {
delete list2Clean[minKeyName];
list2Clean[keyName] = item.hits;
if (item.hits > 0) {
cleanOnlyEmpty = false;
}
}
}
}
}
var depends = [];
for (var kName in list2Clean) {
if (list2Clean.hasOwnProperty(kName)) {
var name_1 = kName.substring(AbstractKeyValueStorage.DATA_PREFIX.length);
depends.push(_this.remove(name_1));
}
}
Promise.all(depends).then(function () {
_this.maintenanceProcessing = false;
resolve();
}).catch(function () {
_this.maintenanceProcessing = false;
resolve();
});
}).catch(reject);
}
else {
resolve();
}
});
};
/**
* Генерирует имя для использования в качестве ключа
*
* @param args Аргументы участвующие в формировании ключа
* @returns {string}
*/
AbstractKeyValueStorage.prototype.getCacheKey = function () {
var args = [];
for (var _i = 0; _i < arguments.length; _i++) {
args[_i] = arguments[_i];
}
var result = JSON.stringify(args);
return String(Md5.hashStr(result));
};
/**
* Установка значения в хранилище и помечание его тегами
*
* @param {string} key Ключ по которому надо сохранить значение
* @param {KeyValueCacheStructure} item Структура элемента который надо сохранить
* @returns {Promise<T>}
*/
AbstractKeyValueStorage.prototype.setInternal = function (key, item) {
var _this = this;
return new Promise(function (resolve, reject) {
var depends = item.tags.map(function (tagKey) {
return new Promise(function (res, rej) {
var realTagKey = _this.prepareTag(tagKey);
_this._get(realTagKey).then(function (tagValue) {
var iTags = JSON.parse(tagValue);
var sIndex = iTags.indexOf(key);
if (sIndex === -1) {
iTags.push(key);
_this._set(realTagKey, JSON.stringify(iTags)).then(res).catch(rej);
}
else {
res();
}
}).catch(function () {
_this._set(realTagKey, JSON.stringify([key])).then(res).catch(rej);
});
});
});
depends.push(_this._set(_this.prepareKey(key), item.serialize()));
Promise.all(depends).then(resolve).catch(reject);
});
};
/**
* Удаление значения по указанному ключу, помеченное переданными тегами.
* Удаляется ключ с его значением, из тегов которыми он был помечен удаляется этот ключ
* (если тег после удаления ключа становится пустым - он удаляется также)
*
* @param {string} key Ключ по которому надо удалить данные
* @param {string[]} tags Массив тегов которыми помечена удаляемая запись
* @returns {Promise<T>}
*/
AbstractKeyValueStorage.prototype.removeInternal = function (key, tags) {
var _this = this;
return new Promise(function (resolve, reject) {
var depends = tags.map(function (tagKey) {
return new Promise(function (res, rej) {
tagKey = _this.prepareTag(tagKey);
_this._get(tagKey)
.then(function (tagValue) {
var iTags = JSON.parse(tagValue);
var sIndex = iTags.indexOf(key);
if (sIndex > -1) {
iTags.splice(sIndex, 1);
if (iTags.length > 0) {
_this._set(tagKey, JSON.stringify(iTags)).then(res).catch(rej);
}
else {
_this._remove(tagKey).then(res).catch(rej);
}
}
else {
rej();
}
})
.catch(rej);
});
});
depends.push(_this._remove(_this.prepareKey(key)));
Promise.all(depends).then(resolve).catch(reject);
});
};
/**
* Формирует имя тега для сохранения в хранилище
*
* @param {string} tag Оригинальное имя тега (которое указал программист)
* @returns {string}
*/
AbstractKeyValueStorage.prototype.prepareTag = function (tag) {
return AbstractKeyValueStorage.TAGS_PREFIX + tag.trim().toLowerCase();
};
;
/**
* Формирует имя ключа для сохранения в хранилище
*
* @param {string} key Оригинальное имя ключа (которое указал программист)
* @returns {string}
*/
AbstractKeyValueStorage.prototype.prepareKey = function (key) {
return AbstractKeyValueStorage.DATA_PREFIX + key.trim().toLowerCase();
};
return AbstractKeyValueStorage;
}());
export { AbstractKeyValueStorage };
// Количество наименее редко используемых записей (в процентах) которые будут удалены при очередной очистке
AbstractKeyValueStorage.CLEAN_RECORDS_PERCENT = 30;
// Префикс ключей при хранении тегов
AbstractKeyValueStorage.TAGS_PREFIX = 'T_';
// Префикс ключей при хранении данных
AbstractKeyValueStorage.DATA_PREFIX = 'D_';
AbstractKeyValueStorage.decorators = [
{ type: Injectable },
];
/** @nocollapse */
AbstractKeyValueStorage.ctorParameters = function () { return []; };
//# sourceMappingURL=abstract-key-value-storage.service.js.map