From 1f7c47785cdae473a12c842d555dffa001a543c4 Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Mon, 20 Dec 2021 23:51:36 +0200 Subject: [PATCH 01/10] multiple API Keys support added to provider --- examples/cookbook/api-keys/provider-example.js | 5 ++--- lib/utils/web.d.ts | 3 +++ lib/utils/web.js | 6 +++++- src/utils/web.ts | 7 ++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/examples/cookbook/api-keys/provider-example.js b/examples/cookbook/api-keys/provider-example.js index 34ce9ad044..7710e76fdb 100644 --- a/examples/cookbook/api-keys/provider-example.js +++ b/examples/cookbook/api-keys/provider-example.js @@ -1,12 +1,11 @@ // demonstrates how to use API-KEY with provider const { providers } = require("near-api-js"); -const RPC_API_ENDPOINT = ''; const API_KEY = ''; const provider = new providers.JsonRpcProvider({ - url: RPC_API_ENDPOINT, - headers: { 'x-api-key': API_KEY }, + url: 'https://testnet.rpc.near.dev', + apiKeys: { 'https://testnet.rpc.near.dev': API_KEY } }); getNetworkStatus(); diff --git a/lib/utils/web.d.ts b/lib/utils/web.d.ts index 639cb740ea..fbf76af030 100644 --- a/lib/utils/web.d.ts +++ b/lib/utils/web.d.ts @@ -1,5 +1,8 @@ export interface ConnectionInfo { url: string; + apiKeys?: { + [url: string]: string; + }; user?: string; password?: string; allowInsecure?: boolean; diff --git a/lib/utils/web.js b/lib/utils/web.js index 2cd5a75c8b..476846acba 100644 --- a/lib/utils/web.js +++ b/lib/utils/web.js @@ -24,7 +24,11 @@ async function fetchJson(connectionInfoOrUrl, json) { const response = await fetch(connectionInfo.url, { method: json ? 'POST' : 'GET', body: json ? json : undefined, - headers: { ...connectionInfo.headers, 'Content-Type': 'application/json' } + headers: { + 'Content-Type': 'application/json', + 'x-api-key': connectionInfo.apiKeys ? connectionInfo.apiKeys[connectionInfo.url] : undefined, + ...connectionInfo.headers, + }, }); if (!response.ok) { if (response.status === 503) { diff --git a/src/utils/web.ts b/src/utils/web.ts index 8e8dfe6a15..e6dbc9b638 100644 --- a/src/utils/web.ts +++ b/src/utils/web.ts @@ -10,6 +10,7 @@ const RETRY_NUMBER = 10; export interface ConnectionInfo { url: string; + apiKeys?: { [url: string]: string } user?: string; password?: string; allowInsecure?: boolean; @@ -30,7 +31,11 @@ export async function fetchJson(connectionInfoOrUrl: string | ConnectionInfo, js const response = await fetch(connectionInfo.url, { method: json ? 'POST' : 'GET', body: json ? json : undefined, - headers: { ...connectionInfo.headers, 'Content-Type': 'application/json' } + headers: { + 'Content-Type': 'application/json', + 'x-api-key': connectionInfo.apiKeys ? connectionInfo.apiKeys[connectionInfo.url] : undefined, + ...connectionInfo.headers, + }, }); if (!response.ok) { if (response.status === 503) { From 35c70a6c254ff1c2464470b79590dccbc2da65bc Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Tue, 21 Dec 2021 00:41:49 +0200 Subject: [PATCH 02/10] multiple api keys support added to the top level API --- examples/cookbook/api-keys/near-connection.js | 7 +++---- lib/near.d.ts | 4 ++++ lib/near.js | 8 +++++++- src/near.ts | 13 +++++++++++-- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/examples/cookbook/api-keys/near-connection.js b/examples/cookbook/api-keys/near-connection.js index d263241461..b255169012 100644 --- a/examples/cookbook/api-keys/near-connection.js +++ b/examples/cookbook/api-keys/near-connection.js @@ -7,16 +7,15 @@ const CREDENTIALS_DIR = ".near-credentials"; const credentialsPath = path.join(homedir, CREDENTIALS_DIR); const keyStore = new keyStores.UnencryptedFileSystemKeyStore(credentialsPath); -const RPC_API_ENDPOINT = ''; const API_KEY = ''; - const ACCOUNT_ID = ''; const config = { networkId: 'testnet', keyStore, - nodeUrl: RPC_API_ENDPOINT, - headers: { 'x-api-key': API_KEY }, + nodeUrl: '', + // RPC server URL in apiKeys should match the one specified in nodeUrl + apiKeys: { '': API_KEY }, }; async function getState(accountId) { diff --git a/lib/near.d.ts b/lib/near.d.ts index 939162f8fa..5152a16da6 100644 --- a/lib/near.d.ts +++ b/lib/near.d.ts @@ -48,6 +48,10 @@ export interface NearConfig { * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} */ nodeUrl: string; + /** + * RPC API Keys. Used to authenticate users on RPC Server. + */ + apiKeys: string; /** * NEAR RPC API headers. Can be used to pass API KEY and other parameters. * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} diff --git a/lib/near.js b/lib/near.js index c74bce98a0..1d09c0379b 100644 --- a/lib/near.js +++ b/lib/near.js @@ -30,7 +30,13 @@ class Near { this.config = config; this.connection = connection_1.Connection.fromConfig({ networkId: config.networkId, - provider: { type: 'JsonRpcProvider', args: { url: config.nodeUrl, headers: config.headers } }, + provider: { + type: 'JsonRpcProvider', args: { + url: config.nodeUrl, + apiKeys: config.apiKeys, + headers: config.headers, + } + }, signer: config.signer || { type: 'InMemorySigner', keyStore: config.keyStore || (config.deps && config.deps.keyStore) } }); if (config.masterAccount) { diff --git a/src/near.ts b/src/near.ts index aeb7c9facb..70195484fc 100644 --- a/src/near.ts +++ b/src/near.ts @@ -54,7 +54,10 @@ export interface NearConfig { * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} */ nodeUrl: string; - + /** + * RPC API Keys. Used to authenticate users on RPC Server. + */ + apiKeys: string; /** * NEAR RPC API headers. Can be used to pass API KEY and other parameters. * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} @@ -84,7 +87,13 @@ export class Near { this.config = config; this.connection = Connection.fromConfig({ networkId: config.networkId, - provider: { type: 'JsonRpcProvider', args: { url: config.nodeUrl, headers: config.headers } }, + provider: { + type: 'JsonRpcProvider', args: { + url: config.nodeUrl, + apiKeys: config.apiKeys, + headers: config.headers, + } + }, signer: config.signer || { type: 'InMemorySigner', keyStore: config.keyStore || (config.deps && config.deps.keyStore) } }); if (config.masterAccount) { From 952558e8af8a5da6022ff7c767a0290293f96ff2 Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Tue, 21 Dec 2021 00:45:32 +0200 Subject: [PATCH 03/10] links replaced with placeholders --- examples/cookbook/api-keys/provider-example.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/cookbook/api-keys/provider-example.js b/examples/cookbook/api-keys/provider-example.js index 7710e76fdb..c0dcb932a5 100644 --- a/examples/cookbook/api-keys/provider-example.js +++ b/examples/cookbook/api-keys/provider-example.js @@ -4,8 +4,8 @@ const { providers } = require("near-api-js"); const API_KEY = ''; const provider = new providers.JsonRpcProvider({ - url: 'https://testnet.rpc.near.dev', - apiKeys: { 'https://testnet.rpc.near.dev': API_KEY } + url: '', + apiKeys: { '': API_KEY } }); getNetworkStatus(); From 2ec72007eeda14dbdff0e3509aa88fc254ca343d Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Tue, 21 Dec 2021 00:48:37 +0200 Subject: [PATCH 04/10] added disclaimer --- examples/cookbook/api-keys/provider-example.js | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/cookbook/api-keys/provider-example.js b/examples/cookbook/api-keys/provider-example.js index c0dcb932a5..2338991b0a 100644 --- a/examples/cookbook/api-keys/provider-example.js +++ b/examples/cookbook/api-keys/provider-example.js @@ -5,6 +5,7 @@ const API_KEY = ''; const provider = new providers.JsonRpcProvider({ url: '', + // RPC server URL in apiKeys should match the one specified in url apiKeys: { '': API_KEY } }); From 2cdb754aa195f38f974c2757825e95b652d763fa Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Thu, 23 Dec 2021 17:49:58 +0200 Subject: [PATCH 05/10] example added --- examples/cookbook/failover-configuration.js | 25 +++++++++++++++++++++ src/providers/json-rpc-provider.ts | 2 +- src/utils/web.ts | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 examples/cookbook/failover-configuration.js diff --git a/examples/cookbook/failover-configuration.js b/examples/cookbook/failover-configuration.js new file mode 100644 index 0000000000..3819269a62 --- /dev/null +++ b/examples/cookbook/failover-configuration.js @@ -0,0 +1,25 @@ +// Demonstrates how to use failover functionality with provider +const { providers } = require("near-api-js"); + +//TODO: replace with placeholders +const MAIN_RPC_SERVER = 'https://rpc.testnet.near.org'; +const FAILOVER_RPC_SERVER_1 = 'https://rpc.ci-testnet.near.org'; +const FAILOVER_RPC_SERVER_2 = 'https://testnet.rpc.near.dev'; //this one needs API Key + +// Provider example +const provider = new providers.JsonRpcProvider({ + url: MAIN_RPC_SERVER, + /* In case of several unsuccessful calls to the main RPC Server, + near-api-js will make an attempt to execute call on failover RPC servers. */ + failoverUrls: [FAILOVER_RPC_SERVER_1, FAILOVER_RPC_SERVER_2], +}); + +async function getNetworkStatus() { + const result = await provider.status(); + console.log(result); +} + +getNetworkStatus(); + +// Connection example +//TODO⏎ \ No newline at end of file diff --git a/src/providers/json-rpc-provider.ts b/src/providers/json-rpc-provider.ts index 64204630eb..227521f548 100644 --- a/src/providers/json-rpc-provider.ts +++ b/src/providers/json-rpc-provider.ts @@ -379,7 +379,7 @@ export class JsonRpcProvider extends Provider { return response; } catch (error) { if (error.type === 'TimeoutError') { - if (!process.env['NEAR_NO_LOGS']){ + if (!process.env['NEAR_NO_LOGS']) { console.warn(`Retrying request to ${method} as it has timed out`, params); } return null; diff --git a/src/utils/web.ts b/src/utils/web.ts index e6dbc9b638..257a58bbe6 100644 --- a/src/utils/web.ts +++ b/src/utils/web.ts @@ -10,6 +10,7 @@ const RETRY_NUMBER = 10; export interface ConnectionInfo { url: string; + failoverRpcUrls?: string[], apiKeys?: { [url: string]: string } user?: string; password?: string; From 93584a06daaa0e650482b254486489c7bcf56aa4 Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Thu, 23 Dec 2021 18:45:21 +0200 Subject: [PATCH 06/10] RPC server rotation function added --- examples/cookbook/failover-configuration.js | 6 ++---- lib/providers/json-rpc-provider.d.ts | 5 +++++ lib/providers/json-rpc-provider.js | 12 ++++++++++++ lib/utils/web.d.ts | 2 +- lib/utils/web.js | 11 ++++++----- src/providers/json-rpc-provider.ts | 11 +++++++++++ src/utils/web.ts | 16 +++++++++------- 7 files changed, 46 insertions(+), 17 deletions(-) diff --git a/examples/cookbook/failover-configuration.js b/examples/cookbook/failover-configuration.js index 3819269a62..b32f9a9087 100644 --- a/examples/cookbook/failover-configuration.js +++ b/examples/cookbook/failover-configuration.js @@ -8,10 +8,8 @@ const FAILOVER_RPC_SERVER_2 = 'https://testnet.rpc.near.dev'; //this one needs A // Provider example const provider = new providers.JsonRpcProvider({ - url: MAIN_RPC_SERVER, - /* In case of several unsuccessful calls to the main RPC Server, - near-api-js will make an attempt to execute call on failover RPC servers. */ - failoverUrls: [FAILOVER_RPC_SERVER_1, FAILOVER_RPC_SERVER_2], + // TODO: what is happening here + url: [MAIN_RPC_SERVER, FAILOVER_RPC_SERVER_1, FAILOVER_RPC_SERVER_2], }); async function getNetworkStatus() { diff --git a/lib/providers/json-rpc-provider.d.ts b/lib/providers/json-rpc-provider.d.ts index ba97db325d..e3a1970d86 100644 --- a/lib/providers/json-rpc-provider.d.ts +++ b/lib/providers/json-rpc-provider.d.ts @@ -155,6 +155,11 @@ export declare class JsonRpcProvider extends Provider { * @param blockId Block hash or height, or null for latest. */ gasPrice(blockId: BlockId | null): Promise; + /** + * Part of the RPC failover design. + * Changing current (first) rpc server and moves it to the and of the queue. + */ + private rotateRpcServers; /** * Directly call the RPC specifying the method and params * diff --git a/lib/providers/json-rpc-provider.js b/lib/providers/json-rpc-provider.js index c4fb1c2623..39a68c305e 100644 --- a/lib/providers/json-rpc-provider.js +++ b/lib/providers/json-rpc-provider.js @@ -294,6 +294,16 @@ class JsonRpcProvider extends provider_1.Provider { async gasPrice(blockId) { return await this.sendJsonRpc('gas_price', [blockId]); } + /** + * Part of the RPC failover design. + * Changing current (first) rpc server and moves it to the and of the queue. + */ + rotateRpcServers() { + if (typeof this.connection.url === 'string') { + return; + } + this.connection.url.push(this.connection.url.shift()); + } /** * Directly call the RPC specifying the method and params * @@ -337,6 +347,8 @@ class JsonRpcProvider extends provider_1.Provider { if (!process.env['NEAR_NO_LOGS']) { console.warn(`Retrying request to ${method} as it has timed out`, params); } + // switch to another server if it's available + this.rotateRpcServers(); return null; } throw error; diff --git a/lib/utils/web.d.ts b/lib/utils/web.d.ts index fbf76af030..5ceb9e457d 100644 --- a/lib/utils/web.d.ts +++ b/lib/utils/web.d.ts @@ -1,5 +1,5 @@ export interface ConnectionInfo { - url: string; + url: string | string[]; apiKeys?: { [url: string]: string; }; diff --git a/lib/utils/web.js b/lib/utils/web.js index 476846acba..2a3198005a 100644 --- a/lib/utils/web.js +++ b/lib/utils/web.js @@ -19,20 +19,21 @@ async function fetchJson(connectionInfoOrUrl, json) { else { connectionInfo = connectionInfoOrUrl; } + const currentRpcServer = typeof connectionInfo.url === 'string' ? connectionInfo.url : connectionInfo.url[0]; const response = await exponential_backoff_1.default(START_WAIT_TIME_MS, RETRY_NUMBER, BACKOFF_MULTIPLIER, async () => { try { - const response = await fetch(connectionInfo.url, { + const response = await fetch(currentRpcServer, { method: json ? 'POST' : 'GET', body: json ? json : undefined, headers: { 'Content-Type': 'application/json', - 'x-api-key': connectionInfo.apiKeys ? connectionInfo.apiKeys[connectionInfo.url] : undefined, + 'x-api-key': connectionInfo.apiKeys ? connectionInfo.apiKeys[currentRpcServer] : undefined, ...connectionInfo.headers, }, }); if (!response.ok) { if (response.status === 503) { - errors_1.logWarning(`Retrying HTTP request for ${connectionInfo.url} as it's not available now`); + errors_1.logWarning(`Retrying HTTP request for ${currentRpcServer} as it's not available now`); return null; } throw http_errors_1.default(response.status, await response.text()); @@ -41,14 +42,14 @@ async function fetchJson(connectionInfoOrUrl, json) { } catch (error) { if (error.toString().includes('FetchError') || error.toString().includes('Failed to fetch')) { - errors_1.logWarning(`Retrying HTTP request for ${connectionInfo.url} because of error: ${error}`); + errors_1.logWarning(`Retrying HTTP request for ${currentRpcServer} because of error: ${error}`); return null; } throw error; } }); if (!response) { - throw new providers_1.TypedError(`Exceeded ${RETRY_NUMBER} attempts for ${connectionInfo.url}.`, 'RetriesExceeded'); + throw new providers_1.TypedError(`Exceeded ${RETRY_NUMBER} attempts for ${currentRpcServer}.`, 'RetriesExceeded'); } return await response.json(); } diff --git a/src/providers/json-rpc-provider.ts b/src/providers/json-rpc-provider.ts index 227521f548..6a1a067e4b 100644 --- a/src/providers/json-rpc-provider.ts +++ b/src/providers/json-rpc-provider.ts @@ -339,6 +339,15 @@ export class JsonRpcProvider extends Provider { return await this.sendJsonRpc('gas_price', [blockId]); } + /** + * Part of the RPC failover design. + * Changing current (first) rpc server and moves it to the and of the queue. + */ + private rotateRpcServers() { + if (typeof this.connection.url === 'string') { return; } + this.connection.url.push(this.connection.url.shift()); + } + /** * Directly call the RPC specifying the method and params * @@ -382,6 +391,8 @@ export class JsonRpcProvider extends Provider { if (!process.env['NEAR_NO_LOGS']) { console.warn(`Retrying request to ${method} as it has timed out`, params); } + // switch to another server if it's available + this.rotateRpcServers(); return null; } diff --git a/src/utils/web.ts b/src/utils/web.ts index 257a58bbe6..60c45f0351 100644 --- a/src/utils/web.ts +++ b/src/utils/web.ts @@ -9,8 +9,8 @@ const BACKOFF_MULTIPLIER = 1.5; const RETRY_NUMBER = 10; export interface ConnectionInfo { - url: string; - failoverRpcUrls?: string[], + // RPC Server URL or the prioritized array of such URLs + url: string | string[]; apiKeys?: { [url: string]: string } user?: string; password?: string; @@ -27,20 +27,22 @@ export async function fetchJson(connectionInfoOrUrl: string | ConnectionInfo, js connectionInfo = connectionInfoOrUrl as ConnectionInfo; } + const currentRpcServer = typeof connectionInfo.url === 'string' ? connectionInfo.url : connectionInfo.url[0]; + const response = await exponentialBackoff(START_WAIT_TIME_MS, RETRY_NUMBER, BACKOFF_MULTIPLIER, async () => { try { - const response = await fetch(connectionInfo.url, { + const response = await fetch(currentRpcServer, { method: json ? 'POST' : 'GET', body: json ? json : undefined, headers: { 'Content-Type': 'application/json', - 'x-api-key': connectionInfo.apiKeys ? connectionInfo.apiKeys[connectionInfo.url] : undefined, + 'x-api-key': connectionInfo.apiKeys ? connectionInfo.apiKeys[currentRpcServer] : undefined, ...connectionInfo.headers, }, }); 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 ${currentRpcServer} as it's not available now`); return null; } throw createError(response.status, await response.text()); @@ -48,14 +50,14 @@ export async function fetchJson(connectionInfoOrUrl: string | ConnectionInfo, js 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 ${currentRpcServer} 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 ${currentRpcServer}.`, 'RetriesExceeded'); } return await response.json(); } From fb77268e81e770498f7bb1f35ab0d5344e4ca1c1 Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Thu, 23 Dec 2021 18:49:28 +0200 Subject: [PATCH 07/10] added console logs for server rotation --- lib/providers/json-rpc-provider.js | 6 +++++- src/providers/json-rpc-provider.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/providers/json-rpc-provider.js b/lib/providers/json-rpc-provider.js index 39a68c305e..6a06219ab1 100644 --- a/lib/providers/json-rpc-provider.js +++ b/lib/providers/json-rpc-provider.js @@ -302,7 +302,11 @@ class JsonRpcProvider extends provider_1.Provider { if (typeof this.connection.url === 'string') { return; } - this.connection.url.push(this.connection.url.shift()); + const currentRpcServer = this.connection.url.shift(); + this.connection.url.push(currentRpcServer); + if (!process.env['NEAR_NO_LOGS']) { + console.warn(`Switched from ${currentRpcServer} RPC Server to ${this.connection.url[0]}`); + } } /** * Directly call the RPC specifying the method and params diff --git a/src/providers/json-rpc-provider.ts b/src/providers/json-rpc-provider.ts index 6a1a067e4b..404671ffe7 100644 --- a/src/providers/json-rpc-provider.ts +++ b/src/providers/json-rpc-provider.ts @@ -345,7 +345,11 @@ export class JsonRpcProvider extends Provider { */ private rotateRpcServers() { if (typeof this.connection.url === 'string') { return; } - this.connection.url.push(this.connection.url.shift()); + const currentRpcServer = this.connection.url.shift(); + this.connection.url.push(currentRpcServer); + if (!process.env['NEAR_NO_LOGS']) { + console.warn(`Switched from ${currentRpcServer} RPC Server to ${this.connection.url[0]}`); + } } /** From f2c92f554e3cfbcc825acca1beb7f2940c9cb094 Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Thu, 23 Dec 2021 18:56:38 +0200 Subject: [PATCH 08/10] RPC Server rotation function test added --- test/providers.test.js | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/test/providers.test.js b/test/providers.test.js index 730763d879..3b11912c02 100644 --- a/test/providers.test.js +++ b/test/providers.test.js @@ -1,5 +1,5 @@ const nearApi = require('../src/index'); -const testUtils = require('./test-utils'); +const testUtils = require('./test-utils'); const BN = require('bn.js'); const base58 = require('bs58'); @@ -64,7 +64,7 @@ test('json rpc fetch validators info', withProvider(async (provider) => { expect(validators.current_validators.length).toBeGreaterThanOrEqual(1); })); -test('txStatus with string hash and buffer hash', withProvider(async(provider) => { +test('txStatus with string hash and buffer hash', withProvider(async (provider) => { const near = await testUtils.setUpTestConnection(); const sender = await testUtils.createAccount(near); const receiver = await testUtils.createAccount(near); @@ -76,7 +76,7 @@ test('txStatus with string hash and buffer hash', withProvider(async(provider) = expect(responseWithUint8Array).toMatchObject(outcome); })); -test('json rpc query with block_id', withProvider(async(provider) => { +test('json rpc query with block_id', withProvider(async (provider) => { const stat = await provider.status(); let block_id = stat.sync_info.latest_block_height - 1; @@ -111,7 +111,7 @@ test('json rpc query view_state', withProvider(async (provider) => { await contract.setValue({ args: { value: 'hello' } }); - return testUtils.waitFor(async() => { + return testUtils.waitFor(async () => { const response = await provider.query({ request_type: 'view_state', finality: 'final', @@ -152,7 +152,7 @@ test('json rpc query view_code', withProvider(async (provider) => { const account = await testUtils.createAccount(near); const contract = await testUtils.deployContract(account, testUtils.generateUniqueString('test')); - return testUtils.waitFor(async() => { + return testUtils.waitFor(async () => { const response = await provider.query({ request_type: 'view_code', finality: 'final', @@ -175,7 +175,7 @@ test('json rpc query call_function', withProvider(async (provider) => { await contract.setValue({ args: { value: 'hello' } }); - return testUtils.waitFor(async() => { + return testUtils.waitFor(async () => { const response = await provider.query({ request_type: 'call_function', finality: 'final', @@ -200,7 +200,7 @@ test('json rpc query call_function', withProvider(async (provider) => { }); })); -test('final tx result', async() => { +test('final tx result', async () => { const result = { status: { SuccessValue: 'e30=' }, transaction: { id: '11111', outcome: { status: { SuccessReceiptId: '11112' }, logs: [], receipt_ids: ['11112'], gas_burnt: 1 } }, @@ -212,7 +212,7 @@ test('final tx result', async() => { expect(nearApi.providers.getTransactionLastResult(result)).toEqual({}); }); -test('final tx result with null', async() => { +test('final tx result with null', async () => { const result = { status: 'Failure', transaction: { id: '11111', outcome: { status: { SuccessReceiptId: '11112' }, logs: [], receipt_ids: ['11112'], gas_burnt: 1 } }, @@ -224,7 +224,7 @@ test('final tx result with null', async() => { expect(nearApi.providers.getTransactionLastResult(result)).toEqual(null); }); -test('json rpc light client proof', async() => { +test('json rpc light client proof', async () => { const near = await testUtils.setUpTestConnection(); const workingAccount = await testUtils.createAccount(near); const executionOutcome = await workingAccount.sendMoney(workingAccount.accountId, new BN(10000)); @@ -335,4 +335,20 @@ test('near json rpc fetch node status', async () => { const near = await nearApi.connect(config); let response = await near.connection.provider.status(); expect(response.chain_id).toBeTruthy(); +}); + +test('JsonRpc rotateRpcServers', async () => { + const SERVER_1 = "server1"; + const SERVER_2 = "server2"; + const SERVER_3 = "server3"; + const provider = new nearApi.providers.JsonRpcProvider({ url: [SERVER_1, SERVER_2, SERVER_3] }); + expect(provider.connection.url.length).toEqual(3); + expect(provider.connection.url[0]).toMatch(SERVER_1); + expect(provider.connection.url[1]).toMatch(SERVER_2); + expect(provider.connection.url[2]).toMatch(SERVER_3); + provider.rotateRpcServers(); + expect(provider.connection.url.length).toEqual(3); + expect(provider.connection.url[0]).toMatch(SERVER_2); + expect(provider.connection.url[1]).toMatch(SERVER_3); + expect(provider.connection.url[2]).toMatch(SERVER_1); }); \ No newline at end of file From 1b557dc363f52e55a7cdc49cdfc7cb0087c81c69 Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Thu, 23 Dec 2021 19:20:32 +0200 Subject: [PATCH 09/10] connection example added --- examples/cookbook/failover-configuration.js | 29 +++++++++++++++------ lib/near.d.ts | 5 ++-- src/near.ts | 7 +++-- 3 files changed, 29 insertions(+), 12 deletions(-) diff --git a/examples/cookbook/failover-configuration.js b/examples/cookbook/failover-configuration.js index b32f9a9087..ba60185ef3 100644 --- a/examples/cookbook/failover-configuration.js +++ b/examples/cookbook/failover-configuration.js @@ -1,14 +1,13 @@ -// Demonstrates how to use failover functionality with provider -const { providers } = require("near-api-js"); +// Demonstrates how to use failover functionality RPC Servers +const { providers, connect } = require("near-api-js"); -//TODO: replace with placeholders -const MAIN_RPC_SERVER = 'https://rpc.testnet.near.org'; -const FAILOVER_RPC_SERVER_1 = 'https://rpc.ci-testnet.near.org'; -const FAILOVER_RPC_SERVER_2 = 'https://testnet.rpc.near.dev'; //this one needs API Key +const MAIN_RPC_SERVER = ''; +const FAILOVER_RPC_SERVER_1 = ''; +const FAILOVER_RPC_SERVER_2 = ''; // Provider example const provider = new providers.JsonRpcProvider({ - // TODO: what is happening here + // preoritized list of RPC Servers url: [MAIN_RPC_SERVER, FAILOVER_RPC_SERVER_1, FAILOVER_RPC_SERVER_2], }); @@ -20,4 +19,18 @@ async function getNetworkStatus() { getNetworkStatus(); // Connection example -//TODO⏎ \ No newline at end of file +const ACCOUNT_ID = ""; + +const config = { + networkId: 'testnet', + nodeUrl: [MAIN_RPC_SERVER, FAILOVER_RPC_SERVER_1, FAILOVER_RPC_SERVER_2], +}; + +async function getState(accountId) { + const near = await connect(config); + const account = await near.account(accountId); + const state = await account.state(); + console.log(state); +} + +getState(ACCOUNT_ID); \ No newline at end of file diff --git a/lib/near.d.ts b/lib/near.d.ts index 5152a16da6..1aa6dd6ef3 100644 --- a/lib/near.d.ts +++ b/lib/near.d.ts @@ -44,10 +44,11 @@ export interface NearConfig { */ networkId: string; /** - * NEAR RPC API url. used to make JSON RPC calls to interact with NEAR. + * NEAR RPC API url. Used to make JSON RPC calls to interact with NEAR. + * You can set multiple RPC Server URLs to turn on failover functionality. * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} */ - nodeUrl: string; + nodeUrl: string | string[]; /** * RPC API Keys. Used to authenticate users on RPC Server. */ diff --git a/src/near.ts b/src/near.ts index 70195484fc..a7b278d8fc 100644 --- a/src/near.ts +++ b/src/near.ts @@ -50,14 +50,17 @@ export interface NearConfig { networkId: string; /** - * NEAR RPC API url. used to make JSON RPC calls to interact with NEAR. + * NEAR RPC API url. Used to make JSON RPC calls to interact with NEAR. + * You can set multiple RPC Server URLs to turn on failover functionality. * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} */ - nodeUrl: string; + nodeUrl: string | string[]; + /** * RPC API Keys. Used to authenticate users on RPC Server. */ apiKeys: string; + /** * NEAR RPC API headers. Can be used to pass API KEY and other parameters. * @see {@link JsonRpcProvider.JsonRpcProvider | JsonRpcProvider} From da0085299cd6d5bd73aa2f577bad59eec0d6f9cb Mon Sep 17 00:00:00 2001 From: Serhii Volovyk Date: Thu, 23 Dec 2021 19:33:02 +0200 Subject: [PATCH 10/10] lint --- src/utils/web.ts | 2 +- test/providers.test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/web.ts b/src/utils/web.ts index 60c45f0351..87da66a844 100644 --- a/src/utils/web.ts +++ b/src/utils/web.ts @@ -11,7 +11,7 @@ const RETRY_NUMBER = 10; export interface ConnectionInfo { // RPC Server URL or the prioritized array of such URLs url: string | string[]; - apiKeys?: { [url: string]: string } + apiKeys?: { [url: string]: string }; user?: string; password?: string; allowInsecure?: boolean; diff --git a/test/providers.test.js b/test/providers.test.js index 3b11912c02..6b9e3295b5 100644 --- a/test/providers.test.js +++ b/test/providers.test.js @@ -338,9 +338,9 @@ test('near json rpc fetch node status', async () => { }); test('JsonRpc rotateRpcServers', async () => { - const SERVER_1 = "server1"; - const SERVER_2 = "server2"; - const SERVER_3 = "server3"; + const SERVER_1 = 'server1'; + const SERVER_2 = 'server2'; + const SERVER_3 = 'server3'; const provider = new nearApi.providers.JsonRpcProvider({ url: [SERVER_1, SERVER_2, SERVER_3] }); expect(provider.connection.url.length).toEqual(3); expect(provider.connection.url[0]).toMatch(SERVER_1);