Merge pull request #32 from embark-framework/fix/throw-error-when-connection-impossible

fix: pass error when connection doesn't happen
This commit is contained in:
Iuri Matias 2019-01-25 17:38:58 -05:00 committed by GitHub
commit 096e7ed61a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 229 additions and 23 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
dist
node_modules
package
yarn.lock
.idea
.vscode

View File

@ -28,7 +28,7 @@
"clean": "rimraf dist embark.min.js embarkjs-*.tgz package",
"http-server": "http-server",
"prepare": "npm run build",
"test": "echo \"Error: no test specified\" && exit 1",
"test": "mocha --exit",
"webpack": "webpack"
},
"repository": {
@ -56,15 +56,19 @@
"async-es": "2.6.1"
},
"devDependencies": {
"@babel/cli": "7.0.0-rc.1",
"@babel/core": "7.0.0-rc.1",
"@babel/plugin-transform-runtime": "7.0.0-rc.1",
"@babel/preset-env": "7.0.0-rc.1",
"@babel/cli": "7.2.3",
"@babel/core": "7.2.2",
"@babel/plugin-transform-runtime": "7.2.0",
"@babel/preset-env": "7.2.3",
"@babel/register": "7.0.0",
"ajv": "6.5.2",
"chai": "4.2.0",
"cross-env": "5.2.0",
"http-server": "0.11.1",
"mocha": "5.2.0",
"npm-run-all": "4.1.5",
"rimraf": "2.6.2",
"web3": "1.0.0-beta.37",
"webpack": "4.16.1",
"webpack-cli": "3.0.8"
},

View File

