Skip to content

Commit

Permalink
fix: provide better error handling for invalid space ids
Browse files Browse the repository at this point in the history
closes #81
  • Loading branch information
juancarlosfarah committed May 16, 2019
1 parent f016419 commit 81a952c
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 20 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"@sentry/browser": "5.1.1",
"@sentry/electron": "0.17.1",
"archiver": "3.0.0",
"bson-objectid": "1.2.5",
"cheerio": "1.0.0-rc.3",
"classnames": "2.2.6",
"connected-react-router": "6.4.0",
Expand Down
3 changes: 3 additions & 0 deletions src/Styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ const Styles = theme => ({
input: {
margin: theme.spacing.unit,
},
button: {
margin: theme.spacing.unit,
},
});

export default Styles;
19 changes: 13 additions & 6 deletions src/components/VisitSpace.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,11 @@ import Styles from '../Styles';
import Loader from './LoadSpace';
import {
ERROR_MESSAGE_HEADER,
INVALID_SPACE_ID,
OFFLINE_ERROR_MESSAGE,
} from '../config/messages';
import MainMenu from './common/MainMenu';
import { isValidSpaceId } from '../utils/validators';

class VisitSpace extends Component {
state = {
Expand Down Expand Up @@ -64,13 +66,22 @@ class VisitSpace extends Component {
if (!window.navigator.onLine) {
return toastr.error(ERROR_MESSAGE_HEADER, OFFLINE_ERROR_MESSAGE);
}
if (!isValidSpaceId(id)) {
return toastr.error(ERROR_MESSAGE_HEADER, INVALID_SPACE_ID);
}
if (id && id !== '') {
const { replace } = history;
return replace(`/space/${id}`);
}
return false;
};

handleKeyPress = event => {
if (event.key === 'Enter') {
this.handleClick();
}
};

render() {
const { classes, theme, activity } = this.props;
const { open, spaceId } = this.state;
Expand Down Expand Up @@ -137,12 +148,7 @@ class VisitSpace extends Component {
>
<div className={classes.drawerHeader} />
<FormControl className={classes.formControl}>
<Typography
variant="h4"
color="inherit"
align="center"
style={{ margin: '2rem' }}
>
<Typography variant="h4" color="inherit" style={{ margin: '2rem' }}>
Visit a Space
</Typography>
<Input
Expand All @@ -152,6 +158,7 @@ class VisitSpace extends Component {
inputProps={{
'aria-label': 'Space ID',
}}
onKeyPress={this.handleKeyPress}
autoFocus
value={spaceId}
type="text"
Expand Down
132 changes: 118 additions & 14 deletions src/components/space/SpaceNotFound.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,124 @@
import React from 'react';
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import Button from '@material-ui/core//Button';
import { HOME_PATH } from '../../config/paths';
import CssBaseline from '@material-ui/core/CssBaseline';
import AppBar from '@material-ui/core/AppBar';
import classNames from 'classnames';
import Toolbar from '@material-ui/core/Toolbar';
import IconButton from '@material-ui/core/IconButton';
import MenuIcon from '@material-ui/icons/Menu';
import Drawer from '@material-ui/core/Drawer';
import Divider from '@material-ui/core/Divider';
import ChevronLeftIcon from '@material-ui/icons/ChevronLeft';
import ChevronRightIcon from '@material-ui/icons/ChevronRight';
import { withStyles } from '@material-ui/core';
import Typography from '@material-ui/core/Typography';
import MainMenu from '../common/MainMenu';
import Styles from '../../Styles';
import { HOME_PATH, VISIT_PATH } from '../../config/paths';

const SpaceNotFound = ({ history: { replace } }) => {
return (
<div>
Space not found.
<Button onClick={() => replace(HOME_PATH)}>Home</Button>
</div>
);
};
class SpaceNotFound extends Component {
state = {
open: false,
};

SpaceNotFound.propTypes = {
history: PropTypes.shape({ length: PropTypes.number.isRequired }).isRequired,
};
static propTypes = {
classes: PropTypes.shape({}).isRequired,
theme: PropTypes.shape({}).isRequired,
history: PropTypes.shape({ length: PropTypes.number.isRequired })
.isRequired,
};

export default withRouter(SpaceNotFound);
handleDrawerOpen = () => {
this.setState({ open: true });
};

handleDrawerClose = () => {
this.setState({ open: false });
};

render() {
const { open } = this.state;
const {
history: { replace },
classes,
theme,
} = this.props;
return (
<div className={classes.root}>
<CssBaseline />
<AppBar
position="fixed"
className={classNames(classes.appBar, {
[classes.appBarShift]: open,
})}
>
<Toolbar disableGutters={!open}>
<IconButton
color="inherit"
aria-label="Open drawer"
onClick={this.handleDrawerOpen}
className={classNames(classes.menuButton, open && classes.hide)}
>
<MenuIcon />
</IconButton>
</Toolbar>
</AppBar>
<Drawer
className={classes.drawer}
variant="persistent"
anchor="left"
open={open}
classes={{
paper: classes.drawerPaper,
}}
>
<div className={classes.drawerHeader}>
<IconButton onClick={this.handleDrawerClose}>
{theme.direction === 'ltr' ? (
<ChevronLeftIcon />
) : (
<ChevronRightIcon />
)}
</IconButton>
</div>
<Divider />
<MainMenu />
</Drawer>
<main
className={classNames(classes.content, {
[classes.contentShift]: open,
})}
>
<div className={classes.drawerHeader} />
<div>
<Typography variant="h5" color="inherit" style={{ margin: '2rem' }}>
Space Not Found
</Typography>
<Button
variant="contained"
color="primary"
className={classes.button}
onClick={() => replace(HOME_PATH)}
>
Home
</Button>
<Button
variant="contained"
color="primary"
className={classes.button}
onClick={() => replace(VISIT_PATH)}
>
Visit Another Space
</Button>
</div>
</main>
</div>
);
}
}

const StyledComponent = withStyles(Styles, { withTheme: true })(SpaceNotFound);

export default withRouter(StyledComponent);
1 change: 1 addition & 0 deletions src/config/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export const APPLICATION = 'Application';
export const IFRAME = 'application/octet-stream';
export const DEFAULT_RADIUS = 50;
export const DEFAULT_LANGUAGE = 'en_all';
export const SHORT_ID_LENGTH = 6;
2 changes: 2 additions & 0 deletions src/config/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const ERROR_GETTING_USER_FOLDER =
'There was an error getting your user folder.';
const ERROR_GETTING_LANGUAGE = 'There was an error getting the language.';
const ERROR_SETTING_LANGUAGE = 'There was an error setting the language.';
const INVALID_SPACE_ID = 'Invalid space ID.';

module.exports = {
ERROR_GETTING_LANGUAGE,
Expand All @@ -52,4 +53,5 @@ module.exports = {
ERROR_GETTING_GEOLOCATION,
ERROR_GETTING_SPACES_NEARBY,
ERROR_GETTING_USER_FOLDER,
INVALID_SPACE_ID,
};
12 changes: 12 additions & 0 deletions src/utils/validators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import ObjectId from 'bson-objectid';
import { SHORT_ID_LENGTH } from '../config/constants';

const isValidSpaceShortId = id => {
return id.length === SHORT_ID_LENGTH && /^[a-z0-9]+$/i.test(id);
};

const isValidLongSpaceId = id => ObjectId.isValid(id);

const isValidSpaceId = id => isValidLongSpaceId(id) || isValidSpaceShortId(id);

export { isValidSpaceId, isValidLongSpaceId, isValidSpaceShortId };
5 changes: 5 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2841,6 +2841,11 @@ bser@^2.0.0:
dependencies:
node-int64 "^0.4.0"

bson-objectid@1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/bson-objectid/-/bson-objectid-1.2.5.tgz#622c0c9d40c02aece5ac5b61a70248ec303047f0"
integrity sha512-tKLZNr1eyK/Dd48k/5FHXmpN69dUHMioQCvNv1kxy8qJINkoyQkjf7+u933EZ77bMXC3lmN1HmyFm2WS6mRYxg==

buffer-alloc-unsafe@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0"
Expand Down

0 comments on commit 81a952c

Please sign in to comment.