Add storage subscription API endpoints (#126)

* Add storage subscriptions

* StorageResult formatting

* Decoding WIP

* Storage subsciptions in place

* Allow null decodes, ref https://github.com/polkadot-js/apps/issues/215

* e2e tests for subscribeStorage

* Cache subscription results, return with changeset
This commit is contained in:
Jaco Greeff
2018-08-11 20:50:57 +02:00
committed by GitHub
parent 4d815ef726
commit 187c66f600
50 changed files with 463 additions and 278 deletions
+1 -1
View File
@@ -84,6 +84,6 @@ export default function mocks ({ emitter, storage, subscriptions }: MockState):
setStorageBn(storage, stateTimestamp.current, Math.floor(Date.now() / 1000));
updateSubs(subscriptions, 'subscribe_newHead', headerEncode(newHead));
updateSubs(subscriptions, 'chain_subscribeNewHead', headerEncode(newHead));
}, 5000);
}
+1 -1
View File
@@ -24,7 +24,7 @@ describe('state', () => {
expect(
state.subscriptions
).toMatchObject({
'subscribe_newHead': {
'chain_subscribeNewHead': {
callbacks: {},
lastValue: null
}
+2 -2
View File
@@ -18,8 +18,8 @@ const SUBSCRIPTIONS: string[] = Array.prototype.concat.apply(
.filter((method) =>
method.isSubscription
)
.map(({ name }) =>
`subscribe_${name}`
.map(({ name, section }) =>
`${section}_${name}`
)
)
);
@@ -19,7 +19,7 @@ describe('subscribe', () => {
});
it('returns a subscription id', () => {
return subscribe(state, 'subscribe_newHead', [() => void 0]).then((id) => {
return subscribe(state, '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, 'subscribe_newHead', [cb]).then((id) => {
expect(state.subscriptionMap[id]).toEqual('subscribe_newHead');
expect(state.subscriptions['subscribe_newHead'].callbacks[id]).toEqual(cb);
return subscribe(state, '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['subscribe_newHead'].lastValue = 'testValue';
state.subscriptions['chain_subscribeNewHead'].lastValue = 'testValue';
subscribe(state, 'subscribe_newHead', [(_, value) => {
subscribe(state, 'chain_subscribeNewHead', [(_, value) => {
expect(value).toEqual('testValue');
done();
}]);
@@ -13,23 +13,23 @@ describe('unsubscribe', () => {
beforeEach(() => {
state = createState();
return subscribe(state, 'subscribe_newHead', [() => void 0]).then((_id) => {
return subscribe(state, 'chain_subscribeNewHead', [() => void 0]).then((_id) => {
id = _id;
});
});
it('fails on unknown ids', () => {
return unsubscribe(state, 'subscribe_newHead', 5).catch((error) => {
return unsubscribe(state, 'chain_subscribeNewHead', 5).catch((error) => {
expect(error.message).toMatch(/Unable to find/);
});
});
it('unsubscribes successfully', () => {
return unsubscribe(state, 'subscribe_newHead', id);
return unsubscribe(state, 'chain_subscribeNewHead', id);
});
it('fails on double unsubscribe', () => {
return unsubscribe(state, 'subscribe_newHead', id)
return unsubscribe(state, '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, 'subscribe_newHead', id).then(() => {
return unsubscribe(state, 'chain_subscribeNewHead', id).then(() => {
expect(state.subscriptionMap[id]).not.toBeDefined();
expect(state.subscriptions['subscribe_newHead'].callbacks[id]).not.toBeDefined();
expect(state.subscriptions['chain_subscribeNewHead'].callbacks[id]).not.toBeDefined();
});
});
});
+59
View File
@@ -0,0 +1,59 @@
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import storage from '@polkadot/storage';
import createApi from '../../api/src';
import Ws from '../src/ws';
describe.skip('e2e', () => {
let api;
beforeEach(() => {
jest.setTimeout(30000);
api = createApi(new Ws('ws://127.0.0.1:9944'));
});
it('subscribes to chain_subscribeNewHead', (done) => {
let count = 0;
// tslint:disable-next-line
api.chain
.subscribeNewHead((error, data) => {
if (error) {
return done(error);
}
expect(data).toBeDefined();
if (++count === 3) {
done();
}
})
.then((subscriptionId) => {
console.log('newHead: subscriptionId =', subscriptionId);
});
});
it('subscribes to storage', (done) => {
api.state
.subscribeStorage([
[storage.staking.public.freeBalanceOf, '5Ejbye9R8ygByQPrDSasaUid1munedPZUmg4f118HGmtodGp'],
[storage.staking.public.freeBalanceOf, '5DTestUPts3kjeXSTMyerHihn1uwMfLj8vU8sqF7qYrFacT7']
], (error, data) => {
if (error) {
return done(error);
}
expect(data).toHaveLength(2);
expect(data[0].toNumber()).toEqual(0);
expect(data[1].toNumber()).not.toEqual(0);
done();
})
.then((subscriptionId) => {
console.log('newHead: subscriptionId =', subscriptionId);
});
});
});
-36
View File
@@ -1,36 +0,0 @@
// Copyright 2017-2018 @polkadot/api-provider authors & contributors
// This software may be modified and distributed under the terms
// of the ISC license. See the LICENSE file for details.
import { ApiInterface } from '../../api/src/types';
import createApi from '../../api/src';
import Ws from '../src/ws';
describe.skip('e2e', () => {
let api: ApiInterface;
beforeEach(() => {
jest.setTimeout(30000);
api = createApi(new Ws('ws://127.0.0.1:9944'));
});
it('subscribes to client_newHead', (done) => {
let count = 0;
// tslint:disable-next-line
api.chain
.newHead((error: Error | null, data: any) => {
console.log('newHead: result =', [error, data]);
expect(data).toBeDefined();
if (++count === 3) {
done();
}
})
.then((subscriptionId) => {
console.log('newHead: subscriptionId =', subscriptionId);
});
});
});