Skip to content

Commit

Permalink
Support nodeUrls (near#733)
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveYuOWO committed Jan 25, 2022
1 parent 0139380 commit 65b35fa
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 27 deletions.
5 changes: 5 additions & 0 deletions lib/near.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 11 additions & 1 deletion lib/near.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion lib/providers/json-rpc-provider.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions lib/providers/json-rpc-provider.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion lib/utils/web.d.ts

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 11 additions & 6 deletions lib/utils/web.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion src/near.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,12 @@ export interface NearConfig {
*/
nodeUrl: string;

/**
* NEAR RPC API urls. Compatibility with existing nodeUrl, multiple URLs for JSON RPC calls.
* @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider}
*/
nodeUrls: string;

/**
* NEAR RPC API headers. Can be used to pass API KEY and other parameters.
* @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider}
Expand Down Expand Up @@ -82,9 +88,21 @@ export class Near {

constructor(config: NearConfig) {
this.config = config;

const nodeUrls = [];
if(config.nodeUrl) {
nodeUrls.push(config.nodeUrl);
}
if(config.nodeUrls) {
nodeUrls.push(...config.nodeUrls);
}
if(nodeUrls.length === 0) {
throw new Error('Need to pass nodeUrl or nodeUrls.');
}

this.connection = Connection.fromConfig({
networkId: config.networkId,
provider: { type: 'JsonRpcProvider', args: { url: config.nodeUrl, headers: config.headers } },
provider: { type: 'JsonRpcProvider', args: { selectUrlIndex: 0, urls: nodeUrls, headers: config.headers } },
signer: config.signer || { type: 'InMemorySigner', keyStore: config.keyStore || (config.deps && config.deps.keyStore) }
});
if (config.masterAccount) {
Expand Down
8 changes: 4 additions & 4 deletions src/providers/json-rpc-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ export class JsonRpcProvider extends Provider {
/**
* @param connectionInfoOrUrl ConnectionInfo or RPC API endpoint URL (deprecated)
*/
constructor(connectionInfoOrUrl?: string | ConnectionInfo) {
constructor(connectionInfoOrUrls?: string | ConnectionInfo) {
super();
if (connectionInfoOrUrl != null && typeof connectionInfoOrUrl == 'object') {
this.connection = connectionInfoOrUrl as ConnectionInfo;
if (connectionInfoOrUrls != null && typeof connectionInfoOrUrls == 'object') {
this.connection = connectionInfoOrUrls as ConnectionInfo;
} else {
const deprecate = depd('JsonRpcProvider(url?: string)');
deprecate('use `JsonRpcProvider(connectionInfo: ConnectionInfo)` instead');
this.connection = { url: connectionInfoOrUrl as string };
this.connection = { selectUrlIndex: 0, urls: [connectionInfoOrUrls as string] };
}
}

Expand Down
21 changes: 13 additions & 8 deletions src/utils/web.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ const BACKOFF_MULTIPLIER = 1.5;
const RETRY_NUMBER = 10;

export interface ConnectionInfo {
url: string;
selectUrlIndex: number;
urls: string[];
user?: string;
password?: string;
allowInsecure?: boolean;
Expand All @@ -18,38 +19,42 @@ export interface ConnectionInfo {
}

export async function fetchJson(connectionInfoOrUrl: string | ConnectionInfo, json?: string): Promise<any> {
let connectionInfo: ConnectionInfo = { url: null };
let connectionInfo: ConnectionInfo = { selectUrlIndex: 0, urls: [] };
let selectUrlIndex = 0;
if (typeof (connectionInfoOrUrl) === 'string') {
connectionInfo.url = connectionInfoOrUrl;
connectionInfo.urls = [connectionInfoOrUrl];
} else {
connectionInfo = connectionInfoOrUrl as ConnectionInfo;
}

const response = await exponentialBackoff(START_WAIT_TIME_MS, RETRY_NUMBER, BACKOFF_MULTIPLIER, async () => {
if(connectionInfo.selectUrlIndex) {
selectUrlIndex = connectionInfo.selectUrlIndex;
}
connectionInfo.selectUrlIndex = (selectUrlIndex + 1) % connectionInfo.urls.length;
try {
const response = await fetch(connectionInfo.url, {
const response = await fetch(connectionInfo.urls[selectUrlIndex], {
method: json ? 'POST' : 'GET',
body: json ? json : undefined,
headers: { ...connectionInfo.headers, 'Content-Type': 'application/json' }
});
if (!response.ok) {
if (response.status === 503) {
logWarning(`Retrying HTTP request for ${connectionInfo.url} as it's not available now`);
logWarning(`Retrying HTTP request for ${connectionInfo.urls[selectUrlIndex]} as it's not available now`);
return null;
}
throw createError(response.status, await response.text());
}
return response;
} catch (error) {
if (error.toString().includes('FetchError') || error.toString().includes('Failed to fetch')) {
logWarning(`Retrying HTTP request for ${connectionInfo.url} because of error: ${error}`);
logWarning(`Retrying HTTP request for ${connectionInfo.urls[selectUrlIndex]} because of error: ${error}`);
return null;
}
throw error;
}
});
if (!response) {
throw new TypedError(`Exceeded ${RETRY_NUMBER} attempts for ${connectionInfo.url}.`, 'RetriesExceeded');
throw new TypedError(`Exceeded ${RETRY_NUMBER} attempts for ${connectionInfo.urls[selectUrlIndex]}.`, 'RetriesExceeded');
}
return await response.json();
}
2 changes: 1 addition & 1 deletion test/utils/web.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ describe('web', () => {
expect(result.result.chain_id).toBe('testnet');
});
test('object parameter in fetchJson', async () => {
const connection = { url: 'https://rpc.testnet.near.org' };
const connection = { urls: ['https://rpc.testnet.near.org'], selectUrlIndex: 0 };
const statusRequest = {
'jsonrpc': '2.0',
'id': 'dontcare',
Expand Down

0 comments on commit 65b35fa

Please sign in to comment.