Skip to content

Commit

Permalink
Enable connecting to multiple products per WS connection (coinbase#27)
Browse files Browse the repository at this point in the history
Add tests for the WebsocketClient module.
  • Loading branch information
mihar committed Jan 16, 2017
1 parent c4da4c4 commit 6ffe0bb
Show file tree
Hide file tree
Showing 3 changed files with 176 additions and 4 deletions.
38 changes: 34 additions & 4 deletions lib/clients/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ var signRequest = require('../../lib/request_signer').signRequest;

/**
* Create a new connection to a websocket feed
* @param productID {string} Options. The GDAX product to connect to. Default: 'BTC-USD'
* @param productIDs {array} The GDAX products to listen to. Default: ['BTC-USD']
* @param websocketURI {string} Optional. The websocker URL. Default: The official GDAX feed.
* @param auth {object} Optional. An object containing your API ket details (key, secret & passphrase)
*/
var WebsocketClient = function(productID, websocketURI, auth) {
var WebsocketClient = function(productIDs, websocketURI, auth) {
var self = this;
self.productID = productID || 'BTC-USD';
self.productIDs = self._determineProductIDs(productIDs);
self.websocketURI = websocketURI || 'wss:https://ws-feed.gdax.com';
if (auth && !(auth.secret && auth.key && auth.passphrase)) {
throw new Error('Invalid or incomplete authentication credentials. You should either provide all of the secret, key and passphrase fields, or leave auth null');
Expand All @@ -39,6 +39,7 @@ _.assign(WebsocketClient.prototype, new function() {
self.socket.on('message', self.onMessage.bind(self));
self.socket.on('open', self.onOpen.bind(self));
self.socket.on('close', self.onClose.bind(self));
self.socket.on('error', self.onError.bind(self));
};

prototype.disconnect = function() {
Expand All @@ -57,7 +58,7 @@ _.assign(WebsocketClient.prototype, new function() {

var subscribeMessage = {
type: 'subscribe',
product_id: self.productID
product_ids: self.productIDs
};

// Add Signature
Expand Down Expand Up @@ -90,5 +91,34 @@ _.assign(WebsocketClient.prototype, new function() {
self.emit('message', JSON.parse(data));
};

prototype.onError = function(err) {
var self = this;

if (!err) {
return;
}

if (err.message === 'unexpected server response (429)') {
err = new Error('You are connecting too fast and are being throttled! Make sure you subscribe to multiple books on one connection.');
throw err;
}

self.emit('error', err);
};

prototype._determineProductIDs = function(productIDs) {
if (!productIDs || !productIDs.length) {
return ['BTC-USD'];
}

if (Array.isArray(productIDs)) {
return productIDs;
}

// If we got this far, it means it's a string.
// Return an array for backwards compatibility.
return [productIDs];
}

});
module.exports = exports = WebsocketClient;
4 changes: 4 additions & 0 deletions tests/lib/ws_testserver.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
var wss = require('ws').Server;
module.exports = function(port, cb) {
return new wss({ port: port }, cb);
}
138 changes: 138 additions & 0 deletions tests/websocket.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
var assert = require('assert');
var nock = require('nock');

var Gdax = require('../index.js');

var key = 'key';
var secret = 'secret';
var passphrase = 'passphrase';

var testserver = require('./lib/ws_testserver');
var port = 56632;

suite('WebsocketClient');

describe('WebsocketClient', function() {
test('connects to specified server', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient(['BTC-EUR'], 'ws:https://localhost:' + port);
websocketClient.on('open', function() {
server.close();
done();
});
});
});

test('subscribes to the default product (BTC-USD) if undefined', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient(null, 'ws:https://localhost:' + port);
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
var msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-USD']
});

server.close();
done();
});
});
});

test('subscribes to the default product (BTC-USD) if empty string', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient('', 'ws:https://localhost:' + port);
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
var msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-USD']
});

server.close();
done();
});
});
});

test('subscribes to the default product (BTC-USD) if empty array passed', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient([], 'ws:https://localhost:' + port);
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
var msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-USD']
});

server.close();
done();
});
});
});

test('subscribes to the specified products', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient(['BTC-EUR'], 'ws:https://localhost:' + port);
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
var msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['BTC-EUR']
});

server.close();
done();
});
});
});

test('subscribes to the specified product (backward compatibility)', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient('ETH-USD', 'ws:https://localhost:' + port);
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
var msg = JSON.parse(data);
assert.deepEqual(msg, {
type: 'subscribe',
product_ids: ['ETH-USD']
});

server.close();
done();
});
});
});

test('passes authentication details through', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient('ETH-USD', 'ws:https://localhost:' + port, {
key: 'suchkey',
secret: 'suchsecret',
passphrase: 'muchpassphrase'
});
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
var msg = JSON.parse(data);
assert.equal(msg.type, 'subscribe');
assert.equal(msg.key, 'suchkey');
assert.equal(msg.passphrase, 'muchpassphrase');
assert(msg.timestamp);
assert(msg.signature);

server.close();
done();
});
});
});
});

0 comments on commit 6ffe0bb

Please sign in to comment.