feat(cp-gql-mock-server, cp-frontend): Add missing dg and service error messaging

This commit is contained in:
JUDIT GRESKOVITS 2017-08-07 18:11:13 +01:00 committed by Judit Greskovits
parent 0917d67b07
commit 2eb7f4197f
18 changed files with 281 additions and 79 deletions

View File

@ -1,3 +1,4 @@
export { default as Loader } from './loader'; export { default as Loader } from './loader';
export { default as ErrorMessage } from './error'; export { default as ErrorMessage } from './error';
export { default as WarningMessage } from './warning'; export { default as WarningMessage } from './warning';
export { default as ModalErrorMessage } from './modal-error';

View File

@ -0,0 +1,24 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import { ModalHeading, ModalText, Button } from 'joyent-ui-toolkit';
const StyledHeading = styled(ModalHeading)`
color: ${props => props.theme.red};
`;
const ModalErrorMessage = ({ title, message, onCloseClick }) =>
<div>
<StyledHeading>{title}</StyledHeading>
<ModalText marginBottom="3">{message}
</ModalText>
<Button onClick={onCloseClick} secondary>Close </Button>
</div>;
ModalErrorMessage.propTypes = {
title: PropTypes.string,
message: PropTypes.string.isRequired,
onCloseClick: PropTypes.func.isRequired
};
export default ModalErrorMessage;

View File

@ -2,3 +2,4 @@ export { default as Breadcrumb } from './breadcrumb';
export { default as Menu } from './menu'; export { default as Menu } from './menu';
export { default as Header } from './header'; export { default as Header } from './header';
export { default as Title } from './title'; export { default as Title } from './title';
export { default as NotFound } from './not-found';

View File

@ -0,0 +1,44 @@
import React from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';
import remcalc from 'remcalc';
import { H1, H2, P, Button } from 'joyent-ui-toolkit';
import { LayoutContainer } from '@components/layout';
const StyledContainer = styled.div`
margin-top: ${remcalc(60)};
`;
const StyledTitle = styled(H1)`
font-weight: normal;
font-size: ${remcalc(32)};
`;
const StyledP = styled(P)`
margin-bottom: ${remcalc(30)};
max-width: ${remcalc(490)};
`;
const NotFound = ({
title = 'I have no memory of this place',
message = 'HTTP 404: We cant find what you are looking for. Next time, always follow your nose.',
link = 'Back to dashboard',
to = '/deployment-groups'
}) => (
<LayoutContainer>
<StyledContainer>
<StyledTitle>{title}</StyledTitle>
<StyledP>{message}</StyledP>
<Button to={to}>{link}</Button>
</StyledContainer>
</LayoutContainer>
);
NotFound.propTypes = {
title: PropTypes.string,
message: PropTypes.string,
link: PropTypes.string,
to: PropTypes.string
}
export default NotFound;

View File

