Skip to content
This repository has been archived by the owner on Aug 5, 2020. It is now read-only.

Commit

Permalink
Add custom cache key strategy support (#1218)
Browse files Browse the repository at this point in the history
* Add custom cache key strategy support

* Revert prettier changes

* Add tests to cacheManager

* Flow fixes

* Enable use of state in custom cache key strategies

* Update type of setCacheIfProd to make state optional

* Make customCacheKeyStrategies shared across calls of cacheManagers

* Whitespace

* Allow null as a return type
  • Loading branch information
hhtran authored and threehams committed Dec 11, 2018
1 parent eef634b commit efa2669
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/gluestick/src/__tests__/mocks/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const request: Request = {
url: '/',
headers: { 'user-agent': '' },
method: 'GET',
query: {},
};

const entriesConfig: EntriesConfig = {
Expand Down
1 change: 1 addition & 0 deletions packages/gluestick/src/renderer/__tests__/render.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ describe('renderer/render', () => {
url: '',
hostname: '',
method: 'GET',
query: {},
};

const store = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,19 @@ describe('renderer/helpers/cacheManager', () => {
true,
);
const cache = {
get: jest.fn(key => (key === 'localhost/' ? 'output' : null)),
get: jest.fn(key => {
if (key === 'localhost/') {
return 'output';
} else if (key === 'localhost-/test-123') {
return 'cache';
} else if (key === 'localhost-/test-124') {
return 'cache2';
}
return null;
}),
set: jest.fn(),
};

it('should return cached output', () => {
expect(
cacheManager.getCachedIfProd(
Expand Down Expand Up @@ -77,5 +87,114 @@ describe('renderer/helpers/cacheManager', () => {
1000000,
]);
});

describe('when a custom cache key strategy is defined', () => {
beforeAll(() => {
cache.set.mockClear();
cacheManager.setCacheIfProd(
{
hostname: 'localhost',
url: '/test',
query: {
styleId: '123',
unused: 'hello',
},
},
'cache',
1000,
cache,
({ hostname, url, query }) => `${hostname}-${url}-${query.styleId}`,
);
cacheManager.setCacheIfProd(
{
hostname: 'localhost',
url: '/test',
query: {
styleId: '124',
unused: 'hello',
},
},
'cache',
1000,
cache,
({ hostname, url, query }) => `${hostname}-${url}-${query.styleId}`,
);

const currentState = { currentUser: { loaded: true } };
cacheManager.setCacheIfProd(
{
hostname: 'localhost',
url: '/login',
query: {
styleId: '124',
unused: 'hello',
},
},
'cache',
1000,
cache,
({ hostname, url }, state) => {
if (state.currentUser.loaded) {
return null;
}
return `${hostname}-${url}`;
},
currentState,
);
});

it('should set cache using the custom cache key', () => {
expect(cache.set.mock.calls[0]).toEqual([
'localhost-/test-123',
'cache',
1000000,
]);
});

it('should return cached output', () => {
const cached1 = cacheManager.getCachedIfProd(
{
hostname: 'localhost',
url: '/test',
query: {
styleId: '123',
unused: 'hello',
},
},
cache,
);
const cached2 = cacheManager.getCachedIfProd(
{
hostname: 'localhost',
url: '/test',
query: {
styleId: '123',
unused: 'blah',
},
},
cache,
);
const cached3 = cacheManager.getCachedIfProd(
{
hostname: 'localhost',
url: '/test',
query: {
styleId: '124',
unused: 'hello',
},
},
cache,
);
expect(cached1).toEqual('cache');
expect(cached2).toEqual('cache');
expect(cached3).toEqual('cache2');
});

describe('when a falsy value is returned from the custom cache key', () => {
it('should not set the cache', () => {
expect(cache.set.mock.calls[2]).toBeUndefined();
});
});
});
});
});
24 changes: 20 additions & 4 deletions packages/gluestick/src/renderer/helpers/cacheManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,19 @@ const getCacheKey = ({
return `${hostname}${url}`;
};

const customCacheKeyStrategies = {};
module.exports = function createCacheManager(
logger: BaseLogger,
isProduction: boolean,
): CacheManager {
const getCachedIfProd: GetCachedIfProd = (req, cache = _cache) => {
if (isProduction) {
const key: string = getCacheKey(req);
const defaultKey: string = getCacheKey(req);
let key = defaultKey;
if (customCacheKeyStrategies[defaultKey]) {
const cacheKeyStrategy = customCacheKeyStrategies[defaultKey];
key = cacheKeyStrategy(req);
}
const value: string = cache.get(key);
if (value) {
logger.debug(`Get cached: ${key}`);
Expand All @@ -47,11 +53,21 @@ module.exports = function createCacheManager(
value,
maxAge = DEFAULT_TTL,
cache = _cache,
cacheKeyStrategy,
state,
) => {
if (isProduction) {
const key: string = getCacheKey(req);
logger.debug(`Set cache: ${key}`);
cache.set(key, value, maxAge * 1000);
const defaultKey: string = getCacheKey(req);
let key = defaultKey;
if (cacheKeyStrategy) {
key = cacheKeyStrategy(req, state);
customCacheKeyStrategies[defaultKey] = cacheKeyStrategy;
}

if (key) {
cache.set(key, value, maxAge * 1000);
logger.debug(`Set cache: ${key}`);
}
}
};
return {
Expand Down
9 changes: 8 additions & 1 deletion packages/gluestick/src/renderer/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,14 @@ module.exports = async function render(
responseString = `${docType}${renderToStaticMarkup(rootElement)}`;
}
if (currentRoute.cache) {
cacheManager.setCacheIfProd(req, responseString, currentRoute.cacheTTL);
cacheManager.setCacheIfProd(
req,
responseString,
currentRoute.cacheTTL,
undefined, // Do not override the default cache
currentRoute.cacheKey,
currentState,
);
}
return {
responseString,
Expand Down
3 changes: 3 additions & 0 deletions packages/gluestick/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export type Request = {
hostname: string,
headers: Object,
method: string,
query: Object,
};

export type RenderRequirements = {
Expand All @@ -155,6 +156,8 @@ export type SetCacheIfProd = (
value: string,
maxAge?: number,
cache?: Object,
cacheKeyStrategy?: (req: Request, state?: Object) => string | null,
state?: Object,
) => void;

export type CacheManager = {
Expand Down

0 comments on commit efa2669

Please sign in to comment.