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:
commit
096e7ed61a
|
@ -1,6 +1,7 @@
|
|||
dist
|
||||
node_modules
|
||||
package
|
||||
yarn.lock
|
||||
|
||||
.idea
|
||||
.vscode
|
||||
|
|
14
package.json
14
package.json
|
@ -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"
|
||||
},
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -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
|
||||
}
|
Loading…
Reference in New Issue