Codec types (call definitions in API) (#650)

* Codec types (call definitions)

* Pass Codec & Subscription generic types

* WIP typing

* Typing seems ok

* Linting

* Queryable storage retuns Hash & U64 for .hash and .size

* Better typing for SubmittableExtrinsic

* Indentation.

* Update with needsCallback

* Update SubmittableExtrinsic.ts
This commit is contained in:
Jaco Greeff
2019-01-31 14:15:59 +01:00
committed by GitHub
parent 4c563a7ca0
commit 661cb3c453
10 changed files with 296 additions and 214 deletions
+94 -84
View File
@@ -4,18 +4,20 @@
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { Storage } from '@polkadot/storage/types';
import { Codec, CodecArg, CodecCallback } from '@polkadot/types/types';
import { RxResult } from './rx/types';
import {
ApiBaseInterface, ApiInterface$Rx, ApiInterface$Events, ApiOptions,
DecoratedRpc, DecoratedRpc$Section,
DecoratedRpc, DecoratedRpc$Method, DecoratedRpc$Section,
Derive, DeriveSection,
OnCallFunction,
HashResult, U64Result,
OnCallDefinition, OnCallFunction,
QueryableModuleStorage, QueryableStorage, QueryableStorageFunction,
SubmittableExtrinsicFunction, SubmittableExtrinsics, SubmittableModuleExtrinsics
} from './types';
import EventEmitter from 'eventemitter3';
import { Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { map } from 'rxjs/operators';
import decorateDerive, { Derive as DeriveInterface } from '@polkadot/api-derive/index';
import extrinsicsFromMeta from '@polkadot/extrinsics/fromMetadata';
import RpcBase from '@polkadot/rpc-core/index';
@@ -25,7 +27,6 @@ import registry from '@polkadot/types/codec/typeRegistry';
import { Event, Hash, Metadata, Method, RuntimeVersion } from '@polkadot/types/index';
import { MethodFunction, ModulesWithMethods } from '@polkadot/types/Method';
import { StorageFunction } from '@polkadot/types/StorageKey';
import { Codec } from '@polkadot/types/types';
import { assert, compactStripLength, isFunction, isObject, isUndefined, logger, u8aToHex } from '@polkadot/util';
import SubmittableExtrinsic from './SubmittableExtrinsic';
@@ -47,20 +48,20 @@ const l = logger('api/decorator');
* `api._rx`.
*/
function rxOnCall (
method: OnCallFunction<Observable<Codec | undefined | null>>,
params: Array<any>,
_isSubscription?: boolean
): Observable<Codec | undefined | null> {
method: OnCallFunction<RxResult, RxResult>,
params: Array<CodecArg> = [],
_callback?: CodecCallback
): RxResult {
return method(...params);
}
export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall> {
export default abstract class ApiBase<CodecResult, SubscriptionResult> implements ApiBaseInterface<CodecResult, SubscriptionResult> {
private _eventemitter: EventEmitter;
private _derive?: Derive<OnCall>;
private _extrinsics?: SubmittableExtrinsics<OnCall>;
private _derive?: Derive<CodecResult, SubscriptionResult>;
private _extrinsics?: SubmittableExtrinsics<CodecResult, SubscriptionResult>;
private _genesisHash?: Hash;
private _query?: QueryableStorage<OnCall>;
private _rpc: DecoratedRpc<OnCall>;
private _query?: QueryableStorage<CodecResult, SubscriptionResult>;
private _rpc: DecoratedRpc<CodecResult, SubscriptionResult>;
protected _rpcBase: RpcBase; // FIXME These two could be merged
protected _rpcRx: RpcRx; // FIXME These two could be merged
private _runtimeMetadata?: Metadata;
@@ -152,10 +153,10 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
* });
* ```
*/
get derive (): Derive<OnCall> {
get derive (): Derive<CodecResult, SubscriptionResult> {
assert(!isUndefined(this._derive), INIT_ERROR);
return this._derive as Derive<OnCall>;
return this._derive as Derive<CodecResult, SubscriptionResult>;
}
/**
@@ -172,10 +173,10 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
* });
* ```
*/
get query (): QueryableStorage<OnCall> {
get query (): QueryableStorage<CodecResult, SubscriptionResult> {
assert(!isUndefined(this._query), INIT_ERROR);
return this._query as QueryableStorage<OnCall>;
return this._query as QueryableStorage<CodecResult, SubscriptionResult>;
}
/**
@@ -192,7 +193,7 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
* });
* ```
*/
get rpc (): DecoratedRpc<OnCall> {
get rpc (): DecoratedRpc<CodecResult, SubscriptionResult> {
return this._rpc;
}
@@ -210,10 +211,10 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
* });
* ```
*/
get tx (): SubmittableExtrinsics<OnCall> {
get tx (): SubmittableExtrinsics<CodecResult, SubscriptionResult> {
assert(!isUndefined(this._extrinsics), INIT_ERROR);
return this._extrinsics as SubmittableExtrinsics<OnCall>;
return this._extrinsics as SubmittableExtrinsics<CodecResult, SubscriptionResult>;
}
/**
@@ -266,6 +267,8 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
return this;
}
protected abstract onCall (method: OnCallFunction<RxResult, RxResult>, params?: Array<CodecArg>, callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult;
private emit (type: ApiInterface$Events, ...args: Array<any>): void {
this._eventemitter.emit(type, ...args);
}
@@ -323,8 +326,6 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
}
}
protected abstract onCall (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean): OnCall;
private decorateFunctionMeta (input: MetaDecoration, output: MetaDecoration): MetaDecoration {
output.meta = input.meta;
output.method = input.method;
@@ -338,32 +339,40 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
return output;
}
private decorateRpc<T> (
rpc: RpcRx,
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
): DecoratedRpc<T> {
private decorateRpc<C, S> (rpc: RpcRx, onCall: OnCallDefinition<C, S>): DecoratedRpc<C, S> {
return ['author', 'chain', 'state', 'system'].reduce((result, _sectionName) => {
const sectionName = _sectionName as keyof DecoratedRpc<T>;
const sectionName = _sectionName as keyof DecoratedRpc<C, S>;
result[sectionName] = Object.keys(rpc[sectionName]).reduce((section, methodName) => {
// FIXME Find a better way to know if a particular method is a subscription or not
const isSubscription = methodName.includes('subscribe');
const method = (...params: any[]) => onCall(rpc[sectionName][methodName], params, isSubscription);
const needsCallback = methodName.includes('subscribe');
// These signatures are allowed and exposed here (bit or a stoopid way, but checked
// RPCs and we have 3 max args, with subs max one arg... YMMV) -
// (arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult;
// (arg1: CodecArg, callback: CodecCallback): SubscriptionResult;
// (callback: CodecCallback): SubscriptionResult;
const method = ((...args: Array<any>): C | S => {
let callback: CodecCallback | undefined;
let params = args;
if (args.length && isFunction(args[args.length - 1])) {
callback = args[args.length - 1];
params = args.slice(0, args.length - 1);
}
return onCall(rpc[sectionName][methodName], params, callback, needsCallback);
}) as DecoratedRpc$Method<C, S>;
section[methodName] = method;
return section;
}, {} as DecoratedRpc$Section<T>);
}, {} as DecoratedRpc$Section<C, S>);
return result;
}, {} as DecoratedRpc<T>);
}, {} as DecoratedRpc<C, S>);
}
private decorateExtrinsics<T> (
extrinsics: ModulesWithMethods,
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
): SubmittableExtrinsics<T> {
private decorateExtrinsics<C, S> (extrinsics: ModulesWithMethods, onCall: OnCallDefinition<C, S>): SubmittableExtrinsics<C, S> {
return Object.keys(extrinsics).reduce((result, sectionName) => {
const section = extrinsics[sectionName];
@@ -371,27 +380,20 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
result[methodName] = this.decorateExtrinsicEntry(section[methodName], onCall);
return result;
}, {} as SubmittableModuleExtrinsics<T>);
}, {} as SubmittableModuleExtrinsics<C, S>);
return result;
}, {} as SubmittableExtrinsics<T>);
}, {} as SubmittableExtrinsics<C, S>);
}
private decorateExtrinsicEntry<T> (
method: MethodFunction,
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
): SubmittableExtrinsicFunction<T> {
const decorated: any = (...args: Array<any>): SubmittableExtrinsic<T> =>
new SubmittableExtrinsic(this._rx as ApiInterface$Rx, onCall, method(...args));
private decorateExtrinsicEntry<C, S> (method: MethodFunction, onCall: OnCallDefinition<C, S>): SubmittableExtrinsicFunction<C, S> {
const decorated: any = (...params: Array<CodecArg>): SubmittableExtrinsic<C, S> =>
new SubmittableExtrinsic(this._rx as ApiInterface$Rx, onCall, method(...params));
return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction<T>;
return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction<C, S>;
}
private decorateStorage<T> (
storage: Storage,
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
): QueryableStorage<T> {
private decorateStorage<C, S> (storage: Storage, onCall: OnCallDefinition<C, S>): QueryableStorage<C, S> {
return Object.keys(storage).reduce((result, sectionName) => {
const section = storage[sectionName];
@@ -399,61 +401,69 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
result[methodName] = this.decorateStorageEntry(section[methodName], onCall);
return result;
}, {} as QueryableModuleStorage<T>);
}, {} as QueryableModuleStorage<C, S>);
return result;
}, {} as QueryableStorage<T>);
}, {} as QueryableStorage<C, S>);
}
private decorateStorageEntry<T> (
method: StorageFunction,
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
): QueryableStorageFunction<T> {
const decorated: any = (...args: any): T =>
onCall(
(...args: any) => this._rpcRx.state
.subscribeStorage([[method, args[0]]])
private decorateStorageEntry<C, S> (method: StorageFunction, onCall: OnCallDefinition<C, S>): QueryableStorageFunction<C, S> {
// These signatures are allowed and exposed here -
// (arg?: CodecArg): CodecResult;
// (arg: CodecArg, callback: CodecCallback): SubscriptionResult;
// (callback: CodecCallback): SubscriptionResult;
const decorated = ((...args: Array<any>): C | S => {
let callback: CodecCallback | undefined;
let params = args;
if (args.length && isFunction(args[args.length - 1])) {
callback = args[args.length - 1];
params = args.slice(0, args.length - 1);
}
return onCall(
(arg: CodecArg) => this._rpcRx.state
.subscribeStorage([[method, arg]])
.pipe(
// errors can occur in the case of malformed methods + args
catchError(() => of([])),
// state_storage returns an array of values, since we have just subscribed to
// a single entry, we pull that from the array and return it as-is
map((result: Array<Codec | null | undefined> = []): Codec | null | undefined =>
map((result: Array<Codec>): Codec =>
result[0]
)
),
args
params,
callback
);
}) as QueryableStorageFunction<C, S>;
decorated.at = (hash: Hash, arg?: any): T =>
decorated.at = (hash: Hash | Uint8Array | string, arg?: CodecArg): C =>
onCall(
// same as above (for single result), in the case of errors on creation, return `undefined`
arg => this._rpcRx.state.getStorage([method, arg], hash).pipe(catchError(() => of())),
(arg: CodecArg) => this._rpcRx.state.getStorage([method, arg], hash),
[arg]
);
) as C;
decorated.hash = (arg?: any): T =>
// FIXME The unknown cast is needed since the onCall result, `C | S` cannot
// be converted from C to the actual result required
decorated.hash = (arg?: CodecArg): HashResult<C, S> =>
onCall(
arg => this._rpcRx.state.getStorageSize([method, arg]).pipe(catchError(() => of())),
(arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]),
[arg]
);
) as unknown as HashResult<C, S>;
decorated.size = (arg?: any): T =>
// FIXME as above...
decorated.size = (arg?: CodecArg): U64Result<C, S> =>
onCall(
arg => this._rpcRx.state.getStorageSize([method, arg]).pipe(catchError(() => of())),
(arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]),
[arg]
);
) as unknown as U64Result<C, S>;
decorated.key = (arg?: any): string =>
decorated.key = (arg?: CodecArg): string =>
u8aToHex(compactStripLength(method(arg))[1]);
return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction<T>;
return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction<C, S>;
}
private decorateDerive<T> (
apiRx: ApiInterface$Rx,
onCall: (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>, isSubscription?: boolean) => T
): Derive<T> {
private decorateDerive<C, S> (apiRx: ApiInterface$Rx, onCall: OnCallDefinition<C, S>): Derive<C, S> {
const derive = decorateDerive(apiRx);
return Object.keys(derive).reduce((result, _sectionName) => {
@@ -464,9 +474,9 @@ export default abstract class ApiBase<OnCall> implements ApiBaseInterface<OnCall
section[methodName] = method;
return section;
}, {} as DeriveSection<T>);
}, {} as DeriveSection<C, S>);
return result;
}, {} as Derive<T>);
}, {} as Derive<C, S>);
}
}
+94 -40
View File
@@ -3,84 +3,138 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { KeyringPair } from '@polkadot/keyring/types';
import { Extrinsic, ExtrinsicStatus, Index, Method, SignedBlock } from '@polkadot/types/index';
import { AnyNumber, AnyU8a } from '@polkadot/types/types';
import { ApiInterface$Rx, OnCallFunction, SubmittableSendResult } from './types';
import { Extrinsic, ExtrinsicStatus, EventRecord, Index, Method, SignedBlock, Text } from '@polkadot/types/index';
import { AnyNumber, AnyU8a, CodecCallback } from '@polkadot/types/types';
import { ApiInterface$Rx, OnCallDefinition } from './types';
import { Observable, of, combineLatest } from 'rxjs';
import { first, map, switchMap } from 'rxjs/operators';
import { Struct, Vector } from '@polkadot/types/codec';
import filterEvents from './util/filterEvents';
export default class SubmittableExtrinsic<OnCall> extends Extrinsic {
private _api: ApiInterface$Rx;
private _onCall: OnCallFunction<OnCall>;
// Re-introduce once we use submitExtrinsic as well
// type SumbitableResultResult<CodecResult, SubscriptionResult> =
// CodecResult extends Observable<any>
// ? Observable<SubmittableResult>
// : Promise<SubmittableResult>;
constructor (api: ApiInterface$Rx, onCall: OnCallFunction<OnCall>, extrinsic: Extrinsic | Method) {
type SumbitableResultSubscription<CodecResult, SubscriptionResult> =
SubscriptionResult extends Observable<any>
? Observable<SubmittableResult>
: Promise<() => void>;
export class SubmittableResult extends Struct {
constructor (value?: any) {
super({
events: Vector.with(EventRecord),
status: ExtrinsicStatus,
type: Text
}, value);
}
/**
* @description the contained events
*/
get events (): Array<EventRecord> {
return this.get('events') as Vector<EventRecord>;
}
/**
* @description the status
*/
get status (): ExtrinsicStatus {
return this.get('status') as ExtrinsicStatus;
}
/**
* @description the type
*/
get type (): string {
return (this.get('type') as Text).toString();
}
}
export default class SubmittableExtrinsic<CodecResult, SubscriptionResult> extends Extrinsic {
private _api: ApiInterface$Rx;
private _onCall: OnCallDefinition<CodecResult, SubscriptionResult>;
constructor (api: ApiInterface$Rx, onCall: OnCallDefinition<CodecResult, SubscriptionResult>, extrinsic: Extrinsic | Method) {
super(extrinsic);
this._api = api;
this._onCall = onCall;
}
private trackStatus (status: ExtrinsicStatus, statusCb?: (result: SubmittableSendResult) => any): Observable<SubmittableSendResult> {
private statusObservable (status: ExtrinsicStatus): Observable<SubmittableResult> {
if (status.type !== 'Finalised') {
return this._onCall(
() => of({
return of(
new SubmittableResult({
status,
type: status.type
}),
[statusCb],
false
) as unknown as Observable<SubmittableSendResult>;
})
);
}
const blockHash = status.asFinalised;
return this._onCall(
() => combineLatest(
this._api.rpc.chain.getBlock(blockHash),
this._api.query.system.events.at(blockHash)
).pipe(
map(([signedBlock, allEvents]) => ({
events: filterEvents(this.hash, signedBlock as SignedBlock, allEvents as any),
return combineLatest(
this._api.rpc.chain.getBlock(blockHash) as Observable<SignedBlock>,
this._api.query.system.events.at(blockHash) as Observable<Vector<EventRecord>>
).pipe(
map(([signedBlock, allEvents]) =>
new SubmittableResult({
events: filterEvents(this.hash, signedBlock, allEvents),
status,
type: status.type
}))
),
[statusCb],
false
) as unknown as Observable<SubmittableSendResult>;
})
)
);
}
send (statusCb?: (result: SubmittableSendResult) => any): Observable<SubmittableSendResult> {
private sendObservable (): Observable<SubmittableResult> {
return (this._api.rpc.author
.submitAndWatchExtrinsic(this) as Observable<ExtrinsicStatus>)
.pipe(
switchMap((status) =>
this.statusObservable(status)
)
);
}
// FIXME We currently only cater for callback in Promise-world, submitExtrinsic -> Hash
// should also be catered for returning SumbitableResultResult
send (statusCb?: (result: SubmittableResult) => any): SumbitableResultSubscription<CodecResult, SubscriptionResult> {
return this._onCall(
() => (this._api.rpc.author
.submitAndWatchExtrinsic(this) as Observable<ExtrinsicStatus>)
.pipe(switchMap((status) => this.trackStatus(status, statusCb))),
[statusCb],
() => this.sendObservable(),
[],
statusCb as CodecCallback,
true
) as unknown as Observable<SubmittableSendResult>;
) as unknown as SumbitableResultSubscription<CodecResult, SubscriptionResult>;
}
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic<OnCall> {
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic<CodecResult, SubscriptionResult> {
super.sign(signerPair, nonce, blockHash || this._api.genesisHash);
return this;
}
signAndSend (signerPair: KeyringPair, statusCb?: (result: SubmittableSendResult) => any): Observable<SubmittableSendResult> {
// FIXME As per previous FIXME on send, same applies
signAndSend (signerPair: KeyringPair, statusCb?: (result: SubmittableResult) => any): SumbitableResultSubscription<CodecResult, SubscriptionResult> {
return this._onCall(
() => this._api.query.system
.accountNonce(signerPair.address())
() => (this._api.query.system
.accountNonce(signerPair.address()) as Observable<Index>)
.pipe(
first(),
switchMap((nonce) =>
this.sign(signerPair, nonce as Index).send(statusCb)
this
.sign(signerPair, nonce)
.sendObservable()
)
),
[statusCb],
false
) as unknown as Observable<SubmittableSendResult>;
[],
statusCb as CodecCallback,
true
) as unknown as SumbitableResultSubscription<CodecResult, SubscriptionResult>;
}
}
+3 -3
View File
@@ -2,13 +2,13 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { PromiseSubscription } from './types';
import { SubscriptionResult } from './types';
import { isFunction } from '@polkadot/util';
export type CombinatorCallback = (value: Array<any>) => any;
export type CombinatorFunction = {
(cb: (value: any) => any): PromiseSubscription
(cb: (value: any) => any): SubscriptionResult
};
export default class Combinator {
@@ -18,7 +18,7 @@ export default class Combinator {
protected _fns: Array<CombinatorFunction> = [];
protected _isActive: boolean = true;
protected _results: Array<any> = [];
protected _subscriptions: Array<PromiseSubscription> = [];
protected _subscriptions: Array<SubscriptionResult> = [];
constructor (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback) {
this._callback = callback;
+2 -2
View File
@@ -2,13 +2,13 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { PromiseSubscription } from './types';
import { SubscriptionResult } from './types';
import Combinator from './Combinator';
describe('Combinator', () => {
let fns: Array<(value: any) => void> = [];
const storeFn = async (cb: (value: any) => void): PromiseSubscription => {
const storeFn = async (cb: (value: any) => void): SubscriptionResult => {
fns.push(cb);
return () => undefined;
+15 -20
View File
@@ -3,19 +3,18 @@
// 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, OnCall, PromiseSubscription } from './types';
import { CodecArg, CodecCallback } from '@polkadot/types/types';
import { RxResult } from '../rx/types';
import { ApiOptions, OnCallFunction } from '../types';
import { ApiPromiseInterface, CodecResult, SubscriptionResult } from './types';
import { EMPTY, Observable } from 'rxjs';
import { EMPTY } from 'rxjs';
import { catchError, first, tap } from 'rxjs/operators';
import { Codec } from '@polkadot/types/types';
import { isFunction } from '@polkadot/util';
import { isFunction, assert } from '@polkadot/util';
import ApiBase from '../Base';
import Combinator, { CombinatorCallback, CombinatorFunction } from './Combinator';
type RxFn = (...params: Array<any>) => Observable<Codec | undefined | null>;
/**
* # @polkadot/api/promise
*
@@ -102,7 +101,7 @@ type RxFn = (...params: Array<any>) => Observable<Codec | undefined | null>;
* });
* ```
*/
export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInterface {
export default class ApiPromise extends ApiBase<CodecResult, SubscriptionResult> implements ApiPromiseInterface {
private _isReady: Promise<ApiPromise>;
/**
@@ -185,7 +184,7 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
* });
* ```
*/
async combineLatest (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback): PromiseSubscription {
async combineLatest (fns: Array<CombinatorFunction | [CombinatorFunction, ...Array<any>]>, callback: CombinatorCallback): SubscriptionResult {
const combinator = new Combinator(fns, callback);
return (): void => {
@@ -193,15 +192,11 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
};
}
protected onCall (method: RxFn, params: Array<any>, isSubscription?: boolean): OnCall {
if (!params || params.length === 0 || isSubscription === false) {
return method(...params).pipe(first()).toPromise();
}
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = [], callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult {
// When we need a subscription, ensure that a valid callback is actually passed
assert(!needsCallback || isFunction(callback), 'Expected a callback to be passed with subscriptions');
const cb = params[params.length - 1];
const remainingParams = params.slice(0, - 1);
if (!isFunction(cb)) {
if (!callback) {
return method(...params).pipe(first()).toPromise();
}
@@ -210,7 +205,7 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
// tslint:disable-next-line
return new Promise((resolve, reject) => {
let isCompleted = false;
const subscription = method(...remainingParams)
const subscription = method(...params)
.pipe(
// if we find an error (invalid params, etc), reject the promise
catchError((error) => {
@@ -232,7 +227,7 @@ export default class ApiPromise extends ApiBase<OnCall> implements ApiPromiseInt
}
})
)
.subscribe(cb);
}) as PromiseSubscription;
.subscribe(callback);
}) as SubscriptionResult;
}
}
+6 -6
View File
@@ -8,14 +8,14 @@ import { QueryableStorageFunction as QueryableStorageFunctionBase, SubmittableEx
import ApiBase from '../Base';
import SubmittableExtrinsicBase from '../SubmittableExtrinsic';
export type PromiseSubscription = Promise<() => any>;
export type SubscriptionResult = Promise<() => any>;
export type OnCall = Promise<Codec | null | undefined> | PromiseSubscription;
export type CodecResult = Promise<Codec>;
export interface ApiPromiseInterface extends ApiBase<OnCall> {
export interface ApiPromiseInterface extends ApiBase<CodecResult, SubscriptionResult> {
readonly isReady: Promise<ApiPromiseInterface>;
}
export type QueryableStorageFunction = QueryableStorageFunctionBase<OnCall>;
export type SubmittableExtrinsic = SubmittableExtrinsicBase<OnCall>;
export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase<OnCall>;
export type QueryableStorageFunction = QueryableStorageFunctionBase<CodecResult, SubscriptionResult>;
export type SubmittableExtrinsic = SubmittableExtrinsicBase<CodecResult, SubscriptionResult>;
export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase<CodecResult, SubscriptionResult>;
+4 -4
View File
@@ -3,11 +3,11 @@
// of the Apache-2.0 license. See the LICENSE file for details.
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { ApiRxInterface, OnCall } from './types';
import { CodecArg } from '@polkadot/types/types';
import { ApiRxInterface, RxResult } from './types';
import { ApiOptions, OnCallFunction } from '../types';
import { Observable, from } from 'rxjs';
import { Codec } from '@polkadot/types/types';
import ApiBase from '../Base';
@@ -112,7 +112,7 @@ import ApiBase from '../Base';
* });
* ```
*/
export default class ApiRx extends ApiBase<OnCall> implements ApiRxInterface {
export default class ApiRx extends ApiBase<RxResult, RxResult> implements ApiRxInterface {
private _isReady: Observable<ApiRx>;
/**
@@ -190,7 +190,7 @@ export default class ApiRx extends ApiBase<OnCall> implements ApiRxInterface {
return this._isReady;
}
protected onCall (method: OnCallFunction<Observable<Codec | undefined | null>>, params: Array<any>): OnCall {
protected onCall (method: OnCallFunction<RxResult, RxResult>, params: Array<CodecArg> = []): RxResult {
return method(...params);
}
}
+2 -2
View File
@@ -7,9 +7,9 @@ import { Codec } from '@polkadot/types/types';
import ApiBase from '../Base';
export type OnCall = Observable<Codec | null | undefined>;
export type RxResult = Observable<Codec>;
export interface ApiRxInterface extends ApiBase<OnCall> {
export interface ApiRxInterface extends ApiBase<RxResult, RxResult> {
readonly isConnected: Observable<boolean>;
readonly isReady: Observable<ApiRxInterface>;
}
+66 -53
View File
@@ -5,79 +5,92 @@
import { Observable } from 'rxjs';
import { ProviderInterface } from '@polkadot/rpc-provider/types';
import { RpcRxInterface$Events } from '@polkadot/rpc-rx/types';
import { EventRecord, ExtrinsicStatus, Hash, Metadata, RuntimeVersion } from '@polkadot/types/index';
import { Codec, Constructor } from '@polkadot/types/types';
import { Hash, Metadata, RuntimeVersion, u64 as U64 } from '@polkadot/types/index';
import { CodecArg, CodecCallback, Constructor } from '@polkadot/types/types';
import { MethodFunction } from '@polkadot/types/Method';
import { StorageFunction } from '@polkadot/types/StorageKey';
import { RxResult } from './rx/types';
import SubmittableExtrinsic from './SubmittableExtrinsic';
export type OnCallFunction<OnCall> = (...args: any[]) => OnCall;
export type OnCallDefinition<CodecResult, SubscriptionResult> = (method: OnCallFunction<RxResult, RxResult>, params?: Array<CodecArg>, callback?: CodecCallback, needsCallback?: boolean) => CodecResult | SubscriptionResult;
export type DecoratedRpc$Method<OnCall> = (...params: Array<any>) => OnCall;
export type OnCallFunction<CodecResult, SubscriptionResult> = (...params: Array<CodecArg>) => CodecResult | SubscriptionResult;
export interface DecoratedRpc$Section<OnCall> {
[index: string]: DecoratedRpc$Method<OnCall>;
// checked against max. params in jsonrpc, 1 for subs, 3 without
export interface DecoratedRpc$Method<CodecResult, SubscriptionResult> {
(callback: CodecCallback): SubscriptionResult;
(arg1: CodecArg, callback: CodecCallback): SubscriptionResult;
(arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult;
}
export interface DecoratedRpc<OnCall> {
author: DecoratedRpc$Section<OnCall>;
chain: DecoratedRpc$Section<OnCall>;
state: DecoratedRpc$Section<OnCall>;
system: DecoratedRpc$Section<OnCall>;
export interface DecoratedRpc$Section<CodecResult, SubscriptionResult> {
[index: string]: DecoratedRpc$Method<CodecResult, SubscriptionResult>;
}
interface QueryableStorageFunctionBase<OnCall> extends StorageFunction {
(arg?: any): OnCall;
at: (hash: Uint8Array | string, arg?: any) => OnCall;
hash: (arg?: any) => OnCall; // Observable<Hash>
key: (arg?: any) => string;
size: (arg?: any) => OnCall; // Observable<U64>
export interface DecoratedRpc<CodecResult, SubscriptionResult> {
author: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
chain: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
state: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
system: DecoratedRpc$Section<CodecResult, SubscriptionResult>;
}
interface QueryableStorageFunctionPromise<OnCall> extends QueryableStorageFunctionBase<OnCall> {
(arg?: any, cb?: (value?: Codec) => any): OnCall;
export type HashResult<CodecResult, SubscriptionResult> =
CodecResult extends Observable<any>
? Observable<Hash>
: Promise<Hash>;
export type U64Result<CodecResult, SubscriptionResult> =
CodecResult extends Observable<any>
? Observable<U64>
: Promise<U64>;
export interface QueryableStorageFunctionBase<CodecResult, SubscriptionResult> extends StorageFunction {
(arg?: CodecArg): CodecResult;
at: (hash: Hash | Uint8Array | string, arg?: CodecArg) => CodecResult;
hash: (arg?: CodecArg) => HashResult<CodecResult, SubscriptionResult>;
key: (arg?: CodecArg) => string;
size: (arg?: CodecArg) => U64Result<CodecResult, SubscriptionResult>;
}
export type QueryableStorageFunction<OnCall> =
OnCall extends Observable<any>
? QueryableStorageFunctionBase<OnCall>
: QueryableStorageFunctionPromise<OnCall>;
export interface QueryableModuleStorage<OnCall> {
[index: string]: QueryableStorageFunction<OnCall>;
interface QueryableStorageFunctionPromise<CodecResult, SubscriptionResult> extends QueryableStorageFunctionBase<CodecResult, SubscriptionResult> {
(callback: CodecCallback): SubscriptionResult;
(arg: CodecArg, callback: CodecCallback): SubscriptionResult;
}
export interface QueryableStorage<OnCall> {
[index: string]: QueryableModuleStorage<OnCall>;
export type QueryableStorageFunction<CodecResult, SubscriptionResult> =
CodecResult extends Observable<any>
? QueryableStorageFunctionBase<CodecResult, SubscriptionResult>
: QueryableStorageFunctionPromise<CodecResult, SubscriptionResult>;
export interface QueryableModuleStorage<CodecResult, SubscriptionResult> {
[index: string]: QueryableStorageFunction<CodecResult, SubscriptionResult>;
}
export interface SubmittableExtrinsicFunction<OnCall> extends MethodFunction {
(...args: any[]): SubmittableExtrinsic<OnCall>;
export interface QueryableStorage<CodecResult, SubscriptionResult> {
[index: string]: QueryableModuleStorage<CodecResult, SubscriptionResult>;
}
export interface SubmittableModuleExtrinsics<OnCall> {
[index: string]: SubmittableExtrinsicFunction<OnCall>;
export interface SubmittableExtrinsicFunction<CodecResult, SubscriptionResult> extends MethodFunction {
(...params: Array<CodecArg>): SubmittableExtrinsic<CodecResult, SubscriptionResult>;
}
export interface SubmittableExtrinsics<OnCall> {
[index: string]: SubmittableModuleExtrinsics<OnCall>;
export interface SubmittableModuleExtrinsics<CodecResult, SubscriptionResult> {
[index: string]: SubmittableExtrinsicFunction<CodecResult, SubscriptionResult>;
}
export type SubmittableSendResult = {
events?: Array<EventRecord>,
status: ExtrinsicStatus,
type: string
};
export type DeriveMethod<OnCall> = (...params: Array<any>) => OnCall;
export interface DeriveSection<OnCall> {
[index: string]: DeriveMethod<OnCall>;
export interface SubmittableExtrinsics<CodecResult, SubscriptionResult> {
[index: string]: SubmittableModuleExtrinsics<CodecResult, SubscriptionResult>;
}
export interface Derive<OnCall> {
[index: string]: DeriveSection<OnCall>;
export type DeriveMethod<CodecResult, SubscriptionResult> = (...params: Array<CodecArg>) => CodecResult | SubscriptionResult;
export interface DeriveSection<CodecResult, SubscriptionResult> {
[index: string]: DeriveMethod<CodecResult, SubscriptionResult>;
}
export interface Derive<CodecResult, SubscriptionResult> {
[index: string]: DeriveSection<CodecResult, SubscriptionResult>;
}
export interface ApiOptions {
@@ -93,22 +106,22 @@ export interface ApiOptions {
types?: { [name: string]: Constructor };
}
export interface ApiInterface$Decorated<OnCall> {
export interface ApiInterface$Decorated<CodecResult, SubscriptionResult> {
genesisHash: Hash;
hasSubscriptions: boolean;
runtimeMetadata: Metadata;
runtimeVersion: RuntimeVersion;
derive: Derive<OnCall>;
query: QueryableStorage<OnCall>;
rpc: DecoratedRpc<OnCall>;
tx: SubmittableExtrinsics<OnCall>;
derive: Derive<CodecResult, SubscriptionResult>;
query: QueryableStorage<CodecResult, SubscriptionResult>;
rpc: DecoratedRpc<CodecResult, SubscriptionResult>;
tx: SubmittableExtrinsics<CodecResult, SubscriptionResult>;
}
export type ApiInterface$Rx = ApiInterface$Decorated<Observable<Codec | null | undefined>>;
export type ApiInterface$Rx = ApiInterface$Decorated<RxResult, RxResult>;
export type ApiInterface$Events = RpcRxInterface$Events | 'ready';
export interface ApiBaseInterface<OnCall> extends Readonly<ApiInterface$Decorated<OnCall>> {
export interface ApiBaseInterface<CodecResult, SubscriptionResult> extends Readonly<ApiInterface$Decorated<CodecResult, SubscriptionResult>> {
on: (type: ApiInterface$Events, handler: (...args: Array<any>) => any) => this;
once: (type: ApiInterface$Events, handler: (...args: Array<any>) => any) => this;
}
+10
View File
@@ -4,6 +4,16 @@
import BN from 'bn.js';
export type CodecArg = Codec | BN | Boolean | String | Uint8Array | boolean | number | string | undefined | CodecArgArray | CodecArgObject;
export type CodecCallback <T extends Codec = Codec> = (result: T) => any;
interface CodecArgObject {
[index: string]: CodecArg;
}
interface CodecArgArray extends Array<CodecArg> { }
export type AnyNumber = BN | Uint8Array | number | string;
export type AnyString = string | String;