From 20e28d6c70a7c4af9f3da2ebda1e81d9f8ba7414 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Mon, 4 Dec 2017 23:58:33 -0800 Subject: Add requestId to subscription messages and update json-schemas --- packages/connect/CHANGELOG.md | 4 ++ packages/connect/src/index.ts | 4 ++ packages/connect/src/types.ts | 12 ++++-- .../src/utils/orderbook_channel_message_parsers.ts | 12 ++---- packages/connect/src/ws_orderbook_channel.ts | 43 +++++++++++++--------- .../snapshot_orderbook_channel_message.ts | 4 +- .../unknown_orderbook_channel_message.ts | 2 +- .../update_orderbook_channel_message.ts | 4 +- .../test/orderbook_channel_message_parsers_test.ts | 12 +++++- 9 files changed, 62 insertions(+), 35 deletions(-) (limited to 'packages/connect') diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index b6a9eb6ea..28f4149bf 100644 --- a/packages/connect/CHANGELOG.md +++ b/packages/connect/CHANGELOG.md @@ -1,5 +1,9 @@ # CHANGELOG +vx.x.x +------------------------ + * Expose WebSocketOrderbookChannel and associated types to public interface + v0.2.0 - _November 29, 2017_ ------------------------ * Add SignedOrder and TokenTradeInfo to the public interface diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts index aabf233fe..ec369a606 100644 --- a/packages/connect/src/index.ts +++ b/packages/connect/src/index.ts @@ -1,10 +1,14 @@ export {HttpClient} from './http_client'; +export {WebSocketOrderbookChannel} from './ws_orderbook_channel'; export { Client, ECSignature, FeesRequest, FeesResponse, Order, + OrderbookChannel, + OrderbookChannelHandler, + OrderbookChannelSubscriptionOpts, OrderbookRequest, OrderbookResponse, OrdersRequest, diff --git a/packages/connect/src/types.ts b/packages/connect/src/types.ts index 71dd8dac2..0657a1d63 100644 --- a/packages/connect/src/types.ts +++ b/packages/connect/src/types.ts @@ -57,9 +57,12 @@ export interface OrderbookChannelSubscriptionOpts { } export interface OrderbookChannelHandler { - onSnapshot: (channel: OrderbookChannel, snapshot: OrderbookResponse) => void; - onUpdate: (channel: OrderbookChannel, order: SignedOrder) => void; - onError: (channel: OrderbookChannel, err: Error) => void; + onSnapshot: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, + snapshot: OrderbookResponse) => void; + onUpdate: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, + order: SignedOrder) => void; + onError: (channel: OrderbookChannel, subscriptionOpts: OrderbookChannelSubscriptionOpts, + err: Error) => void; onClose: (channel: OrderbookChannel) => void; } @@ -76,16 +79,19 @@ export enum OrderbookChannelMessageTypes { export interface SnapshotOrderbookChannelMessage { type: OrderbookChannelMessageTypes.Snapshot; + requestId: number; payload: OrderbookResponse; } export interface UpdateOrderbookChannelMessage { type: OrderbookChannelMessageTypes.Update; + requestId: number; payload: SignedOrder; } export interface UnknownOrderbookChannelMessage { type: OrderbookChannelMessageTypes.Unknown; + requestId: number; payload: undefined; } diff --git a/packages/connect/src/utils/orderbook_channel_message_parsers.ts b/packages/connect/src/utils/orderbook_channel_message_parsers.ts index 9cd160428..486a416ef 100644 --- a/packages/connect/src/utils/orderbook_channel_message_parsers.ts +++ b/packages/connect/src/utils/orderbook_channel_message_parsers.ts @@ -15,28 +15,24 @@ export const orderbookChannelMessageParsers = { const messageObj = JSON.parse(utf8Data); const type: string = _.get(messageObj, 'type'); assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`); + assert.isString('type', type); switch (type) { case (OrderbookChannelMessageTypes.Snapshot): { assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema); const orderbook = messageObj.payload; typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook); - return { - type, - payload: orderbook, - }; + return messageObj; } case (OrderbookChannelMessageTypes.Update): { assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema); const order = messageObj.payload; typeConverters.convertOrderStringFieldsToBigNumber(order); - return { - type, - payload: order, - }; + return messageObj; } default: { return { type: OrderbookChannelMessageTypes.Unknown, + requestId: 0, payload: undefined, }; } diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts index d9cc1b4c5..346be20c0 100644 --- a/packages/connect/src/ws_orderbook_channel.ts +++ b/packages/connect/src/ws_orderbook_channel.ts @@ -22,9 +22,10 @@ export class WebSocketOrderbookChannel implements OrderbookChannel { private apiEndpointUrl: string; private client: WebSocket.client; private connectionIfExists?: WebSocket.connection; + private subscriptionCounter = 0; /** * Instantiates a new WebSocketOrderbookChannel instance - * @param url The base url for making API calls + * @param url The relayer API base WS url you would like to interact with * @return An instance of WebSocketOrderbookChannel */ constructor(url: string) { @@ -46,23 +47,25 @@ export class WebSocketOrderbookChannel implements OrderbookChannel { assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate')); assert.isFunction('handler.onError', _.get(handler, 'onError')); assert.isFunction('handler.onClose', _.get(handler, 'onClose')); + this.subscriptionCounter += 1; const subscribeMessage = { type: 'subscribe', channel: 'orderbook', + requestId: this.subscriptionCounter, payload: subscriptionOpts, }; this._getConnection((error, connection) => { if (!_.isUndefined(error)) { - handler.onError(this, error); + handler.onError(this, subscriptionOpts, error); } else if (!_.isUndefined(connection) && connection.connected) { connection.on(WebsocketConnectionEventType.Error, wsError => { - handler.onError(this, wsError); + handler.onError(this, subscriptionOpts, wsError); }); connection.on(WebsocketConnectionEventType.Close, () => { handler.onClose(this); }); connection.on(WebsocketConnectionEventType.Message, message => { - this._handleWebSocketMessage(message, handler); + this._handleWebSocketMessage(subscribeMessage.requestId, subscriptionOpts, message, handler); }); connection.sendUTF(JSON.stringify(subscribeMessage)); } @@ -90,30 +93,34 @@ export class WebSocketOrderbookChannel implements OrderbookChannel { this.client.connect(this.apiEndpointUrl); } } - private _handleWebSocketMessage(message: WebSocket.IMessage, handler: OrderbookChannelHandler): void { + private _handleWebSocketMessage(requestId: number, subscriptionOpts: OrderbookChannelSubscriptionOpts, + message: WebSocket.IMessage, handler: OrderbookChannelHandler): void { if (!_.isUndefined(message.utf8Data)) { try { const utf8Data = message.utf8Data; const parserResult = orderbookChannelMessageParsers.parser(utf8Data); const type = parserResult.type; - switch (parserResult.type) { - case (OrderbookChannelMessageTypes.Snapshot): { - handler.onSnapshot(this, parserResult.payload); - break; - } - case (OrderbookChannelMessageTypes.Update): { - handler.onUpdate(this, parserResult.payload); - break; - } - default: { - handler.onError(this, new Error(`Message has missing a type parameter: ${utf8Data}`)); + if (parserResult.requestId === requestId) { + switch (parserResult.type) { + case (OrderbookChannelMessageTypes.Snapshot): { + handler.onSnapshot(this, subscriptionOpts, parserResult.payload); + break; + } + case (OrderbookChannelMessageTypes.Update): { + handler.onUpdate(this, subscriptionOpts, parserResult.payload); + break; + } + default: { + handler.onError( + this, subscriptionOpts, new Error(`Message has missing a type parameter: ${utf8Data}`)); + } } } } catch (error) { - handler.onError(this, error); + handler.onError(this, subscriptionOpts, error); } } else { - handler.onError(this, new Error(`Message does not contain utf8Data`)); + handler.onError(this, subscriptionOpts, new Error(`Message does not contain utf8Data`)); } } } diff --git a/packages/connect/test/fixtures/standard_relayer_api/snapshot_orderbook_channel_message.ts b/packages/connect/test/fixtures/standard_relayer_api/snapshot_orderbook_channel_message.ts index 3cedafb20..1d7e67055 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/snapshot_orderbook_channel_message.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/snapshot_orderbook_channel_message.ts @@ -5,13 +5,13 @@ const orderbookJsonString = JSON.stringify(orderbookJSON); export const snapshotOrderbookChannelMessage = `{ "type": "snapshot", "channel": "orderbook", - "channelId": 1, + "requestId": 1, "payload": ${orderbookJsonString} }`; export const malformedSnapshotOrderbookChannelMessage = `{ "type": "snapshot", "channel": "orderbook", - "channelId": 1, + "requestId": 1, "payload": {} }`; diff --git a/packages/connect/test/fixtures/standard_relayer_api/unknown_orderbook_channel_message.ts b/packages/connect/test/fixtures/standard_relayer_api/unknown_orderbook_channel_message.ts index 842738d99..cbedff60e 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/unknown_orderbook_channel_message.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/unknown_orderbook_channel_message.ts @@ -5,6 +5,6 @@ const orderJSONString = JSON.stringify(orderResponseJSON); export const unknownOrderbookChannelMessage = `{ "type": "superGoodUpdate", "channel": "orderbook", - "channelId": 1, + "requestId": 1, "payload": ${orderJSONString} }`; diff --git a/packages/connect/test/fixtures/standard_relayer_api/update_orderbook_channel_message.ts b/packages/connect/test/fixtures/standard_relayer_api/update_orderbook_channel_message.ts index bc83854c6..0e2c7523b 100644 --- a/packages/connect/test/fixtures/standard_relayer_api/update_orderbook_channel_message.ts +++ b/packages/connect/test/fixtures/standard_relayer_api/update_orderbook_channel_message.ts @@ -5,13 +5,13 @@ const orderJSONString = JSON.stringify(orderResponseJSON); export const updateOrderbookChannelMessage = `{ "type": "update", "channel": "orderbook", - "channelId": 1, + "requestId": 1, "payload": ${orderJSONString} }`; export const malformedUpdateOrderbookChannelMessage = `{ "type": "update", "channel": "orderbook", - "channelId": 1, + "requestId": 1, "payload": {} }`; diff --git a/packages/connect/test/orderbook_channel_message_parsers_test.ts b/packages/connect/test/orderbook_channel_message_parsers_test.ts index 0ad0b12b3..2c776b095 100644 --- a/packages/connect/test/orderbook_channel_message_parsers_test.ts +++ b/packages/connect/test/orderbook_channel_message_parsers_test.ts @@ -41,12 +41,22 @@ describe('orderbookChannelMessageParsers', () => { it('throws when message does not include a type', () => { const typelessMessage = `{ "channel": "orderbook", - "channelId": 1, + "requestId": 1, "payload": {} }`; const badCall = () => orderbookChannelMessageParsers.parser(typelessMessage); expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`); }); + it('throws when type is not a string', () => { + const messageWithBadType = `{ + "type": 1, + "channel": "orderbook", + "requestId": 1, + "payload": {} + }`; + const badCall = () => orderbookChannelMessageParsers.parser(messageWithBadType); + expect(badCall).throws('Expected type to be of type string, encountered: 1'); + }); it('throws when snapshot message has malformed payload', () => { const badCall = () => orderbookChannelMessageParsers.parser(malformedSnapshotOrderbookChannelMessage); -- cgit v1.2.3