@ -3,9 +3,10 @@ import PropTypes from 'prop-types';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import DeploymentGroupDeleteMutation from '@graphql/DeploymentGroupDeleteMutation.gql'; import DeploymentGroupDeleteMutation from '@graphql/DeploymentGroupDeleteMutation.gql';
import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql'; import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql';
import { Loader, ErrorMessage } from '@components/messaging'; import { Loader, ModalErrorMessage } from '@components/messaging';
import { DeploymentGroupDelete as DeploymentGroupDeleteComponent } from '@components/deployment-group'; import { DeploymentGroupDelete as DeploymentGroupDeleteComponent } from '@components/deployment-group';
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit'; import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit'
import { withNotFound, GqlPaths } from '@containers/navigation';
class DeploymentGroupDelete extends Component { class DeploymentGroupDelete extends Component {
@ -18,7 +19,7 @@ class DeploymentGroupDelete extends Component {
} }
render() { render() {
const { loading, error } = this.props; const { location, history, match, loading, error } = this.props;
const handleCloseClick = evt => { const handleCloseClick = evt => {
const closeUrl = match.url.split('/').slice(0, -2).join('/'); const closeUrl = match.url.split('/').slice(0, -2).join('/');
@ -36,32 +37,26 @@ class DeploymentGroupDelete extends Component {
if (error) { if (error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ErrorMessage <ModalErrorMessage
title='Ooops!' title='Ooops!'
message='An error occurred while loading your deployment group.' /> message='An error occurred while loading your deployment group.'
onCloseClick={handleCloseClick} />
</Modal> </Modal>
); );
} }
const { const {
deploymentGroup, deploymentGroup,
deleteDeploymentGroup, deleteDeploymentGroup
history,
match
} = this.props; } = this.props;
if (this.state.error) { if (this.state.error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalHeading> <ModalErrorMessage
Deleting a deployment group: <br /> {deploymentGroup.name}
</ModalHeading>
<ErrorMessage
title='Ooops!' title='Ooops!'
message='An error occurred while attempting to delete your deployment group.' /> message={`An error occured while attempting to delete the ${deploymentGroup.name} deployment group.`}
<Button onClick={handleCloseClick} secondary> onCloseClick={handleCloseClick} />
Ok
</Button>
</Modal> </Modal>
); );
} }
@ -70,6 +65,7 @@ class DeploymentGroupDelete extends Component {
deleteDeploymentGroup(deploymentGroup.id) deleteDeploymentGroup(deploymentGroup.id)
.then(() => handleCloseClick()) .then(() => handleCloseClick())
.catch((err) => { .catch((err) => {
console.log('err = ', err);
this.setState({ error: err }); this.setState({ error: err });
}); });
}; };
@ -120,7 +116,8 @@ const DeploymentGroupGql = graphql(DeploymentGroupQuery, {
const DeploymentGroupDeleteWithData = compose( const DeploymentGroupDeleteWithData = compose(
DeleteDeploymentGroupGql, DeleteDeploymentGroupGql,
DeploymentGroupGql DeploymentGroupGql,
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
)(DeploymentGroupDelete); )(DeploymentGroupDelete);
export default DeploymentGroupDeleteWithData; export default DeploymentGroupDeleteWithData;

View File

@ -13,6 +13,7 @@ import { ErrorMessage, Loader } from '@components/messaging';
import DeploymentGroupsQuery from '@graphql/DeploymentGroups.gql'; import DeploymentGroupsQuery from '@graphql/DeploymentGroups.gql';
import DeploymentGroupsImportableQuery from '@graphql/DeploymentGroupsImportable.gql'; import DeploymentGroupsImportableQuery from '@graphql/DeploymentGroupsImportable.gql';
import { H2, H3, Small, IconButton, BinIcon } from 'joyent-ui-toolkit'; import { H2, H3, Small, IconButton, BinIcon } from 'joyent-ui-toolkit';
import { withNotFound, GqlPaths } from '@containers/navigation';
const DGsRows = Row.extend` const DGsRows = Row.extend`
margin-top: ${remcalc(-7)}; margin-top: ${remcalc(-7)};
@ -216,5 +217,6 @@ export default compose(
props: ({ data: { importableDeploymentGroups } }) => ({ props: ({ data: { importableDeploymentGroups } }) => ({
importable: importableDeploymentGroups importable: importableDeploymentGroups
}) })
}) }),
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
)(DeploymentGroupList); )(DeploymentGroupList);

View File

