Pubsub for postMessage Provider (UI2)
Proper method-name displaying on request approval is missing
This commit is contained in:
parent
36f94fde05
commit
13b96ae7c8
@ -17,8 +17,8 @@
|
|||||||
import EventEmitter from 'eventemitter3';
|
import EventEmitter from 'eventemitter3';
|
||||||
|
|
||||||
import Contract from './contract';
|
import Contract from './contract';
|
||||||
import { PromiseProvider, Http as HttpProvider, PostMessage as PostMessageProvider, WsSecure as WsSecureProvider } from './provider';
|
import { PromiseProvider, Http as HttpProvider, PostMessage as PostMessageProvider, Ws as WsProvider } from './provider';
|
||||||
import { Http as HttpTransport, WsSecure as WsSecureTransport } from './transport';
|
import { Http as HttpTransport, Ws as WsTransport } from './transport';
|
||||||
|
|
||||||
import { Db, Eth, Parity, Net, Personal, Shell, Shh, Signer, Trace, Web3 } from './rpc';
|
import { Db, Eth, Parity, Net, Personal, Shell, Shh, Signer, Trace, Web3 } from './rpc';
|
||||||
import Subscriptions from './subscriptions';
|
import Subscriptions from './subscriptions';
|
||||||
@ -36,7 +36,6 @@ export default class Api extends EventEmitter {
|
|||||||
console.log(provider);
|
console.log(provider);
|
||||||
console.warn(new Error('deprecated: Api needs provider with send() function, old-style Transport found instead'));
|
console.warn(new Error('deprecated: Api needs provider with send() function, old-style Transport found instead'));
|
||||||
}
|
}
|
||||||
|
|
||||||
// does use new provider interface (not promiseProvider)
|
// does use new provider interface (not promiseProvider)
|
||||||
if (provider && isFunction(provider.subscribe)) {
|
if (provider && isFunction(provider.subscribe)) {
|
||||||
this._pubsub = new Pubsub(provider);
|
this._pubsub = new Pubsub(provider);
|
||||||
@ -187,12 +186,12 @@ export default class Api extends EventEmitter {
|
|||||||
static Provider = {
|
static Provider = {
|
||||||
Http: HttpProvider,
|
Http: HttpProvider,
|
||||||
PostMessage: PostMessageProvider,
|
PostMessage: PostMessageProvider,
|
||||||
WsSecure: WsSecureProvider
|
Ws: WsProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: kept for backwards compatibility
|
// NOTE: kept for backwards compatibility
|
||||||
static Transport = {
|
static Transport = {
|
||||||
Http: HttpTransport,
|
Http: HttpTransport,
|
||||||
WsSecure: WsSecureTransport
|
Ws: WsTransport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,4 @@ export PromiseProvider from './promise';
|
|||||||
|
|
||||||
export Http from './http';
|
export Http from './http';
|
||||||
export PostMessage from './postMessage';
|
export PostMessage from './postMessage';
|
||||||
export WsSecure from './wsSecure';
|
export Ws from './ws';
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
export default class PostMessage {
|
export default class PostMessage {
|
||||||
id = 0;
|
id = 0;
|
||||||
_callbacks = {};
|
_messages = {};
|
||||||
|
|
||||||
constructor (token, destination) {
|
constructor (token, destination) {
|
||||||
this._token = token;
|
this._token = token;
|
||||||
@ -28,17 +28,55 @@ export default class PostMessage {
|
|||||||
addMiddleware () {
|
addMiddleware () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_send (data) {
|
||||||
|
this._destination.postMessage(data, '*');
|
||||||
|
}
|
||||||
|
|
||||||
send = (method, params, callback) => {
|
send = (method, params, callback) => {
|
||||||
const id = ++this.id;
|
const id = ++this.id;
|
||||||
|
|
||||||
this._callbacks[id] = callback;
|
this._messages[id] = { callback };
|
||||||
this._destination.postMessage({
|
this._send({
|
||||||
id,
|
id,
|
||||||
from: this._token,
|
from: this._token,
|
||||||
method,
|
method,
|
||||||
params,
|
params,
|
||||||
token: this._token
|
token: this._token
|
||||||
}, '*');
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
subscribe = (api, callback, params) => {
|
||||||
|
console.log('paritySubscribe', JSON.stringify(params), api, callback);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = ++this.id;
|
||||||
|
|
||||||
|
this._messages[id] = { callback, resolve, reject, subscription: true, initial: true };
|
||||||
|
this._send({
|
||||||
|
id,
|
||||||
|
from: this._token,
|
||||||
|
api,
|
||||||
|
params,
|
||||||
|
token: this._token
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribe = (subId) => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const id = ++this.id;
|
||||||
|
|
||||||
|
this._messages[id] = { callback: (e, v) => e ? reject(e) : resolve(v) };
|
||||||
|
this._send({
|
||||||
|
id,
|
||||||
|
from: this._token,
|
||||||
|
subId,
|
||||||
|
token: this._token
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
unsubscribeAll () {
|
||||||
|
return this.unsubscribe('*');
|
||||||
}
|
}
|
||||||
|
|
||||||
receiveMessage = ({ data: { id, error, from, token, result }, origin, source }) => {
|
receiveMessage = ({ data: { id, error, from, token, result }, origin, source }) => {
|
||||||
@ -50,7 +88,13 @@ export default class PostMessage {
|
|||||||
console.error(from, error);
|
console.error(from, error);
|
||||||
}
|
}
|
||||||
|
|
||||||
this._callbacks[id](error && new Error(error), result);
|
if (this._messages[id].subscription) {
|
||||||
this._callbacks[id] = null;
|
console.log('subscription', result, 'initial?', this._messages[id].initial);
|
||||||
|
this._messages[id].initial ? this._messages[id].resolve(result) : this._messages[id].callback(error && new Error(error), result);
|
||||||
|
this._messages[id].initial = false;
|
||||||
|
} else {
|
||||||
|
this._messages[id].callback(error && new Error(error), result);
|
||||||
|
this._messages[id] = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { WsSecure as Transport } from '../transport';
|
import { Ws as Transport } from '../transport';
|
||||||
|
|
||||||
export default class WsSecure extends Transport {
|
export default class Ws extends Transport {
|
||||||
send = (method, params, callback) => {
|
send = (method, params, callback) => {
|
||||||
this
|
this
|
||||||
._execute(method, params)
|
._execute(method, params)
|
@ -20,11 +20,11 @@ export default class PubsubBase {
|
|||||||
this._provider = provider;
|
this._provider = provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
addListener (module, eventName, callback, eventParams) {
|
addListener (module, eventName, handler, eventParams) {
|
||||||
return eventParams
|
return eventParams
|
||||||
? this._provider.subscribe(module, callback, [eventName, eventParams])
|
? this._provider.subscribe(module, handler, [eventName, eventParams])
|
||||||
: this._provider.subscribe(module, callback, [eventName, []]);
|
: this._provider.subscribe(module, handler, [eventName, []]);
|
||||||
// this._transport.subscribe(module, callback, eventName); After Patch from tomac is merged to master! => eth_subscribe does not support empty array as params
|
// this._transport.subscribe(module, handler, eventName); After Patch from tomac is merged to master! => eth_subscribe does not support empty array as params
|
||||||
}
|
}
|
||||||
|
|
||||||
removeListener (subscriptionIds) {
|
removeListener (subscriptionIds) {
|
||||||
|
@ -29,8 +29,8 @@ export default class Trace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
call (options, whatTrace = ['trace'], blockNumber = 'latest') {
|
call (options, whatTrace = ['trace'], blockNumber = 'latest') {
|
||||||
return this._transport
|
return this._provider
|
||||||
.execute('trace_call', inOptions(options), inTraceType(whatTrace), inBlockNumber(blockNumber))
|
.send('trace_call', inOptions(options), inTraceType(whatTrace), inBlockNumber(blockNumber))
|
||||||
.then(outTraceReplay);
|
.then(outTraceReplay);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -15,6 +15,6 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export Http from './http';
|
export Http from './http';
|
||||||
export WsSecure from './wsSecure';
|
export Ws from './ws';
|
||||||
export TransportError from './error';
|
export TransportError from './error';
|
||||||
export Middleware from './middleware';
|
export Middleware from './middleware';
|
||||||
|
@ -14,4 +14,4 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
export default from './wsSecure';
|
export default from './ws';
|
@ -14,9 +14,9 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import WsSecure from './wsSecure';
|
import Ws from './ws';
|
||||||
|
|
||||||
const ws = new WsSecure('ws://localhost:8546/');
|
const ws = new Ws('ws://localhost:8546/');
|
||||||
|
|
||||||
describe('transport/WsSecure', () => {
|
describe('transport/WsSecure', () => {
|
||||||
it('connects and makes a call to web3_clientVersion', () => {
|
it('connects and makes a call to web3_clientVersion', () => {
|
@ -21,7 +21,7 @@ import JsonRpcBase from '../jsonRpcBase';
|
|||||||
import TransportError from '../error';
|
import TransportError from '../error';
|
||||||
|
|
||||||
/* global WebSocket */
|
/* global WebSocket */
|
||||||
export default class WsSecure extends JsonRpcBase {
|
export default class Ws extends JsonRpcBase {
|
||||||
// token is optional (secure API)
|
// token is optional (secure API)
|
||||||
constructor (url, token = null, autoconnect = true) {
|
constructor (url, token = null, autoconnect = true) {
|
||||||
super();
|
super();
|
||||||
@ -321,6 +321,16 @@ export default class WsSecure extends JsonRpcBase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsubscribeAll () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var unsubscribed = 0;
|
||||||
|
let keys = Object.keys(this._messages);
|
||||||
|
|
||||||
|
keys.forEach(i => this._messages[i].subscription ? this.unsubscribe(this._messages[i].subId).then(_ => unsubscribed++, reject) : null);
|
||||||
|
resolve(unsubscribed);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
unsubscribe (messageId) {
|
unsubscribe (messageId) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const id = this.id;
|
const id = this.id;
|
@ -15,16 +15,16 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import { TEST_WS_URL, mockWs } from '../../../../test/mockRpc';
|
import { TEST_WS_URL, mockWs } from '../../../../test/mockRpc';
|
||||||
import WsSecure from './wsSecure';
|
import Ws from './ws';
|
||||||
|
|
||||||
describe('api/transport/WsSecure', () => {
|
describe('api/transport/ws', () => {
|
||||||
let transport;
|
let transport;
|
||||||
let scope;
|
let scope;
|
||||||
|
|
||||||
describe('transport emitter', () => {
|
describe('transport emitter', () => {
|
||||||
const connect = () => {
|
const connect = () => {
|
||||||
const scope = mockWs();
|
const scope = mockWs();
|
||||||
const transport = new WsSecure(TEST_WS_URL);
|
const transport = new Ws(TEST_WS_URL);
|
||||||
|
|
||||||
return { transport, scope };
|
return { transport, scope };
|
||||||
};
|
};
|
||||||
@ -57,7 +57,7 @@ describe('api/transport/WsSecure', () => {
|
|||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
scope = mockWs([{ method: 'test_anyCall', reply: 'TestResult' }]);
|
scope = mockWs([{ method: 'test_anyCall', reply: 'TestResult' }]);
|
||||||
transport = new WsSecure(TEST_WS_URL);
|
transport = new Ws(TEST_WS_URL);
|
||||||
|
|
||||||
return transport
|
return transport
|
||||||
.execute('test_anyCall', [1, 2, 3])
|
.execute('test_anyCall', [1, 2, 3])
|
||||||
@ -98,7 +98,7 @@ describe('api/transport/WsSecure', () => {
|
|||||||
describe('errors', () => {
|
describe('errors', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
scope = mockWs([{ method: 'test_anyCall', reply: { error: { code: 1, message: 'TestError' } } }]);
|
scope = mockWs([{ method: 'test_anyCall', reply: { error: { code: 1, message: 'TestError' } } }]);
|
||||||
transport = new WsSecure(TEST_WS_URL);
|
transport = new Ws(TEST_WS_URL);
|
||||||
});
|
});
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
@ -35,7 +35,7 @@ export default class SecureApi extends Api {
|
|||||||
static getTransport (url, sysuiToken, protocol) {
|
static getTransport (url, sysuiToken, protocol) {
|
||||||
const transportUrl = SecureApi.transportUrl(url, protocol);
|
const transportUrl = SecureApi.transportUrl(url, protocol);
|
||||||
|
|
||||||
return new Api.Provider.WsSecure(transportUrl, sysuiToken, false);
|
return new Api.Provider.Ws(transportUrl, sysuiToken, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static transportUrl (url, protocol) {
|
static transportUrl (url, protocol) {
|
||||||
|
@ -91,16 +91,20 @@ export default class Store {
|
|||||||
|
|
||||||
@action approveSingleRequest = ({ queueId, request: { data, source } }) => {
|
@action approveSingleRequest = ({ queueId, request: { data, source } }) => {
|
||||||
this.removeRequest(queueId);
|
this.removeRequest(queueId);
|
||||||
this.executeMethodCall(data, source);
|
if (data.api) {
|
||||||
|
this.executePubsubCall(data, source);
|
||||||
|
} else {
|
||||||
|
this.executeMethodCall(data, source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@action approveRequest = (queueId, approveAll) => {
|
@action approveRequest = (queueId, approveAll) => {
|
||||||
const queued = this.findRequest(queueId);
|
const queued = this.findRequest(queueId);
|
||||||
|
|
||||||
if (approveAll) {
|
if (approveAll) {
|
||||||
const { request: { data: { method, token } } } = queued;
|
const { request: { data: { method, token, params } } } = queued;
|
||||||
|
|
||||||
this.getFilteredSection(method).methods.forEach((m) => {
|
this.getFilteredSection(method || params[0]).methods.forEach((m) => {
|
||||||
this.addTokenPermission(m, token);
|
this.addTokenPermission(m, token);
|
||||||
this.findMatchingRequests(m, token).forEach(this.approveSingleRequest);
|
this.findMatchingRequests(m, token).forEach(this.approveSingleRequest);
|
||||||
});
|
});
|
||||||
@ -152,7 +156,7 @@ export default class Store {
|
|||||||
}
|
}
|
||||||
|
|
||||||
findMatchingRequests (_method, _token) {
|
findMatchingRequests (_method, _token) {
|
||||||
return this.requests.filter(({ request: { data: { method, token } } }) => method === _method && token === _token);
|
return this.requests.filter(({ request: { data: { method, token, params } } }) => (method === _method || (params && params[0] === _method)) && token === _token);
|
||||||
}
|
}
|
||||||
|
|
||||||
_methodCallbackPost = (id, source, token) => {
|
_methodCallbackPost = (id, source, token) => {
|
||||||
@ -169,6 +173,16 @@ export default class Store {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
executePubsubCall = ({ api, id, token, params }, source) => {
|
||||||
|
const callback = this._methodCallbackPost(id, source, token);
|
||||||
|
|
||||||
|
// TODO: enable security pubsub
|
||||||
|
this.provider.subscribe(api, callback, params).then((v, e) => {
|
||||||
|
console.log('Error and result', v, e);
|
||||||
|
this._methodCallbackPost(id, source, token)(null, v);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
executeMethodCall = ({ id, from, method, params, token }, source) => {
|
executeMethodCall = ({ id, from, method, params, token }, source) => {
|
||||||
const visibleStore = VisibleStore.get();
|
const visibleStore = VisibleStore.get();
|
||||||
const callback = this._methodCallbackPost(id, source, token);
|
const callback = this._methodCallbackPost(id, source, token);
|
||||||
@ -225,18 +239,27 @@ export default class Store {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { from, method, token } = data;
|
const { from, method, token, params, api, subId, id } = data;
|
||||||
|
|
||||||
if (!from || from === 'shell' || from !== token) {
|
if (!from || from === 'shell' || from !== token) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.getFilteredSection(method) && !this.hasTokenPermission(method, token)) {
|
if ((method && this.getFilteredSection(method) && !this.hasTokenPermission(method, token)) ||
|
||||||
|
(api && this.getFilteredSection(params[0]) && !this.hasTokenPermission(method, token))) {
|
||||||
this.queueRequest({ data, origin, source });
|
this.queueRequest({ data, origin, source });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (api) {
|
||||||
this.executeMethodCall(data, source);
|
console.log('apiCall', data);
|
||||||
|
this.executePubsubCall(data, source);
|
||||||
|
} else if (subId) {
|
||||||
|
subId === '*'
|
||||||
|
? this.provider.unsubscribeAll().then(v => this._methodCallbackPost(id, source, token)(null, v))
|
||||||
|
: this.provider.unsubscribe(subId).then(v => this._methodCallbackPost(id, source, token)(null, v));
|
||||||
|
} else {
|
||||||
|
this.executeMethodCall(data, source);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static instance = null;
|
static instance = null;
|
||||||
|
@ -27,6 +27,7 @@ import { IndexRoute, Redirect, Route, Router, hashHistory } from 'react-router';
|
|||||||
import qs from 'querystring';
|
import qs from 'querystring';
|
||||||
|
|
||||||
import Api from '@parity/api';
|
import Api from '@parity/api';
|
||||||
|
import Abi from '@parity/abi';
|
||||||
import builtinDapps from '@parity/shared/config/dappsBuiltin.json';
|
import builtinDapps from '@parity/shared/config/dappsBuiltin.json';
|
||||||
import viewsDapps from '@parity/shared/config/dappsViews.json';
|
import viewsDapps from '@parity/shared/config/dappsViews.json';
|
||||||
import ContractInstances from '@parity/shared/contracts';
|
import ContractInstances from '@parity/shared/contracts';
|
||||||
@ -80,6 +81,13 @@ const dappsHistory = HistoryStore.get('dapps');
|
|||||||
function onEnterDapp ({ params: { id } }) {
|
function onEnterDapp ({ params: { id } }) {
|
||||||
const token = DappRequestsStore.get().createToken(id);
|
const token = DappRequestsStore.get().createToken(id);
|
||||||
|
|
||||||
|
// on app switch unsubscribe all subscriptions
|
||||||
|
if (window.ethereum) {
|
||||||
|
window.ethereum.unsubscribeAll();
|
||||||
|
}
|
||||||
|
// old API uses window.parity
|
||||||
|
window.parity = { Api, Abi };
|
||||||
|
|
||||||
window.ethereum = new Api.Provider.PostMessage(token, window);
|
window.ethereum = new Api.Provider.PostMessage(token, window);
|
||||||
|
|
||||||
if (!dapps[id] || !dapps[id].skipHistory) {
|
if (!dapps[id] || !dapps[id].skipHistory) {
|
||||||
|
Loading…
Reference in New Issue
Block a user