Vector from u8a, StorageChangeSet with gaps (#228)

This commit is contained in:
Jaco Greeff
2018-10-09 12:56:20 +02:00
committed by GitHub
parent 6b7cd3211b
commit 32f491ec31
4 changed files with 93 additions and 28 deletions
+16 -10
View File
@@ -169,37 +169,43 @@ export default class Api implements ApiInterface {
);
}
private formatOutput (method: RpcMethod, params: Array<Base>, result?: any): Base {
private formatOutput (method: RpcMethod, params: Array<Base>, result?: any): Base | Array<Base | undefined> {
const base = createType(method.type as string).fromJSON(result);
if (method.type === 'StorageData') {
// single return value (via state.getStorage), decode the value based on the
// outputType that we have specified
const type = (params[0] as StorageKey).outputType;
if (type) {
return createType(type, base.raw);
}
} else if (method.type === 'StorageChangeSet') {
return (params[0] as Vector<StorageKey>).reduce((vector, _key: StorageKey) => {
// multiple return values (via state.storage subscription), decode the values
// one at a time, all based on the query types
return (params[0] as Vector<StorageKey>).reduce((result, _key: StorageKey) => {
const type = _key.outputType;
if (!type) {
throw new Error('Cannot format StorageChangeSet, output type missing for key');
}
// see if we have a result value for this specific key
const key = _key.toHex();
const item = (base as StorageChangeSet).changes.find((item) =>
item.key.toHex() === key
);
if (!item) {
throw new Error('No result found for key in StorageChangeSet');
}
// if we don't have a value, do not fill in the entry, it will be up to the
// caller to sort this out, either ignoring or having a cache for older values
result.push(
item
? createType(type, item.value.value)
: undefined
);
vector.push(createType(type, item.value.value));
return vector;
}, new (Vector.with(Base))());
return result;
}, [] as Array<Base | undefined>);
}
return base;
+17
View File
@@ -2,6 +2,10 @@
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import extrinsics from '@polkadot/extrinsics/static';
import createType from './createType';
import Method from '../Method';
import Text from '../Text';
import Vector from './Vector';
@@ -10,6 +14,8 @@ describe('Vector', () => {
beforeEach(() => {
array = new Vector(Text, [ '1', '23', '345', '4567', new Text('56789') ]);
Method.injectExtrinsics(extrinsics);
});
it('wraps a sequence of values', () => {
@@ -41,6 +47,17 @@ describe('Vector', () => {
expect(array.Type).toEqual('Text');
});
it('decodes a complex type via construction', () => {
const test = createType('Vec<(PropIndex, Proposal, AccountId)>', new Uint8Array([
4, 10, 0, 0, 0, 0, 3, 80, 123, 10, 9, 34, 48, 120, 52, 50, 34, 58, 32, 34, 48, 120, 52, 51, 34, 10, 125, 10, 209, 114, 167, 76, 218, 76, 134, 89, 18, 195, 43, 160, 168, 10, 87, 174, 105, 171, 174, 65, 14, 92, 203, 89, 222, 232, 78, 47, 68, 50, 219, 79
]));
const first = test.get(0);
expect(first.get(0).toNumber()).toEqual(10);
expect(first.get(1).callIndex).toEqual(new Uint8Array([0, 3]));
expect(first.get(2).toString()).toEqual('5GoKvZWG5ZPYL1WUovuHW3zJBWBP5eT8CbqjdRY4Q6iMaDtZ');
});
describe('array-like functions', () => {
it('allows retrieval of a specific item', () => {
expect(
+33 -18
View File
@@ -3,6 +3,7 @@
// of the ISC license. See the LICENSE file for details.
import u8aConcat from '@polkadot/util/u8a/concat';
import toU8a from '@polkadot/util/u8a/toU8a';
import Base from './Base';
import Compact, { DEFAULT_LENGTH_BITS } from './Compact';
@@ -17,18 +18,41 @@ export default class Vector <
> extends Base<Array<T>> {
private _Type: { new(value?: any): T };
constructor (Type: { new(value?: any): T }, value: Array<any> = [] as Array<any>) {
constructor (Type: { new(value?: any): T }, value: Uint8Array | string | Array<any> = [] as Array<any>) {
super(
value.map((entry) =>
entry instanceof Type
? entry
: new Type(entry)
)
Vector.decode(Type, value)
);
this._Type = Type;
}
static decode <T> (Type: { new(value?: any): T }, value: Uint8Array | string | Array<any>): Array<T> {
if (Array.isArray(value)) {
return value.map((entry) =>
entry instanceof Type
? entry
: new Type(entry)
);
}
const u8a = toU8a(value);
let [offset, _length] = Compact.decodeU8a(value, DEFAULT_LENGTH_BITS);
const length = _length.toNumber();
const result = [];
for (let index = 0; index < length; index++) {
// @ts-ignore Not sure why we get "Property 'fromU8a' does not exist on type 'T'.", T extends Base in def?
const decoded = new Type().fromU8a(u8a.subarray(offset));
result.push(decoded as T);
offset += decoded.byteLength();
}
return result;
}
static with <O extends Base> (Type: { new(value?: any): O }): { new(value?: any): Vector<O> } {
return class extends Vector<O> {
constructor (value?: Array<any>) {
@@ -64,7 +88,8 @@ export default class Vector <
}
fromJSON (input: any): Vector<T> {
this.raw = input.map((input: any) =>
// input could be null/undefined to indicate empty
this.raw = (input || []).map((input: any) =>
new this._Type().fromJSON(input)
);
@@ -72,17 +97,7 @@ export default class Vector <
}
fromU8a (input: Uint8Array): Vector<T> {
let [offset, _length] = Compact.decodeU8a(input, DEFAULT_LENGTH_BITS);
const length = _length.toNumber();
this.raw = [];
for (let index = 0; index < length; index++) {
const raw = new this._Type().fromU8a(input.subarray(offset));
this.raw.push(raw as T);
offset += raw.byteLength();
}
this.raw = Vector.decode(this._Type, input);
return this;
}
@@ -111,6 +111,33 @@ describe('getTypeValue', () => {
]
});
});
it('returns a type structure (actual)', () => {
expect(
getTypeDef('Vec<(PropIndex, Proposal, AccountId)>')
).toEqual({
info: TypeDefInfo.Vector,
type: 'Vec<(PropIndex, Proposal, AccountId)>',
sub: {
info: TypeDefInfo.Tuple,
type: '(PropIndex, Proposal, AccountId)',
sub: [
{
info: TypeDefInfo.Plain,
type: 'PropIndex'
},
{
info: TypeDefInfo.Plain,
type: 'Proposal'
},
{
info: TypeDefInfo.Plain,
type: 'AccountId'
}
]
}
});
});
});
describe('getTypeClass', () => {