@ -11,6 +11,8 @@ import { Title } from '@components/navigation';
import { Loader, ErrorMessage } from '@components/messaging'; import { Loader, ErrorMessage } from '@components/messaging';
import { InstanceListItem, EmptyInstances } from '@components/instances'; import { InstanceListItem, EmptyInstances } from '@components/instances';
import { withNotFound, GqlPaths } from '@containers/navigation';
const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => { const InstanceList = ({ deploymentGroup, instances = [], loading, error }) => {
const _title = <Title>Instances</Title>; const _title = <Title>Instances</Title>;
@ -75,7 +77,7 @@ const InstanceListGql = graphql(InstancesQuery, {
} }
}; };
}, },
props: ({ data: { deploymentGroup, loading, error } }) => ({ props: ({ data: { deploymentGroup, loading, error }}) => ({
deploymentGroup, deploymentGroup,
instances: sortBy( instances: sortBy(
forceArray( forceArray(
@ -92,4 +94,10 @@ const InstanceListGql = graphql(InstancesQuery, {
}) })
}); });
export default compose(InstanceListGql)(InstanceList); export default compose(
InstanceListGql,
withNotFound([
GqlPaths.DEPLOYMENT_GROUP,
GqlPaths.SERVICES
])
)(InstanceList);

View File

@ -1,12 +1,15 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { compose } from 'react-apollo';
import { Breadcrumb as BreadcrumbComponent } from '@components/navigation'; import { Breadcrumb as BreadcrumbComponent } from '@components/navigation';
import withNotFound from './not-found-hoc';
import { import {
deploymentGroupBySlugSelector, deploymentGroupBySlugSelector,
serviceBySlugSelector serviceBySlugSelector
} from '@root/state/selectors'; } from '@root/state/selectors';
const Breadcrumb = ({ deploymentGroup, service, location }) => { const Breadcrumb = ({ deploymentGroup, service, location }) => {
const path = location.pathname.split('/'); const path = location.pathname.split('/');
const links = [ const links = [
@ -33,7 +36,7 @@ const Breadcrumb = ({ deploymentGroup, service, location }) => {
return <BreadcrumbComponent links={links} />; return <BreadcrumbComponent links={links} />;
}; };
const ConnectedBreadcrumb = connect( const connectBreadcrumb = connect(
(state, ownProps) => { (state, ownProps) => {
const params = ownProps.match.params; const params = ownProps.match.params;
const deploymentGroupSlug = params.deploymentGroup; const deploymentGroupSlug = params.deploymentGroup;
@ -47,6 +50,9 @@ const ConnectedBreadcrumb = connect(
}; };
}, },
dispatch => ({}) dispatch => ({})
)(Breadcrumb); );
export default ConnectedBreadcrumb; export default compose(
connectBreadcrumb,
withNotFound()
)(Breadcrumb);

View File

@ -1,3 +1,4 @@
export { default as Header } from './header'; export { default as Header } from './header';
export { default as Breadcrumb } from './breadcrumb'; export { default as Breadcrumb } from './breadcrumb';
export { default as Menu } from './menu'; export { default as Menu } from './menu';
export { default as withNotFound, GqlPaths } from './not-found-hoc';

View File

@ -1,8 +1,11 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { compose } from 'react-apollo';
import withNotFound from './not-found-hoc';
import { Menu as MenuComponent } from '@components/navigation'; import { Menu as MenuComponent } from '@components/navigation';
const Menu = ({ match, sections }) => { const Menu = ({ location, match, sections }) => {
if (!sections || !sections.length) { if (!sections || !sections.length) {
return null; return null;
} }
@ -16,7 +19,7 @@ const Menu = ({ match, sections }) => {
return <MenuComponent links={sectionsWithPathnames} />; return <MenuComponent links={sectionsWithPathnames} />;
}; };
const ConnectedMenu = connect( const connectMenu = connect(
(state, ownProps) => { (state, ownProps) => {
const params = ownProps.match.params; const params = ownProps.match.params;
const deploymentGroupSlug = params.deploymentGroup; const deploymentGroupSlug = params.deploymentGroup;
@ -35,6 +38,9 @@ const ConnectedMenu = connect(
}; };
}, },
dispatch => ({}) dispatch => ({})
)(Menu); );
export default ConnectedMenu; export default compose(
connectMenu,
withNotFound()
)(Menu);

View File

@ -0,0 +1,81 @@
import React, { Component } from 'react';
import { NotFound } from '@components/navigation';
export const GqlPaths = {
DEPLOYMENT_GROUP: 'deploymentGroup',
SERVICES: 'services'
}
export default (paths) => {
return (WrappedComponent) => {
return class extends Component {
constructor(props) {
super(props);
}
componentWillReceiveProps(nextProps) {
if(paths) {
const {
error,
location,
history,
match
} = nextProps;
if (error && (!location.state || !location.state.notFound)) {
if(error.graphQLErrors && error.graphQLErrors.length) {
const graphQLError = error.graphQLErrors[0];
if(graphQLError.message === 'Not Found') {
const notFound = graphQLError.path.pop();
if(paths.indexOf(notFound) > -1) {
history.replace(location.pathname, { notFound });
}
}
}
}
}
}
render() {
const {
error,
location,
match
} = this.props;
if(location.state && location.state.notFound) {
const notFound = location.state.notFound;
if(paths && paths.indexOf(notFound) > -1) {
let title;
let to;
let link;
if(notFound === 'services' || notFound === 'service') {
title = 'This service doesnt exist';
to = match.url.split('/').slice(0, 3).join('/');
link = 'Back to services';
}
else if(notFound === 'deploymentGroup') {
title = 'This deployment group doesnt exist';
to = '/deployment-group';
link = 'Back to dashboard';
}
return (
<NotFound
title={title}
message='Sorry, but our princess is in another castle.'
to={to}
link={link}
/>
)
}
return null;
}
return <WrappedComponent {...this.props} />
}
}
}
}

View File

@ -2,10 +2,11 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import ServicesDeleteMutation from '@graphql/ServicesDeleteMutation.gql'; import ServicesDeleteMutation from '@graphql/ServicesDeleteMutation.gql';
import { Loader, ErrorMessage } from '@components/messaging'; import { Loader, ModalErrorMessage } from '@components/messaging';
import { ServiceDelete as ServiceDeleteComponent } from '@components/service'; import { ServiceDelete as ServiceDeleteComponent } from '@components/service';
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit'; import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
import ServiceGql from './service-gql'; import ServiceGql from './service-gql';
import { withNotFound, GqlPaths } from '@containers/navigation';
class ServiceDelete extends Component { class ServiceDelete extends Component {
@ -18,7 +19,7 @@ class ServiceDelete extends Component {
} }
render() { render() {
const { loading, error } = this.props; const { loading, error, match, history, location } = this.props;
const handleCloseClick = evt => { const handleCloseClick = evt => {
const closeUrl = match.url.split('/').slice(0, -2).join('/'); const closeUrl = match.url.split('/').slice(0, -2).join('/');
@ -36,27 +37,23 @@ class ServiceDelete extends Component {
if (error) { if (error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ErrorMessage <ModalErrorMessage
title='Ooops!' title='Ooops!'
message='An error occured while loading your service.' /> message='An error occured while loading your service.'
onCloseClick={handleCloseClick} />
</Modal> </Modal>
); );
} }
const { service, deleteServices, history, match } = this.props; const { service, deleteServices } = this.props;
if(this.state.error) { if(this.state.error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalHeading> <ModalErrorMessage
Deleting a service: <br /> {service.name}
</ModalHeading>
<ErrorMessage
title='Ooops!' title='Ooops!'
message='An error occurred while attempting to delete your service.' /> message={`An error occured while attempting to delete the ${service.name} service.`}
<Button onClick={handleCloseClick} secondary> onCloseClick={handleCloseClick} />
Ok
</Button>
</Modal> </Modal>
); );
} }
@ -96,4 +93,8 @@ const DeleteServicesGql = graphql(ServicesDeleteMutation, {
}) })
}); });
export default compose(DeleteServicesGql, ServiceGql)(ServiceDelete); export default compose(
DeleteServicesGql,
ServiceGql,
withNotFound([ GqlPaths.SERVICES ])
)(ServiceDelete);

