Add cache interface, factory, and basic in-mem implementation
This commit is contained in:
parent
42d9cc101f
commit
088fdfb1ef
@ -59,6 +59,10 @@ export * from './service/HTTPServer'
|
|||||||
export * from './service/Routing'
|
export * from './service/Routing'
|
||||||
export * from './service/Middlewares'
|
export * from './service/Middlewares'
|
||||||
|
|
||||||
|
export * from './support/cache/Cache'
|
||||||
|
export * from './support/cache/MemoryCache'
|
||||||
|
export * from './support/cache/CacheFactory'
|
||||||
|
|
||||||
export * from './views/ViewEngine'
|
export * from './views/ViewEngine'
|
||||||
export * from './views/ViewEngineFactory'
|
export * from './views/ViewEngineFactory'
|
||||||
export * from './views/PugViewEngine'
|
export * from './views/PugViewEngine'
|
||||||
|
@ -14,6 +14,7 @@ import {Logging} from '../service/Logging';
|
|||||||
import {RunLevelErrorHandler} from "./RunLevelErrorHandler";
|
import {RunLevelErrorHandler} from "./RunLevelErrorHandler";
|
||||||
import {Unit, UnitStatus} from "./Unit";
|
import {Unit, UnitStatus} from "./Unit";
|
||||||
import * as dotenv from 'dotenv';
|
import * as dotenv from 'dotenv';
|
||||||
|
import {CacheFactory} from "../support/cache/CacheFactory";
|
||||||
|
|
||||||
export function env(key: string, defaultValue?: any): any {
|
export function env(key: string, defaultValue?: any): any {
|
||||||
return Application.getApplication().env(key, defaultValue)
|
return Application.getApplication().env(key, defaultValue)
|
||||||
@ -103,6 +104,8 @@ export class Application extends Container {
|
|||||||
this.bootstrapEnvironment()
|
this.bootstrapEnvironment()
|
||||||
this.setupLogging()
|
this.setupLogging()
|
||||||
|
|
||||||
|
this.registerFactory(new CacheFactory()) // FIXME move this somewhere else?
|
||||||
|
|
||||||
this.make<Logging>(Logging).debug(`Application root: ${this.baseDir}`)
|
this.make<Logging>(Logging).debug(`Application root: ${this.baseDir}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
31
src/support/cache/Cache.ts
vendored
Normal file
31
src/support/cache/Cache.ts
vendored
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* Abstract interface class for an application cache object.
|
||||||
|
*/
|
||||||
|
export abstract class Cache {
|
||||||
|
/**
|
||||||
|
* Fetch a value from the cache by its key.
|
||||||
|
* @param {string} key
|
||||||
|
* @return Promise<any|undefined>
|
||||||
|
*/
|
||||||
|
public abstract fetch(key: string): string | undefined | Promise<string | undefined>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store the given value in the cache by key.
|
||||||
|
* @param {string} key
|
||||||
|
* @param {string} value
|
||||||
|
*/
|
||||||
|
public abstract put(key: string, value: string): void | Promise<void>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the cache has the given key.
|
||||||
|
* @param {string} key
|
||||||
|
* @return Promise<boolean>
|
||||||
|
*/
|
||||||
|
public abstract has(key: string): boolean | Promise<boolean>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop the given key from the cache.
|
||||||
|
* @param {string} key
|
||||||
|
*/
|
||||||
|
public abstract drop(key: string): void | Promise<void>;
|
||||||
|
}
|
72
src/support/cache/CacheFactory.ts
vendored
Normal file
72
src/support/cache/CacheFactory.ts
vendored
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import {
|
||||||
|
AbstractFactory,
|
||||||
|
Container,
|
||||||
|
DependencyRequirement,
|
||||||
|
PropertyDependency,
|
||||||
|
isInstantiable,
|
||||||
|
DEPENDENCY_KEYS_METADATA_KEY,
|
||||||
|
DEPENDENCY_KEYS_PROPERTY_METADATA_KEY
|
||||||
|
} from "@extollo/di"
|
||||||
|
import {Collection, ErrorWithContext} from "@extollo/util"
|
||||||
|
import {Logging} from "../../service/Logging";
|
||||||
|
import {Config} from "../../service/Config";
|
||||||
|
import {Cache} from "./Cache"
|
||||||
|
import {MemoryCache} from "./MemoryCache";
|
||||||
|
|
||||||
|
export class CacheFactory extends AbstractFactory {
|
||||||
|
protected readonly logging: Logging
|
||||||
|
protected readonly config: Config
|
||||||
|
|
||||||
|
private static loggedMemoryCacheWarningOnce = false
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
super({})
|
||||||
|
this.logging = Container.getContainer().make<Logging>(Logging)
|
||||||
|
this.config = Container.getContainer().make<Config>(Config)
|
||||||
|
}
|
||||||
|
|
||||||
|
produce(dependencies: any[], parameters: any[]): Cache {
|
||||||
|
return new (this.getCacheClass())
|
||||||
|
}
|
||||||
|
|
||||||
|
match(something: any) {
|
||||||
|
return something === Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
getDependencyKeys(): Collection<DependencyRequirement> {
|
||||||
|
const meta = Reflect.getMetadata(DEPENDENCY_KEYS_METADATA_KEY, this.getCacheClass())
|
||||||
|
if ( meta ) return meta
|
||||||
|
return new Collection<DependencyRequirement>()
|
||||||
|
}
|
||||||
|
|
||||||
|
getInjectedProperties(): Collection<PropertyDependency> {
|
||||||
|
const meta = new Collection<PropertyDependency>()
|
||||||
|
let currentToken = this.getCacheClass()
|
||||||
|
|
||||||
|
do {
|
||||||
|
const loadedMeta = Reflect.getMetadata(DEPENDENCY_KEYS_PROPERTY_METADATA_KEY, currentToken)
|
||||||
|
if ( loadedMeta ) meta.concat(loadedMeta)
|
||||||
|
currentToken = Object.getPrototypeOf(currentToken)
|
||||||
|
} while (Object.getPrototypeOf(currentToken) !== Function.prototype && Object.getPrototypeOf(currentToken) !== Object.prototype)
|
||||||
|
|
||||||
|
return meta
|
||||||
|
}
|
||||||
|
|
||||||
|
protected getCacheClass() {
|
||||||
|
const CacheClass = this.config.get('server.cache.driver', MemoryCache)
|
||||||
|
if ( CacheClass === MemoryCache && !CacheFactory.loggedMemoryCacheWarningOnce ) {
|
||||||
|
this.logging.warn(`You are using the default memory-based cache driver. It is recommended you configure a persistent cache driver instead.`)
|
||||||
|
CacheFactory.loggedMemoryCacheWarningOnce = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( !isInstantiable(CacheClass) || !(CacheClass.prototype instanceof Cache) ) {
|
||||||
|
const e = new ErrorWithContext('Provided session class does not extend from @extollo/lib.Cache');
|
||||||
|
e.context = {
|
||||||
|
config_key: 'server.cache.driver',
|
||||||
|
class: CacheClass.toString(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CacheClass
|
||||||
|
}
|
||||||
|
}
|
28
src/support/cache/MemoryCache.ts
vendored
Normal file
28
src/support/cache/MemoryCache.ts
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import {Collection} from "@extollo/util"
|
||||||
|
import {Cache} from "./Cache"
|
||||||
|
|
||||||
|
export class MemoryCache extends Cache {
|
||||||
|
private static cacheItems: Collection<{key: string, value: string}> = new Collection<{key: string; value: string}>()
|
||||||
|
|
||||||
|
public fetch(key: string): string | Promise<string | undefined> | undefined {
|
||||||
|
return MemoryCache.cacheItems
|
||||||
|
.firstWhere('key', '=', key)?.value
|
||||||
|
}
|
||||||
|
|
||||||
|
public put(key: string, value: string): void | Promise<void> {
|
||||||
|
const existing = MemoryCache.cacheItems.firstWhere('key', '=', key)
|
||||||
|
if ( existing ) {
|
||||||
|
existing.value = value
|
||||||
|
} else {
|
||||||
|
MemoryCache.cacheItems.push({ key, value })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(key: string): boolean | Promise<boolean> {
|
||||||
|
return !!MemoryCache.cacheItems.firstWhere('key', '=', key)
|
||||||
|
}
|
||||||
|
|
||||||
|
public drop(key: string): void | Promise<void> {
|
||||||
|
MemoryCache.cacheItems = MemoryCache.cacheItems.where('key', '!=', key)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user