Handle error codes for bad package selection

Handles 409 and 422 HTTP codes for bad package selection. Shows
popup for appropriate error message and gives a link for build
logs.

Fixes #12
Fixes #8

Signed-off-by: Sudhanshu Gautam <me@sudhanshug.com>
This commit is contained in:
Sudhanshu Gautam 2019-08-26 01:15:05 +05:30
parent 5e3b827489
commit acdb7741e8
6 changed files with 123 additions and 65 deletions

View file

@ -9,7 +9,7 @@ import {
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
function AlertDialog({open, cancelHandler, acceptHandler, text, title, cancelComponent, acceptComponent}) { function AlertDialog({open, cancelHandler, acceptHandler, body, title, cancelComponent, acceptComponent}) {
return ( return (
<Dialog <Dialog
open={open} open={open}
@ -21,17 +21,24 @@ function AlertDialog({open, cancelHandler, acceptHandler, text, title, cancelCom
id="alert-dialog-title">{title}</DialogTitle> id="alert-dialog-title">{title}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText id="alert-dialog-description"> <DialogContentText id="alert-dialog-description">
{text} {body}
</DialogContentText> </DialogContentText>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
<Button onClick={acceptHandler} color="primary"> {
{acceptComponent} acceptHandler && (
</Button> <Button onClick={acceptHandler} color="primary">
<Button onClick={cancelHandler} color="secondary" {acceptComponent}
variant="contained" autoFocus> </Button>
{cancelComponent} )
</Button> }
{
cancelHandler && (
<Button onClick={cancelHandler} color="secondary" variant="contained" autoFocus>
{cancelComponent}
</Button>
)
}
</DialogActions> </DialogActions>
</Dialog> </Dialog>
); );
@ -41,7 +48,7 @@ AlertDialog.propTypes = {
open: PropTypes.bool, open: PropTypes.bool,
cancelHandler: PropTypes.func, cancelHandler: PropTypes.func,
acceptHandler: PropTypes.func, acceptHandler: PropTypes.func,
text: PropTypes.string, body: PropTypes.object,
title: PropTypes.string, title: PropTypes.string,
cancelComponent: PropTypes.elementType, cancelComponent: PropTypes.elementType,
acceptComponent: PropTypes.object, acceptComponent: PropTypes.object,

View file

@ -82,8 +82,9 @@ class Home extends React.Component {
fuzzySet: null, fuzzySet: null,
showAdvanced: true, showAdvanced: true,
basicInterface: 0, basicInterface: 0,
errorDialogMessage: <></>,
openErrorDialog: false,
}; };
basicInterface = 0;
confirmingBuild = false; confirmingBuild = false;
dataService = new DataService(); dataService = new DataService();
@ -258,7 +259,7 @@ class Home extends React.Component {
const packageArray = this.state.packageName.split(/[,\n]+/); const packageArray = this.state.packageName.split(/[,\n]+/);
packageArray.forEach((package_name) => { packageArray.forEach((package_name) => {
package_name = package_name.replace(' ', ''); package_name = package_name.replace(' ', '');
if (package_name !== '') { if (package_name !== '' && packages.indexOf(package_name) === -1) {
packages.push(package_name); packages.push(package_name);
} }
}); });
@ -315,11 +316,22 @@ class Home extends React.Component {
} else if (buildStatusResponse.status === 200) { } else if (buildStatusResponse.status === 200) {
await this.displayBuiltImageData(buildStatusResponse); await this.displayBuiltImageData(buildStatusResponse);
resolve(); resolve();
} else if (buildStatusResponse.status === 409) {
this.setState({
openErrorDialog: true,
errorDialogMessage: (
<>
{buildStatusResponse.data.error} <br />
<a href={buildStatusResponse.data.error}>Build logs</a>
</>
),
});
resolve();
} else { } else {
throw buildStatusResponse.data; throw buildStatusResponse.data;
} }
} catch (err) { } catch (e) {
reject(err); reject(e);
} }
}); });
}; };
@ -336,6 +348,7 @@ class Home extends React.Component {
builtImages: [], builtImages: [],
}); });
let buildResponse = await this.dataService.buildImage(board, packages, target, version); let buildResponse = await this.dataService.buildImage(board, packages, target, version);
console.log(buildResponse);
if (buildResponse.status === 202 && buildResponse.data['request_hash'] !== undefined) { if (buildResponse.status === 202 && buildResponse.data['request_hash'] !== undefined) {
const request_hash = buildResponse.data['request_hash']; const request_hash = buildResponse.data['request_hash'];
await sleep(buildStatusCheckInterval); await sleep(buildStatusCheckInterval);
@ -345,12 +358,34 @@ class Home extends React.Component {
} else { } else {
throw buildResponse.data; throw buildResponse.data;
} }
} catch (err) { } catch (e) {
this.setState({ if (e.response.status === 409) {
isBuilding: false, this.setState({
showUnexpectedErrorBar: true, isBuilding: false,
}); openErrorDialog: true,
console.log(err); errorDialogMessage: (
<>
{e.response.data.error} <br />
<a href={asu + e.response.data.log} target="_blank" rel="noopener noreferrer">Build logs</a>
</>
),
});
} else if (e.response.status === 422) {
this.setState({
isBuilding: false,
openErrorDialog: true,
errorDialogMessage: (
<>
{e.response.data.error}
</>
),
});
} else {
this.setState({
isBuilding: false,
showUnexpectedErrorBar: true,
});
}
} }
}; };
@ -361,6 +396,12 @@ class Home extends React.Component {
}); });
} }
closeErrorDialog = () => {
this.setState({
openErrorDialog: false,
});
}
render() { render() {
const warning432 = this.state.showDeviceData && this.state.deviceLoaded && const warning432 = this.state.showDeviceData && this.state.deviceLoaded &&
parseInt( parseInt(
@ -410,7 +451,7 @@ class Home extends React.Component {
> >
{ {
this.state.data.map((version, i) => ( this.state.data.map((version, i) => (
<MenuItem value={i} key={version.name}> <MenuItem value={i} key={version.revision}>
<em>{version.name}</em> <em>{version.name}</em>
</MenuItem> </MenuItem>
)) ))
@ -501,28 +542,24 @@ class Home extends React.Component {
<Grid item xs> <Grid item xs>
<b>{this.props.t('Downloads')}: </b> <b>{this.props.t('Downloads')}: </b>
{ {
this.state.selection.device.images.map((image) => { this.state.selection.device.images.map((image) =>
return ( <div key={image.name}>
<> <Button
<br /> className="download-button"
<Button href={asu_download +
key={image.name} this.state.data[this.state.selection.version].path + '/targets/' +
className="download-button" this.state.selection.device.target + '/' +
href={asu_download + image.name}
this.state.data[this.state.selection.version].path + '/targets/' + color="primary"
this.state.selection.device.target + '/' + variant="contained"
image.name} onClick={() => this.downloadingImageIndicatorShow()}
color="primary" >
variant="contained" <CloudDownloadIcon
onClick={() => this.downloadingImageIndicatorShow()} className="download-icon"/>
> {image.name.split('-').reverse()[0].split('.')[0]}
<CloudDownloadIcon </Button>
className="download-icon"/> </div>
{image.name.split('-').reverse()[0].split('.')[0]} )
</Button>
</>
);
})
} }
&nbsp; &nbsp;
{ {
@ -538,17 +575,15 @@ class Home extends React.Component {
<Paper elevation={0} className="package-list-input"> <Paper elevation={0} className="package-list-input">
<div> <div>
{ {
this.state.packages.map((package_name, i) => { this.state.packages.map((package_name, i) =>
return (
<Chip className="package" <Chip className="package"
key={package_name} key={package_name + i}
size="small" size="small"
onDelete={() => this.deletePackage( onDelete={() => this.deletePackage(
i)} i)}
label={package_name} label={package_name}
/> />
); )
})
} }
<Tooltip <Tooltip
title={<span>Use comma or new line separated array. <br/>Press enter to apply.</span>}> title={<span>Use comma or new line separated array. <br/>Press enter to apply.</span>}>
@ -581,8 +616,11 @@ class Home extends React.Component {
{this.props.t('Cancel')} {this.props.t('Cancel')}
</Button> </Button>
&nbsp; &nbsp;
&nbsp;
<CircularProgress size={20} style={{verticalAlign: 'middle'}}/> <CircularProgress size={20} style={{verticalAlign: 'middle'}}/>
&nbsp;
Building image Building image
&nbsp;
{ {
this.state.queuePosition !== -1 && ( this.state.queuePosition !== -1 && (
<span> (Position in queue: {this.state.queuePosition}) </span> <span> (Position in queue: {this.state.queuePosition}) </span>
@ -651,17 +689,27 @@ class Home extends React.Component {
cancelHandler={this.closeConfirmBuildDialog} cancelHandler={this.closeConfirmBuildDialog}
acceptHandler={this.buildImage} acceptHandler={this.buildImage}
open={this.confirmingBuild} open={this.confirmingBuild}
text={this.props.t( body={
'Building image requires computation resources, so we would request you to check if this selection is what you want')} <>
{this.props.t('Building image requires computation resources, so we would request you to check if this selection is what you want')}
</>
}
title={this.props.t( title={this.props.t(
'Please confirm that you want to perform this action')} 'Please confirm that you want to perform this action')}
cancelComponent={this.props.t('Cancel')} cancelComponent={this.props.t('Cancel')}
acceptComponent={ acceptComponent={
<> <>
{this.props.t('Build')} &nbsp; <BuildIcon/> {this.props.t('Build')} &nbsp; <BuildIcon/>
</> </>
} }
/> />
<AlertDialog
cancelHandler={this.closeErrorDialog}
open={this.state.openErrorDialog}
body={this.state.errorDialogMessage}
title={this.props.t('There is an error with the packages you selected')}
cancelComponent={this.props.t('Dismiss')}
/>
<Container className="home-container"> <Container className="home-container">
<Paper className="home-container-paper"> <Paper className="home-container-paper">
{this.state.devicesLoaded ? onLoad : notLoaded} {this.state.devicesLoaded ? onLoad : notLoaded}

View file

@ -2,6 +2,7 @@ $bg-color: #f0f0f0;
.home-container { .home-container {
margin-top: 30px; margin-top: 30px;
margin-bottom: 100px;
.home-container-paper { .home-container-paper {
padding: 30px; padding: 30px;
@ -76,7 +77,7 @@ $bg-color: #f0f0f0;
} }
.download-button { .download-button {
margin-top: 10px; margin-top: 17px;
.download-icon { .download-icon {
margin-right: 10px; margin-right: 10px;

View file

@ -22,6 +22,8 @@
"OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard", "OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard",
"Download OpenWrt firmware for your device!": "Laden Sie die OpenWrt-Firmware für Ihr Gerät herunter!", "Download OpenWrt firmware for your device!": "Laden Sie die OpenWrt-Firmware für Ihr Gerät herunter!",
"Target": "Ziel", "Target": "Ziel",
"Dismiss": "Entlassen",
"There is an error with the packages you selected": "Bei den ausgewählten Paketen ist ein Fehler aufgetreten",
"Downloads": "Herunterladen", "Downloads": "Herunterladen",
"Please use the input below to download firmware for your device!": "Bitte benutzen Sie den unten stehenden Eingang, um die Firmware für Ihr Gerät herunterzuladen!" "Please use the input below to download firmware for your device!": "Bitte benutzen Sie den unten stehenden Eingang, um die Firmware für Ihr Gerät herunterzuladen!"
} }

View file

@ -22,6 +22,8 @@
"OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard", "OpenWrt Firmware Selector Wizard": "OpenWrt Firmware Selector Wizard",
"Download OpenWrt firmware for your device!": "Download OpenWrt firmware for your device!", "Download OpenWrt firmware for your device!": "Download OpenWrt firmware for your device!",
"Target": "Target", "Target": "Target",
"Dismiss": "Dismiss",
"There is an error with the packages you selected": "There is an error with the packages you selected",
"Downloads": "Downloads", "Downloads": "Downloads",
"Please use the input below to download firmware for your device!": "Please use the input below to download firmware for your device!" "Please use the input below to download firmware for your device!": "Please use the input below to download firmware for your device!"
} }

View file

@ -13,17 +13,15 @@ class DataService {
getDevicePackages = (version, target, profile) => axios.get(base_api + 'packages_image?distro=openwrt&version=' + version.toLowerCase() + '&target=' + target + '&profile=' + profile.toLowerCase()); getDevicePackages = (version, target, profile) => axios.get(base_api + 'packages_image?distro=openwrt&version=' + version.toLowerCase() + '&target=' + target + '&profile=' + profile.toLowerCase());
buildImage = (board, packages, target, version) => { buildImage = (board, packages, target, version) => axios.post(base_api + 'build-request', {
return axios.post(base_api + 'build-request', { profile: board,
profile: board, board,
board, defaults: '',
defaults: '', distro: 'openwrt',
distro: 'openwrt', packages,
packages, target,
target, version,
version, });
});
};
buildStatusCheck = (request_hash) => axios.get(base_api + 'build-request/' + request_hash); buildStatusCheck = (request_hash) => axios.get(base_api + 'build-request/' + request_hash);