View File

@ -3,10 +3,11 @@ import PropTypes from 'prop-types';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import { reduxForm } from 'redux-form'; import { reduxForm } from 'redux-form';
import ServiceScaleMutation from '@graphql/ServiceScale.gql'; import ServiceScaleMutation from '@graphql/ServiceScale.gql';
import { Loader, ErrorMessage } from '@components/messaging'; import { Loader, ModalErrorMessage } from '@components/messaging';
import { ServiceScale as ServiceScaleComponent } from '@components/service'; import { ServiceScale as ServiceScaleComponent } from '@components/service';
import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit'; import { Modal, ModalHeading, Button } from 'joyent-ui-toolkit';
import ServiceGql from './service-gql'; import ServiceGql from './service-gql';
import { withNotFound, GqlPaths } from '@containers/navigation';
class ServiceScale extends Component { class ServiceScale extends Component {
@ -19,7 +20,7 @@ class ServiceScale extends Component {
} }
render() { render() {
const { loading, error } = this.props; const { loading, error, match, history, location } = this.props;
const handleCloseClick = evt => { const handleCloseClick = evt => {
const closeUrl = match.url.split('/').slice(0, -2).join('/'); const closeUrl = match.url.split('/').slice(0, -2).join('/');
@ -37,27 +38,23 @@ class ServiceScale extends Component {
if (error) { if (error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ErrorMessage <ModalErrorMessage
title='Ooops!' title='Ooops!'
message='An error occured while loading your service.' /> message='An error occured while loading your service.'
onCloseClick={handleCloseClick} />
</Modal> </Modal>
); );
} }
const { service, scale, history, match } = this.props; const { service, scale } = this.props;
if(this.state.error) { if(this.state.error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalHeading> <ModalErrorMessage
Scaling a service: <br /> {service.name}
</ModalHeading>
<ErrorMessage
title='Ooops!' title='Ooops!'
message='An error occurred while attempting to scale your service.' /> message={`An error occured while attempting to scale the ${service.name} service.`}
<Button onClick={handleCloseClick} secondary> onCloseClick={handleCloseClick} />
Ok
</Button>
</Modal> </Modal>
); );
} }
@ -124,4 +121,8 @@ const ServiceScaleGql = graphql(ServiceScaleMutation, {
}) })
}); });
export default compose(ServiceScaleGql, ServiceGql)(ServiceScale); export default compose(
ServiceScaleGql,
ServiceGql,
withNotFound([ GqlPaths.SERVICES ])
)(ServiceScale);

