Skip to content
This repository has been archived by the owner on Jan 20, 2020. It is now read-only.

Heartbeat #76

Merged
merged 11 commits into from
Sep 19, 2017
Merged
Show file tree
Hide file tree
Changes from 8 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
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,6 +316,12 @@ var Gdax = require('gdax');
var websocket = new Gdax.WebsocketClient(['BTC-USD', 'ETH-USD']);
websocket.on('message', function(data) { console.log(data); });
```

Optionally set the heartbeat mode.
```javascript
var websocket = new Gdax.WebsocketClient(['BTC-USD','ETH-USD'], {heartbeat: true});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here it would be good to expand upon the optional parameters (websocket URI, authentication) just so that people know more without having to dig into the code themselves to figure out how it works, or why their old implementation is breaking if they've upgraded.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, I'll update

```

The following events can be emitted from the `WebsocketClient`:
* `open`
* `message`
Expand Down
45 changes: 34 additions & 11 deletions lib/clients/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,21 @@ var signRequest = require('../../lib/request_signer').signRequest;
/**
* Create a new connection to a websocket feed
* @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)
* @param options {object} Optional. It can contain auth, websocketURI and heartbeat
* - auth {object} Optional. An object containing your API ket details (key, secret & passphrase)
* - websocketURI {string} Optional. The websocker URL. Default: The official GDAX feed.
* - heartbeat {boolean} Optional. Receive heartbeat messages once a second
*/
var WebsocketClient = function(productIDs, websocketURI, auth) {
var WebsocketClient = function(productIDs, options) {
var self = this;
options = options || {};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Due to the breaking changes in the way the WebsocketClient takes parameters, do you think it would be good to add the following more parameter error handling?

For example, maybe checking to ensure that options is in fact an object, and not a string (perhaps websocketURI).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not the only case either, just a thought of one potential.

The idea is to look at this and ensure it is as backwards compatible friendly as possible, without making it mess.

self.productIDs = Utils.determineProductIDs(productIDs);
self.websocketURI = websocketURI || 'wss://ws-feed.gdax.com';
if (auth && !(auth.secret && auth.key && auth.passphrase)) {
self.websocketURI = options.websocketURI || 'wss://ws-feed.gdax.com';
if (options.auth && !(options.auth.secret && options.auth.key && options.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');
}
self.auth = auth || {};
self.auth = options.auth || {};
self.heartbeat = options.heartbeat || false;
EventEmitter.call(self);
self.connect();
};
Expand Down Expand Up @@ -73,11 +77,30 @@ _.assign(WebsocketClient.prototype, new function() {

self.socket.send(JSON.stringify(subscribeMessage));

// Set a 30 second ping to keep connection alive
self.pinger = setInterval(function() {
self.socket.ping("keepalive");
}, 30000);

// Add heartbeat option
if(self.heartbeat) {
var heartbeatMessage = {
type: 'heartbeat',
on: true
};

// Add Signature
if (self.auth.secret) {
var sig = signRequest(self.auth, 'GET', '/users/self');
heartbeatMessage.signature = sig.signature
heartbeatMessage.key = sig.key
heartbeatMessage.passphrase = sig.passphrase
heartbeatMessage.timestamp = sig.timestamp
}

self.socket.send(JSON.stringify(heartbeatMessage));
} else {
// Set a 30 second ping to keep connection alive
self.pinger = setInterval(function () {
self.socket.ping("keepalive");
}, 30000);
}

};

prototype.onClose = function() {
Expand Down
2 changes: 1 addition & 1 deletion lib/orderbook_sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ var OrderbookSync = function(productIDs, apiURI, websocketURI, authenticatedClie
self.loadOrderbook(productID);
});

WebsocketClient.call(self, self.productIDs, self.websocketURI);
WebsocketClient.call(self, self.productIDs, {websocketURI: self.websocketURI});
};

util.inherits(OrderbookSync, WebsocketClient);
Expand Down
60 changes: 50 additions & 10 deletions tests/websocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ 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://localhost:' + port);
var websocketClient = new Gdax.WebsocketClient(['BTC-EUR'], {websocketURI: 'ws://localhost:' + port});
websocketClient.on('open', function() {
server.close();
done();
Expand All @@ -25,7 +25,7 @@ describe('WebsocketClient', function() {

test('subscribes to the default product (BTC-USD) if undefined', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient(null, 'ws://localhost:' + port);
var websocketClient = new Gdax.WebsocketClient(null, {websocketURI: 'ws://localhost:' + port});
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
Expand All @@ -43,7 +43,7 @@ describe('WebsocketClient', function() {

test('subscribes to the default product (BTC-USD) if empty string', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient('', 'ws://localhost:' + port);
var websocketClient = new Gdax.WebsocketClient('', {websocketURI: 'ws://localhost:' + port});
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
Expand All @@ -61,7 +61,7 @@ describe('WebsocketClient', function() {

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://localhost:' + port);
var websocketClient = new Gdax.WebsocketClient([], {websocketURI: 'ws://localhost:' + port});
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
Expand All @@ -79,7 +79,7 @@ describe('WebsocketClient', function() {

test('subscribes to the specified products', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient(['BTC-EUR'], 'ws://localhost:' + port);
var websocketClient = new Gdax.WebsocketClient(['BTC-EUR'], {websocketURI: 'ws://localhost:' + port});
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
Expand All @@ -97,7 +97,7 @@ describe('WebsocketClient', function() {

test('subscribes to the specified product (backward compatibility)', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient('ETH-USD', 'ws://localhost:' + port);
var websocketClient = new Gdax.WebsocketClient('ETH-USD', {websocketURI: 'ws://localhost:' + port});
});
server.on('connection', function(socket) {
socket.on('message', function(data) {
Expand All @@ -115,10 +115,13 @@ describe('WebsocketClient', function() {

test('passes authentication details through', function(done) {
var server = testserver(++port, function() {
var websocketClient = new Gdax.WebsocketClient('ETH-USD', 'ws://localhost:' + port, {
key: 'suchkey',
secret: 'suchsecret',
passphrase: 'muchpassphrase'
var websocketClient = new Gdax.WebsocketClient('ETH-USD', {
websocketURI: 'ws://localhost:' + port,
auth: {
key: 'suchkey',
secret: 'suchsecret',
passphrase: 'muchpassphrase'
}
});
});
server.on('connection', function(socket) {
Expand All @@ -135,4 +138,41 @@ describe('WebsocketClient', function() {
});
});
});

test('passes heartbeat details through', function (done) {
var calls = {heartbeat: false, subscribe: false};

var server = testserver(++port, function () {
var websocketClient = new Gdax.WebsocketClient('ETH-USD', {
websocketURI: 'ws://localhost:' + port,
heartbeat: true,
auth: {
key: 'suchkey',
secret: 'suchsecret',
passphrase: 'muchpassphrase'
}
});
});

server.on('connection', function (socket) {
socket.on('message', function (data) {
var msg = JSON.parse(data);

calls[msg.type] = true;

assert.equal(msg.key, 'suchkey');
assert.equal(msg.passphrase, 'muchpassphrase');
assert(msg.timestamp);
assert(msg.signature);


if(msg.type === 'heartbeat') {
// assert both (heartbeat and subscribe) calls were made
assert.deepEqual(calls, { heartbeat: true, subscribe: true });
server.close();
done();
}
});
});
});
});