Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 61 additions & 21 deletions lib/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ var winston = require('winston');
var lodash = require('lodash');
var request = require('request');
var Promise = require('promise');
var parseLinkHeader = require('parse-link-header');

var DSN = require('./dsn').DSN;
var Projects = require('./projects');
Expand Down Expand Up @@ -39,6 +40,7 @@ var Client = function(dsn, config) {
this.config = lodash.defaults(config || {}, {
version: 0,
logging: false,
requestOptions: {}
});

this.logger = new(winston.Logger)({
Expand Down Expand Up @@ -92,41 +94,32 @@ Client.prototype.teams = null;
*/
Client.prototype.events = null;

/**
* Make a request to the Sentry API.
*
* @method request
* @param {String} path The request path.
* @param {Object} options Request options. These are the same as the request module's options.
* @param {Function} callback Function to execute when the request completes.
* @return {Promise}
*/
Client.prototype.request = function(path, options, callback) {
var uri = util.format('%s/api/%d/%s', this.dsn.uri, this.config.version, path);
Client.prototype._merge = function(page, pages) {
if (lodash.isArray(pages)) {
return pages.concat(page);
} else {
return lodash.merge(pages, page);
}
};

Client.prototype._request = function(uri, options) {
this.logger.info(options.method, uri);

if (lodash.isUndefined(callback)) {
callback = function() {};
}

return new Promise(function (resolve, reject) {
return new Promise(function(resolve, reject) {
request(lodash.extend({
uri: uri,
json: true,
auth: {
user: this.dsn.publicKey,
},
}, options), function(error, response, body) {
}, options, this.config.requestOptions), function(error, response, body) {
if (error) {
this.logger.error(error);
callback(error, null);
reject(error);
}
else if (response.statusCode >= 200 && response.statusCode < 300) {
this.logger.info(response.statusCode, response.statusMessage);
callback(null, body);
resolve(body);
resolve([response, body]);
}
else {
this.logger.warn(response.statusCode, response.statusMessage);
Expand All @@ -139,13 +132,60 @@ Client.prototype.request = function(path, options, callback) {
else {
error = new Error(response.statusCode);
}
callback(error);
reject(error)
}
}.bind(this));
}.bind(this));
};

/**
* Make a request to the Sentry API.
*
* @method request
* @param {String} path The request path.
* @param {Object} options Request options. These are the same as the request module's options.
* @param {Function} callback Function to execute when the request completes.
* @return {Promise}
*/
Client.prototype.request = function(path, options, callback) {
var uri = util.format('%s/api/%d/%s', this.dsn.uri, this.config.version, path);

if (lodash.isUndefined(callback)) {
callback = function() {};
}

var self = this;
function load(uri) {
return self._request(uri, options)
.then(function(result) {
var response = result[0];
var body = result[1];

if (response.headers.link) {
var next = parseLinkHeader(response.headers.link).next;
if (next && next.results === 'true' && next.url) {
return load(next.url, options)
.then(function (tailResult) {
return self._merge(tailResult, body);
});
}
}

return body;
});
}

return load(uri)
.catch(function(error) {
callback(error, null);
throw error;
})
.then(function(result) {
callback(null, result);
return result;
});
};

/**
* Convenience method for making GET requests.
*
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sentry-api",
"version": "0.0.8",
"version": "0.1.1",
"description": "A client for the Sentry API",
"main": "index.js",
"scripts": {
Expand All @@ -13,6 +13,7 @@
"license": "ISC",
"dependencies": {
"lodash": "^3.10.1",
"parse-link-header": "^0.4.1",
"promise": "^7.0.4",
"request": "^2.65.0",
"winston": "^2.1.0"
Expand Down
71 changes: 47 additions & 24 deletions test/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,42 +6,65 @@ var Client = require('../lib/client').Client;
exports.testConstructor = function(test) {
test.expect(2);

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');
test.equal(client.config.version, 0, 'Should default version to 0.');
test.equal(client.config.logging, false, 'Should default logging to false.');

test.done();
};

exports.testRequest = function(test) {
test.expect(2);
test.expect(7);

var reqheaders = {
'authorization': 'Basic ' + new Buffer('PUBLIC:').toString('base64'),
'accept': 'application/json'
};

var request = nock('https://host.com', {
reqheaders: {
'authorization': 'Basic ' + new Buffer('PUBLIC:').toString('base64'),
'accept': 'application/json'
}
})
var request1 = nock('https://test.com', {reqheaders: reqheaders})
.get('/api/0/path')
.reply(200, {foo: 'bar'});
.reply(200, {foo: 'FOO'}, {
'Link': '<https://test.com/api/0/path/?&cursor=0>; rel="previous"; results="false", <https://test.com/api/0/path/?&cursor=2>; rel="next"; results="true"',
});

var request2 = nock('https://test.com', {reqheaders: reqheaders})
.get('/api/0/path/?&cursor=2')
.reply(200, {bar: 'BAR'}, {
'Link': '<https://test.com/api/0/path/?&cursor=1>; rel="previous"; results="true", <https://test.com/api/0/path/?&cursor=3>; rel="next"; results="true"',
});

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var request3 = nock('https://test.com', {reqheaders: reqheaders})
.get('/api/0/path/?&cursor=3')
.reply(200, {baz: 'BAZ'}, {
'Link': '<https://test.com/api/0/path/?&cursor=2>; rel="previous"; results="true", <https://test.com/api/0/path/?&cursor=4>; rel="next"; results="false"',
});

var request4 = nock('https://test.com', {reqheaders: reqheaders})
.get('/api/0/path/?&cursor=3')
.reply(404);

var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.request('path', {}, function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
test.equal(response.foo, 'bar', 'Should return the response as an object.');
test.ok(request1.isDone(), 'Should make the correct request.');
test.ok(request2.isDone(), 'Should request all pages.');
test.ok(request3.isDone(), 'Should request all pages.');
test.ok(!request4.isDone(), 'Should not request pages without results.');
test.equal(response.foo, 'FOO', 'Should return the response as an object.');
test.equal(response.bar, 'BAR', 'Should merge second page.');
test.equal(response.baz, 'BAZ', 'Should merge third page.');
test.done();
});
};

exports.testError = function(test) {
test.expect(2);

var request = nock('https://host.com')
var request = nock('https://test.com')
.get('/api/0/path')
.reply(400);

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.request('path', {}, function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
Expand All @@ -53,11 +76,11 @@ exports.testError = function(test) {
exports.testJsonError = function(test) {
test.expect(2);

var request = nock('https://host.com')
var request = nock('https://test.com')
.get('/api/0/path')
.reply(400, {detail: 'bar'});

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.request('path', {}, function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
Expand All @@ -69,12 +92,12 @@ exports.testJsonError = function(test) {
exports.testGet = function(test) {
test.expect(2);

var request = nock('https://host.com')
var request = nock('https://test.com')
.get('/api/0/path')
.query({foo: 'bar'})
.reply(200, {foo: 'bar'});

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.get('path', {foo: 'bar'}, function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
Expand All @@ -86,11 +109,11 @@ exports.testGet = function(test) {
exports.testPost = function(test) {
test.expect(2);

var request = nock('https://host.com')
var request = nock('https://test.com')
.post('/api/0/path', {foo: 'bar'})
.reply(200, {foo: 'bar'});

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.post('path', {foo: 'bar'}, function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
Expand All @@ -102,11 +125,11 @@ exports.testPost = function(test) {
exports.testPut = function(test) {
test.expect(2);

var request = nock('https://host.com')
var request = nock('https://test.com')
.put('/api/0/path', {foo: 'bar'})
.reply(200, {foo: 'bar'});

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.put('path', {foo: 'bar'}, function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
Expand All @@ -118,11 +141,11 @@ exports.testPut = function(test) {
exports.testDelete = function(test) {
test.expect(2);

var request = nock('https://host.com')
var request = nock('https://test.com')
.delete('/api/0/path')
.reply(200, {foo: 'bar'});

var client = new Client('https://PUBLIC:SECRET@host.com/123');
var client = new Client('https://PUBLIC:SECRET@test.com/123');

client.delete('path', function(error, response) {
test.ok(request.isDone(), 'Should make the correct request.');
Expand Down