Construct toRawType using internal types (#966)

* Construct toRawType using internal types

* typo

* Struct test for with + toRawType

* Tuple tests for toRawType

* Remove extra empty check, already handled

* EnumType with sane toRawType

* Sane toRawType representation for Set

* Use JSON.stringify inside Struct toRawType

* Extrinsic toRawType yields Extrinsic (like a primitive)

* Block toRawType test
This commit is contained in:
Jaco Greeff
2019-06-12 16:45:45 +12:00
committed by Ian He
parent 1827c34b69
commit 548cfbefb8
13 changed files with 164 additions and 38 deletions
+14
View File
@@ -211,4 +211,18 @@ describe('Enum', () => {
});
});
});
describe('toRawType', () => {
it('has a sane output for basic enums', () => {
expect(
new Enum(['foo', 'bar']).toRawType()
).toEqual(JSON.stringify({ _enum: ['foo', 'bar'] }));
});
it('has a sane output for types enums', () => {
expect(
new Enum({ foo: Text, bar: U32 }).toRawType()
).toEqual(JSON.stringify({ _enum: { foo: 'Text', bar: 'u32' } }));
});
});
});
+8 -11
View File
@@ -222,18 +222,15 @@ export default class Enum extends Base<Codec> implements Codec {
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
const kv = this._isBasic
? Object.keys(this._def).map((key) =>
`"${key}"`
)
: Object.entries(this._def).map(([key, Type]) =>
`"${key}":"${new Type().toRawType()}"`
);
const braces = this._isBasic
? '[]'
: '{}';
const _enum = this._isBasic
? Object.keys(this._def)
: Object.entries(this._def).reduce((result, [key, Type]) => {
result[key] = new Type().toRawType();
return `{"_enum":${braces[0]}${kv.join(',')}${braces[1]}}`;
return result;
}, {} as { [index: string]: string });
return JSON.stringify({ _enum });
}
/**
+5 -1
View File
@@ -17,10 +17,14 @@ import Null from '../primitive/Null';
* with a value if/as required/found.
*/
export default class Option<T extends Codec> extends Base<T> implements Codec {
private _Type: Constructor;
constructor (Type: Constructor, value?: any) {
super(
Option.decodeOption(Type, value)
);
this._Type = Type;
}
static decodeOption (Type: Constructor, value?: any): Codec {
@@ -115,7 +119,7 @@ export default class Option<T extends Codec> extends Base<T> implements Codec {
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
return `Option<${this.raw.toRawType()}>`;
return `Option<${new this._Type().toRawType()}>`;
}
/**
+8
View File
@@ -91,4 +91,12 @@ describe('Set', () => {
).toBe(false);
});
});
it('has a sane toRawType representation', () => {
expect(
new Set({ a: 1, b: 2, c: 345 }).toRawType()
).toEqual(JSON.stringify({
_set: { a: 1, b: 2, c: 345 }
}));
});
});
+1 -5
View File
@@ -149,12 +149,8 @@ export default class CodecSet extends Set<string> implements Codec {
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
const kv = Object.entries(this._setValues).map(([key, value]) =>
`"${key}":${value}` // JSON format
);
// FIXME We don't cater for this in createType as of yet
return `{"_set":{${kv.join(',')}}}`;
return JSON.stringify({ _set: this._setValues });
}
/**
+39
View File
@@ -2,10 +2,13 @@
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import AccountId from '../primitive/AccountId';
import Text from '../primitive/Text';
import U32 from '../primitive/U32';
import Balance from '../type/Balance';
import BlockNumber from '../type/BlockNumber';
import { CodecTo } from '../types';
import Compact from './Compact';
import Option from './Option';
import Struct from './Struct';
import Vector from './Vector';
@@ -183,4 +186,40 @@ describe('Struct', () => {
}, { blockNumber: '0x1234567890abcdef' }).toString()
).toEqual('{"blockNumber":"0x1234567890abcdef"}');
});
it('generates sane toRawType', () => {
expect(
new Struct({
accountId: AccountId,
balance: Balance,
blockNumber: BlockNumber,
compactNumber: Compact.with(BlockNumber),
optionNumber: Option.with(BlockNumber),
counter: U32,
vector: Vector.with(AccountId)
}).toRawType()
).toEqual(JSON.stringify({
accountId: 'AccountId',
balance: 'Balance',
blockNumber: 'u64',
compactNumber: 'Compact<u64>',
optionNumber: 'Option<u64>',
counter: 'u32',
vector: 'Vec<AccountId>'
}));
});
it('generates sane toRawType (via with)', () => {
const Type = Struct.with({
accountId: AccountId,
balance: Balance
});
expect(
new Type().toRawType()
).toEqual(JSON.stringify({
accountId: 'AccountId',
balance: 'Balance'
}));
});
});
+15 -14
View File
@@ -28,7 +28,7 @@ export default class Struct<
E extends { [K in keyof S]: string } = { [K in keyof S]: string }
> extends Map<keyof S, Codec> implements Codec {
protected _jsonMap: Map<keyof S, string>;
protected _Types: E;
protected _Types: S;
constructor (Types: S, value: V | Map<any, any> | Array<any> = {} as V, jsonMap: Map<keyof S, string> = new Map()) {
const decoded = Struct.decodeStruct<S, V, T>(Types, value, jsonMap);
@@ -38,14 +38,7 @@ export default class Struct<
);
this._jsonMap = jsonMap;
this._Types = (Object
.keys(Types) as Array<keyof S>)
.reduce((result: E, key) => {
// TS2322: Type 'string' is not assignable to type 'E[keyof S]'.
(result as any)[key] = Types[key].name;
return result;
}, {} as E);
this._Types = Types;
}
/**
@@ -170,7 +163,13 @@ export default class Struct<
* @description Returns the Type description to sthe structure
*/
get Type (): E {
return this._Types;
return (Object
.entries(this._Types) as Array<[keyof S, Constructor]>)
.reduce((result: E, [key, Type]) => {
(result as any)[key] = Type.name;
return result;
}, {} as E);
}
/**
@@ -236,11 +235,13 @@ export default class Struct<
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
const kv = [...this.entries()].map(([key, value]) =>
`"${key}":"${value.toRawType()}"` // double-quotes, JSON
);
return JSON.stringify(
Object.entries(this._Types).reduce((result, [key, Type]) => {
result[key] = new Type().toRawType();
return `{${kv.join(',')}}`;
return result;
}, {} as { [index: string]: string })
);
}
/**
+14
View File
@@ -94,4 +94,18 @@ describe('Tuple', () => {
expect(tuple.eq(['bazzing', 72])).toBe(false);
});
});
describe('toRawType', () => {
it('generates sane value with array types', () => {
expect(
new Tuple([U32, BlockNumber]).toRawType()
).toEqual('(u32,u64)');
});
it('generates sane value with object types', () => {
expect(
new Tuple({ number: U32, blockNumber: BlockNumber }).toRawType()
).toEqual('(u32,u64)');
});
});
});
+4 -4
View File
@@ -21,7 +21,7 @@ type TupleConstructors = Array<Constructor> | {
export default class Tuple extends AbstractArray<Codec> {
private _Types: TupleConstructors;
constructor (Types: TupleConstructors, value: any) {
constructor (Types: TupleConstructors, value?: any) {
super(
...Tuple.decodeTuple(Types, value)
);
@@ -83,9 +83,9 @@ export default class Tuple extends AbstractArray<Codec> {
toRawType (): string {
const types = (
Array.isArray(this._Types)
? this._Types.map((Type) => new Type().toRawType())
: Object.keys(this._Types)
);
? this._Types
: Object.values(this._Types)
).map((Type) => new Type().toRawType());
return `(${types.join(',')})`;
}
+34
View File
@@ -0,0 +1,34 @@
// Copyright 2017-2019 @polkadot/types authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import extrinsics from '@polkadot/extrinsics/static';
import Method from '../primitive/Method';
import Block from './Block';
describe('Block', () => {
beforeEach(() => {
Method.injectMethods(extrinsics);
});
it('has a valid toRawType', () => {
expect(
new Block().toRawType()
).toEqual(
// each of the containing structures have been stringified on their own
JSON.stringify({
header: JSON.stringify({
parentHash: 'H256',
number: 'Compact<u64>',
stateRoot: 'H256',
extrinsicsRoot: 'H256',
digest: JSON.stringify({
logs: 'Vec<{"_enum":{"Other":"Bytes","AuthoritiesChange":"Vec<AccountId>","ChangesTrieRoot":"H256","Seal":"(u64,H512)","Consensus":"(u32,Bytes)"}}>'
})
}),
extrinsics: 'Vec<Extrinsic>'
})
);
});
});
+9
View File
@@ -10,6 +10,15 @@ import U128 from '../primitive/U128';
* The Substrate Balance representation as a [[U128]].
*/
export default class Balance extends U128 {
/**
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
// NOTE Balance we treat as a special case, don't return the base u128 - there
// is typically special processing downstream, i.e. different inputs, it gets
// treated as a primitive in this case
return 'Balance';
}
}
/**
+8
View File
@@ -197,6 +197,14 @@ export default class Extrinsic extends Struct implements IExtrinsic {
return this.toHex();
}
/**
* @description Returns the base runtime type name for this instance
*/
toRawType (): string {
// We are treating this in the same way we do a primitive, this is known
return 'Extrinsic';
}
/**
* @description Encodes the value as a Uint8Array as per the SCALE specifications
* @param isBare true when the value has none of the type-specific prefixes (internal)
+5 -3
View File
@@ -111,9 +111,9 @@ export default class ExtrinsicEra extends Enum implements IExtrinsicEra {
*/
export class ImmortalEra extends U8a {
constructor (value?: AnyU8a) {
super(value);
assert(this.eq(VALID_IMMORTAL), `IMMORTAL: expected ${VALID_IMMORTAL.toHex()}, found ${this.toHex()}`);
// For immortals, we always provide the known value (i.e. treated as a
// constant no matter how it is constructed - it is a fixed structure)
super(VALID_IMMORTAL);
}
}
@@ -155,6 +155,8 @@ export class MortalEra extends Tuple {
const quantizedPhase = phase / quantizeFactor * quantizeFactor;
return [new U64(calPeriod), new U64(quantizedPhase)];
} else if (!value) {
return [new U64(), new U64()];
}
throw new Error('Invalid data passed to Mortal era');