@autofleet/matmon 2.0.0 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  import { getNewLRU, getWithCacheSupport } from './cache';
2
2
  import RedisCache from './redis';
3
- import { ORMCache } from './orm-cache';
4
- export { getNewLRU, getWithCacheSupport, RedisCache, ORMCache, };
3
+ import { ORMCache, ORMTypes } from './orm-cache';
4
+ export { getNewLRU, getWithCacheSupport, RedisCache, ORMCache, ORMTypes };
package/lib/index.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ORMCache = exports.RedisCache = exports.getWithCacheSupport = exports.getNewLRU = void 0;
6
+ exports.ORMTypes = exports.ORMCache = exports.RedisCache = exports.getWithCacheSupport = exports.getNewLRU = void 0;
7
7
  const cache_1 = require("./cache");
8
8
  Object.defineProperty(exports, "getNewLRU", { enumerable: true, get: function () { return cache_1.getNewLRU; } });
9
9
  Object.defineProperty(exports, "getWithCacheSupport", { enumerable: true, get: function () { return cache_1.getWithCacheSupport; } });
@@ -11,3 +11,4 @@ const redis_1 = __importDefault(require("./redis"));
11
11
  exports.RedisCache = redis_1.default;
12
12
  const orm_cache_1 = require("./orm-cache");
13
13
  Object.defineProperty(exports, "ORMCache", { enumerable: true, get: function () { return orm_cache_1.ORMCache; } });
14
+ Object.defineProperty(exports, "ORMTypes", { enumerable: true, get: function () { return orm_cache_1.ORMTypes; } });
@@ -1,10 +1,13 @@
1
1
  import RedisCache from '../redis';
2
2
  export interface ModelOptions {
3
3
  name: string;
4
- associations: {
5
- name: string;
6
- alias: string;
7
- }[];
4
+ associations?: AssociationOptions[];
5
+ }
6
+ export interface AssociationOptions {
7
+ name: string;
8
+ alias: string;
9
+ accessKey?: string;
10
+ innerAssociation?: AssociationOptions;
8
11
  }
