mirror of
https://github.com/status-im/embark-area-51.git
synced 2025-02-10 14:16:47 +00:00
Better error handling for API
Updated error entities so that they work on an api failure (status code != 200). This works with the sagas much better to watch for FAILURE/SUCCESS. http browser errors can be avoided by checking api params. Added timestamp for most of the fiddle calls, to allow for accurate selecting. Add request payload to errors for better error entity selection.
This commit is contained in:
parent
69aea01b8b
commit
9f264dc0d4
@ -179,7 +179,7 @@ export const fiddleCompile = {
|
||||
|
||||
export const FIDDLE_DEPLOY = createRequestTypes('FIDDLE_DEPLOY');
|
||||
export const fiddleDeploy = {
|
||||
post: (compiledCode) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode}),
|
||||
post: (compiledCode, timestamp) => action(FIDDLE_DEPLOY[REQUEST], {compiledCode, timestamp}),
|
||||
success: (response) => {
|
||||
return action(FIDDLE_DEPLOY[SUCCESS], {fiddleDeploys: response.result});
|
||||
},
|
||||
@ -188,14 +188,20 @@ export const fiddleDeploy = {
|
||||
|
||||
export const FIDDLE_FILE = createRequestTypes('FIDDLE_FILE');
|
||||
export const fiddleFile = {
|
||||
request: () => action(FIDDLE_FILE[REQUEST]),
|
||||
success: (codeToCompile) => action(FIDDLE_FILE[SUCCESS], {codeToCompile}),
|
||||
failure: (error) => action(FIDDLE_FILE[FAILURE], {error})
|
||||
request: (timestamp) => {
|
||||
return action(FIDDLE_FILE[REQUEST], {timestamp});
|
||||
},
|
||||
success: (codeToCompile, payload) => action(FIDDLE_FILE[SUCCESS], {fiddleFiles: [{codeToCompile, ...payload}]}),
|
||||
failure: (error, payload) => {
|
||||
return action(FIDDLE_FILE[FAILURE], {fiddleFiles: [{error, ...payload}]});
|
||||
}
|
||||
};
|
||||
|
||||
export const FIDDLE_PROFILE = createRequestTypes('FIDDLE_PROFILE');
|
||||
export const fiddleProfile = {
|
||||
post: (compiledCode, timestamp) => action(FIDDLE_PROFILE[REQUEST], {compiledCode, timestamp}),
|
||||
post: (compiledCode, timestamp) => {
|
||||
return action(FIDDLE_PROFILE[REQUEST], {compiledCode, timestamp});
|
||||
},
|
||||
success: (fiddleProfile, payload) => {
|
||||
return action(FIDDLE_PROFILE[SUCCESS], {fiddleProfiles: [{...fiddleProfile, ...payload}]});
|
||||
},
|
||||
|
@ -141,7 +141,7 @@ export function postFiddleCompile(payload) {
|
||||
}
|
||||
|
||||
export function postFiddleDeploy(payload) {
|
||||
return post('/contract/deploy', {compiledContract: payload.compiledCode});
|
||||
return post('/contract/deploy', payload);
|
||||
}
|
||||
|
||||
export function fetchFiles() {
|
||||
@ -149,6 +149,6 @@ export function fetchFiles() {
|
||||
}
|
||||
|
||||
export function postFiddleProfile(payload) {
|
||||
return post('/contract/profiler/profile', {compiledContract: payload.compiledCode});
|
||||
return post('/profiler/profile', payload);
|
||||
}
|
||||
|
||||
|
@ -41,7 +41,7 @@ class FiddleContainer extends Component {
|
||||
|
||||
componentDidMount() {
|
||||
this.setState({loadingMessage: 'Loading saved state...'});
|
||||
this.props.fetchLastFiddle();
|
||||
this.props.fetchLastFiddle(Date.now());
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
@ -222,7 +222,7 @@ class FiddleContainer extends Component {
|
||||
fatalFiddleCard={this._renderFatalCard("Failed to compile", fiddleCompileError)}
|
||||
fatalFiddleDeployCard={this._renderFatalCard("Failed to deploy", fiddleDeployError)}
|
||||
compiledContractsCard={compiledFiddle && compiledFiddle.compilationResult && this._renderSuccessCard("Contract(s) compiled!",
|
||||
<ContractFunctions contractProfile={profiledFiddle}
|
||||
profiledFiddle && <ContractFunctions contractProfile={profiledFiddle}
|
||||
contractFunctions={deployedFiddle}
|
||||
onlyConstructor
|
||||
postContractFunction={this._onDeployClick}/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {combineReducers} from 'redux';
|
||||
import {REQUEST, SUCCESS} from "../actions";
|
||||
import {REQUEST, SUCCESS, FAILURE} from "../actions";
|
||||
|
||||
const BN_FACTOR = 10000;
|
||||
const voidAddress = '0x0000000000000000000000000000000000000000';
|
||||
@ -22,6 +22,7 @@ const entitiesDefaultState = {
|
||||
fiddleCompiles: [],
|
||||
fiddleDeploys: [],
|
||||
fiddleProfiles: [],
|
||||
fiddleFiles: [],
|
||||
versions: [],
|
||||
plugins: [],
|
||||
ensRecords: [],
|
||||
@ -122,7 +123,7 @@ function errorMessage(state = null, action) {
|
||||
}
|
||||
|
||||
function errorEntities(state = {}, action) {
|
||||
if (!action.type.endsWith(SUCCESS)) {
|
||||
if (!action.type.endsWith(FAILURE)) {
|
||||
return state;
|
||||
}
|
||||
let newState = {};
|
||||
|
@ -13,7 +13,7 @@ function *doRequest(entity, apiFn, payload) {
|
||||
if(response) {
|
||||
yield put(entity.success(response.data, payload));
|
||||
} else if (error) {
|
||||
yield put(entity.failure(error));
|
||||
yield put(entity.failure(error, payload));
|
||||
}
|
||||
}
|
||||
|
||||
@ -264,6 +264,7 @@ export default function *root() {
|
||||
fork(watchFetchContract),
|
||||
fork(watchFetchTransaction),
|
||||
fork(watchPostFiddleCompile),
|
||||
fork(watchPostFiddleCompileSuccess),
|
||||
fork(watchPostFiddleDeploy),
|
||||
fork(watchPostFiddleProfile),
|
||||
fork(watchFetchLastFiddle),
|
||||
|
@ -169,11 +169,11 @@ class ContractsManager {
|
||||
'/embark-api/contract/deploy',
|
||||
(req, res) => {
|
||||
this.logger.trace(`POST request /embark-api/contract/deploy:\n ${JSON.stringify(req.body)}`);
|
||||
if(typeof req.body.compiledContract !== 'object'){
|
||||
return res.send({error: 'Body parameter \'compiledContract\' must be an object'});
|
||||
if(typeof req.body.compiledCode !== 'object'){
|
||||
return res.send({error: 'Body parameter \'compiledCode\' must be an object'});
|
||||
}
|
||||
self.compiledContracts = Object.assign(self.compiledContracts, req.body.compiledContract);
|
||||
const contractNames = Object.keys(req.body.compiledContract);
|
||||
self.compiledContracts = Object.assign(self.compiledContracts, req.body.compiledCode);
|
||||
const contractNames = Object.keys(req.body.compiledCode);
|
||||
self.build((err, _mgr) => {
|
||||
if(err){
|
||||
return res.send({error: err.message});
|
||||
|
@ -105,13 +105,14 @@ class Profiler {
|
||||
'post',
|
||||
'/embark-api/profiler/profile',
|
||||
(req, res) => {
|
||||
let contract = req.body.compiledContract;
|
||||
|
||||
if(!contract){
|
||||
res.send({error: 'Body parameter \'compiledContract\' is required (MISSING_PARAM).'});
|
||||
if (!(req.body.fiddleCompiles && req.body.fiddleCompiles[0] && req.body.fiddleCompiles[0].compilationResult)) {
|
||||
return res.status(204).send(); // send emptry response
|
||||
}
|
||||
|
||||
this.profileJSON(contract, (err, table) => {
|
||||
const contract = req.body.fiddleCompiles[0].compilationResult;
|
||||
if (typeof contract !== 'object') {
|
||||
return res.status(422).send({error: 'Body parameter \'compiledCode\' must be a string'});
|
||||
}
|
||||
this.profileContract(contract, (err, table) => {
|
||||
if (err) {
|
||||
return res.send({error: err.message});
|
||||
}
|
||||
|
@ -20,15 +20,19 @@ class Solidity {
|
||||
'post',
|
||||
'/embark-api/contract/compile',
|
||||
(req, res) => {
|
||||
if(typeof req.body.codeToCompile !== 'string'){
|
||||
return res.send({error: 'Body parameter \'codeToCompile\' must be a string'});
|
||||
if (!(req.body.fiddleFiles && req.body.fiddleFiles[0] && req.body.fiddleFiles[0].codeToCompile)) {
|
||||
return res.status(204).send(); // send emptry response
|
||||
}
|
||||
const input = {'fiddle': {content: req.body.codeToCompile.replace(/\r\n/g, '\n')}};
|
||||
const sourceCode = req.body.fiddleFiles[0].codeToCompile;
|
||||
if (typeof sourceCode !== 'string') {
|
||||
return res.status(422).send({error: 'Body parameter \'codeToCompile\' must be a string'});
|
||||
}
|
||||
const input = {'fiddle': {content: sourceCode.replace(/\r\n/g, '\n')}};
|
||||
this.compile_solidity_code(input, {}, true, (errors, compilationResult) => {
|
||||
// write code to filesystem so we can view the source after page refresh
|
||||
const className = !compilationResult ? 'temp' : Object.keys(compilationResult).join('_');
|
||||
this._writeFiddleToFile(req.body.codeToCompile, className, Boolean(compilationResult), (err) => {
|
||||
if(err) this.logger.trace('Error writing fiddle to filesystem: ', err);
|
||||
this._writeFiddleToFile(sourceCode, className, Boolean(compilationResult), (err) => {
|
||||
if (err) this.logger.trace('Error writing fiddle to filesystem: ', err);
|
||||
}); // async, do not need to wait
|
||||
|
||||
const responseData = {errors: errors, compilationResult: compilationResult};
|
||||
@ -39,23 +43,23 @@ class Solidity {
|
||||
);
|
||||
}
|
||||
|
||||
_writeFiddleToFile(code, className, isCompiled, cb){
|
||||
_writeFiddleToFile(code, className, isCompiled, cb) {
|
||||
fs.mkdirp('.embark/fiddles', (err) => {
|
||||
if(err) return cb(err);
|
||||
if (err) return cb(err);
|
||||
|
||||
// always write to temp.sol file
|
||||
const filePath = Solidity._getFiddlePath('temp');
|
||||
fs.writeFile(filePath, code, 'utf8', cb);
|
||||
|
||||
// if it's compiled, also write to [classname].sol
|
||||
if(isCompiled){
|
||||
if (isCompiled) {
|
||||
const filePath = Solidity._getFiddlePath(className);
|
||||
fs.writeFile(filePath, code, 'utf8', cb);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static _getFiddlePath(className){
|
||||
static _getFiddlePath(className) {
|
||||
return fs.dappPath(`.embark/fiddles/${className}.sol`);
|
||||
}
|
||||
|
||||
@ -64,7 +68,7 @@ class Solidity {
|
||||
self.solcW.compile(jsonObj, function (err, output) {
|
||||
self.events.emit('contracts:compile:solc', jsonObj);
|
||||
|
||||
if(err){
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
if (output.errors && returnAllErrors) {
|
||||
@ -72,7 +76,7 @@ class Solidity {
|
||||
}
|
||||
|
||||
if (output.errors) {
|
||||
for (let i=0; i<output.errors.length; i++) {
|
||||
for (let i = 0; i < output.errors.length; i++) {
|
||||
if (output.errors[i].type === 'Warning') {
|
||||
self.logger.warn(output.errors[i].formattedMessage);
|
||||
}
|
||||
@ -156,7 +160,7 @@ class Solidity {
|
||||
|
||||
const className = contractName;
|
||||
let filename = contractFile;
|
||||
if(filename === 'fiddle') filename = Solidity._getFiddlePath(className);
|
||||
if (filename === 'fiddle') filename = Solidity._getFiddlePath(className);
|
||||
|
||||
compiled_object[className] = {};
|
||||
compiled_object[className].code = contract.evm.bytecode.object;
|
||||
|
@ -20,7 +20,7 @@ class Pipeline {
|
||||
const self = this;
|
||||
self.events.setCommandHandler("files:contract", (filename, cb) => {
|
||||
// handle case where we have a fiddle file and not a file stored in the dapp
|
||||
if(filename.indexOf('.embark/fiddles') > -1){
|
||||
if (filename.indexOf('.embark/fiddles') > -1) {
|
||||
return fs.readFile(filename, 'utf8', (err, source) => {
|
||||
if (err) return cb({error: err});
|
||||
cb(source);
|
||||
@ -47,7 +47,12 @@ class Pipeline {
|
||||
'/embark-api/files/lastfiddle',
|
||||
(req, res) => {
|
||||
fs.readFile(fs.dappPath('.embark/fiddles/temp.sol'), 'utf8', (err, source) => {
|
||||
if (err) return res.send({error: err.message});
|
||||
if (err) {
|
||||
if (err.message.indexOf('ENOENT') > -1) {
|
||||
return res.status(204).send(); // send empty response
|
||||
}
|
||||
return res.status(400).send({error: err.message});
|
||||
}
|
||||
res.send(source);
|
||||
});
|
||||
}
|
||||
@ -79,7 +84,7 @@ class Pipeline {
|
||||
}
|
||||
|
||||
async.waterfall([
|
||||
function createPlaceholderPage(next){
|
||||
function createPlaceholderPage(next) {
|
||||
self.events.request('embark-building-placeholder', (html) => {
|
||||
fs.mkdirpSync(self.buildDir); // create dist/ folder if not already exists
|
||||
fs.writeFile(self.buildDir + 'index.html', html, next);
|
||||
@ -107,7 +112,7 @@ class Pipeline {
|
||||
self.events.request('contracts:list', (_err, contracts) => {
|
||||
// ensure the .embark/contracts directory exists (create if not exists)
|
||||
fs.mkdirp(fs.dappPath(".embark/contracts", ''), (err) => {
|
||||
if(err) return next(err);
|
||||
if (err) return next(err);
|
||||
|
||||
// Create a file .embark/contracts/index.js that requires all contract files
|
||||
// Used to enable alternate import syntax:
|
||||
@ -124,9 +129,9 @@ class Pipeline {
|
||||
|
||||
// add the contract to the exports list to support alternate import syntax
|
||||
importsHelperFile.write(`"${contract.className}": require('./${contract.className}').default`);
|
||||
if(idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add
|
||||
if (idx < contracts.length - 1) importsHelperFile.write(',\n'); // add a comma if we have more contracts to add
|
||||
});
|
||||
}, function(){
|
||||
}, function () {
|
||||
importsHelperFile.write('\n}'); // close the module.exports = {}
|
||||
importsHelperFile.close(next); // close the write stream
|
||||
});
|
||||
@ -135,110 +140,110 @@ class Pipeline {
|
||||
},
|
||||
function assetFileWrite(next) {
|
||||
async.eachOf(self.assetFiles, function (files, targetFile, cb) {
|
||||
async.map(files,
|
||||
function (file, fileCb) {
|
||||
self.logger.trace("reading " + file.filename);
|
||||
async.map(files,
|
||||
function (file, fileCb) {
|
||||
self.logger.trace("reading " + file.filename);
|
||||
|
||||
// Not a JS file
|
||||
if (file.filename.indexOf('.js') < 0) {
|
||||
return file.content(function (fileContent) {
|
||||
self.runPlugins(file, fileContent, fileCb);
|
||||
});
|
||||
}
|
||||
|
||||
// JS files
|
||||
async.waterfall([
|
||||
function runWebpack(next) {
|
||||
let built = false;
|
||||
const webpackProcess = new ProcessLauncher({
|
||||
modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
|
||||
logger: self.logger,
|
||||
events: self.events,
|
||||
exitCallback: function (code) {
|
||||
if (!built) {
|
||||
return next(`File building of ${file.filename} exited with code ${code} before the process finished`);
|
||||
}
|
||||
if (code) {
|
||||
self.logger(__('File building process exited with code ', code));
|
||||
}
|
||||
}
|
||||
});
|
||||
webpackProcess.send({action: constants.pipeline.init, options: {env: self.env}});
|
||||
webpackProcess.send({action: constants.pipeline.build, file, importsList});
|
||||
|
||||
webpackProcess.once('result', constants.pipeline.built, (msg) => {
|
||||
built = true;
|
||||
webpackProcess.kill();
|
||||
return next(msg.error);
|
||||
});
|
||||
},
|
||||
|
||||
function readFile(next) {
|
||||
fs.readFile('./.embark/' + file.filename, (err, data) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
next(null, data.toString());
|
||||
});
|
||||
},
|
||||
|
||||
function runPluginsOnContent(fileContent, next) {
|
||||
self.runPlugins(file, fileContent, next);
|
||||
}
|
||||
|
||||
], function (err, contentFile) {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
return fileCb(err);
|
||||
}
|
||||
|
||||
fileCb(null, contentFile);
|
||||
// Not a JS file
|
||||
if (file.filename.indexOf('.js') < 0) {
|
||||
return file.content(function (fileContent) {
|
||||
self.runPlugins(file, fileContent, fileCb);
|
||||
});
|
||||
},
|
||||
function (err, contentFiles) {
|
||||
if (err) {
|
||||
self.logger.error(__('errors found while generating') + ' ' + targetFile);
|
||||
}
|
||||
let dir = targetFile.split('/').slice(0, -1).join('/');
|
||||
self.logger.trace("creating dir " + self.buildDir + dir);
|
||||
fs.mkdirpSync(self.buildDir + dir);
|
||||
|
||||
// if it's a directory
|
||||
if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) {
|
||||
let targetDir = targetFile;
|
||||
|
||||
if (targetDir.slice(-1) !== '/') {
|
||||
targetDir = targetDir + '/';
|
||||
}
|
||||
|
||||
async.each(contentFiles, function (file, mapCb) {
|
||||
let filename = file.filename.replace(file.basedir + '/', '');
|
||||
self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);
|
||||
|
||||
fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb);
|
||||
}, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
let content = contentFiles.map(function (file) {
|
||||
if (file === undefined) {
|
||||
return "";
|
||||
}
|
||||
return file.content;
|
||||
}).join("\n");
|
||||
|
||||
self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim);
|
||||
if(new RegExp(/^index.html?/i).test(targetFile)){
|
||||
targetFile = targetFile.replace('index', 'index-temp');
|
||||
placeholderPage = targetFile;
|
||||
}
|
||||
fs.writeFile(self.buildDir + targetFile, content, cb);
|
||||
}
|
||||
);
|
||||
},
|
||||
|
||||
// JS files
|
||||
async.waterfall([
|
||||
function runWebpack(next) {
|
||||
let built = false;
|
||||
const webpackProcess = new ProcessLauncher({
|
||||
modulePath: utils.joinPath(__dirname, 'webpackProcess.js'),
|
||||
logger: self.logger,
|
||||
events: self.events,
|
||||
exitCallback: function (code) {
|
||||
if (!built) {
|
||||
return next(`File building of ${file.filename} exited with code ${code} before the process finished`);
|
||||
}
|
||||
if (code) {
|
||||
self.logger(__('File building process exited with code ', code));
|
||||
}
|
||||
}
|
||||
});
|
||||
webpackProcess.send({action: constants.pipeline.init, options: {env: self.env}});
|
||||
webpackProcess.send({action: constants.pipeline.build, file, importsList});
|
||||
|
||||
webpackProcess.once('result', constants.pipeline.built, (msg) => {
|
||||
built = true;
|
||||
webpackProcess.kill();
|
||||
return next(msg.error);
|
||||
});
|
||||
},
|
||||
|
||||
function readFile(next) {
|
||||
fs.readFile('./.embark/' + file.filename, (err, data) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
next(null, data.toString());
|
||||
});
|
||||
},
|
||||
|
||||
function runPluginsOnContent(fileContent, next) {
|
||||
self.runPlugins(file, fileContent, next);
|
||||
}
|
||||
|
||||
], function (err, contentFile) {
|
||||
if (err) {
|
||||
self.logger.error(err);
|
||||
return fileCb(err);
|
||||
}
|
||||
|
||||
fileCb(null, contentFile);
|
||||
});
|
||||
},
|
||||
function (err, contentFiles) {
|
||||
if (err) {
|
||||
self.logger.error(__('errors found while generating') + ' ' + targetFile);
|
||||
}
|
||||
let dir = targetFile.split('/').slice(0, -1).join('/');
|
||||
self.logger.trace("creating dir " + self.buildDir + dir);
|
||||
fs.mkdirpSync(self.buildDir + dir);
|
||||
|
||||
// if it's a directory
|
||||
if (targetFile.slice(-1) === '/' || targetFile.indexOf('.') === -1) {
|
||||
let targetDir = targetFile;
|
||||
|
||||
if (targetDir.slice(-1) !== '/') {
|
||||
targetDir = targetDir + '/';
|
||||
}
|
||||
|
||||
async.each(contentFiles, function (file, mapCb) {
|
||||
let filename = file.filename.replace(file.basedir + '/', '');
|
||||
self.logger.info("writing file " + (self.buildDir + targetDir + filename).bold.dim);
|
||||
|
||||
fs.copy(file.path, self.buildDir + targetDir + filename, {overwrite: true}, mapCb);
|
||||
}, cb);
|
||||
return;
|
||||
}
|
||||
|
||||
let content = contentFiles.map(function (file) {
|
||||
if (file === undefined) {
|
||||
return "";
|
||||
}
|
||||
return file.content;
|
||||
}).join("\n");
|
||||
|
||||
self.logger.info(__("writing file") + " " + (self.buildDir + targetFile).bold.dim);
|
||||
if (new RegExp(/^index.html?/i).test(targetFile)) {
|
||||
targetFile = targetFile.replace('index', 'index-temp');
|
||||
placeholderPage = targetFile;
|
||||
}
|
||||
fs.writeFile(self.buildDir + targetFile, content, cb);
|
||||
}
|
||||
);
|
||||
},
|
||||
next);
|
||||
},
|
||||
function removePlaceholderPage(next){
|
||||
function removePlaceholderPage(next) {
|
||||
let placeholderFile = self.buildDir + placeholderPage;
|
||||
fs.access(self.buildDir + placeholderPage, (err) => {
|
||||
if (err) return next(); // index-temp doesn't exist, do nothing
|
||||
@ -257,7 +262,7 @@ class Pipeline {
|
||||
return fileCb(null, {content: fileContent, filename: file.filename, path: file.path, basedir: file.basedir, modified: true});
|
||||
}
|
||||
async.eachSeries(self.pipelinePlugins,
|
||||
function(plugin, pluginCB) {
|
||||
function (plugin, pluginCB) {
|
||||
if (file.options && file.options.skipPipeline) {
|
||||
return pluginCB();
|
||||
}
|
||||
@ -292,7 +297,7 @@ class Pipeline {
|
||||
function writeContractsJSON(contracts, next) {
|
||||
async.each(contracts, (contract, eachCb) => {
|
||||
fs.writeJson(fs.dappPath(self.buildDir, 'contracts', contract.className + ".json"), contract, {spaces: 2}, eachCb);
|
||||
}, () => { next(); });
|
||||
}, () => {next();});
|
||||
}
|
||||
], cb);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user