aboutsummaryrefslogtreecommitdiffstats
path: root/packages/connect
diff options
context:
space:
mode:
Diffstat (limited to 'packages/connect')
-rw-r--r--packages/connect/CHANGELOG.md7
-rw-r--r--packages/connect/package.json10
-rw-r--r--packages/connect/scripts/postpublish.js2
-rw-r--r--packages/connect/scripts/stagedocs.js24
-rw-r--r--packages/connect/src/http_client.ts17
-rw-r--r--packages/connect/src/index.ts1
-rw-r--r--packages/connect/src/schemas/schemas.ts2
-rw-r--r--packages/connect/src/schemas/websocket_orderbook_channel_config_schema.ts10
-rw-r--r--packages/connect/src/types.ts7
-rw-r--r--packages/connect/src/ws_orderbook_channel.ts36
-rw-r--r--packages/connect/test/http_client_test.ts6
11 files changed, 101 insertions, 21 deletions
diff --git a/packages/connect/CHANGELOG.md b/packages/connect/CHANGELOG.md
index 18c808e5e..75ffb5274 100644
--- a/packages/connect/CHANGELOG.md
+++ b/packages/connect/CHANGELOG.md
@@ -1,8 +1,13 @@
# CHANGELOG
-## v0.6.0 - _TBD, 2018_
+## v0.6.1 - _February 16, 2018_
+
+ * Fix JSON parse empty response (#407)
+
+## v0.6.0 - _February 16, 2018_
* Add pagination options to HttpClient methods (#393)
+ * Add heartbeat configuration to WebSocketOrderbookChannel constructor (#406)
## v0.5.7 - _February 9, 2018_
diff --git a/packages/connect/package.json b/packages/connect/package.json
index d1e18adf5..c5c8f4ea0 100644
--- a/packages/connect/package.json
+++ b/packages/connect/package.json
@@ -1,6 +1,6 @@
{
"name": "@0xproject/connect",
- "version": "0.5.8",
+ "version": "0.6.1",
"description": "A javascript library for interacting with the standard relayer api",
"keywords": [
"connect",
@@ -37,9 +37,9 @@
},
"homepage": "https://github.com/0xProject/0x.js/packages/connect/README.md",
"dependencies": {
- "@0xproject/assert": "^0.0.19",
- "@0xproject/json-schemas": "^0.7.11",
- "@0xproject/utils": "^0.3.3",
+ "@0xproject/assert": "^0.0.20",
+ "@0xproject/json-schemas": "^0.7.12",
+ "@0xproject/utils": "^0.3.4",
"isomorphic-fetch": "^2.2.1",
"lodash": "^4.17.4",
"query-string": "^5.0.1",
@@ -65,6 +65,6 @@
"tslint": "5.8.0",
"typedoc": "~0.8.0",
"typescript": "2.7.1",
- "web3-typescript-typings": "^0.9.10"
+ "web3-typescript-typings": "^0.9.11"
}
}
diff --git a/packages/connect/scripts/postpublish.js b/packages/connect/scripts/postpublish.js
index 24384b228..869bad099 100644
--- a/packages/connect/scripts/postpublish.js
+++ b/packages/connect/scripts/postpublish.js
@@ -14,7 +14,7 @@ postpublish_utils
tag = result.tag;
version = result.version;
const releaseName = postpublish_utils.getReleaseName(subPackageName, version);
- return postpublish_utils.publishReleaseNotes(tag, releaseName);
+ return postpublish_utils.publishReleaseNotesAsync(tag, releaseName);
})
.then(function(release) {
console.log('POSTPUBLISH: Release successful, generating docs...');
diff --git a/packages/connect/scripts/stagedocs.js b/packages/connect/scripts/stagedocs.js
new file mode 100644
index 000000000..868112a37
--- /dev/null
+++ b/packages/connect/scripts/stagedocs.js
@@ -0,0 +1,24 @@
+const execAsync = require('async-child-process').execAsync;
+const postpublish_utils = require('../../../scripts/postpublish_utils');
+
+const cwd = __dirname + '/..';
+const S3BucketPath = 's3://staging-connect-docs-jsons/';
+const jsonFilePath = __dirname + '/../' + postpublish_utils.generatedDocsDirectoryName + '/index.json';
+const version = process.env.DOCS_VERSION;
+
+execAsync('JSON_FILE_PATH=' + jsonFilePath + ' PROJECT_DIR=' + __dirname + '/.. yarn docs:json', {
+ cwd,
+})
+.then(function(result) {
+ if (result.stderr !== '') {
+ throw new Error(result.stderr);
+ }
+ const fileName = 'v' + version + '.json';
+ const s3Url = S3BucketPath + fileName;
+ return execAsync('S3_URL=' + s3Url + ' yarn upload_docs_json', {
+ cwd,
+ });
+})
+.catch(function(err) {
+ console.log(err);
+});
diff --git a/packages/connect/src/http_client.ts b/packages/connect/src/http_client.ts
index 815d42e67..cf0aaef0d 100644
--- a/packages/connect/src/http_client.ts
+++ b/packages/connect/src/http_client.ts
@@ -23,7 +23,7 @@ import { relayerResponseJsonParsers } from './utils/relayer_response_json_parser
const TRAILING_SLASHES_REGEX = /\/+$/;
const DEFAULT_PAGED_REQUEST_OPTS: PagedRequestOpts = {
- page: 0,
+ page: 1,
perPage: 100,
};
/**
@@ -66,7 +66,7 @@ export class HttpClient implements Client {
}
/**
* Retrieve token pair info from the API
- * @param requestOpts Options specifying token information to retrieve and page information, defaults to { page: 0, perPage: 100 }
+ * @param requestOpts Options specifying token information to retrieve and page information, defaults to { page: 1, perPage: 100 }
* @return The resulting TokenPairsItems that match the request
*/
public async getTokenPairsAsync(requestOpts?: TokenPairsRequestOpts & PagedRequestOpts): Promise<TokenPairsItem[]> {
@@ -83,7 +83,7 @@ export class HttpClient implements Client {
}
/**
* Retrieve orders from the API
- * @param requestOpts Options specifying orders to retrieve and page information, defaults to { page: 0, perPage: 100 }
+ * @param requestOpts Options specifying orders to retrieve and page information, defaults to { page: 1, perPage: 100 }
* @return The resulting SignedOrders that match the request
*/
public async getOrdersAsync(requestOpts?: OrdersRequestOpts & PagedRequestOpts): Promise<SignedOrder[]> {
@@ -112,7 +112,7 @@ export class HttpClient implements Client {
/**
* Retrieve an orderbook from the API
* @param request An OrderbookRequest instance describing the specific orderbook to retrieve
- * @param requestOpts Options specifying page information, defaults to { page: 0, perPage: 100 }
+ * @param requestOpts Options specifying page information, defaults to { page: 1, perPage: 100 }
* @return The resulting OrderbookResponse that matches the request
*/
public async getOrderbookAsync(
@@ -172,13 +172,12 @@ export class HttpClient implements Client {
body: JSON.stringify(payload),
headers,
});
- const json = await response.json();
+ const text = await response.text();
if (!response.ok) {
- const errorString = `${response.status} - ${response.statusText}\n${requestType} ${url}\n${JSON.stringify(
- json,
- )}`;
+ const errorString = `${response.status} - ${response.statusText}\n${requestType} ${url}\n${text}`;
throw Error(errorString);
}
- return json;
+ const result = !_.isEmpty(text) ? JSON.parse(text) : undefined;
+ return result;
}
}
diff --git a/packages/connect/src/index.ts b/packages/connect/src/index.ts
index 344a32e28..bb42384f9 100644
--- a/packages/connect/src/index.ts
+++ b/packages/connect/src/index.ts
@@ -17,4 +17,5 @@ export {
TokenPairsItem,
TokenPairsRequestOpts,
TokenTradeInfo,
+ WebSocketOrderbookChannelConfig,
} from './types';
diff --git a/packages/connect/src/schemas/schemas.ts b/packages/connect/src/schemas/schemas.ts
index 0b8b798a9..b9a8472fb 100644
--- a/packages/connect/src/schemas/schemas.ts
+++ b/packages/connect/src/schemas/schemas.ts
@@ -3,6 +3,7 @@ import { orderBookRequestSchema } from './orderbook_request_schema';
import { ordersRequestOptsSchema } from './orders_request_opts_schema';
import { pagedRequestOptsSchema } from './paged_request_opts_schema';
import { tokenPairsRequestOptsSchema } from './token_pairs_request_opts_schema';
+import { webSocketOrderbookChannelConfigSchema } from './websocket_orderbook_channel_config_schema';
export const schemas = {
feesRequestSchema,
@@ -10,4 +11,5 @@ export const schemas = {
ordersRequestOptsSchema,
pagedRequestOptsSchema,
tokenPairsRequestOptsSchema,
+ webSocketOrderbookChannelConfigSchema,
};
diff --git a/packages/connect/src/schemas/websocket_orderbook_channel_config_schema.ts b/packages/connect/src/schemas/websocket_orderbook_channel_config_schema.ts
new file mode 100644
index 000000000..81c0cac9c
--- /dev/null
+++ b/packages/connect/src/schemas/websocket_orderbook_channel_config_schema.ts
@@ -0,0 +1,10 @@
+export const webSocketOrderbookChannelConfigSchema = {
+ id: '/WebSocketOrderbookChannelConfig',
+ type: 'object',
+ properties: {
+ heartbeatIntervalMs: {
+ type: 'number',
+ minimum: 10,
+ },
+ },
+};
diff --git a/packages/connect/src/types.ts b/packages/connect/src/types.ts
index 970eff498..5f837b0b3 100644
--- a/packages/connect/src/types.ts
+++ b/packages/connect/src/types.ts
@@ -44,6 +44,13 @@ export interface OrderbookChannel {
}
/*
+ * heartbeatInterval: Interval in milliseconds that the orderbook channel should ping the underlying websocket. Default: 15000
+ */
+export interface WebSocketOrderbookChannelConfig {
+ heartbeatIntervalMs?: number;
+}
+
+/*
* 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
diff --git a/packages/connect/src/ws_orderbook_channel.ts b/packages/connect/src/ws_orderbook_channel.ts
index 822a022f4..5aa730d8f 100644
--- a/packages/connect/src/ws_orderbook_channel.ts
+++ b/packages/connect/src/ws_orderbook_channel.ts
@@ -3,6 +3,7 @@ import { schemas } from '@0xproject/json-schemas';
import * as _ from 'lodash';
import * as WebSocket from 'websocket';
+import { schemas as clientSchemas } from './schemas/schemas';
import {
OrderbookChannel,
OrderbookChannelHandler,
@@ -10,9 +11,13 @@ import {
OrderbookChannelSubscriptionOpts,
WebsocketClientEventType,
WebsocketConnectionEventType,
+ WebSocketOrderbookChannelConfig,
} from './types';
import { orderbookChannelMessageParser } from './utils/orderbook_channel_message_parser';
+const DEFAULT_HEARTBEAT_INTERVAL_MS = 15000;
+const MINIMUM_HEARTBEAT_INTERVAL_MS = 10;
+
/**
* This class includes all the functionality related to interacting with a websocket endpoint
* that implements the standard relayer API v0
@@ -21,15 +26,25 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
private _apiEndpointUrl: string;
private _client: WebSocket.client;
private _connectionIfExists?: WebSocket.connection;
+ private _heartbeatTimerIfExists?: NodeJS.Timer;
private _subscriptionCounter = 0;
+ private _heartbeatIntervalMs: number;
/**
* Instantiates a new WebSocketOrderbookChannel instance
* @param url The relayer API base WS url you would like to interact with
+ * @param config The configuration object. Look up the type for the description.
* @return An instance of WebSocketOrderbookChannel
*/
- constructor(url: string) {
+ constructor(url: string, config?: WebSocketOrderbookChannelConfig) {
assert.isUri('url', url);
+ if (!_.isUndefined(config)) {
+ assert.doesConformToSchema('config', config, clientSchemas.webSocketOrderbookChannelConfigSchema);
+ }
this._apiEndpointUrl = url;
+ this._heartbeatIntervalMs =
+ _.isUndefined(config) || _.isUndefined(config.heartbeatIntervalMs)
+ ? DEFAULT_HEARTBEAT_INTERVAL_MS
+ : config.heartbeatIntervalMs;
this._client = new WebSocket.client();
}
/**
@@ -63,7 +78,7 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
connection.on(WebsocketConnectionEventType.Error, wsError => {
handler.onError(this, subscriptionOpts, wsError);
});
- connection.on(WebsocketConnectionEventType.Close, () => {
+ connection.on(WebsocketConnectionEventType.Close, (code: number, desc: string) => {
handler.onClose(this, subscriptionOpts);
});
connection.on(WebsocketConnectionEventType.Message, message => {
@@ -80,6 +95,9 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
if (!_.isUndefined(this._connectionIfExists)) {
this._connectionIfExists.close();
}
+ if (!_.isUndefined(this._heartbeatTimerIfExists)) {
+ clearInterval(this._heartbeatTimerIfExists);
+ }
}
private _getConnection(callback: (error?: Error, connection?: WebSocket.connection) => void) {
if (!_.isUndefined(this._connectionIfExists) && this._connectionIfExists.connected) {
@@ -87,6 +105,20 @@ export class WebSocketOrderbookChannel implements OrderbookChannel {
} else {
this._client.on(WebsocketClientEventType.Connect, connection => {
this._connectionIfExists = connection;
+ if (this._heartbeatIntervalMs >= MINIMUM_HEARTBEAT_INTERVAL_MS) {
+ this._heartbeatTimerIfExists = setInterval(() => {
+ connection.ping('');
+ }, this._heartbeatIntervalMs);
+ } else {
+ callback(
+ new Error(
+ `Heartbeat interval is ${
+ this._heartbeatIntervalMs
+ }ms which is less than the required minimum of ${MINIMUM_HEARTBEAT_INTERVAL_MS}ms`,
+ ),
+ undefined,
+ );
+ }
callback(undefined, this._connectionIfExists);
});
this._client.on(WebsocketClientEventType.ConnectFailed, error => {
diff --git a/packages/connect/test/http_client_test.ts b/packages/connect/test/http_client_test.ts
index 93b252ace..311dc96e6 100644
--- a/packages/connect/test/http_client_test.ts
+++ b/packages/connect/test/http_client_test.ts
@@ -41,7 +41,7 @@ describe('HttpClient', () => {
describe('#getTokenPairsAsync', () => {
const url = `${relayUrl}/token_pairs`;
it('gets token pairs with default options when none are provided', async () => {
- const urlWithQuery = `${url}?page=0&per_page=100`;
+ const urlWithQuery = `${url}?page=1&per_page=100`;
fetchMock.get(urlWithQuery, tokenPairsResponseJSON);
const tokenPairs = await relayerClient.getTokenPairsAsync();
expect(tokenPairs).to.be.deep.equal(tokenPairsResponse);
@@ -66,7 +66,7 @@ describe('HttpClient', () => {
describe('#getOrdersAsync', () => {
const url = `${relayUrl}/orders`;
it('gets orders with default options when none are provided', async () => {
- const urlWithQuery = `${url}?page=0&per_page=100`;
+ const urlWithQuery = `${url}?page=1&per_page=100`;
fetchMock.get(urlWithQuery, ordersResponseJSON);
const orders = await relayerClient.getOrdersAsync();
expect(orders).to.be.deep.equal(ordersResponse);
@@ -110,7 +110,7 @@ describe('HttpClient', () => {
it('gets orderbook with default page options when none are provided', async () => {
const urlWithQuery = `${url}?baseTokenAddress=${
request.baseTokenAddress
- }&page=0&per_page=100&quoteTokenAddress=${request.quoteTokenAddress}`;
+ }&page=1&per_page=100&quoteTokenAddress=${request.quoteTokenAddress}`;
fetchMock.get(urlWithQuery, orderbookJSON);
const orderbook = await relayerClient.getOrderbookAsync(request);
expect(orderbook).to.be.deep.equal(orderbookResponse);