Skip to content

Commit

Permalink
feat: Pass meta data into native methods
Browse files Browse the repository at this point in the history
  • Loading branch information
Jan Krems committed Apr 21, 2016
1 parent 75d21e9 commit 109f6d1
Show file tree
Hide file tree
Showing 7 changed files with 146 additions and 4 deletions.
11 changes: 8 additions & 3 deletions lib/fetch.browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,12 +239,18 @@ function _fetch(url, options) {

var timeout = defaultTimeout(options.timeout, DEFAULT_TIMEOUT);

var method = options.method || 'GET';
var nativeOptions = {
// All official fetch options:
method: options.method || 'GET',
method: method,
headers: filterHeaders(assign(defaultHeaders, options.headers)),
body: body,
redirect: options.redirect,
// Some things we might want to expose for instrumentation to pick up:
serviceName: options.serviceName,
endpointName: options.endpointName,
methodName: options.methodName || method.toLowerCase(),
pathParams: options.pathParams,
};
var patchedPathname = replacePathParams(urlObj.pathname, options.pathParams);
var patchedSearch = generateSearch(urlObj.query, options.qs);
Expand All @@ -257,7 +263,6 @@ function _fetch(url, options) {
path: patchedPathname + patchedSearch,
};
var nativeUrl = Url.format(patchedUrl);
var request = new Request(nativeUrl, nativeOptions);
var result = new Bluebird(function withResponseTimeout(resolve, reject) {
function onTimedOut() {
var error = new Error('Fetching from ' + urlObj.hostname + ' timed out');
Expand All @@ -270,7 +275,7 @@ function _fetch(url, options) {
clearTimeout(timeoutHandle);
return response;
}
Bluebird.resolve(fetch(request))
Bluebird.resolve(fetch(nativeUrl, nativeOptions))
.then(killTimeout)
.then(partial(verifyAndExtendResponse, nativeUrl, options))
.then(resolve, reject);
Expand Down
8 changes: 7 additions & 1 deletion lib/fetch.js
Original file line number Diff line number Diff line change
Expand Up @@ -217,13 +217,14 @@ function _fetch(urlObj, options) {
maxFreeSockets: options.maxFreeSockets || 256,
});

var method = options.method || 'GET';
return request({
agent: agent,
protocol: urlObj.protocol,
host: urlObj.host,
hostname: hostname,
port: urlObj.port,
method: options.method || 'GET',
method: method,
path: replacePathParams(urlObj.pathname, options.pathParams) + generateSearch(urlObj.query, options.qs),
headers: filterHeaders(assign(defaultHeaders, options.headers)),
auth: unifyAuth(options.auth || urlObj.auth),
Expand All @@ -233,6 +234,11 @@ function _fetch(urlObj, options) {
timeout: defaultTimeout(options.timeout, DEFAULT_TIMEOUT),
minStatusCode: defaultStatusCode(options.minStatusCode, 200),
maxStatusCode: defaultStatusCode(options.maxStatusCode, 299),
// Some things we might want to expose for instrumentation to pick up:
serviceName: options.serviceName,
endpointName: options.endpointName,
methodName: options.methodName || method.toLowerCase(),
pathParams: options.pathParams,
});
}

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"main": "lib/gofer.js",
"browser": {
"./lib/fetch.js": "./lib/fetch.browser.js",
"./test/instrument.js": "./test/instrument.browser.js",
"./test/mock-service.js": "./test/mock-service.browser.js"
},
"homepage": "https://github.com/groupon/gofer",
Expand Down
19 changes: 19 additions & 0 deletions test/instrument.browser.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
'use strict';
var assign = require('lodash/assign');

var original = typeof window !== 'undefined' ? window.fetch : null;

function instrument() {
window.fetch = function fetch(url, options) {
// This is terrible instrumentation because it doesn't handle
// all possible arguments. E.g. `url` could also be an instance
// of Request.
assign(instrument, options || {});
return original.apply(this, arguments);
};
}
module.exports = instrument;

instrument.reset = function reset() {
window.fetch = original;
};
21 changes: 21 additions & 0 deletions test/instrument.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';
var http = require('http');

var assign = require('lodash/assign');

var original = http.request;

function instrument() {
http.request = function request(options) {
// This is terrible instrumentation because it doesn't handle
// all possible arguments. `options` isn't the only possible
// call signature of `http.request`.
assign(instrument, options);
return original.apply(this, arguments);
};
}
module.exports = instrument;

instrument.reset = function reset() {
http.request = original;
};
87 changes: 87 additions & 0 deletions test/instrumentation.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';
var assert = require('assertive');
var assign = require('lodash/assign');

var Gofer = require('../');

var instrument = require('./instrument');

var options = require('./mock-service');

function ensureEmpty() {
assign(instrument, {
serviceName: null,
endpointName: null,
methodName: null,
pathParams: null,
});
assert.equal(null, instrument.serviceName);
assert.equal(null, instrument.endpointName);
assert.equal(null, instrument.methodName);
assert.equal(null, instrument.pathParams);
}

describe('Verify instrumentation support', function () {
var client = new Gofer({ instrumented: options }, 'instrumented');

client.registerEndpoints({
echo: function (fetch) {
return function () {
return fetch('/{x}', { method: 'PUT', pathParams: { x: 'echo' } });
};
},
named: function (fetch) {
return function () {
return fetch('/echo', { method: 'PUT', methodName: 'echo' });
};
},
});

before('add instrumentation', instrument);
after('remove instrumentation', instrument.reset);

describe('direct request', function () {
before(ensureEmpty);

before('make a request', function () {
return client.fetch('/echo');
});

it('sets the meta data', function () {
assert.equal('instrumented', instrument.serviceName);
assert.equal(undefined, instrument.endpointName);
assert.equal('get', instrument.methodName);
assert.equal(undefined, instrument.pathParams);
});
});

describe('call endpoint', function () {
before(ensureEmpty);

before('make a request', function () {
return client.echo();
});

it('sets the meta data', function () {
assert.equal('instrumented', instrument.serviceName);
assert.equal('echo', instrument.endpointName);
assert.equal('put', instrument.methodName);
assert.deepEqual({ x: 'echo' }, instrument.pathParams);
});
});

describe('with explicit methodName', function () {
before(ensureEmpty);

before('make a request', function () {
return client.named();
});

it('sets the meta data', function () {
assert.equal('instrumented', instrument.serviceName);
assert.equal('named', instrument.endpointName);
assert.equal('echo', instrument.methodName);
assert.equal(undefined, instrument.pathParams);
});
});
});
3 changes: 3 additions & 0 deletions test/mock-service.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ function handleRequest(req, res) {
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, HEAD, OPTIONS, DELETE, PATCH');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');

// Preflight requests that return a 404 confuse Chrome
if (req.method === 'OPTIONS') return res.end();

var pathname = parseUrl(req.url).pathname;
if (/^\/echo/.test(pathname)) {
return sendEcho(req, res);
Expand Down

0 comments on commit 109f6d1

Please sign in to comment.