From 6591d6e7bdd7f456cbbad030db61783c2a726aa8 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Tue, 10 Jan 2023 14:51:38 +0100 Subject: [PATCH 01/12] chore: initial plugin implementation --- package-lock.json | 13 +++++++++++++ package.json | 5 ++++- src/internal.ts | 5 +++-- src/lib.ts | 2 -- src/models.ts | 3 +++ src/plugins/index.ts | 26 ++++++++++++++++++++++++++ src/plugins/models.ts | 14 ++++++++++++++ 7 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 src/plugins/index.ts create mode 100644 src/plugins/models.ts diff --git a/package-lock.json b/package-lock.json index 7cea37e..3879261 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,9 @@ "name": "@jointly/cache-candidate", "version": "0.2.0", "license": "MIT", + "dependencies": { + "hook-fn": "^1.0.3" + }, "devDependencies": { "@commitlint/cli": "^17.3.0", "@commitlint/config-conventional": "^17.3.0", @@ -4079,6 +4082,11 @@ "node": ">=8" } }, + "node_modules/hook-fn": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hook-fn/-/hook-fn-1.0.3.tgz", + "integrity": "sha512-ohtle67yoyTsdggj2iVy/D1JxK8QZSR9LbSAAKTc83xqOmUg0YXAingdiSRwqyvjOiKAj/3mm+HQhuO1JVeTfA==" + }, "node_modules/hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", @@ -10372,6 +10380,11 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "hook-fn": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/hook-fn/-/hook-fn-1.0.3.tgz", + "integrity": "sha512-ohtle67yoyTsdggj2iVy/D1JxK8QZSR9LbSAAKTc83xqOmUg0YXAingdiSRwqyvjOiKAj/3mm+HQhuO1JVeTfA==" + }, "hosted-git-info": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", diff --git a/package.json b/package.json index 936a930..1e2aab3 100644 --- a/package.json +++ b/package.json @@ -61,5 +61,8 @@ "url": "git://github.com/JointlyTech/cache-candidate.git" }, "license": "MIT", - "author": "Jointly " + "author": "Jointly ", + "dependencies": { + "hook-fn": "^1.0.3" + } } diff --git a/src/internal.ts b/src/internal.ts index 9948313..e719bdb 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -8,6 +8,7 @@ import { RunningQueryCache, TimeFrameCache } from './models'; +import { pippo } from './plugins'; function isTimeFrameCacheRecordExpired(executionEnd: any, options: any) { return Date.now() < executionEnd + options.timeFrame; @@ -326,7 +327,7 @@ export async function letsCandidate({ const execution = originalMethod(...args); // If execution is not a promise, handle the result and return it. if (!(execution instanceof Promise)) { - handleResult({ + pippo('a', 'b')(handleResult)({ result: execution, runningQueryCache, key, @@ -341,7 +342,7 @@ export async function letsCandidate({ runningQueryCache.set(key, execution); execution.then((result: unknown) => - handleResult({ + pippo('a', 'b')(handleResult)({ result, runningQueryCache, key, diff --git a/src/lib.ts b/src/lib.ts index f6caacb..8258f90 100644 --- a/src/lib.ts +++ b/src/lib.ts @@ -11,7 +11,6 @@ export function CacheCandidate(_options: Partial = {}) { options } = getInitialState(_options); - // Execute the function and get the execution times. return function ( target: any, propertyKey: string, @@ -55,7 +54,6 @@ export function cacheCandidate( options } = getInitialState(_options); - // Execute the function and get the execution times. return async (...args: any[]) => letsCandidate({ options, diff --git a/src/models.ts b/src/models.ts index 04a6ab5..36882eb 100644 --- a/src/models.ts +++ b/src/models.ts @@ -1,3 +1,5 @@ +import { CacheCandidatePlugin } from './plugins/models'; + export interface CandidateFunctionOptions { timeFrameCacheRecords: Array; options: CacheCandidateOptions; @@ -32,6 +34,7 @@ export interface CacheCandidateOptions { }) => void; onLog: ({ key, event }: { key: string; event: Events }) => void; }; + plugins?: Array; dependencyKeys?: | string | number diff --git a/src/plugins/index.ts b/src/plugins/index.ts new file mode 100644 index 0000000..5c3cfc8 --- /dev/null +++ b/src/plugins/index.ts @@ -0,0 +1,26 @@ +import { hook } from 'hook-fn'; +import { CacheCandidatePlugin, Hooks } from './models'; + +export function ExecuteHook( + hook: Hooks, + plugins: Array = [], + ...args: any[] +) { + for (const plugin of plugins) { + const actionableHook = plugin.hooks.find((h) => h.hook === hook); + if (actionableHook) { + actionableHook.action(...args); + } + } +} + +export function pippo(HookBefore: string, HookAfter: string) { + return hook({ + before: ({ context, args }) => { + console.log('before', HookBefore, args); + }, + after: ({ context, args, result }) => { + console.log('after', HookAfter, args); + } + }); +} diff --git a/src/plugins/models.ts b/src/plugins/models.ts new file mode 100644 index 0000000..67377c3 --- /dev/null +++ b/src/plugins/models.ts @@ -0,0 +1,14 @@ +export enum Hooks { + REFRESH_KEEP_ALIVE_PRE = 'REFRESH_KEEP_ALIVE_PRE', + REFRESH_KEEP_ALIVE_POST = 'REFRESH_KEEP_ALIVE_POST' +} + +export type ActionableHook = { + hook: Hooks; + action: (...args: any[]) => void; +}; + +export type CacheCandidatePlugin = { + name: string; + hooks: Array; +}; From 5e74da0417324ebe9d3ce0c380d33aa7ca5b81df Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Tue, 10 Jan 2023 18:35:33 +0100 Subject: [PATCH 02/12] chore: second iteration. plugin system works without breaking tests. Co-authored-by: Carmelo Badalamenti --- package-lock.json | 24 ++++++++-------- src/default.ts | 1 + src/internal.ts | 64 +++++++++++++++++++++++++++++++++++-------- src/models.ts | 3 ++ src/plugins/index.ts | 20 ++++++++------ src/plugins/models.ts | 23 ++++++++++++++-- 6 files changed, 100 insertions(+), 35 deletions(-) diff --git a/package-lock.json b/package-lock.json index 3879261..7f90c5c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4083,9 +4083,9 @@ } }, "node_modules/hook-fn": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/hook-fn/-/hook-fn-1.0.3.tgz", - "integrity": "sha512-ohtle67yoyTsdggj2iVy/D1JxK8QZSR9LbSAAKTc83xqOmUg0YXAingdiSRwqyvjOiKAj/3mm+HQhuO1JVeTfA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hook-fn/-/hook-fn-1.0.4.tgz", + "integrity": "sha512-BGcl7mkRinWm2eEGiLvrjkmVPmJCcq4qZdYqpGsXNqebjZ9j6s0WwAM+7SPC3c4YFBBr5t543rErpkmNjJxxrQ==" }, "node_modules/hosted-git-info": { "version": "4.1.0", @@ -5122,9 +5122,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -10381,9 +10381,9 @@ "dev": true }, "hook-fn": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/hook-fn/-/hook-fn-1.0.3.tgz", - "integrity": "sha512-ohtle67yoyTsdggj2iVy/D1JxK8QZSR9LbSAAKTc83xqOmUg0YXAingdiSRwqyvjOiKAj/3mm+HQhuO1JVeTfA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/hook-fn/-/hook-fn-1.0.4.tgz", + "integrity": "sha512-BGcl7mkRinWm2eEGiLvrjkmVPmJCcq4qZdYqpGsXNqebjZ9j6s0WwAM+7SPC3c4YFBBr5t543rErpkmNjJxxrQ==" }, "hosted-git-info": { "version": "4.1.0", @@ -11157,9 +11157,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonfile": { diff --git a/src/default.ts b/src/default.ts index 1e6b619..abd6f07 100644 --- a/src/default.ts +++ b/src/default.ts @@ -7,6 +7,7 @@ export const CacheCandidateOptionsDefault: CacheCandidateOptions = { requestsThreshold: 3, cache: makeAsyncMap(), keepAlive: false, + plugins: [], events: { onCacheHit: (_) => { return _; diff --git a/src/internal.ts b/src/internal.ts index e719bdb..bea5e8a 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -3,12 +3,15 @@ import { CacheCandidateOptionsDefault } from './default'; import { cacheCandidateDependencyManager } from './manager'; import { CacheCandidateOptions, + DataCacheRecordNotFound, Events, KeepAliveCache, RunningQueryCache, + RunningQueryRecordNotFound, TimeFrameCache } from './models'; -import { pippo } from './plugins'; +import { ExecuteHook } from './plugins'; +import { Hooks, PluginPayload } from './plugins/models'; function isTimeFrameCacheRecordExpired(executionEnd: any, options: any) { return Date.now() < executionEnd + options.timeFrame; @@ -51,7 +54,7 @@ function getRunningQueryRecord({ options.events.onLog({ key, event: Events.RUNNING_QUERY }); return runningQueryCache.get(key); } - return undefined; + return RunningQueryRecordNotFound; } export function getDataCacheKey(...args: any[]) { @@ -78,14 +81,13 @@ async function getDataCacheRecord({ // Remove the dataCache record if the time frame has passed. if (isDataCacheRecordExpired({ birthTime, options })) { await deleteDataCacheRecord({ options, key }); - return undefined; + return DataCacheRecordNotFound; } else { // Return the cached data - options.events.onCacheHit({ key }); return result; } } - return undefined; + return DataCacheRecordNotFound; } async function addDataCacheRecord({ options, key, result }) { @@ -113,7 +115,8 @@ function handleResult({ options, timeframeCache, keepAliveTimeoutCache, - args + args, + HookPayload }: { result: unknown; runningQueryCache: RunningQueryCache; @@ -123,6 +126,7 @@ function handleResult({ timeframeCache: TimeFrameCache; keepAliveTimeoutCache: KeepAliveCache; args: any[]; + HookPayload: PluginPayload; }): any { const executionEnd = Date.now(); const executionTime = executionEnd - executionStart; @@ -145,8 +149,14 @@ function handleResult({ }); if (exceedingAmount >= options.requestsThreshold) { + ExecuteHook(Hooks.DATACACHE_RECORD_ADD_PRE, options.plugins, HookPayload); addDataCacheRecord({ options, key, result }) .then(async () => { + ExecuteHook( + Hooks.DATACACHE_RECORD_ADD_POST, + options.plugins, + HookPayload + ); options.events.onCacheSet({ key }); if (options.dependencyKeys !== undefined) { let dependencyKeys: any = options.dependencyKeys; @@ -297,9 +307,18 @@ export async function letsCandidate({ args: any[]; originalMethod: (...args: any[]) => Promise; }) { + const HookPayload = { + options, + key, + keepAliveTimeoutCache, + runningQueryCache, + timeframeCache, + fnArgs: args + }; + ExecuteHook(Hooks.INIT, options.plugins, HookPayload); // Check if result exists in dataCache const cachedData = await getDataCacheRecord({ options, key }); - if (typeof cachedData !== 'undefined') { + if (cachedData !== DataCacheRecordNotFound) { if (options.keepAlive) { refreshKeepAliveRecord({ keepAliveTimeoutCache, @@ -307,6 +326,12 @@ export async function letsCandidate({ options }); } + + ExecuteHook(Hooks.CACHE_HIT, options.plugins, { + ...HookPayload, + result: cachedData + }); + options.events.onCacheHit({ key }); return Promise.resolve(cachedData); } @@ -316,18 +341,31 @@ export async function letsCandidate({ key, runningQueryCache }); - if (typeof runningQuery !== 'undefined') return runningQuery; + + if (runningQuery !== RunningQueryRecordNotFound) { + ExecuteHook(Hooks.CACHE_HIT, options.plugins, { + ...HookPayload, + result: runningQuery + }); + options.events.onCacheHit({ key }); + return runningQuery; + } // Check the timeframeCache and delete every element that has passed the time frame. expireTimeFrameCacheRecords({ options, key, timeframeCache }); // Execute the function + ExecuteHook(Hooks.EXECUTION_PRE, options.plugins, HookPayload); options.events.onBeforeFunctionExecution({ key }); const executionStart = Date.now(); const execution = originalMethod(...args); + ExecuteHook(Hooks.EXECUTION_POST, options.plugins, { + ...HookPayload, + result: execution + }); // If execution is not a promise, handle the result and return it. if (!(execution instanceof Promise)) { - pippo('a', 'b')(handleResult)({ + handleResult({ result: execution, runningQueryCache, key, @@ -335,14 +373,15 @@ export async function letsCandidate({ options, timeframeCache, keepAliveTimeoutCache, - args + args, + HookPayload }); return execution; } runningQueryCache.set(key, execution); execution.then((result: unknown) => - pippo('a', 'b')(handleResult)({ + handleResult({ result, runningQueryCache, key, @@ -350,7 +389,8 @@ export async function letsCandidate({ options, timeframeCache, keepAliveTimeoutCache, - args + args, + HookPayload }) ); diff --git a/src/models.ts b/src/models.ts index 36882eb..ec56307 100644 --- a/src/models.ts +++ b/src/models.ts @@ -68,3 +68,6 @@ export enum Events { CHECKING_MILLISECOND_THRESHOLD = 'CHECKING_MILLISECOND_THRESHOLD', CHECKING_REQUESTS_THRESHOLD = 'CHECKING_REQUESTS_THRESHOLD' } + +export const DataCacheRecordNotFound = Symbol('DataCacheRecordNotFound'); +export const RunningQueryRecordNotFound = Symbol('RunningQueryRecordNotFound'); diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 5c3cfc8..be0e6ae 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,26 +1,30 @@ import { hook } from 'hook-fn'; -import { CacheCandidatePlugin, Hooks } from './models'; +import { CacheCandidatePlugin, Hooks, PluginPayload } from './models'; export function ExecuteHook( hook: Hooks, plugins: Array = [], - ...args: any[] + payload: PluginPayload ) { for (const plugin of plugins) { const actionableHook = plugin.hooks.find((h) => h.hook === hook); if (actionableHook) { - actionableHook.action(...args); + actionableHook.action(payload); } } } -export function pippo(HookBefore: string, HookAfter: string) { +export function pluginHookWrap( + HookBefore: Hooks, + HookAfter: Hooks, + payload: PluginPayload +) { return hook({ - before: ({ context, args }) => { - console.log('before', HookBefore, args); + before: ({ args }) => { + ExecuteHook(HookBefore, args[0].plugins, payload); }, - after: ({ context, args, result }) => { - console.log('after', HookAfter, args); + after: ({ args, result }) => { + ExecuteHook(HookAfter, args[0].plugins, { ...payload, result }); } }); } diff --git a/src/plugins/models.ts b/src/plugins/models.ts index 67377c3..1f766f7 100644 --- a/src/plugins/models.ts +++ b/src/plugins/models.ts @@ -1,11 +1,28 @@ +export type PluginPayload = { + options: any; + key: string; + keepAliveTimeoutCache: any; + runningQueryCache: any; + timeframeCache: any; + fnArgs: any[]; + result?: any; +}; + export enum Hooks { - REFRESH_KEEP_ALIVE_PRE = 'REFRESH_KEEP_ALIVE_PRE', - REFRESH_KEEP_ALIVE_POST = 'REFRESH_KEEP_ALIVE_POST' + INIT = 'INIT', + EXECUTION_PRE = 'EXECUTION_PRE', + EXECUTION_POST = 'EXECUTION_POST', + DATACACHE_RECORD_ADD_PRE = 'DATACACHE_RECORD_ADD_PRE', + DATACACHE_RECORD_ADD_POST = 'DATACACHE_RECORD_ADD_POST', + //DATACACHE_RECORD_DELETE_PRE = 'DATACACHE_RECORD_DELETE_PRE', + //DATACACHE_RECORD_DELETE_POST = 'DATACACHE_RECORD_DELETE_POST', + CACHE_HIT = 'CACHE_HIT' + //CACHE_MISS = 'CACHE_MISS', } export type ActionableHook = { hook: Hooks; - action: (...args: any[]) => void; + action: (payload: PluginPayload) => void; }; export type CacheCandidatePlugin = { From 25e3dd9c551a6069cbe3c7784d9f828c6ea32e43 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Wed, 11 Jan 2023 14:30:49 +0100 Subject: [PATCH 03/12] chore: iterating developement. tests not working yet --- src/index.test.ts | 289 +++++++++++++------------ src/internal.ts | 48 ++-- src/models.ts | 4 +- src/plugins/models.ts | 13 +- src/plugins/poc/dependency-keys.ts | 52 +++++ src/{ => plugins/poc}/manager/index.ts | 2 +- src/test/options.ts | 2 +- 7 files changed, 231 insertions(+), 179 deletions(-) create mode 100644 src/plugins/poc/dependency-keys.ts rename src/{ => plugins/poc}/manager/index.ts (96%) diff --git a/src/index.test.ts b/src/index.test.ts index a5a7de8..51a21d0 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,9 +1,10 @@ import { cacheCandidate } from './lib'; -import { cacheCandidateDependencyManager } from './manager'; +import { cacheCandidateDependencyManager } from './plugins/poc/manager'; import { MockClass } from './test/MockClass'; import { MockClass as MockClass2 } from './test/MockClass2'; import { MockClass as MockClass3 } from './test/MockClass3'; import { MockClass as MockClass4 } from './test/MockClass4'; +import { PluginDependencyKeys } from './plugins/poc/dependency-keys'; import { step, @@ -22,159 +23,167 @@ beforeEach(async () => { flushMaps(); }); -it('should verify cache is empty', async () => { - expect(eventHits.get('onCacheSet')).toBe(0); - expect(eventHits.get('onCacheHit')).toBe(0); - expect(eventHits.get('onCacheDelete')).toBe(0); -}); +describe('CacheCandidate - Simple', () => { + it('should verify cache is empty', async () => { + expect(eventHits.get('onCacheSet')).toBe(0); + expect(eventHits.get('onCacheHit')).toBe(0); + expect(eventHits.get('onCacheDelete')).toBe(0); + }); -it('should separate the cache entries for MockClass and MockClass2 even if original names are equal', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - const mock2 = new MockClass2(step, step, step, step); - mock.mockFunction(step); - await sleep(TTL + EXECUTION_MARGIN); - mock2.mockFunction(step); - await sleep(TTL + EXECUTION_MARGIN); - expect(eventHits.get('onCacheSet')).toBe(2); - expect(eventHits.get('onCacheHit')).toBe(0); -}); + it('should separate the cache entries for MockClass and MockClass2 even if original names are equal', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + const mock2 = new MockClass2(step, step, step, step); + mock.mockFunction(step); + await sleep(TTL + EXECUTION_MARGIN); + mock2.mockFunction(step); + await sleep(TTL + EXECUTION_MARGIN); + expect(eventHits.get('onCacheSet')).toBe(2); + expect(eventHits.get('onCacheHit')).toBe(0); + }); -it('should call onCacheDelete for sync method', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - mock.mockFunction(step); - await sleep(TTL + EXECUTION_MARGIN); - expect(eventHits.get('onCacheDelete')).toBe(1); -}); + it('should call onCacheDelete for sync method', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + mock.mockFunction(step); + await sleep(TTL + EXECUTION_MARGIN); + expect(eventHits.get('onCacheDelete')).toBe(1); + }); -it('should call onCacheDelete for async method', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - await mock.mockAsyncFunction(step); - await sleep(TTL + EXECUTION_MARGIN); - expect(eventHits.get('onCacheDelete')).toBe(1); -}); + it('should call onCacheDelete for async method', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + await mock.mockAsyncFunction(step); + await sleep(TTL + EXECUTION_MARGIN); + expect(eventHits.get('onCacheDelete')).toBe(1); + }); -it('should call onCacheSet for sync method', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(eventHits.get('onCacheSet')).toBe(1); -}); + it('should call onCacheSet for sync method', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + expect(eventHits.get('onCacheSet')).toBe(1); + }); -it('should call onCacheHit for sync method', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(eventHits.get('onCacheHit')).toBe(1); -}); + it('should call onCacheHit for sync method', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + expect(eventHits.get('onCacheHit')).toBe(1); + }); -it('should call onCacheSet for async method', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - await mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - expect(eventHits.get('onCacheSet')).toBe(1); -}); + it('should call onCacheSet for async method', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + await mock.mockAsyncFunction(step); + await sleep(EXECUTION_MARGIN); + expect(eventHits.get('onCacheSet')).toBe(1); + }); -it('should call onCacheHit for async method', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - await mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - await mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - expect(eventHits.get('onCacheHit')).toBe(1); -}); + it('should call onCacheHit for async method', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + await mock.mockAsyncFunction(step); + await sleep(EXECUTION_MARGIN); + await mock.mockAsyncFunction(step); + await sleep(EXECUTION_MARGIN); + expect(eventHits.get('onCacheHit')).toBe(1); + }); -it('should make an item expire after TTL', async () => { - const step = stepper(); - const mock = new MockClass(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(eventHits.get('onCacheSet')).toBe(1); - expect(eventHits.get('onCacheHit')).toBe(1); - await sleep(TTL + EXECUTION_MARGIN); - expect(eventHits.get('onCacheDelete')).toBe(1); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(eventHits.get('onCacheSet')).toBe(2); - expect(eventHits.get('onCacheHit')).toBe(1); + it('should make an item expire after TTL', async () => { + const step = stepper(); + const mock = new MockClass(step, step, step, step); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + expect(eventHits.get('onCacheSet')).toBe(1); + expect(eventHits.get('onCacheHit')).toBe(1); + await sleep(TTL + EXECUTION_MARGIN); + expect(eventHits.get('onCacheDelete')).toBe(1); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + expect(eventHits.get('onCacheSet')).toBe(2); + expect(eventHits.get('onCacheHit')).toBe(1); + }); }); -it('should expose manager', async () => { - const step = stepper(); - new MockClass(step, step, step, step); - expect(cacheCandidateDependencyManager).toBeDefined(); -}); +describe('CacheCandidatePlugin - CacheCandidate', () => { + it('should expose manager', async () => { + const step = stepper(); + new MockClass(step, step, step, step); + expect(cacheCandidateDependencyManager).toBeDefined(); + }); -it('should fill manager map if dependencyKeys is defined as array', async () => { - const step = stepper(); - const mock = new MockClass3(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); - mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); -}); + it('should fill manager map if dependencyKeys is defined as array', async () => { + const step = stepper(); + const mock = new MockClass3(step, step, step, step); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(2); + mock.mockAsyncFunction(step); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(2); + }); -it('should fill manager map if dependencyKeys is defined as function', async () => { - const step = stepper(); - const mock = new MockClass4(step, step, step, step); - /*mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(manager.instances.size).toBe(3);*/ - await mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(3); -}); + it('should fill manager map if dependencyKeys is defined as function', async () => { + const step = stepper(); + const mock = new MockClass4(step, step, step, step); + await mock.mockAsyncFunction(step); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(3); + }); -it('should delete a record if invalidated', async () => { - const step = stepper(); - const mock = new MockClass3(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); - cacheCandidateDependencyManager.invalidate('a'); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); -}); + it('should delete a record if invalidated', async () => { + const step = stepper(); + const mock = new MockClass3(step, step, step, step); + mock.mockFunction(step); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(2); + cacheCandidateDependencyManager.invalidate('a'); + expect(cacheCandidateDependencyManager.instances.size).toBe(2); + }); -it('should behave in the same way as a decorator if the higher-order function is used', async () => { - let counter = 0; - const mockFn = (step: number) => - new Promise((resolve) => { - counter += step; - resolve(counter); + it('should behave in the same way as a decorator if the higher-order function is used', async () => { + let counter = 0; + const mockFn = (step: number) => + new Promise((resolve) => { + counter += step; + resolve(counter); + }); + const wrappedMockFn = cacheCandidate(mockFn, { + requestsThreshold: 1, + plugins: [ + { + ...PluginDependencyKeys, + ...{ + additionalParameters: { dependencyKeys: (result: number) => result } + } + } + ] }); - const wrappedMockFn = cacheCandidate(mockFn, { - requestsThreshold: 1, - dependencyKeys(result) { - return result; - } + let result: unknown; + result = await wrappedMockFn(1); + await sleep(EXECUTION_MARGIN); + expect(result).toBe(1); + result = await wrappedMockFn(1); + await sleep(EXECUTION_MARGIN); + expect(result).toBe(1); + result = await wrappedMockFn(1); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(1); + await sleep(EXECUTION_MARGIN); + expect(result).toBe(1); + cacheCandidateDependencyManager.invalidate(0); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(1); + cacheCandidateDependencyManager.invalidate(1); + await sleep(EXECUTION_MARGIN); + expect(cacheCandidateDependencyManager.instances.size).toBe(1); + await sleep(EXECUTION_MARGIN); }); - let result: unknown; - result = await wrappedMockFn(1); - await sleep(EXECUTION_MARGIN); - expect(result).toBe(1); - result = await wrappedMockFn(1); - await sleep(EXECUTION_MARGIN); - expect(result).toBe(1); - result = await wrappedMockFn(1); - await sleep(EXECUTION_MARGIN); - expect(result).toBe(1); - cacheCandidateDependencyManager.invalidate(0); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(1); - cacheCandidateDependencyManager.invalidate(1); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(1); - await sleep(EXECUTION_MARGIN); }); diff --git a/src/internal.ts b/src/internal.ts index bea5e8a..4544af2 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -1,6 +1,5 @@ import { createHash } from 'crypto'; import { CacheCandidateOptionsDefault } from './default'; -import { cacheCandidateDependencyManager } from './manager'; import { CacheCandidateOptions, DataCacheRecordNotFound, @@ -74,13 +73,15 @@ function isDataCacheRecordExpired({ async function getDataCacheRecord({ options, - key + key, + HookPayload }): Promise { if (await options.cache.has(key)) { const { result, birthTime } = await options.cache.get(key); // Remove the dataCache record if the time frame has passed. if (isDataCacheRecordExpired({ birthTime, options })) { - await deleteDataCacheRecord({ options, key }); + await deleteDataCacheRecord({ options, key, HookPayload }); + options.events.onCacheDelete({ key }); return DataCacheRecordNotFound; } else { // Return the cached data @@ -101,10 +102,12 @@ async function addDataCacheRecord({ options, key, result }) { ); } -async function deleteDataCacheRecord({ options, key }) { +async function deleteDataCacheRecord({ options, key, HookPayload }) { + ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_PRE, options.plugins, HookPayload); await options.cache.delete(key); - options.events.onCacheDelete({ key }); - cacheCandidateDependencyManager.deleteKey(key); + ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_POST, options.plugins, HookPayload); + /** @todo: check */ + //cacheCandidateDependencyManager.deleteKey(key); } function handleResult({ @@ -158,7 +161,8 @@ function handleResult({ HookPayload ); options.events.onCacheSet({ key }); - if (options.dependencyKeys !== undefined) { + /** @todo: check */ + /*if (options.dependencyKeys !== undefined) { let dependencyKeys: any = options.dependencyKeys; dependencyKeys = await remapDependencyKeys(dependencyKeys, result); cacheCandidateDependencyManager.register({ @@ -166,43 +170,21 @@ function handleResult({ dependencyKeys, cacheAdapter: options.cache }); - } + }*/ }) .finally(() => { runningQueryCache.delete(key); keepAliveTimeoutCache.set( key, setTimeout(() => { - deleteDataCacheRecord({ options, key }); + deleteDataCacheRecord({ options, key, HookPayload }); }, options.ttl) ); }); } } -async function remapDependencyKeys(dependencyKeys: any, result: unknown) { - if (typeof dependencyKeys === 'function') { - dependencyKeys = dependencyKeys(result); - if (dependencyKeys instanceof Promise) { - dependencyKeys = await dependencyKeys; - } - } - - if (Array.isArray(dependencyKeys)) { - dependencyKeys = dependencyKeys.map((key) => { - return typeof key === 'number' ? key.toString() : key; - }); - } - - if (typeof dependencyKeys === 'number') { - dependencyKeys = [dependencyKeys.toString()]; - } - - if (typeof dependencyKeys === 'string') { - dependencyKeys = [dependencyKeys]; - } - return dependencyKeys; -} +/** @todo: check */ function getExceedingAmount({ options, @@ -317,7 +299,7 @@ export async function letsCandidate({ }; ExecuteHook(Hooks.INIT, options.plugins, HookPayload); // Check if result exists in dataCache - const cachedData = await getDataCacheRecord({ options, key }); + const cachedData = await getDataCacheRecord({ options, key, HookPayload }); if (cachedData !== DataCacheRecordNotFound) { if (options.keepAlive) { refreshKeepAliveRecord({ diff --git a/src/models.ts b/src/models.ts index ec56307..ae60d0b 100644 --- a/src/models.ts +++ b/src/models.ts @@ -1,4 +1,4 @@ -import { CacheCandidatePlugin } from './plugins/models'; +import { CacheCandidatePluginWithAdditionalParameters } from './plugins/models'; export interface CandidateFunctionOptions { timeFrameCacheRecords: Array; @@ -34,7 +34,7 @@ export interface CacheCandidateOptions { }) => void; onLog: ({ key, event }: { key: string; event: Events }) => void; }; - plugins?: Array; + plugins?: Array; dependencyKeys?: | string | number diff --git a/src/plugins/models.ts b/src/plugins/models.ts index 1f766f7..3550330 100644 --- a/src/plugins/models.ts +++ b/src/plugins/models.ts @@ -14,8 +14,8 @@ export enum Hooks { EXECUTION_POST = 'EXECUTION_POST', DATACACHE_RECORD_ADD_PRE = 'DATACACHE_RECORD_ADD_PRE', DATACACHE_RECORD_ADD_POST = 'DATACACHE_RECORD_ADD_POST', - //DATACACHE_RECORD_DELETE_PRE = 'DATACACHE_RECORD_DELETE_PRE', - //DATACACHE_RECORD_DELETE_POST = 'DATACACHE_RECORD_DELETE_POST', + DATACACHE_RECORD_DELETE_PRE = 'DATACACHE_RECORD_DELETE_PRE', + DATACACHE_RECORD_DELETE_POST = 'DATACACHE_RECORD_DELETE_POST', CACHE_HIT = 'CACHE_HIT' //CACHE_MISS = 'CACHE_MISS', } @@ -29,3 +29,12 @@ export type CacheCandidatePlugin = { name: string; hooks: Array; }; + +export type CacheCandidatePluginAdditionalParameters = { + additionalParameters?: { + [key: string]: any; + }; +}; + +export type CacheCandidatePluginWithAdditionalParameters = + CacheCandidatePlugin & CacheCandidatePluginAdditionalParameters; diff --git a/src/plugins/poc/dependency-keys.ts b/src/plugins/poc/dependency-keys.ts new file mode 100644 index 0000000..a514ff2 --- /dev/null +++ b/src/plugins/poc/dependency-keys.ts @@ -0,0 +1,52 @@ +import { CacheCandidatePlugin, Hooks } from '../models'; +import { cacheCandidateDependencyManager } from './manager'; + +export const PluginDependencyKeys: CacheCandidatePlugin = { + name: 'dependencyKeys', + hooks: [ + { + hook: Hooks.DATACACHE_RECORD_DELETE_POST, + action: async ({ options, key, fnArgs }) => { + cacheCandidateDependencyManager.deleteKey(key); + } + }, + { + hook: Hooks.DATACACHE_RECORD_ADD_POST, + action: async ({ options, key, fnArgs, result }) => { + if (options.dependencyKeys !== undefined) { + let dependencyKeys: any = options.dependencyKeys; + dependencyKeys = await remapDependencyKeys(dependencyKeys, result); + cacheCandidateDependencyManager.register({ + key, + dependencyKeys, + cacheAdapter: options.cache + }); + } + } + } + ] +}; + +async function remapDependencyKeys(dependencyKeys: any, result: unknown) { + if (typeof dependencyKeys === 'function') { + dependencyKeys = dependencyKeys(result); + if (dependencyKeys instanceof Promise) { + dependencyKeys = await dependencyKeys; + } + } + + if (Array.isArray(dependencyKeys)) { + dependencyKeys = dependencyKeys.map((key) => { + return typeof key === 'number' ? key.toString() : key; + }); + } + + if (typeof dependencyKeys === 'number') { + dependencyKeys = [dependencyKeys.toString()]; + } + + if (typeof dependencyKeys === 'string') { + dependencyKeys = [dependencyKeys]; + } + return dependencyKeys; +} diff --git a/src/manager/index.ts b/src/plugins/poc/manager/index.ts similarity index 96% rename from src/manager/index.ts rename to src/plugins/poc/manager/index.ts index 709c52c..766d5f3 100644 --- a/src/manager/index.ts +++ b/src/plugins/poc/manager/index.ts @@ -1,4 +1,4 @@ -import { CacheCandidateCacheAdapter } from '../models'; +import { CacheCandidateCacheAdapter } from '../../../models'; const makeDependencyManager = () => { const instances = new Map(); diff --git a/src/test/options.ts b/src/test/options.ts index 0a5c27d..c9da8d2 100644 --- a/src/test/options.ts +++ b/src/test/options.ts @@ -1,4 +1,4 @@ -import { cacheCandidateDependencyManager } from '../manager'; +import { cacheCandidateDependencyManager } from '../plugins/poc/manager'; import { CacheCandidateOptions } from '../models'; export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); From b9c4804a91b7336f6f1556b2189a34f0eed36c34 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Wed, 11 Jan 2023 14:39:31 +0100 Subject: [PATCH 04/12] chore: fixed simple tests. plugins yet to test --- src/internal.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/internal.ts b/src/internal.ts index 4544af2..4123bfc 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -81,7 +81,6 @@ async function getDataCacheRecord({ // Remove the dataCache record if the time frame has passed. if (isDataCacheRecordExpired({ birthTime, options })) { await deleteDataCacheRecord({ options, key, HookPayload }); - options.events.onCacheDelete({ key }); return DataCacheRecordNotFound; } else { // Return the cached data @@ -106,6 +105,7 @@ async function deleteDataCacheRecord({ options, key, HookPayload }) { ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_PRE, options.plugins, HookPayload); await options.cache.delete(key); ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_POST, options.plugins, HookPayload); + options.events.onCacheDelete({ key }); /** @todo: check */ //cacheCandidateDependencyManager.deleteKey(key); } @@ -184,8 +184,6 @@ function handleResult({ } } -/** @todo: check */ - function getExceedingAmount({ options, key, From 6f7e864c1410c8c4b68b8602c41302ecef9a5641 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Wed, 11 Jan 2023 15:14:00 +0100 Subject: [PATCH 05/12] chore: feature completed. linting and separation of concerns to do. --- src/index.test.ts | 13 +++---------- src/internal.ts | 11 +++++------ src/plugins/index.ts | 10 +++++++--- src/plugins/models.ts | 2 +- src/plugins/poc/dependency-keys.ts | 23 ++++++++++++----------- src/test/MockClass3.ts | 6 +++--- src/test/MockClass4.ts | 20 +++++++++----------- src/test/options.ts | 16 ++++++++++++++++ 8 files changed, 56 insertions(+), 45 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 51a21d0..9233c96 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -4,7 +4,6 @@ import { MockClass } from './test/MockClass'; import { MockClass as MockClass2 } from './test/MockClass2'; import { MockClass as MockClass3 } from './test/MockClass3'; import { MockClass as MockClass4 } from './test/MockClass4'; -import { PluginDependencyKeys } from './plugins/poc/dependency-keys'; import { step, @@ -13,7 +12,8 @@ import { ENOUGH_TIME, TTL, EXECUTION_MARGIN, - flushMaps + flushMaps, + pluginsOptions } from './test/options'; const stepper = step(); @@ -157,14 +157,7 @@ describe('CacheCandidatePlugin - CacheCandidate', () => { }); const wrappedMockFn = cacheCandidate(mockFn, { requestsThreshold: 1, - plugins: [ - { - ...PluginDependencyKeys, - ...{ - additionalParameters: { dependencyKeys: (result: number) => result } - } - } - ] + ...pluginsOptions() }); let result: unknown; result = await wrappedMockFn(1); diff --git a/src/internal.ts b/src/internal.ts index 4123bfc..8772ed7 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -155,11 +155,10 @@ function handleResult({ ExecuteHook(Hooks.DATACACHE_RECORD_ADD_PRE, options.plugins, HookPayload); addDataCacheRecord({ options, key, result }) .then(async () => { - ExecuteHook( - Hooks.DATACACHE_RECORD_ADD_POST, - options.plugins, - HookPayload - ); + ExecuteHook(Hooks.DATACACHE_RECORD_ADD_POST, options.plugins, { + ...HookPayload, + result + }); options.events.onCacheSet({ key }); /** @todo: check */ /*if (options.dependencyKeys !== undefined) { @@ -288,7 +287,7 @@ export async function letsCandidate({ originalMethod: (...args: any[]) => Promise; }) { const HookPayload = { - options, + options: { ...options, plugins: undefined }, key, keepAliveTimeoutCache, runningQueryCache, diff --git a/src/plugins/index.ts b/src/plugins/index.ts index be0e6ae..d28ccbf 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,15 +1,19 @@ import { hook } from 'hook-fn'; -import { CacheCandidatePlugin, Hooks, PluginPayload } from './models'; +import { + CacheCandidatePluginWithAdditionalParameters, + Hooks, + PluginPayload +} from './models'; export function ExecuteHook( hook: Hooks, - plugins: Array = [], + plugins: Array = [], payload: PluginPayload ) { for (const plugin of plugins) { const actionableHook = plugin.hooks.find((h) => h.hook === hook); if (actionableHook) { - actionableHook.action(payload); + actionableHook.action(payload, plugin.additionalParameters); } } } diff --git a/src/plugins/models.ts b/src/plugins/models.ts index 3550330..ef6f430 100644 --- a/src/plugins/models.ts +++ b/src/plugins/models.ts @@ -22,7 +22,7 @@ export enum Hooks { export type ActionableHook = { hook: Hooks; - action: (payload: PluginPayload) => void; + action: (payload: PluginPayload, additionalParameters: any) => void; }; export type CacheCandidatePlugin = { diff --git a/src/plugins/poc/dependency-keys.ts b/src/plugins/poc/dependency-keys.ts index a514ff2..d9fbe1d 100644 --- a/src/plugins/poc/dependency-keys.ts +++ b/src/plugins/poc/dependency-keys.ts @@ -6,22 +6,23 @@ export const PluginDependencyKeys: CacheCandidatePlugin = { hooks: [ { hook: Hooks.DATACACHE_RECORD_DELETE_POST, - action: async ({ options, key, fnArgs }) => { + action: async ({ options, key, fnArgs }, additionalParameters) => { cacheCandidateDependencyManager.deleteKey(key); } }, { hook: Hooks.DATACACHE_RECORD_ADD_POST, - action: async ({ options, key, fnArgs, result }) => { - if (options.dependencyKeys !== undefined) { - let dependencyKeys: any = options.dependencyKeys; - dependencyKeys = await remapDependencyKeys(dependencyKeys, result); - cacheCandidateDependencyManager.register({ - key, - dependencyKeys, - cacheAdapter: options.cache - }); - } + action: async ( + { options, key, fnArgs, result }, + additionalParameters + ) => { + let dependencyKeys: any = additionalParameters.dependencyKeys; + dependencyKeys = await remapDependencyKeys(dependencyKeys, result); + cacheCandidateDependencyManager.register({ + key, + dependencyKeys, + cacheAdapter: options.cache + }); } } ] diff --git a/src/test/MockClass3.ts b/src/test/MockClass3.ts index 8c635a7..e2b4159 100644 --- a/src/test/MockClass3.ts +++ b/src/test/MockClass3.ts @@ -1,5 +1,5 @@ import { CacheCandidate } from '../lib'; -import { options } from './options'; +import { options, pluginsOptions } from './options'; export class MockClass { constructor( @@ -9,12 +9,12 @@ export class MockClass { public bAsync: number ) {} - @CacheCandidate({ ...options, ...{ dependencyKeys: ['a', 'b'] } }) + @CacheCandidate({ ...options, ...pluginsOptions(['a', 'b']) }) async mockAsyncFunction(step: number) { return step; } - @CacheCandidate({ ...options, ...{ dependencyKeys: ['a', 'b'] } }) + @CacheCandidate({ ...options, ...pluginsOptions(['a', 'b']) }) mockFunction(step: number) { return step; } diff --git a/src/test/MockClass4.ts b/src/test/MockClass4.ts index adcb1f8..e8c5022 100644 --- a/src/test/MockClass4.ts +++ b/src/test/MockClass4.ts @@ -1,5 +1,5 @@ import { CacheCandidate } from '../lib'; -import { options } from './options'; +import { options, pluginsOptions } from './options'; export class MockClass { constructor( @@ -11,15 +11,13 @@ export class MockClass { @CacheCandidate({ ...options, - ...{ - dependencyKeys: function (result) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(result); - }, 10); - }); - } - } + ...pluginsOptions(function (result) { + return new Promise((resolve) => { + setTimeout(() => { + resolve(result); + }, 10); + }); + }) }) async mockAsyncFunction(step: number) { return new Promise((resolve) => { @@ -29,7 +27,7 @@ export class MockClass { }); } - @CacheCandidate({ ...options, ...{ dependencyKeys: (result) => result } }) + @CacheCandidate({ ...options, ...pluginsOptions((result) => result) }) mockFunction(step: number) { return [step, step + 1, step + 2]; } diff --git a/src/test/options.ts b/src/test/options.ts index c9da8d2..2716367 100644 --- a/src/test/options.ts +++ b/src/test/options.ts @@ -1,5 +1,6 @@ import { cacheCandidateDependencyManager } from '../plugins/poc/manager'; import { CacheCandidateOptions } from '../models'; +import { PluginDependencyKeys } from '../plugins/poc/dependency-keys'; export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const TTL = 100; @@ -65,3 +66,18 @@ export function flushMaps() { } cacheCandidateDependencyManager.instances.clear(); } + +export function pluginsOptions( + dependencyKeys: any = (result: number) => result +) { + return { + plugins: [ + { + ...PluginDependencyKeys, + ...{ + additionalParameters: { dependencyKeys } + } + } + ] + }; +} From 4337ca0b879dc5417472d2307dc90c912a3f9b66 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Thu, 12 Jan 2023 09:58:41 +0100 Subject: [PATCH 06/12] chore: linting and fixing open handle --- src/index.test.ts | 1 + src/index.ts | 3 ++- src/plugins/poc/dependency-keys.ts | 7 ++----- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/index.test.ts b/src/index.test.ts index 9233c96..f97cc3d 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -157,6 +157,7 @@ describe('CacheCandidatePlugin - CacheCandidate', () => { }); const wrappedMockFn = cacheCandidate(mockFn, { requestsThreshold: 1, + ttl: 800, ...pluginsOptions() }); let result: unknown; diff --git a/src/index.ts b/src/index.ts index fb3c0ed..7794bae 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ export * from './default'; export * from './models'; export * from './lib'; -export * from './manager'; +export * from './plugins/poc/dependency-keys'; +export * from './plugins/poc/manager'; diff --git a/src/plugins/poc/dependency-keys.ts b/src/plugins/poc/dependency-keys.ts index d9fbe1d..5bd0303 100644 --- a/src/plugins/poc/dependency-keys.ts +++ b/src/plugins/poc/dependency-keys.ts @@ -6,16 +6,13 @@ export const PluginDependencyKeys: CacheCandidatePlugin = { hooks: [ { hook: Hooks.DATACACHE_RECORD_DELETE_POST, - action: async ({ options, key, fnArgs }, additionalParameters) => { + action: async ({ key }) => { cacheCandidateDependencyManager.deleteKey(key); } }, { hook: Hooks.DATACACHE_RECORD_ADD_POST, - action: async ( - { options, key, fnArgs, result }, - additionalParameters - ) => { + action: async ({ options, key, result }, additionalParameters) => { let dependencyKeys: any = additionalParameters.dependencyKeys; dependencyKeys = await remapDependencyKeys(dependencyKeys, result); cacheCandidateDependencyManager.register({ From ce3ccce326eeae13b0c3a1b654610fd951e7f0c4 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Thu, 12 Jan 2023 17:40:33 +0100 Subject: [PATCH 07/12] chore: removed comments --- src/internal.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/internal.ts b/src/internal.ts index 8772ed7..c223cf3 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -106,8 +106,6 @@ async function deleteDataCacheRecord({ options, key, HookPayload }) { await options.cache.delete(key); ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_POST, options.plugins, HookPayload); options.events.onCacheDelete({ key }); - /** @todo: check */ - //cacheCandidateDependencyManager.deleteKey(key); } function handleResult({ @@ -160,16 +158,6 @@ function handleResult({ result }); options.events.onCacheSet({ key }); - /** @todo: check */ - /*if (options.dependencyKeys !== undefined) { - let dependencyKeys: any = options.dependencyKeys; - dependencyKeys = await remapDependencyKeys(dependencyKeys, result); - cacheCandidateDependencyManager.register({ - key, - dependencyKeys, - cacheAdapter: options.cache - }); - }*/ }) .finally(() => { runningQueryCache.delete(key); From 843cf2a76d153f3d7b530a29d2dea7d50879615b Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Mon, 16 Jan 2023 15:25:52 +0100 Subject: [PATCH 08/12] chore: removed useless dependencyKeys param Co-authored-by: Carmelo Badalamenti --- src/models.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/models.ts b/src/models.ts index ae60d0b..72df077 100644 --- a/src/models.ts +++ b/src/models.ts @@ -35,17 +35,6 @@ export interface CacheCandidateOptions { onLog: ({ key, event }: { key: string; event: Events }) => void; }; plugins?: Array; - dependencyKeys?: - | string - | number - | Array - | (( - result: DataCacheRecord['result'] - ) => - | string - | number - | Array - | Promise | Array>); } export interface DataCacheRecord { From 997d3fc1fe5f240e589be66a23c113f608e02627 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Mon, 16 Jan 2023 15:26:19 +0100 Subject: [PATCH 09/12] chore: internal refactor, async execute hooks, better types --- src/internal.ts | 34 ++++++++++++++++++++---------- src/plugins/index.ts | 28 ++++++++++++++++++++---- src/plugins/models.ts | 2 +- src/plugins/poc/dependency-keys.ts | 1 + 4 files changed, 49 insertions(+), 16 deletions(-) diff --git a/src/internal.ts b/src/internal.ts index c223cf3..cf97ef4 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -102,13 +102,21 @@ async function addDataCacheRecord({ options, key, result }) { } async function deleteDataCacheRecord({ options, key, HookPayload }) { - ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_PRE, options.plugins, HookPayload); + await ExecuteHook( + Hooks.DATACACHE_RECORD_DELETE_PRE, + options.plugins, + HookPayload + ); await options.cache.delete(key); - ExecuteHook(Hooks.DATACACHE_RECORD_DELETE_POST, options.plugins, HookPayload); + await ExecuteHook( + Hooks.DATACACHE_RECORD_DELETE_POST, + options.plugins, + HookPayload + ); options.events.onCacheDelete({ key }); } -function handleResult({ +async function handleResult({ result, runningQueryCache, key, @@ -128,7 +136,7 @@ function handleResult({ keepAliveTimeoutCache: KeepAliveCache; args: any[]; HookPayload: PluginPayload; -}): any { +}): Promise { const executionEnd = Date.now(); const executionTime = executionEnd - executionStart; options.events.onAfterFunctionExecution({ key, executionTime }); @@ -150,10 +158,14 @@ function handleResult({ }); if (exceedingAmount >= options.requestsThreshold) { - ExecuteHook(Hooks.DATACACHE_RECORD_ADD_PRE, options.plugins, HookPayload); + await ExecuteHook( + Hooks.DATACACHE_RECORD_ADD_PRE, + options.plugins, + HookPayload + ); addDataCacheRecord({ options, key, result }) .then(async () => { - ExecuteHook(Hooks.DATACACHE_RECORD_ADD_POST, options.plugins, { + await ExecuteHook(Hooks.DATACACHE_RECORD_ADD_POST, options.plugins, { ...HookPayload, result }); @@ -282,7 +294,7 @@ export async function letsCandidate({ timeframeCache, fnArgs: args }; - ExecuteHook(Hooks.INIT, options.plugins, HookPayload); + await ExecuteHook(Hooks.INIT, options.plugins, HookPayload); // Check if result exists in dataCache const cachedData = await getDataCacheRecord({ options, key, HookPayload }); if (cachedData !== DataCacheRecordNotFound) { @@ -294,7 +306,7 @@ export async function letsCandidate({ }); } - ExecuteHook(Hooks.CACHE_HIT, options.plugins, { + await ExecuteHook(Hooks.CACHE_HIT, options.plugins, { ...HookPayload, result: cachedData }); @@ -310,7 +322,7 @@ export async function letsCandidate({ }); if (runningQuery !== RunningQueryRecordNotFound) { - ExecuteHook(Hooks.CACHE_HIT, options.plugins, { + await ExecuteHook(Hooks.CACHE_HIT, options.plugins, { ...HookPayload, result: runningQuery }); @@ -322,11 +334,11 @@ export async function letsCandidate({ expireTimeFrameCacheRecords({ options, key, timeframeCache }); // Execute the function - ExecuteHook(Hooks.EXECUTION_PRE, options.plugins, HookPayload); + await ExecuteHook(Hooks.EXECUTION_PRE, options.plugins, HookPayload); options.events.onBeforeFunctionExecution({ key }); const executionStart = Date.now(); const execution = originalMethod(...args); - ExecuteHook(Hooks.EXECUTION_POST, options.plugins, { + await ExecuteHook(Hooks.EXECUTION_POST, options.plugins, { ...HookPayload, result: execution }); diff --git a/src/plugins/index.ts b/src/plugins/index.ts index d28ccbf..6d987d8 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,23 +1,43 @@ import { hook } from 'hook-fn'; import { + ActionableHook, CacheCandidatePluginWithAdditionalParameters, Hooks, PluginPayload } from './models'; -export function ExecuteHook( +export async function ExecuteHook( hook: Hooks, plugins: Array = [], payload: PluginPayload ) { for (const plugin of plugins) { - const actionableHook = plugin.hooks.find((h) => h.hook === hook); - if (actionableHook) { - actionableHook.action(payload, plugin.additionalParameters); + const instanceHooks = plugin.hooks.filter((h) => h.hook === hook); + if (instanceHooks.length > 1) { + throw new Error( + `Only one hook instance per plugin is allowed. ${plugin.name} has ${instanceHooks.length} instances of ${hook}}` + ); + } + if (instanceHooks.length === 0) { + continue; + } + const instanceHook = instanceHooks[0]; + if (isActionable(instanceHook)) { + await instanceHook.action(payload, plugin.additionalParameters); + } else { + throw new Error( + `Hook ${hook} for plugin ${plugin.name} is not actionable.` + ); } } } +function isActionable(hook: ActionableHook) { + return [hook.action !== undefined, typeof hook.action === 'function'].every( + (i) => i === true + ); +} + export function pluginHookWrap( HookBefore: Hooks, HookAfter: Hooks, diff --git a/src/plugins/models.ts b/src/plugins/models.ts index ef6f430..58f9972 100644 --- a/src/plugins/models.ts +++ b/src/plugins/models.ts @@ -22,7 +22,7 @@ export enum Hooks { export type ActionableHook = { hook: Hooks; - action: (payload: PluginPayload, additionalParameters: any) => void; + action: (payload: PluginPayload, additionalParameters: any) => Promise; }; export type CacheCandidatePlugin = { diff --git a/src/plugins/poc/dependency-keys.ts b/src/plugins/poc/dependency-keys.ts index 5bd0303..a1a17de 100644 --- a/src/plugins/poc/dependency-keys.ts +++ b/src/plugins/poc/dependency-keys.ts @@ -13,6 +13,7 @@ export const PluginDependencyKeys: CacheCandidatePlugin = { { hook: Hooks.DATACACHE_RECORD_ADD_POST, action: async ({ options, key, result }, additionalParameters) => { + if (!additionalParameters.dependencyKeys) return; let dependencyKeys: any = additionalParameters.dependencyKeys; dependencyKeys = await remapDependencyKeys(dependencyKeys, result); cacheCandidateDependencyManager.register({ From 7f2604d4143f635a0a5ef5757b2db0b1fefdb80d Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Mon, 16 Jan 2023 16:25:53 +0100 Subject: [PATCH 10/12] chore: separated different packages to allow plugin mechanism --- package-lock.json | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/package-lock.json b/package-lock.json index 7f90c5c..43b50e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.2.0", "license": "MIT", "dependencies": { + "@jointly/cache-candidate-plugin-base": "^1.0.0", "hook-fn": "^1.0.3" }, "devDependencies": { @@ -1934,6 +1935,11 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jointly/cache-candidate-plugin-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jointly/cache-candidate-plugin-base/-/cache-candidate-plugin-base-1.0.0.tgz", + "integrity": "sha512-1pJWcQVCEqbtjogcPv7yu4z+dhSomBSZbIThESi8PWAXS6OV0ro/iZxJreSp+uW1JMokeU7KaWFAMljR/YOS0w==" + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", @@ -8768,6 +8774,11 @@ "chalk": "^4.0.0" } }, + "@jointly/cache-candidate-plugin-base": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@jointly/cache-candidate-plugin-base/-/cache-candidate-plugin-base-1.0.0.tgz", + "integrity": "sha512-1pJWcQVCEqbtjogcPv7yu4z+dhSomBSZbIThESi8PWAXS6OV0ro/iZxJreSp+uW1JMokeU7KaWFAMljR/YOS0w==" + }, "@jridgewell/gen-mapping": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", From 807c9432fff08429636db736945c8ccf4d222ae5 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Mon, 16 Jan 2023 16:26:04 +0100 Subject: [PATCH 11/12] chore: separated different packages to allow plugin mechanism p2 --- package.json | 1 + src/plugins/index.ts | 4 +- src/plugins/models.ts | 40 ---------------- src/plugins/poc/dependency-keys.ts | 51 --------------------- src/plugins/poc/manager/index.ts | 73 ------------------------------ 5 files changed, 3 insertions(+), 166 deletions(-) delete mode 100644 src/plugins/models.ts delete mode 100644 src/plugins/poc/dependency-keys.ts delete mode 100644 src/plugins/poc/manager/index.ts diff --git a/package.json b/package.json index 1e2aab3..bf2bd35 100644 --- a/package.json +++ b/package.json @@ -63,6 +63,7 @@ "license": "MIT", "author": "Jointly ", "dependencies": { + "@jointly/cache-candidate-plugin-base": "^1.0.0", "hook-fn": "^1.0.3" } } diff --git a/src/plugins/index.ts b/src/plugins/index.ts index 6d987d8..2c24227 100644 --- a/src/plugins/index.ts +++ b/src/plugins/index.ts @@ -1,10 +1,10 @@ -import { hook } from 'hook-fn'; import { ActionableHook, CacheCandidatePluginWithAdditionalParameters, Hooks, PluginPayload -} from './models'; +} from '@jointly/cache-candidate-plugin-base'; +import { hook } from 'hook-fn'; export async function ExecuteHook( hook: Hooks, diff --git a/src/plugins/models.ts b/src/plugins/models.ts deleted file mode 100644 index 58f9972..0000000 --- a/src/plugins/models.ts +++ /dev/null @@ -1,40 +0,0 @@ -export type PluginPayload = { - options: any; - key: string; - keepAliveTimeoutCache: any; - runningQueryCache: any; - timeframeCache: any; - fnArgs: any[]; - result?: any; -}; - -export enum Hooks { - INIT = 'INIT', - EXECUTION_PRE = 'EXECUTION_PRE', - EXECUTION_POST = 'EXECUTION_POST', - DATACACHE_RECORD_ADD_PRE = 'DATACACHE_RECORD_ADD_PRE', - DATACACHE_RECORD_ADD_POST = 'DATACACHE_RECORD_ADD_POST', - DATACACHE_RECORD_DELETE_PRE = 'DATACACHE_RECORD_DELETE_PRE', - DATACACHE_RECORD_DELETE_POST = 'DATACACHE_RECORD_DELETE_POST', - CACHE_HIT = 'CACHE_HIT' - //CACHE_MISS = 'CACHE_MISS', -} - -export type ActionableHook = { - hook: Hooks; - action: (payload: PluginPayload, additionalParameters: any) => Promise; -}; - -export type CacheCandidatePlugin = { - name: string; - hooks: Array; -}; - -export type CacheCandidatePluginAdditionalParameters = { - additionalParameters?: { - [key: string]: any; - }; -}; - -export type CacheCandidatePluginWithAdditionalParameters = - CacheCandidatePlugin & CacheCandidatePluginAdditionalParameters; diff --git a/src/plugins/poc/dependency-keys.ts b/src/plugins/poc/dependency-keys.ts deleted file mode 100644 index a1a17de..0000000 --- a/src/plugins/poc/dependency-keys.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { CacheCandidatePlugin, Hooks } from '../models'; -import { cacheCandidateDependencyManager } from './manager'; - -export const PluginDependencyKeys: CacheCandidatePlugin = { - name: 'dependencyKeys', - hooks: [ - { - hook: Hooks.DATACACHE_RECORD_DELETE_POST, - action: async ({ key }) => { - cacheCandidateDependencyManager.deleteKey(key); - } - }, - { - hook: Hooks.DATACACHE_RECORD_ADD_POST, - action: async ({ options, key, result }, additionalParameters) => { - if (!additionalParameters.dependencyKeys) return; - let dependencyKeys: any = additionalParameters.dependencyKeys; - dependencyKeys = await remapDependencyKeys(dependencyKeys, result); - cacheCandidateDependencyManager.register({ - key, - dependencyKeys, - cacheAdapter: options.cache - }); - } - } - ] -}; - -async function remapDependencyKeys(dependencyKeys: any, result: unknown) { - if (typeof dependencyKeys === 'function') { - dependencyKeys = dependencyKeys(result); - if (dependencyKeys instanceof Promise) { - dependencyKeys = await dependencyKeys; - } - } - - if (Array.isArray(dependencyKeys)) { - dependencyKeys = dependencyKeys.map((key) => { - return typeof key === 'number' ? key.toString() : key; - }); - } - - if (typeof dependencyKeys === 'number') { - dependencyKeys = [dependencyKeys.toString()]; - } - - if (typeof dependencyKeys === 'string') { - dependencyKeys = [dependencyKeys]; - } - return dependencyKeys; -} diff --git a/src/plugins/poc/manager/index.ts b/src/plugins/poc/manager/index.ts deleted file mode 100644 index 766d5f3..0000000 --- a/src/plugins/poc/manager/index.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { CacheCandidateCacheAdapter } from '../../../models'; - -const makeDependencyManager = () => { - const instances = new Map(); - return { - register: ({ - key, - dependencyKeys, - cacheAdapter - }: { - key: unknown; - dependencyKeys: Array; - cacheAdapter: CacheCandidateCacheAdapter; - }) => { - for (const dependencyKey of dependencyKeys) { - if (!instances.has(dependencyKey)) { - instances.set(dependencyKey, [ - { - key, - cacheAdapter - } - ]); - } else { - instances.get(dependencyKey).push({ - key, - cacheAdapter - }); - } - } - }, - invalidate: (dependencyKey: string | number) => { - if (typeof dependencyKey === 'number') { - dependencyKey = dependencyKey.toString(); - } - - if (!instances.has(dependencyKey)) { - return; - } - - instances.get(dependencyKey).forEach(({ key, cacheAdapter }) => { - cacheAdapter.delete(key); - }); - }, - deleteKey: (dataCacheRecordKey: string) => { - for (const [dependencyKey, keys] of instances.entries()) { - if (keys.some((_key) => _key.key === dataCacheRecordKey)) { - if (keys.length === 1) { - instances.delete(dependencyKey); - } else { - instances.set( - dependencyKey, - keys.filter((_key) => _key.key !== dataCacheRecordKey) - ); - } - } - } - }, - instances - }; -}; - -export const cacheCandidateDependencyManager = makeDependencyManager(); - -/* -[abc][1,2,3] -[def][3,4,5] - -[1] => [abc] -[2] => [abc] -[3] => [abc, def] -[4] => [def] -[5] => [def] -*/ From 92661cea8a26d3eede5a48025b62216f7854f112 Mon Sep 17 00:00:00 2001 From: Michael Di Prisco Date: Mon, 16 Jan 2023 16:34:02 +0100 Subject: [PATCH 12/12] chore: removed additional dependency key references --- src/index.test.ts | 58 ++---------------------------------------- src/internal.ts | 2 +- src/models.ts | 2 +- src/test/MockClass3.ts | 21 --------------- src/test/MockClass4.ts | 34 ------------------------- src/test/options.ts | 18 ------------- 6 files changed, 4 insertions(+), 131 deletions(-) delete mode 100644 src/test/MockClass3.ts delete mode 100644 src/test/MockClass4.ts diff --git a/src/index.test.ts b/src/index.test.ts index f97cc3d..ed35b48 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,9 +1,6 @@ import { cacheCandidate } from './lib'; -import { cacheCandidateDependencyManager } from './plugins/poc/manager'; import { MockClass } from './test/MockClass'; import { MockClass as MockClass2 } from './test/MockClass2'; -import { MockClass as MockClass3 } from './test/MockClass3'; -import { MockClass as MockClass4 } from './test/MockClass4'; import { step, @@ -12,8 +9,7 @@ import { ENOUGH_TIME, TTL, EXECUTION_MARGIN, - flushMaps, - pluginsOptions + flushMaps } from './test/options'; const stepper = step(); @@ -110,43 +106,6 @@ describe('CacheCandidate - Simple', () => { expect(eventHits.get('onCacheSet')).toBe(2); expect(eventHits.get('onCacheHit')).toBe(1); }); -}); - -describe('CacheCandidatePlugin - CacheCandidate', () => { - it('should expose manager', async () => { - const step = stepper(); - new MockClass(step, step, step, step); - expect(cacheCandidateDependencyManager).toBeDefined(); - }); - - it('should fill manager map if dependencyKeys is defined as array', async () => { - const step = stepper(); - const mock = new MockClass3(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); - mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); - }); - - it('should fill manager map if dependencyKeys is defined as function', async () => { - const step = stepper(); - const mock = new MockClass4(step, step, step, step); - await mock.mockAsyncFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(3); - }); - - it('should delete a record if invalidated', async () => { - const step = stepper(); - const mock = new MockClass3(step, step, step, step); - mock.mockFunction(step); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); - cacheCandidateDependencyManager.invalidate('a'); - expect(cacheCandidateDependencyManager.instances.size).toBe(2); - }); it('should behave in the same way as a decorator if the higher-order function is used', async () => { let counter = 0; @@ -157,8 +116,7 @@ describe('CacheCandidatePlugin - CacheCandidate', () => { }); const wrappedMockFn = cacheCandidate(mockFn, { requestsThreshold: 1, - ttl: 800, - ...pluginsOptions() + ttl: 800 }); let result: unknown; result = await wrappedMockFn(1); @@ -167,17 +125,5 @@ describe('CacheCandidatePlugin - CacheCandidate', () => { result = await wrappedMockFn(1); await sleep(EXECUTION_MARGIN); expect(result).toBe(1); - result = await wrappedMockFn(1); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(1); - await sleep(EXECUTION_MARGIN); - expect(result).toBe(1); - cacheCandidateDependencyManager.invalidate(0); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(1); - cacheCandidateDependencyManager.invalidate(1); - await sleep(EXECUTION_MARGIN); - expect(cacheCandidateDependencyManager.instances.size).toBe(1); - await sleep(EXECUTION_MARGIN); }); }); diff --git a/src/internal.ts b/src/internal.ts index cf97ef4..4de8f0c 100644 --- a/src/internal.ts +++ b/src/internal.ts @@ -10,7 +10,7 @@ import { TimeFrameCache } from './models'; import { ExecuteHook } from './plugins'; -import { Hooks, PluginPayload } from './plugins/models'; +import { Hooks, PluginPayload } from '@jointly/cache-candidate-plugin-base'; function isTimeFrameCacheRecordExpired(executionEnd: any, options: any) { return Date.now() < executionEnd + options.timeFrame; diff --git a/src/models.ts b/src/models.ts index 72df077..a39c01a 100644 --- a/src/models.ts +++ b/src/models.ts @@ -1,4 +1,4 @@ -import { CacheCandidatePluginWithAdditionalParameters } from './plugins/models'; +import { CacheCandidatePluginWithAdditionalParameters } from '@jointly/cache-candidate-plugin-base'; export interface CandidateFunctionOptions { timeFrameCacheRecords: Array; diff --git a/src/test/MockClass3.ts b/src/test/MockClass3.ts deleted file mode 100644 index e2b4159..0000000 --- a/src/test/MockClass3.ts +++ /dev/null @@ -1,21 +0,0 @@ -import { CacheCandidate } from '../lib'; -import { options, pluginsOptions } from './options'; - -export class MockClass { - constructor( - public a: number, - public b: number, - public aAsync: number, - public bAsync: number - ) {} - - @CacheCandidate({ ...options, ...pluginsOptions(['a', 'b']) }) - async mockAsyncFunction(step: number) { - return step; - } - - @CacheCandidate({ ...options, ...pluginsOptions(['a', 'b']) }) - mockFunction(step: number) { - return step; - } -} diff --git a/src/test/MockClass4.ts b/src/test/MockClass4.ts deleted file mode 100644 index e8c5022..0000000 --- a/src/test/MockClass4.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { CacheCandidate } from '../lib'; -import { options, pluginsOptions } from './options'; - -export class MockClass { - constructor( - public a: number, - public b: number, - public aAsync: number, - public bAsync: number - ) {} - - @CacheCandidate({ - ...options, - ...pluginsOptions(function (result) { - return new Promise((resolve) => { - setTimeout(() => { - resolve(result); - }, 10); - }); - }) - }) - async mockAsyncFunction(step: number) { - return new Promise((resolve) => { - setTimeout(() => { - resolve([step, step + 1, step + 2]); - }, 10); - }); - } - - @CacheCandidate({ ...options, ...pluginsOptions((result) => result) }) - mockFunction(step: number) { - return [step, step + 1, step + 2]; - } -} diff --git a/src/test/options.ts b/src/test/options.ts index 2716367..27c262f 100644 --- a/src/test/options.ts +++ b/src/test/options.ts @@ -1,6 +1,4 @@ -import { cacheCandidateDependencyManager } from '../plugins/poc/manager'; import { CacheCandidateOptions } from '../models'; -import { PluginDependencyKeys } from '../plugins/poc/dependency-keys'; export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); export const TTL = 100; @@ -64,20 +62,4 @@ export function flushMaps() { for (const [key] of eventHits) { eventHits.set(key, 0); } - cacheCandidateDependencyManager.instances.clear(); -} - -export function pluginsOptions( - dependencyKeys: any = (result: number) => result -) { - return { - plugins: [ - { - ...PluginDependencyKeys, - ...{ - additionalParameters: { dependencyKeys } - } - } - ] - }; }