Update subscription naming handling (#266)

This commit is contained in:
Jaco Greeff
2018-10-12 15:39:00 +02:00
committed by Amaury Martiny
parent 1a4d60efcb
commit 78101e1292
28 changed files with 90 additions and 95 deletions
+1 -1
View File
@@ -120,7 +120,7 @@ export default class ApiBase {
let observable;
try {
observable = this._api.state.storage(keys);
observable = this._api.state.subscribeStorage(keys);
} catch (error) {
observable = EMPTY;
}
+3 -3
View File
@@ -12,7 +12,7 @@ import ApiQueries from './Queries';
export default class ApiCalls extends ApiQueries {
bestNumber = (): Observable<BlockNumber | undefined> => {
return this
.newHead()
.subscribeNewHead()
.pipe(
map((header?: Header): BlockNumber | undefined =>
header && header.blockNumber
@@ -30,8 +30,8 @@ export default class ApiCalls extends ApiQueries {
return this._api.chain.getBlock(hash);
}
newHead = (): Observable<Header | undefined> => {
return this._api.chain.newHead().pipe(
subscribeNewHead = (): Observable<Header | undefined> => {
return this._api.chain.subscribeNewHead().pipe(
defaultIfEmpty()
);
}
+2 -2
View File
@@ -40,7 +40,7 @@ import { ApiPromise } from '@polkadot/api';
const api = await ApiPromise.create();
// make a call to retrieve the current network head
api.rpc.chain.newHead((header) => {
api.rpc.chain.subscribeNewHead((header) => {
console.log(`Chain is at #${header.blockNumber}`);
});
```
@@ -54,7 +54,7 @@ import { ApiRx } from '@polkadot/api';
const api = await ApiRx.create().toPromise();
// make a call to retrieve the current network head
api.rpc.chain.newHead().subscribe((header) => {
api.rpc.chain.subscribeNewHead().subscribe((header) => {
console.log(`Chain is at #${header.blockNumber}`);
});
```
+2 -2
View File
@@ -51,7 +51,7 @@ export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S,
* import Api from '@polkadot/api/rx';
*
* new Api().isReady.subscribe((api) => {
* api.rpc.newHead().subscribe((header) => {
* api.rpc.subscribeNewHead().subscribe((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* });
@@ -102,7 +102,7 @@ export default abstract class ApiBase<R, S, E> implements ApiBaseInterface<R, S,
*
* ```javascript
* api.rpc.chain
* .newHead()
* .subscribeNewHead()
* .subscribe((header) => {
* console.log('new header', header);
* });
@@ -19,7 +19,7 @@ export default class SubmittableExtrinsic extends Extrinsic {
send (statusCb?: (status: ExtrinsicStatus) => any): Promise<Hash> {
if (status) {
return this._api.rpc.author.extrinsicUpdate(this, statusCb);
return this._api.rpc.author.submitAndWatchExtrinsic(this, statusCb);
}
return this._api.rpc.author.submitExtrinsic(this);
+3 -3
View File
@@ -41,7 +41,7 @@ import { StorageFunction } from '@polkadot/types/StorageKey';
* const api = await ApiPromise.create();
*
* // make a subscription to the network head
* api.rpc.chain.newHead((header) => {
* api.rpc.chain.subscribeNewHead((header) => {
* console.log(`Chain is at #${header.blockNumber}`);
* });
* ```
@@ -138,7 +138,7 @@ export default class ApiPromise extends ApiBase<Rpc, QueryableStorage, Submittab
* import Api from '@polkadot/api/promise';
*
* new Api().isReady.then((api) => {
* api.rpc.newHead((header) => {
* api.rpc.subscribeNewHead((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* });
@@ -206,7 +206,7 @@ export default class ApiPromise extends ApiBase<Rpc, QueryableStorage, Submittab
return this.rpc.state.getStorage([method, args[0]]);
}
return this.rpc.state.storage(
return this.rpc.state.subscribeStorage(
[[method, args.length === 1 ? undefined : args[0]]],
(result: Array<Base | null | undefined> = []) =>
args[args.length - 1](result[0])
+1 -1
View File
@@ -19,7 +19,7 @@ export default class SubmittableExtrinsic extends Extrinsic {
}
send (): Observable<ExtrinsicStatus> {
return this._api.rpc.author.extrinsicUpdate(this);
return this._api.rpc.author.submitAndWatchExtrinsic(this);
}
sign (signerPair: KeyringPair, nonce: AnyNumber, blockHash?: AnyU8a): SubmittableExtrinsic {
+3 -3
View File
@@ -46,7 +46,7 @@ const l = logger('api-rx');
* const api = await ApiRx.create().toPromise();
*
* // make a call to retrieve the current network head
* api.rpc.chain.newHead().subscribe((header) => {
* api.rpc.chain.subscribeNewHead().subscribe((header) => {
* console.log(`Chain is at #${header.blockNumber}`);
* });
* ```
@@ -154,7 +154,7 @@ export default class ApiRx extends ApiBase<RpcRx, QueryableStorage, SubmittableE
* import Api from '@polkadot/api/rx';
*
* new Api().isReady.subscribe((api) => {
* api.rpc.newHead().subscribe((header) => {
* api.rpc.subscribeNewHead().subscribe((header) => {
* console.log(`new block #${header.blockNumber.toNumber()}`);
* });
* });
@@ -231,7 +231,7 @@ export default class ApiRx extends ApiBase<RpcRx, QueryableStorage, SubmittableE
let observable;
try {
observable = this.rpc.state.storage([[method, arg]]);
observable = this.rpc.state.subscribeStorage([[method, arg]]);
} catch (error) {
// in the case of an exception (upon creation of key), just return an empty
observable = EMPTY;
@@ -35,7 +35,7 @@ describe.skip('e2e queries', () => {
});
it('subscribes to queries', (done) => {
api.rpc.chain.newHead((error, header) => {
api.rpc.chain.subscribeNewHead((error, header) => {
expect(error).toBe(null);
expect(header.blockNumber.isZero()).toBe(false);
+3 -7
View File
@@ -53,17 +53,13 @@ Retrieving best header via subscription -
```js
api.chain
.newHead((error, header) => {
if (error) {
console.error('error:', error);
}
console.log(`best #${header.blockNumber.toString()}`);
.subscribeNewHead((header) => {
console.log(`best #${header.blockNumber}`);
})
.then((subscriptionId) => {
console.log(`subscriptionId: ${subscriptionId}`);
// id for the subscription, can unsubscribe via
// api.chain.newHead.unsubscribe(subscriptionId);
// api.chain.subscribeNewHead.unsubscribe(subscriptionId);
})
.catch((error) => {
console.error('error subscribing:', error);
+1 -1
View File
@@ -52,7 +52,7 @@ describe('formatting', () => {
it('encodes multiple keys, decoding multiple results', (done) => {
api.state
.storage(
.subscribeStorage(
[
[storage.balances.freeBalance, ADDR_ONE],
[storage.balances.freeBalance, ADDR_TWO]
+14 -7
View File
@@ -87,18 +87,20 @@ export default class Rpc implements RpcInterface {
return Object
.keys(methods)
.reduce((exposed, method) => {
const rpcName = `${section}_${method}`;
const def = methods[method];
exposed[method] = def.isSubscription
? this.createMethodSubscribe(rpcName, def)
: this.createMethodSend(rpcName, def);
? this.createMethodSubscribe(def)
: this.createMethodSend(def);
return exposed;
}, {} as RpcInterface$Section);
}
private createMethodSend (rpcName: string, method: RpcMethod): RpcInterface$Section$Method {
private createMethodSend (method: RpcMethod): RpcInterface$Section$Method {
const rpcName = `${method.section}_${method.method}`;
const call = async (...values: Array<any>): Promise<any> => {
// TODO Warn on deprecated methods
try {
@@ -119,9 +121,14 @@ export default class Rpc implements RpcInterface {
return call as RpcInterface$Section$Method;
}
private createMethodSubscribe (rpcName: string, method: RpcMethod): RpcInterface$Section$Method {
private createMethodSubscribe (method: RpcMethod): RpcInterface$Section$Method {
const [updateType, subMethod, unsubMethod] = method.pubsub;
const subName = `${method.section}_${subMethod}`;
const unsubName = `${method.section}_${unsubMethod}`;
const subscriptionType = `${method.section}_${updateType}`;
const unsubscribe = (subscriptionId: any): Promise<any> =>
this._provider.unsubscribe(rpcName, method.subscribe[1], subscriptionId);
this._provider.unsubscribe(subscriptionType, unsubName, subscriptionId);
const _call = async (...values: Array<any>): Promise<any> => {
try {
const cb: ProviderInterface$Callback = values.pop();
@@ -139,7 +146,7 @@ export default class Rpc implements RpcInterface {
cb(this.formatOutput(method, params, result));
};
return this._provider.subscribe(rpcName, method.subscribe[0], paramsJson, update);
return this._provider.subscribe(subscriptionType, subName, paramsJson, update);
} catch (error) {
const message = `${Rpc.signature(method)}:: ${error.message}`;
+5 -3
View File
@@ -16,11 +16,13 @@ describe('methodSend', () => {
params: [
{ name: 'foo', type: 'Bytes' }
],
section: 'test',
type: 'Bytes'
},
bleh: {
method: 'bleh',
params: [],
section: 'test',
type: 'Bytes'
}
};
@@ -35,7 +37,7 @@ describe('methodSend', () => {
});
it('wraps errors with the call signature', () => {
const method = api.createMethodSend('test_blah', methods.blah);
const method = api.createMethodSend(methods.blah);
return method().catch((error) => {
expect(error.message).toMatch(/blah \(foo: Bytes\): Bytes/);
@@ -43,7 +45,7 @@ describe('methodSend', () => {
});
it('checks for mismatched parameters', () => {
const method = api.createMethodSend('test_bleh', methods.bleh);
const method = api.createMethodSend(methods.bleh);
return method(1).catch((error) => {
expect(error.message).toMatch(/parameters, 1 found instead/);
@@ -51,7 +53,7 @@ describe('methodSend', () => {
});
it('calls the provider with the correct parameters', () => {
const method = api.createMethodSend('test_blah', methods.blah);
const method = api.createMethodSend(methods.blah);
// Args are length-prefixed, because it's a Bytes
return method(new Uint8Array([2 << 2, 0x12, 0x34])).then(() => {
+2 -6
View File
@@ -14,16 +14,12 @@ describe.skip('e2e chain', () => {
api = new Rpc(new Ws('ws://127.0.0.1:9944'));
});
it('subscribes to chain_newHead', (done) => {
it('subscribes via subscribeNewHead', (done) => {
let count = 0;
// tslint:disable-next-line
api.chain
.newHead((error, header) => {
if (error) {
return done(error);
}
.subscribeNewHead((header) => {
expect(header).toBeDefined();
if (++count === 3) {
@@ -22,11 +22,7 @@ describe.skip('e2e krumme lanke', () => {
[storage.system.accountNonce, '5Ejbye9R8ygByQPrDSasaUid1munedPZUmg4f118HGmtodGp'],
[storage.balances.freeBalance, '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7']
],
(error, data) => {
if (error) {
return done(error);
}
(data) => {
expect(data).toHaveLength(2);
expect(data[0].toNumber()).toEqual(0);
expect(data[1].toNumber()).not.toEqual(0);
@@ -35,7 +31,7 @@ describe.skip('e2e krumme lanke', () => {
}
)
.then((subscriptionId) => {
console.log('newHead: subscriptionId =', subscriptionId);
console.log('stoarge subscriptionId =', subscriptionId);
});
});
+1 -5
View File
@@ -19,11 +19,7 @@ describe.skip('e2e subscriptions', () => {
let count = 0;
return api.state
.storage([[storage.timestamp.now]], (error, data) => {
if (error) {
return done(error);
}
.subscribeStorage([[storage.timestamp.now]], (data) => {
expect(data).toBeDefined();
if (++count === 3) {
+1 -1
View File
@@ -79,6 +79,6 @@ export default function mocks ({ emitter, db, subscriptions }: MockState): void
setStorageBn(db, storage.timestamp.now, Math.floor(Date.now() / 1000));
updateSubs(subscriptions, 'chain_newHead', new Header(newHead).toJSON());
updateSubs(subscriptions, 'chain_subscribeNewHead', new Header(newHead).toJSON());
}, 5000);
}
+1 -1
View File
@@ -24,7 +24,7 @@ describe('state', () => {
expect(
state.subscriptions
).toMatchObject({
'chain_newHead': {
'chain_subscribeNewHead': {
callbacks: {},
lastValue: null
}
@@ -19,7 +19,7 @@ describe('subscribe', () => {
});
it('returns a subscription id', () => {
return subscribe(state, 'chain_newHead', 'chain_newHead', [() => void 0]).then((id) => {
return subscribe(state, 'chain_newHead', 'chain_subscribeNewHead', [() => void 0]).then((id) => {
expect(id).toEqual(state.subscriptionId);
});
});
@@ -27,16 +27,16 @@ describe('subscribe', () => {
it('stores the mapping values', () => {
const cb = () => void 0;
return subscribe(state, 'chain_newHead', 'chain_newHead', [cb]).then((id) => {
expect(state.subscriptionMap[id]).toEqual('chain_newHead');
expect(state.subscriptions['chain_newHead'].callbacks[id]).toEqual(cb);
return subscribe(state, 'chain_newHead', 'chain_subscribeNewHead', [cb]).then((id) => {
expect(state.subscriptionMap[id]).toEqual('chain_subscribeNewHead');
expect(state.subscriptions['chain_subscribeNewHead'].callbacks[id]).toEqual(cb);
});
});
it('calls back with the last known value', (done) => {
state.subscriptions['chain_newHead'].lastValue = 'testValue';
state.subscriptions['chain_subscribeNewHead'].lastValue = 'testValue';
subscribe(state, 'chain_newHead', 'chain_newHead', [(_, value) => {
subscribe(state, 'chain_newHead', 'chain_subscribeNewHead', [(_, value) => {
expect(value).toEqual('testValue');
done();
}]);
@@ -13,23 +13,23 @@ describe('unsubscribe', () => {
beforeEach(() => {
state = createState();
return subscribe(state, 'chain_newHead', 'chain_newHead', [() => void 0]).then((_id) => {
return subscribe(state, 'chain_newHead', 'chain_subscribeNewHead', [() => void 0]).then((_id) => {
id = _id;
});
});
it('fails on unknown ids', () => {
return unsubscribe(state, 'chain_newHead', 'chain_newHead', 5).catch((error) => {
return unsubscribe(state, 'chain_newHead', 'chain_subscribeNewHead', 5).catch((error) => {
expect(error.message).toMatch(/Unable to find/);
});
});
it('unsubscribes successfully', () => {
return unsubscribe(state, 'chain_newHead', 'chain_newHead', id);
return unsubscribe(state, 'chain_newHead', 'chain_subscribeNewHead', id);
});
it('fails on double unsubscribe', () => {
return unsubscribe(state, 'chain_newHead', 'chain_newHead', id)
return unsubscribe(state, 'chain_newHead', 'chain_subscribeNewHead', id)
.then(() => unsubscribe(state, id))
.catch((error) => {
expect(error.message).toMatch(/Unable to find/);
@@ -37,9 +37,9 @@ describe('unsubscribe', () => {
});
it('clears the subscriptions', () => {
return unsubscribe(state, 'chain_newHead', 'chain_newHead', id).then(() => {
return unsubscribe(state, 'chain_newHead', 'chain_subscribeNewHead', id).then(() => {
expect(state.subscriptionMap[id]).not.toBeDefined();
expect(state.subscriptions['chain_newHead'].callbacks[id]).not.toBeDefined();
expect(state.subscriptions['chain_subscribeNewHead'].callbacks[id]).not.toBeDefined();
});
});
});
+2 -2
View File
@@ -186,9 +186,9 @@ export default class WsProvider implements WSProviderInterface {
*
* ```javascript
* const provider = new WsProvider('ws://127.0.0.1:9944');
* const api = new Rpc(provider);
* const rpc = new Rpc(provider);
*
* api.state.storage([[storage.balances.freeBalance, <Address>]], (_, values) => {
* rpc.state.subscribeStorage([[storage.balances.freeBalance, <Address>]], (_, values) => {
* console.log(values)
* }).then((subscriptionId) => {
* console.log('balance changes subscription id: ', subscriptionId)
+2 -2
View File
@@ -20,7 +20,7 @@ describe.skip('e2e subscriptions', () => {
let count = 0;
api.state
.storage([[storage.timestamp.now]])
.subscribeStorage([[storage.timestamp.now]])
.subscribe((data) => {
console.error('timestamp.now', data);
@@ -32,7 +32,7 @@ describe.skip('e2e subscriptions', () => {
it('retrieves multiple values', (done) => {
api.state
.storage([
.subscribeStorage([
[storage.timestamp.now],
[storage.session.sessionLength],
[storage.staking.sessionsPerEra]
@@ -45,7 +45,7 @@ describe.skip('e2e transfer', () => {
extrinsic.sign(keyring.alice, 0, genesisHash);
return api.author.extrinsicUpdate(extrinsic, (status) => {
return api.author.submitAndWatchExtrinsic(extrinsic, (status) => {
console.log(status);
if (status.type === 'Finalised') {
+7 -7
View File
@@ -22,17 +22,17 @@ const submitExtrinsic: RpcMethodOpt = {
type: 'Hash'
};
const extrinsicUpdate: RpcMethodOpt = {
const submitAndWatchExtrinsic: RpcMethodOpt = {
description: 'Subscribe and watch an extrinsic until unsubscribed',
isSigned: true,
isSubscription: true,
subscribe: [
'author_submitAndWatchExtrinsic',
'author_unwatchExtrinsic'
],
params: [
createParam('extinsic', 'Extrinsic')
],
pubsub: [
'extrinsicUpdate',
'submitAndWatchExtrinsic',
'unwatchExtrinsic'
],
type: 'ExtrinsicStatus'
};
@@ -49,6 +49,6 @@ export default {
methods: {
pendingExtrinsics: createMethod(section, 'pendingExtrinsics', pendingExtrinsics),
submitExtrinsic: createMethod(section, 'submitExtrinsic', submitExtrinsic),
extrinsicUpdate: createMethod(section, 'extrinsicUpdate', extrinsicUpdate)
submitAndWatchExtrinsic: createMethod(section, 'submitAndWatchExtrinsic', submitAndWatchExtrinsic)
}
} as RpcSection;
+7 -6
View File
@@ -47,13 +47,14 @@ const getRuntimeVersion: RpcMethodOpt = {
type: 'RuntimeVersion'
};
const newHead: RpcMethodOpt = {
const subscribeNewHead: RpcMethodOpt = {
description: 'Retrieves the best header via subscription',
subscribe: [
'chain_subscribeNewHead',
'chain_unsubscribeNewHead'
],
params: [],
pubsub: [
'newHead',
'subscribeNewHead',
'unsubscribeNewHead'
],
type: 'Header'
};
@@ -73,6 +74,6 @@ export default {
getHead: createMethod(section, 'getHead', getHead),
getHeader: createMethod(section, 'getHeader', getHeader),
getRuntimeVersion: createMethod(section, 'getRuntimeVersion', getRuntimeVersion),
newHead: createMethod(section, 'newHead', newHead)
subscribeNewHead: createMethod(section, 'subscribeNewHead', subscribeNewHead)
}
} as RpcSection;
+3 -3
View File
@@ -6,17 +6,17 @@ import { RpcMethodOpt, RpcMethod } from '../types';
import isUndefined from '@polkadot/util/is/undefined';
export default function createMethod (section: string, method: string, { description, params, type, isDeprecated = false, isHidden = false, isSigned = false, subscribe }: RpcMethodOpt): RpcMethod {
export default function createMethod (section: string, method: string, { description, isDeprecated = false, isHidden = false, isSigned = false, params, pubsub, type }: RpcMethodOpt): RpcMethod {
return {
description,
isDeprecated,
isHidden,
isSigned,
isSubscription: !isUndefined(subscribe),
isSubscription: !isUndefined(pubsub),
method,
params,
pubsub: pubsub || ['', '', ''],
section,
subscribe: subscribe || ['', ''],
type
};
}
+7 -6
View File
@@ -99,16 +99,17 @@ const queryStorage: RpcMethodOpt = {
type: 'Vec<StorageChangeSet>'
};
const storage: RpcMethodOpt = {
const subscribeStorage: RpcMethodOpt = {
description: 'Subscribes to storage changes for the provided keys',
subscribe: [
'state_subscribeStorage',
'state_unsubscribeStorage'
],
params: [
// @ts-ignore The Vec<> wrap is fine
createParam('keys', 'Vec<StorageKey>')
],
pubsub: [
'storage',
'subscribeStorage',
'unsubscribeStorage'
],
type: 'StorageChangeSet'
};
@@ -133,6 +134,6 @@ export default {
getStorageSize: createMethod(section, 'getStorageSize', getStorageSize),
getStorageSizeAt: createMethod(section, 'getStorageSizeAt', getStorageSizeAt),
queryStorage: createMethod(section, 'queryStorage', queryStorage),
storage: createMethod(section, 'storage', storage)
subscribeStorage: createMethod(section, 'subscribeStorage', subscribeStorage)
}
} as RpcSection;
+2 -2
View File
@@ -17,7 +17,7 @@ export type RpcMethodOpt = {
isSigned?: boolean,
isSubscription?: boolean,
params: Array<RpcParam>,
subscribe?: [string, string],
pubsub?: [string, string, string],
type: CodecTypes
};
@@ -30,8 +30,8 @@ export type RpcMethod = {
isSubscription: boolean,
method: string,
params: Array<RpcParam>,
pubsub: [string, string, string],
section: string,
subscribe: [string, string],
type: CodecTypes
};