Internal adjustments (part 6) (#4138)

* Internal adjustments (part 6)

* Additional
This commit is contained in:
Jaco
2021-11-01 11:35:17 +02:00
committed by GitHub
parent 0cbca0adb8
commit 761ceaeaa3
4 changed files with 147 additions and 113 deletions
+63 -41
View File
@@ -322,7 +322,11 @@ export abstract class Decorate<ApiType extends ApiTypes> 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<ApiType extends ApiTypes> 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<ApiType extends ApiTypes> 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<string, Record<string, unknown>>)[section][method];
delete (this._rx.rpc as Record<string, Record<string, unknown>>)[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<string, Record<string, unknown>>)[section][method];
delete (this._rx.rpc as Record<string, Record<string, unknown>>)[section][method];
}
}
}
protected _decorateRpc<ApiType extends ApiTypes> (rpc: RpcCore & RpcInterface, decorateMethod: DecorateMethod<ApiType>, input: Partial<DecoratedRpc<ApiType, RpcInterface>> = {}): DecoratedRpc<ApiType, RpcInterface> {
return rpc.sections.reduce((out, _sectionName): DecoratedRpc<ApiType, RpcInterface> => {
const sectionName = _sectionName as keyof DecoratedRpc<ApiType, RpcInterface>;
const out = input as DecoratedRpc<ApiType, RpcInterface>;
for (let s = 0; s < rpc.sections.length; s++) {
const sectionName = rpc.sections[s] as keyof DecoratedRpc<ApiType, RpcInterface>;
if (!(out as Record<string, unknown>)[sectionName]) {
// out and section here are horrors to get right from a typing perspective :(
(out as Record<string, unknown>)[sectionName] = Object.entries(rpc[sectionName]).reduce((section, [methodName, method]): DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]> => {
const section = {} as DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]>;
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<string, unknown>)[methodName] = decorateMethod(method, { methodName }) as unknown;
@@ -370,13 +380,13 @@ export abstract class Decorate<ApiType extends ApiTypes> extends Events {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
(section as Record<string, { meta: unknown }>)[methodName].meta = method.meta;
}
}
return section;
}, {} as DecoratedRpcSection<ApiType, RpcInterface[typeof sectionName]>);
(out as Record<string, unknown>)[sectionName] = section;
}
}
return out;
}, input as DecoratedRpc<ApiType, RpcInterface>);
return out;
}
// only be called if supportMulti is true
@@ -417,17 +427,19 @@ export abstract class Decorate<ApiType extends ApiTypes> extends Events {
}
protected _decorateExtrinsics<ApiType extends ApiTypes> ({ tx }: DecoratedMeta, decorateMethod: DecorateMethod<ApiType>): SubmittableExtrinsics<ApiType> {
const creator = createSubmittable(this._type, this._rx, decorateMethod);
const creator = createSubmittable(this._type, this._rx, decorateMethod) as SubmittableExtrinsics<ApiType>;
return Object.entries(tx).reduce((out, [name, section]): SubmittableExtrinsics<ApiType> => {
out[name] = Object.entries(section).reduce((out, [name, method]): SubmittableModuleExtrinsics<ApiType> => {
out[name] = this._decorateExtrinsicEntry(method, creator);
for (const [name, section] of Object.entries(tx)) {
const methods: SubmittableModuleExtrinsics<ApiType> = {};
return out;
}, {} as SubmittableModuleExtrinsics<ApiType>);
for (const [name, method] of Object.entries(section)) {
methods[name] = this._decorateExtrinsicEntry(method, creator);
}
return out;
}, creator as SubmittableExtrinsics<ApiType>);
creator[name] = methods;
}
return creator;
}
private _decorateExtrinsicEntry<ApiType extends ApiTypes> (method: CallFunction, creator: (value: Call | Uint8Array | string) => SubmittableExtrinsic<ApiType>): SubmittableExtrinsicFunction<ApiType> {
@@ -443,27 +455,35 @@ export abstract class Decorate<ApiType extends ApiTypes> extends Events {
}
protected _decorateStorage<ApiType extends ApiTypes> ({ query }: DecoratedMeta, decorateMethod: DecorateMethod<ApiType>): QueryableStorage<ApiType> {
return Object.entries(query).reduce((out, [name, section]): QueryableStorage<ApiType> => {
out[name] = Object.entries(section).reduce((out, [name, method]): QueryableModuleStorage<ApiType> => {
out[name] = this._decorateStorageEntry(method, decorateMethod);
const result = {} as QueryableStorage<ApiType>;
return out;
}, {} as QueryableModuleStorage<ApiType>);
for (const [name, section] of Object.entries(query)) {
const methods: QueryableModuleStorage<ApiType> = {};
return out;
}, {} as QueryableStorage<ApiType>);
for (const [name, method] of Object.entries(section)) {
methods[name] = this._decorateStorageEntry(method, decorateMethod);
}
result[name] = methods;
}
return result;
}
protected _decorateStorageAt<ApiType extends ApiTypes> ({ query, registry }: DecoratedMeta, decorateMethod: DecorateMethod<ApiType>, blockHash: Uint8Array): QueryableStorageAt<ApiType> {
return Object.entries(query).reduce((out, [name, section]): QueryableStorageAt<ApiType> => {
out[name] = Object.entries(section).reduce((out, [name, method]): QueryableModuleStorageAt<ApiType> => {
out[name] = this._decorateStorageEntryAt(registry, method, decorateMethod, blockHash);
const result = {} as QueryableStorageAt<ApiType>;
return out;
}, {} as QueryableModuleStorageAt<ApiType>);
for (const [name, section] of Object.entries(query)) {
const methods: QueryableModuleStorageAt<ApiType> = {};
return out;
}, {} as QueryableStorageAt<ApiType>);
for (const [name, method] of Object.entries(section)) {
methods[name] = this._decorateStorageEntryAt(registry, method, decorateMethod, blockHash);
}
result[name] = methods;
}
return result;
}
private _decorateStorageEntry<ApiType extends ApiTypes> (creator: StorageEntry, decorateMethod: DecorateMethod<ApiType>): QueryableStorageEntry<ApiType> {
@@ -652,10 +672,11 @@ export abstract class Decorate<ApiType extends ApiTypes> 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<ApiType extends ApiTypes> 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))
);
}
+17 -9
View File
@@ -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<ApiType extends ApiTypes> extends Decorate<ApiType> {
#healthTimer: NodeJS.Timeout | null = null;
@@ -242,17 +246,21 @@ export abstract class Init<ApiType extends ApiTypes> extends Decorate<ApiType> {
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<string, Record<string, unknown>>)[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<string, Record<string, unknown>>)[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<ApiType extends ApiTypes> extends Decorate<ApiType> {
// 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
+44 -35
View File
@@ -26,16 +26,17 @@ function warn (prefix: string, type: 'calls' | 'modules', [added, removed]: Stri
}
}
function extractKeys (src: Record<string, Record<string, any>>, dst: Record<string, Record<string, any>>): 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<string, Record<string, any>>, dst: Record<string, Record<string, any>>): 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<string, Record<string, any>>, dst: Record<
function findMethodExcludes (src: Record<string, Record<string, any>>, dst: Record<string, Record<string, any>>): 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<string, Record<string, any>>, dst: Record<string, Record<string, any>>): StringsStrings {
@@ -75,9 +78,11 @@ function extractMethods (src: Record<string, Record<string, any>>, dst: Record<s
*/
export function augmentObject (prefix: string | null, src: Record<string, Record<string, unknown>>, dst: Record<string, Record<string, unknown>>, fromEmpty = false): Record<string, Record<string, any>> {
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<string, Record
warn(prefix, 'calls', extractMethods(src, dst));
}
return Object
.keys(src)
.reduce((newSection, sectionName): Record<string, Record<string, unknown>> => {
const section = src[sectionName];
const srcKeys = Object.keys(src);
newSection[sectionName] = Object
.keys(section)
.reduce((result, methodName): Record<string, unknown> => {
// 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;
}
+23 -28
View File
@@ -6,10 +6,16 @@ import type { ApiTypes, DecorateMethod, MethodResult } from '../types';
// Most generic typings for `api.derive.*.*`
type AnyDerive = Record<string, Record<string, AnyFunction>>;
// Exact typings for a particular section `api.derive.section.*`
type DeriveSection<ApiType extends ApiTypes, Section extends Record<string, AnyFunction>> = { [MethodName in keyof Section]: MethodResult<ApiType, Section[MethodName]> };
type DeriveSection<ApiType extends ApiTypes, Section extends Record<string, AnyFunction>> = {
[MethodName in keyof Section]: MethodResult<ApiType, Section[MethodName]>
};
// Exact typings for all sections `api.derive.*.*`
export type DeriveAllSections<ApiType extends ApiTypes, AllSections extends AnyDerive> = { [SectionName in keyof AllSections]: DeriveSection<ApiType, AllSections[SectionName]> };
export type DeriveAllSections<ApiType extends ApiTypes, AllSections extends AnyDerive> = {
[SectionName in keyof AllSections]: DeriveSection<ApiType, AllSections[SectionName]>
};
// A technically unsafe version of Object.keys(obj) that assumes that
// obj only has known properties of T
@@ -21,38 +27,27 @@ function keys<T extends Record<string, unknown>> (obj: T): (keyof T)[] {
* This is a methods decorator which keeps all type information.
*/
function decorateMethods<ApiType extends ApiTypes, Section extends Record<string, AnyFunction>> (section: Section, decorateMethod: DecorateMethod<ApiType>): DeriveSection<ApiType, Section> {
return keys(section).reduce(
<MethodName extends keyof Section>(
acc: DeriveSection<ApiType, Section>,
methodName: MethodName
): DeriveSection<ApiType, Section> => {
const method = section[methodName];
const result = {} as DeriveSection<ApiType, Section>;
const names = keys(section);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
acc[methodName] = decorateMethod(method) as MethodResult<ApiType, Section[MethodName]>;
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<ApiType, Section>
);
return result;
}
/**
* This is a section decorator which keeps all type information.
*/
export function decorateSections<ApiType extends ApiTypes, AllSections extends AnyDerive> (
allSections: AllSections,
decorateMethod: DecorateMethod<ApiType>
): DeriveAllSections<ApiType, AllSections> {
return keys(allSections).reduce(
<MethodName extends keyof AllSections>(
acc: DeriveAllSections<ApiType, AllSections>,
sectionName: MethodName
): DeriveAllSections<ApiType, AllSections> => {
acc[sectionName] = decorateMethods(allSections[sectionName], decorateMethod);
export function decorateSections<ApiType extends ApiTypes, AllSections extends AnyDerive> (allSections: AllSections, decorateMethod: DecorateMethod<ApiType>): DeriveAllSections<ApiType, AllSections> {
const result = {} as DeriveAllSections<ApiType, AllSections>;
const names = keys(allSections);
return acc;
},
{} as DeriveAllSections<ApiType, AllSections>
);
for (let k = 0; k < names.length; k++) {
result[names[k]] = decorateMethods(allSections[names[k]], decorateMethod);
}
return result;
}