Update websocket for SRA v2
This commit is contained in:
parent
f2d1d95355
commit
075e3a41c8
@ -1,4 +1,12 @@
|
||||
[
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"changes": [
|
||||
{
|
||||
"note": "Updated for SRA v2"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"timestamp": 1534210131,
|
||||
"version": "1.0.5",
|
||||
|
@ -31,7 +31,7 @@ export interface OrdersChannelHandler {
|
||||
onUpdate: (
|
||||
channel: OrdersChannel,
|
||||
subscriptionOpts: OrdersChannelSubscriptionOpts,
|
||||
order: APIOrder,
|
||||
orders: APIOrder[],
|
||||
) => void;
|
||||
onError: (channel: OrdersChannel, err: Error, subscriptionOpts?: OrdersChannelSubscriptionOpts) => void;
|
||||
onClose: (channel: OrdersChannel) => void;
|
||||
@ -48,13 +48,13 @@ export enum OrdersChannelMessageTypes {
|
||||
|
||||
export interface UpdateOrdersChannelMessage {
|
||||
type: OrdersChannelMessageTypes.Update;
|
||||
requestId: number;
|
||||
payload: APIOrder;
|
||||
requestId: string;
|
||||
payload: APIOrder[];
|
||||
}
|
||||
|
||||
export interface UnknownOrdersChannelMessage {
|
||||
type: OrdersChannelMessageTypes.Unknown;
|
||||
requestId: number;
|
||||
requestId: string;
|
||||
payload: undefined;
|
||||
}
|
||||
|
||||
|
@ -15,15 +15,15 @@ export const ordersChannelMessageParser = {
|
||||
assert.assert(!_.isUndefined(type), `Message is missing a type parameter: ${utf8Data}`);
|
||||
assert.isString('type', type);
|
||||
// ensure we have a request id for the resulting message
|
||||
const requestId: number = _.get(messageObj, 'requestId');
|
||||
const requestId: string = _.get(messageObj, 'requestId');
|
||||
assert.assert(!_.isUndefined(requestId), `Message is missing a requestId parameter: ${utf8Data}`);
|
||||
assert.isNumber('requestId', requestId);
|
||||
assert.isString('requestId', requestId);
|
||||
switch (type) {
|
||||
case OrdersChannelMessageTypes.Update: {
|
||||
assert.doesConformToSchema('message', messageObj, schemas.relayerApiOrdersChannelUpdateSchema);
|
||||
const orderJson = messageObj.payload;
|
||||
const order = relayerResponseJsonParsers.parseAPIOrderJson(orderJson);
|
||||
return _.assign(messageObj, { payload: order });
|
||||
const ordersJson = messageObj.payload;
|
||||
const orders = relayerResponseJsonParsers.parseAPIOrdersJson(ordersJson);
|
||||
return _.assign(messageObj, { payload: orders });
|
||||
}
|
||||
default: {
|
||||
return {
|
@ -22,7 +22,10 @@ export const relayerResponseJsonParsers = {
|
||||
},
|
||||
parseOrdersJson(json: any): OrdersResponse {
|
||||
assert.doesConformToSchema('relayerApiOrdersResponse', json, schemas.relayerApiOrdersResponseSchema);
|
||||
return { ...json, records: json.records.map(relayerResponseJsonParsers.parseAPIOrderJson.bind(relayerResponseJsonParsers)) };
|
||||
return { ...json, records: relayerResponseJsonParsers.parseAPIOrdersJson(json.records) };
|
||||
},
|
||||
parseAPIOrdersJson(json: any): APIOrder[] {
|
||||
return json.map(relayerResponseJsonParsers.parseAPIOrderJson.bind(relayerResponseJsonParsers));
|
||||
},
|
||||
parseAPIOrderJson(json: any): APIOrder {
|
||||
assert.doesConformToSchema('relayerApiOrder', json, schemas.relayerApiOrderSchema);
|
||||
|
@ -9,7 +9,11 @@ import {
|
||||
OrdersChannelSubscriptionOpts,
|
||||
} from './types';
|
||||
import { assert } from './utils/assert';
|
||||
import { ordersChannelMessageParser } from './utils/orderbook_channel_message_parser';
|
||||
import { ordersChannelMessageParser } from './utils/orders_channel_message_parser';
|
||||
|
||||
export interface OrdersChannelSubscriptionOptsMap {
|
||||
[key: string]: OrdersChannelSubscriptionOpts;
|
||||
}
|
||||
|
||||
/**
|
||||
* This class includes all the functionality related to interacting with a websocket endpoint
|
||||
@ -18,7 +22,7 @@ import { ordersChannelMessageParser } from './utils/orderbook_channel_message_pa
|
||||
export class WebSocketOrdersChannel implements OrdersChannel {
|
||||
private readonly _client: WebSocket.w3cwebsocket;
|
||||
private readonly _handler: OrdersChannelHandler;
|
||||
private readonly _subscriptionOptsList: OrdersChannelSubscriptionOpts[] = [];
|
||||
private readonly _subscriptionOptsMap: OrdersChannelSubscriptionOptsMap = {};
|
||||
/**
|
||||
* Instantiates a new WebSocketOrdersChannel instance
|
||||
* @param client A WebSocket client
|
||||
@ -50,11 +54,12 @@ export class WebSocketOrdersChannel implements OrdersChannel {
|
||||
public subscribe(subscriptionOpts: OrdersChannelSubscriptionOpts): void {
|
||||
assert.isOrdersChannelSubscriptionOpts('subscriptionOpts', subscriptionOpts);
|
||||
assert.assert(this._client.readyState === WebSocket.w3cwebsocket.OPEN, 'WebSocket connection is closed');
|
||||
this._subscriptionOptsList.push(subscriptionOpts);
|
||||
const requestId = uuid();
|
||||
this._subscriptionOptsMap[requestId] = subscriptionOpts;
|
||||
const subscribeMessage = {
|
||||
type: 'subscribe',
|
||||
channel: 'orders',
|
||||
requestId: uuid(),
|
||||
requestId,
|
||||
payload: subscriptionOpts,
|
||||
};
|
||||
this._client.send(JSON.stringify(subscribeMessage));
|
||||
@ -73,7 +78,7 @@ export class WebSocketOrdersChannel implements OrdersChannel {
|
||||
try {
|
||||
const data = message.data;
|
||||
const parserResult = ordersChannelMessageParser.parse(data);
|
||||
const subscriptionOpts = this._subscriptionOptsList[parserResult.requestId];
|
||||
const subscriptionOpts = this._subscriptionOptsMap[parserResult.requestId];
|
||||
if (_.isUndefined(subscriptionOpts)) {
|
||||
this._handler.onError(
|
||||
this,
|
||||
|
@ -5,6 +5,6 @@ const orderJSONString = JSON.stringify(orderResponseJSON);
|
||||
export const unknownOrdersChannelMessage = `{
|
||||
"type": "superGoodUpdate",
|
||||
"channel": "orderbook",
|
||||
"requestId": 1,
|
||||
"payload": ${orderJSONString}
|
||||
"requestId": "6ce8c5a6-5c46-4027-a44a-51831c77b8a1",
|
||||
"payload": [${orderJSONString}]
|
||||
}`;
|
@ -1,17 +0,0 @@
|
||||
import * as orderResponseJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
|
||||
|
||||
const orderJSONString = JSON.stringify(orderResponseJSON);
|
||||
|
||||
export const updateOrdersChannelMessage = `{
|
||||
"type": "update",
|
||||
"channel": "orderbook",
|
||||
"requestId": 1,
|
||||
"payload": ${orderJSONString}
|
||||
}`;
|
||||
|
||||
export const malformedUpdateOrdersChannelMessage = `{
|
||||
"type": "update",
|
||||
"channel": "orderbook",
|
||||
"requestId": 1,
|
||||
"payload": {}
|
||||
}`;
|
17
packages/connect/test/fixtures/standard_relayer_api/update_orders_channel_message.ts
vendored
Normal file
17
packages/connect/test/fixtures/standard_relayer_api/update_orders_channel_message.ts
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
import * as apiOrderJSON from './order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f.json';
|
||||
|
||||
const apiOrderJSONString = JSON.stringify(apiOrderJSON);
|
||||
|
||||
export const updateOrdersChannelMessage = `{
|
||||
"type": "update",
|
||||
"channel": "orders",
|
||||
"requestId": "5a1ce3a2-22b9-41e6-a615-68077512e9e2",
|
||||
"payload": [${apiOrderJSONString}]
|
||||
}`;
|
||||
|
||||
export const malformedUpdateOrdersChannelMessage = `{
|
||||
"type": "update",
|
||||
"channel": "orders",
|
||||
"requestId": "4d8efcee-adde-4475-9601-f0b30962ca2b",
|
||||
"payload": {}
|
||||
}`;
|
@ -191,6 +191,3 @@ describe('HttpClient', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// https://example.com/fee_recipients?networkId=42&page=3&perPage=50
|
||||
// https://example.com/fee_recipients?networkId=42&page=3&perPage=50
|
@ -9,15 +9,9 @@ chai.config.includeStack = true;
|
||||
chai.use(dirtyChai);
|
||||
const expect = chai.expect;
|
||||
const emptyOrdersChannelHandler = {
|
||||
onUpdate: () => {
|
||||
_.noop();
|
||||
},
|
||||
onError: () => {
|
||||
_.noop();
|
||||
},
|
||||
onClose: () => {
|
||||
_.noop();
|
||||
},
|
||||
onUpdate: _.noop,
|
||||
onError: _.noop,
|
||||
onClose: _.noop,
|
||||
};
|
||||
|
||||
describe('ordersChannelFactory', () => {
|
@ -2,14 +2,14 @@ import * as chai from 'chai';
|
||||
import * as dirtyChai from 'dirty-chai';
|
||||
import 'mocha';
|
||||
|
||||
import { ordersChannelMessageParser } from '../src/utils/orderbook_channel_message_parser';
|
||||
import { ordersChannelMessageParser } from '../src/utils/orders_channel_message_parser';
|
||||
|
||||
import { orderResponse } from './fixtures/standard_relayer_api/order/0xabc67323774bdbd24d94f977fa9ac94a50f016026fd13f42990861238897721f';
|
||||
import { unknownOrdersChannelMessage } from './fixtures/standard_relayer_api/unknown_orderbook_channel_message';
|
||||
import { unknownOrdersChannelMessage } from './fixtures/standard_relayer_api/unknown_orders_channel_message';
|
||||
import {
|
||||
malformedUpdateOrdersChannelMessage,
|
||||
updateOrdersChannelMessage,
|
||||
} from './fixtures/standard_relayer_api/update_orderbook_channel_message';
|
||||
} from './fixtures/standard_relayer_api/update_orders_channel_message';
|
||||
|
||||
chai.config.includeStack = true;
|
||||
chai.use(dirtyChai);
|
||||
@ -20,7 +20,7 @@ describe('ordersChannelMessageParser', () => {
|
||||
it('parses update messages', () => {
|
||||
const updateMessage = ordersChannelMessageParser.parse(updateOrdersChannelMessage);
|
||||
expect(updateMessage.type).to.be.equal('update');
|
||||
expect(updateMessage.payload).to.be.deep.equal(orderResponse);
|
||||
expect(updateMessage.payload).to.be.deep.equal([orderResponse]);
|
||||
});
|
||||
it('returns unknown message for messages with unsupported types', () => {
|
||||
const unknownMessage = ordersChannelMessageParser.parse(unknownOrdersChannelMessage);
|
||||
@ -29,9 +29,9 @@ describe('ordersChannelMessageParser', () => {
|
||||
});
|
||||
it('throws when message does not include a type', () => {
|
||||
const typelessMessage = `{
|
||||
"channel": "orderbook",
|
||||
"requestId": 1,
|
||||
"payload": {}
|
||||
"channel": "orders",
|
||||
"requestId": "4d8efcee-adde-4475-9601-f0b30962ca2b",
|
||||
"payload": []
|
||||
}`;
|
||||
const badCall = () => ordersChannelMessageParser.parse(typelessMessage);
|
||||
expect(badCall).throws(`Message is missing a type parameter: ${typelessMessage}`);
|
||||
@ -39,9 +39,9 @@ describe('ordersChannelMessageParser', () => {
|
||||
it('throws when type is not a string', () => {
|
||||
const messageWithBadType = `{
|
||||
"type": 1,
|
||||
"channel": "orderbook",
|
||||
"requestId": 1,
|
||||
"payload": {}
|
||||
"channel": "orders",
|
||||
"requestId": "4d8efcee-adde-4475-9601-f0b30962ca2b",
|
||||
"payload": []
|
||||
}`;
|
||||
const badCall = () => ordersChannelMessageParser.parse(messageWithBadType);
|
||||
expect(badCall).throws('Expected type to be of type string, encountered: 1');
|
||||
@ -53,7 +53,7 @@ describe('ordersChannelMessageParser', () => {
|
||||
it('throws when input message is not valid JSON', () => {
|
||||
const nonJsonString = 'h93b{sdfs9fsd f';
|
||||
const badCall = () => ordersChannelMessageParser.parse(nonJsonString);
|
||||
expect(badCall).throws('Unexpected assetData h in JSON at position 0');
|
||||
expect(badCall).throws('Unexpected token h in JSON at position 0');
|
||||
});
|
||||
});
|
||||
});
|
@ -11,18 +11,9 @@ chai.config.includeStack = true;
|
||||
chai.use(dirtyChai);
|
||||
const expect = chai.expect;
|
||||
const emptyOrdersChannelHandler = {
|
||||
onSnapshot: () => {
|
||||
_.noop();
|
||||
},
|
||||
onUpdate: () => {
|
||||
_.noop();
|
||||
},
|
||||
onError: () => {
|
||||
_.noop();
|
||||
},
|
||||
onClose: () => {
|
||||
_.noop();
|
||||
},
|
||||
onUpdate: _.noop,
|
||||
onError: _.noop,
|
||||
onClose: _.noop,
|
||||
};
|
||||
|
||||
describe('WebSocketOrdersChannel', () => {
|
||||
@ -34,15 +25,14 @@ describe('WebSocketOrdersChannel', () => {
|
||||
const subscriptionOpts = {
|
||||
baseAssetData: '0x323b5d4c32345ced77393b3530b1eed0f346429d',
|
||||
quoteAssetData: '0xef7fff64389b814a946f3e92105513705ca6b990',
|
||||
snapshot: true,
|
||||
limit: 100,
|
||||
};
|
||||
describe('#subscribe', () => {
|
||||
it('throws when subscriptionOpts does not conform to schema', () => {
|
||||
const badSubscribeCall = openOrdersChannel.subscribe.bind(openOrdersChannel, {});
|
||||
expect(badSubscribeCall).throws(
|
||||
'Expected subscriptionOpts to conform to schema /RelayerApiOrdersChannelSubscribePayload\nEncountered: {}\nValidation errors: instance requires property "baseAssetData", instance requires property "quoteAssetData"',
|
||||
);
|
||||
const badSubscribeCall = openOrdersChannel.subscribe.bind(openOrdersChannel, {
|
||||
makerAssetData: 5,
|
||||
});
|
||||
expect(badSubscribeCall).throws();
|
||||
});
|
||||
it('does not throw when inputs are of correct types', () => {
|
||||
const goodSubscribeCall = openOrdersChannel.subscribe.bind(openOrdersChannel, subscriptionOpts);
|
Loading…
x
Reference in New Issue
Block a user