From b16ccb253ac472c1693193d893514971cf384cf3 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Mon, 18 Feb 2019 11:58:50 +0100 Subject: [PATCH] Inject custom derives (#711) --- packages/api-derive/src/index.spec.ts | 75 +++++++++++++++++++-------- packages/api-derive/src/index.ts | 35 ++++++++++--- packages/api/src/Base.ts | 2 +- packages/api/src/types.ts | 5 ++ 4 files changed, 86 insertions(+), 31 deletions(-) diff --git a/packages/api-derive/src/index.spec.ts b/packages/api-derive/src/index.spec.ts index 51d7e8af3d..b171e8c995 100644 --- a/packages/api-derive/src/index.spec.ts +++ b/packages/api-derive/src/index.spec.ts @@ -2,7 +2,7 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. -import { Observable } from 'rxjs'; +import { Observable, from } from 'rxjs'; import ApiRx from '@polkadot/api/rx'; import { ApiInterface$Rx } from '@polkadot/api/types'; import MockProvider from '@polkadot/rpc-provider/mock'; @@ -34,31 +34,62 @@ const testFunction = (api: ApiInterface$Rx) => { }; describe('derive', () => { - const api = new ApiRx(new MockProvider()); + describe('builtin', () => { + const api = new ApiRx(new MockProvider()); - beforeAll((done) => api.isReady.subscribe(() => done())); + beforeAll((done) => { + api.isReady.subscribe(() => done()); + }); - testFunction(api)('accounts', 'idAndIndex', []); - testFunction(api)('accounts', 'idToIndex', []); - testFunction(api)('accounts', 'indexes', []); - testFunction(api)('accounts', 'indexToId', []); + testFunction(api)('accounts', 'idAndIndex', []); + testFunction(api)('accounts', 'idToIndex', []); + testFunction(api)('accounts', 'indexes', []); + testFunction(api)('accounts', 'indexToId', []); - testFunction(api)('balances', 'fees', []); - testFunction(api)('balances', 'validatingBalance', []); - testFunction(api)('balances', 'validatingBalances', []); - testFunction(api)('balances', 'votingBalance', []); - testFunction(api)('balances', 'votingBalances', []); - testFunction(api)('balances', 'votingBalancesNominatorsFor', []); + testFunction(api)('balances', 'fees', []); + testFunction(api)('balances', 'validatingBalance', []); + testFunction(api)('balances', 'validatingBalances', []); + testFunction(api)('balances', 'votingBalance', []); + testFunction(api)('balances', 'votingBalances', []); + testFunction(api)('balances', 'votingBalancesNominatorsFor', []); - testFunction(api)('chain', 'bestNumber', []); - testFunction(api)('chain', 'bestNumberFinalised', []); + testFunction(api)('chain', 'bestNumber', []); + testFunction(api)('chain', 'bestNumberFinalised', []); - testFunction(api)('democracy', 'referendumInfos', []); - testFunction(api)('democracy', 'referendums', []); - testFunction(api)('democracy', 'referendumVotesFor', []); - testFunction(api)('democracy', 'votes', []); + testFunction(api)('democracy', 'referendumInfos', []); + testFunction(api)('democracy', 'referendums', []); + testFunction(api)('democracy', 'referendumVotesFor', []); + testFunction(api)('democracy', 'votes', []); - testFunction(api)('session', 'eraLength', []); - testFunction(api)('session', 'eraProgress', []); - testFunction(api)('session', 'sessionProgress', []); + testFunction(api)('session', 'eraLength', []); + testFunction(api)('session', 'eraProgress', []); + testFunction(api)('session', 'sessionProgress', []); + }); + + describe('custom', () => { + const api = new ApiRx({ + derives: { + balances: { + fees: () => () => from(['a', 'b']) + }, + custom: { + test: () => () => from([1, 2, 3]) + } + }, + provider: new MockProvider() + }); + + beforeAll((done) => { + api.isReady.subscribe(() => done()); + }); + + // override + testFunction(api)('balances', 'fees', ['a', 'b']); + + // new + testFunction(api)('custom' as any, 'test', [1, 2, 3]); + + // existing + testFunction(api)('chain', 'bestNumber', []); + }); }); diff --git a/packages/api-derive/src/index.ts b/packages/api-derive/src/index.ts index f64ca86060..824c818da3 100644 --- a/packages/api-derive/src/index.ts +++ b/packages/api-derive/src/index.ts @@ -2,15 +2,16 @@ // This software may be modified and distributed under the terms // of the Apache-2.0 license. See the LICENSE file for details. +import { Observable } from 'rxjs'; import { ApiInterface$Rx } from '@polkadot/api/types'; +import { cache } from './util/cache'; import * as accounts from './accounts'; import * as balances from './balances'; import * as chain from './chain'; import * as democracy from './democracy'; import * as session from './session'; import * as staking from './staking'; -import { cache } from './util/cache'; // Put all derived functions in an object, for easier Object.keys()-ing. const functions = { accounts, balances, chain, democracy, session, staking }; @@ -23,6 +24,16 @@ type ReturnTypes any>> = { [P in keyof T]: ReturnType }; +export interface DeriveCustomMethod { + (api: ApiInterface$Rx): (...args: Array) => Observable; +} + +export interface DeriveCustom { + [index: string]: { + [index: string]: DeriveCustomMethod + }; +} + export interface Derive { accounts: ReturnTypes; balances: ReturnTypes; @@ -32,22 +43,30 @@ export interface Derive { staking: ReturnTypes; } -export default function decorateDerive (api: ApiInterface$Rx): Derive { - const derive = {} as Derive; - +function injectFunctions (api: ApiInterface$Rx, derive: Derive, functions: DeriveCustom): Derive { Object.keys(functions).forEach((sectionName: string) => { const section = functions[sectionName as keyof Derive]; - derive[sectionName as keyof Derive] = Object.keys(section).reduce((result, methodName) => { + const result = derive[sectionName as keyof Derive] = derive[sectionName as keyof Derive] || {}; + + Object.keys(section).forEach((methodName) => { // Create cache for the section_method function // @ts-ignore No idea how to make this work... const cached = cache(section[methodName as keyof section]); + // Add this cached function into the result // @ts-ignore No idea how to make this work... result[methodName as keyof section] = cached(api); - - return result; - }, {} as ReturnTypes); + }); }); return derive; } + +export default function decorateDerive (api: ApiInterface$Rx, custom: DeriveCustom = {}): Derive { + const derive = {} as Derive; + + injectFunctions(api, derive, functions); + injectFunctions(api, derive, custom); + + return derive; +} diff --git a/packages/api/src/Base.ts b/packages/api/src/Base.ts index d78529b6b4..ae738881f3 100644 --- a/packages/api/src/Base.ts +++ b/packages/api/src/Base.ts @@ -517,7 +517,7 @@ export default abstract class ApiBase implement } private decorateDerive (apiRx: ApiInterface$Rx, onCall: OnCallDefinition): Derive { - const derive = decorateDerive(apiRx); + const derive = decorateDerive(apiRx, this._options.derives); return Object.keys(derive).reduce((result, _sectionName) => { const sectionName = _sectionName as keyof DeriveInterface; diff --git a/packages/api/src/types.ts b/packages/api/src/types.ts index 610c2faed9..569b39e69e 100644 --- a/packages/api/src/types.ts +++ b/packages/api/src/types.ts @@ -3,6 +3,7 @@ // of the Apache-2.0 license. See the LICENSE file for details. import { Observable } from 'rxjs'; +import { DeriveCustom } from '@polkadot/api-derive/index'; import { ProviderInterface } from '@polkadot/rpc-provider/types'; import { RpcRxInterface$Events } from '@polkadot/rpc-rx/types'; import { Extrinsic, Hash, Metadata, RuntimeVersion, u64 as U64 } from '@polkadot/types/index'; @@ -109,6 +110,10 @@ export interface Derive { } export interface ApiOptions { + /** + * @description Add custom derives to be injected + */ + derives?: DeriveCustom; /** * @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`