Merge branch 'master' into master
diff --git a/.gitignore b/.gitignore
index 3c0ac2f..23d4050 100644
--- a/.gitignore
+++ b/.gitignore
@@ -34,4 +34,5 @@
.vscode
.idea
-.DS_Store
\ No newline at end of file
+.DS_Store
+env.sh
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e49a3ea
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,4 @@
+language: node_js
+node_js:
+- '6.3.1'
+
diff --git a/README.md b/README.md
index 9311c2f..36f5d8b 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
#Plugins
+Build Status [](https://travis-ci.org/apigee/microgateway-plugins)
+
##Overview
This repo contains plugins for the [microgateway-core project](https://github.com/apigee/microgateway-core).
@@ -55,4 +57,4 @@
```javascript
next([err])
-```
\ No newline at end of file
+```
diff --git a/accumulate-request/index.js b/accumulate-request/index.js
index b1e7643..d162087 100644
--- a/accumulate-request/index.js
+++ b/accumulate-request/index.js
@@ -14,7 +14,6 @@
* should only be used when it is known that request/response bodies are small.
*/
module.exports.init = function(config, logger, stats) {
-
function accumulate(req, data) {
if (!req._chunks) req._chunks = [];
req._chunks.push(data);
@@ -29,11 +28,13 @@
onend_request: function(req, res, data, next) {
if (data && data.length > 0) accumulate(req, data);
- var content = Buffer.concat(req._chunks);
+ var content = null;
+ if (req._chunks && req._chunks.length) {
+ content = Buffer.concat(req._chunks);
+ }
delete req._chunks;
next(null, content);
}
-
};
}
diff --git a/accumulate-response/index.js b/accumulate-response/index.js
index 8be60af..1ff26a2 100644
--- a/accumulate-response/index.js
+++ b/accumulate-response/index.js
@@ -29,7 +29,10 @@
onend_response: function(req, res, data, next) {
if (data && data.length > 0) accumulate(res, data);
- var content = Buffer.concat(res._chunks);
+ var content = null;
+ if(res._chunks && res._chunks.length) {
+ content = Buffer.concat(res._chunks);
+ }
delete res._chunks;
next(null, content);
}
diff --git a/analytics/index.js b/analytics/index.js
index 23c57c7..175ca09 100644
--- a/analytics/index.js
+++ b/analytics/index.js
@@ -9,6 +9,15 @@
record.apiproxy = res.proxy.name;
record.apiproxy_revision = res.proxy.revision;
}
+
+ if(config.mask_request_uri) {
+ record.request_uri = config.mask_request_uri;
+ }
+
+ if(config.mask_request_path) {
+ record.request_path = config.mask_request_path;
+ }
+
cb(null, record);
};
diff --git a/cache/index.js b/cache/index.js
deleted file mode 100644
index e7c08b0..0000000
--- a/cache/index.js
+++ /dev/null
@@ -1,67 +0,0 @@
-'use strict';
-
-var cache = require('volos-cache-memory');
-var async = require('async');
-var debug = require('debug')('gateway:cache');
-
-var getKey = function (req) {
- return req.token.application_name;
-}
-var caches = [];
-
-var middleware = function (req, res, next) {
- if (!req.token
- || !req.token.api_product_list
- || !req.token.api_product_list.length
- ) {
- return next();
- }
-
- debug('cache checking products', req.token.api_product_list);
-
- req.originalUrl = req.originalUrl || req.url; // emulate connect
-
- var found = false;
- // this is arbitrary, but not sure there's a better way?
- async.eachSeries(req.token.api_product_list,
- function (productName, cb) {
- if(!found) {
- var cache = caches[productName];
- executeDefaultCache(cache, req, res, cb);
- found = true;
- }else{
- cb();
- }
- },
- function (err) {
- next(err);
- }
- );
-}
-
-var executeDefaultCache = function (cache, req, res, cb) {
- cb();
-};
-
-module.exports.init = function (config, logger, stats) {
- var options = {
- key: getKey
- };
- Object.keys(config).forEach(function (productName) {
- var product = config[productName];
- if (!product.ttl) {
- debug('could not load cache for ' + productName + ' because ttl is missing', product)
- return; // skip non-cache config
- }
- var cacheInstance = cache.create(config[productName]);
- caches[productName] = cacheInstance;
- debug('created cache for', productName);
- debug('caches are ', caches)
- });
-
- return {
- onrequest: function (req, res, next) {
- middleware(req, res, next);
- }
- };
-};
\ No newline at end of file
diff --git a/cache/package.json b/cache/package.json
deleted file mode 100644
index 7516753..0000000
--- a/cache/package.json
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "name": "edgemicro-plugin-cache",
- "version": "1.0.0",
- "description": "caching plugin for the gateway",
- "main": "index.js",
- "scripts": {
- "test": "mocha tests"
- },
- "author": "sfeldman@apigee.com",
- "dependencies": {
- "async": "^1.5.2",
- "debug": "^2.2.0",
- "volos-cache-memory": "^0.10.0"
- },
- "devDependencies": {
- "mocha": "^2.4.5",
- "should": "^8.2.1"
- }
-}
diff --git a/cache/tests/testIndex.js b/cache/tests/testIndex.js
deleted file mode 100644
index a55c654..0000000
--- a/cache/tests/testIndex.js
+++ /dev/null
@@ -1,12 +0,0 @@
-var should = require('should');
-var assert = require('assert');
-var plugin = require('../index');
-describe('construct',function(){
- it('should work',function(done){
- var req = plugin.init();
- req.onrequest(null,null,function(err){
- should(err).be.null
- done();
- })
- }) ;
-});
\ No newline at end of file
diff --git a/index.js b/index.js
index 01f90dd..fd4f20a 100644
--- a/index.js
+++ b/index.js
@@ -4,7 +4,6 @@
'accumulate-request':require('./accumulate-request'),
'accumulate-response':require('./accumulate-response'),
analytics:require('./analytics'),
- cache:require('./cache'),
'header-uppercase':require('./header-uppercase'),
oauth:require('./oauth'),
quota:require('./quota'),
@@ -13,4 +12,6 @@
'transform-uppercase':require('./transform-uppercase'),
accesscontrol:require('./accesscontrol'),
eurekaclient:require('./eurekaclient')
+ json2xml: require('./json2xml'),
+ healthcheck: require('./healthcheck')
}
diff --git a/json2xml/index.js b/json2xml/index.js
index 2b78e72..8548727 100644
--- a/json2xml/index.js
+++ b/json2xml/index.js
@@ -74,14 +74,14 @@
//if plugin is disabled don't process headers.
if (!disable) {
- if (method === "get" && acceptType === "application/xml") {
+ if (method === "get" && acceptType.indexOf("application/xml") > -1) {
responseXML = true;
- } else if (method === "get" && acceptType === "application/json") {
+ } else if (method === "get" && acceptType.indexOf("application/json") > -1) {
responseJSON = true;
- } else if (method !== "get" && contentType === "application/json") {
+ } else if (method !== "get" && contentType.indexOf("application/json") > -1) {
requestJSON = true;
responseJSON = true;
- } else if (method !== "get" && contentType === "application/xml") {
+ } else if (method !== "get" && contentType.indexOf("application/xml") > -1) {
requestXML = true;
responseXML = true;
}
@@ -156,9 +156,9 @@
if (data && data.length > 0 && disable == false) accumulateResponse(res, data);
var contentType = res.getHeader('content-type');
- if (contentType === "application/xml") {
+ if (contentType.indexOf("application/xml") > -1) {
responseIsXML = true;
- } else if (contentType === "application/json") {
+ } else if (contentType.indexOf("application/json") > -1) {
responseIsJSON = true;
}
@@ -191,4 +191,4 @@
}
}
};
-}
\ No newline at end of file
+}
diff --git a/package.json b/package.json
index 09aa659..a068b50 100644
--- a/package.json
+++ b/package.json
@@ -1,22 +1,25 @@
{
"name": "microgateway-plugins",
- "version": "2.0.4",
+ "version": "2.3.5",
"description": "Plugins for Apige Edge Microgateway",
"main": "index.js",
"scripts": {
- "test": "bin/test"
+ "test": "./node_modules/.bin/mocha -R spec"
},
"dependencies": {
"async": "^1.4.2",
"debug": "^2.2.0",
- "volos-spikearrest-memory": "^0.10.0",
- "volos-quota-memory": "^0.11.0",
- "volos-quota-apigee": "^0.13.0",
+ "js2xmlparser": "^2.0.2",
"jsonwebtoken": "^5.0.1",
"lodash": "^3.10.1",
"request": "^2.65.0",
+ "toobusy-js": "^0.5.1",
+ "volos-analytics-apigee": "^0.4.0",
"volos-cache-memory": "^0.10.0",
- "volos-analytics-apigee": "^0.4.0"
+ "volos-quota-apigee": "^0.13.0",
+ "volos-quota-memory": "^0.11.0",
+ "volos-spikearrest-memory": "^0.10.0",
+ "xml2js": "^0.4.17"
},
"devDependencies": {
"js-yaml": "^3.4.2",
diff --git a/quota-memory/index.js b/quota-memory/index.js
index 6d0579d..337bf6e 100644
--- a/quota-memory/index.js
+++ b/quota-memory/index.js
@@ -5,7 +5,6 @@
var debug = require('debug')('gateway:quota');
module.exports.init = function(config, logger, stats) {
-
var quotas = {}; // productName -> connectMiddleware
var options = {
@@ -14,7 +13,10 @@
Object.keys(config).forEach(function(productName) {
var product = config[productName];
- if (!product.uri && !product.key && !product.secret && !product.allow && !product.interval) return; // skip non-quota config
+ if (!product.uri && !product.key && !product.secret && !product.allow && !product.interval) {
+ return; // skip non-quota config
+ }
+
var quota = Quota.create(config[productName]);
quotas[productName] = quota.connectMiddleware().apply(options);
debug('created quota for', productName);
diff --git a/quota/index.js b/quota/index.js
index 0830ecf..7e377fb 100644
--- a/quota/index.js
+++ b/quota/index.js
@@ -9,12 +9,19 @@
var quotas = {}; // productName -> connectMiddleware
var options = {
- key: function(req) { return req.token.application_name; }
+ key: function(req) {
+ return req.token.application_name;
+ }
};
Object.keys(config).forEach(function(productName) {
var product = config[productName];
- if (!product.uri && !product.key && !product.secret && !product.allow && !product.interval) return; // skip non-quota config
+ if (!product.uri && !product.key && !product.secret && !product.allow && !product.interval) {
+ // skip non-quota config
+ console.log('Not enough info for configuring quota');
+ return;
+ }
+
var quota = Quota.create(config[productName]);
quotas[productName] = quota.connectMiddleware().apply(options);
debug('created quota for', productName);
diff --git a/test/accumulate-request-test.js b/test/accumulate-request-test.js
new file mode 100644
index 0000000..1983c25
--- /dev/null
+++ b/test/accumulate-request-test.js
@@ -0,0 +1,107 @@
+const accumulateRequest = require('../accumulate-request/index');
+const assert = require('assert');
+
+describe('accumulate request plugin', () => {
+ var plugin = null;
+
+ beforeEach(() => {
+ var config = {};
+ var logger = {};
+ var stats = {};
+
+ plugin = accumulateRequest.init.apply(null, [config, logger, stats]);
+ });
+
+ it('exposes an ondata_request handler', () => {
+ assert.ok(plugin.ondata_request);
+ });
+
+ it('exposes an onend_request handler', () => {
+ assert.ok(plugin.onend_request);
+ });
+
+ it('calls back with two null function arguments in the ondata_request handler', (done) => {
+ var cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ done();
+ }
+
+
+ plugin.ondata_request.apply(null, [{}, {}, Buffer.alloc(5, 'a'), cb]);
+ });
+
+ it('will collect all buffers provided to ondata_request handler, concatenate them, and return them as a single buffer', (done) => {
+ var desiredResult = 'aaaaaaaaaaaaaaa';
+
+ var ondata_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ assert.ok(req._chunks);
+ }
+
+ var onend_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result.toString(), desiredResult);
+ done();
+ }
+
+ var req = {};
+
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), ondata_cb]);
+
+ plugin.onend_request.apply(null, [req, {}, null, onend_cb]);
+ });
+
+ it('will append data included in the end call to the buffer', (done) => {
+ var desiredResult = 'aaaaaaaaaaaaaaaaaaaa';
+
+ var ondata_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ assert.ok(req._chunks);
+ }
+
+ var onend_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result.toString(), desiredResult);
+ done();
+ }
+
+ var req = {};
+
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), ondata_cb]);
+
+ plugin.onend_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), onend_cb]);
+ });
+
+ it('will create a req._chunks object on the request object', (done) => {
+ var req = {};
+ var cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ assert.ok(req._chunks);
+ assert.equal(req._chunks.toString(), 'aaaaa');
+ done();
+ }
+
+ plugin.ondata_request.apply(null, [req, {}, Buffer.alloc(5, 'a'), cb]);
+ });
+
+ it('will call the callback with null if no data events are provided.', (done) => {
+ var onend_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ done();
+ }
+
+ var req = {};
+
+ plugin.onend_request.apply(null, [req, {}, null, onend_cb]);
+
+ });
+})
diff --git a/test/accumulate-response-test.js b/test/accumulate-response-test.js
new file mode 100644
index 0000000..0a13840
--- /dev/null
+++ b/test/accumulate-response-test.js
@@ -0,0 +1,107 @@
+const accumulateResponse = require('../accumulate-response/index');
+const assert = require('assert');
+
+describe('accumulate response plugin', () => {
+ var plugin = null;
+
+ beforeEach(() => {
+ var config = {};
+ var logger = {};
+ var stats = {};
+
+ plugin = accumulateResponse.init.apply(null, [config, logger, stats]);
+ });
+
+ it('exposes an ondata_response handler', () => {
+ assert.ok(plugin.ondata_response);
+ });
+
+ it('exposes an onend_response handler', () => {
+ assert.ok(plugin.onend_response);
+ });
+
+ it('calls back with two null function arguments in the ondata_response handler', (done) => {
+ var cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ done();
+ }
+
+
+ plugin.ondata_response.apply(null, [{}, {}, Buffer.alloc(5, 'a'), cb]);
+ });
+
+ it('will collect all buffers provided to ondata_response handler, concatenate them, and return them as a single buffer', (done) => {
+ var desiredResult = 'aaaaaaaaaaaaaaa';
+
+ var ondata_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ assert.ok(res._chunks);
+ }
+
+ var onend_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result.toString(), desiredResult);
+ done();
+ }
+
+ var res = {};
+
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), ondata_cb]);
+
+ plugin.onend_response.apply(null, [{}, res, null, onend_cb]);
+ });
+
+ it('will append data included in the end call to the buffer', (done) => {
+ var desiredResult = 'aaaaaaaaaaaaaaaaaaaa';
+
+ var ondata_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ assert.ok(res._chunks);
+ }
+
+ var onend_cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result.toString(), desiredResult);
+ done();
+ }
+
+ var res = {};
+
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), ondata_cb]);
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), ondata_cb]);
+
+ plugin.onend_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), onend_cb]);
+ });
+
+ it('will create a res._chunks object on the request object', (done) => {
+ var res = {};
+ var cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ assert.ok(res._chunks);
+ assert.equal(res._chunks.toString(), 'aaaaa');
+ done();
+ }
+
+ plugin.ondata_response.apply(null, [{}, res, Buffer.alloc(5, 'a'), cb]);
+ });
+
+ it('will callback with null if no response body is present in targetResponse', (done) => {
+ var res = {};
+ var cb = (err, result) => {
+ assert.equal(err, null);
+ assert.equal(result, null);
+ done();
+ }
+
+ plugin.onend_response.apply(null, [{}, res, null, cb]);
+
+ });
+
+})
diff --git a/test/quota-test.js b/test/quota-test.js
new file mode 100644
index 0000000..e8a2a31
--- /dev/null
+++ b/test/quota-test.js
@@ -0,0 +1,93 @@
+const quota = require('../quota/index');
+const assert = require('assert');
+
+var exampleConfig = {
+ EdgeMicroTestProduct: {
+ allow: process.env.QUOTA_ALLOW,
+ interval: Number(process.env.QUOTA_INTERVAL),
+ timeUnit: process.env.QUOTA_TIMEUNIT,
+ bufferSize: process.env.QUOTA_BUFFERSIZE,
+ uri: process.env.QUOTA_URI,
+ key: process.env.QUOTA_KEY,
+ secret: process.env.QUOTA_SECRET
+ }
+}
+
+describe('quota plugin', () => {
+ var plugin = null;
+
+ beforeEach(() => {
+ var logger = {};
+ var stats = {};
+
+ plugin = quota.init.apply(null, [exampleConfig, logger, stats]);
+
+ });
+
+ it('exposes an onrequest handler', () => {
+ assert.ok(plugin.onrequest);
+ });
+
+ it('will quota limit after 3 API calls', (done) => {
+ var count = 0;
+ var onrequest_cb = (err) => {
+ count++;
+ if(count == 4) {
+ assert.equal(count, 4);
+ assert.equal(err.message, 'exceeded quota');
+ done();
+ }
+ };
+
+ var req = {
+ token: {
+ application_name: '0e7762f4-ea67-4cc1-ae4a-21598c35b18f',
+ api_product_list: ['EdgeMicroTestProduct']
+ }
+ }
+
+ var res = {
+ headers: {},
+ setHeader: (key, val) => {
+ res.headers[key] = val;
+ }
+ }
+
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ });
+
+ it('will not quota limit before 3 API calls', (done) => {
+ var count = 0;
+ var onrequest_cb = (err) => {
+ count++;
+ if(count == 3) {
+ assert.equal(count, 3);
+ assert.ok(!(err instanceof Error));
+ done();
+ }
+ };
+
+ var req = {
+ token: {
+ application_name: '0e7762f4-ea67-4cc1-ae4a-21598c35b18f',
+ api_product_list: ['EdgeMicroTestProduct']
+ }
+ }
+
+ var res = {
+ headers: {},
+ setHeader: (key, val) => {
+ res.headers[key] = val;
+ }
+ }
+
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ });
+});
+
+
diff --git a/test/spike-arrest-test.js b/test/spike-arrest-test.js
new file mode 100644
index 0000000..409eb77
--- /dev/null
+++ b/test/spike-arrest-test.js
@@ -0,0 +1,48 @@
+const spikeArrest = require('../spikearrest/index');
+const assert = require('assert');
+
+describe('spike arrest plugin', () => {
+ var plugin = null;
+
+ beforeEach(() => {
+ var config = {
+ timeUnit: 'minute',
+ bufferSize: 0,
+ allow: 1
+ };
+ var logger = {};
+ var stats = {};
+
+ plugin = spikeArrest.init.apply(null, [config, logger, stats]);
+ });
+
+ it('exposes an onrequest handler', () => {
+ assert.ok(plugin.onrequest);
+ });
+
+ it('will accept a request', (done) => {
+ var onrequest_cb = () => {
+ done();
+ };
+
+ var req = {};
+ var res = {};
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ });
+
+ it('will reject a requests that come in too fast', (done) => {
+ var onrequest_cb = () => {
+ };
+
+ var onrequest_cb_second = (req, res, next) => {
+ assert.ok(req instanceof Error);
+ assert.equal(req.message, 'SpikeArrest engaged');
+ done();
+ };
+
+ var req = {};
+ var res = {};
+ plugin.onrequest.apply(null, [req, res, onrequest_cb]);
+ plugin.onrequest.apply(null, [req, res, onrequest_cb_second]);
+ });
+});