Add Option.unwrapOrDefault (#1942)

* Add Option.unwrapOrDefault

* linting

* metadata createType(registry, ...) -> registry.createType(...)

* Linting

* Cleanup tests
This commit is contained in:
Jaco Greeff
2020-02-25 14:15:25 +01:00
committed by GitHub
parent 71e82aece8
commit c3c77ffe43
16 changed files with 106 additions and 112 deletions
@@ -5,11 +5,9 @@
import { MetadataLatest } from '@polkadot/types/interfaces/metadata';
import { AnyJson, Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toCallsOnly (registry: Registry, { extrinsic, modules }: MetadataLatest): AnyJson {
return createType(registry, 'MetadataLatest', {
return registry.createType('MetadataLatest', {
// FIXME, this needs typing, not any
modules: modules.map(({ calls, name }): any => ({
name,
+9 -13
View File
@@ -6,17 +6,15 @@
import { EventMetadataV1, FunctionMetadataV1, MetadataV0, MetadataV1, ModuleMetadataV1, RuntimeModuleMetadataV0, StorageFunctionMetadataV1 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { stringUpperFirst } from '@polkadot/util';
import { createType } from '@polkadot/types/create';
import { Option, Vec } from '@polkadot/types/codec';
import { Option } from '@polkadot/types/codec';
import { Text } from '@polkadot/types/primitive';
import { stringUpperFirst } from '@polkadot/util';
/** @internal */
function toV1Calls (registry: Registry, { module: { call: { functions } } }: RuntimeModuleMetadataV0): Option<FunctionMetadataV1> {
return functions.length
? new Option(registry, Vec.with('FunctionMetadataV1'), functions)
: new Option(registry, Vec.with('FunctionMetadataV1'));
? new Option(registry, 'Vec<FunctionMetadataV1>', functions)
: new Option(registry, 'Vec<FunctionMetadataV1>');
}
/** @internal */
@@ -24,27 +22,25 @@ function toV1Events (registry: Registry, metadataV0: MetadataV0, prefix: Text):
const events = metadataV0.outerEvent.events.find((event): boolean => event[0].eq(prefix));
return events
? new Option(registry, Vec.with('EventMetadataV1'), events[1])
: new Option(registry, Vec.with('EventMetadataV1'));
? new Option(registry, 'Vec<EventMetadataV1>', events[1])
: new Option(registry, 'Vec<EventMetadataV1>');
}
/** @internal */
function toV1Storage (registry: Registry, { storage }: RuntimeModuleMetadataV0): Option<StorageFunctionMetadataV1> {
return storage.isSome
? new Option(registry, 'Vec<StorageFunctionMetadataV1>', storage.unwrap().functions)
: new Option(registry, 'Vec<StorageFunctionMetadataV1>');
return new Option(registry, 'Vec<StorageFunctionMetadataV1>', storage.unwrapOr(undefined)?.functions);
}
/** @internal */
export default function toV1 (registry: Registry, metadataV0: MetadataV0): MetadataV1 {
return createType(registry, 'MetadataV1', {
return registry.createType('MetadataV1', {
modules: metadataV0.modules.map((mod): ModuleMetadataV1 => {
// The prefix of this module (capitalized)
const prefix = mod.storage.isSome
? mod.storage.unwrap().prefix.toString()
: stringUpperFirst(mod.prefix.toString()); // If this module doesn't have storage, we just assume the prefix is the name capitalized
return createType(registry, 'ModuleMetadataV1', {
return registry.createType('ModuleMetadataV1', {
name: mod.prefix, // Not capitalized
prefix, // Capitalized
storage: toV1Storage(registry, mod),
+1 -3
View File
@@ -5,9 +5,7 @@
import { MetadataV1, MetadataV2 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV2 (registry: Registry, metadataV1: MetadataV1): MetadataV2 {
return createType(registry, 'MetadataV2', metadataV1);
return registry.createType('MetadataV2', metadataV1);
}
+1 -3
View File
@@ -5,11 +5,9 @@
import { MetadataV10, MetadataV11 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV11 (registry: Registry, { modules }: MetadataV10): MetadataV11 {
return createType(registry, 'MetadataV11', {
return registry.createType('MetadataV11', {
modules,
// This is new in V11, pass V0 here - something non-existing, telling the API to use
// the fallback for this information (on-chain detection)
@@ -7,7 +7,6 @@ import { FunctionMetadataV11, FunctionMetadataLatest, MetadataV11, MetadataLates
import { Registry } from '@polkadot/types/types';
import { getModuleTypes } from '@polkadot/types/known';
import { createType } from '@polkadot/types/create';
import { Type } from '@polkadot/types/primitive';
import { stringCamelCase } from '@polkadot/util';
@@ -33,7 +32,7 @@ function convertCalls (registry: Registry, calls: FunctionMetadataV11[], section
return calls.map(({ args, documentation, name }): FunctionMetadataLatest => {
args.forEach(({ type }): void => setTypeOverride(sectionTypes, type));
return createType(registry, 'FunctionMetadataLatest', { args, name, documentation });
return registry.createType('FunctionMetadataLatest', { args, name, documentation });
});
}
@@ -42,7 +41,7 @@ function convertCalls (registry: Registry, calls: FunctionMetadataV11[], section
* @internal
**/
function convertStorage (registry: Registry, { items, prefix }: StorageMetadataV11, sectionTypes: OverrideModuleType): StorageMetadataLatest {
return createType(registry, 'StorageMetadataLatest', {
return registry.createType('StorageMetadataLatest', {
items: items.map(({ documentation, fallback, modifier, name, type }): StorageEntryMetadataLatest => {
let resultType: Type;
@@ -56,7 +55,7 @@ function convertStorage (registry: Registry, { items, prefix }: StorageMetadataV
setTypeOverride(sectionTypes, resultType);
return createType(registry, 'StorageEntryMetadataLatest', { documentation, fallback, modifier, name, type });
return registry.createType('StorageEntryMetadataLatest', { documentation, fallback, modifier, name, type });
}),
prefix
});
@@ -68,13 +67,13 @@ function convertStorage (registry: Registry, { items, prefix }: StorageMetadataV
* @internal
**/
export default function toLatest (registry: Registry, { modules, extrinsic }: MetadataV11): MetadataLatest {
return createType(registry, 'MetadataLatest', {
return registry.createType('MetadataLatest', {
modules: modules.map((mod): ModuleMetadataLatest => {
const calls = mod.calls.unwrapOr(null);
const storage = mod.storage.unwrapOr(null);
const sectionTypes = getModuleTypes(stringCamelCase(mod.name.toString()));
return createType(registry, 'ModuleMetadataLatest', {
return registry.createType('ModuleMetadataLatest', {
...mod,
calls: calls
? convertCalls(registry, calls, sectionTypes)
+1 -3
View File
@@ -5,9 +5,7 @@
import { MetadataV2, MetadataV3 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV3 (registry: Registry, metadataV2: MetadataV2): MetadataV3 {
return createType(registry, 'MetadataV3', metadataV2);
return registry.createType('MetadataV3', metadataV2);
}
+8 -9
View File
@@ -5,7 +5,6 @@
import { MetadataV3, MetadataV4, ModuleMetadataV4, StorageFunctionMetadataV3, StorageFunctionMetadataV4 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
import { Option } from '@polkadot/types/codec';
/** @internal */
@@ -18,34 +17,34 @@ function toV4StorageFunction (registry: Registry, storageFn: StorageFunctionMeta
const [newType, index] = type.isPlain
? [type.asPlain, 0]
: type.isMap
? [createType(registry, 'MapTypeV4', {
hasher: createType(registry, 'StorageHasherV4', 'Twox128'),
? [registry.createType('MapTypeV4', {
hasher: registry.createType('StorageHasherV4', 'Twox128'),
key: type.asMap.key,
value: type.asMap.value,
linked: type.asMap.linked
}), 1]
: [createType(registry, 'DoubleMapTypeV4', {
hasher: createType(registry, 'StorageHasherV4', 'Twox128'),
: [registry.createType('DoubleMapTypeV4', {
hasher: registry.createType('StorageHasherV4', 'Twox128'),
key1: type.asDoubleMap.key1,
key2: type.asDoubleMap.key2,
value: type.asDoubleMap.value,
key2Hasher: type.asDoubleMap.key2Hasher
}), 2];
return createType(registry, 'StorageFunctionMetadataV4', {
return registry.createType('StorageFunctionMetadataV4', {
documentation,
fallback,
name,
modifier,
type: createType(registry, 'StorageFunctionTypeV4', newType, index)
type: registry.createType('StorageFunctionTypeV4', newType, index)
});
}
/** @internal */
export default function toV4 (registry: Registry, { modules }: MetadataV3): MetadataV4 {
return createType(registry, 'MetadataV4', {
return registry.createType('MetadataV4', {
modules: modules.map(({ calls, events, name, prefix, storage }): ModuleMetadataV4 =>
createType(registry, 'ModuleMetadataV4', {
registry.createType('ModuleMetadataV4', {
calls,
events,
name,
+7 -9
View File
@@ -5,11 +5,9 @@
import { MetadataV4, MetadataV5, ModuleMetadataV5, StorageFunctionMetadataV4, StorageFunctionMetadataV5, StorageHasherV5 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { assert } from '@polkadot/util';
import { Option } from '@polkadot/types/codec';
import { createType } from '@polkadot/types/create';
import Text from '@polkadot/types/primitive/Text';
import { assert } from '@polkadot/util';
const hasherMap: Map<string, string> = new Map([
['blake2_128', 'Blake2_128'],
@@ -25,7 +23,7 @@ function toStorageHasher (registry: Registry, text: Text): StorageHasherV5 {
assert(mapped, `Invalid Storage hasher: ${text.toString()}`);
return createType(registry, 'StorageHasherV5', mapped);
return registry.createType('StorageHasherV5', mapped);
}
/** @internal */
@@ -35,7 +33,7 @@ function toV5StorageFunction (registry: Registry, storageFn: StorageFunctionMeta
? [type.asPlain, 0]
: type.isMap
? [type.asMap, 1]
: [createType(registry, 'DoubleMapTypeV5', {
: [registry.createType('DoubleMapTypeV5', {
hasher: type.asDoubleMap.hasher,
key1: type.asDoubleMap.key1,
key2: type.asDoubleMap.key2,
@@ -43,20 +41,20 @@ function toV5StorageFunction (registry: Registry, storageFn: StorageFunctionMeta
key2Hasher: toStorageHasher(registry, type.asDoubleMap.key2Hasher)
}), 2];
return createType(registry, 'StorageFunctionMetadataV5', {
return registry.createType('StorageFunctionMetadataV5', {
documentation,
fallback,
name,
modifier,
type: createType(registry, 'StorageFunctionTypeV5', newType, index)
type: registry.createType('StorageFunctionTypeV5', newType, index)
});
}
/** @internal */
export default function toV5 (registry: Registry, { modules }: MetadataV4): MetadataV5 {
return createType(registry, 'MetadataV5', {
return registry.createType('MetadataV5', {
modules: modules.map(({ calls, events, name, prefix, storage }): ModuleMetadataV5 =>
createType(registry, 'ModuleMetadataV5', {
registry.createType('ModuleMetadataV5', {
name,
prefix,
storage: storage.isSome
+2 -4
View File
@@ -5,13 +5,11 @@
import { MetadataV5, MetadataV6, ModuleMetadataV6 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV6 (registry: Registry, { modules }: MetadataV5): MetadataV6 {
return createType(registry, 'MetadataV6', {
return registry.createType('MetadataV6', {
modules: modules.map(({ calls, events, name, prefix, storage }): ModuleMetadataV6 =>
createType(registry, 'ModuleMetadataV6', {
registry.createType('ModuleMetadataV6', {
calls,
constants: [],
events,
+3 -5
View File
@@ -5,19 +5,17 @@
import { MetadataV6, MetadataV7, ModuleMetadataV7 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV7 (registry: Registry, { modules }: MetadataV6): MetadataV7 {
return createType(registry, 'MetadataV7', {
return registry.createType('MetadataV7', {
modules: modules.map(({ calls, constants, events, name, prefix, storage }): ModuleMetadataV7 =>
createType(registry, 'ModuleMetadataV7', {
registry.createType('ModuleMetadataV7', {
calls,
constants,
events,
name,
storage: storage.isSome
? createType(registry, 'StorageMetadataV7', { prefix, items: storage.unwrap() })
? registry.createType('StorageMetadataV7', { prefix, items: storage.unwrap() })
: null
})
)
+2 -4
View File
@@ -5,13 +5,11 @@
import { MetadataV7, MetadataV8, ModuleMetadataV8 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV8 (registry: Registry, { modules }: MetadataV7): MetadataV8 {
return createType(registry, 'MetadataV8', {
return registry.createType('MetadataV8', {
modules: modules.map(({ calls, constants, events, name, storage }): ModuleMetadataV8 =>
createType(registry, 'ModuleMetadataV8', {
registry.createType('ModuleMetadataV8', {
calls,
constants,
errors: [],
+1 -3
View File
@@ -5,9 +5,7 @@
import { MetadataV8, MetadataV9 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
/** @internal */
export default function toV9 (registry: Registry, { modules }: MetadataV8): MetadataV9 {
return createType(registry, 'MetadataV9', { modules });
return registry.createType('MetadataV9', { modules });
}
+5 -7
View File
@@ -5,8 +5,6 @@
import { MetadataV9, MetadataV10, ModuleMetadataV9, ModuleMetadataV10, StorageEntryMetadataV9, StorageEntryTypeV9, StorageHasherV9, StorageHasherV10 } from '@polkadot/types/interfaces/metadata';
import { Registry } from '@polkadot/types/types';
import { createType } from '@polkadot/types/create';
// migrate a storage hasher type
// see https://github.com/paritytech/substrate/pull/4462
/** @internal */
@@ -14,10 +12,10 @@ function createStorageHasher (registry: Registry, hasher: StorageHasherV9): Stor
// Blake2_128_Concat has been added at index 2, so we increment all the
// indexes greater than 2
if (hasher.toNumber() >= 2) {
return createType(registry, 'StorageHasherV10', hasher.toNumber() + 1);
return registry.createType('StorageHasherV10', hasher.toNumber() + 1);
}
return createType(registry, 'StorageHasherV10', hasher);
return registry.createType('StorageHasherV10', hasher);
}
/** @internal */
@@ -44,14 +42,14 @@ function createStorageType (registry: Registry, entryType: StorageEntryTypeV9):
function convertModule (registry: Registry, mod: ModuleMetadataV9): ModuleMetadataV10 {
const storage = mod.storage.unwrapOr(null);
return createType(registry, 'ModuleMetadataV10', {
return registry.createType('ModuleMetadataV10', {
...mod,
storage: storage
? {
...storage,
items: storage.items.map((item: StorageEntryMetadataV9): any => ({
...item,
type: createType(registry, 'StorageEntryTypeV10', ...createStorageType(registry, item.type))
type: registry.createType('StorageEntryTypeV10', ...createStorageType(registry, item.type))
}))
}
: null
@@ -60,7 +58,7 @@ function convertModule (registry: Registry, mod: ModuleMetadataV9): ModuleMetada
/** @internal */
export default function toV10 (registry: Registry, { modules }: MetadataV9): MetadataV10 {
return createType(registry, 'MetadataV10', {
return registry.createType('MetadataV10', {
modules: modules.map((mod): ModuleMetadataV10 => convertModule(registry, mod))
});
}
+17 -9
View File
@@ -49,7 +49,7 @@ describe('Option', (): void => {
// Option<Option<Bytes>> (hence the length, since it is from storage)
const HEX = '0x210100000000000000000000000000000000000000000000000000000000000000000000000000000000011b4d03dd8c01f1049143cf9c4c817e4b167f1d1b83e5c6f0f10d89ba1e7bce';
// whatc the hex prefix and length
// watch the hex prefix and length
expect(
new Option(registry, Bytes, HEX).toHex().substr(6)
).toEqual(HEX.substr(2));
@@ -59,7 +59,7 @@ describe('Option', (): void => {
const HEX = '0x12345678';
expect(
(new Option(registry, U32, HEX).unwrap() as U32).toNumber()
new Option(registry, U32, HEX).unwrap().toNumber()
).toEqual(0x12345678);
});
@@ -97,22 +97,30 @@ describe('Option', (): void => {
});
describe('utils', (): void => {
const testeq = new Option(registry, Text, '1234');
const test = new Option(registry, Text, '1234');
it('compares against other option', (): void => {
expect(testeq.eq(new Option(registry, Text, '1234'))).toBe(true);
expect(test.eq(new Option(registry, Text, '1234'))).toBe(true);
});
it('compares against raw value', (): void => {
expect(testeq.eq('1234')).toBe(true);
expect(test.eq('1234')).toBe(true);
});
it('unwraps to default if empty', (): void => {
expect(new Option(registry, Text).unwrapOr('6789')).toBe('6789');
it('unwrapOr to specified if empty', (): void => {
expect(new Option(registry, Text).unwrapOr('6789').toString()).toEqual('6789');
});
it('unwraps to value if non-empty', (): void => {
expect((new Option(registry, Text, '1234').unwrapOr(null) as Text).toString()).toBe('1234');
it('unwrapOr to specified if non-empty', (): void => {
expect(new Option(registry, Text, '1234').unwrapOr(null)?.toString()).toEqual('1234');
});
it('unwrapOrDefault to default if empty', (): void => {
expect(new Option(registry, U32).unwrapOrDefault().toNumber()).toEqual(0);
});
it('unwrapOrDefault to specified if non-empty', (): void => {
expect(new Option(registry, U32, '1234').unwrapOrDefault().toNumber()).toEqual(1234);
});
});
});
+40 -29
View File
@@ -10,6 +10,32 @@ import Null from '../primitive/Null';
import { typeToConstructor } from './utils';
import Base from './Base';
/** @internal */
function decodeOptionU8a (registry: Registry, Type: Constructor, value: Uint8Array): Codec {
return !value.length || value[0] === 0
? new Null(registry)
: new Type(registry, value.subarray(1));
}
/** @internal */
function decodeOption (registry: Registry, Type: Constructor, value?: any): Codec {
if (isNull(value) || isUndefined(value) || value instanceof Null) {
return new Null(registry);
// eslint-disable-next-line @typescript-eslint/no-use-before-define
} else if (value instanceof Option) {
return decodeOption(registry, Type, value.value);
} else if (value instanceof Type) {
// don't re-create, use as it (which also caters for derived types)
return value;
} else if (isU8a(value)) {
// the isU8a check happens last in the if-tree - since the wrapped value
// may be an instance of it, so Type and Option checks go in first
return decodeOptionU8a(registry, Type, value);
}
return new Type(registry, value);
}
/**
* @name Option
* @description
@@ -19,42 +45,17 @@ import Base from './Base';
* with a value if/as required/found.
*/
export default class Option<T extends Codec> extends Base<T> {
private _Type: Constructor;
private _Type: Constructor<T>;
constructor (registry: Registry, Type: Constructor | keyof InterfaceTypes, value?: any) {
constructor (registry: Registry, Type: Constructor<T> | keyof InterfaceTypes, value?: any) {
const Clazz = typeToConstructor(registry, Type);
super(registry, Option.decodeOption(registry, Clazz, value));
super(registry, decodeOption(registry, Clazz, value));
this._Type = Clazz;
}
/** @internal */
public static decodeOption (registry: Registry, Type: Constructor, value?: any): Codec {
if (isNull(value) || isUndefined(value) || value instanceof Null) {
return new Null(registry);
} else if (value instanceof Option) {
return Option.decodeOption(registry, Type, value.value);
} else if (value instanceof Type) {
// don't re-create, use as it (which also caters for derived types)
return value;
} else if (isU8a(value)) {
// the isU8a check happens last in the if-tree - since the wrapped value
// may be an instance of it, so Type and Option checks go in first
return Option.decodeOptionU8a(registry, Type, value);
}
return new Type(registry, value);
}
/** @internal */
private static decodeOptionU8a (registry: Registry, Type: Constructor, value: Uint8Array): Codec {
return !value.length || value[0] === 0
? new Null(registry)
: new Type(registry, value.subarray(1));
}
public static with<O extends Codec> (Type: Constructor | keyof InterfaceTypes): Constructor<Option<O>> {
public static with<O extends Codec> (Type: Constructor<O> | keyof InterfaceTypes): Constructor<Option<O>> {
return class extends Option<O> {
constructor (registry: Registry, value?: any) {
super(registry, Type, value);
@@ -170,4 +171,14 @@ export default class Option<T extends Codec> extends Base<T> {
? this.unwrap()
: defaultValue;
}
/**
* @description Returns the value that the Option represents (if available) or defaultValue if none
* @param defaultValue The value to return if the option isNone
*/
public unwrapOrDefault (): T {
return this.isSome
? this.unwrap()
: new this._Type(this.registry);
}
}