From 236a74fd581515b45fd3472f76d9faa39c9a194e Mon Sep 17 00:00:00 2001 From: Himmet Avsar Date: Wed, 23 Oct 2019 15:38:04 +0200 Subject: [PATCH] structure update, helper npm script --- index.d.ts | 2 +- package-lock.json | 25 ++++++++ package.json | 10 ++-- .../cache.decorator.ts} | 6 +- src/index.ts | 16 ++--- .../fs.json.storage.ts} | 4 +- .../memory.storage.ts} | 4 +- .../redis.storage.ts} | 6 +- .../IStorage.ts => storage/storage.types.ts} | 4 +- .../caching/ExpirationStrategy.spec.ts | 60 ------------------- .../caching/abstract.base.strategy.ts} | 9 ++- .../caching/cache.strategy.types.ts} | 4 +- .../caching/expiration.strategy.ts} | 34 +++++------ .../key/json.stringify.strategy.ts} | 6 +- .../key/key.strategy.types.ts} | 6 +- .../cache.decorator.test.ts | 5 +- test/expiration.strategy.test.ts | 59 ++++++++++++++++++ .../fs.json.storage.test.ts | 2 +- .../memory.storage.test.ts | 2 +- .../redis.storage.test.ts | 6 +- tsconfig.json | 7 ++- 21 files changed, 156 insertions(+), 121 deletions(-) rename src/{CacheDecorator.ts => decorator/cache.decorator.ts} (82%) rename src/{storages/FsJsonStorage.ts => storage/fs.json.storage.ts} (91%) rename src/{storages/MemoryStorage.ts => storage/memory.storage.ts} (77%) rename src/{storages/RedisStorage.ts => storage/redis.storage.ts} (91%) rename src/{storages/IStorage.ts => storage/storage.types.ts} (85%) delete mode 100644 src/strategies/caching/ExpirationStrategy.spec.ts rename src/{strategies/caching/AbstractBaseStrategy.ts => strategy/caching/abstract.base.strategy.ts} (61%) rename src/{strategies/caching/ICacheStrategy.ts => strategy/caching/cache.strategy.types.ts} (97%) rename src/{strategies/caching/ExpirationStrategy.ts => strategy/caching/expiration.strategy.ts} (58%) rename src/{strategies/key/JSONStringifyStrategy.ts => strategy/key/json.stringify.strategy.ts} (56%) rename src/{strategies/key/IKeyStrategy.ts => strategy/key/key.strategy.types.ts} (63%) rename src/CacheDecorator.spec.ts => test/cache.decorator.test.ts (94%) create mode 100644 test/expiration.strategy.test.ts rename src/storages/FsJsonStorage.spec.ts => test/fs.json.storage.test.ts (96%) rename src/storages/MemoryStorage.spec.ts => test/memory.storage.test.ts (88%) rename src/storages/RedisStorage.spec.ts => test/redis.storage.test.ts (95%) diff --git a/index.d.ts b/index.d.ts index d7fd6f4..6f5f473 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1 +1 @@ -export { Cache, RedisStorage, ExpirationStrategy, FsJsonStorage, MemoryStorage } from './src'; \ No newline at end of file +export { Cache, RedisStorage, ExpirationStrategy, FsJsonStorage, MemoryStorage } from './src' diff --git a/package-lock.json b/package-lock.json index d81903e..9ba389b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -148,6 +148,11 @@ "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, + "double-ended-queue": { + "version": "2.1.0-0", + "resolved": "https://registry.npmjs.org/double-ended-queue/-/double-ended-queue-2.1.0-0.tgz", + "integrity": "sha1-ED01J/0xUo9AGIEwyEHv3XgmTlw=" + }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", @@ -369,6 +374,26 @@ "resolve": "^1.11.1" } }, + "redis": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/redis/-/redis-2.8.0.tgz", + "integrity": "sha512-M1OkonEQwtRmZv4tEWF2VgpG0JWJ8Fv1PhlgT5+B+uNq2cA3Rt1Yt/ryoR+vQNOQcIEgdCdfH0jr3bDpihAw1A==", + "requires": { + "double-ended-queue": "^2.1.0-0", + "redis-commands": "^1.2.0", + "redis-parser": "^2.6.0" + } + }, + "redis-commands": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/redis-commands/-/redis-commands-1.5.0.tgz", + "integrity": "sha512-6KxamqpZ468MeQC3bkWmCB1fp56XL64D4Kf0zJSwDZbVLLm7KFkoIcHrgRvQ+sk8dnhySs7+yBg94yIkAK7aJg==" + }, + "redis-parser": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/redis-parser/-/redis-parser-2.6.0.tgz", + "integrity": "sha1-Uu0J2srBCPGmMcB+m2mUHnoZUEs=" + }, "resolve": { "version": "1.12.0", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz", diff --git a/package.json b/package.json index b01393b..aa41a65 100644 --- a/package.json +++ b/package.json @@ -4,11 +4,12 @@ "description": "Simple and extensible caching module supporting decorators", "main": "src/index.js", "scripts": { - "test": "mocha src/*.spec.js src/**/*.spec.js", - "tdd": "mocha -w src/*.spec.js src/**/*.spec.js", - "tdd-debug-brk": "mocha --inspect-brk src/**/*.spec.js", + "test": "mocha", + "tdd": "mocha -w", + "tdd-debug-brk": "mocha --inspect-brk", "build": "tsc -p .", "prepublish": "tsc -p .", + "clean": "git clean -fdx src", "prepare": "tsc -p .", "dev": "tsc -p . -w" }, @@ -43,7 +44,8 @@ "@types/bluebird": "3.5.27", "@types/node": "10.14.17", "@types/redis": "^2.8.13", - "bluebird": "3.5.5" + "bluebird": "3.5.5", + "redis": "^2.8.0" }, "devDependencies": { "@types/mocha": "5.2.5", diff --git a/src/CacheDecorator.ts b/src/decorator/cache.decorator.ts similarity index 82% rename from src/CacheDecorator.ts rename to src/decorator/cache.decorator.ts index 6252b9b..6596caf 100644 --- a/src/CacheDecorator.ts +++ b/src/decorator/cache.decorator.ts @@ -1,6 +1,6 @@ -import { IKeyStrategy } from './strategies/key/IKeyStrategy' -import { AbstractBaseStrategy } from './strategies/caching/AbstractBaseStrategy' -import { JSONStringifyKeyStrategy } from './strategies/key/JSONStringifyStrategy' +import { JSONStringifyKeyStrategy } from '../strategy/key/json.stringify.strategy' +import { AbstractBaseStrategy } from '../strategy/caching/abstract.base.strategy' +import { IKeyStrategy } from '..' const defaultKeyStrategy = new JSONStringifyKeyStrategy() diff --git a/src/index.ts b/src/index.ts index 9cf76df..38159af 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,9 +1,9 @@ -import {RedisStorage} from './storages/RedisStorage'; -import {FsJsonStorage} from './storages/FsJsonStorage'; -import {MemoryStorage} from './storages/MemoryStorage'; -import {ExpirationStrategy} from './strategies/caching/ExpirationStrategy'; -import {Cache} from './CacheDecorator'; -import {IKeyStrategy} from './strategies/key/IKeyStrategy'; -import {ICacheStrategy} from './strategies/caching/ICacheStrategy'; +import { RedisStorage } from './storage/redis.storage' +import { FsJsonStorage } from './storage/fs.json.storage' +import { MemoryStorage } from './storage/memory.storage' +import { ExpirationStrategy } from './strategy/caching/expiration.strategy' +import { IKeyStrategy } from './strategy/key/key.strategy.types' +import { ICacheStrategy } from './strategy/caching/cache.strategy.types' +import { Cache } from './decorator/cache.decorator' -export {Cache, ExpirationStrategy, MemoryStorage, FsJsonStorage, RedisStorage, IKeyStrategy, ICacheStrategy}; \ No newline at end of file +export { Cache, ExpirationStrategy, MemoryStorage, FsJsonStorage, RedisStorage, IKeyStrategy, ICacheStrategy } diff --git a/src/storages/FsJsonStorage.ts b/src/storage/fs.json.storage.ts similarity index 91% rename from src/storages/FsJsonStorage.ts rename to src/storage/fs.json.storage.ts index 425e077..2b00168 100644 --- a/src/storages/FsJsonStorage.ts +++ b/src/storage/fs.json.storage.ts @@ -1,9 +1,9 @@ -import { IStorage } from './IStorage' +import { StorageTypes } from './storage.types' import * as Bluebird from 'bluebird' const Fs = Bluebird.promisifyAll(require('fs')) -export class FsJsonStorage implements IStorage { +export class FsJsonStorage implements StorageTypes { constructor(public jsonFilePath: string) { if (!Fs.existsSync(this.jsonFilePath)) { diff --git a/src/storages/MemoryStorage.ts b/src/storage/memory.storage.ts similarity index 77% rename from src/storages/MemoryStorage.ts rename to src/storage/memory.storage.ts index 2a08545..699eddf 100644 --- a/src/storages/MemoryStorage.ts +++ b/src/storage/memory.storage.ts @@ -1,6 +1,6 @@ -import { IStorage } from './IStorage' +import { StorageTypes } from './storage.types' -export class MemoryStorage implements IStorage { +export class MemoryStorage implements StorageTypes { private memCache: any = {} diff --git a/src/storages/RedisStorage.ts b/src/storage/redis.storage.ts similarity index 91% rename from src/storages/RedisStorage.ts rename to src/storage/redis.storage.ts index 2e80888..3bbd2e5 100644 --- a/src/storages/RedisStorage.ts +++ b/src/storage/redis.storage.ts @@ -1,4 +1,4 @@ -import { IStorage } from './IStorage' +import { StorageTypes } from './storage.types' import * as Bluebird from 'bluebird' import * as Redis from 'redis' import { ClientOpts } from 'redis' @@ -7,7 +7,7 @@ import { RedisClient } from '../custom' Bluebird.promisifyAll(Redis.RedisClient.prototype) Bluebird.promisifyAll(Redis.Multi.prototype) -export class RedisStorage implements IStorage { +export class RedisStorage implements StorageTypes { private client: RedisClient @@ -37,4 +37,4 @@ export class RedisStorage implements IStorage { public async clear(): Promise { return this.client.flushdbAsync() } -} \ No newline at end of file +} diff --git a/src/storages/IStorage.ts b/src/storage/storage.types.ts similarity index 85% rename from src/storages/IStorage.ts rename to src/storage/storage.types.ts index efd0c06..63cd569 100644 --- a/src/storages/IStorage.ts +++ b/src/storage/storage.types.ts @@ -3,10 +3,10 @@ interface ICacheEntry { meta: any; } -export interface IStorage { +export interface StorageTypes { getItem(key: string): Promise; setItem(key: string, content: ICacheEntry): Promise; clear(): Promise; -} \ No newline at end of file +} diff --git a/src/strategies/caching/ExpirationStrategy.spec.ts b/src/strategies/caching/ExpirationStrategy.spec.ts deleted file mode 100644 index 66ef8e3..0000000 --- a/src/strategies/caching/ExpirationStrategy.spec.ts +++ /dev/null @@ -1,60 +0,0 @@ -import {MemoryStorage} from '../../storages/MemoryStorage'; -import * as Assert from "assert"; -import {ExpirationStrategy} from './ExpirationStrategy'; - -interface ITestType { - user: { - name: string - } -} - -const data: ITestType = { - user: {name: "test"} -}; - -describe("ExpirationStrategy", () => { - it("Should set cache item correctly with isLazy", async () => { - const cacher = new ExpirationStrategy(new MemoryStorage()); - - await cacher.setItem("test", data, {ttl: 10}); - const entry = await cacher.getItem("test"); - - Assert.deepStrictEqual(entry, data); - }); - - it("Should return no item if cache expires istantly with isLazy", async () => { - const cacher = new ExpirationStrategy(new MemoryStorage()); - - await cacher.setItem("test", data, {ttl: -1}); - const entry = await cacher.getItem("test"); - Assert.deepStrictEqual(entry, undefined); - }); - - it("Should not find cache item after ttl with isLazy disabled", async () => { - const cacher = new ExpirationStrategy(new MemoryStorage()); - - await cacher.setItem("test", data, {ttl: 0.001, isLazy: false}); - await wait(10); - - const entry = await cacher.getItem("test"); - Assert.deepStrictEqual(entry, undefined); - }); - - it("Should ignore isLazy and ttl options if isCachedForever option is provided and cache forever", async () => { - const cacher = new ExpirationStrategy(new MemoryStorage()); - - await cacher.setItem("test", data, {ttl: 0, isLazy: false, isCachedForever: true}); - await wait(10); - - const entry = await cacher.getItem("test"); - Assert.deepStrictEqual(entry, data); - }); -}); - -function wait(ms: number): Promise { - return new Promise((resolve, _reject) => { - setTimeout(() => { - resolve(); - }, ms); - }); -} \ No newline at end of file diff --git a/src/strategies/caching/AbstractBaseStrategy.ts b/src/strategy/caching/abstract.base.strategy.ts similarity index 61% rename from src/strategies/caching/AbstractBaseStrategy.ts rename to src/strategy/caching/abstract.base.strategy.ts index 4e23c31..d2496db 100644 --- a/src/strategies/caching/AbstractBaseStrategy.ts +++ b/src/strategy/caching/abstract.base.strategy.ts @@ -1,11 +1,14 @@ -import { IStorage } from '../../storages/IStorage'; -import { ICacheStrategy } from './ICacheStrategy'; +import { StorageTypes } from '../../storage/storage.types' +import { ICacheStrategy } from './cache.strategy.types' export abstract class AbstractBaseStrategy implements ICacheStrategy { - constructor(protected storage: IStorage) { } + constructor(protected storage: StorageTypes) { + } public async abstract getItem(key: string): Promise; + public async abstract setItem(key: string, content: any, options: any): Promise; + public async abstract clear(): Promise; } diff --git a/src/strategies/caching/ICacheStrategy.ts b/src/strategy/caching/cache.strategy.types.ts similarity index 97% rename from src/strategies/caching/ICacheStrategy.ts rename to src/strategy/caching/cache.strategy.types.ts index bf14fb9..9fe0179 100644 --- a/src/strategies/caching/ICacheStrategy.ts +++ b/src/strategy/caching/cache.strategy.types.ts @@ -1,5 +1,7 @@ export interface ICacheStrategy { getItem(key: string): Promise; + setItem(key: string, content: any, options: any): Promise; + clear(): Promise; -} \ No newline at end of file +} diff --git a/src/strategies/caching/ExpirationStrategy.ts b/src/strategy/caching/expiration.strategy.ts similarity index 58% rename from src/strategies/caching/ExpirationStrategy.ts rename to src/strategy/caching/expiration.strategy.ts index 74059fc..c8ef99c 100644 --- a/src/strategies/caching/ExpirationStrategy.ts +++ b/src/strategy/caching/expiration.strategy.ts @@ -1,5 +1,5 @@ -import { IStorage } from '../../storages/IStorage'; -import { AbstractBaseStrategy } from './AbstractBaseStrategy'; +import { StorageTypes } from '../../storage/storage.types' +import { AbstractBaseStrategy } from './abstract.base.strategy' interface IExpiringCacheItem { content: any; @@ -17,48 +17,48 @@ interface IOptions { export class ExpirationStrategy extends AbstractBaseStrategy { - constructor(storage: IStorage) { - super(storage); + constructor(storage: StorageTypes) { + super(storage) } public async getItem(key: string): Promise { - const item = await this.storage.getItem(key); + const item = await this.storage.getItem(key) if (item && item.meta && item.meta.ttl && this.isItemExpired(item)) { - await this.storage.setItem(key, undefined); - return undefined; + await this.storage.setItem(key, undefined) + return undefined } - return item ? item.content : undefined; + return item ? item.content : undefined } public async setItem(key: string, content: any, options: IOptions): Promise { - options = { ttl: 60, isLazy: true, isCachedForever: false, ...options } + options = {ttl: 60, isLazy: true, isCachedForever: false, ...options} - let meta = {}; + let meta = {} if (!options.isCachedForever) { meta = { ttl: options.ttl * 1000, createdAt: Date.now() - }; + } if (!options.isLazy) { setTimeout(() => { - this.unsetKey(key); - }, options.ttl); + this.unsetKey(key) + }, options.ttl) } } - await this.storage.setItem(key, {meta, content}); + await this.storage.setItem(key, {meta, content}) } public async clear(): Promise { - await this.storage.clear(); + await this.storage.clear() } private isItemExpired(item: IExpiringCacheItem): boolean { - return Date.now() > item.meta.createdAt + item.meta.ttl; + return Date.now() > item.meta.createdAt + item.meta.ttl } private async unsetKey(key: string): Promise { - await this.storage.setItem(key, undefined); + await this.storage.setItem(key, undefined) } } diff --git a/src/strategies/key/JSONStringifyStrategy.ts b/src/strategy/key/json.stringify.strategy.ts similarity index 56% rename from src/strategies/key/JSONStringifyStrategy.ts rename to src/strategy/key/json.stringify.strategy.ts index 7b97287..80f3a15 100644 --- a/src/strategies/key/JSONStringifyStrategy.ts +++ b/src/strategy/key/json.stringify.strategy.ts @@ -1,7 +1,9 @@ -import { IKeyStrategy } from './IKeyStrategy' +import { IKeyStrategy } from '../../index' -export class JSONStringifyKeyStrategy implements IKeyStrategy { +class JSONStringifyKeyStrategy implements IKeyStrategy { public getKey(className: string, methodName: string, args: any[]): Promise | string { return `${className}:${methodName}:${JSON.stringify(args)}` } } + +export { JSONStringifyKeyStrategy } diff --git a/src/strategies/key/IKeyStrategy.ts b/src/strategy/key/key.strategy.types.ts similarity index 63% rename from src/strategies/key/IKeyStrategy.ts rename to src/strategy/key/key.strategy.types.ts index 180c6c7..9c197d6 100644 --- a/src/strategies/key/IKeyStrategy.ts +++ b/src/strategy/key/key.strategy.types.ts @@ -1,3 +1,5 @@ -export interface IKeyStrategy { +interface IKeyStrategy { getKey(className: string, methodName: string, args: any[]): Promise | string; -} \ No newline at end of file +} + +export { IKeyStrategy } diff --git a/src/CacheDecorator.spec.ts b/test/cache.decorator.test.ts similarity index 94% rename from src/CacheDecorator.spec.ts rename to test/cache.decorator.test.ts index 5316cd5..07bba50 100644 --- a/src/CacheDecorator.spec.ts +++ b/test/cache.decorator.test.ts @@ -1,8 +1,5 @@ -import { MemoryStorage } from './storages/MemoryStorage' -import { ExpirationStrategy } from './strategies/caching/ExpirationStrategy' import * as Assert from 'assert' -import { Cache } from './CacheDecorator' -import { IKeyStrategy } from './strategies/key/IKeyStrategy' +import { Cache, ExpirationStrategy, IKeyStrategy, MemoryStorage } from '../src' const strategy = new ExpirationStrategy(new MemoryStorage()) const data = ['user', 'max', 'test'] diff --git a/test/expiration.strategy.test.ts b/test/expiration.strategy.test.ts new file mode 100644 index 0000000..bb16a98 --- /dev/null +++ b/test/expiration.strategy.test.ts @@ -0,0 +1,59 @@ +import * as Assert from 'assert' +import { ExpirationStrategy, MemoryStorage } from '../src' + +interface ITestType { + user: { + name: string + } +} + +const data: ITestType = { + user: {name: 'test'} +} + +describe('ExpirationStrategy', () => { + it('Should set cache item correctly with isLazy', async () => { + const cacher = new ExpirationStrategy(new MemoryStorage()) + + await cacher.setItem('test', data, {ttl: 10}) + const entry = await cacher.getItem('test') + + Assert.deepStrictEqual(entry, data) + }) + + it('Should return no item if cache expires istantly with isLazy', async () => { + const cacher = new ExpirationStrategy(new MemoryStorage()) + + await cacher.setItem('test', data, {ttl: -1}) + const entry = await cacher.getItem('test') + Assert.deepStrictEqual(entry, undefined) + }) + + it('Should not find cache item after ttl with isLazy disabled', async () => { + const cacher = new ExpirationStrategy(new MemoryStorage()) + + await cacher.setItem('test', data, {ttl: 0.001, isLazy: false}) + await wait(10) + + const entry = await cacher.getItem('test') + Assert.deepStrictEqual(entry, undefined) + }) + + it('Should ignore isLazy and ttl options if isCachedForever option is provided and cache forever', async () => { + const cacher = new ExpirationStrategy(new MemoryStorage()) + + await cacher.setItem('test', data, {ttl: 0, isLazy: false, isCachedForever: true}) + await wait(10) + + const entry = await cacher.getItem('test') + Assert.deepStrictEqual(entry, data) + }) +}) + +function wait(ms: number): Promise { + return new Promise((resolve, _reject) => { + setTimeout(() => { + resolve() + }, ms) + }) +} diff --git a/src/storages/FsJsonStorage.spec.ts b/test/fs.json.storage.test.ts similarity index 96% rename from src/storages/FsJsonStorage.spec.ts rename to test/fs.json.storage.test.ts index 33e7796..3892b87 100644 --- a/src/storages/FsJsonStorage.spec.ts +++ b/test/fs.json.storage.test.ts @@ -1,6 +1,6 @@ import * as Assert from 'assert' import * as Fs from 'fs' -import { FsJsonStorage } from './FsJsonStorage' +import { FsJsonStorage } from '../src' const cacheFile = 'cache.json' diff --git a/src/storages/MemoryStorage.spec.ts b/test/memory.storage.test.ts similarity index 88% rename from src/storages/MemoryStorage.spec.ts rename to test/memory.storage.test.ts index 8bd2087..a18626b 100644 --- a/src/storages/MemoryStorage.spec.ts +++ b/test/memory.storage.test.ts @@ -1,5 +1,5 @@ import * as Assert from 'assert' -import { MemoryStorage } from './MemoryStorage' +import { MemoryStorage } from '../src' describe('MemoryStorage', () => { it('Should add cache item correctly', async () => { diff --git a/src/storages/RedisStorage.spec.ts b/test/redis.storage.test.ts similarity index 95% rename from src/storages/RedisStorage.spec.ts rename to test/redis.storage.test.ts index 7dfeb30..1d576bc 100644 --- a/src/storages/RedisStorage.spec.ts +++ b/test/redis.storage.test.ts @@ -1,7 +1,7 @@ -import { RedisStorage } from './RedisStorage' import * as Proxyquire from 'proxyquire' import * as Sinon from 'sinon' import * as Assert from 'assert' +import { RedisStorage } from '../src' Proxyquire.noCallThru() @@ -21,7 +21,7 @@ const RedisMock = { prototype: {} } } -const MockRedisStorage: typeof RedisStorage = Proxyquire.load('./RedisStorage', { +const MockRedisStorage: typeof RedisStorage = Proxyquire.load('../src/storage/redis.storage', { 'redis': RedisMock }).RedisStorage @@ -68,7 +68,7 @@ describe('RedisStorage', () => { prototype: {} } } - const MockRedisFailStorage: typeof RedisStorage = Proxyquire.load('./RedisStorage', { + const MockRedisFailStorage: typeof RedisStorage = Proxyquire.load('../src/storage/redis.storage', { 'redis': RedisMock }).RedisStorage diff --git a/tsconfig.json b/tsconfig.json index 2ef990c..110d43e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -20,10 +20,13 @@ "node_modules/@types" ], "allowJs": false, - "rootDir": "src" + "rootDirs": [ + "src", + "test" + ] }, "exclude": [ "node_modules" ], "compileOnSave": true -} \ No newline at end of file +}