From 761ceaeaa35182d030ee14e022028321aff41f65 Mon Sep 17 00:00:00 2001 From: Jaco Date: Mon, 1 Nov 2021 10:35:17 +0100 Subject: [PATCH] Internal adjustments (part 6) (#4138) * Internal adjustments (part 6) * Additional --- packages/api/src/base/Decorate.ts | 104 +++++++++++++++---------- packages/api/src/base/Init.ts | 26 ++++--- packages/api/src/util/augmentObject.ts | 79 ++++++++++--------- packages/api/src/util/decorate.ts | 51 ++++++------ 4 files changed, 147 insertions(+), 113 deletions(-) diff --git a/packages/api/src/base/Decorate.ts b/packages/api/src/base/Decorate.ts index f4580be8dd..cc5197103d 100644 --- a/packages/api/src/base/Decorate.ts +++ b/packages/api/src/base/Decorate.ts @@ -322,7 +322,11 @@ export abstract class Decorate extends Events { protected _filterRpcMethods (exposed: string[]): void { const hasResults = exposed.length !== 0; const allKnown = [...this._rpcCore.mapping.entries()]; - const allKeys = allKnown.reduce((allKeys: string[], [, { alias, endpoint, method, pubsub, section }]): string[] => { + const allKeys: string[] = []; + + for (let i = 0; i < allKnown.length; i++) { + const [, { alias, endpoint, method, pubsub, section }] = allKnown[i]; + allKeys.push(`${section}_${method}`); if (pubsub) { @@ -337,11 +341,10 @@ export abstract class Decorate extends Events { if (endpoint) { allKeys.push(endpoint); } + } - return allKeys; - }, []); - const unknown = exposed.filter((k) => !allKeys.includes(k)); - const deletion = allKnown.filter(([k]) => hasResults && !exposed.includes(k) && k !== 'rpc_methods'); + const filterKey = (k: string) => !allKeys.includes(k); + const unknown = exposed.filter(filterKey); if (unknown.length) { l.warn(`RPC methods not decorated: ${unknown.join(', ')}`); @@ -349,19 +352,26 @@ export abstract class Decorate extends Events { // loop through all entries we have (populated in decorate) and filter as required // only remove when we have results and method missing, or with no results if optional - deletion.forEach(([, { method, section }]): void => { - delete (this._rpc as Record>)[section][method]; - delete (this._rx.rpc as Record>)[section][method]; - }); + for (let i = 0; i < allKnown.length; i++) { + const [k, { method, section }] = allKnown[i]; + + if (hasResults && !exposed.includes(k) && k !== 'rpc_methods') { + delete (this._rpc as Record>)[section][method]; + delete (this._rx.rpc as Record>)[section][method]; + } + } } protected _decorateRpc (rpc: RpcCore & RpcInterface, decorateMethod: DecorateMethod, input: Partial> = {}): DecoratedRpc { - return rpc.sections.reduce((out, _sectionName): DecoratedRpc => { - const sectionName = _sectionName as keyof DecoratedRpc; + const out = input as DecoratedRpc; + + for (let s = 0; s < rpc.sections.length; s++) { + const sectionName = rpc.sections[s] as keyof DecoratedRpc; if (!(out as Record)[sectionName]) { - // out and section here are horrors to get right from a typing perspective :( - (out as Record)[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection => { + const section = {} as DecoratedRpcSection; + + for (const [methodName, method] of Object.entries(rpc[sectionName])) { // skip subscriptions where we have a non-subscribe interface if (this.hasSubscriptions || !(methodName.startsWith('subscribe') || methodName.startsWith('unsubscribe'))) { (section as Record)[methodName] = decorateMethod(method, { methodName }) as unknown; @@ -370,13 +380,13 @@ export abstract class Decorate extends Events { // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access (section as Record)[methodName].meta = method.meta; } + } - return section; - }, {} as DecoratedRpcSection); + (out as Record)[sectionName] = section; } + } - return out; - }, input as DecoratedRpc); + return out; } // only be called if supportMulti is true @@ -417,17 +427,19 @@ export abstract class Decorate extends Events { } protected _decorateExtrinsics ({ tx }: DecoratedMeta, decorateMethod: DecorateMethod): SubmittableExtrinsics { - const creator = createSubmittable(this._type, this._rx, decorateMethod); + const creator = createSubmittable(this._type, this._rx, decorateMethod) as SubmittableExtrinsics; - return Object.entries(tx).reduce((out, [name, section]): SubmittableExtrinsics => { - out[name] = Object.entries(section).reduce((out, [name, method]): SubmittableModuleExtrinsics => { - out[name] = this._decorateExtrinsicEntry(method, creator); + for (const [name, section] of Object.entries(tx)) { + const methods: SubmittableModuleExtrinsics = {}; - return out; - }, {} as SubmittableModuleExtrinsics); + for (const [name, method] of Object.entries(section)) { + methods[name] = this._decorateExtrinsicEntry(method, creator); + } - return out; - }, creator as SubmittableExtrinsics); + creator[name] = methods; + } + + return creator; } private _decorateExtrinsicEntry (method: CallFunction, creator: (value: Call | Uint8Array | string) => SubmittableExtrinsic): SubmittableExtrinsicFunction { @@ -443,27 +455,35 @@ export abstract class Decorate extends Events { } protected _decorateStorage ({ query }: DecoratedMeta, decorateMethod: DecorateMethod): QueryableStorage { - return Object.entries(query).reduce((out, [name, section]): QueryableStorage => { - out[name] = Object.entries(section).reduce((out, [name, method]): QueryableModuleStorage => { - out[name] = this._decorateStorageEntry(method, decorateMethod); + const result = {} as QueryableStorage; - return out; - }, {} as QueryableModuleStorage); + for (const [name, section] of Object.entries(query)) { + const methods: QueryableModuleStorage = {}; - return out; - }, {} as QueryableStorage); + for (const [name, method] of Object.entries(section)) { + methods[name] = this._decorateStorageEntry(method, decorateMethod); + } + + result[name] = methods; + } + + return result; } protected _decorateStorageAt ({ query, registry }: DecoratedMeta, decorateMethod: DecorateMethod, blockHash: Uint8Array): QueryableStorageAt { - return Object.entries(query).reduce((out, [name, section]): QueryableStorageAt => { - out[name] = Object.entries(section).reduce((out, [name, method]): QueryableModuleStorageAt => { - out[name] = this._decorateStorageEntryAt(registry, method, decorateMethod, blockHash); + const result = {} as QueryableStorageAt; - return out; - }, {} as QueryableModuleStorageAt); + for (const [name, section] of Object.entries(query)) { + const methods: QueryableModuleStorageAt = {}; - return out; - }, {} as QueryableStorageAt); + for (const [name, method] of Object.entries(section)) { + methods[name] = this._decorateStorageEntryAt(registry, method, decorateMethod, blockHash); + } + + result[name] = methods; + } + + return result; } private _decorateStorageEntry (creator: StorageEntry, decorateMethod: DecorateMethod): QueryableStorageEntry { @@ -652,10 +672,11 @@ export abstract class Decorate extends Events { const queryCall = at ? (startKey: string) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey, at) : (startKey: string) => this._rpcCore.state.getKeysPaged(headKey, PAGE_SIZE_K, startKey); + const setMeta = (key: StorageKey) => key.setMeta(meta, section, method); return startSubject.pipe( switchMap(queryCall), - map((keys) => keys.map((key) => key.setMeta(meta, section, method))), + map((keys) => keys.map(setMeta)), tap((keys): void => { setTimeout((): void => { keys.length === PAGE_SIZE_K @@ -672,9 +693,10 @@ export abstract class Decorate extends Events { assert(iterKey && meta.type.isMap, 'keys can only be retrieved on maps'); const headKey = iterKey(...opts.args).toHex(); + const setMeta = (key: StorageKey) => key.setMeta(meta, section, method); return this._rpcCore.state.getKeysPaged(headKey, opts.pageSize, opts.startKey || headKey).pipe( - map((keys) => keys.map((key) => key.setMeta(meta, section, method))) + map((keys) => keys.map(setMeta)) ); } diff --git a/packages/api/src/base/Init.ts b/packages/api/src/base/Init.ts index d93d9be8b5..8b61926899 100644 --- a/packages/api/src/base/Init.ts +++ b/packages/api/src/base/Init.ts @@ -23,6 +23,10 @@ const KEEPALIVE_INTERVAL = 10000; const l = logger('api/init'); +function textToString (t: Text): string { + return t.toString(); +} + export abstract class Init extends Decorate { #healthTimer: NodeJS.Timeout | null = null; @@ -242,17 +246,21 @@ export abstract class Init extends Decorate { this._runtimeChain = source.runtimeChain; this._runtimeVersion = source.runtimeVersion; - const methods: string[] = []; - // manually build a list of all available methods in this RPC, we are // going to filter on it to align the cloned RPC without making a call - Object.keys(source.rpc).forEach((section): void => { - Object.keys((source.rpc as Record>)[section]).forEach((method): void => { - methods.push(`${section}_${method}`); - }); - }); + const sections = Object.keys(source.rpc); + const rpcs: string[] = []; - this._filterRpc(methods, getSpecRpc(this.registry, source.runtimeChain, source.runtimeVersion.specName)); + for (let s = 0; s < sections.length; s++) { + const section = sections[s]; + const methods = Object.keys((source.rpc as Record>)[section]); + + for (let m = 0; m < methods.length; m++) { + rpcs.push(`${section}_${methods[m]}`); + } + } + + this._filterRpc(rpcs, getSpecRpc(this.registry, source.runtimeChain, source.runtimeVersion.specName)); return [source.genesisHash, source.runtimeMetadata]; } @@ -335,7 +343,7 @@ export abstract class Init extends Decorate { // initializes the registry & RPC this._initRegistry(this.registry, chain, runtimeVersion, metadata, chainProps); - this._filterRpc(rpcMethods.methods.map((t) => t.toString()), getSpecRpc(this.registry, chain, runtimeVersion.specName)); + this._filterRpc(rpcMethods.methods.map(textToString), getSpecRpc(this.registry, chain, runtimeVersion.specName)); this._subscribeUpdates(); // setup the initial registry, when we have none diff --git a/packages/api/src/util/augmentObject.ts b/packages/api/src/util/augmentObject.ts index 2486e6d831..01e12b2a9b 100644 --- a/packages/api/src/util/augmentObject.ts +++ b/packages/api/src/util/augmentObject.ts @@ -26,16 +26,17 @@ function warn (prefix: string, type: 'calls' | 'modules', [added, removed]: Stri } } -function extractKeys (src: Record>, dst: Record>): StringsStrings { - return [Object.keys(src), Object.keys(dst)]; +function findSectionExcludes (a: string[], b: string[]): string[] { + return a.filter((s) => !b.includes(s)); } -function findSectionExcludes (a: string[], b: string[]): string[] { - return a.filter((section) => !b.includes(section)); +function findSectionIncludes (a: string[], b: string[]): string[] { + return a.filter((s) => b.includes(s)); } function extractSections (src: Record>, dst: Record>): StringsStrings { - const [srcSections, dstSections] = extractKeys(src, dst); + const srcSections = Object.keys(src); + const dstSections = Object.keys(dst); return [ findSectionExcludes(srcSections, dstSections), @@ -45,20 +46,22 @@ function extractSections (src: Record>, dst: Record< function findMethodExcludes (src: Record>, dst: Record>): string[] { const srcSections = Object.keys(src); - const dstSections = Object.keys(dst); + const dstSections = findSectionIncludes(Object.keys(dst), srcSections); + const excludes: string[] = []; - return dstSections - .filter((section) => srcSections.includes(section)) - .reduce((rmMethods: string[], section): string[] => { - const srcMethods = Object.keys(src[section]); + for (let s = 0; s < dstSections.length; s++) { + const section = dstSections[s]; + const srcMethods = Object.keys(src[section]); + const dstMethods = Object.keys(dst[section]); - return rmMethods.concat( - ...Object - .keys(dst[section]) - .filter((method) => !srcMethods.includes(method)) - .map((method) => `${section}.${method}`) - ); - }, []); + excludes.push( + ...dstMethods + .filter((m) => !srcMethods.includes(m)) + .map((m) => `${section}.${m}`) + ); + } + + return excludes; } function extractMethods (src: Record>, dst: Record>): StringsStrings { @@ -75,9 +78,11 @@ function extractMethods (src: Record>, dst: Record>, dst: Record>, fromEmpty = false): Record> { if (fromEmpty) { - Object.keys(dst).forEach((key): void => { - delete dst[key]; - }); + const dstKeys = Object.keys(dst); + + for (let k = 0; k < dstKeys.length; k++) { + delete dst[dstKeys[k]]; + } } if (prefix && Object.keys(dst).length) { @@ -85,22 +90,26 @@ export function augmentObject (prefix: string | null, src: Record> => { - const section = src[sectionName]; + const srcKeys = Object.keys(src); - newSection[sectionName] = Object - .keys(section) - .reduce((result, methodName): Record => { - // TODO When it does match, check the actual details and warn when there are differences - if (!result[methodName]) { - result[methodName] = section[methodName]; - } + for (let s = 0; s < srcKeys.length; s++) { + const section = srcKeys[s]; - return result; - }, dst[sectionName] || {}); + if (!dst[section]) { + dst[section] = {}; + } - return newSection; - }, dst); + const methods = Object.keys(src[section]); + + for (let m = 0; m < methods.length; m++) { + const method = methods[m]; + + // TODO When it does match, check the actual details and warn when there are differences + if (!dst[section][method]) { + dst[section][method] = src[section][method]; + } + } + } + + return dst; } diff --git a/packages/api/src/util/decorate.ts b/packages/api/src/util/decorate.ts index 80a22f655f..d469335044 100644 --- a/packages/api/src/util/decorate.ts +++ b/packages/api/src/util/decorate.ts @@ -6,10 +6,16 @@ import type { ApiTypes, DecorateMethod, MethodResult } from '../types'; // Most generic typings for `api.derive.*.*` type AnyDerive = Record>; + // Exact typings for a particular section `api.derive.section.*` -type DeriveSection> = { [MethodName in keyof Section]: MethodResult }; +type DeriveSection> = { + [MethodName in keyof Section]: MethodResult +}; + // Exact typings for all sections `api.derive.*.*` -export type DeriveAllSections = { [SectionName in keyof AllSections]: DeriveSection }; +export type DeriveAllSections = { + [SectionName in keyof AllSections]: DeriveSection +}; // A technically unsafe version of Object.keys(obj) that assumes that // obj only has known properties of T @@ -21,38 +27,27 @@ function keys> (obj: T): (keyof T)[] { * This is a methods decorator which keeps all type information. */ function decorateMethods> (section: Section, decorateMethod: DecorateMethod): DeriveSection { - return keys(section).reduce( - ( - acc: DeriveSection, - methodName: MethodName - ): DeriveSection => { - const method = section[methodName]; + const result = {} as DeriveSection; + const names = keys(section); - // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment - acc[methodName] = decorateMethod(method) as MethodResult; + for (let k = 0; k < names.length; k++) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + result[names[k]] = decorateMethod(section[names[k]]); + } - return acc; - }, - {} as DeriveSection - ); + return result; } /** * This is a section decorator which keeps all type information. */ -export function decorateSections ( - allSections: AllSections, - decorateMethod: DecorateMethod -): DeriveAllSections { - return keys(allSections).reduce( - ( - acc: DeriveAllSections, - sectionName: MethodName - ): DeriveAllSections => { - acc[sectionName] = decorateMethods(allSections[sectionName], decorateMethod); +export function decorateSections (allSections: AllSections, decorateMethod: DecorateMethod): DeriveAllSections { + const result = {} as DeriveAllSections; + const names = keys(allSections); - return acc; - }, - {} as DeriveAllSections - ); + for (let k = 0; k < names.length; k++) { + result[names[k]] = decorateMethods(allSections[names[k]], decorateMethod); + } + + return result; }