diff --git a/packages/api-format/package.json b/packages/api-format/package.json index 5739d8f904c3..bcd8d4c249e6 100644 --- a/packages/api-format/package.json +++ b/packages/api-format/package.json @@ -34,7 +34,7 @@ }, "dependencies": { "@polkadot/primitives-json": "^0.9.5", - "@polkadot/util": "^0.18.2", + "@polkadot/util": "^0.18.4", "babel-runtime": "^6.26.0" } } diff --git a/packages/api-jsonrpc/src/chain/getHead.js b/packages/api-jsonrpc/src/chain/getHead.js new file mode 100644 index 000000000000..53e3de130c65 --- /dev/null +++ b/packages/api-jsonrpc/src/chain/getHead.js @@ -0,0 +1,20 @@ +// 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. +// @flow + +import type { InterfaceMethodDefinition } from '../types'; + +/** + @name getHead + @signature chain_getHead (): HeaderHash + @summary Retrieves the best headerHash. + @description + Return the block hash for the lastest/best. +*/ +module.exports = ({ + inputs: [], + output: { + type: 'HeaderHash' + } +}: InterfaceMethodDefinition); diff --git a/packages/api-jsonrpc/src/chain/index.js b/packages/api-jsonrpc/src/chain/index.js index 536612c91172..6f5187406ab8 100644 --- a/packages/api-jsonrpc/src/chain/index.js +++ b/packages/api-jsonrpc/src/chain/index.js @@ -5,13 +5,17 @@ import type { InterfaceDefinition } from '../types'; +const getHead = require('./getHead'); const getHeader = require('./getHeader'); +const newHead = require('./newHead'); /** @summary Methods to retrieve chain data. */ module.exports = ({ methods: { - getHeader + getHead, + getHeader, + newHead } }: InterfaceDefinition); diff --git a/packages/api-jsonrpc/src/chain/newHead.js b/packages/api-jsonrpc/src/chain/newHead.js new file mode 100644 index 000000000000..7ad16f74bfc6 --- /dev/null +++ b/packages/api-jsonrpc/src/chain/newHead.js @@ -0,0 +1,21 @@ +// 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. +// @flow + +import type { InterfaceMethodDefinition } from '../types'; + +/** + @name getHead + @signature chain_newHead (): HeaderHash + @summary Retrieves the best headerHash via subscription. + @description + Return the block hash for the lastest/best. +*/ +module.exports = ({ + isSubscription: true, + inputs: [], + output: { + type: 'HeaderHash' + } +}: InterfaceMethodDefinition); diff --git a/packages/api-jsonrpc/src/types.js b/packages/api-jsonrpc/src/types.js index 465d33365952..9366a367871c 100644 --- a/packages/api-jsonrpc/src/types.js +++ b/packages/api-jsonrpc/src/types.js @@ -7,7 +7,7 @@ export type InterfaceTypes = 'author' | 'chain' | 'extra' | 'state'; export type FormatInputType = 'Bytes' | 'H256' | 'HeaderHash' | 'String'; -export type FormatOutputType = 'BlockNumber' | 'Bytes' | 'Header' | 'U64'; +export type FormatOutputType = 'BlockNumber' | 'Bytes' | 'Header' | 'HeaderHash' | 'U64'; export type InterfaceInputType = { name: string, @@ -18,8 +18,14 @@ export type InterfaceOutputType = { type: FormatOutputType }; +export type InterfaceMethodDefinition$Subscribe = { + subscribe: string, + unsubscribe: string +} + export type InterfaceMethodDefinition = { - deprecated?: boolean, + isDeprecated?: boolean, + isSubscription?: boolean, inputs: Array, output: InterfaceOutputType }; diff --git a/packages/api-provider/package.json b/packages/api-provider/package.json index 7f176dd8081f..b6d79b92ac63 100644 --- a/packages/api-provider/package.json +++ b/packages/api-provider/package.json @@ -34,7 +34,7 @@ "nock": "^9.1.0" }, "dependencies": { - "@polkadot/util": "^0.18.2", + "@polkadot/util": "^0.18.4", "babel-runtime": "^6.26.0", "isomorphic-fetch": "^2.2.1", "websocket": "^1.0.25" diff --git a/packages/api/package.json b/packages/api/package.json index 8738b63ee1c1..1ee5d8791185 100644 --- a/packages/api/package.json +++ b/packages/api/package.json @@ -33,7 +33,7 @@ "@polkadot/api-format": "^0.8.6", "@polkadot/api-jsonrpc": "^0.8.6", "@polkadot/api-provider": "^0.8.6", - "@polkadot/util": "^0.18.2", + "@polkadot/util": "^0.18.4", "babel-runtime": "^6.26.0" } } diff --git a/packages/api/src/create/interface.js b/packages/api/src/create/interface.js index fd2724cd6dad..4fa86076d0fb 100644 --- a/packages/api/src/create/interface.js +++ b/packages/api/src/create/interface.js @@ -9,23 +9,23 @@ import type { ApiInterface$Section } from '../types'; const interfaces = require('@polkadot/api-jsonrpc'); -const createMethod = require('./method'); -const createSubscribe = require('./subscribe'); +const methodSend = require('./methodSend'); +const methodSubscribe = require('./methodSubscribe'); module.exports = function createInterface (provider: ProviderInterface, section: InterfaceTypes): ApiInterface$Section { const exposed: $Shape = {}; const { methods } = interfaces[section]; - exposed.subscribe = createSubscribe(provider, section, methods); - exposed.unsubscribe = provider.unsubscribe; - return Object .keys(methods) .reduce((exposed, name: string) => { const rpcName = `${section}_${name}`; - const method = createMethod(provider, rpcName, methods[name]); + const def = methods[name]; - exposed[name] = method; + // flowlint-next-line sketchy-null-bool:off + exposed[name] = def.isSubscription + ? methodSubscribe(provider, rpcName, name, methods[name]) + : methodSend(provider, rpcName, name, methods[name]); return exposed; }, exposed); diff --git a/packages/api/src/create/interface.spec.js b/packages/api/src/create/interface.spec.js index b42a852820d2..6b8bf445d522 100644 --- a/packages/api/src/create/interface.spec.js +++ b/packages/api/src/create/interface.spec.js @@ -16,6 +16,11 @@ jest.mock('./@polkadot/api-jsonrpc', () => ({ bleh: { inputs: [], output: { type: 'Address' } + }, + pubsub: { + isSubscription: true, + inputs: [], + output: { type: 'Address' } } } } @@ -38,7 +43,7 @@ describe('createInterface', () => { it('adds the specified methods to the interface', () => { expect(Object.keys(container)).toEqual( - ['subscribe', 'unsubscribe', 'blah', 'bleh'] + ['blah', 'bleh', 'pubsub'] ); }); diff --git a/packages/api/src/create/method.js b/packages/api/src/create/methodSend.js similarity index 66% rename from packages/api/src/create/method.js rename to packages/api/src/create/methodSend.js index 9cd0e2ca3add..0da74ca99474 100644 --- a/packages/api/src/create/method.js +++ b/packages/api/src/create/methodSend.js @@ -5,17 +5,17 @@ import type { InterfaceMethodDefinition } from '@polkadot/api-jsonrpc/types'; import type { ProviderInterface } from '@polkadot/api-provider/types'; +import type { ApiInterface$Section$Method } from '../types'; const formatOutput = require('@polkadot/api-format/output'); const ExtError = require('@polkadot/util/ext/error'); const jsonrpcSignature = require('@polkadot/util/jsonrpc/signature'); -type Method = (..._params: Array) => Promise; - const createParams = require('./params'); -module.exports = function createMethod (provider: ProviderInterface, rpcName: string, { inputs, output }: InterfaceMethodDefinition): Method { - return async (..._params: Array): Promise => { +module.exports = function createMethodSend (provider: ProviderInterface, rpcName: string, name: string, { inputs, output }: InterfaceMethodDefinition): ApiInterface$Section$Method { + const call = async (..._params: Array): Promise => { + // TODO: Deprecated warning try { const params = createParams(rpcName, _params, inputs); const result = await provider.send(rpcName, params); @@ -25,4 +25,7 @@ module.exports = function createMethod (provider: ProviderInterface, rpcName: st throw new ExtError(`${jsonrpcSignature(rpcName, inputs, output)}:: ${error.message}`, (error: ExtError).code); } }; + + // flowlint-next-line unclear-type:off + return ((call: any): ApiInterface$Section$Method); }; diff --git a/packages/api/src/create/method.spec.js b/packages/api/src/create/methodSend.spec.js similarity index 78% rename from packages/api/src/create/method.spec.js rename to packages/api/src/create/methodSend.spec.js index 616432695556..426361a31045 100644 --- a/packages/api/src/create/method.spec.js +++ b/packages/api/src/create/methodSend.spec.js @@ -2,9 +2,9 @@ // This software may be modified and distributed under the terms // of the ISC license. See the LICENSE file for details. -const createMethod = require('./method'); +const createMethod = require('./methodSend'); -describe('createMethod', () => { +describe('methodCall', () => { let methods; let provider; @@ -30,7 +30,7 @@ describe('createMethod', () => { }); it('wraps errors with the call signature', () => { - const method = createMethod(provider, 'test_blah', methods.blah); + const method = createMethod(provider, 'test_blah', 'blah', methods.blah); return method().catch((error) => { expect(error.message).toMatch(/test_blah \(foo: Bytes\): Bytes/); @@ -38,7 +38,7 @@ describe('createMethod', () => { }); it('checks for mismatched parameters', () => { - const method = createMethod(provider, 'test_bleh', methods.bleh); + const method = createMethod(provider, 'test_bleh', 'bleh', methods.bleh); return method(1).catch((error) => { expect(error.message).toMatch(/0 params expected, found 1 instead/); @@ -46,7 +46,7 @@ describe('createMethod', () => { }); it('calls the provider with the correct parameters', () => { - const method = createMethod(provider, 'test_blah', methods.blah); + const method = createMethod(provider, 'test_blah', 'blah', methods.blah); return method(new Uint8Array([0x12, 0x34])).then(() => { expect(provider.send).toHaveBeenCalledWith('test_blah', ['0x1234']); diff --git a/packages/api/src/create/subscribeMethod.js b/packages/api/src/create/methodSubscribe.js similarity index 65% rename from packages/api/src/create/subscribeMethod.js rename to packages/api/src/create/methodSubscribe.js index 385a67512767..9171293d672f 100644 --- a/packages/api/src/create/subscribeMethod.js +++ b/packages/api/src/create/methodSubscribe.js @@ -5,6 +5,7 @@ import type { InterfaceMethodDefinition } from '@polkadot/api-jsonrpc/types'; import type { ProviderInterface, ProviderInterface$Callback } from '@polkadot/api-provider/types'; +import type { ApiInterface$Section$Method } from '../types'; const formatOutput = require('@polkadot/api-format/output'); const assert = require('@polkadot/util/assert'); @@ -12,12 +13,12 @@ const ExtError = require('@polkadot/util/ext/error'); const isFunction = require('@polkadot/util/is/function'); const jsonrpcSignature = require('@polkadot/util/jsonrpc/signature'); -type Method = (...params: Array) => Promise; - const createParams = require('./params'); -module.exports = function createSubscribeMethod (provider: ProviderInterface, rpcName: string, { inputs, output }: InterfaceMethodDefinition): Method { - return async (..._params: Array): Promise => { +module.exports = function methodSubscribe (provider: ProviderInterface, rpcName: string, name: string, { inputs, output }: InterfaceMethodDefinition): ApiInterface$Section$Method { + const unsubscribe = (subscriptionId: mixed): Promise => + provider.send(`unsubscribe_${name}`, [subscriptionId]); + const call = async (..._params: Array): Promise => { try { // flowlint-next-line unclear-type:off const cb = ((_params.pop(): any): ProviderInterface$Callback); @@ -26,7 +27,7 @@ module.exports = function createSubscribeMethod (provider: ProviderInterface, rp const params = createParams(rpcName, _params, inputs); - return provider.subscribe(rpcName, params, (error: ?Error, result: mixed) => { + return provider.subscribe(`subscribe_${name}`, params, (error: ?Error, result: mixed) => { if (error) { cb(error); } else { @@ -37,4 +38,9 @@ module.exports = function createSubscribeMethod (provider: ProviderInterface, rp throw new ExtError(`${jsonrpcSignature(rpcName, inputs, output)}:: ${error.message}`, (error: ExtError).code); } }; + + call.unsubscribe = unsubscribe; + + // flowlint-next-line unclear-type:off + return ((call: any): ApiInterface$Section$Method); }; diff --git a/packages/api/src/create/subscribe.js b/packages/api/src/create/subscribe.js deleted file mode 100644 index 25d39306536b..000000000000 --- a/packages/api/src/create/subscribe.js +++ /dev/null @@ -1,25 +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. -// @flow - -import type { InterfaceDefinition$Methods } from '@polkadot/api-jsonrpc/types'; -import type { ProviderInterface } from '@polkadot/api-provider/types'; - -const assert = require('@polkadot/util/assert'); - -type Method = (name: string, ...params: Array) => Promise; - -const subscribeMethod = require('./subscribeMethod'); - -module.exports = function createSubscribe (provider: ProviderInterface, section: string, methods: InterfaceDefinition$Methods): Method { - return async (name: string, ...params: Array): Promise => { - const rpcName = `${section}_${name}`; - - assert(methods[name], `Unable to find '${rpcName}' subscription`); - - const fn = subscribeMethod(provider, rpcName, methods[name]); - - return fn.apply(null, params); - }; -}; diff --git a/packages/api/src/create/subscribe.spec.js b/packages/api/src/create/subscribe.spec.js deleted file mode 100644 index 3f8a435268f0..000000000000 --- a/packages/api/src/create/subscribe.spec.js +++ /dev/null @@ -1,76 +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 createSubscribe = require('./subscribe'); - -describe('createSubscribe', () => { - let methods; - let provider; - let sub; - let cb; - - beforeEach(() => { - methods = { - blah: { - inputs: [ - { name: 'foo', type: 'Address' } - ], - output: { type: 'Address' } - }, - bleh: { - inputs: [], - output: { type: 'Address' } - } - }; - - provider = { - subscribe: jest.fn((method, params, _cb) => { - cb = _cb; - - return Promise.resolve(12345); - }), - unsubscribe: jest.fn(() => { - return Promise.resolve(true); - }) - }; - - sub = createSubscribe(provider, 'test', methods); - }); - - it('it does not subscribe to not-found endpoint', () => { - return sub('notFound').catch((error) => { - expect(error.message).toMatch(/Unable to find 'test_notFound' subscription/); - }); - }); - - it('returns the subscription', () => { - return sub('bleh', () => true).then((id) => { - expect(id).toEqual(12345); - }); - }); - - it('returns values as they are available', (done) => { - sub('bleh', () => { - done(); - }); - - cb(null, '0x123'); - }); - - it('returns errors as they are available', (done) => { - sub('bleh', (error) => { - expect(error.message).toEqual('test error'); - - done(); - }); - - cb(new Error('test error')); - }); - - it('checks that valid callback is provided', () => { - return sub('bleh').catch((error) => { - expect(error.message).toMatch(/Expected callback in last position/); - }); - }); -}); diff --git a/packages/api/src/types.js b/packages/api/src/types.js index 2e4bdafbd71b..f7bf5995b167 100644 --- a/packages/api/src/types.js +++ b/packages/api/src/types.js @@ -5,13 +5,13 @@ import type { InterfaceTypes } from '@polkadot/api-jsonrpc/types'; -export type ApiInterface$Section$Method = (...params: Array) => Promise; +export type ApiInterface$Section$Method = { + (...params: Array): Promise; + unsubscribe: (id: number) => Promise; +} export type ApiInterface$Section = { - [string]: ApiInterface$Section$Method, - - subscribe: (name: string, ...params: Array) => Promise, - unsubscribe: (id: number) => Promise + [string]: ApiInterface$Section$Method }; export type ApiInterface = { diff --git a/yarn.lock b/yarn.lock index a4e65bf8a069..3bcfed7a0baf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -209,9 +209,9 @@ deasync "^0.1.12" ip-regex "^2.1.0" -"@polkadot/util@^0.18.2": - version "0.18.2" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-0.18.2.tgz#a00fc2c27e63f9ed1b297eb9109db97234677194" +"@polkadot/util@^0.18.4": + version "0.18.4" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-0.18.4.tgz#b1886a3d4663d302fe1234705f506fae95b371b8" dependencies: babel-runtime "^6.26.0" bn.js "^4.11.8"