Allow any provider into API constructor (#444)

* Allow any provider into API constructor

* Update documentation

* Remove combineLatest exposure (deprecation error)

* Linting & test cleanups

* Exclude docs from codeclimate
This commit is contained in:
Jaco Greeff
2018-12-06 11:28:40 +01:00
committed by GitHub
parent ba53f712e4
commit 69b05c7fef
21 changed files with 112 additions and 82 deletions
+1
View File
@@ -1,2 +1,3 @@
exclude_patterns:
- "**/*.spec.js"
- "docs/**/*.js"
+1 -1
View File
@@ -9,5 +9,5 @@
"packages": [
"packages/*"
],
"version": "0.32.24"
"version": "0.33.0"
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api-observable",
"version": "0.32.24",
"version": "0.33.0",
"description": "An Observable wrapper around the Polkadot JS API",
"main": "index.js",
"keywords": [
@@ -30,10 +30,10 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-rx#readme",
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/extrinsics": "^0.32.24",
"@polkadot/rpc-core": "^0.32.24",
"@polkadot/rpc-provider": "^0.32.24",
"@polkadot/storage": "^0.32.24",
"@polkadot/extrinsics": "^0.33.0",
"@polkadot/rpc-core": "^0.33.0",
"@polkadot/rpc-provider": "^0.33.0",
"@polkadot/storage": "^0.33.0",
"@types/rx": "^4.1.1",
"rxjs": "^6.3.3"
}
+5 -5
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/api",
"version": "0.32.24",
"version": "0.33.0",
"description": "An RxJS wrapper around the Polkadot JS RPC",
"main": "index.js",
"keywords": [
@@ -30,10 +30,10 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/api#readme",
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/extrinsics": "^0.32.24",
"@polkadot/rpc-provider": "^0.32.24",
"@polkadot/rpc-rx": "^0.32.24",
"@polkadot/storage": "^0.32.24",
"@polkadot/extrinsics": "^0.33.0",
"@polkadot/rpc-provider": "^0.33.0",
"@polkadot/rpc-rx": "^0.33.0",
"@polkadot/storage": "^0.33.0",
"@types/rx": "^4.1.1",
"rxjs": "^6.3.3"
}
+19 -28
View File
@@ -2,10 +2,10 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ApiBaseInterface, ApiInterface$Events } from './types';
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { ApiBaseInterface, ApiInterface$Events, ApiOptions } from './types';
import EventEmitter from 'eventemitter3';
import WsProvider from '@polkadot/rpc-provider/ws';
import Rpc from '@polkadot/rpc-core/index';
import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
import { Storage } from '@polkadot/storage/types';
@@ -15,8 +15,7 @@ import { Hash, Method, RuntimeVersion } from '@polkadot/types/index';
import Event from '@polkadot/types/Event';
import RuntimeMetadata from '@polkadot/types/Metadata';
import { Extrinsics } from '@polkadot/types/Method';
import { Constructor } from '@polkadot/types/types';
import { assert, isUndefined, logger } from '@polkadot/util';
import { assert, isFunction, isObject, isUndefined, logger } from '@polkadot/util';
type MetaDecoration = {
callIndex?: Uint8Array,
@@ -30,18 +29,6 @@ const l = logger('api');
const INIT_ERROR = `Api needs to be initialised before using, listen on 'ready'`;
export interface ApiOptions {
/**
* WebSocket provider from rpc-provider/ws. If not specified, it will default to connecting to the
* localhost with the default port, i.e. `ws://127.0.0.1:9944`
*/
wsProvider?: WsProvider;
/**
* Additional types used by runtime modules. This is nessusary if the runtime modules uses non-buildin types.
*/
additionalTypes?: {[name: string]: Constructor};
}
export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S, E> {
private _eventemitter: EventEmitter;
protected _extrinsics?: E;
@@ -55,7 +42,7 @@ export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S,
/**
* @description Create an instance of the class
*
* @param options Options object to create API instance
* @param options Options object to create API instance or a Provider instance
*
* @example
* <BR>
@@ -70,13 +57,17 @@ export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S,
* });
* ```
*/
constructor (options: ApiOptions) {
constructor (provider: ApiOptions | ProviderInterface = {}) {
const options = isObject(provider) && isFunction((provider as ProviderInterface).send)
? { provider } as ApiOptions
: provider as ApiOptions;
this._eventemitter = new EventEmitter();
this._rpcBase = new Rpc(options.wsProvider);
this._rpcBase = new Rpc(options.provider);
this._rpc = this.decorateRpc(this._rpcBase);
if (options.additionalTypes) {
registry.register(options.additionalTypes);
if (options.types) {
registry.register(options.types);
}
this.init();
@@ -91,6 +82,13 @@ export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S,
return this._genesisHash as Hash;
}
/**
* @description `true` when subscriptions are supported
*/
get hasSubscriptions (): boolean {
return this._rpcBase._provider.hasSubscriptions;
}
/**
* @description Yields the current attached runtime metadata. Generally this is only used to construct extrinsics & storage, but is useful for current runtime inspection.
*/
@@ -205,13 +203,6 @@ export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S,
this._rpcBase._provider.on('connected', async () => {
this.emit('connected');
// TODO When re-connected (i.e. disconnected and then connected), we want to do a couple of things
// - refresh metadata as needed, decorating again
// - need to refresh genesisHash, extrinsic resub only when it matches
if (isReady) {
return;
}
const hasMeta = await this.loadMeta();
if (hasMeta && !isReady) {
@@ -15,6 +15,7 @@ describe('SubmittableExtrinsic', () => {
Method.injectExtrinsics(extrinsics);
api = {
hasSubscriptions: true,
rpc: {
author: {
submitAndWatchExtrinsic: () => Promise.resolve('submitAndWatchExtrinsic'),
@@ -18,11 +18,11 @@ export default class SubmittableExtrinsic extends Extrinsic {
}
send (statusCb?: (status: ExtrinsicStatus) => any): Promise<Hash> {
if (statusCb) {
return this._api.rpc.author.submitAndWatchExtrinsic(this, statusCb);
if (!statusCb || !this._api.hasSubscriptions) {
return this._api.rpc.author.submitExtrinsic(this);
}
return this._api.rpc.author.submitExtrinsic(this);
return this._api.rpc.author.submitAndWatchExtrinsic(this, statusCb);
}
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic {
+17 -12
View File
@@ -2,20 +2,23 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { ApiOptions } from '../types';
import { ApiPromiseInterface, QueryableStorageFunction, QueryableModuleStorage, QueryableStorage, SubmittableExtrinsics, SubmittableModuleExtrinsics, SubmittableExtrinsicFunction } from './types';
import Rpc from '@polkadot/rpc-core/index';
import WsProvider from '@polkadot/rpc-provider/ws';
import { Storage } from '@polkadot/storage/types';
import { Codec } from '@polkadot/types/types';
import { Extrinsics, ExtrinsicFunction } from '@polkadot/types/Method';
import { StorageFunction } from '@polkadot/types/StorageKey';
import { isFunction } from '@polkadot/util';
import { isFunction, logger } from '@polkadot/util';
import ApiBase, { ApiOptions } from '../Base';
import ApiBase from '../Base';
import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator';
import SubmittableExtrinsic from './SubmittableExtrinsic';
const l = logger('api/promise');
/**
* # @polkadot/api/promise
*
@@ -108,7 +111,8 @@ export default class ApiPromise extends ApiBase<Rpc, QueryableStorage, Submittab
/**
* @description Creates an ApiPromise instance using the supplied provider. Returns an Promise containing the actual Api instance.
*
* @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or [[WsProvider]]
* @param options options that is passed to the class contructor. Can be either [[ApiOptions]] or a
* provider (see the constructor arguments)
*
* @example
* <BR>
@@ -123,14 +127,16 @@ export default class ApiPromise extends ApiBase<Rpc, QueryableStorage, Submittab
* });
* ```
*/
static create (options: ApiOptions | WsProvider = {}): Promise<ApiPromise> {
static create (options: ApiOptions | ProviderInterface = {}): Promise<ApiPromise> {
return new ApiPromise(options).isReady;
}
/**
* @description Creates an instance of the ApiPromise class
*
* @param options Options to create an instance. Can be either [[ApiOptions]] or [[WsProvider]]
* @param options Options to create an instance. This can be either [[ApiOptions]] or
* an [[HttpProvider]] or [[WsProvider]]. In the case of [[HttpProvider]] subscriptions
* are not supported, only latest values are returned.
*
* @example
* <BR>
@@ -145,12 +151,7 @@ export default class ApiPromise extends ApiBase<Rpc, QueryableStorage, Submittab
* });
* ```
*/
constructor (options: ApiOptions | WsProvider = {}) {
if (options instanceof WsProvider) {
options = {
wsProvider: options
};
}
constructor (options?: ApiOptions | ProviderInterface) {
super(options);
this._isReady = new Promise((resolveReady) =>
@@ -234,6 +235,10 @@ export default class ApiPromise extends ApiBase<Rpc, QueryableStorage, Submittab
const decorated: any = (...args: Array<any>): Promise<Codec | null | undefined> => {
if (args.length === 0 || !isFunction(args[args.length - 1])) {
return this.rpc.state.getStorage([method, args[0]]);
} else if (!this.hasSubscriptions && isFunction(args[args.length - 1])) {
l.warn(`Storage subscription to ${method.section}.${method.name} ignored, provider does not support subscriptions`);
return this.rpc.state.getStorage([method, args.length === 1 ? undefined : args[0]]);
}
return this.rpc.state.subscribeStorage(
+8 -10
View File
@@ -2,20 +2,21 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { ApiOptions } from '../types';
import { ApiRxInterface, QueryableStorageFunction, QueryableModuleStorage, QueryableStorage, SubmittableExtrinsics, SubmittableModuleExtrinsics, SubmittableExtrinsicFunction } from './types';
import { EMPTY, Observable, from } from 'rxjs';
import { defaultIfEmpty, map } from 'rxjs/operators';
import Rpc from '@polkadot/rpc-core/index';
import WsProvider from '@polkadot/rpc-provider/ws';
import RpcRx from '@polkadot/rpc-rx/index';
import { Storage } from '@polkadot/storage/types';
import { Codec } from '@polkadot/types/types';
import { Extrinsics, ExtrinsicFunction } from '@polkadot/types/Method';
import { StorageFunction } from '@polkadot/types/StorageKey';
import { logger } from '@polkadot/util';
import { assert, logger } from '@polkadot/util';
import ApiBase, { ApiOptions } from '../Base';
import ApiBase from '../Base';
import SubmittableExtrinsic from './SubmittableExtrinsic';
const l = logger('api-rx');
@@ -138,7 +139,7 @@ export default class ApiRx extends ApiBase<RpcRx, QueryableStorage, SubmittableE
* });
* ```
*/
static create (options: ApiOptions | WsProvider = {}): Observable<ApiRx> {
static create (options?: ApiOptions | ProviderInterface): Observable<ApiRx> {
return new ApiRx(options).isReady;
}
@@ -160,14 +161,11 @@ export default class ApiRx extends ApiBase<RpcRx, QueryableStorage, SubmittableE
* });
* ```
*/
constructor (options: ApiOptions | WsProvider = {}) {
if (options instanceof WsProvider) {
options = {
wsProvider: options
};
}
constructor (options?: ApiOptions | ProviderInterface) {
super(options);
assert(this.hasSubscriptions, 'ApiRx can only be used with a provider supporting subscriptions');
this._isReady = from(
// convinced you can observable from an event, however my mind groks this form better
new Promise((resolveReady) =>
+18 -3
View File
@@ -2,15 +2,17 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { RpcRxInterface$Events } from '@polkadot/rpc-rx/types';
import { Hash, RuntimeVersion } from '@polkadot/types/index';
import RuntimeMetadata from '@polkadot/types/Metadata';
import { Hash, Metadata, RuntimeVersion } from '@polkadot/types/index';
import { Constructor } from '@polkadot/types/types';
export type ApiInterface$Events = RpcRxInterface$Events | 'ready';
export interface ApiBaseInterface<R, S, E> {
readonly genesisHash: Hash;
readonly runtimeMetadata: RuntimeMetadata;
readonly hasSubscriptions: boolean;
readonly runtimeMetadata: Metadata;
readonly runtimeVersion: RuntimeVersion;
readonly query: S;
readonly rpc: R;
@@ -18,3 +20,16 @@ export interface ApiBaseInterface<R, S, E> {
on: (type: ApiInterface$Events, handler: (...args: Array<any>) => any) => void;
}
export interface ApiOptions {
/**
* @description Transport Provider from rpc-provider. If not specified, it will default to
* connecting to a WsProvider connecting localhost with the default port, i.e. `ws://127.0.0.1:9944`
*/
provider?: ProviderInterface;
/**
* @description Additional types used by runtime modules. This is nessusary if the runtime modules
* uses types not available in the base Substrate runtime.
*/
types?: {[name: string]: Constructor};
}
+4 -4
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-core",
"version": "0.32.24",
"version": "0.33.0",
"description": "A JavaScript wrapper for the Polkadot JsonRPC interface",
"main": "index.js",
"keywords": [
@@ -30,9 +30,9 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-core#readme",
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/jsonrpc": "^0.32.24",
"@polkadot/rpc-provider": "^0.32.24",
"@polkadot/types": "^0.32.24",
"@polkadot/jsonrpc": "^0.33.0",
"@polkadot/rpc-provider": "^0.33.0",
"@polkadot/types": "^0.33.0",
"@polkadot/util": "^0.33.8"
}
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-provider",
"version": "0.32.24",
"version": "0.33.0",
"description": "Transport providers for the API",
"main": "index.js",
"keywords": [
@@ -31,7 +31,7 @@
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/keyring": "^0.33.8",
"@polkadot/storage": "^0.32.24",
"@polkadot/storage": "^0.33.0",
"@polkadot/util": "^0.33.8",
"@polkadot/util-crypto": "^0.33.8",
"@types/nock": "^9.3.0",
+7
View File
@@ -49,6 +49,13 @@ export default class HttpProvider implements ProviderInterface {
this.endpoint = endpoint;
}
/**
* @summary `true` when this provider supports subscriptions
*/
get hasSubscriptions (): boolean {
return false;
}
/**
* @summary Whether the node is connected or not.
* @return {boolean} true if connected
+4
View File
@@ -67,6 +67,10 @@ export default class Mock implements ProviderInterface {
this.init();
}
get hasSubscriptions (): boolean {
return false;
}
isConnected (): boolean {
return true;
}
+1
View File
@@ -42,6 +42,7 @@ export type ProviderInterface$Emitted = 'connected' | 'disconnected';
export type ProviderInterface$EmitCb = (value?: any) => any;
export interface ProviderInterface {
readonly hasSubscriptions: boolean;
isConnected (): boolean;
on (type: ProviderInterface$Emitted, sub: ProviderInterface$EmitCb): void;
send (method: string, params: Array<any>): Promise<any>;
+7
View File
@@ -95,6 +95,13 @@ export default class WsProvider implements WSProviderInterface {
}
}
/**
* @summary `true` when this provider supports subscriptions
*/
get hasSubscriptions (): boolean {
return true;
}
/**
* @summary Manually connect
* @description The [[WsProvider]] connects automatically by default, however if you decided otherwise, you may
+3 -3
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/rpc-rx",
"version": "0.32.24",
"version": "0.33.0",
"description": "An RxJs wrapper around the Polkadot JS API",
"main": "index.js",
"keywords": [
@@ -30,8 +30,8 @@
"homepage": "https://github.com/polkadot-js/api/tree/master/packages/rpc-rx#readme",
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/rpc-core": "^0.32.24",
"@polkadot/rpc-provider": "^0.32.24",
"@polkadot/rpc-core": "^0.33.0",
"@polkadot/rpc-provider": "^0.33.0",
"@types/rx": "^4.1.1",
"rxjs": "^6.3.3"
}
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/extrinsics",
"version": "0.32.24",
"version": "0.33.0",
"main": "index.js",
"repository": "https://github.com/polkadot-js/api.git",
"author": "Jaco Greeff <jacogr@gmail.com>",
@@ -11,7 +11,7 @@
"license": "Apache-2.0",
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/types": "^0.32.24",
"@polkadot/types": "^0.33.0",
"@polkadot/util": "^0.33.8"
}
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/jsonrpc",
"version": "0.32.24",
"version": "0.33.0",
"description": "Method definitions for the Polkadot RPC layer",
"main": "index.js",
"engines": {
+2 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/storage",
"version": "0.32.24",
"version": "0.33.0",
"description": "Generic wrapper wrapper around state storage",
"main": "index.js",
"engines": {
@@ -31,7 +31,7 @@
"dependencies": {
"@babel/runtime": "^7.1.5",
"@polkadot/keyring": "^0.33.8",
"@polkadot/types": "^0.32.24",
"@polkadot/types": "^0.33.0",
"@polkadot/util": "^0.33.8",
"@polkadot/util-crypto": "^0.33.8"
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@polkadot/types",
"version": "0.32.24",
"version": "0.33.0",
"description": "Implementation of the Parity codec",
"main": "index.js",
"keywords": [