From 51b2fce8c13c8e9393558d34dd664b7dd7749941 Mon Sep 17 00:00:00 2001 From: Brandon Millman Date: Wed, 18 Apr 2018 15:22:02 -0400 Subject: Implement backendClient object to unify calls to the portal api --- packages/website/package.json | 2 + packages/website/ts/blockchain.ts | 16 +++---- packages/website/ts/components/wallet/wallet.tsx | 23 ++++----- packages/website/ts/pages/wiki/wiki.tsx | 53 ++++++++++----------- packages/website/ts/types.ts | 9 ++++ packages/website/ts/utils/backend_client.ts | 59 ++++++++++++++++++++++++ 6 files changed, 111 insertions(+), 51 deletions(-) create mode 100644 packages/website/ts/utils/backend_client.ts (limited to 'packages/website') diff --git a/packages/website/package.json b/packages/website/package.json index a8011dc8d..d77d60207 100644 --- a/packages/website/package.json +++ b/packages/website/package.json @@ -32,6 +32,7 @@ "lodash": "^4.17.4", "material-ui": "^0.17.1", "moment": "2.21.0", + "query-string": "^6.0.0", "react": "15.6.1", "react-copy-to-clipboard": "^4.2.3", "react-document-title": "^2.0.3", @@ -58,6 +59,7 @@ "@types/lodash": "4.14.104", "@types/material-ui": "0.18.0", "@types/node": "^8.0.53", + "@types/query-string": "^5.1.0", "@types/react": "^16.0.34", "@types/react-copy-to-clipboard": "^4.2.0", "@types/react-dom": "^16.0.3", diff --git a/packages/website/ts/blockchain.ts b/packages/website/ts/blockchain.ts index 3edc00644..e90dfa747 100644 --- a/packages/website/ts/blockchain.ts +++ b/packages/website/ts/blockchain.ts @@ -47,6 +47,7 @@ import { Token, TokenByAddress, } from 'ts/types'; +import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { errorReporter } from 'ts/utils/error_reporter'; @@ -854,14 +855,13 @@ export class Blockchain { } } private async _updateDefaultGasPriceAsync() { - const endpoint = `${configs.BACKEND_BASE_URL}/eth_gas_station`; - const response = await fetch(endpoint); - if (response.status !== 200) { - return; // noop and we keep hard-coded default + try { + const gasInfo = await backendClient.getGasInfoAsync(); + const gasPriceInGwei = new BigNumber(gasInfo.average / 10); + const gasPriceInWei = gasPriceInGwei.mul(1000000000); + this._defaultGasPrice = gasPriceInWei; + } catch (err) { + return; } - const gasInfo = await response.json(); - const gasPriceInGwei = new BigNumber(gasInfo.average / 10); - const gasPriceInWei = gasPriceInGwei.mul(1000000000); - this._defaultGasPrice = gasPriceInWei; } } // tslint:disable:max-file-line-count diff --git a/packages/website/ts/components/wallet/wallet.tsx b/packages/website/ts/components/wallet/wallet.tsx index 8c9e3be0f..d1ae38550 100644 --- a/packages/website/ts/components/wallet/wallet.tsx +++ b/packages/website/ts/components/wallet/wallet.tsx @@ -36,7 +36,7 @@ import { TokenState, TokenStateByAddress, } from 'ts/types'; -import { configs } from 'ts/utils/configs'; +import { backendClient } from 'ts/utils/backend_client'; import { constants } from 'ts/utils/constants'; import { utils } from 'ts/utils/utils'; import { styles as walletItemStyles } from 'ts/utils/wallet_item_styles'; @@ -72,11 +72,6 @@ interface AccessoryItemConfig { allowanceToggleConfig?: AllowanceToggleConfig; } -interface WebsiteBackendPriceInfo { - price: string; - address: string; -} - const styles: Styles = { root: { width: 346, @@ -496,17 +491,15 @@ export class Wallet extends React.Component { if (_.isEmpty(tokenAddresses)) { return {}; } - const tokenAddressesQueryString = tokenAddresses.join(','); - const endpoint = `${configs.BACKEND_BASE_URL}/prices?tokens=${tokenAddressesQueryString}`; - const response = await fetch(endpoint); - if (response.status !== 200) { + try { + const websiteBackendPriceInfos = await backendClient.getPriceInfosAsync(tokenAddresses); + const addresses = _.map(websiteBackendPriceInfos, info => info.address); + const prices = _.map(websiteBackendPriceInfos, info => new BigNumber(info.price)); + const pricesByAddress = _.zipObject(addresses, prices); + return pricesByAddress; + } catch (err) { return {}; } - const websiteBackendPriceInfos: WebsiteBackendPriceInfo[] = await response.json(); - const addresses = _.map(websiteBackendPriceInfos, info => info.address); - const prices = _.map(websiteBackendPriceInfos, info => new BigNumber(info.price)); - const pricesByAddress = _.zipObject(addresses, prices); - return pricesByAddress; } private _openWrappedEtherActionRow(wrappedEtherDirection: Side) { this.setState({ diff --git a/packages/website/ts/pages/wiki/wiki.tsx b/packages/website/ts/pages/wiki/wiki.tsx index 1330cbf86..7ed2b750d 100644 --- a/packages/website/ts/pages/wiki/wiki.tsx +++ b/packages/website/ts/pages/wiki/wiki.tsx @@ -19,6 +19,7 @@ import { SidebarHeader } from 'ts/components/sidebar_header'; import { TopBar } from 'ts/components/top_bar/top_bar'; import { Dispatcher } from 'ts/redux/dispatcher'; import { Article, ArticlesBySection, WebsitePaths } from 'ts/types'; +import { backendClient } from 'ts/utils/backend_client'; import { configs } from 'ts/utils/configs'; import { constants } from 'ts/utils/constants'; import { Translate } from 'ts/utils/translate'; @@ -200,34 +201,30 @@ export class Wiki extends React.Component { ); } private async _fetchArticlesBySectionAsync(): Promise { - const endpoint = `${configs.BACKEND_BASE_URL}${WebsitePaths.Wiki}`; - const response = await fetch(endpoint); - if (response.status === constants.HTTP_NO_CONTENT_STATUS_CODE) { - // We need to backoff and try fetching again later - this._wikiBackoffTimeoutId = window.setTimeout(() => { - // tslint:disable-next-line:no-floating-promises - this._fetchArticlesBySectionAsync(); - }, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS); - return; - } - if (response.status !== 200) { - // TODO: Show the user an error message when the wiki fail to load - const errMsg = await response.text(); - logUtils.log(`Failed to load wiki: ${response.status} ${errMsg}`); - return; - } - const articlesBySection = await response.json(); - if (!this._isUnmounted) { - this.setState( - { - articlesBySection, - }, - async () => { - await utils.onPageLoadAsync(); - const hash = this.props.location.hash.slice(1); - sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID); - }, - ); + try { + const articlesBySection = await backendClient.getWikiArticlesBySectionAsync(); + if (!this._isUnmounted) { + this.setState( + { + articlesBySection, + }, + async () => { + await utils.onPageLoadAsync(); + const hash = this.props.location.hash.slice(1); + sharedUtils.scrollToHash(hash, sharedConstants.SCROLL_CONTAINER_ID); + }, + ); + } + } catch (err) { + const errMsg = `${err}`; + if (_.includes(errMsg, `${constants.HTTP_NO_CONTENT_STATUS_CODE}`)) { + // We need to backoff and try fetching again later + this._wikiBackoffTimeoutId = window.setTimeout(() => { + // tslint:disable-next-line:no-floating-promises + this._fetchArticlesBySectionAsync(); + }, WIKI_NOT_READY_BACKOUT_TIMEOUT_MS); + return; + } } } private _getMenuSubsectionsBySection(articlesBySection: ArticlesBySection) { diff --git a/packages/website/ts/types.ts b/packages/website/ts/types.ts index 989c0a032..98d080afb 100644 --- a/packages/website/ts/types.ts +++ b/packages/website/ts/types.ts @@ -507,4 +507,13 @@ export interface RelayerInfo { marketShare: number; topTokens: Token[]; } + +export interface WebsiteBackendPriceInfo { + price: string; + address: string; +} + +export interface WebsiteBackendGasInfo { + average: number; +} // tslint:disable:max-file-line-count diff --git a/packages/website/ts/utils/backend_client.ts b/packages/website/ts/utils/backend_client.ts new file mode 100644 index 000000000..366519856 --- /dev/null +++ b/packages/website/ts/utils/backend_client.ts @@ -0,0 +1,59 @@ +import { BigNumber, logUtils } from '@0xproject/utils'; +import * as _ from 'lodash'; +import * as queryString from 'query-string'; + +import { ArticlesBySection, ItemByAddress, WebsiteBackendGasInfo, WebsiteBackendPriceInfo } from 'ts/types'; +import { configs } from 'ts/utils/configs'; +import { errorReporter } from 'ts/utils/error_reporter'; + +const ETH_GAS_STATION_ENDPOINT = '/eth_gas_station'; +const PRICES_ENDPOINT = '/prices'; +const WIKI_ENDPOINT = '/wiki'; + +export const backendClient = { + async getGasInfoAsync(): Promise { + const result = await requestAsync(ETH_GAS_STATION_ENDPOINT); + return result; + }, + async getPriceInfosAsync(tokenAddresses: string[]): Promise { + if (_.isEmpty(tokenAddresses)) { + return []; + } + const joinedTokenAddresses = tokenAddresses.join(','); + const queryParams = { + tokens: joinedTokenAddresses, + }; + const result = await requestAsync(PRICES_ENDPOINT, queryParams); + return result; + }, + async getWikiArticlesBySectionAsync(): Promise { + const result = await requestAsync(WIKI_ENDPOINT); + return result; + }, +}; + +async function requestAsync(endpoint: string, queryParams?: object): Promise { + const query = queryStringFromQueryParams(queryParams); + const url = `${configs.BACKEND_BASE_URL}${endpoint}${query}`; + const response = await fetch(url); + if (response.status !== 200) { + const errorText = `Error requesting url: ${url}, ${response.status}: ${response.statusText}`; + logUtils.log(errorText); + const error = Error(errorText); + // tslint:disable-next-line:no-floating-promises + errorReporter.reportAsync(error); + throw error; + } + const result = await response.json(); + return result; +} + +function queryStringFromQueryParams(queryParams?: object): string { + // if params are undefined or empty, return an empty string + if (_.isUndefined(queryParams) || _.isEmpty(queryParams)) { + return ''; + } + // stringify the formatted object + const stringifiedParams = queryString.stringify(queryParams); + return `?${stringifiedParams}`; +} -- cgit v1.2.3