aboutsummaryrefslogtreecommitdiffstats
path: root/packages/connect/src
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2017-11-15 11:50:10 +0800
committerFabio Berger <me@fabioberger.com>2017-11-15 11:50:10 +0800
commit4e39a957c733d5593dc6d3fe81f96e43e4cc347a (patch)
tree57d54386c65934b996777df7bc5823bdd6953e1b /packages/connect/src
parent0cf719a744f5472483dafd0b21afd6b9167168d3 (diff)
parentfe7ad22cc14e161b9246f7c50e8c1085778253a2 (diff)
downloaddexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.tar
dexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.tar.gz
dexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.tar.bz2
dexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.tar.lz
dexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.tar.xz
dexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.tar.zst
dexon-sol-tools-4e39a957c733d5593dc6d3fe81f96e43e4cc347a.zip
Merge branch 'development' of github.com:0xProject/0x.js into development
* 'development' of github.com:0xProject/0x.js: Add lodash noop to emptyOrderbookChannelHandler Fix lint error Add connect to monorepo
Diffstat (limited to 'packages/connect/src')
-rw-r--r--packages/connect/src/globals.d.ts6
-rw-r--r--packages/connect/src/http_client.ts171
-rw-r--r--packages/connect/src/index.ts15
-rw-r--r--packages/connect/src/schemas/relayer_fees_request_schema.ts8
-rw-r--r--packages/connect/src/schemas/relayer_orderbook_request_schema.ts8
-rw-r--r--packages/connect/src/schemas/relayer_orders_request_schema.ts16
-rw-r--r--packages/connect/src/schemas/relayer_token_pairs_request_schema.ts8
-rw-r--r--packages/connect/src/schemas/schemas.ts15
-rw-r--r--packages/connect/src/types.ts120
-rw-r--r--packages/connect/src/utils/orderbook_channel_message_parsers.ts43
-rw-r--r--packages/connect/src/utils/type_converters.ts31
-rw-r--r--packages/connect/src/ws_orderbook_channel.ts127
12 files changed, 568 insertions, 0 deletions
diff --git a/packages/connect/src/globals.d.ts b/packages/connect/src/globals.d.ts
new file mode 100644
index 000000000..078e189cd
--- /dev/null
+++ b/packages/connect/src/globals.d.ts
@@ -0,0 +1,6 @@
+declare module 'dirty-chai';
+
+declare module '*.json' {
+ const value: any;
+ export default value;
+}
diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts
new file mode 100644
index 000000000..ab8c6bfa1
--- /dev/null
+++ b/packages/connect/src/http_client.ts
@@ -0,0 +1,171 @@
+import 'isomorphic-fetch';
+import * as _ from 'lodash';
+import {BigNumber} from 'bignumber.js';
+import * as queryString from 'query-string';
+import {assert} from '@0xproject/assert';
+import {schemas} from '@0xproject/json-schemas';
+import {SignedOrder} from '0x.js';
+import {
+ Client,
+ FeesRequest,
+ FeesResponse,
+ OrderbookRequest,
+ OrderbookResponse,
+ OrdersRequest,
+ TokenPairsItem,
+ TokenPairsRequest,
+} from './types';
+import {schemas as clientSchemas} from './schemas/schemas';
+import {typeConverters} from './utils/type_converters';
+
+interface RequestOptions {
+ params?: object;
+ payload?: object;
+}
+
+enum RequestType {
+ Get = 'GET',
+ Post = 'POST',
+}
+
+/**
+ * This class includes all the functionality related to interacting with a set of HTTP endpoints
+ * that implement the standard relayer API v0
+ */
+export class HttpClient implements Client {
+ private apiEndpointUrl: string;
+ /**
+ * Instantiates a new HttpClient instance
+ * @param url The base url for making API calls
+ * @return An instance of HttpClient
+ */
+ constructor(url: string) {
+ assert.isHttpUrl('url', url);
+ this.apiEndpointUrl = url;
+ }
+ /**
+ * Retrieve token pair info from the API
+ * @param request A TokenPairsRequest instance describing specific token information
+ * to retrieve
+ * @return The resulting TokenPairsItems that match the request
+ */
+ public async getTokenPairsAsync(request?: TokenPairsRequest): Promise<TokenPairsItem[]> {
+ if (!_.isUndefined(request)) {
+ assert.doesConformToSchema('request', request, clientSchemas.relayerTokenPairsRequestSchema);
+ }
+ const requestOpts = {
+ params: request,
+ };
+ const tokenPairs = await this._requestAsync('/token_pairs', RequestType.Get, requestOpts);
+ assert.doesConformToSchema(
+ 'tokenPairs', tokenPairs, schemas.relayerApiTokenPairsResponseSchema);
+ _.each(tokenPairs, (tokenPair: object) => {
+ typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
+ 'tokenA.minAmount',
+ 'tokenA.maxAmount',
+ 'tokenB.minAmount',
+ 'tokenB.maxAmount',
+ ]);
+ });
+ return tokenPairs;
+ }
+ /**
+ * Retrieve orders from the API
+ * @param request An OrdersRequest instance describing specific orders to retrieve
+ * @return The resulting SignedOrders that match the request
+ */
+ public async getOrdersAsync(request?: OrdersRequest): Promise<SignedOrder[]> {
+ if (!_.isUndefined(request)) {
+ assert.doesConformToSchema('request', request, clientSchemas.relayerOrdersRequestSchema);
+ }
+ const requestOpts = {
+ params: request,
+ };
+ const orders = await this._requestAsync(`/orders`, RequestType.Get, requestOpts);
+ assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
+ _.each(orders, (order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
+ return orders;
+ }
+ /**
+ * Retrieve a specific order from the API
+ * @param orderHash An orderHash generated from the desired order
+ * @return The SignedOrder that matches the supplied orderHash
+ */
+ public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
+ assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
+ const order = await this._requestAsync(`/order/${orderHash}`, RequestType.Get);
+ assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
+ typeConverters.convertOrderStringFieldsToBigNumber(order);
+ return order;
+ }
+ /**
+ * Retrieve an orderbook from the API
+ * @param request An OrderbookRequest instance describing the specific orderbook to retrieve
+ * @return The resulting OrderbookResponse that matches the request
+ */
+ public async getOrderbookAsync(request: OrderbookRequest): Promise<OrderbookResponse> {
+ assert.doesConformToSchema('request', request, clientSchemas.relayerOrderBookRequestSchema);
+ const requestOpts = {
+ params: request,
+ };
+ const orderBook = await this._requestAsync('/orderbook', RequestType.Get, requestOpts);
+ assert.doesConformToSchema('orderBook', orderBook, schemas.relayerApiOrderBookResponseSchema);
+ typeConverters.convertOrderbookStringFieldsToBigNumber(orderBook);
+ return orderBook;
+ }
+ /**
+ * Retrieve fee information from the API
+ * @param request A FeesRequest instance describing the specific fees to retrieve
+ * @return The resulting FeesResponse that matches the request
+ */
+ public async getFeesAsync(request: FeesRequest): Promise<FeesResponse> {
+ assert.doesConformToSchema('request', request, schemas.relayerApiFeesPayloadSchema);
+ typeConverters.convertBigNumberFieldsToStrings(request, [
+ 'makerTokenAmount',
+ 'takerTokenAmount',
+ 'expirationUnixTimestampSec',
+ 'salt',
+ ]);
+ const requestOpts = {
+ payload: request,
+ };
+ const fees = await this._requestAsync('/fees', RequestType.Post, requestOpts);
+ assert.doesConformToSchema('fees', fees, schemas.relayerApiFeesResponseSchema);
+ typeConverters.convertStringsFieldsToBigNumbers(fees, ['makerFee', 'takerFee']);
+ return fees;
+ }
+ /**
+ * Submit a signed order to the API
+ * @param signedOrder A SignedOrder instance to submit
+ */
+ public async submitOrderAsync(signedOrder: SignedOrder): Promise<void> {
+ assert.doesConformToSchema('signedOrder', signedOrder, schemas.signedOrderSchema);
+ const requestOpts = {
+ payload: signedOrder,
+ };
+ await this._requestAsync('/order', RequestType.Post, requestOpts);
+ }
+ private async _requestAsync(path: string, requestType: RequestType, requestOptions?: RequestOptions): Promise<any> {
+ const params = _.get(requestOptions, 'params');
+ const payload = _.get(requestOptions, 'payload');
+ let query = '';
+ if (!_.isUndefined(params) && !_.isEmpty(params)) {
+ const stringifiedParams = queryString.stringify(params);
+ query = `?${stringifiedParams}`;
+ }
+ const url = `${this.apiEndpointUrl}/v0${path}${query}`;
+ const headers = new Headers({
+ 'content-type': 'application/json',
+ });
+ const response = await fetch(url, {
+ method: requestType,
+ body: payload,
+ headers,
+ });
+ if (!response.ok) {
+ throw Error(response.statusText);
+ }
+ const json = await response.json();
+ return json;
+ }
+}
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
new file mode 100644
index 000000000..5e97f4f26
--- /dev/null
+++ b/packages/connect/src/index.ts
@@ -0,0 +1,15 @@
+export {HttpClient} from './http_client';
+export {WebSocketOrderbookChannel} from './ws_orderbook_channel';
+export {
+ Client,
+ FeesRequest,
+ FeesResponse,
+ OrderbookChannel,
+ OrderbookChannelHandler,
+ OrderbookChannelSubscriptionOpts,
+ OrderbookRequest,
+ OrderbookResponse,
+ OrdersRequest,
+ TokenPairsItem,
+ TokenPairsRequest,
+} from './types';
diff --git a/packages/connect/src/schemas/relayer_fees_request_schema.ts b/packages/connect/src/schemas/relayer_fees_request_schema.ts
new file mode 100644
index 000000000..9408c94a0
--- /dev/null
+++ b/packages/connect/src/schemas/relayer_fees_request_schema.ts
@@ -0,0 +1,8 @@
+export const relayerOrderBookRequestSchema = {
+ id: '/RelayerOrderBookRequest',
+ type: 'object',
+ properties: {
+ baseTokenAddress: {$ref: '/Address'},
+ quoteTokenAddress: {$ref: '/Address'},
+ },
+};
diff --git a/packages/connect/src/schemas/relayer_orderbook_request_schema.ts b/packages/connect/src/schemas/relayer_orderbook_request_schema.ts
new file mode 100644
index 000000000..9408c94a0
--- /dev/null
+++ b/packages/connect/src/schemas/relayer_orderbook_request_schema.ts
@@ -0,0 +1,8 @@
+export const relayerOrderBookRequestSchema = {
+ id: '/RelayerOrderBookRequest',
+ type: 'object',
+ properties: {
+ baseTokenAddress: {$ref: '/Address'},
+ quoteTokenAddress: {$ref: '/Address'},
+ },
+};
diff --git a/packages/connect/src/schemas/relayer_orders_request_schema.ts b/packages/connect/src/schemas/relayer_orders_request_schema.ts
new file mode 100644
index 000000000..c11bc77be
--- /dev/null
+++ b/packages/connect/src/schemas/relayer_orders_request_schema.ts
@@ -0,0 +1,16 @@
+export const relayerOrdersRequestSchema = {
+ id: '/RelayerOrdersRequest',
+ type: 'object',
+ properties: {
+ exchangeContractAddress: {$ref: '/Address'},
+ tokenAddress: {$ref: '/Address'},
+ makerTokenAddress: {$ref: '/Address'},
+ takerTokenAddress: {$ref: '/Address'},
+ tokenA: {$ref: '/Address'},
+ tokenB: {$ref: '/Address'},
+ maker: {$ref: '/Address'},
+ taker: {$ref: '/Address'},
+ trader: {$ref: '/Address'},
+ feeRecipient: {$ref: '/Address'},
+ },
+};
diff --git a/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts b/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts
new file mode 100644
index 000000000..8013e1454
--- /dev/null
+++ b/packages/connect/src/schemas/relayer_token_pairs_request_schema.ts
@@ -0,0 +1,8 @@
+export const relayerTokenPairsRequestSchema = {
+ id: '/RelayerTokenPairsRequest',
+ type: 'object',
+ properties: {
+ tokenA: {$ref: '/Address'},
+ tokenB: {$ref: '/Address'},
+ },
+};
diff --git a/packages/connect/src/schemas/schemas.ts b/packages/connect/src/schemas/schemas.ts
new file mode 100644
index 000000000..97ac672bf
--- /dev/null
+++ b/packages/connect/src/schemas/schemas.ts
@@ -0,0 +1,15 @@
+import {
+ relayerOrderBookRequestSchema,
+} from './relayer_orderbook_request_schema';
+import {
+ relayerOrdersRequestSchema,
+} from './relayer_orders_request_schema';
+import {
+ relayerTokenPairsRequestSchema,
+} from './relayer_token_pairs_request_schema';
+
+export const schemas = {
+ relayerOrderBookRequestSchema,
+ relayerOrdersRequestSchema,
+ relayerTokenPairsRequestSchema,
+};
diff --git a/packages/connect/src/types.ts b/packages/connect/src/types.ts
new file mode 100644
index 000000000..75b6b8020
--- /dev/null
+++ b/packages/connect/src/types.ts
@@ -0,0 +1,120 @@
+import {SignedOrder} from '0x.js';
+import {BigNumber} from 'bignumber.js';
+
+export interface Client {
+ getTokenPairsAsync: (request?: TokenPairsRequest) => Promise<TokenPairsItem[]>;
+ getOrdersAsync: (request?: OrdersRequest) => Promise<SignedOrder[]>;
+ getOrderAsync: (orderHash: string) => Promise<SignedOrder>;
+ getOrderbookAsync: (request: OrderbookRequest) => Promise<OrderbookResponse>;
+ getFeesAsync: (request: FeesRequest) => Promise<FeesResponse>;
+ submitOrderAsync: (signedOrder: SignedOrder) => Promise<void>;
+}
+
+export interface OrderbookChannel {
+ subscribe: (subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler) => void;
+ close: () => void;
+}
+
+export interface OrderbookChannelHandler {
+ onSnapshot: (channel: OrderbookChannel, snapshot: OrderbookResponse) => void;
+ onUpdate: (channel: OrderbookChannel, order: SignedOrder) => void;
+ onError: (channel: OrderbookChannel, err: Error) => void;
+ onClose: (channel: OrderbookChannel) => void;
+}
+
+export type OrderbookChannelMessage =
+ SnapshotOrderbookChannelMessage |
+ UpdateOrderbookChannelMessage |
+ UnknownOrderbookChannelMessage;
+
+export enum OrderbookChannelMessageTypes {
+ Snapshot = 'snapshot',
+ Update = 'update',
+ Unknown = 'unknown',
+}
+
+export interface SnapshotOrderbookChannelMessage {
+ type: OrderbookChannelMessageTypes.Snapshot;
+ payload: OrderbookResponse;
+}
+
+export interface UpdateOrderbookChannelMessage {
+ type: OrderbookChannelMessageTypes.Update;
+ payload: SignedOrder;
+}
+
+export interface UnknownOrderbookChannelMessage {
+ type: OrderbookChannelMessageTypes.Unknown;
+ payload: undefined;
+}
+
+/*
+ * baseTokenAddress: The address of token designated as the baseToken in the currency pair calculation of price
+ * quoteTokenAddress: The address of token designated as the quoteToken in the currency pair calculation of price
+ * snapshot: If true, a snapshot of the orderbook will be sent before the updates to the orderbook
+ * limit: Maximum number of bids and asks in orderbook snapshot
+ */
+export interface OrderbookChannelSubscriptionOpts {
+ baseTokenAddress: string;
+ quoteTokenAddress: string;
+ snapshot: boolean;
+ limit: number;
+}
+
+export interface TokenPairsRequest {
+ tokenA?: string;
+ tokenB?: string;
+}
+
+export interface TokenPairsItem {
+ tokenA: TokenTradeInfo;
+ tokenB: TokenTradeInfo;
+}
+
+export interface TokenTradeInfo {
+ address: string;
+ minAmount: BigNumber;
+ maxAmount: BigNumber;
+ precision: number;
+}
+
+export interface OrdersRequest {
+ exchangeContractAddress?: string;
+ tokenAddress?: string;
+ makerTokenAddress?: string;
+ takerTokenAddress?: string;
+ tokenA?: string;
+ tokenB?: string;
+ maker?: string;
+ taker?: string;
+ trader?: string;
+ feeRecipient?: string;
+}
+
+export interface OrderbookRequest {
+ baseTokenAddress: string;
+ quoteTokenAddress: string;
+}
+
+export interface OrderbookResponse {
+ bids: SignedOrder[];
+ asks: SignedOrder[];
+}
+
+export interface FeesRequest {
+ exchangeContractAddress: string;
+ maker: string;
+ taker: string;
+ makerTokenAddress: string;
+ takerTokenAddress: string;
+ makerTokenAmount: BigNumber;
+ takerTokenAmount: BigNumber;
+ expirationUnixTimestampSec: BigNumber;
+ salt: BigNumber;
+}
+
+export interface FeesResponse {
+ feeRecipient: string;
+ makerFee: BigNumber;
+ takerFee: BigNumber;
+}
diff --git a/packages/connect/src/utils/orderbook_channel_message_parsers.ts b/packages/connect/src/utils/orderbook_channel_message_parsers.ts
new file mode 100644
index 000000000..b590b189b
--- /dev/null
+++ b/packages/connect/src/utils/orderbook_channel_message_parsers.ts
@@ -0,0 +1,43 @@
+import * as _ from 'lodash';
+import {SignedOrder} from '0x.js';
+import {assert} from '@0xproject/assert';
+import {schemas} from '@0xproject/json-schemas';
+import {
+ OrderbookChannelMessage,
+ OrderbookChannelMessageTypes,
+} from '../types';
+import {typeConverters} from './type_converters';
+
+export const orderbookChannelMessageParsers = {
+ parser(utf8Data: string): OrderbookChannelMessage {
+ const messageObj = JSON.parse(utf8Data);
+ const type: string = _.get(messageObj, 'type');
+ assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
+ switch (type) {
+ case (OrderbookChannelMessageTypes.Snapshot): {
+ assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
+ const orderbook = messageObj.payload;
+ typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook);
+ return {
+ type,
+ payload: orderbook,
+ };
+ }
+ case (OrderbookChannelMessageTypes.Update): {
+ assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
+ const order = messageObj.payload;
+ typeConverters.convertOrderStringFieldsToBigNumber(order);
+ return {
+ type,
+ payload: order,
+ };
+ }
+ default: {
+ return {
+ type: OrderbookChannelMessageTypes.Unknown,
+ payload: undefined,
+ };
+ }
+ }
+ },
+};
diff --git a/packages/connect/src/utils/type_converters.ts b/packages/connect/src/utils/type_converters.ts
new file mode 100644
index 000000000..bf17a5629
--- /dev/null
+++ b/packages/connect/src/utils/type_converters.ts
@@ -0,0 +1,31 @@
+import * as _ from 'lodash';
+import {BigNumber} from 'bignumber.js';
+
+// TODO: convert all of these to non-mutating, pure functions
+export const typeConverters = {
+ convertOrderbookStringFieldsToBigNumber(orderbook: object): void {
+ _.each(orderbook, (orders: object[]) => {
+ _.each(orders, (order: object) => this.convertOrderStringFieldsToBigNumber(order));
+ });
+ },
+ convertOrderStringFieldsToBigNumber(order: object): void {
+ this.convertStringsFieldsToBigNumbers(order, [
+ 'makerTokenAmount',
+ 'takerTokenAmount',
+ 'makerFee',
+ 'takerFee',
+ 'expirationUnixTimestampSec',
+ 'salt',
+ ]);
+ },
+ convertBigNumberFieldsToStrings(obj: object, fields: string[]): void {
+ _.each(fields, field => {
+ _.update(obj, field, (value: BigNumber) => value.toString());
+ });
+ },
+ convertStringsFieldsToBigNumbers(obj: object, fields: string[]): void {
+ _.each(fields, field => {
+ _.update(obj, field, (value: string) => new BigNumber(value));
+ });
+ },
+};
diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts
new file mode 100644
index 000000000..78b823dbe
--- /dev/null
+++ b/packages/connect/src/ws_orderbook_channel.ts
@@ -0,0 +1,127 @@
+import * as _ from 'lodash';
+import * as WebSocket from 'websocket';
+import {assert} from '@0xproject/assert';
+import {schemas} from '@0xproject/json-schemas';
+import {SignedOrder} from '0x.js';
+import {
+ OrderbookChannel,
+ OrderbookChannelHandler,
+ OrderbookChannelMessageTypes,
+ OrderbookChannelSubscriptionOpts,
+} from './types';
+import {orderbookChannelMessageParsers} from './utils/orderbook_channel_message_parsers';
+
+enum ConnectionEventType {
+ Close = 'close',
+ Error = 'error',
+ Message = 'message',
+}
+
+enum ClientEventType {
+ Connect = 'connect',
+ ConnectFailed = 'connectFailed',
+}
+
+/**
+ * This class includes all the functionality related to interacting with a websocket endpoint
+ * that implements the standard relayer API v0
+ */
+export class WebSocketOrderbookChannel implements OrderbookChannel {
+ private apiEndpointUrl: string;
+ private client: WebSocket.client;
+ private connectionIfExists?: WebSocket.connection;
+ /**
+ * Instantiates a new WebSocketOrderbookChannel instance
+ * @param url The base url for making API calls
+ * @return An instance of WebSocketOrderbookChannel
+ */
+ constructor(url: string) {
+ assert.isUri('url', url);
+ this.apiEndpointUrl = url;
+ this.client = new WebSocket.client();
+ }
+ /**
+ * Subscribe to orderbook snapshots and updates from the websocket
+ * @param subscriptionOpts An OrderbookChannelSubscriptionOpts instance describing which
+ * token pair to subscribe to
+ * @param handler An OrderbookChannelHandler instance that responds to various
+ * channel updates
+ */
+ public subscribe(subscriptionOpts: OrderbookChannelSubscriptionOpts, handler: OrderbookChannelHandler): void {
+ assert.doesConformToSchema(
+ 'subscriptionOpts', subscriptionOpts, schemas.relayerApiOrderbookChannelSubscribePayload);
+ assert.isFunction('handler.onSnapshot', _.get(handler, 'onSnapshot'));
+ assert.isFunction('handler.onUpdate', _.get(handler, 'onUpdate'));
+ assert.isFunction('handler.onError', _.get(handler, 'onError'));
+ assert.isFunction('handler.onClose', _.get(handler, 'onClose'));
+ const subscribeMessage = {
+ type: 'subscribe',
+ channel: 'orderbook',
+ payload: subscriptionOpts,
+ };
+ this._getConnection((error, connection) => {
+ if (!_.isUndefined(error)) {
+ handler.onError(this, error);
+ } else if (!_.isUndefined(connection) && connection.connected) {
+ connection.on(ConnectionEventType.Error, wsError => {
+ handler.onError(this, wsError);
+ });
+ connection.on(ConnectionEventType.Close, () => {
+ handler.onClose(this);
+ });
+ connection.on(ConnectionEventType.Message, message => {
+ this._handleWebSocketMessage(message, handler);
+ });
+ connection.sendUTF(JSON.stringify(subscribeMessage));
+ }
+ });
+ }
+ /**
+ * Close the websocket and stop receiving updates
+ */
+ public close() {
+ if (!_.isUndefined(this.connectionIfExists)) {
+ this.connectionIfExists.close();
+ }
+ }
+ private _getConnection(callback: (error?: Error, connection?: WebSocket.connection) => void) {
+ if (!_.isUndefined(this.connectionIfExists) && this.connectionIfExists.connected) {
+ callback(undefined, this.connectionIfExists);
+ } else {
+ this.client.on(ClientEventType.Connect, connection => {
+ this.connectionIfExists = connection;
+ callback(undefined, this.connectionIfExists);
+ });
+ this.client.on(ClientEventType.ConnectFailed, error => {
+ callback(error, undefined);
+ });
+ this.client.connect(this.apiEndpointUrl);
+ }
+ }
+ private _handleWebSocketMessage(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}`));
+ }
+ }
+ } catch (error) {
+ handler.onError(this, error);
+ }
+ } else {
+ handler.onError(this, new Error(`Message does not contain utf8Data`));
+ }
+ }
+}