Merge pull request #539 from 0xProject/feature/website/backend-client

Implement backendClient object to unify calls to the portal api
This commit is contained in:
Brandon Millman
2018-04-18 22:25:03 -04:00
committed by GitHub
7 changed files with 123 additions and 52 deletions

View File

@@ -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",

View File

@@ -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

View File

@@ -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<WalletProps, WalletState> {
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({

View File

@@ -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<WikiProps, WikiState> {
);
}
private async _fetchArticlesBySectionAsync(): Promise<void> {
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) {

View File

@@ -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

View File

@@ -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<WebsiteBackendGasInfo> {
const result = await requestAsync(ETH_GAS_STATION_ENDPOINT);
return result;
},
async getPriceInfosAsync(tokenAddresses: string[]): Promise<WebsiteBackendPriceInfo[]> {
if (_.isEmpty(tokenAddresses)) {
return [];
}
const joinedTokenAddresses = tokenAddresses.join(',');
const queryParams = {
tokens: joinedTokenAddresses,
};
const result = await requestAsync(PRICES_ENDPOINT, queryParams);
return result;
},
async getWikiArticlesBySectionAsync(): Promise<ArticlesBySection> {
const result = await requestAsync(WIKI_ENDPOINT);
return result;
},
};
async function requestAsync(endpoint: string, queryParams?: object): Promise<any> {
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}`;
}

View File

@@ -275,7 +275,7 @@
dependencies:
"@types/node" "*"
"@types/query-string@^5.0.1":
"@types/query-string@^5.0.1", "@types/query-string@^5.1.0":
version "5.1.0"
resolved "https://registry.yarnpkg.com/@types/query-string/-/query-string-5.1.0.tgz#7f40cdea49ddafa0ea4f3db35fb6c24d3bfd4dcc"
@@ -8451,6 +8451,13 @@ query-string@^5.0.1:
object-assign "^4.1.0"
strict-uri-encode "^1.0.0"
query-string@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.0.0.tgz#8b8f39447b73e8290d6f5e3581779218e9171142"
dependencies:
decode-uri-component "^0.2.0"
strict-uri-encode "^2.0.0"
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@@ -9930,6 +9937,10 @@ strict-uri-encode@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
strict-uri-encode@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
string-editor@^0.1.0:
version "0.1.2"
resolved "https://registry.yarnpkg.com/string-editor/-/string-editor-0.1.2.tgz#f5ff1b5ac4aed7ac6c2fb8de236d1551b20f61d0"