Browse Source

Drop custom cache in favour of Got v8 cache (#48)

pull/49/head
Luke Childs 7 years ago
committed by GitHub
parent
commit
7883fb7e43
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 29
      README.md
  2. 5
      package.json
  3. 81
      src/index.js
  4. 92
      test/cache.js

29
README.md

@ -64,24 +64,20 @@ const onionoo = new Onionoo({
'clients', 'clients',
'uptime' 'uptime'
], ],
cache: { cache: false
store: 'memory',
ttl: 18000,
max: 500
}
}); });
``` ```
## Cache Stores ## Cache
This module makes use of [`node-cache-manager`](https://github.com/BryanDonovan/node-cache-manager) to support multiple cache stores. By default cached responses are stored in memory. You can easily disable the cache or use a more scalable cache store such as Redis by using `node-cache-manager`'s [store engine](https://github.com/BryanDonovan/node-cache-manager#store-engines) modules. By default no cache is used. You can easily cache in memory or to a more scaleable store like Redis using [Keyv storage adapters](https://github.com/lukechilds/keyv#official-storage-adapters).
Disable cache: Cache in memory:
```js ```js
const Onionoo = require('onionoo'); const Onionoo = require('onionoo');
const onionoo = new Onionoo({ const onionoo = new Onionoo({
cache: false cache: new Map()
}); });
``` ```
@ -89,11 +85,12 @@ Use persistent Redis cache:
```js ```js
const Onionoo = require('onionoo'); const Onionoo = require('onionoo');
const redisStore = require('cache-manager-redis'); const KeyvRedis = require('@keyv/redis');
const redis = new KeyvRedis('redis://user:pass@localhost:6379');
const onionoo = new Onionoo({ const onionoo = new Onionoo({
cache: { cache: redis
store: redisStore
}
}); });
``` ```
@ -123,10 +120,10 @@ Array of endpoints to be returned as methods on the `Onionoo` instance.
##### options.cache ##### options.cache
Type: `object`, `boolean`<br> Type: `object`<br>
Default: `{ store: 'memory', ttl: 18000, max: 500 }` Default: `false`
Options object to be merged with default options and passed to [`node-cache-manager`](https://github.com/BryanDonovan/node-cache-manager). Alternatively, if set to false, caching will be disabled. [Keyv](https://github.com/lukechilds/keyv) storage adapter instance for storing cached data.
### onionoo.endpoint([query]) ### onionoo.endpoint([query])

5
package.json

@ -26,16 +26,11 @@
}, },
"homepage": "https://github.com/lukechilds/onionoo-node-client", "homepage": "https://github.com/lukechilds/onionoo-node-client",
"dependencies": { "dependencies": {
"cache-manager": "2.5.0",
"deep-assign": "2.0.0",
"expired": "1.3.12",
"got": "8.0.0" "got": "8.0.0"
}, },
"devDependencies": { "devDependencies": {
"ava": "^0.24.0", "ava": "^0.24.0",
"coveralls": "^3.0.0", "coveralls": "^3.0.0",
"date-fns": "^1.21.1",
"delay": "^2.0.0",
"nock": "^9.0.2", "nock": "^9.0.2",
"nyc": "^11.0.0", "nyc": "^11.0.0",
"xo": "^0.19.0" "xo": "^0.19.0"

81
src/index.js

@ -1,8 +1,5 @@
const querystring = require('querystring'); const querystring = require('querystring');
const got = require('got'); const got = require('got');
const cacheManager = require('cache-manager');
const expired = require('expired');
const deepAssign = require('deep-assign');
const pkg = require('../package.json'); const pkg = require('../package.json');
class Onionoo { class Onionoo {
@ -21,13 +18,6 @@ class Onionoo {
'uptime' 'uptime'
] ]
}, options); }, options);
if (options.cache !== false) {
this.options.cache = cacheManager.caching(Object.assign({
store: 'memory',
ttl: 18000, // 5 hours
max: 500
}, options.cache));
}
// Return object containing endpoint methods // Return object containing endpoint methods
return this.options.endpoints.reduce((onionoo, endpoint) => { return this.options.endpoints.reduce((onionoo, endpoint) => {
@ -46,74 +36,13 @@ class Onionoo {
let url = `${this.options.baseUrl}/${endpoint}`; let url = `${this.options.baseUrl}/${endpoint}`;
url += qs ? `?${qs}` : ''; url += qs ? `?${qs}` : '';
// If caching is disabled, just make the request // Make request
if (!this.options.cache) { return got(url, {
return this.makeRequest(url);
}
// If caching is enabled, check for url in cache
return this.options.cache.get(url)
.then(cachedResult => {
const options = {};
// If we have it cached
if (cachedResult) {
// Return the cached entry if it's still valid
if (!expired(cachedResult.headers)) {
return cachedResult;
// If it's stale, add last-modified date to headers
} else if (cachedResult.headers['last-modified']) {
options.headers = {
'if-modified-since': cachedResult.headers['last-modified']
};
}
}
// Make a request
return this.makeRequest(url, options)
.then(response => {
// If we get a 304, fill in the body
if (response.statusCode === 304) {
response.body = cachedResult.body;
}
// If we get a 200 or 304, cache it
if ([200, 304].includes(response.statusCode)) {
this.options.cache.set(url, response);
}
return response;
});
});
}
// Returns a promise for a request
makeRequest(url, options = {}) {
options = deepAssign({
headers: { headers: {
'user-agent': `onionoo-node-client v${pkg.version} (${pkg.homepage})` 'user-agent': `onionoo-node-client v${pkg.version} (${pkg.homepage})`
} },
}, options); json: true,
cache: this.options.cache
return got(url, options)
.catch(err => {
// Don't throw 304 responses
if (err.statusCode === 304) {
return err.response;
}
throw err;
})
.then(response => {
// Format response
response = {
statusCode: response.statusCode,
statusMessage: response.statusMessage,
headers: response.headers,
body: response.body && JSON.parse(response.body)
};
return response;
}); });
} }
} }

92
test/cache.js

@ -1,12 +1,10 @@
import test from 'ava'; import test from 'ava';
import nock from 'nock'; import nock from 'nock';
import subSeconds from 'date-fns/sub_seconds';
import delay from 'delay';
import Onionoo from '../'; import Onionoo from '../';
import data from './fixtures/data'; import data from './fixtures/data';
test('Cache can be disabled', async t => { test('Cache is disabled by default', async t => {
const onionoo = new Onionoo({cache: false}); const onionoo = new Onionoo();
const defaultEndpoint = data.defaultEndpoints[0]; const defaultEndpoint = data.defaultEndpoints[0];
const responseHeaders = { const responseHeaders = {
@ -21,7 +19,7 @@ test('Cache can be disabled', async t => {
const response = await onionoo[defaultEndpoint](); const response = await onionoo[defaultEndpoint]();
t.deepEqual(response.body, data.dummyResponse); t.false(response.fromCache);
t.truthy(scope.isDone()); t.truthy(scope.isDone());
const scope2 = nock(data.defaultBaseUrl) const scope2 = nock(data.defaultBaseUrl)
@ -30,12 +28,12 @@ test('Cache can be disabled', async t => {
const response2 = await onionoo[defaultEndpoint](); const response2 = await onionoo[defaultEndpoint]();
t.deepEqual(response2.body, data.dummyResponse); t.false(response2.fromCache);
t.truthy(scope2.isDone()); t.truthy(scope2.isDone());
}); });
test('Responses with future max-age are cached', async t => { test('Cache options is passed through to Got', async t => {
const onionoo = new Onionoo(); const onionoo = new Onionoo({cache: new Map()});
const defaultEndpoint = data.defaultEndpoints[0]; const defaultEndpoint = data.defaultEndpoints[0];
const responseHeaders = { const responseHeaders = {
@ -50,84 +48,10 @@ test('Responses with future max-age are cached', async t => {
const response = await onionoo[defaultEndpoint](); const response = await onionoo[defaultEndpoint]();
t.deepEqual(response.body, data.dummyResponse); t.false(response.fromCache);
t.truthy(scope.isDone()); t.truthy(scope.isDone());
const cachedResponse = await onionoo[defaultEndpoint]();
t.deepEqual(cachedResponse.body, data.dummyResponse);
});
test('Responses older than max-age are not cached', async t => {
const onionoo = new Onionoo();
const defaultEndpoint = data.defaultEndpoints[0];
const responseHeaders = {
date: subSeconds(new Date(), 15).toUTCString(),
age: 0,
'cache-control': 'public, max-age=10'
};
const scope = nock(data.defaultBaseUrl)
.get(`/${defaultEndpoint}`)
.reply(200, data.dummyResponse, responseHeaders);
const response = await onionoo[defaultEndpoint]();
t.deepEqual(response.body, data.dummyResponse);
t.truthy(scope.isDone());
const scope2 = nock(data.defaultBaseUrl)
.get(`/${defaultEndpoint}`)
.reply(200, data.dummyResponse, responseHeaders);
const response2 = await onionoo[defaultEndpoint](); const response2 = await onionoo[defaultEndpoint]();
t.deepEqual(response2.body, data.dummyResponse); t.true(response2.fromCache);
t.truthy(scope2.isDone());
});
test('When expired, add last-modified date to headers and handle 304', async t => {
const onionoo = new Onionoo();
const defaultEndpoint = data.defaultEndpoints[0];
const initialDate = new Date().toUTCString();
const responseHeaders = {
date: initialDate,
age: 0,
'cache-control': 'public, max-age=1',
'last-modified': initialDate
};
const scope = nock(data.defaultBaseUrl)
.get(`/${defaultEndpoint}`)
.reply(200, data.dummyResponse, responseHeaders);
const response = await onionoo[defaultEndpoint]();
t.deepEqual(response.body, data.dummyResponse);
t.truthy(scope.isDone());
const requestHeaders = {
'if-modified-since': initialDate
};
const responseHeaders304 = {
date: new Date().toUTCString(),
age: 0,
'cache-control': 'public, max-age=10',
'last-modified': initialDate
};
const scope2 = nock(data.defaultBaseUrl, {requestHeaders})
.get(`/${defaultEndpoint}`)
.reply(304, null, responseHeaders304);
const response2 = await delay(2000).then(onionoo[defaultEndpoint]);
t.deepEqual(response2.body, data.dummyResponse);
t.truthy(scope2.isDone());
const cachedResponse = await onionoo[defaultEndpoint]();
t.deepEqual(cachedResponse.body, data.dummyResponse);
}); });

Loading…
Cancel
Save