superagent-cache
Advanced tools
Comparing version 0.2.1 to 1.0.0
{ | ||
"name": "superagent-cache", | ||
"version": "0.2.1", | ||
"version": "1.0.0", | ||
"description": "Superagent with flexible built-in caching.", | ||
@@ -8,3 +8,3 @@ "main": "superagentCache.js", | ||
"superagent": "1.1.0", | ||
"cache-service-cache-module": "1.0.0" | ||
"cache-service-cache-module": "1.1.0" | ||
}, | ||
@@ -11,0 +11,0 @@ "devDependencies": { |
@@ -9,3 +9,3 @@ # superagent-cache | ||
Require and instantiate superagent-cache as follows to get the [default configuration](#what-does-the-default-configuraiton-give-me): | ||
Require and instantiate superagent-cache as follows to get the [default configuration](#what-does-the-default-configuration-give-me): | ||
```javascript | ||
@@ -73,3 +73,3 @@ var superagent = require('superagent-cache')(); | ||
You get the 'default configurations' when you don't provide any params to the `require('superagent-cache')()` command. This will return a fresh instance of `superagent` and bundle an instance of [cacheModule](https://github.com/jpodwys/cache-service-cache-module) for storing data. `cacheModule` is a slim, in-memory cache. | ||
You get the 'default configuration' when you don't provide any params to the `require('superagent-cache')()` command. This will return a fresh instance of `superagent` and bundle an instance of [cacheModule](https://github.com/jpodwys/cache-service-cache-module) for storing data. `cacheModule` is a slim, in-memory cache. | ||
@@ -235,3 +235,3 @@ # How Do I Use a Custom Configuration? | ||
Use this function when you need to override all of your caches' `defaultExpiration` properties (set via cache-service) for a particular cache entry. | ||
Use this function when you need to override your `cache`'s `defaultExpiration` property for a particular cache entry. | ||
@@ -258,2 +258,12 @@ #### Arguments | ||
## .backgroundRefresh(value) | ||
> See the [Using Background Refresh](#using-background-refresh) section for more information. | ||
Tell the underlying `cache` provided in the `require` command to enable background refresh for the generated key and value. If a function is provided, it will use the function, if a boolean is provided, it will use the boolean, if nothing is provided, it will default to true. | ||
#### Arguments | ||
* value: boolean || function || undefined, default: true | ||
## ._end(callback (err, response)) | ||
@@ -277,2 +287,73 @@ | ||
# Using Background Refresh | ||
With a typical cache setup, you're left to find the perfect compromise between having a long expiration so that users don't have to suffer through the worst case load time, and a short expiration so data doesn't get stale. `superagent-cache` eliminates the need to worry about users suffering through the longest wait time by automatically refreshing keys for you. | ||
#### How do I turn it on? | ||
By default, background refresh is off. It will turn itself on the first time you use the `.backgroundRefresh)` chainable. | ||
#### Setup | ||
`superagent-cache` relies on the background refresh feature of the `cache` param you pass into the `require` command. When you use the `.backgroundRefresh()` chainable, `superagent-cache` passes the provided value into `cache`. This means that if you're using `cache-service`, you almost certainly want `cache-service`'s `writeToVolatileCaches` property set to `true` (it defaults to `true`) so that the data set by background refresh will propogate forward to earlier caches (`cache-service` ONLY background refreshses to the final cache passed to it) | ||
#### Configure | ||
If desired, configure the following properties within `cache`: | ||
* `backgroundRefreshInterval` | ||
* `backgroundRefreshMinTtl` | ||
* `backgroundRefreshIntervalCheck` | ||
#### Use | ||
Background refresh is exposed via the `.backgroundRefresh()` chainable. | ||
When `true` or no param is passed to `.backgroundRefresh()`, it will generate a `superagent` call identical to the one that triggered it and pass that to `cache`. | ||
```javascript | ||
superagent | ||
.get(uri) | ||
.backgroundRefresh() | ||
.end(function (err, response){ | ||
//Response will no be refreshed in the background | ||
} | ||
); | ||
``` | ||
When a function is passed, it will use that function. Read on for background refresh function requirements. | ||
```javascript | ||
var refresh = function(key, cb){ | ||
var response = goGetData(); | ||
cb(null, response); | ||
} | ||
superagent | ||
.get(uri) | ||
.backgroundRefresh(refresh) | ||
.end(function (err, response){ | ||
//Response will no be refreshed in the background | ||
} | ||
); | ||
``` | ||
When `false` is passed, it will do nothing. | ||
#### The Refresh Param | ||
###### refresh(key, cb(err, response)) | ||
* key: type: string: this is the key that is being refreshed | ||
* cb: type: function: you must trigger this function to pass the data that should replace the current key's value | ||
The `refresh` param MUST be a function that accepts `key` and a callback function that accepts `err` and `response` as follows: | ||
```javascript | ||
var refresh = function(key, cb){ | ||
var response = goGetData(); | ||
cb(null, response); | ||
} | ||
``` | ||
# More Usage Examples | ||
@@ -332,11 +413,1 @@ | ||
* `superagent-cache` is now more flexible, allowing usage of any cache that matches `cache-service`'s API. To make it lighter, then, the hard dependency on `cache-service` was replaced with the much lighter `cacheModule`. As a result, `superagent-cache` can no longer construct a `cache-service` instance for you. If you wish to use `cache-service`, you must instantiate it externally and hand it in as `cache`--the second param in the `require` command. | ||
# Roadmap | ||
* ~~Make it so superagent-cache's `.end()` callback function does not require an `err` param~~ | ||
* ~~Make sure that `resetProps()` gets called when `._end()` is called directly~~ | ||
* ~~Add unit tests for the various ways headers can be added to calls~~ | ||
* ~~Add the 'More Usage Examples' section~~ | ||
* ~~Remove the hard dependency on `superagent-cache` and allow users to use any cache that matched `superagent-cache`'s API~~ | ||
* Add unit tests for the other points above | ||
* Add thorough comments and param descriptions to the code |
@@ -0,1 +1,7 @@ | ||
/** | ||
* superagentCache constructor | ||
* @constructor | ||
* @param {superagent instance} agent (optional) | ||
* @param {cache module} cache (optional) | ||
*/ | ||
module.exports = function(agent, cache){ | ||
@@ -17,2 +23,6 @@ | ||
/** | ||
* Whether to execute an http query if the cache does not have the generated key | ||
* @param {boolean} doQuery | ||
*/ | ||
Request.prototype.doQuery = function(doQuery){ | ||
@@ -23,2 +33,6 @@ props.doQuery = doQuery; | ||
/** | ||
* Remove the given params from the query object after executing an http query and before generating a cache key | ||
* @param {array of strings} pruneParams | ||
*/ | ||
Request.prototype.pruneParams = function(pruneParams){ | ||
@@ -29,2 +43,6 @@ props.pruneParams = pruneParams; | ||
/** | ||
* Remove the given options from the headers object after executing an http query and before generating a cache key | ||
* @param {boolean} pruneOptions | ||
*/ | ||
Request.prototype.pruneOptions = function(pruneOptions){ | ||
@@ -35,2 +53,6 @@ props.pruneOptions = pruneOptions; | ||
/** | ||
* Execute some logic on superagent's http response object before caching and returning it | ||
* @param {function} prune | ||
*/ | ||
Request.prototype.prune = function(prune){ | ||
@@ -41,2 +63,6 @@ props.prune = prune; | ||
/** | ||
* Retrieve a top-level property from superagent's http response object before to cache and return | ||
* @param {string} responseProp | ||
*/ | ||
Request.prototype.responseProp = function(responseProp){ | ||
@@ -47,2 +73,6 @@ props.responseProp = responseProp; | ||
/** | ||
* Set an expiration for this key that will override the configured cache's default expiration | ||
* @param {integer} expiration (seconds) | ||
*/ | ||
Request.prototype.expiration = function(expiration){ | ||
@@ -53,2 +83,6 @@ props.expiration = expiration; | ||
/** | ||
* Whether to cache superagent's http response object when it "empty"--especially useful with .prune and .pruneParams | ||
* @param {string} responseProp | ||
*/ | ||
Request.prototype.cacheWhenEmpty = function(cacheWhenEmpty){ | ||
@@ -59,4 +93,19 @@ props.cacheWhenEmpty = cacheWhenEmpty; | ||
/** | ||
* Initialize a background refresh for the generated key and value | ||
* @param {boolean | function} backgroundRefresh | ||
*/ | ||
Request.prototype.backgroundRefresh = function(backgroundRefresh){ | ||
props.backgroundRefresh = (typeof backgroundRefresh !== 'undefined') ? backgroundRefresh : true; | ||
return this; | ||
} | ||
/** | ||
* An alias for the .end function because I use ._end and .end for other things | ||
*/ | ||
Request.prototype.execute = Request.prototype.end; | ||
/** | ||
* Wraps the .end function so that .resetProps gets called--callable so that no caching logic takes place | ||
*/ | ||
Request.prototype._end = function(cb){ | ||
@@ -67,2 +116,6 @@ resetProps(); | ||
/** | ||
* Execute all caching and http logic | ||
* @param {function} cb | ||
*/ | ||
Request.prototype.end = function(cb){ | ||
@@ -93,3 +146,7 @@ var curProps = props; | ||
if(!isEmpty(response) || curProps.cacheWhenEmpty){ | ||
superagent.cache.set(key, response, curProps.expiration, function(){ | ||
var refresh = curProps.backgroundRefresh || null; | ||
if(typeof refresh == 'boolean'){ | ||
refresh = getBackgroundRefreshFunction(curProps); | ||
} | ||
superagent.cache.set(key, response, curProps.expiration, refresh, function (){ | ||
callbackExecutor(cb, err, response, key); | ||
@@ -127,2 +184,5 @@ }); | ||
/** | ||
* Set this.req to null so that future http calls get a branc new req object | ||
*/ | ||
Request.prototype.reset = function(){ | ||
@@ -132,2 +192,7 @@ this.req = null; | ||
/** | ||
* Generate a cache key unique to this query | ||
* @param {object} reg | ||
* @param {object} cProps | ||
*/ | ||
function keygen(req, cProps){ | ||
@@ -140,3 +205,3 @@ var cleanParams = null; | ||
} | ||
var options = (req.req && req.req._headers) ? req.req._headers : {}; | ||
var options = (req.req && req.req._headers) ? req.req._headers : null; | ||
if(cProps.pruneParams || cProps.pruneOptions){ | ||
@@ -148,10 +213,15 @@ cleanParams = (cProps.pruneParams) ? pruneObj(cloneObject(params), cProps.pruneParams) : params; | ||
nameSpace: superagent.cache.nameSpace, | ||
method: req.method, | ||
uri: req.url, | ||
params: cleanParams || params || {}, | ||
options: cleanOptions || options || {} | ||
params: cleanParams || params || null, | ||
options: cleanOptions || options || null | ||
}); | ||
} | ||
/** | ||
* Convert an array to an object | ||
* @param {array} arr | ||
*/ | ||
function arrayToObj(arr){ | ||
if(arr){ | ||
if(arr && arr.length){ | ||
var obj = {}; | ||
@@ -171,2 +241,6 @@ for(var i = 0; i < arr.length; i++){ | ||
/** | ||
* Convert a string to an object | ||
* @param {string} str | ||
*/ | ||
function stringToObj(str){ | ||
@@ -189,2 +263,8 @@ if(str){ | ||
/** | ||
* Remove properties from an object | ||
* @param {object} obj | ||
* @param {array} props | ||
* @param {boolean} isOptions | ||
*/ | ||
function pruneObj(obj, props, isOptions){ | ||
@@ -201,2 +281,6 @@ for(var i = 0; i < props.length; i++){ | ||
/** | ||
* Simplify superagent's http response object | ||
* @param {object} r | ||
*/ | ||
function gutResponse(r){ | ||
@@ -213,2 +297,6 @@ var newResponse = {}; | ||
/** | ||
* Determine whether a value is considered empty | ||
* @param {*} val | ||
*/ | ||
function isEmpty(val){ | ||
@@ -218,2 +306,6 @@ return (val === false || val === null || (typeof val == 'object' && Object.keys(val).length == 0)); | ||
/** | ||
* Return a cloneof an object | ||
* @param {object} obj | ||
*/ | ||
function cloneObject(obj){ | ||
@@ -229,2 +321,5 @@ var newObj = {}; | ||
/** | ||
* Reset superagent-cache's default query properties | ||
*/ | ||
function resetProps(){ | ||
@@ -234,2 +329,36 @@ props = {doQuery: true, cacheWhenEmpty: true}; | ||
/** | ||
* Generate a background refresh query identical to the current query | ||
* @param {object} curProps | ||
*/ | ||
function getBackgroundRefreshFunction(curProps){ | ||
return function(key, cb){ | ||
key = JSON.parse(key); | ||
var method = key.method.toLowerCase(); | ||
var request = superagent | ||
[method](key.uri) | ||
.doQuery(curProps.doQuery) | ||
.pruneParams(curProps.pruneParams) | ||
.pruneOptions(curProps.pruneOptions) | ||
.prune(curProps.prune) | ||
.responseProp(curProps.responseProp) | ||
.expiration(curProps.expiration) | ||
.cacheWhenEmpty(curProps.cacheWhenEmpty); | ||
if(key.params){ | ||
request.query(key.params) | ||
} | ||
if(key.options){ | ||
request.set(key.options); | ||
} | ||
request.end(cb); | ||
} | ||
} | ||
/** | ||
* Handle the varying number of callback output params | ||
* @param {function} cb | ||
* @param {object} err | ||
* @param {object} response | ||
* @param {string} key | ||
*/ | ||
function callbackExecutor(cb, err, response, key){ | ||
@@ -250,2 +379,8 @@ if(cb.length === 1){ | ||
/** | ||
* Instantates an exception to be thrown | ||
* @param {string} name | ||
* @param {string} message | ||
* @return {exception} | ||
*/ | ||
function exception(name, message){ | ||
@@ -256,4 +391,2 @@ this.name = name; | ||
var noop = function(){} | ||
if(!agent){ | ||
@@ -260,0 +393,0 @@ return superagent; |
var expect = require('expect'); | ||
var express = require('express'); | ||
var superagent = require('superagent'); | ||
require('../../superagentCache')(superagent); | ||
var cModule = require('cache-service-cache-module'); | ||
var cacheModule = new cModule({backgroundRefreshInterval: 500}); | ||
require('../../superagentCache')(superagent, cacheModule); | ||
@@ -123,3 +125,3 @@ var app = express(); | ||
}); | ||
}, 10); | ||
}, 20); | ||
} | ||
@@ -176,3 +178,3 @@ ); | ||
} | ||
) | ||
); | ||
}); | ||
@@ -192,3 +194,3 @@ | ||
} | ||
) | ||
); | ||
}); | ||
@@ -327,2 +329,129 @@ | ||
describe('superagentCache background refresh tests', function () { | ||
it('.get() .expiration() .end() background refresh should not work if the chainable is not used', function (done) { | ||
superagent | ||
.get('localhost:3000/one') | ||
.expiration(1) | ||
.end(function (err, response, key){ | ||
expect(typeof key).toBe('string'); | ||
expect(response.body.key).toBe('one'); | ||
setTimeout(function(){ | ||
superagent.cache.get(key, function (err, response, key){ | ||
expect(response).toBe(null); | ||
done(); | ||
}); | ||
}, 1500); | ||
} | ||
); | ||
}); | ||
it('.get() .expiration() .backgroundRefresh(true) .end() background refresh should refresh a key shortly before expiration', function (done) { | ||
superagent | ||
.get('localhost:3000/one') | ||
.expiration(1) | ||
.backgroundRefresh(true) | ||
.end(function (err, response, key){ | ||
expect(typeof key).toBe('string'); | ||
expect(response.body.key).toBe('one'); | ||
setTimeout(function(){ | ||
superagent.cache.get(key, function (err, response){ | ||
expect(response.body.key).toBe('one'); | ||
done(); | ||
}); | ||
}, 1500); | ||
} | ||
); | ||
}); | ||
it('.get() .query(string&string) .expiration() .end() background refresh should not work if the chainable is not used', function (done) { | ||
superagent | ||
.get('localhost:3000/params') | ||
.query('pruneParams=true&otherParams=false') | ||
.pruneParams(['pruneParams']) | ||
.end(function (err, response, key){ | ||
expect(response.body.pruneParams).toBe('true'); | ||
expect(response.body.otherParams).toBe('false'); | ||
expect(key.indexOf('pruneParams')).toBe(-1); | ||
expect(key.indexOf('otherParams')).toBeGreaterThan(-1); | ||
setTimeout(function(){ | ||
superagent.cache.get(key, function (err, response){ | ||
expect(response).toBe(null); | ||
done(); | ||
}); | ||
}, 1500); | ||
} | ||
); | ||
}); | ||
it('.get() .query(string&string) .expiration() .backgroundRefresh(true) .end() background refresh should refresh a key shortly before expiration', function (done) { | ||
superagent | ||
.get('localhost:3000/params') | ||
.query('pruneParams=true&otherParams=false') | ||
.pruneParams(['pruneParams']) | ||
.backgroundRefresh(true) | ||
.end(function (err, response, key){ | ||
expect(response.body.pruneParams).toBe('true'); | ||
expect(response.body.otherParams).toBe('false'); | ||
expect(key.indexOf('pruneParams')).toBe(-1); | ||
expect(key.indexOf('otherParams')).toBeGreaterThan(-1); | ||
setTimeout(function(){ | ||
superagent.cache.get(key, function (err, response){ | ||
expect(response.body.pruneParams).toBe('true'); | ||
expect(response.body.otherParams).toBe('false'); | ||
expect(key.indexOf('pruneParams')).toBe(-1); | ||
expect(key.indexOf('otherParams')).toBeGreaterThan(-1); | ||
done(); | ||
}); | ||
}, 1500); | ||
} | ||
); | ||
}); | ||
it('.get() .expiration() .backgroundRefresh(function) .end() background refresh should refresh a key shortly before expiration', function (done) { | ||
var refresh = function(key, cb){ | ||
cb(null, {body:{key: 'one'}}); | ||
} | ||
superagent | ||
.get('localhost:3000/one') | ||
.expiration(1) | ||
.backgroundRefresh(refresh) | ||
.end(function (err, response, key){ | ||
expect(typeof key).toBe('string'); | ||
expect(response.body.key).toBe('one'); | ||
setTimeout(function(){ | ||
superagent.cache.get(key, function (err, response){ | ||
expect(response.body.key).toBe('one'); | ||
done(); | ||
}); | ||
}, 1500); | ||
} | ||
); | ||
}); | ||
it('.get() .expiration() .backgroundRefresh(function) .end() background refresh should refresh a key shortly before expiration', function (done) { | ||
var refresh = function(key, cb){ | ||
cb(null, {body:{key: 'two'}}); | ||
} | ||
superagent | ||
.get('localhost:3000/one') | ||
.expiration(1) | ||
.backgroundRefresh(refresh) | ||
.end(function (err, response, key){ | ||
expect(typeof key).toBe('string'); | ||
expect(response.body.key).toBe('one'); | ||
setTimeout(function(){ | ||
superagent.cache.get(key, function (err, response){ | ||
expect(response.body.key).toBe('two'); | ||
done(); | ||
}); | ||
}, 1500); | ||
} | ||
); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
42601
752
0
408
+ Addedcache-service-cache-module@1.1.0(transitive)
- Removedcache-service-cache-module@1.0.0(transitive)