9
12
  export interface Adapter {
10
13
  ormInstance: any;
@@ -1,5 +1,5 @@
1
1
  import { ModelOptions } from './adapter';
2
- declare enum ORMTypes {
2
+ export declare enum ORMTypes {
3
3
  SEQUELIZE = "sequelize"
4
4
  }
5
5
  interface ORMCacheOptions {
@@ -7,7 +7,6 @@ interface ORMCacheOptions {
7
7
  models: ModelOptions[];
8
8
  ormInstance: any;
9
9
  debug: boolean;
10
- ttl: number;
11
10
  }
12
11
  export declare const ORMCache: (options: ORMCacheOptions) => void;
13
12
  export {};
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.ORMCache = void 0;
6
+ exports.ORMCache = exports.ORMTypes = void 0;
7
7
  const sequelize_adapter_1 = __importDefault(require("./sequelize-adapter"));
8
8
  const errors_1 = require("./errors");
9
9
  const redis_1 = __importDefault(require("../redis"));
@@ -11,7 +11,7 @@ const logger_1 = __importDefault(require("../logger"));
11
11
  var ORMTypes;
12
12
  (function (ORMTypes) {
13
13
  ORMTypes["SEQUELIZE"] = "sequelize";
14
- })(ORMTypes || (ORMTypes = {}));
14
+ })(ORMTypes = exports.ORMTypes || (exports.ORMTypes = {}));
15
15
  const ORMInstanceFactory = (options) => {
16
16
  switch (options.type) {
17
17
  case ORMTypes.SEQUELIZE: {
@@ -23,13 +23,12 @@ const ORMInstanceFactory = (options) => {
23
23
  }
24
24
  };
25
25
  exports.ORMCache = (options) => {
26
- const { type, models, ormInstance, } = options;
26
+ const { models } = options;
27
27
  logger_1.default.info('Starting ORM Cache', { options });
28
28
  const adapter = ORMInstanceFactory(options);
29
29
  const cache = new redis_1.default({
30
30
  host: process.env.REDIS_HOST,
31
31
  port: process.env.REDIS_PORT,
32
- ttl: options.ttl || 90,
33
32
  });
34
33
  // eslint-disable-next-line array-callback-return
35
34
  models.map((modelOptions) => {
@@ -1,11 +1,12 @@
1
1
  import { Sequelize } from 'sequelize';
2
- import { Adapter, ModelOptions } from './adapter';
2
+ import { Adapter, AssociationOptions, ModelOptions } from './adapter';
3
3
  import RedisCache from '../redis';
4
4
  export default class SequelizeAdapter implements Adapter {
5
5
  ormInstance: Sequelize;
6
6
  debugMode: boolean;
7
7
  constructor(sequelize: Sequelize, debug: any);
8
8
  getModel(modelName: string): any;
9
+ getModelDependencies(modelName: string): AssociationOptions[];
9
10
  debug(message: any, payload: any): void;
10
11
  injectGetWithCacheFunction(cache: RedisCache, modelOptions: ModelOptions): void;
11
12
  addInvalidationHooks(cache: RedisCache, modelOptions: ModelOptions): void;
@@ -15,10 +15,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
15
15
  const logger_1 = __importDefault(require("../logger"));
16
16
  const util_1 = require("util");
17
17
  const { AF_SERVICE_NAME } = process.env;
18
+ const ORM_CACHE_PREFIX = 'ormCache';
18
19
  const INVALIDATION_HOOKS = ['afterSave', 'afterUpdate', 'afterDestroy'];
19
20
  const BULK_HOOKS = ['beforeBulkUpdate', 'beforeBulkDestroy'];
20
- const generateInstanceKey = (modelOptions, id) => `${AF_SERVICE_NAME}:${modelOptions.name}_${id}_INCLUDING_${modelOptions.associations.map(a => a.name).join('_')}`;
21
- const generateDependencyKey = (modelName, associationName, associationId) => `${AF_SERVICE_NAME}:${modelName}_${associationName}_${associationId}_DEPENDENCIES`;
21
+ const generateInstanceKey = (name, id) => `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${name}_${id}`;
22
+ const generateDependencyKey = (modelName, associationName, associationId) => `${AF_SERVICE_NAME}:${ORM_CACHE_PREFIX}:${modelName}_${associationName}_${associationId}_DEPENDENCIES`;
23
+ const getInstanceDependencyKeys = (modelOptions, instance) => {
24
+ const keys = modelOptions.associations
25
+ .filter((associationOptions) => instance[associationOptions.alias])
26
+ .map((associationOptions) => {
27
+ const accessKey = associationOptions.accessKey;
28
+ const depKeys = [];
29
+ if (Array.isArray(instance[associationOptions.alias])) {
30
+ instance[associationOptions.alias].map(associationInstance => {
31
+ depKeys.push(generateDependencyKey(modelOptions.name, associationOptions.name, associationInstance[accessKey]));
32
+ if (associationOptions.innerAssociation &&
33
+ associationInstance[associationOptions.innerAssociation.alias]) {
34
+ depKeys.push(generateDependencyKey(modelOptions.name, associationOptions.innerAssociation.name, associationInstance[associationOptions.innerAssociation.alias][associationOptions.innerAssociation.accessKey]));
35
+ }
36
+ });
37
+ return depKeys;
38
+ }
39
+ return [
40
+ generateDependencyKey(modelOptions.name, associationOptions.name, instance[associationOptions.alias][accessKey]),
41
+ ];
42
+ });
43
+ return keys.reduce((flattenArray, array) => flattenArray.concat(array), []);
44
+ };
22
45
  const handleTransactionHook = (instance, options, func) => {
23
46
  const { transaction } = options;
24
47
  if (transaction) {
@@ -36,17 +59,37 @@ class SequelizeAdapter {
36
59
  getModel(modelName) {
37
60
  return this.ormInstance.models[modelName];
38
61
  }
62
+ getModelDependencies(modelName) {
63
+ const { associations } = this.ormInstance.models[modelName];
64
+ return [
65
+ ...Object.keys(associations).map(association => {
66
+ const sequelizeAssociation = associations[association];
67
+ const associationOptions = {
68
+ alias: sequelizeAssociation.as,
69
+ name: sequelizeAssociation.target.name,
70
+ accessKey: sequelizeAssociation.target.primaryKeyAttribute,
71
+ };
72
+ if (sequelizeAssociation.through) {
73
+ const relationModel = sequelizeAssociation.through.model;
74
+ associationOptions.innerAssociation = {
75
+ alias: relationModel.name,
76
+ name: relationModel.name,
77
+ accessKey: relationModel.primaryKeyAttribute,
78
+ };
79
+ }
80
+ return associationOptions;
81
+ }),
82
+ ];
83
+ }
39
84
  debug(message, payload) {
40
85
  if (this.debugMode) {
41
86
  logger_1.default.info(`[ORM_CACHE Debug] ${message}`, payload);
42
87
  }
43
88
  }
44
89
  injectGetWithCacheFunction(cache, modelOptions) {
45
- const addDependencies = (modelName, instance) => __awaiter(this, void 0, void 0, function* () {
46
- const dependencyKeys = modelOptions.associations
47
- .filter(association => instance[association.alias])
48
- .map(association => generateDependencyKey(modelName, association.name, instance[association.alias].id));
49
- const instanceKey = generateInstanceKey(modelOptions, instance.id);
90
+ const addDependencies = (instance) => __awaiter(this, void 0, void 0, function* () {
91
+ const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);
92
+ const instanceKey = generateInstanceKey(modelOptions.name, instance.id);
50
93
  this.debug('Adding dependencies', { instanceKey, dependencyKeys });
51
94
  const addDependenciesMulti = cache.getClient().multi();
52
95
  const addDependenciesMultiAsync = util_1.promisify(addDependenciesMulti.exec).bind(addDependenciesMulti);
@@ -55,29 +98,28 @@ class SequelizeAdapter {
55
98
  });
56
99
  const model = this.getModel(modelOptions.name);
57
100
  model.findByPkCached = (id, scopes, options) => __awaiter(this, void 0, void 0, function* () {
58
- const cacheKey = generateInstanceKey(modelOptions, id);
101
+ const cacheKey = generateInstanceKey(modelOptions.name, id);
59
102
  let value = JSON.parse(yield cache.getClient().getAsync(cacheKey));
60
103
  if (!value) {
61
104
  this.debug('Value not found in cache, looking in db', { id, cacheKey });
62
105
  value = yield model.scope(scopes).findByPk(id, options);
63
106
  this.debug('Value from DB', { value: value || 'not found', cacheKey });
64
- const [setRes] = yield Promise.all([
107
+ yield Promise.all([
65
108
  cache.getClient().setAsync(cacheKey, JSON.stringify(value)),
66
- value && addDependencies(modelOptions.name, value),
109
+ value && addDependencies(value),
67
110
  ]);
68
111
  }
69
112
  else {
113
+ value = this.getModel(modelOptions.name).build(value, { isNewRecord: false, include: options.include });
70
114
  this.debug('Found cached value', { value, id, cacheKey });
71
115
  }
72
116
  return value;
73
117
  });
74
118
  }
75
119
  addInvalidationHooks(cache, modelOptions) {
76
- const invalidateModelInstance = (modelName, instance) => __awaiter(this, void 0, void 0, function* () {
77
- const dependencyKeys = modelOptions.associations
78
- .filter(association => instance[association.alias])
79
- .map(association => generateDependencyKey(modelName, association.name, instance[association.alias].id));
80
- const instanceKey = generateInstanceKey(modelOptions, instance.id);
120
+ const invalidateModelInstance = (instance) => __awaiter(this, void 0, void 0, function* () {
121
+ const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);
122
+ const instanceKey = generateInstanceKey(modelOptions.name, instance.id);
81
123
  this.debug('Removing dependencies', { instance, instanceKey, dependencyKeys });
82
124
  const removeMulti = cache.getClient().multi();
83
125
  const removeMultiAsync = util_1.promisify(removeMulti.exec).bind(removeMulti);
@@ -85,8 +127,8 @@ class SequelizeAdapter {
85
127
  removeMulti.del(instanceKey);
86
128
  return removeMultiAsync();
87
129
  });
88
- const invalidateModelInstanceByAssociation = (modelName, association, associationId) => __awaiter(this, void 0, void 0, function* () {
89
- const dependentInstancesKeys = yield cache.getClient().smembersAsync(generateDependencyKey(modelName, association, associationId));
130
+ const invalidateModelInstanceByAssociation = (association, associationId) => __awaiter(this, void 0, void 0, function* () {
131
+ const dependentInstancesKeys = yield cache.getClient().smembersAsync(generateDependencyKey(modelOptions.name, association, associationId));
90
132
  this.debug('Invalidating dependent instances', { dependentInstancesKeys });
91
133
  const removeMulti = cache.getClient().multi();
92
134
  const removeMultiAsync = util_1.promisify(removeMulti.exec).bind(removeMulti);
@@ -95,9 +137,7 @@ class SequelizeAdapter {
95
137
  if (!instance) {
96
138
  return [];
97
139
  }
98
- const dependencyKeys = modelOptions.associations
99
- .filter(association => instance[association.alias])
100
- .map(association => generateDependencyKey(modelName, association.name, instance[association.alias].id));
140
+ const dependencyKeys = getInstanceDependencyKeys(modelOptions, instance);
101
141
  dependencyKeys.reduce((multi, key) => multi.srem(key, instanceKey), removeMulti);
102
142
  removeMulti.del(instanceKey);
103
143
  return dependencyKeys;
@@ -106,12 +146,19 @@ class SequelizeAdapter {
106
146
  return removeMultiAsync();
107
147
  });
108
148
  const model = this.getModel(modelOptions.name);
109
- INVALIDATION_HOOKS.map(hook => model.addHook(hook, (instance, options) => handleTransactionHook(instance, options, instance => invalidateModelInstance(modelOptions.name, instance))));
149
+ INVALIDATION_HOOKS.map(hook => model.addHook(hook, (instance, options) => handleTransactionHook(instance, options, instance => invalidateModelInstance(instance))));
110
150
  BULK_HOOKS.map(hook => model.addHook(hook, options => options.individualHook = true));
111
- modelOptions.associations.map(association => {
112
- const associationModel = this.getModel(association.name);
113
- INVALIDATION_HOOKS.map(hook => associationModel.addHook(hook, (instance, options) => handleTransactionHook(instance, options, ({ id }) => invalidateModelInstanceByAssociation(modelOptions.name, association.name, id))));
151
+ modelOptions.associations = this.getModelDependencies(modelOptions.name);
152
+ this.debug(`Adding Invalidations Hooks to ${modelOptions.name}'s associations`, { associations: modelOptions.associations });
153
+ modelOptions.associations.map((associationOptions) => {
154
+ const associationModel = this.getModel(associationOptions.name);
155
+ INVALIDATION_HOOKS.map(hook => associationModel.addHook(hook, (instance, options) => handleTransactionHook(instance, options, associationInstance => invalidateModelInstanceByAssociation(associationOptions.name, associationInstance[associationOptions.accessKey]))));
114
156
  BULK_HOOKS.map(hook => associationModel.addHook(hook, options => options.individualHook = true));
157
+ if (associationOptions.innerAssociation) {
158
+ const innerAssociationModel = this.getModel(associationOptions.innerAssociation.name);
159
+ INVALIDATION_HOOKS.map(hook => innerAssociationModel.addHook(hook, (instance, options) => handleTransactionHook(instance, options, innerAssociationInstance => invalidateModelInstanceByAssociation(associationOptions.innerAssociation.name, innerAssociationInstance[associationOptions.innerAssociation.accessKey]))));
160
+ BULK_HOOKS.map(hook => innerAssociationModel.addHook(hook, options => options.individualHook = true));
161
+ }
115
162
  });
116
163
  }
117
164
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@autofleet/matmon",
3
- "version": "2.0.0",
3
+ "version": "2.0.3",
4
4
  "description": "manage cache",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",