Skip to content

Commit

Permalink
Initial version.
Browse files Browse the repository at this point in the history
  • Loading branch information
Anthony Frasso committed Aug 12, 2016
1 parent 72fed3b commit fa28369
Show file tree
Hide file tree
Showing 6 changed files with 263 additions and 0 deletions.
57 changes: 57 additions & 0 deletions .jscsrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
{
"disallowEmptyBlocks": true,
"disallowFunctionDeclarations": true,
"disallowImplicitTypeConversion": ["numeric", "boolean", "binary", "string"],
"disallowKeywordsOnNewLine": [ "else" ],
"disallowMixedSpacesAndTabs": true,
"disallowMultipleLineBreaks": true,
"disallowNewlineBeforeBlockStatements": true,
"disallowPaddingNewlinesInBlocks": true,
"disallowQuotedKeysInObjects": true,
"disallowSpaceAfterObjectKeys": true,
"disallowSpaceAfterPrefixUnaryOperators": [ "++", "--", "+", "-", "~", "!" ],
"disallowSpaceBeforeComma": true,
"disallowSpaceBeforePostfixUnaryOperators": [ "++", "--" ],
"disallowSpaceBeforeSemicolon": true,
"disallowSpacesInAnonymousFunctionExpression": {
"beforeOpeningRoundBrace": true
},
"disallowSpacesInCallExpression": true,
"disallowSpacesInsideParentheses": true,
"disallowTabs": true,
"disallowTrailingComma": true,
"disallowTrailingWhitespace": true,
"maximumLineLength": 120,
"requireBlocksOnNewline": true,
"requireCamelCaseOrUpperCaseIdentifiers": true,
"requireCapitalizedConstructorsNew": true,
"requireCapitalizedConstructors": true,
"requireCommaBeforeLineBreak": true,
"requireCurlyBraces": [ "if", "else", "for", "while", "do", "try", "catch" ],
"requireDotNotation": true,
"requireImportAlphabetized": true,
"requireLineBreakAfterVariableAssignment": true,
"requireLineFeedAtFileEnd": true,
"requireMatchingFunctionName": true,
"requireOperatorBeforeLineBreak": true,
"requireSemicolons": true,
"requireSpaceAfterBinaryOperators": true,
"requireSpaceAfterComma": true,
"requireSpaceAfterKeywords": [ "if", "else", "for", "while", "do", "switch", "try", "catch" ],
"requireSpaceAfterLineComment": true,
"requireSpaceBeforeBinaryOperators": true,
"requireSpaceBeforeBlockStatements": true,
"requireSpaceBetweenArguments": true,
"requireSpacesInAnonymousFunctionExpression": {
"beforeOpeningCurlyBrace": true
},
"requireSpacesInForStatement": true,
"requireSpacesInFunctionDeclaration": { "beforeOpeningCurlyBrace": true },
"requireSpacesInFunctionExpression": { "beforeOpeningCurlyBrace": true },
"requireSpacesInsideArrayBrackets": "all",
"requireSpacesInsideObjectBrackets": "all",
"validateCommentPosition": { "position": "above", "allExcept": [ "pragma" ] },
"validateIndentation": 2,
"validateParameterSeparator": ", ",
"validateQuoteMarks": { "mark": "\"", "escape": true }
}
6 changes: 6 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"expr": true,
"esversion": 6,
"futurehostile": true,
"predef": [ "-Promise" ]
}
14 changes: 14 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
language: node_js
node_js:
- "6"
- "5"
script:
- grunt
deploy:
provider: npm
api_key:
secure: lUv5YgSSvuW8OfTHkvkRaq6wyyobzy1ewQ2XsBIINxYUnqBL3iN+UNvuqJ2dZibwuaXbBB0HXT5/w1HQAykvTjeXi9VwrFJk6v1IoS1tUIm48Ln4OBY67UkYIiZFEAl3Sh73yxUxbQmSjJP9vtV877t5eeb0cn4cTaRhUVBHpLU6XXeR8Sl343C6Xwy+EJ+ZP4SRcJ/pu/Ze1uvC5ys9BN1o1L8anFeNDJL58BMmExy3WcAKUyKPl7jSzMfhTeGT1AjH2BVuOyPsansqLqxxKVtSOGvxaQkH/ubhwKdQOVMUZ/hFYFy+O8aIvwTNUpHvhQRBw6KIUH7QBLONVDUcdBn6xQWebdq6551ujEMyB9WTxs5MaOzHEbEx4prnSuNBg3MD+0Zzh8qugn5rtjG/T0YvhrsMp6o5rYpcmceCBY6PiLGnCxTetqgzfIjNt+RtU7dwDFWBHIn7sRZa/CyQ4zD0Ohq2c0KHevp2Pbr85cS7J9EBsC+qbbsYXWwzgqSNVa60cCYAiGcQuRzBkJDOu4wi8/til6hLhhXkENj0ei4lgJBqnRwsHF6R3vl9UyNtEY243xAwfEmpVT99dw1khzxLWpkGvJq7+zrFUJYo8XpSnRmNz9HLgip70NN9DU1CB500Ym7qtmARaTDRJQXkgBTqKm7KdGOAXSZ0Djv69Mg=
on:
tags: true
branch: master
node: "5"
46 changes: 46 additions & 0 deletions Gruntfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
// Loads values from package.json so they can be used within this file.
pkg: grunt.file.readJSON("package.json"),
// JSHint checks code quality.
jshint: {
files: [ "src/**/*.js", "!node_modules/**", "!coverage/**" ],
options: {
jshintrc: true
}
},
// JSCS checks code style against standards.
jscs: {
main: [ "src/**/*.js", "!node_modules/**", "!coverage/**" ]
},
// Mocha is a test framework for node.js.
mochaTest: {
options: {
timeout: 30000
},
test: {
src: [ "src/test/**/*.js" ]
}
},
// Istanbul is a code coverage tool for use with Mocha.
mocha_istanbul: { // jscs:ignore requireCamelCaseOrUpperCaseIdentifiers
coverage: {
src: "src/test/**/*.js",
options: {
istanbulOptions: [ "--include-all-sources" ]
}
}
}
});

