Add connected() to API interface (#2580)
* Add connected() to API interface * Slight doc update * Align ApiRx * isConnected() usage * Updates * Retrieve isConnected for API via observable * Remove (now) useless Rx test
This commit is contained in:
@@ -142,7 +142,7 @@ export default abstract class Decorate<ApiType extends ApiTypes> extends Events
|
||||
this._options = options;
|
||||
this._type = type;
|
||||
this._rpcCore = new RpcCore(this.#instanceId, this.#registry, thisProvider, this._options.rpc);
|
||||
this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected());
|
||||
this._isConnected = new BehaviorSubject(this._rpcCore.provider.isConnected);
|
||||
this._rx.hasSubscriptions = this._rpcCore.provider.hasSubscriptions;
|
||||
this._rx.registry = this.#registry;
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ export default abstract class Init<ApiType extends ApiTypes> extends Decorate<Ap
|
||||
// If the provider was instantiated earlier, and has already emitted a
|
||||
// 'connected' event, then the `on('connected')` won't fire anymore. To
|
||||
// cater for this case, we call manually `this._onProviderConnect`.
|
||||
if (this._rpcCore.provider.isConnected()) {
|
||||
if (this._rpcCore.provider.isConnected) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.#onProviderConnect();
|
||||
}
|
||||
|
||||
@@ -117,6 +117,13 @@ export default abstract class ApiBase<ApiType extends ApiTypes> extends Init<Api
|
||||
return this._rpcCore.provider.hasSubscriptions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description true is the underlying provider is connected
|
||||
*/
|
||||
public get isConnected (): boolean {
|
||||
return this._isConnected.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description The library information name & version (from package.json)
|
||||
*/
|
||||
@@ -232,11 +239,18 @@ export default abstract class ApiBase<ApiType extends ApiTypes> extends Init<Api
|
||||
return assertResult(this._extrinsics);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Connect from the underlying provider, halting all network traffic
|
||||
*/
|
||||
public connect (): Promise<void> {
|
||||
return this._rpcCore.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Disconnect from the underlying provider, halting all network traffic
|
||||
*/
|
||||
public disconnect (): void {
|
||||
this._rpcCore.disconnect();
|
||||
public disconnect (): Promise<void> {
|
||||
return this._rpcCore.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -9,7 +9,6 @@ import { map, switchMap } from 'rxjs/operators';
|
||||
import testingPairs from '@polkadot/keyring/testingPairs';
|
||||
import Mock from '@polkadot/rpc-provider/mock/index';
|
||||
import { TypeRegistry } from '@polkadot/types';
|
||||
import { isObservable } from '@polkadot/util';
|
||||
|
||||
import { SingleAccountSigner } from '../../test/util';
|
||||
import ApiRx from './Api';
|
||||
@@ -24,14 +23,6 @@ describe('ApiRx', (): void => {
|
||||
provider = new Mock(registry);
|
||||
});
|
||||
|
||||
it('has isConnected', (): void => {
|
||||
const api = new ApiRx();
|
||||
|
||||
expect(
|
||||
isObservable(api.isConnected)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
describe('decorator.signAsync', (): void => {
|
||||
it('signs a transfer using an external signer', (): void => {
|
||||
const signer = new SingleAccountSigner(registry, keyring.alice_session);
|
||||
|
||||
@@ -173,13 +173,6 @@ export default class ApiRx extends ApiBase<'rxjs'> {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Observable that carries the connected state for the provider. Results in a boolean flag that is true/false based on the connectivity.
|
||||
*/
|
||||
public get isConnected (): Observable<boolean> {
|
||||
return this._isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Observable that returns the first time we are connected and loaded
|
||||
*/
|
||||
|
||||
@@ -129,11 +129,25 @@ export default class Rpc implements RpcInterface {
|
||||
this.addUserInterfaces(userRpc);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns the connected status of a provider
|
||||
*/
|
||||
public get isConnected (): boolean {
|
||||
return this.provider.isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually connect from the attached provider
|
||||
*/
|
||||
public connect (): Promise<void> {
|
||||
return this.provider.connect();
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually disconnect from the attached provider
|
||||
*/
|
||||
public disconnect (): void {
|
||||
this.provider.disconnect();
|
||||
public disconnect (): Promise<void> {
|
||||
return this.provider.disconnect();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -31,7 +31,7 @@ describe('Http', (): void => {
|
||||
});
|
||||
|
||||
it('always returns isConnected true', (): void => {
|
||||
expect(http.isConnected()).toEqual(true);
|
||||
expect(http.isConnected).toEqual(true);
|
||||
});
|
||||
|
||||
it('does not (yet) support subscribe', (): Promise<number | void> => {
|
||||
|
||||
@@ -67,10 +67,17 @@ export default class HttpProvider implements ProviderInterface {
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually connect from the connection
|
||||
*/
|
||||
public async connect (): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually disconnect from the connection
|
||||
*/
|
||||
public disconnect (): void {
|
||||
public async disconnect (): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
|
||||
@@ -78,7 +85,7 @@ export default class HttpProvider implements ProviderInterface {
|
||||
* @summary Whether the node is connected or not.
|
||||
* @return {boolean} true if connected
|
||||
*/
|
||||
public isConnected (): boolean {
|
||||
public get isConnected (): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,11 +98,15 @@ export default class Mock implements ProviderInterface {
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
|
||||
public disconnect (): void {
|
||||
public async connect (): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
|
||||
public isConnected (): boolean {
|
||||
public async disconnect (): Promise<void> {
|
||||
// noop
|
||||
}
|
||||
|
||||
public get isConnected (): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -44,9 +44,11 @@ export type ProviderInterfaceEmitCb = (value?: any) => any;
|
||||
|
||||
export interface ProviderInterface {
|
||||
readonly hasSubscriptions: boolean;
|
||||
readonly isConnected: boolean;
|
||||
|
||||
clone (): ProviderInterface;
|
||||
disconnect (): void;
|
||||
isConnected (): boolean;
|
||||
connect (): Promise<void>;
|
||||
disconnect (): Promise<void>;
|
||||
on (type: ProviderInterfaceEmitted, sub: ProviderInterfaceEmitCb): () => void;
|
||||
send (method: string, params: any[]): Promise<any>;
|
||||
subscribe (type: string, method: string, params: any[], cb: ProviderInterfaceCallback): Promise<number | string>;
|
||||
|
||||
@@ -30,10 +30,6 @@ interface WsStateSubscription extends SubscriptionHandler {
|
||||
params: any[];
|
||||
}
|
||||
|
||||
interface WSProviderInterface extends ProviderInterface {
|
||||
connect (): void;
|
||||
}
|
||||
|
||||
const ALIASSES: { [index: string]: string } = {
|
||||
chain_finalisedHead: 'chain_finalizedHead',
|
||||
chain_subscribeFinalisedHeads: 'chain_subscribeFinalizedHeads',
|
||||
@@ -62,7 +58,7 @@ const l = logger('api-ws');
|
||||
*
|
||||
* @see [[HttpProvider]]
|
||||
*/
|
||||
export default class WsProvider implements WSProviderInterface {
|
||||
export default class WsProvider implements ProviderInterface {
|
||||
readonly #coder: Coder;
|
||||
|
||||
readonly #endpoints: string[];
|
||||
@@ -123,6 +119,14 @@ export default class WsProvider implements WSProviderInterface {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Whether the node is connected or not.
|
||||
* @return {boolean} true if connected
|
||||
*/
|
||||
public get isConnected (): boolean {
|
||||
return this.#isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Returns a clone of the object
|
||||
*/
|
||||
@@ -152,31 +156,34 @@ export default class WsProvider implements WSProviderInterface {
|
||||
this.#websocket.onopen = this.#onSocketOpen;
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
|
||||
this.#emit('error', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Manually disconnect from the connection, clearing autoconnect logic
|
||||
*/
|
||||
public disconnect (): void {
|
||||
if (isNull(this.#websocket)) {
|
||||
throw new Error('Cannot disconnect on a non-open websocket');
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
public async disconnect (): Promise<void> {
|
||||
try {
|
||||
assert(!isNull(this.#websocket), 'Cannot disconnect on a non-connected websocket');
|
||||
|
||||
// switch off autoConnect, we are in manual mode now
|
||||
this.#autoConnectMs = 0;
|
||||
|
||||
// 1000 - Normal closure; the connection successfully completed
|
||||
this.#websocket.close(1000);
|
||||
this.#websocket = null;
|
||||
} catch (error) {
|
||||
l.error(error);
|
||||
|
||||
this.#emit('error', error);
|
||||
|
||||
throw error;
|
||||
}
|
||||
|
||||
// switch off autoConnect, we are in manual mode now
|
||||
this.#autoConnectMs = 0;
|
||||
|
||||
// 1000 - Normal closure; the connection successfully completed
|
||||
this.#websocket.close(1000);
|
||||
this.#websocket = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @summary Whether the node is connected or not.
|
||||
* @return {boolean} true if connected
|
||||
*/
|
||||
public isConnected (): boolean {
|
||||
return this.#isConnected;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -220,7 +227,7 @@ export default class WsProvider implements WSProviderInterface {
|
||||
subscription
|
||||
};
|
||||
|
||||
if (this.isConnected() && !isNull(this.#websocket)) {
|
||||
if (this.isConnected && !isNull(this.#websocket)) {
|
||||
this.#websocket.send(json);
|
||||
} else {
|
||||
this.#queued[id] = json;
|
||||
@@ -297,8 +304,7 @@ export default class WsProvider implements WSProviderInterface {
|
||||
|
||||
if (this.#autoConnectMs > 0) {
|
||||
setTimeout((): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
this.connect();
|
||||
this.connect().catch(() => { /* noop */ });
|
||||
}, this.#autoConnectMs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,26 +30,26 @@ describe('onConnect', (): void => {
|
||||
it('Does not connect when autoConnect is false', (): void => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL, 0);
|
||||
|
||||
expect(provider.isConnected()).toBe(false);
|
||||
expect(provider.isConnected).toBe(false);
|
||||
});
|
||||
|
||||
it('Does connect when autoConnect is true', async () => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL, 1);
|
||||
|
||||
await sleepMs(100); // Hack to give the provider time to connect
|
||||
expect(provider.isConnected()).toBe(true);
|
||||
expect(provider.isConnected).toBe(true);
|
||||
});
|
||||
|
||||
it('Creates a new WebSocket instance by calling the connect() method', async () => {
|
||||
const provider: WsProvider = new WsProvider(TEST_WS_URL, false);
|
||||
|
||||
expect(provider.isConnected()).toBe(false);
|
||||
expect(provider.isConnected).toBe(false);
|
||||
expect(mocks[0].server.clients().length).toBe(0);
|
||||
|
||||
await provider.connect();
|
||||
await sleepMs(100); // Hack to give the provider time to connect
|
||||
|
||||
expect(provider.isConnected()).toBe(true);
|
||||
expect(provider.isConnected).toBe(true);
|
||||
expect(mocks[0].server.clients().length).toBe(1);
|
||||
});
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('onConnect', (): void => {
|
||||
await provider.connect();
|
||||
await sleepMs(100); // Hack to give the provider time to connect
|
||||
|
||||
expect(provider.isConnected()).toBe(true);
|
||||
expect(provider.isConnected).toBe(true);
|
||||
});
|
||||
|
||||
it('Connects to the second endpoint when the first is unreachable', async () => {
|
||||
@@ -71,7 +71,7 @@ describe('onConnect', (): void => {
|
||||
|
||||
await sleepMs(100); // Hack to give the provider time to connect
|
||||
|
||||
expect(provider.isConnected()).toBe(true);
|
||||
expect(provider.isConnected).toBe(true);
|
||||
});
|
||||
|
||||
it('Connects to the second endpoint when the first is dropped', async () => {
|
||||
@@ -92,7 +92,7 @@ describe('onConnect', (): void => {
|
||||
|
||||
// Check that second server is connected
|
||||
expect(mocks[1].server.clients().length).toBe(1);
|
||||
expect(provider.isConnected()).toBe(true);
|
||||
expect(provider.isConnected).toBe(true);
|
||||
});
|
||||
|
||||
it('Round-robin of endpoints on WsProvider', async () => {
|
||||
@@ -124,7 +124,7 @@ describe('onConnect', (): void => {
|
||||
// Check that first server is connected and the next one isn't
|
||||
expect(mocks[index].server.clients().length).toBe(1);
|
||||
expect(mockNext[index].server.clients().length).toBe(0);
|
||||
expect(provider.isConnected()).toBe(true);
|
||||
expect(provider.isConnected).toBe(true);
|
||||
|
||||
// Close connection from first server
|
||||
mocks[index].server.clients()[0].close();
|
||||
|
||||
@@ -25,7 +25,7 @@ describe('Ws', (): void => {
|
||||
|
||||
it('returns the connected state', (): void => {
|
||||
expect(
|
||||
createWs([]).isConnected()
|
||||
createWs([]).isConnected
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user