From 661cb3c4534fcd1565b44e4eae59f169b319f70d Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 31 Jan 2019 14:15:59 +0100 Subject: [PATCH] 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 --- packages/api/src/Base.ts | 178 ++++++++++--------- packages/api/src/SubmittableExtrinsic.ts | 134 +++++++++----- packages/api/src/promise/Combinator.ts | 6 +- packages/api/src/promise/Combinators.spec.ts | 4 +- packages/api/src/promise/index.ts | 35 ++-- packages/api/src/promise/types.ts | 12 +- packages/api/src/rx/index.ts | 8 +- packages/api/src/rx/types.ts | 4 +- packages/api/src/types.ts | 119 +++++++------ packages/types/src/types.ts | 10 ++ 10 files changed, 296 insertions(+), 214 deletions(-) diff --git a/packages/api/src/Base.ts b/packages/api/src/Base.ts index 26fc7dceb1..08a0d41eb4 100644 --- a/packages/api/src/Base.ts +++ b/packages/api/src/Base.ts @@ -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>, - params: Array, - _isSubscription?: boolean -): Observable { + method: OnCallFunction, + params: Array = [], + _callback?: CodecCallback +): RxResult { return method(...params); } -export default abstract class ApiBase implements ApiBaseInterface { +export default abstract class ApiBase implements ApiBaseInterface { private _eventemitter: EventEmitter; - private _derive?: Derive; - private _extrinsics?: SubmittableExtrinsics; + private _derive?: Derive; + private _extrinsics?: SubmittableExtrinsics; private _genesisHash?: Hash; - private _query?: QueryableStorage; - private _rpc: DecoratedRpc; + private _query?: QueryableStorage; + private _rpc: DecoratedRpc; 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 implements ApiBaseInterface { + get derive (): Derive { assert(!isUndefined(this._derive), INIT_ERROR); - return this._derive as Derive; + return this._derive as Derive; } /** @@ -172,10 +173,10 @@ export default abstract class ApiBase implements ApiBaseInterface { + get query (): QueryableStorage { assert(!isUndefined(this._query), INIT_ERROR); - return this._query as QueryableStorage; + return this._query as QueryableStorage; } /** @@ -192,7 +193,7 @@ export default abstract class ApiBase implements ApiBaseInterface { + get rpc (): DecoratedRpc { return this._rpc; } @@ -210,10 +211,10 @@ export default abstract class ApiBase implements ApiBaseInterface { + get tx (): SubmittableExtrinsics { assert(!isUndefined(this._extrinsics), INIT_ERROR); - return this._extrinsics as SubmittableExtrinsics; + return this._extrinsics as SubmittableExtrinsics; } /** @@ -266,6 +267,8 @@ export default abstract class ApiBase implements ApiBaseInterface, params?: Array, callback?: CodecCallback, needsCallback?: boolean): CodecResult | SubscriptionResult; + private emit (type: ApiInterface$Events, ...args: Array): void { this._eventemitter.emit(type, ...args); } @@ -323,8 +326,6 @@ export default abstract class ApiBase implements ApiBaseInterface>, params: Array, 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 implements ApiBaseInterface ( - rpc: RpcRx, - onCall: (method: OnCallFunction>, params: Array, isSubscription?: boolean) => T - ): DecoratedRpc { - + private decorateRpc (rpc: RpcRx, onCall: OnCallDefinition): DecoratedRpc { return ['author', 'chain', 'state', 'system'].reduce((result, _sectionName) => { - const sectionName = _sectionName as keyof DecoratedRpc; + const sectionName = _sectionName as keyof DecoratedRpc; 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): 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; + section[methodName] = method; return section; - }, {} as DecoratedRpc$Section); + }, {} as DecoratedRpc$Section); return result; - }, {} as DecoratedRpc); + }, {} as DecoratedRpc); } - private decorateExtrinsics ( - extrinsics: ModulesWithMethods, - onCall: (method: OnCallFunction>, params: Array, isSubscription?: boolean) => T - ): SubmittableExtrinsics { - + private decorateExtrinsics (extrinsics: ModulesWithMethods, onCall: OnCallDefinition): SubmittableExtrinsics { return Object.keys(extrinsics).reduce((result, sectionName) => { const section = extrinsics[sectionName]; @@ -371,27 +380,20 @@ export default abstract class ApiBase implements ApiBaseInterface); + }, {} as SubmittableModuleExtrinsics); return result; - }, {} as SubmittableExtrinsics); + }, {} as SubmittableExtrinsics); } - private decorateExtrinsicEntry ( - method: MethodFunction, - onCall: (method: OnCallFunction>, params: Array, isSubscription?: boolean) => T - ): SubmittableExtrinsicFunction { - const decorated: any = (...args: Array): SubmittableExtrinsic => - new SubmittableExtrinsic(this._rx as ApiInterface$Rx, onCall, method(...args)); + private decorateExtrinsicEntry (method: MethodFunction, onCall: OnCallDefinition): SubmittableExtrinsicFunction { + const decorated: any = (...params: Array): SubmittableExtrinsic => + new SubmittableExtrinsic(this._rx as ApiInterface$Rx, onCall, method(...params)); - return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction; + return this.decorateFunctionMeta(method, decorated) as SubmittableExtrinsicFunction; } - private decorateStorage ( - storage: Storage, - onCall: (method: OnCallFunction>, params: Array, isSubscription?: boolean) => T - ): QueryableStorage { - + private decorateStorage (storage: Storage, onCall: OnCallDefinition): QueryableStorage { return Object.keys(storage).reduce((result, sectionName) => { const section = storage[sectionName]; @@ -399,61 +401,69 @@ export default abstract class ApiBase implements ApiBaseInterface); + }, {} as QueryableModuleStorage); return result; - }, {} as QueryableStorage); + }, {} as QueryableStorage); } - private decorateStorageEntry ( - method: StorageFunction, - onCall: (method: OnCallFunction>, params: Array, isSubscription?: boolean) => T - ): QueryableStorageFunction { - const decorated: any = (...args: any): T => - onCall( - (...args: any) => this._rpcRx.state - .subscribeStorage([[method, args[0]]]) + private decorateStorageEntry (method: StorageFunction, onCall: OnCallDefinition): QueryableStorageFunction { + // These signatures are allowed and exposed here - + // (arg?: CodecArg): CodecResult; + // (arg: CodecArg, callback: CodecCallback): SubscriptionResult; + // (callback: CodecCallback): SubscriptionResult; + const decorated = ((...args: Array): 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 => + map((result: Array): Codec => result[0] ) ), - args + params, + callback ); + }) as QueryableStorageFunction; - 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 => onCall( - arg => this._rpcRx.state.getStorageSize([method, arg]).pipe(catchError(() => of())), + (arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]), [arg] - ); + ) as unknown as HashResult; - decorated.size = (arg?: any): T => + // FIXME as above... + decorated.size = (arg?: CodecArg): U64Result => onCall( - arg => this._rpcRx.state.getStorageSize([method, arg]).pipe(catchError(() => of())), + (arg: CodecArg) => this._rpcRx.state.getStorageSize([method, arg]), [arg] - ); + ) as unknown as U64Result; - decorated.key = (arg?: any): string => + decorated.key = (arg?: CodecArg): string => u8aToHex(compactStripLength(method(arg))[1]); - return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction; + return this.decorateFunctionMeta(method, decorated) as QueryableStorageFunction; } - private decorateDerive ( - apiRx: ApiInterface$Rx, - onCall: (method: OnCallFunction>, params: Array, isSubscription?: boolean) => T - ): Derive { + private decorateDerive (apiRx: ApiInterface$Rx, onCall: OnCallDefinition): Derive { const derive = decorateDerive(apiRx); return Object.keys(derive).reduce((result, _sectionName) => { @@ -464,9 +474,9 @@ export default abstract class ApiBase implements ApiBaseInterface); + }, {} as DeriveSection); return result; - }, {} as Derive); + }, {} as Derive); } } diff --git a/packages/api/src/SubmittableExtrinsic.ts b/packages/api/src/SubmittableExtrinsic.ts index f992a73315..be31da76b7 100644 --- a/packages/api/src/SubmittableExtrinsic.ts +++ b/packages/api/src/SubmittableExtrinsic.ts @@ -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 extends Extrinsic { - private _api: ApiInterface$Rx; - private _onCall: OnCallFunction; +// Re-introduce once we use submitExtrinsic as well +// type SumbitableResultResult = +// CodecResult extends Observable +// ? Observable +// : Promise; - constructor (api: ApiInterface$Rx, onCall: OnCallFunction, extrinsic: Extrinsic | Method) { +type SumbitableResultSubscription = + SubscriptionResult extends Observable + ? Observable + : 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 { + return this.get('events') as Vector; + } + + /** + * @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 extends Extrinsic { + private _api: ApiInterface$Rx; + private _onCall: OnCallDefinition; + + constructor (api: ApiInterface$Rx, onCall: OnCallDefinition, extrinsic: Extrinsic | Method) { super(extrinsic); this._api = api; this._onCall = onCall; } - private trackStatus (status: ExtrinsicStatus, statusCb?: (result: SubmittableSendResult) => any): Observable { + private statusObservable (status: ExtrinsicStatus): Observable { if (status.type !== 'Finalised') { - return this._onCall( - () => of({ + return of( + new SubmittableResult({ status, type: status.type - }), - [statusCb], - false - ) as unknown as Observable; + }) + ); } 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, + this._api.query.system.events.at(blockHash) as Observable> + ).pipe( + map(([signedBlock, allEvents]) => + new SubmittableResult({ + events: filterEvents(this.hash, signedBlock, allEvents), status, type: status.type - })) - ), - [statusCb], - false - ) as unknown as Observable; + }) + ) + ); } - send (statusCb?: (result: SubmittableSendResult) => any): Observable { + private sendObservable (): Observable { + return (this._api.rpc.author + .submitAndWatchExtrinsic(this) as Observable) + .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 { return this._onCall( - () => (this._api.rpc.author - .submitAndWatchExtrinsic(this) as Observable) - .pipe(switchMap((status) => this.trackStatus(status, statusCb))), - [statusCb], + () => this.sendObservable(), + [], + statusCb as CodecCallback, true - ) as unknown as Observable; + ) as unknown as SumbitableResultSubscription; } - sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic { + sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic { super.sign(signerPair, nonce, blockHash || this._api.genesisHash); return this; } - signAndSend (signerPair: KeyringPair, statusCb?: (result: SubmittableSendResult) => any): Observable { + // FIXME As per previous FIXME on send, same applies + signAndSend (signerPair: KeyringPair, statusCb?: (result: SubmittableResult) => any): SumbitableResultSubscription { return this._onCall( - () => this._api.query.system - .accountNonce(signerPair.address()) + () => (this._api.query.system + .accountNonce(signerPair.address()) as Observable) .pipe( first(), switchMap((nonce) => - this.sign(signerPair, nonce as Index).send(statusCb) + this + .sign(signerPair, nonce) + .sendObservable() ) ), - [statusCb], - false - ) as unknown as Observable; + [], + statusCb as CodecCallback, + true + ) as unknown as SumbitableResultSubscription; } } diff --git a/packages/api/src/promise/Combinator.ts b/packages/api/src/promise/Combinator.ts index a87c074c62..d41e6e82c8 100644 --- a/packages/api/src/promise/Combinator.ts +++ b/packages/api/src/promise/Combinator.ts @@ -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; 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 = []; protected _isActive: boolean = true; protected _results: Array = []; - protected _subscriptions: Array = []; + protected _subscriptions: Array = []; constructor (fns: Array]>, callback: CombinatorCallback) { this._callback = callback; diff --git a/packages/api/src/promise/Combinators.spec.ts b/packages/api/src/promise/Combinators.spec.ts index 376ad90447..9d60b695e5 100644 --- a/packages/api/src/promise/Combinators.spec.ts +++ b/packages/api/src/promise/Combinators.spec.ts @@ -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; diff --git a/packages/api/src/promise/index.ts b/packages/api/src/promise/index.ts index 019dfdcd37..b893235719 100644 --- a/packages/api/src/promise/index.ts +++ b/packages/api/src/promise/index.ts @@ -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) => Observable; - /** * # @polkadot/api/promise * @@ -102,7 +101,7 @@ type RxFn = (...params: Array) => Observable; * }); * ``` */ -export default class ApiPromise extends ApiBase implements ApiPromiseInterface { +export default class ApiPromise extends ApiBase implements ApiPromiseInterface { private _isReady: Promise; /** @@ -185,7 +184,7 @@ export default class ApiPromise extends ApiBase implements ApiPromiseInt * }); * ``` */ - async combineLatest (fns: Array]>, callback: CombinatorCallback): PromiseSubscription { + async combineLatest (fns: Array]>, callback: CombinatorCallback): SubscriptionResult { const combinator = new Combinator(fns, callback); return (): void => { @@ -193,15 +192,11 @@ export default class ApiPromise extends ApiBase implements ApiPromiseInt }; } - protected onCall (method: RxFn, params: Array, isSubscription?: boolean): OnCall { - if (!params || params.length === 0 || isSubscription === false) { - return method(...params).pipe(first()).toPromise(); - } + protected onCall (method: OnCallFunction, params: Array = [], 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 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 implements ApiPromiseInt } }) ) - .subscribe(cb); - }) as PromiseSubscription; + .subscribe(callback); + }) as SubscriptionResult; } } diff --git a/packages/api/src/promise/types.ts b/packages/api/src/promise/types.ts index 4bbfe634e1..ec0a3ee92d 100644 --- a/packages/api/src/promise/types.ts +++ b/packages/api/src/promise/types.ts @@ -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 | PromiseSubscription; +export type CodecResult = Promise; -export interface ApiPromiseInterface extends ApiBase { +export interface ApiPromiseInterface extends ApiBase { readonly isReady: Promise; } -export type QueryableStorageFunction = QueryableStorageFunctionBase; -export type SubmittableExtrinsic = SubmittableExtrinsicBase; -export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase; +export type QueryableStorageFunction = QueryableStorageFunctionBase; +export type SubmittableExtrinsic = SubmittableExtrinsicBase; +export type SubmittableExtrinsicFunction = SubmittableExtrinsicFunctionBase; diff --git a/packages/api/src/rx/index.ts b/packages/api/src/rx/index.ts index 66ebbba2e4..aac8f190a6 100644 --- a/packages/api/src/rx/index.ts +++ b/packages/api/src/rx/index.ts @@ -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 implements ApiRxInterface { +export default class ApiRx extends ApiBase implements ApiRxInterface { private _isReady: Observable; /** @@ -190,7 +190,7 @@ export default class ApiRx extends ApiBase implements ApiRxInterface { return this._isReady; } - protected onCall (method: OnCallFunction>, params: Array): OnCall { + protected onCall (method: OnCallFunction, params: Array = []): RxResult { return method(...params); } } diff --git a/packages/api/src/rx/types.ts b/packages/api/src/rx/types.ts index 9695dcc770..f66c8053cd 100644 --- a/packages/api/src/rx/types.ts +++ b/packages/api/src/rx/types.ts @@ -7,9 +7,9 @@ import { Codec } from '@polkadot/types/types'; import ApiBase from '../Base'; -export type OnCall = Observable; +export type RxResult = Observable; -export interface ApiRxInterface extends ApiBase { +export interface ApiRxInterface extends ApiBase { readonly isConnected: Observable; readonly isReady: Observable; } diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 90f45e2f12..6672a60b36 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -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 = (...args: any[]) => OnCall; +export type OnCallDefinition = (method: OnCallFunction, params?: Array, callback?: CodecCallback, needsCallback?: boolean) => CodecResult | SubscriptionResult; -export type DecoratedRpc$Method = (...params: Array) => OnCall; +export type OnCallFunction = (...params: Array) => CodecResult | SubscriptionResult; -export interface DecoratedRpc$Section { - [index: string]: DecoratedRpc$Method; +// checked against max. params in jsonrpc, 1 for subs, 3 without +export interface DecoratedRpc$Method { + (callback: CodecCallback): SubscriptionResult; + (arg1: CodecArg, callback: CodecCallback): SubscriptionResult; + (arg1?: CodecArg, arg2?: CodecArg, arg3?: CodecArg): CodecResult; } -export interface DecoratedRpc { - author: DecoratedRpc$Section; - chain: DecoratedRpc$Section; - state: DecoratedRpc$Section; - system: DecoratedRpc$Section; +export interface DecoratedRpc$Section { + [index: string]: DecoratedRpc$Method; } -interface QueryableStorageFunctionBase extends StorageFunction { - (arg?: any): OnCall; - at: (hash: Uint8Array | string, arg?: any) => OnCall; - hash: (arg?: any) => OnCall; // Observable - key: (arg?: any) => string; - size: (arg?: any) => OnCall; // Observable +export interface DecoratedRpc { + author: DecoratedRpc$Section; + chain: DecoratedRpc$Section; + state: DecoratedRpc$Section; + system: DecoratedRpc$Section; } -interface QueryableStorageFunctionPromise extends QueryableStorageFunctionBase { - (arg?: any, cb?: (value?: Codec) => any): OnCall; +export type HashResult = + CodecResult extends Observable + ? Observable + : Promise; + +export type U64Result = + CodecResult extends Observable + ? Observable + : Promise; + +export interface QueryableStorageFunctionBase extends StorageFunction { + (arg?: CodecArg): CodecResult; + at: (hash: Hash | Uint8Array | string, arg?: CodecArg) => CodecResult; + hash: (arg?: CodecArg) => HashResult; + key: (arg?: CodecArg) => string; + size: (arg?: CodecArg) => U64Result; } -export type QueryableStorageFunction = - OnCall extends Observable - ? QueryableStorageFunctionBase - : QueryableStorageFunctionPromise; - -export interface QueryableModuleStorage { - [index: string]: QueryableStorageFunction; +interface QueryableStorageFunctionPromise extends QueryableStorageFunctionBase { + (callback: CodecCallback): SubscriptionResult; + (arg: CodecArg, callback: CodecCallback): SubscriptionResult; } -export interface QueryableStorage { - [index: string]: QueryableModuleStorage; +export type QueryableStorageFunction = + CodecResult extends Observable + ? QueryableStorageFunctionBase + : QueryableStorageFunctionPromise; + +export interface QueryableModuleStorage { + [index: string]: QueryableStorageFunction; } -export interface SubmittableExtrinsicFunction extends MethodFunction { - (...args: any[]): SubmittableExtrinsic; +export interface QueryableStorage { + [index: string]: QueryableModuleStorage; } -export interface SubmittableModuleExtrinsics { - [index: string]: SubmittableExtrinsicFunction; +export interface SubmittableExtrinsicFunction extends MethodFunction { + (...params: Array): SubmittableExtrinsic; } -export interface SubmittableExtrinsics { - [index: string]: SubmittableModuleExtrinsics; +export interface SubmittableModuleExtrinsics { + [index: string]: SubmittableExtrinsicFunction; } -export type SubmittableSendResult = { - events?: Array, - status: ExtrinsicStatus, - type: string -}; - -export type DeriveMethod = (...params: Array) => OnCall; - -export interface DeriveSection { - [index: string]: DeriveMethod; +export interface SubmittableExtrinsics { + [index: string]: SubmittableModuleExtrinsics; } -export interface Derive { - [index: string]: DeriveSection; +export type DeriveMethod = (...params: Array) => CodecResult | SubscriptionResult; + +export interface DeriveSection { + [index: string]: DeriveMethod; +} + +export interface Derive { + [index: string]: DeriveSection; } export interface ApiOptions { @@ -93,22 +106,22 @@ export interface ApiOptions { types?: { [name: string]: Constructor }; } -export interface ApiInterface$Decorated { +export interface ApiInterface$Decorated { genesisHash: Hash; hasSubscriptions: boolean; runtimeMetadata: Metadata; runtimeVersion: RuntimeVersion; - derive: Derive; - query: QueryableStorage; - rpc: DecoratedRpc; - tx: SubmittableExtrinsics; + derive: Derive; + query: QueryableStorage; + rpc: DecoratedRpc; + tx: SubmittableExtrinsics; } -export type ApiInterface$Rx = ApiInterface$Decorated>; +export type ApiInterface$Rx = ApiInterface$Decorated; export type ApiInterface$Events = RpcRxInterface$Events | 'ready'; -export interface ApiBaseInterface extends Readonly> { +export interface ApiBaseInterface extends Readonly> { on: (type: ApiInterface$Events, handler: (...args: Array) => any) => this; once: (type: ApiInterface$Events, handler: (...args: Array) => any) => this; } diff --git a/packages/types/src/types.ts b/packages/types/src/types.ts index 40365c502f..d6454c2acb 100644 --- a/packages/types/src/types.ts +++ b/packages/types/src/types.ts @@ -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 = (result: T) => any; + +interface CodecArgObject { + [index: string]: CodecArg; +} + +interface CodecArgArray extends Array { } + export type AnyNumber = BN | Uint8Array | number | string; export type AnyString = string | String;