grunt.loadNpmTasks("grunt-contrib-jshint");
grunt.loadNpmTasks("grunt-jscs");
grunt.loadNpmTasks("grunt-mocha-test");
grunt.loadNpmTasks("grunt-mocha-istanbul");

// Default task(s).
grunt.registerTask("default", [ "jshint", "jscs", "test", "coverage" ]);
grunt.registerTask("test", [ "mochaTest:test" ]);
grunt.registerTask("coverage", [ "mocha_istanbul" ]);
};
33 changes: 33 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{
"name": "cache-manager-express-mw",
"version": "0.0.1",
"description": "Middleware for Express that uses cache-manager to add a caching layer in front of your application.",
"main": "src/index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/afrasso/cache-manager-express-mw.git"
},
"keywords": [
"cache-manager",
"express",
"cache"
],
"author": "Anthony Frasso",
"license": "MIT",
"bugs": {
"url": "https://github.com/afrasso/cache-manager-express-mw/issues"
},
"homepage": "https://github.com/afrasso/cache-manager-express-mw#readme",
"devDependencies": {
"grunt": "^1.0.1",
"grunt-contrib-jshint": "^1.0.0",
"grunt-jscs": "^3.0.1",
"grunt-mocha-istanbul": "^5.0.2",
"grunt-mocha-test": "^0.12.7",
"mocha": "^3.0.2",
"mocha-istanbul": "^0.3.0"
}
}
107 changes: 107 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
var _ = require("lodash"),
Promise = require("bluebird");

var caching = function(cache, options) {
var prefix = options && options.prefix ? `${options.prefix}:` : "";
var cacheControlAccessibility =
options && options.cacheControlAccessibility ? options.cacheControlAccessibility : "public";

var getMaxAge = function(res) {
var cacheControlHeader = res.get("Cache-Control");
if (!cacheControlHeader) {
return;
}
var match = cacheControlHeader.match(/.*max-age=(\d+).*/);
if (!match || match.length < 2) {
return;
}
var maxAge = parseInt(match[1]);
return maxAge;
};

var getValue = function(key) {
return new Promise(function(resolve, reject) {
cache.get(key, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};

var getTtl = function(key) {
if (typeof cache.ttl !== "function") {
return Promise.resolve();
}
return new Promise(function(resolve, reject) {
cache.ttl(key, function(err, result) {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
};

var setCacheControlHeader = function(res, ttl) {
if (ttl) {
res.set("Cache-Control", `${cacheControlAccessibility}, max-age=${ttl}`);
}
};

var handleCacheHit = function(res, key, value) {
getTtl(key)
.then(ttl => setCacheControlHeader(res, ttl))
.then(x => {
// This is dumb, but it results in a prettier JSON format
try {
var obj = JSON.parse(value.body);
res.status(value.statusCode).json(obj);
} catch (error) {
res.status(value.statusCode).send(value.body);
}
});
};

var handleCacheMiss = function(res, key) {
var send = res.send.bind(res);

res.send = function(body) {
var ret = send(body);

if (/^2/.test(res.statusCode)) {
cache.set(key, { statusCode: res.statusCode, body: body }, { ttl: getMaxAge(res) });
}

return ret;
};
};

var middleware = function(req, res, next) {
if (!cache) {
next();
return;
}

var query = _.assign({ }, options.defaults, req.query);
var sortedQueryString = _(query).keys().sortBy().map(key => `${key}=${query[key]}`).join("&");
var key = `${prefix}${req.method}:${req.path}?${sortedQueryString}`;

getValue(key)
.then(value => {
if (value) {
handleCacheHit(res, key, value);
} else {
handleCacheMiss(res, key);
next();
}
});
};

return middleware;
};

module.exports = caching;

0 comments on commit fa28369

Please sign in to comment.