diff --git a/package.json b/package.json index a96efe8..9b50e82 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,9 @@ "express": "4.13.4", "lodash": "4.13.1", "mongodb": "2.1.18", - "pg": "6.0.1" + "object-hash": "^1.1.3", + "pg": "6.0.1", + "redis": "^2.6.2" }, "devDependencies": { "chai": "3.5.0", diff --git a/src/app/AppConfig.ts b/src/app/AppConfig.ts index 1c713c3..c9a2113 100644 --- a/src/app/AppConfig.ts +++ b/src/app/AppConfig.ts @@ -1,4 +1,4 @@ -import { Configuration, PropertySource, Import } from "@sklechko/framework"; +import { Configuration, PropertySource, Import, EnableCaching } from "@sklechko/framework"; import { ControllersConfig } from "./controllers/ControllersConfig"; import { RepositoriesConfig } from "./repositories/RepositoriesConfig"; import { ServicesConfig } from "./services/ServicesConfig"; @@ -9,6 +9,7 @@ import { AspectsConfig } from "./aspects/AspectsConfig"; @Import(ControllersConfig, RepositoriesConfig, ServicesConfig, InterceptorsConfig, PostProcessorsConfig, AspectsConfig) @PropertySource(__dirname + '/../resources/app.properties.json') +@EnableCaching() @ActiveProfiles('mongo') @Configuration() export class AppConfig {} \ No newline at end of file diff --git a/src/app/controllers/GreetingsController.ts b/src/app/controllers/GreetingsController.ts index 24664e8..8d10ed0 100644 --- a/src/app/controllers/GreetingsController.ts +++ b/src/app/controllers/GreetingsController.ts @@ -4,6 +4,7 @@ import { import {GreetService} from "../services/GreetService"; import { Environment } from "@sklechko/framework/lib/di/Environment"; import { Timed } from "../postProcessors/timed/Timed"; +import { CacheableService } from "../services/CacheableService"; class AbstractGreetingCtrl { @@ -19,11 +20,14 @@ export class GreetingsController extends AbstractGreetingCtrl { @Inject() private env: Environment; + @Inject() + private cacheableService: CacheableService; + getName() { return new Promise(function (resolve) { setTimeout(function () { resolve(RequestContextHolder.getRequest().params.name); - }, 2000); + }, 5000); }); } @@ -43,4 +47,45 @@ export class GreetingsController extends AbstractGreetingCtrl { var preHandleMessage = ( RequestContextHolder.getResponse()).preHandleProperty; return { greet, someProperty, preHandleMessage }; } + + @RequestMapping({ path: '/hi/cacheable', method: RequestMethod.GET }) + async cacheable () { + await this.cacheableService.getModel('name', 2, new CustomClass()); + return { greet: 'Model Saved !!!' }; + } + + @RequestMapping({ path: '/hi/cacheEvict', method: RequestMethod.GET }) + async deleteCache () { + await this.cacheableService.deleteModel('name'); + return { greet: 'Model deleted !!!' }; + } + + @RequestMapping({ path: '/hi/cachePut', method: RequestMethod.GET }) + async saveCache () { + await this.cacheableService.getModelPut('otherProp', new CustomClass()); + return { greet: 'Model Saved and cache updated!!!' }; + } +} + +export class CustomClass { + private property1; + property2; + private prop; + private nestedProp; + + constructor() { + this.prop = 'name'; + this.property2 = 'publicProperty'; + this.nestedProp = new OtherClass(); + } + + methodOne(){} +} + +export class OtherClass { + private className; + + constructor() { + this.className = 'class One'; + } } \ No newline at end of file diff --git a/src/app/repositories/CacheProviders/RedisCacheProvider.ts b/src/app/repositories/CacheProviders/RedisCacheProvider.ts new file mode 100644 index 0000000..5414158 --- /dev/null +++ b/src/app/repositories/CacheProviders/RedisCacheProvider.ts @@ -0,0 +1,71 @@ +import {Component, Qualifier} from "@sklechko/framework"; +import { I_CACHE_PROVIDER_TOKEN, ICacheProvider } from "@sklechko/framework/lib/processors/cache/ICacheProvider"; +var redis = require('redis'); + +@Qualifier(I_CACHE_PROVIDER_TOKEN) +@Component() +export class RedisCacheProvider implements ICacheProvider{ + + private client; + private caches: Map; + + constructor () { + this.caches = new Map(); + this.client = redis.createClient(); + } + + async get(key, cacheName): Promise { + await this.getCache(cacheName); + let result = await this.getPromise(this.client, this.client.get, key); + return JSON.parse(result); + } + async set(key, result, cacheName): Promise { + await this.getCache(cacheName); + return await this.getPromise(this.client, this.client.set, [key, JSON.stringify(result)]); + } + + async flushdb(cacheName): Promise { + await this.getCache(cacheName); + this.client.flushdb(); + } + + async del(key, cacheName): Promise { + await this.getCache(cacheName); + this.client.del(key); + } + + private async getCache(cacheName: string) { + let cacheNumber: number = this.caches.get(cacheName); + if(cacheNumber === undefined) { + cacheNumber = this.createNewCache(); + this.caches.set(cacheName, cacheNumber); + } + await this.getPromise(this.client, this.client.select, cacheNumber); + } + + private createNewCache() { + let max = 0; + let cacheIter = this.caches.values(); + while(true) { + let value = cacheIter.next().value; + if(value === undefined) break; + max++; + } + return max; + } + + private getPromise(thisArg, method, ...args): Promise { + return new Promise((resolve, reject) => { + let callback = function (error, result) { + if (error) { + reject(error); + } else { + resolve(result); + } + }; + + args.push(callback); + Reflect.apply(method, thisArg, args); + }); + } +} \ No newline at end of file diff --git a/src/app/services/CacheableService.ts b/src/app/services/CacheableService.ts new file mode 100644 index 0000000..92fafce --- /dev/null +++ b/src/app/services/CacheableService.ts @@ -0,0 +1,28 @@ +import {Component, CacheEvict, Cacheable, CachePut} from "@sklechko/framework"; + +@Component() +export class CacheableService { + + @Cacheable({ cacheName: 'model', key: '#third.prop' }) + async getModel(id: string, second, third): Promise { + return new Promise(function (resolve) { + setTimeout(function () { + resolve("Returns the model for the given id!"); + }, 5000); + }); + } + + @CacheEvict({ cacheName: 'model', allEntries: false, key: '#id' }) + async deleteModel(id: string): Promise { + return "The todo with the given id was deleted"; + } + + @CachePut({ cacheName: 'model', key: '#second.prop' }) + async getModelPut(id: string, second): Promise { + return new Promise(function (resolve) { + setTimeout(function () { + resolve("Returns the model for the given id and sets it in the cache!"); + }, 5000); + }); + } +} \ No newline at end of file