Skip to content
This repository has been archived by the owner on Jun 14, 2018. It is now read-only.

Commit

Permalink
Merge pull request #6 from trufflesuite/error-handling
Browse files Browse the repository at this point in the history
Expand web3.js transport method wrapping to improve error reporting
  • Loading branch information
tcoulter authored Jul 18, 2017
2 parents 2957ac0 + 4932988 commit bdce4fd
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 27 deletions.
23 changes: 23 additions & 0 deletions error.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var inherits = require("util").inherits;
var TruffleError = require("truffle-error");

// HACK: string comparison seems to be only way to identify being unable to
// connect to RPC node.
var NOT_CONNECTED_MESSAGE = 'Invalid JSON RPC response: ""';

function ProviderError(message, error) {
if (message == NOT_CONNECTED_MESSAGE) {
message = "Could not connect to your Ethereum client. " +
"Please check that your Ethereum client:\n" +
" - is running\n" +
" - is accepting RPC connections (i.e., \"--rpc\" option is used in geth)\n" +
" - is accessible over the network\n" +
" - is properly configured in your Truffle configuration file (truffle.js)\n";
}
ProviderError.super_.call(this, message);
this.message = message;
}

inherits(ProviderError, TruffleError);

module.exports = ProviderError;
30 changes: 3 additions & 27 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,10 @@
var Web3 = require("web3");

var wrapper = require('./wrapper');

module.exports = {
wrap: function(provider, options) {
options = options || {};

if (options.verbose || options.verboseRpc) {
this.makeVerbose(provider, options.logger);
}
return provider;
},

makeVerbose: function(provider, logger) {
logger = logger || console;

// // If you want to see what web3 is sending and receiving.
var oldAsync = provider.sendAsync;

if (oldAsync.is_verbose) return;

provider.sendAsync = function(options, callback) {
logger.log(" > " + JSON.stringify(options, null, 2).split("\n").join("\n > "));
oldAsync.call(provider, options, function(error, result) {
if (error == null) {
logger.log(" < " + JSON.stringify(result, null, 2).split("\n").join("\n < "));
}
callback(error, result)
});
};

provider.sendAsync.is_verbose = true;
return wrapper.wrap(provider, options);
},

create: function(options) {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
},
"homepage": "https://github.com/trufflesuite/truffle-provider#readme",
"dependencies": {
"truffle-error": "0.0.2",
"web3": "^0.18.0"
},
"devDependencies": {
Expand Down
138 changes: 138 additions & 0 deletions wrapper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
var ProviderError = require('./error');

module.exports = {
/*
* Web3.js Transport Wrapper
*
* Wraps an underlying web3 provider's RPC transport methods (send/sendAsync)
* for Truffle-specific purposes, mainly for logging / request verbosity.
*/
wrap: function(provider, options) {
/* wrapping should be idempotent */
if (provider._alreadyWrapped) return provider;

/* setup options defaults */
options = options || {};
// custom logger
options.logger = options.logger || console;
// to see what web3 is sending and receiving.
options.verbose = options.verbose || options.verboseRpc || false;

/* create wrapper functions for before/after send */
var preHook = this.preHook(options);
var postHook = this.postHook(options);

var originalSend = provider.send.bind(provider);
var originalSendAsync = provider.sendAsync.bind(provider);

/* overwrite methods */
provider.send = this.send(originalSend, preHook, postHook);
provider.sendAsync = this.sendAsync(originalSendAsync, preHook, postHook);

/* mark as wrapped */
provider._alreadyWrapped = true;

return provider;
},

/*
* Transport Hook Generators
*
* Used to wrap underlying web3.js behavior before/after sending request
* payloads to the RPC.
*
* Transport hooks may be used to perform additional operations before/after
* sending, and/or to modify request/response data.
*
* Each generator accepts an `options` argument and uses it to construct
* and return a function.
*
* Returned functions accept relevant arguments and return potentially new
* versions of those arguments (for payload/result/error overrides)
*/

// before send/sendAsync
preHook: function(options) {
return function(payload) {
if (options.verbose) {
// for request payload debugging
options.logger.log(" > " + JSON.stringify(payload, null, 2).split("\n").join("\n > "));
}

return payload;
};
},

// after send/sendAsync
postHook: function(options) {
return function(payload, error, result) {
if (error != null) {
// wrap errors in internal error class
error = new ProviderError(error.message, error);
return [payload, error, result];
}

if (options.verbose) {
options.logger.log(" < " + JSON.stringify(result, null, 2).split("\n").join("\n < "));
}

return [payload, error, result];
};
},

/*
* Transport Method Generators
*
* Generate wrapped versions of `send`/`sendAsync`, given original method and
* transport hooks.
*
* Pre-condition: originals are bound correctly (`send.bind(provider)`)
*
* Return the wrapped function matching the original function's signature.
*/

// wrap a `provider.send` function with behavior hooks
// returns a function(payload) to replace `provider.send`
send: function(originalSend, preHook, postHook) {
return function(payload) {
var result = null;
var error = null;

payload = preHook(payload);

try {
result = originalSend(payload);
} catch (e) {
error = e;
}

var modified = postHook(payload, error, result);
payload = modified[0];
error = modified[1];
result = modified[2];

if (error != null) {
throw error;
}

return result;
}
},

// wrap a `provider.sendAsync` function with behavior hooks
// returns a function(payload, callback) to replace `provider.sendAsync`
sendAsync: function(originalSendAsync, preHook, postHook) {
return function(payload, callback) {
payload = preHook(payload);

originalSendAsync(payload, function(error, result) {
var modified = postHook(payload, error, result);
payload = modified[0];
error = modified[1];
result = modified[2];

callback(error, result);
});
};
}
}

0 comments on commit bdce4fd

Please sign in to comment.