View File

@ -21,6 +21,8 @@ import { ServicesQuickActions } from '@components/services';
import { Message } from 'joyent-ui-toolkit'; import { Message } from 'joyent-ui-toolkit';
import { withNotFound, GqlPaths } from '@containers/navigation';
const StyledContainer = styled.div` const StyledContainer = styled.div`
position: relative; position: relative;
`; `;
@ -55,7 +57,8 @@ class ServiceList extends Component {
push, push,
restartServices, restartServices,
stopServices, stopServices,
startServices startServices,
location
} = this.props; } = this.props;
if (loading && !forceArray(services).length) { if (loading && !forceArray(services).length) {
@ -77,6 +80,7 @@ class ServiceList extends Component {
} }
if ( if (
deploymentGroup &&
deploymentGroup.status === 'PROVISIONING' && deploymentGroup.status === 'PROVISIONING' &&
!forceArray(services).length !forceArray(services).length
) { ) {
@ -222,7 +226,7 @@ const ServicesGql = graphql(ServicesQuery, {
} }
}; };
}, },
props: ({ data: { deploymentGroup, loading, error } }) => ({ props: ({ data: { deploymentGroup, loading, error }}) => ({
deploymentGroup, deploymentGroup,
services: deploymentGroup services: deploymentGroup
? processServices(deploymentGroup.services, null) ? processServices(deploymentGroup.services, null)
@ -256,7 +260,8 @@ const ServiceListWithData = compose(
ServicesStopGql, ServicesStopGql,
ServicesStartGql, ServicesStartGql,
ServicesGql, ServicesGql,
UiConnect UiConnect,
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
)(ServiceList); )(ServiceList);
export default ServiceListWithData; export default ServiceListWithData;

View File

@ -1,10 +1,12 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { compose } from 'react-apollo';
import { Col, Row } from 'react-styled-flexboxgrid'; import { Col, Row } from 'react-styled-flexboxgrid';
import remcalc from 'remcalc'; import remcalc from 'remcalc';
import unitcalc from 'unitcalc'; import unitcalc from 'unitcalc';
import { LayoutContainer } from '@components/layout'; import { LayoutContainer } from '@components/layout';
import { Title } from '@components/navigation'; import { Title } from '@components/navigation';
import { withNotFound } from '@containers/navigation';
import { H2, FormGroup, Toggle, ToggleList, Legend } from 'joyent-ui-toolkit'; import { H2, FormGroup, Toggle, ToggleList, Legend } from 'joyent-ui-toolkit';
@ -19,6 +21,11 @@ const PaddedRow = Row.extend`
`; `;
const ServicesMenu = ({ location, history: { push } }) => { const ServicesMenu = ({ location, history: { push } }) => {
if(location.state && location.state.notFound) {
return null;
}
const toggleValue = location.pathname.split('-').pop(); const toggleValue = location.pathname.split('-').pop();
const handleToggle = evt => { const handleToggle = evt => {
@ -57,4 +64,4 @@ ServicesMenu.propTypes = {
history: PropTypes.object.isRequired history: PropTypes.object.isRequired
}; };
export default ServicesMenu; export default compose(withNotFound())(ServicesMenu);

View File

@ -2,6 +2,7 @@ import React, { Component } from 'react';
import { compose, graphql } from 'react-apollo'; import { compose, graphql } from 'react-apollo';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import styled from 'styled-components'; import styled from 'styled-components';
import forceArray from 'force-array';
import ServicesQuery from '@graphql/Services.gql'; import ServicesQuery from '@graphql/Services.gql';
import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql'; import ServicesRestartMutation from '@graphql/ServicesRestartMutation.gql';
import ServicesStopMutation from '@graphql/ServicesStopMutation.gql'; import ServicesStopMutation from '@graphql/ServicesStopMutation.gql';
@ -17,6 +18,8 @@ import { ServicesQuickActions } from '@components/services';
import { Topology } from 'joyent-ui-toolkit'; import { Topology } from 'joyent-ui-toolkit';
import { withNotFound, GqlPaths } from '@containers/navigation';
const StyledBackground = styled.div` const StyledBackground = styled.div`
padding: ${unitcalc(4)}; padding: ${unitcalc(4)};
background-color: ${props => props.theme.whiteActive}; background-color: ${props => props.theme.whiteActive};
@ -50,10 +53,11 @@ class ServicesTopology extends Component {
toggleServicesQuickActions, toggleServicesQuickActions,
restartServices, restartServices,
stopServices, stopServices,
startServices startServices,
location
} = this.props; } = this.props;
if (loading) { if (loading && !forceArray(services).length) {
return ( return (
<LayoutContainer center> <LayoutContainer center>
<Loader /> <Loader />
@ -62,7 +66,6 @@ class ServicesTopology extends Component {
} }
if (error) { if (error) {
return ( return (
<LayoutContainer> <LayoutContainer>
<ErrorMessage <ErrorMessage
@ -73,6 +76,7 @@ class ServicesTopology extends Component {
} }
if ( if (
deploymentGroup &&
deploymentGroup.status === 'PROVISIONING' && deploymentGroup.status === 'PROVISIONING' &&
!forceArray(services).length !forceArray(services).length
) { ) {
@ -232,7 +236,8 @@ const ServicesTopologyWithData = compose(
ServicesStopGql, ServicesStopGql,
ServicesStartGql, ServicesStartGql,
ServicesGql, ServicesGql,
UiConnect UiConnect,
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ])
)(ServicesTopology); )(ServicesTopology);
export default ServicesTopologyWithData; export default ServicesTopologyWithData;

View File

@ -21,6 +21,8 @@ import {
import { DeploymentGroupDelete } from '@containers/deployment-group'; import { DeploymentGroupDelete } from '@containers/deployment-group';
import { NotFound } from '@components/navigation';
const Container = styled.div` const Container = styled.div`
display: flex; display: flex;
flex: 1 1 auto; flex: 1 1 auto;
@ -30,25 +32,23 @@ const Container = styled.div`
const rootRedirect = p => <Redirect to="/deployment-groups" />; const rootRedirect = p => <Redirect to="/deployment-groups" />;
const deploymentGroupRedirect = p => const servicesListRedirect = p =>
<Redirect <Redirect
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`} to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`}
/>; />;
const servicesTopologyRedirect = p =>
<Redirect
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-topology`}
/>;
const serviceRedirect = p => const serviceRedirect = p =>
<Redirect <Redirect
to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match
.params.service}/instances`} .params.service}/instances`}
/>; />;
// TODO component to be designed const App = p => (
const notFound = p => {
return <p>
NOT FOUND
</p>;
}
const APP = p => (
<div> <div>
<Switch> <Switch>
@ -141,7 +141,7 @@ const APP = p => (
<Route <Route
path="/deployment-groups/:deploymentGroup" path="/deployment-groups/:deploymentGroup"
component={deploymentGroupRedirect} component={servicesListRedirect}
/> />
</Switch> </Switch>
@ -171,11 +171,11 @@ const APP = p => (
<Route <Route
path="/deployment-groups/:deploymentGroup/services-list" path="/deployment-groups/:deploymentGroup/services-list"
component={deploymentGroupRedirect} component={servicesListRedirect}
/> />
<Route <Route
path="/deployment-groups/:deploymentGroup/services-topology" path="/deployment-groups/:deploymentGroup/services-topology"
component={deploymentGroupRedirect} component={servicesTopologyRedirect}
/> />
</Switch> </Switch>
</div> </div>
@ -186,9 +186,9 @@ const Router = (
<Container> <Container>
<Route path="/" component={Header} /> <Route path="/" component={Header} />
<Switch> <Switch>
<Route path="/deployment-groups" component={APP} /> <Route path="/deployment-groups" component={App} />
<Route path="/" exact component={rootRedirect} /> <Route path="/" exact component={rootRedirect} />
<Route path="/*" component={notFound} /> <Route path="/*" component={NotFound} />
</Switch> </Switch>
</Container> </Container>
</BrowserRouter> </BrowserRouter>

View File

@ -9,6 +9,7 @@ const random = require('lodash.random');
const uniq = require('lodash.uniq'); const uniq = require('lodash.uniq');
const yaml = require('js-yaml'); const yaml = require('js-yaml');
const hasha = require('hasha'); const hasha = require('hasha');
const Boom = require('boom');
const wpData = require('./wp-data.json'); const wpData = require('./wp-data.json');
const cpData = require('./cp-data.json'); const cpData = require('./cp-data.json');
@ -96,7 +97,13 @@ const getServices = query => {
).map(serviceId => lfind(services, ['id', serviceId])); ).map(serviceId => lfind(services, ['id', serviceId]));
}); });
return Promise.resolve(services); return Promise.resolve(services)
.then((services) => {
if(!services || !services.length) {
throw Boom.notFound();
}
return services;
});
}; };
const getDeploymentGroups = query => { const getDeploymentGroups = query => {
@ -114,7 +121,12 @@ const getDeploymentGroups = query => {
return Promise.resolve( return Promise.resolve(
deploymentGroups.filter(find(cleanQuery(query))).map(addNestedResolvers) deploymentGroups.filter(find(cleanQuery(query))).map(addNestedResolvers)
); ).then((deploymentGroups) => {
if(!deploymentGroups || !deploymentGroups.length) {
throw Boom.notFound();
}
return deploymentGroups;
});
}; };
const getPortal = () => const getPortal = () =>