@ -28,15 +28,23 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
const self = this;
const checkConnect = (next) => {
this.blockchainConnector.getAccounts((err, _a) => {
if (err) {
this.blockchainConnector.setProvider(null);
}
return next(null, !err);
this.blockchainConnector.getAccounts((error, _a) => {
const provider = self.blockchainConnector.getCurrentProvider();
const connectionString = provider.host;
if (error) this.blockchainConnector.setProvider(null);
return next(null, {
connectionString,
error,
connected: !error
});
});
};
const connectWeb3 = async (next) => {
const connectionString = 'web3://';
if (window.ethereum) {
try {
if (Blockchain.autoEnable) {
@ -45,11 +53,19 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
}
return checkConnect(next);
} catch (error) {
return next(null, false);
return next(null, {
connectionString,
error,
connected: false
});
}
}
return next(null, false);
return next(null, {
connectionString,
error: new Error("web3 provider not detected"),
connected: false
});
};
const connectWebsocket = (value, next) => {
@ -62,20 +78,24 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
checkConnect(next);
};
let connectionErrs = {};
this.doFirst(function(cb) {
reduce(connectionList, false, function(connected, value, next) {
if (connected) {
return next(null, connected);
reduce(connectionList, false, function(result, connectionString, next) {
if (result.connected) {
return next(null, result);
} else if(result) {
connectionErrs[result.connectionString] = result.error;
}
if (value === '$WEB3') {
if (connectionString === '$WEB3') {
connectWeb3(next);
} else if (value.indexOf('ws://') >= 0) {
connectWebsocket(value, next);
} else if (connectionString.indexOf('ws://') >= 0) {
connectWebsocket(connectionString, next);
} else {
connectHttp(value, next);
connectHttp(connectionString, next);
}
}, function(_err, _connected) {
}, function(_err, _connectionErr, _connected) {
self.blockchainConnector.getAccounts((err, accounts) => {
const currentProv = self.blockchainConnector.getCurrentProvider();
if (opts.warnAboutMetamask && currentProv && currentProv.isMetaMask) {
@ -83,7 +103,7 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
// embark will only do this if geth is our client and we are in
// dev mode
if(opts.blockchainClient === 'geth') {
console.warn("%cNote: There is a known issue with Geth that may cause transactions to get stuck when using Metamask. Please log in to the cockpit (http://localhost:8000/embark?enableRegularTxs=true) to enable a workaround. Once logged in, the workaround will automatically be enabled.", "font-size: 2em");
console.warn("%cNote: There is a known issue with Geth that may cause transactions to get stuck when using Metamask. Please log in to the cockpit (http://localhost:8000/embark?enableRegularTxs=true) to enable a workaround. Once logged in, the workaround will automatically be enabled.", "font-size: 2em");
}
if(opts.blockchainClient === 'parity') {
console.warn("%cNote: Parity blocks the connection from browser extensions like Metamask. To resolve this problem, go to https://embark.status.im/docs/blockchain_configuration.html#Using-Parity-and-Metamask", "font-size: 2em");
@ -93,8 +113,11 @@ Blockchain.connect = function(connectionList, opts, doneCb) {
if (accounts) {
self.blockchainConnector.setDefaultAccount(accounts[0]);
}
cb(err);
doneCb(err);
const connectionError = new BlockchainConnectionError(connectionErrs);
cb(connectionErrs);
doneCb(connectionErrs);
});
});
});
@ -266,4 +289,13 @@ Contract.prototype.send = function(value, unit, _options) {
Blockchain.Contract = Contract;
class BlockchainConnectionError extends Error {
constructor(connectionErrors) {
super("Could not establish a connection to a node.");
this.connections = connectionErrors;
this.name = 'BlockchainConnectionError';
}
}
export default Blockchain;

58
test/blockchain_test.js Normal file
View File

@ -0,0 +1,58 @@
/* global before,describe,it */
const {startRPCMockServer, TestProvider} = require('./test');
const async = require('async');
const {assert} = require('chai');
const Blockchain = require('../dist/blockchain');
describe('Blockchain', () => {
describe('#connect', () => {
before(() => {
Blockchain.default.registerProvider('web3', TestProvider);
Blockchain.default.setProvider('web3', {});
});
const scenarios = [
{
description: 'should not keep trying other connections if connected',
servers: [true, true],
visited: [true, false],
error: false
},
{
description: 'should keep trying other connections if not connected',
servers: [false, true],
visited: [true, true],
error: false
},
{
description: 'should return error if no connections succeed',
servers: [false, false],
visited: [true, true],
error: true
}
];
scenarios.forEach(scenario => {
it(scenario.description, done => {
async.parallel(
scenario.servers.map(validServer =>
(cb) => startRPCMockServer({successful: validServer}, cb)
),
(_err, servers) => {
const connStrings = servers.map(server => server.connectionString);
Blockchain.default.connect(connStrings, {}, err => {
if(scenario.error) assert(err);
servers.forEach((server, idx) => {
assert.strictEqual(server.visited, scenario.visited[idx]);
});
done();
});
}
);
});
});
});
});

111
test/test.js Normal file
View File

@ -0,0 +1,111 @@
const async = require('async');
const http = require('http');
const net = require('net');
const Web3 = require('web3');
const startRPCMockServer = (options = {}, callback) => {
const opts = Object.assign({}, {
successful: true
}, options);
let port = 0;
let sock = net.createServer();
let state = { visited: false };
let server = http.createServer({}, (req, res) => {
state.visited = true;
if(!opts.successful) {
res.statusCode = 500;
return res.end();
}
let body = '';
req.on('data', chunk => {
body += chunk.toString();
});
req.on('end', () => {
const request = JSON.parse(body);
const accountsResponse = JSON.stringify({
"jsonrpc": "2.0",
"id": request.id,
"result": [
"0x7c67d951b7338a96168f259a16b7ba25e7a30315"
]
});
res.writeHead(200, {
'Content-Length': Buffer.byteLength(accountsResponse),
'Content-Type': 'application/json'
})
res.end(accountsResponse);
});
});
async.waterfall([
cb => { sock.listen(0, cb) },
cb => { port = sock.address().port; cb() },
cb => { sock.close(cb) },
cb => { server.listen(port, '127.0.0.1', () => cb()); }
], () => {
state.server = server;
state.connectionString = `http://localhost:${port}`;
callback(null, state);
});
}
const TestProvider = {};
TestProvider.init = function(_config) {
this.web3 = global.web3 || new Web3();
global.web3 = global.web3 || this.web3;
};
TestProvider.getInstance = function () {
return this.web3;
};
TestProvider.getAccounts = function () {
return this.web3.eth.getAccounts(...arguments);
};
TestProvider.getNewProvider = function (providerName, ...args) {
return new Web3.providers[providerName](...args);
};
TestProvider.setProvider = function (provider) {
return this.web3.setProvider(provider);
};
TestProvider.getCurrentProvider = function () {
return this.web3.currentProvider;
};
TestProvider.getDefaultAccount = function () {
return this.web3.eth.defaultAccount;
};
TestProvider.setDefaultAccount = function (account) {
this.web3.eth.defaultAccount = account;
};
TestProvider.newContract = function (options) {
return new this.web3.eth.Contract(options.abi, options.address);
};
TestProvider.send = function () {
return this.web3.eth.sendTransaction(...arguments);
};
TestProvider.toWei = function () {
return this.web3.toWei(...arguments);
};
TestProvider.getNetworkId = function () {
return this.web3.eth.net.getId();
};
module.exports = {
TestProvider,
startRPCMockServer
}