Allow optional params (#68)

* Allow optional params

* Don't double-up on method signature

* Cleanup formatters/util
This commit is contained in:
Jaco Greeff
2018-04-17 13:17:19 +02:00
committed by GitHub
parent 28050f86e7
commit 3c40c1de64
12 changed files with 140 additions and 106 deletions
@@ -44,22 +44,15 @@ function formatArrayType (formatters: FormattersFunctionMap, type: FormattersFun
});
}
function format (formatters: FormattersFunctionMap, type: FormattersFunctionType, value: mixed | Array<mixed>): mixed {
if (arrayTypeRegex.test(type)) {
// $FlowFixMe array type
return formatArrayType(formatters, type, value);
}
module.exports = function format (formatters: FormattersFunctionMap, types: Array<FormattersFunctionType>, values: Array<mixed>): Array<mixed> {
return values.map((value, index): mixed => {
const type = types[index];
return formatSingleType(formatters, type, value);
}
if (arrayTypeRegex.test(type)) {
// $FlowFixMe array type
return formatArrayType(formatters, type, value);
}
function formatArray (formatters: FormattersFunctionMap, types: Array<FormattersFunctionType>, values: Array<mixed>): Array<mixed> {
return types.map((type, index): mixed => {
return format(formatters, type, values[index]);
return formatSingleType(formatters, type, value);
});
}
module.exports = {
format,
formatArray
};
+78
View File
@@ -0,0 +1,78 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
const format = require('./format');
const echo = require('./echo');
describe('format', () => {
let formatters;
let echoSpy;
let warnSpy;
beforeEach(() => {
echoSpy = jest.fn(echo);
warnSpy = jest.spyOn(console, 'warn');
formatters = {
'Address': echoSpy,
'Address[]': echoSpy,
'Exception': () => {
throw new Error('something went wrong');
}
};
});
afterEach(() => {
echoSpy.mockRestore();
warnSpy.mockRestore();
});
it('formats unknown types with a fallback', () => {
expect(
format(formatters, ['Unknown'], ['test'])
).toEqual(['test']);
});
it('logs a warning with unknown types', () => {
format(formatters, ['Unknown'], ['test']);
expect(warnSpy).toHaveBeenCalledWith(
expect.anything(), expect.anything(), "Unable to find default formatter for 'Unknown', falling back to echo"
);
});
it('wraps exceptions with the type', () => {
expect(
() => format(formatters, ['Exception'], ['test'])
).toThrow(/Error formatting 'test' as 'Exception'/);
});
describe('primitives', () => {
it('formats known types using the supplied formatter', () => {
format(formatters, ['Address'], ['0xaddress']);
expect(echoSpy).toHaveBeenCalledWith('0xaddress');
});
});
describe('primitive[]', () => {
it('formats using the primitive type', () => {
format(formatters, ['Address[]'], [['0x123', '0x234']]);
expect(echoSpy).toHaveBeenCalledWith('0x123');
expect(echoSpy).toHaveBeenCalledWith('0x234');
});
it('returns formatted values as arrays', () => {
expect(
format(formatters, ['Address[]'], [['0x123', '0x234']])
).toEqual([['0x123', '0x234']]);
});
});
it('formats each value in an array', () => {
expect(
format(formatters, ['Unknown', 'Unknown'], ['test', 'blah'])
).toEqual(['test', 'blah']);
});
});
+2 -2
View File
@@ -8,7 +8,7 @@ import type { InterfaceInputType } from '@polkadot/api-jsonrpc/types';
const bytesEncode = require('@polkadot/primitives-json/bytes/encode');
const hashEncode = require('@polkadot/primitives-json/hash/encode');
const util = require('./util');
const format = require('./format');
const formatters = {
'Bytes': bytesEncode,
@@ -19,5 +19,5 @@ const formatters = {
module.exports = function formatInputs (inputs: Array<InterfaceInputType>, values: Array<mixed>): Array<mixed> {
const types = inputs.map(({ type }) => type);
return util.formatArray(formatters, types, values);
return format(formatters, types, values);
};
+2 -2
View File
@@ -11,7 +11,7 @@ const headerDecode = require('@polkadot/primitives-json/header/decode');
const isNull = require('@polkadot/util/is/null');
const isUndefined = require('@polkadot/util/is/undefined');
const util = require('./util');
const format = require('./format');
const formatters = {
'BlockNumber': bnDecode,
@@ -25,5 +25,5 @@ module.exports = function formatOutput (output: InterfaceOutputType, value?: mix
return value;
}
return util.format(formatters, output.type, value);
return format(formatters, [output.type], [value])[0];
};
-82
View File
@@ -1,82 +0,0 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
const { format, formatArray } = require('./util');
const echo = require('./echo');
describe('util', () => {
let formatters;
let echoSpy;
let warnSpy;
beforeEach(() => {
echoSpy = jest.fn(echo);
warnSpy = jest.spyOn(console, 'warn');
formatters = {
'Address': echoSpy,
'Address[]': echoSpy,
'Exception': () => {
throw new Error('something went wrong');
}
};
});
afterEach(() => {
echoSpy.mockRestore();
warnSpy.mockRestore();
});
describe('format', () => {
it('formats unknown types with a fallback', () => {
expect(
format(formatters, 'Unknown', 'test')
).toEqual('test');
});
it('logs a warning with unknown types', () => {
format(formatters, 'Unknown', 'test');
expect(warnSpy).toHaveBeenCalledWith(
expect.anything(), expect.anything(), "Unable to find default formatter for 'Unknown', falling back to echo"
);
});
it('wraps exceptions with the type', () => {
expect(
() => format(formatters, 'Exception', 'test')
).toThrow(/Error formatting 'test' as 'Exception'/);
});
describe('primitives', () => {
it('formats known types using the supplied formatter', () => {
format(formatters, 'Address', '0xaddress');
expect(echoSpy).toHaveBeenCalledWith('0xaddress');
});
});
describe('primitive[]', () => {
it('formats using the primitive type', () => {
format(formatters, 'Address[]', ['0x123', '0x234']);
expect(echoSpy).toHaveBeenCalledWith('0x123');
expect(echoSpy).toHaveBeenCalledWith('0x234');
});
it('returns formatted values as arrays', () => {
expect(
format(formatters, 'Address[]', ['0x123', '0x234'])
).toEqual(['0x123', '0x234']);
});
});
});
describe('formatArray', () => {
it('formats each value in an array', () => {
expect(
formatArray(formatters, ['Unknown', 'Unknown'], ['test', 'blah'])
).toEqual(['test', 'blah']);
});
});
});
@@ -19,6 +19,7 @@ module.exports = ({
type: 'H256'
},
{
isOptional: true,
name: 'blockHash',
type: 'HeaderHash'
}
+1
View File
@@ -10,6 +10,7 @@ export type FormatInputType = 'Bytes' | 'H256' | 'HeaderHash' | 'String';
export type FormatOutputType = 'BlockNumber' | 'Bytes' | 'Header' | 'HeaderHash' | 'U64';
export type InterfaceInputType = {
isOptional?: boolean,
name: string,
type: FormatInputType
};
+1 -1
View File
@@ -17,7 +17,7 @@ module.exports = function createMethodSend (provider: ProviderInterface, rpcName
const call = async (..._params: Array<mixed>): Promise<mixed> => {
// TODO: Deprecated warning
try {
const params = createParams(rpcName, _params, inputs);
const params = createParams(_params, inputs);
const result = await provider.send(rpcName, params);
return formatOutput(output, result);
+1 -1
View File
@@ -41,7 +41,7 @@ describe('methodCall', () => {
const method = createMethod(provider, 'test_bleh', 'bleh', methods.bleh);
return method(1).catch((error) => {
expect(error.message).toMatch(/0 params expected, found 1 instead/);
expect(error.message).toMatch(/no params expected, found 1 instead/);
});
});
+1 -1
View File
@@ -25,7 +25,7 @@ module.exports = function methodSubscribe (provider: ProviderInterface, rpcName:
assert(isFunction(cb), `Expected callback in last position of params`);
const params = createParams(rpcName, _params, inputs);
const params = createParams(_params, inputs);
const update = (error: ?Error, result?: mixed) => {
cb(error, formatOutput(output, result));
};
+7 -2
View File
@@ -8,8 +8,13 @@ import type { InterfaceInputType } from '@polkadot/api-jsonrpc/types';
const formatInputs = require('@polkadot/api-format/input');
const assert = require('@polkadot/util/assert');
module.exports = function createParams (rpcName: string, params: Array<mixed>, inputs: Array<InterfaceInputType>): Array<mixed> {
assert(inputs.length === params.length, `${rpcName}: ${inputs.length} params expected, found ${params.length} instead`);
module.exports = function createParams (params: Array<mixed>, inputs: Array<InterfaceInputType>): Array<mixed> {
const required = inputs.filter(({ isOptional }) => !isOptional);
const optionalText = inputs.length
? ` (${(inputs.length - required.length) || 'none'} optional)`
: '';
assert(params.length >= required.length && params.length <= inputs.length, `${inputs.length || 'no'} params expected${optionalText}, found ${params.length} instead`);
return formatInputs(inputs, params);
};
+38
View File
@@ -0,0 +1,38 @@
// Copyright 2017-2018 Jaco Greeff
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
const params = require('./params');
describe('params', () => {
let methods;
beforeEach(() => {
methods = {
blah: {
inputs: [
{ name: 'foo', type: 'Bytes' }
],
output: { type: 'Bytes' }
},
bleh: {
inputs: [
{ name: 'foo', type: 'Bytes', isOptional: true }
],
output: { type: 'Bytes' }
}
};
});
it('check against params', () => {
expect(
() => params([], methods.blah.inputs)
).toThrow(/params expected/);
});
it('check against params (required)', () => {
expect(
params([], methods.bleh.inputs)
).toBeDefined();
});
});