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:
emizzle 2018-09-04 12:38:18 +10:00
parent 69aea01b8b
commit 9f264dc0d4
9 changed files with 159 additions and 141 deletions

View File

@ -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}]});
},

View File

@ -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);
}

View File

@ -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}/>

View File

@ -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 = {};

View File

@ -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),

View File

@ -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});

View File

@ -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});
}

View File

@ -20,14 +20,18 @@ 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) => {
this._writeFiddleToFile(sourceCode, className, Boolean(compilationResult), (err) => {
if (err) this.logger.trace('Error writing fiddle to filesystem: ', err);
}); // async, do not need to wait

View File

@ -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);
});
}