import hashObject from 'object-hash'; import EntityRepository from './EntityRepository'; export default class CachableRepository extends EntityRepository { repositoryName: string; cache: any; i18n: any; /** * Constructor method. * @param {Knex} knex * @param {Cache} cache */ constructor(knex, cache, i18n) { super(knex); this.cache = cache; this.i18n = i18n; this.repositoryName = this.constructor.name; } getByCache(key, callback) { return callback(); } /** * Retrieve the cache key of the method name and arguments. * @param {string} method * @param {...any} args * @return {string} */ getCacheKey(method, ...args) { const hashArgs = hashObject({ ...args }); const repositoryName = this.repositoryName; return `${repositoryName}-${method}-${hashArgs}`; } /** * Retrieve all entries with specified relations. * @param withRelations */ all(withRelations?, trx?) { const cacheKey = this.getCacheKey('all', withRelations); return this.getByCache(cacheKey, () => { return super.all(withRelations, trx); }); } /** * Finds list of entities with specified attributes * @param {Object} attributeValues - values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve. * @returns {Promise} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ find(attributeValues = {}, withRelations?) { const cacheKey = this.getCacheKey('find', attributeValues, withRelations); return this.getByCache(cacheKey, () => { return super.find(attributeValues, withRelations); }); } /** * Finds list of entities with attribute values that are different from specified ones * @param {Object} attributeValues - values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {Promise} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ findWhereNot(attributeValues = {}, withRelations?) { const cacheKey = this.getCacheKey( 'findWhereNot', attributeValues, withRelations ); return this.getByCache(cacheKey, () => { return super.findWhereNot(attributeValues, withRelations); }); } /** * Finds list of entities with specified attributes (any of multiple specified values) * Supports both ('attrName', ['value1', 'value2]) and ({attrName: ['value1', 'value2']} formats) * * @param {string|Object} searchParam - attribute name or search criteria object * @param {*[]} [attributeValues] - attribute values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {PromiseLike} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ findWhereIn(searchParam, attributeValues, withRelations?) { const cacheKey = this.getCacheKey( 'findWhereIn', attributeValues, withRelations ); return this.getByCache(cacheKey, () => { return super.findWhereIn(searchParam, attributeValues, withRelations); }); } /** * Finds first entity by given parameters * * @param {Object} attributeValues - values to filter retrieved entities by * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {Promise} */ findOne(attributeValues = {}, withRelations?) { const cacheKey = this.getCacheKey( 'findOne', attributeValues, withRelations ); return this.getByCache(cacheKey, () => { return super.findOne(attributeValues, withRelations); }); } /** * Finds first entity by given parameters * * @param {string || number} id - value of id column of the entity * @param {string || string[]} [withRelations] - name of relation(s) to eagerly retrieve, as defined in model relationMappings() * @returns {Promise} */ findOneById(id, withRelations?) { const cacheKey = this.getCacheKey('findOneById', id, withRelations); return this.getByCache(cacheKey, () => { return super.findOneById(id, withRelations); }); } /** * Persists new entity or an array of entities. * This method does not recursively persist related entities, use createRecursively (to be implemented) for that. * Batch insert only works on PostgreSQL * @param {Object} entity - model instance or parameters for a new entity * @returns {Promise} - query builder. You can chain additional methods to it or call "await" or then() on it to execute */ async create(entity, trx?) { const result = await super.create(entity, trx); // Flushes the repository cache after insert operation. this.flushCache(); return result; } /** * Persists updated entity. If previously set fields are not present, performs an incremental update (does not remove fields unless explicitly set to null) * * @param {Object} entity - single entity instance * @param {Object} [trx] - knex transaction instance. If not specified, new implicit transaction will be used. * @returns {Promise} number of affected rows */ async update(entity, whereAttributes?, trx?) { const result = await super.update(entity, whereAttributes, trx); // Flushes the repository cache after update operation. this.flushCache(); return result; } /** * @param {Object} attributeValues - values to filter deleted entities by * @param {Object} [trx] * @returns {Promise} Query builder. After promise is resolved, returns count of deleted rows */ async deleteBy(attributeValues, trx?) { const result = await super.deleteBy(attributeValues, trx); this.flushCache(); return result; } /** * @param {string || number} id - value of id column of the entity * @returns {Promise} Query builder. After promise is resolved, returns count of deleted rows */ deleteById(id: number | string, trx?) { const result = super.deleteById(id, trx); // Flushes the repository cache after insert operation. this.flushCache(); return result; } /** * * @param {string|number[]} values - */ async deleteWhereIn(field: string, values: string | number[]) { const result = await super.deleteWhereIn(field, values); // Flushes the repository cache after delete operation. this.flushCache(); return result; } /** * * @param {string|number[]} values */ async deleteWhereIdIn(values: string | number[], trx?) { const result = await super.deleteWhereIdIn(values, trx); // Flushes the repository cache after delete operation. this.flushCache(); return result; } /** * * @param graph * @param options */ async upsertGraph(graph, options) { const result = await super.upsertGraph(graph, options); // Flushes the repository cache after insert operation. this.flushCache(); return result; } /** * * @param {} whereAttributes * @param {string} field * @param {number} amount */ async changeNumber(whereAttributes, field: string, amount: number, trx?) { const result = await super.changeNumber( whereAttributes, field, amount, trx ); // Flushes the repository cache after update operation. this.flushCache(); return result; } /** * Flush repository cache. */ flushCache(): void { this.cache.delStartWith(this.repositoryName); } }