Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
trescube committed May 5, 2017
0 parents commit c8e4b28
Show file tree
Hide file tree
Showing 9 changed files with 866 additions and 0 deletions.
33 changes: 33 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
node_modules

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history
1 change: 1 addition & 0 deletions .jshintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
node_modules
22 changes: 22 additions & 0 deletions .jshintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"node": true,
"curly": true,
"eqeqeq": true,
"esversion": 6,
"freeze": true,
"immed": true,
"indent": 2,
"latedef": false,
"newcap": true,
"noarg": true,
"noempty": true,
"nonbsp": true,
"nonew": true,
"plusplus": false,
"quotmark": "single",
"undef": true,
"unused": false,
"maxparams": 4,
"maxdepth": 4,
"maxlen": 140
}
48 changes: 48 additions & 0 deletions ServiceConfiguration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
'use strict';

const _ = require('lodash');

class ServiceConfiguration {
constructor(name, config) {
if (_.isEmpty(name)) {
throw 'name is required';
}

this.name = name;
this.baseUrl = config.url;
this.timeout = config.timeout || 250;
this.retries = config.retries || 3;

}

getName() {
return this.name;
}

getBaseUrl() {
return this.baseUrl;
}

getUrl() {
return this.baseUrl;
}

getRetries() {
return this.retries;
}

getTimeout() {
return this.timeout;
}

getParameters() {
return {};
}

getHeaders() {
return {};
}

}

module.exports = ServiceConfiguration;
107 changes: 107 additions & 0 deletions http_json.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
const request = require('superagent');
const _ = require('lodash');

const ServiceConfiguration = require('./ServiceConfiguration');

function isDoNotTrack(headers) {
return _.has(headers, 'DNT') ||
_.has(headers, 'dnt') ||
_.has(headers, 'do_not_track');
}

// superagent doesn't exposed the assembled GET request, so synthesize it
function synthesizeUrl(serviceConfig, req) {
const parameters = _.map(serviceConfig.getParameters(), (value, key) => {
return `${key}=${value}`;
}).join('&');

if (parameters) {
return encodeURI(`${serviceConfig.getUrl(req)}?${parameters}`);
} else {
return serviceConfig.getUrl(req);
}

}

module.exports = function setup(serviceConfig) {
if (!(serviceConfig instanceof ServiceConfiguration)) {
throw Error('serviceConfig should be an instance of ServiceConfiguration');
}

const logger = require( 'pelias-logger' ).get( serviceConfig.getName() );

if (_.isEmpty(serviceConfig.getBaseUrl())) {
logger.warn(`${serviceConfig.getName()} service disabled`);

return (req, callback) => {
// respond with an error to any call
callback(`${serviceConfig.getName()} service disabled`);
};

}

logger.info(`using ${serviceConfig.getName()} service at ${serviceConfig.getBaseUrl()}`);
return (req, callback) => {
const headers = serviceConfig.getHeaders(req) || {};

// save off do_not_track value for later check
const do_not_track = isDoNotTrack(req.headers);

if (do_not_track) {
headers.dnt = '1';
}

request
.get(serviceConfig.getUrl(req))
.set(headers)
.timeout(serviceConfig.getTimeout())
.retry(serviceConfig.getRetries())
.accept('json')
.query(serviceConfig.getParameters(req))
.on('error', (err) => {
if (err.status) {
// first handle case where a non-200 was returned
if (do_not_track) {
logger.error(`${serviceConfig.getBaseUrl()} [do_not_track] returned status ${err.status}: ${err.response.text}`);
return callback(`${serviceConfig.getBaseUrl()} [do_not_track] returned status ${err.status}: ${err.response.text}`);
} else {
logger.error(`${synthesizeUrl(serviceConfig, req)} returned status ${err.status}: ${err.response.text}`);
return callback(`${synthesizeUrl(serviceConfig, req)} returned status ${err.status}: ${err.response.text}`);
}

}

// handle case that something catastrophic happened while contacting the server
if (do_not_track) {
logger.error(`${serviceConfig.getBaseUrl()} [do_not_track]: ${JSON.stringify(err)}`);
return callback(err);
} else {
logger.error(`${serviceConfig.getUrl(req)}: ${JSON.stringify(err)}`);
return callback(err);
}

})
.end((err, response) => {
// bail early if there's an error (shouldn't happen since it was already handled above)
if (err) {
return;
}

// if json was returned then just return it
if (response.type === 'application/json') {
return callback(null, response.body);
}

if (do_not_track) {
logger.error(`${serviceConfig.getBaseUrl()} [do_not_track] could not parse response: ${response.text}`);
return callback(`${serviceConfig.getBaseUrl()} [do_not_track] could not parse response: ${response.text}`);
} else {
logger.error(`${synthesizeUrl(serviceConfig, req)} could not parse response: ${response.text}`);
return callback(`${synthesizeUrl(serviceConfig, req)} could not parse response: ${response.text}`);
}

});

};

};
35 changes: 35 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "pelias-microservice-wrapper",
"version": "1.0.0",
"description": "Module that wraps ",
"main": "index.js",
"scripts": {
"test": "node test/test | tap-dot",
"lint": "jshint .",
"travis": "npm run check-dependencies && npm test",
"semantic-release": "semantic-release pre && npm publish && semantic-release post",
"validate": "npm ls",
"check-dependencies": "node_modules/.bin/npm-check --production"
},
"author": "Mapzen",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/pelias/microservice-wrapper.git"
},
"keywords": [
"pelias"
],
"devDependencies": {
"express": "^4.15.2",
"pelias-mock-logger": "^1.1.0",
"proxyquire": "^1.7.11",
"tape": "^4.6.3"
},
"dependencies": {
"lodash": "^4.17.4",
"pelias-logger": "^0.2.0",
"superagent": "^3.5.2",
"tap-dot": "^1.0.5"
}
}
47 changes: 47 additions & 0 deletions test/ServiceConfiguration.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const tape = require('tape');
const ServiceConfiguration = require('../ServiceConfiguration');

tape('ServiceConfiguration tests', (test) => {
test.test('timeout and retries overrides should be returned by getters', (t) => {
const configBlob = {
url: 'base url',
timeout: 17,
retries: 19
};

const serviceConfiguration = new ServiceConfiguration('service name', configBlob);

t.equals(serviceConfiguration.getName(), 'service name');
t.equals(serviceConfiguration.getBaseUrl(), 'base url');
t.deepEquals(serviceConfiguration.getParameters(), {});
t.deepEquals(serviceConfiguration.getHeaders(), {});
t.equals(serviceConfiguration.getUrl(), 'base url');
t.equals(serviceConfiguration.getRetries(), 19);
t.equals(serviceConfiguration.getTimeout(), 17);
t.end();

});

test.test('configBlob w/o timeout or retries should default to 250 and 3, respectively', (t) => {
const configBlob = {
url: 'base url'
};

const serviceConfiguration = new ServiceConfiguration('service name', configBlob);

t.equals(serviceConfiguration.getTimeout(), 250, 'should be a default of 250');
t.equals(serviceConfiguration.getRetries(), 3, 'should be a default of 3');
t.end();

});

test.test('missing name should throw error', (t) => {
t.throws(() => {
// lint complains if using `new` and not assigning to something
const config = new ServiceConfiguration(undefined, { url: 'base url' });
}, /^name is required$/);
t.end();

});

});
Loading

0 comments on commit c8e4b28

Please sign in to comment.