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:
@@ -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",
|
||||
|
@@ -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
|
||||
|
@@ -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({
|
||||
|
@@ -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) {
|
||||
|
@@ -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
|
||||
|
59
packages/website/ts/utils/backend_client.ts
Normal file
59
packages/website/ts/utils/backend_client.ts
Normal 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}`;
|
||||
}
|
13
yarn.lock
13
yarn.lock
@@ -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"
|
||||
|
Reference in New Issue
Block a user