diff options
17 files changed, 104 insertions, 57 deletions
diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md index b6a9eb6ea..3637ed2d1 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 (#251) + 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); diff --git a/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts b/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts index 8ded9adb0..2f71531c6 100644 --- a/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts +++ b/packages/json-schemas/schemas/relayer_api_orberbook_channel_subscribe_schema.ts @@ -4,9 +4,10 @@ export const relayerApiOrderbookChannelSubscribeSchema = { properties: { type: {enum: ['subscribe']}, channel: {enum: ['orderbook']}, + requestId: {type: 'number'}, payload: {$ref: '/RelayerApiOrderbookChannelSubscribePayload'}, }, - required: ['type', 'channel', 'payload'], + required: ['type', 'channel', 'requestId', 'payload'], }; export const relayerApiOrderbookChannelSubscribePayload = { diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts index cfc0ddc8f..99037865e 100644 --- a/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts +++ b/packages/json-schemas/schemas/relayer_api_orderbook_channel_snapshot_schema.ts @@ -4,10 +4,10 @@ export const relayerApiOrderbookChannelSnapshotSchema = { properties: { type: {enum: ['snapshot']}, channel: {enum: ['orderbook']}, - channelId: {type: 'number'}, + requestId: {type: 'number'}, payload: {$ref: '/RelayerApiOrderbookChannelSnapshotPayload'}, }, - required: ['type', 'channel', 'channelId', 'payload'], + required: ['type', 'channel', 'requestId', 'payload'], }; export const relayerApiOrderbookChannelSnapshotPayload = { diff --git a/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts b/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts index 51308ed49..90cfd8749 100644 --- a/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts +++ b/packages/json-schemas/schemas/relayer_api_orderbook_channel_update_response_schema.ts @@ -4,8 +4,8 @@ export const relayerApiOrderbookChannelUpdateSchema = { properties: { type: {enum: ['update']}, channel: {enum: ['orderbook']}, - channelId: {type: 'number'}, + requestId: {type: 'number'}, payload: {$ref: '/SignedOrder'}, }, - required: ['type', 'channel', 'channelId', 'payload'], + required: ['type', 'channel', 'requestId', 'payload'], }; diff --git a/packages/json-schemas/test/schema_test.ts b/packages/json-schemas/test/schema_test.ts index 653f70852..758ccac61 100644 --- a/packages/json-schemas/test/schema_test.ts +++ b/packages/json-schemas/test/schema_test.ts @@ -432,6 +432,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -442,6 +443,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -454,8 +456,19 @@ describe('Schema', () => { const checksummedAddress = '0xA2b31daCf30a9C50ca473337c01d8A201ae33e32'; const testCases = [ { + type: 'subscribe', + channel: 'orderbook', + payload: { + baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', + quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', + snapshot: true, + limit: 100, + }, + }, + { type: 'foo', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -464,6 +477,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'bar', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -472,6 +486,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: checksummedAddress, quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -480,6 +495,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: checksummedAddress, @@ -488,6 +504,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', }, @@ -495,6 +512,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', }, @@ -502,6 +520,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -512,6 +531,7 @@ describe('Schema', () => { { type: 'subscribe', channel: 'orderbook', + requestId: 1, payload: { baseTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', quoteTokenAddress: '0x323b5d4c32345ced77393b3530b1eed0f346429d', @@ -530,7 +550,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { bids: [], asks: [], @@ -539,7 +559,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { bids: [ signedOrder, @@ -557,7 +577,7 @@ describe('Schema', () => { { type: 'foo', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { bids: [ signedOrder, @@ -570,7 +590,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'bar', - channelId: 2, + requestId: 2, payload: { bids: [ signedOrder, @@ -595,7 +615,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: '2', + requestId: '2', payload: { bids: [ signedOrder, @@ -608,7 +628,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { bids: [ signedOrder, @@ -618,7 +638,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { asks: [ signedOrder, @@ -628,7 +648,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { bids: [ signedOrder, @@ -641,7 +661,7 @@ describe('Schema', () => { { type: 'snapshot', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: { bids: [ {}, @@ -662,7 +682,7 @@ describe('Schema', () => { { type: 'update', channel: 'orderbook', - channelId: 2, + requestId: 2, payload: signedOrder, }, ]; @@ -673,16 +693,19 @@ describe('Schema', () => { { type: 'foo', channel: 'orderbook', + requestId: 2, payload: signedOrder, }, { type: 'update', channel: 'bar', + requestId: 2, payload: signedOrder, }, { type: 'update', channel: 'orderbook', + requestId: 2, payload: {}, }, ]; diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index e71f61ead..31d725a41 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -504,7 +504,6 @@ export class Blockchain { const subscriptionId = this.zeroEx.exchange.subscribe( ExchangeEvents.LogFill, indexFilterValues, async (err: Error, decodedLogEvent: DecodedLogEvent<LogFillContractEventArgs>) => { - const decodedLog = decodedLogEvent.log; if (err) { // Note: it's not entirely clear from the documentation which // errors will be thrown by `watch`. For now, let's log the error @@ -515,6 +514,7 @@ export class Blockchain { this.stopWatchingExchangeLogFillEventsAsync(); // fire and forget return; } else { + const decodedLog = decodedLogEvent.log; if (!this.doesLogEventInvolveUser(decodedLog)) { return; // We aren't interested in the fill event } diff --git a/packages/website/ts/components/ui/lifecycle_raised_button.tsx b/packages/website/ts/components/ui/lifecycle_raised_button.tsx index 630f71545..338a3bf76 100644 --- a/packages/website/ts/components/ui/lifecycle_raised_button.tsx +++ b/packages/website/ts/components/ui/lifecycle_raised_button.tsx @@ -83,7 +83,7 @@ export class LifeCycleRaisedButton extends this.setState({ buttonState: ButtonState.LOADING, }); - const didSucceed = this.props.onClickAsyncFn(); + const didSucceed = await this.props.onClickAsyncFn(); if (this.didUnmount) { return; // noop since unmount called before async callback returned. } diff --git a/packages/website/ts/components/ui/party.tsx b/packages/website/ts/components/ui/party.tsx index 2927d9d3d..5bafa6071 100644 --- a/packages/website/ts/components/ui/party.tsx +++ b/packages/website/ts/components/ui/party.tsx @@ -73,7 +73,7 @@ export class Party extends React.Component<PartyProps, PartyState> { /> : <div className="mx-auto" - style={{height: IMAGE_DIMENSION, width: IMAGE_DIMENSION}} + style={{height: identiconDiameter, width: identiconDiameter}} > <Identicon address={this.props.address} @@ -102,10 +102,6 @@ version "4.6.2" resolved "https://registry.yarnpkg.com/@types/history/-/history-4.6.2.tgz#12cfaba693ba20f114ed5765467ff25fdf67ddb0" -"@types/isomorphic-fetch@^0.0.34": - version "0.0.34" - resolved "https://registry.yarnpkg.com/@types/isomorphic-fetch/-/isomorphic-fetch-0.0.34.tgz#3c3483e606c041378438e951464f00e4e60706d6" - "@types/jsonschema@^1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@types/jsonschema/-/jsonschema-1.1.1.tgz#08703dfe074010e8e829123111594af731f57b1a" |