aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrandon Millman <brandon.millman@gmail.com>2017-12-20 06:22:38 +0800
committerBrandon Millman <brandon.millman@gmail.com>2017-12-25 10:01:28 +0800
commit8fe81c9d090ce50496f3150602f19433e7aedd1e (patch)
tree7e569cf16cd3a9ad9b09b5e64c5101d0c477f8fe
parent9f3acf8e2888b6105062e47664ecd5adaaf3c889 (diff)
downloaddexon-0x-contracts-8fe81c9d090ce50496f3150602f19433e7aedd1e.tar.gz
dexon-0x-contracts-8fe81c9d090ce50496f3150602f19433e7aedd1e.tar.zst
dexon-0x-contracts-8fe81c9d090ce50496f3150602f19433e7aedd1e.zip
Refactor JSON parsing in HttpClient
-rw-r--r--packages/connect/src/http_client.ts78
-rw-r--r--packages/connect/src/utils/orderbook_channel_message_parser.ts (renamed from packages/connect/src/utils/orderbook_channel_message_parsers.ts)18
-rw-r--r--packages/connect/src/utils/relayer_response_json_parsers.ts42
-rw-r--r--packages/connect/src/utils/type_converters.ts22
-rw-r--r--packages/connect/src/ws_orderbook_channel.ts4
-rw-r--r--packages/connect/test/orderbook_channel_message_parsers_test.ts20
6 files changed, 121 insertions, 63 deletions
diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts
index da052ba3c..f738c7e07 100644
--- a/packages/connect/src/http_client.ts
+++ b/packages/connect/src/http_client.ts
@@ -18,7 +18,7 @@ import {
TokenPairsItem,
TokenPairsRequest,
} from './types';
-import {typeConverters} from './utils/type_converters';
+import {relayerResponseJsonParsers} from './utils/relayer_response_json_parsers';
/**
* This class includes all the functionality related to interacting with a set of HTTP endpoints
@@ -48,18 +48,13 @@ export class HttpClient implements Client {
const requestOpts = {
params: request,
};
- const tokenPairs = await this._requestAsync('/token_pairs', HttpRequestType.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;
+ const result = await this._requestAsync(
+ '/token_pairs',
+ HttpRequestType.Get,
+ relayerResponseJsonParsers.parseTokenPairsJson,
+ requestOpts,
+ );
+ return result;
}
/**
* Retrieve orders from the API
@@ -73,10 +68,13 @@ export class HttpClient implements Client {
const requestOpts = {
params: request,
};
- const orders = await this._requestAsync(`/orders`, HttpRequestType.Get, requestOpts);
- assert.doesConformToSchema('orders', orders, schemas.signedOrdersSchema);
- _.each(orders, (order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
- return orders;
+ const result = await this._requestAsync(
+ `/orders`,
+ HttpRequestType.Get,
+ relayerResponseJsonParsers.parseOrdersJson,
+ requestOpts,
+ );
+ return result;
}
/**
* Retrieve a specific order from the API
@@ -85,10 +83,12 @@ export class HttpClient implements Client {
*/
public async getOrderAsync(orderHash: string): Promise<SignedOrder> {
assert.doesConformToSchema('orderHash', orderHash, schemas.orderHashSchema);
- const order = await this._requestAsync(`/order/${orderHash}`, HttpRequestType.Get);
- assert.doesConformToSchema('order', order, schemas.signedOrderSchema);
- typeConverters.convertOrderStringFieldsToBigNumber(order);
- return order;
+ const result = await this._requestAsync(
+ `/order/${orderHash}`,
+ HttpRequestType.Get,
+ relayerResponseJsonParsers.parseOrderJson,
+ );
+ return result;
}
/**
* Retrieve an orderbook from the API
@@ -100,10 +100,13 @@ export class HttpClient implements Client {
const requestOpts = {
params: request,
};
- const orderBook = await this._requestAsync('/orderbook', HttpRequestType.Get, requestOpts);
- assert.doesConformToSchema('orderBook', orderBook, schemas.relayerApiOrderBookResponseSchema);
- typeConverters.convertOrderbookStringFieldsToBigNumber(orderBook);
- return orderBook;
+ const result = await this._requestAsync(
+ '/orderbook',
+ HttpRequestType.Get,
+ relayerResponseJsonParsers.parseOrderbookResponseJson,
+ requestOpts,
+ );
+ return result;
}
/**
* Retrieve fee information from the API
@@ -115,10 +118,13 @@ export class HttpClient implements Client {
const requestOpts = {
payload: request,
};
- const fees = await this._requestAsync('/fees', HttpRequestType.Post, requestOpts);
- assert.doesConformToSchema('fees', fees, schemas.relayerApiFeesResponseSchema);
- typeConverters.convertStringsFieldsToBigNumbers(fees, ['makerFee', 'takerFee']);
- return fees;
+ const result = await this._requestAsync(
+ '/fees',
+ HttpRequestType.Post,
+ relayerResponseJsonParsers.parseFeesResponseJson,
+ requestOpts,
+ );
+ return result;
}
/**
* Submit a signed order to the API
@@ -129,10 +135,16 @@ export class HttpClient implements Client {
const requestOpts = {
payload: signedOrder,
};
- await this._requestAsync('/order', HttpRequestType.Post, requestOpts);
+ await this._requestAsync(
+ '/order',
+ HttpRequestType.Post,
+ _.noop,
+ requestOpts,
+ );
}
- private async _requestAsync(path: string, requestType: HttpRequestType,
- requestOptions?: HttpRequestOptions): Promise<any> {
+ private async _requestAsync<T>(path: string, requestType: HttpRequestType,
+ jsonParser: (json: any) => T,
+ requestOptions?: HttpRequestOptions): Promise<T> {
const params = _.get(requestOptions, 'params');
const payload = _.get(requestOptions, 'payload');
let query = '';
@@ -154,6 +166,6 @@ export class HttpClient implements Client {
throw Error(response.statusText);
}
const json = await response.json();
- return json;
+ return jsonParser(json);
}
}
diff --git a/packages/connect/src/utils/orderbook_channel_message_parsers.ts b/packages/connect/src/utils/orderbook_channel_message_parser.ts
index d9a84cca2..adc565d29 100644
--- a/packages/connect/src/utils/orderbook_channel_message_parsers.ts
+++ b/packages/connect/src/utils/orderbook_channel_message_parser.ts
@@ -7,10 +7,10 @@ import {
OrderbookChannelMessageTypes,
} from '../types';
-import {typeConverters} from './type_converters';
+import {relayerResponseJsonParsers} from './relayer_response_json_parsers';
-export const orderbookChannelMessageParsers = {
- parser(utf8Data: string): OrderbookChannelMessage {
+export const orderbookChannelMessageParser = {
+ parse(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}`);
@@ -18,15 +18,15 @@ export const orderbookChannelMessageParsers = {
switch (type) {
case (OrderbookChannelMessageTypes.Snapshot): {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelSnapshotSchema);
- const orderbook = messageObj.payload;
- typeConverters.convertOrderbookStringFieldsToBigNumber(orderbook);
- return messageObj;
+ const orderbookJson = messageObj.payload;
+ const orderbook = relayerResponseJsonParsers.parseOrderbookResponseJson(orderbookJson);
+ return _.assign(messageObj, {payload: orderbook});
}
case (OrderbookChannelMessageTypes.Update): {
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrderbookChannelUpdateSchema);
- const order = messageObj.payload;
- typeConverters.convertOrderStringFieldsToBigNumber(order);
- return messageObj;
+ const orderJson = messageObj.payload;
+ const order = relayerResponseJsonParsers.parseOrderJson(orderJson);
+ return _.assign(messageObj, {payload: order});
}
default: {
return {
diff --git a/packages/connect/src/utils/relayer_response_json_parsers.ts b/packages/connect/src/utils/relayer_response_json_parsers.ts
new file mode 100644
index 000000000..02d88f26d
--- /dev/null
+++ b/packages/connect/src/utils/relayer_response_json_parsers.ts
@@ -0,0 +1,42 @@
+import {assert} from '@0xproject/assert';
+import {schemas} from '@0xproject/json-schemas';
+import * as _ from 'lodash';
+
+import {
+ FeesResponse,
+ OrderbookResponse,
+ SignedOrder,
+ TokenPairsItem,
+} from '../types';
+
+import {typeConverters} from './type_converters';
+
+export const relayerResponseJsonParsers = {
+ parseTokenPairsJson(json: any): TokenPairsItem[] {
+ assert.doesConformToSchema('tokenPairs', json, schemas.relayerApiTokenPairsResponseSchema);
+ return json.map((tokenPair: any) => {
+ return typeConverters.convertStringsFieldsToBigNumbers(tokenPair, [
+ 'tokenA.minAmount',
+ 'tokenA.maxAmount',
+ 'tokenB.minAmount',
+ 'tokenB.maxAmount',
+ ]);
+ });
+ },
+ parseOrdersJson(json: any): SignedOrder[] {
+ assert.doesConformToSchema('orders', json, schemas.signedOrdersSchema);
+ return json.map((order: object) => typeConverters.convertOrderStringFieldsToBigNumber(order));
+ },
+ parseOrderJson(json: any): SignedOrder {
+ assert.doesConformToSchema('order', json, schemas.signedOrderSchema);
+ return typeConverters.convertOrderStringFieldsToBigNumber(json);
+ },
+ parseOrderbookResponseJson(json: any): OrderbookResponse {
+ assert.doesConformToSchema('orderBook', json, schemas.relayerApiOrderBookResponseSchema);
+ return typeConverters.convertOrderbookStringFieldsToBigNumber(json);
+ },
+ parseFeesResponseJson(json: any): FeesResponse {
+ assert.doesConformToSchema('fees', json, schemas.relayerApiFeesResponseSchema);
+ return typeConverters.convertStringsFieldsToBigNumbers(json, ['makerFee', 'takerFee']);
+ },
+};
diff --git a/packages/connect/src/utils/type_converters.ts b/packages/connect/src/utils/type_converters.ts
index 28810af1a..5ba1b8291 100644
--- a/packages/connect/src/utils/type_converters.ts
+++ b/packages/connect/src/utils/type_converters.ts
@@ -1,15 +1,17 @@
import {BigNumber} from 'bignumber.js';
import * as _ from 'lodash';
-// 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));
- });
+ convertOrderbookStringFieldsToBigNumber(orderbook: any): any {
+ const bids = _.get(orderbook, 'bids', []);
+ const asks = _.get(orderbook, 'asks', []);
+ return {
+ bids: bids.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
+ asks: asks.map((order: any) => this.convertOrderStringFieldsToBigNumber(order)),
+ };
},
- convertOrderStringFieldsToBigNumber(order: object): void {
- this.convertStringsFieldsToBigNumbers(order, [
+ convertOrderStringFieldsToBigNumber(order: any): any {
+ return this.convertStringsFieldsToBigNumbers(order, [
'makerTokenAmount',
'takerTokenAmount',
'makerFee',
@@ -18,9 +20,11 @@ export const typeConverters = {
'salt',
]);
},
- convertStringsFieldsToBigNumbers(obj: object, fields: string[]): void {
+ convertStringsFieldsToBigNumbers(obj: any, fields: string[]): any {
+ const result = _.assign({}, obj);
_.each(fields, field => {
- _.update(obj, field, (value: string) => new BigNumber(value));
+ _.update(result, field, (value: string) => new BigNumber(value));
});
+ return result;
},
};
diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts
index a669d8094..849fb9a4e 100644
--- a/packages/connect/src/ws_orderbook_channel.ts
+++ b/packages/connect/src/ws_orderbook_channel.ts
@@ -11,7 +11,7 @@ import {
WebsocketClientEventType,
WebsocketConnectionEventType,
} from './types';
-import {orderbookChannelMessageParsers} from './utils/orderbook_channel_message_parsers';
+import {orderbookChannelMessageParser} from './utils/orderbook_channel_message_parser';
/**
* This class includes all the functionality related to interacting with a websocket endpoint
@@ -97,7 +97,7 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
if (!_.isUndefined(message.utf8Data)) {
try {
const utf8Data = message.utf8Data;
- const parserResult = orderbookChannelMessageParsers.parser(utf8Data);
+ const parserResult = orderbookChannelMessageParser.parse(utf8Data);
if (parserResult.requestId === requestId) {
switch (parserResult.type) {
case (OrderbookChannelMessageTypes.Snapshot): {
diff --git a/packages/connect/test/orderbook_channel_message_parsers_test.ts b/packages/connect/test/orderbook_channel_message_parsers_test.ts
index 2c776b095..65286f87b 100644
--- a/packages/connect/test/orderbook_channel_message_parsers_test.ts
+++ b/packages/connect/test/orderbook_channel_message_parsers_test.ts
@@ -2,7 +2,7 @@ import * as chai from 'chai';
import * as dirtyChai from 'dirty-chai';
import 'mocha';
-import {orderbookChannelMessageParsers} from '../src/utils/orderbook_channel_message_parsers';
+import {orderbookChannelMessageParser} from '../src/utils/orderbook_channel_message_parser';
// tslint:disable-next-line:max-line-length
import {orderResponse} from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
@@ -21,20 +21,20 @@ chai.config.includeStack = true;
chai.use(dirtyChai);
const expect = chai.expect;
-describe('orderbookChannelMessageParsers', () => {
+describe('orderbookChannelMessageParser', () => {
describe('#parser', () => {
it('parses snapshot messages', () => {
- const snapshotMessage = orderbookChannelMessageParsers.parser(snapshotOrderbookChannelMessage);
+ const snapshotMessage = orderbookChannelMessageParser.parse(snapshotOrderbookChannelMessage);
expect(snapshotMessage.type).to.be.equal('snapshot');
expect(snapshotMessage.payload).to.be.deep.equal(orderbookResponse);
});
it('parses update messages', () => {
- const updateMessage = orderbookChannelMessageParsers.parser(updateOrderbookChannelMessage);
+ const updateMessage = orderbookChannelMessageParser.parse(updateOrderbookChannelMessage);
expect(updateMessage.type).to.be.equal('update');
expect(updateMessage.payload).to.be.deep.equal(orderResponse);
});
it('returns unknown message for messages with unsupported types', () => {
- const unknownMessage = orderbookChannelMessageParsers.parser(unknownOrderbookChannelMessage);
+ const unknownMessage = orderbookChannelMessageParser.parse(unknownOrderbookChannelMessage);
expect(unknownMessage.type).to.be.equal('unknown');
expect(unknownMessage.payload).to.be.undefined();
});
@@ -44,7 +44,7 @@ describe('orderbookChannelMessageParsers', () => {
"requestId": 1,
"payload": {}
}`;
- const badCall = () => orderbookChannelMessageParsers.parser(typelessMessage);
+ const badCall = () => orderbookChannelMessageParser.parse(typelessMessage);
expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
});
it('throws when type is not a string', () => {
@@ -54,24 +54,24 @@ describe('orderbookChannelMessageParsers', () => {
"requestId": 1,
"payload": {}
}`;
- const badCall = () => orderbookChannelMessageParsers.parser(messageWithBadType);
+ const badCall = () => orderbookChannelMessageParser.parse(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);
+ orderbookChannelMessageParser.parse(malformedSnapshotOrderbookChannelMessage);
// tslint:disable-next-line:max-line-length
const errMsg = 'Validation errors: instance.payload requires property "bids", instance.payload requires property "asks"';
expect(badCall).throws(errMsg);
});
it('throws when update message has malformed payload', () => {
const badCall = () =>
- orderbookChannelMessageParsers.parser(malformedUpdateOrderbookChannelMessage);
+ orderbookChannelMessageParser.parse(malformedUpdateOrderbookChannelMessage);
expect(badCall).throws(/^Expected message to conform to schema/);
});
it('throws when input message is not valid JSON', () => {
const nonJsonString = 'h93b{sdfs9fsd f';
- const badCall = () => orderbookChannelMessageParsers.parser(nonJsonString);
+ const badCall = () => orderbookChannelMessageParser.parse(nonJsonString);
expect(badCall).throws('Unexpected token h in JSON at position 0');
});
});