diff --git a/docs/web3-eth-subscribe.rst b/docs/web3-eth-subscribe.rst index 401a071..d624d53 100644 --- a/docs/web3-eth-subscribe.rst +++ b/docs/web3-eth-subscribe.rst @@ -33,7 +33,9 @@ Returns ``EventEmitter`` - A Subscription instance - ``subscription.id``: The subscription id, used to identify and unsubscribing the subscription. + - ``subscription.subscribe([callback])``: Can be used to re-subscribe with the same parameters. - ``subscription.unsubscribe([callback])``: Unsubscribes the subscription and returns `TRUE` in the callback if successfull. + - ``subscription.arguments``: The subscription arguments, used when re-subscribing. - ``on("data")`` returns ``Object``: Fires on each incoming log with the log object as argument. - ``on("changed")`` returns ``Object``: Fires on each log which was removed from the blockchain. The log will have the additional property ``"removed: true"``. - ``on("error")`` returns ``Object``: Fires when an error in the subscription occours. diff --git a/packages/web3-core-requestManager/package.json b/packages/web3-core-requestManager/package.json index bd89f0b..c5b3725 100644 --- a/packages/web3-core-requestManager/package.json +++ b/packages/web3-core-requestManager/package.json @@ -7,7 +7,6 @@ "main": "src/index.js", "dependencies": { "web3-core-helpers": "^1.0.0", - "web3-providers-ipc": "^1.0.0", "underscore": "^1.8.3", "oboe": "^2.1.3", "xmlhttprequest": "*", diff --git a/packages/web3-core-requestManager/src/givenProvider.js b/packages/web3-core-requestManager/src/givenProvider.js new file mode 100644 index 0000000..0fd19b8 --- /dev/null +++ b/packages/web3-core-requestManager/src/givenProvider.js @@ -0,0 +1,86 @@ +/* + This file is part of web3.js. + + web3.js is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + web3.js is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with web3.js. If not, see . + */ +/** + * @file givenProvider.js + * @author Fabian Vogelsteller + * @date 2017 + */ + +"use strict"; + +var givenProvider = null; + +// ADD GIVEN PROVIDER +/* jshint ignore:start */ +var global = Function('return this')(); + +// EthereumProvider +if(typeof global.ethereumProvider !== 'undefined') { + givenProvider = global.ethereumProvider; + +// Legacy web3.currentProvider +} else if(typeof global.web3 !== 'undefined' && global.web3.currentProvider) { + + if(global.web3.currentProvider.sendAsync) { + global.web3.currentProvider.send = global.web3.currentProvider.sendAsync; + delete global.web3.currentProvider.sendAsync; + } + + // if connection is 'ipcProviderWrapper', add subscription support + if(!global.web3.currentProvider.on && + global.web3.currentProvider.connection && + global.web3.currentProvider.connection.constructor.name === 'ipcProviderWrapper') { + + global.web3.currentProvider.on = function (type, callback) { + + if(typeof callback !== 'function') + throw new Error('The second parameter callback must be a function.'); + + switch(type){ + case 'notification': + this.connection.on('data', function(data) { + var result = ''; + + data = data.toString(); + + try { + result = JSON.parse(data); + } catch(e) { + return callback(new Error('Couldn\'t parse response data'+ data)); + } + + // notification + if(!result.id && result.method.indexOf('_subscription') !== -1) { + callback(null, result); + } + + }); + break; + + default: + this.connection.on(type, callback); + break; + } + }; + } + + givenProvider = global.web3.currentProvider; +} +/* jshint ignore:end */ + + +module.exports = givenProvider; diff --git a/packages/web3-core-requestManager/src/index.js b/packages/web3-core-requestManager/src/index.js index 5788e17..168ab31 100644 --- a/packages/web3-core-requestManager/src/index.js +++ b/packages/web3-core-requestManager/src/index.js @@ -24,9 +24,10 @@ var _ = require('underscore'); -var Jsonrpc = require('./jsonrpc'); var errors = require('web3-core-helpers').errors; -var BatchManager = require('./batch'); +var Jsonrpc = require('./jsonrpc.js'); +var BatchManager = require('./batch.js'); +var givenProvider = require('./givenProvider.js'); @@ -44,31 +45,8 @@ var RequestManager = function RequestManager(provider) { }; -// ADD GIVEN PROVIDER -/* jshint ignore:start */ -var IpcProvider = require('web3-providers-ipc'); -var global = Function('return this')(); -if(typeof global.ethereumProvider !== 'undefined') { - RequestManager.givenProvider = global.ethereumProvider; - -} else if(typeof global.web3 !== 'undefined' && global.web3.currentProvider) { - // if connection object is available, create new provider - if (global.web3.currentProvider.connection) { - RequestManager.givenProvider = new IpcProvider('', global.web3.currentProvider.connection); - - // otherwise subscription aren't available - } else { - if(global.web3.currentProvider.sendAsync) { - global.web3.currentProvider.send = global.web3.currentProvider.sendAsync; - delete global.web3.currentProvider.sendAsync; - } - - RequestManager.givenProvider = global.web3.currentProvider; - } - -} -/* jshint ignore:end */ +RequestManager.givenProvider = givenProvider; /** diff --git a/packages/web3-core-subscriptions/src/subscription.js b/packages/web3-core-subscriptions/src/subscription.js index 3d12d24..1de7af1 100644 --- a/packages/web3-core-subscriptions/src/subscription.js +++ b/packages/web3-core-subscriptions/src/subscription.js @@ -31,6 +31,7 @@ var Subscription = function Subscription(options) { var emitter = new EventEmitter(); this.id = null; this.callback = null; + this.arguments = null; this._reconnectIntervalId = null; this.options = { @@ -86,7 +87,7 @@ Subscription.prototype._validateArgs = function (args) { if(!subscription.params) subscription.params = 0; - if (args.length !== subscription.params + 1) { + if (args.length !== subscription.params) { throw errors.InvalidNumberOfParams(args.length, subscription.params + 1, args[0]); } }; @@ -106,19 +107,13 @@ Subscription.prototype._formatInput = function (args) { return args; } - // replace subscription with given name - if (subscription.subscriptionName) { - args[0] = subscription.subscriptionName; - } - if (!subscription.inputFormatter) { return args; } var formattedArgs = subscription.inputFormatter.map(function (formatter, index) { - return formatter ? formatter(args[index+1]) : args[index+1]; + return formatter ? formatter(args[index]) : args[index]; }); - formattedArgs.unshift(args[0]); return formattedArgs; }; @@ -145,9 +140,33 @@ Subscription.prototype._formatOutput = function (result) { * @return {Object} */ Subscription.prototype._toPayload = function (args) { + var params = []; this.callback = this._extractCallback(args); - var params = this._formatInput(args); - this._validateArgs(params); + + if (!this.subscriptionMethod) { + this.subscriptionMethod = args.shift(); + + // replace subscription with given name + if (this.options.subscription.subscriptionName) { + this.subscriptionMethod = this.options.subscription.subscriptionName; + } + } + + if (!this.arguments) { + this.arguments = this._formatInput(args); + this._validateArgs(this.arguments); + args = []; // make empty after validation + + } + + // re-add subscriptionName + params.push(this.subscriptionMethod); + params = params.concat(this.arguments); + + + if (args.length) { + throw new Error('Only a callback is allowed as parameter on an already instantiated subscription.'); + } return { method: this.options.type + '_subscribe', @@ -178,12 +197,25 @@ Subscription.prototype.unsubscribe = function(callback) { */ Subscription.prototype.subscribe = function() { var _this = this; - var args = arguments; - var payload = this._toPayload(Array.prototype.slice.call(arguments)); + var args = Array.prototype.slice.call(arguments); + var payload = this._toPayload(args); + + if(!payload) { + return this; + } // throw error, if provider doesnt support subscriptions - if(!this.options.requestManager.provider.on) - throw new Error('The current provider doesn\'t support subscriptions', this.options.requestManager.provider); + if(!this.options.requestManager.provider.on) { + var err = new Error('The current provider doesn\'t support subscriptions'+ this.options.requestManager.provider.constructor.name); + this.callback(err, null, this); + this.emit('error', err); + return this; + } + + // if id is there unsubscribe first + if (this.id) { + this.unsubscribe(); + } // store the params in the options object this.options.params = payload.params[1]; @@ -241,12 +273,13 @@ Subscription.prototype.subscribe = function() { // re-subscribe, if connection fails if(_this.options.requestManager.provider.once) { _this._reconnectIntervalId = setInterval(function () { + // TODO check if that makes sense! _this.options.requestManager.provider.reconnect(); }, 500); _this.options.requestManager.provider.once('connect', function () { clearInterval(_this._reconnectIntervalId); - _this.subscribe.apply(_this, args); + _this.subscribe(_this.callback); }); } _this.emit('error', err);