Skip to content

Commit

Permalink
API Subscriptions (#62)
Browse files Browse the repository at this point in the history
* Upgrades

* Add getHead & newHead

* Handle pubsub interfaces

* Flow fixes
  • Loading branch information
jacogr authored Apr 16, 2018
1 parent 409db09 commit 68edc80
Show file tree
Hide file tree
Showing 16 changed files with 101 additions and 137 deletions.
2 changes: 1 addition & 1 deletion packages/api-format/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
20 changes: 20 additions & 0 deletions packages/api-jsonrpc/src/chain/getHead.js
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 5 additions & 1 deletion packages/api-jsonrpc/src/chain/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
21 changes: 21 additions & 0 deletions packages/api-jsonrpc/src/chain/newHead.js
Original file line number Diff line number Diff line change
@@ -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);
10 changes: 8 additions & 2 deletions packages/api-jsonrpc/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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<InterfaceInputType>,
output: InterfaceOutputType
};
Expand Down
2 changes: 1 addition & 1 deletion packages/api-provider/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
2 changes: 1 addition & 1 deletion packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
14 changes: 7 additions & 7 deletions packages/api/src/create/interface.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<ApiInterface$Section> = {};
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);
Expand Down
7 changes: 6 additions & 1 deletion packages/api/src/create/interface.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ jest.mock('./@polkadot/api-jsonrpc', () => ({
bleh: {
inputs: [],
output: { type: 'Address' }
},
pubsub: {
isSubscription: true,
inputs: [],
output: { type: 'Address' }
}
}
}
Expand All @@ -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']
);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<mixed>) => Promise<mixed>;

const createParams = require('./params');

module.exports = function createMethod (provider: ProviderInterface, rpcName: string, { inputs, output }: InterfaceMethodDefinition): Method {
return async (..._params: Array<mixed>): Promise<mixed> => {
module.exports = function createMethodSend (provider: ProviderInterface, rpcName: string, name: string, { inputs, output }: InterfaceMethodDefinition): ApiInterface$Section$Method {
const call = async (..._params: Array<mixed>): Promise<mixed> => {
// TODO: Deprecated warning
try {
const params = createParams(rpcName, _params, inputs);
const result = await provider.send(rpcName, params);
Expand All @@ -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);
};
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -30,23 +30,23 @@ 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/);
});
});

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/);
});
});

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']);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,20 @@

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');
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<mixed>) => Promise<number>;

const createParams = require('./params');

module.exports = function createSubscribeMethod (provider: ProviderInterface, rpcName: string, { inputs, output }: InterfaceMethodDefinition): Method {
return async (..._params: Array<mixed>): Promise<number> => {
module.exports = function methodSubscribe (provider: ProviderInterface, rpcName: string, name: string, { inputs, output }: InterfaceMethodDefinition): ApiInterface$Section$Method {
const unsubscribe = (subscriptionId: mixed): Promise<mixed> =>
provider.send(`unsubscribe_${name}`, [subscriptionId]);
const call = async (..._params: Array<mixed>): Promise<mixed> => {
try {
// flowlint-next-line unclear-type:off
const cb = ((_params.pop(): any): ProviderInterface$Callback);
Expand All @@ -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 {
Expand All @@ -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);
};
25 changes: 0 additions & 25 deletions packages/api/src/create/subscribe.js

This file was deleted.

76 changes: 0 additions & 76 deletions packages/api/src/create/subscribe.spec.js

This file was deleted.

Loading

0 comments on commit 68edc80

Please sign in to comment.