Cater for named event fields (#4915)

* Cater for named event fields

* Adjust name extraction

* Rebuild with named events

* Empty object

* Adjust

* Additional

* Adjust
This commit is contained in:
Jaco
2022-06-09 13:44:31 +03:00
committed by GitHub
parent 322ac0861d
commit 7461527448
14 changed files with 824 additions and 766 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+3 -3
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { IsEvent } from '@polkadot/types/metadata/decorate/types';
import type { AnyTuple } from '@polkadot/types/types';
import type { AnyTuple, Codec } from '@polkadot/types/types';
import type { ApiTypes } from './base';
// eslint-disable-next-line @typescript-eslint/no-unused-vars,@typescript-eslint/no-empty-interface
@@ -16,8 +16,8 @@ export interface DecoratedEvents<ApiType extends ApiTypes> extends AugmentedEven
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export type AugmentedEvent<ApiType extends ApiTypes, T extends AnyTuple = AnyTuple> = IsEvent<T>;
export type AugmentedEvent<ApiType extends ApiTypes, T extends AnyTuple = AnyTuple, N extends Record<string, Codec> = Record<string, Codec>> = IsEvent<T, N>;
export interface ModuleEvents<ApiType extends ApiTypes> {
[key: string]: AugmentedEvent<ApiType, AnyTuple>;
[key: string]: AugmentedEvent<ApiType>;
}
+5 -2
View File
@@ -57,9 +57,12 @@ function events (api: ApiPromise): void {
// existing
if (api.events.balances.Transfer.is(event)) {
// the types are correctly expanded
const [from, to, amount] = event.data;
const [afrom, ato, aamount] = event.data;
console.log(from.toHuman(), to.toHuman(), amount.toBn());
// the types have getters
const { amount, from, to } = event.data;
console.log(afrom.toHuman(), ato.toHuman(), aamount.toBn(), from.toHuman(), to.toHuman(), amount.toBn());
}
// something random
+7 -1
View File
@@ -42,13 +42,19 @@ function generateForMeta (meta: Metadata, dest: string, extraTypes: ExtraTypes,
const args = fields
.map(({ type }) => lookup.getTypeDef(type))
.map((typeDef) => typeDef.lookupName || formatType(registry, allDefs, typeDef, imports));
const names = fields
.map(({ name }) => registry.lookup.sanitizeField(name)[0])
.filter((n): n is string => !!n);
setImports(allDefs, imports, args);
return {
docs,
fields: names.length === args.length
? `{ ${names.map((n, i) => `${n}: ${args[i]}`).join(', ')} }`
: '{}',
name: name.toString(),
type: args.join(', ')
type: `[${args.join(', ')}]`
};
})
.sort(compareName),
+1 -1
View File
@@ -8,7 +8,7 @@ declare module '@polkadot/api-base/types/events' {
{{{name}}}: {
{{#each items}}
{{> docs}}
{{{name}}}: AugmentedEvent<ApiType, [{{{type}}}]>;
{{{name}}}: AugmentedEvent<ApiType, {{{type}}}, {{{fields}}}>;
{{/each}}
{{#unless @root.isStrict}}
/**
@@ -1,6 +1,7 @@
// Copyright 2017-2022 @polkadot/types-create authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Option, Text } from '@polkadot/types-codec';
import type { ICompact, INumber } from '@polkadot/types-codec/types';
import type { TypeDef } from './types';
@@ -11,4 +12,5 @@ export interface ILookup {
}
},
getTypeDef (lookupId: ICompact<INumber> | string | number): TypeDef;
sanitizeField (name: Option<Text>): [string | null, string | null];
}
+39 -3
View File
@@ -8,7 +8,7 @@ import type { EventId } from '../interfaces/system';
import type { IEvent, IEventData, InterfaceTypes, Registry } from '../types';
import { Null, Struct, Tuple } from '@polkadot/types-codec';
import { objectSpread } from '@polkadot/util';
import { objectProperties, objectSpread } from '@polkadot/util';
interface Decoded {
DataType: CodecClass<Null> | CodecClass<GenericEventData>;
@@ -45,6 +45,8 @@ export class GenericEventData extends Tuple implements IEventData {
readonly #method: string;
readonly #names: string[] | null = null;
readonly #section: string;
readonly #typeDef: TypeDef[];
@@ -58,6 +60,16 @@ export class GenericEventData extends Tuple implements IEventData {
this.#method = method;
this.#section = section;
this.#typeDef = fields.map(({ type }) => registry.lookup.getTypeDef(type));
const names = fields
.map(({ name }) => registry.lookup.sanitizeField(name)[0])
.filter((n): n is string => !!n);
if (names.length === fields.length) {
this.#names = names;
objectProperties(this, names, (_, i) => this[i]);
}
}
/**
@@ -74,6 +86,13 @@ export class GenericEventData extends Tuple implements IEventData {
return this.#method;
}
/**
* @description The field names (as available)
*/
public get names (): string[] | null {
return this.#names;
}
/**
* @description The section as a string
*/
@@ -87,6 +106,23 @@ export class GenericEventData extends Tuple implements IEventData {
public get typeDef (): TypeDef[] {
return this.#typeDef;
}
/**
* @description Converts the Object to to a human-friendly JSON, with additional fields, expansion and formatting of information
*/
public override toHuman (isExtended?: boolean): AnyJson {
if (this.#names !== null) {
const json: Record<string, AnyJson> = {};
for (let i = 0; i < this.#names.length; i++) {
json[this.#names[i]] = this[i].toHuman(isExtended);
}
return json;
}
return super.toHuman(isExtended);
}
}
/**
@@ -95,7 +131,7 @@ export class GenericEventData extends Tuple implements IEventData {
* A representation of a system event. These are generated via the [[Metadata]] interfaces and
* specific to a specific Substrate runtime
*/
export class GenericEvent extends Struct implements IEvent<Codec[]> {
export class GenericEvent extends Struct implements IEvent<Codec[], Record<string, Codec>> {
// Currently we _only_ decode from Uint8Array, since we expect it to
// be used via EventRecord
constructor (registry: Registry, _value?: Uint8Array) {
@@ -111,7 +147,7 @@ export class GenericEvent extends Struct implements IEvent<Codec[]> {
/**
* @description The wrapped [[EventData]]
*/
public get data (): GenericEventData {
public get data (): GenericEventData & Record<string, Codec> {
return this.getT('data');
}
@@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { Text, Type, Vec } from '@polkadot/types-codec';
import type { Option, Text, Type, Vec } from '@polkadot/types-codec';
import type { Registry } from '@polkadot/types-codec/types';
import type { ILookup, TypeDef } from '@polkadot/types-create/types';
import type { PortableType } from '../../interfaces/metadata';
@@ -464,6 +464,25 @@ export class PortableRegistry extends Struct implements ILookup {
return this.#typeDefs[lookupIndex];
}
public sanitizeField (name: Option<Text>): [string | null, string | null] {
let nameField: string | null = null;
let nameOrig: string | null = null;
if (name.isSome) {
nameField = stringCamelCase(name.unwrap());
if (nameField.includes('#')) {
nameOrig = nameField;
nameField = nameOrig.replace(/#/g, '_');
} else if (RESERVED.includes(nameField)) {
nameOrig = nameField;
nameField = `${nameField}_`;
}
}
return [nameField, nameOrig];
}
#createSiDef (lookupId: SiLookupTypeId): TypeDef {
const typeDef = this.getTypeDef(lookupId);
const lookupIndex = lookupId.toNumber();
@@ -681,18 +700,9 @@ export class PortableRegistry extends Struct implements ILookup {
if (name.isNone) {
sub[i] = typeDef;
} else {
let nameField = stringCamelCase(name.unwrap());
let nameOrig: string | null = null;
const [nameField, nameOrig] = this.sanitizeField(name);
if (nameField.includes('#')) {
nameOrig = nameField;
nameField = nameOrig.replace(/#/g, '_');
} else if (RESERVED.includes(nameField)) {
nameOrig = nameField;
nameField = `${nameField}_`;
}
if (nameOrig) {
if (nameField && nameOrig) {
alias.set(nameField, nameOrig);
}
@@ -1,7 +1,7 @@
// Copyright 2017-2022 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { AnyTuple, Registry } from '@polkadot/types-codec/types';
import type { AnyTuple, Codec, Registry } from '@polkadot/types-codec/types';
import type { MetadataLatest, PalletMetadataLatest, SiVariant } from '../../../interfaces';
import type { IEvent } from '../../../types';
import type { Events, IsEvent } from '../types';
@@ -26,9 +26,9 @@ export function decorateEvents (registry: Registry, { lookup, pallets }: Metadat
const sectionIndex = version >= 12 ? index.toNumber() : i;
lazyMethod(result, stringCamelCase(name), () =>
lazyVariants(lookup, events.unwrap(), objectNameToString, (variant: SiVariant): IsEvent<AnyTuple> => ({
lazyVariants(lookup, events.unwrap(), objectNameToString, (variant: SiVariant): IsEvent<AnyTuple, Record<string, Codec>> => ({
// We sprinkle in isCodec & isU8a to ensure we are dealing with the correct objects
is: <T extends AnyTuple> (eventRecord: IEvent<AnyTuple>): eventRecord is IEvent<T> =>
is: <T extends AnyTuple, N extends Record<string, Codec>> (eventRecord: IEvent<AnyTuple, Record<string, Codec>>): eventRecord is IEvent<T, N> =>
isCodec(eventRecord) &&
isU8a(eventRecord.index) &&
sectionIndex === eventRecord.index[0] &&
@@ -16,17 +16,17 @@ export interface IsError {
is: (moduleError: DispatchErrorModule | DispatchErrorModuleU8 | DispatchErrorModuleU8a) => boolean;
}
export interface IsEvent <T extends AnyTuple> {
export interface IsEvent <T extends AnyTuple, N extends Record<string, Codec>> {
readonly meta: EventMetadataLatest;
is: (event: IEvent<AnyTuple>) => event is IEvent<T>;
is: (event: IEvent<AnyTuple, Record<string, Codec>>) => event is IEvent<T, N>;
}
export type ModuleConstants = Record<string, ConstantCodec>;
export type ModuleErrors = Record<string, IsError>;
export type ModuleEvents = Record<string, IsEvent<AnyTuple>>;
export type ModuleEvents = Record<string, IsEvent<AnyTuple, Record<string, Codec>>>;
export type ModuleExtrinsics = Record<string, CallFunction>;
+4 -4
View File
@@ -6,9 +6,9 @@ import type { EventMetadataLatest } from '../interfaces/metadata';
import type { Hash } from '../interfaces/runtime';
import type { EventId, Phase } from '../interfaces/system';
export interface IEventRecord<T extends Codec[]> {
export interface IEventRecord<T extends Codec[], N extends Record<string, Codec> = Record<string, Codec>> {
readonly phase: Phase;
readonly event: IEvent<T>;
readonly event: IEvent<T, N>;
readonly topics: Hash[];
}
@@ -18,8 +18,8 @@ export interface IEventData {
readonly section: string;
}
export interface IEvent<T extends Codec[]> {
readonly data: ITuple<T> & IEventData;
export interface IEvent<T extends Codec[], N extends Record<string, Codec> = Record<string, Codec>> {
readonly data: ITuple<T> & IEventData & N;
readonly index: EventId;
readonly method: string;
readonly section: string;