style: format

This commit is contained in:
Sérgio Ramos 2017-08-28 20:21:08 +01:00
parent d1af5eec1a
commit cac9453154
153 changed files with 995 additions and 1225 deletions

View File

@ -16,11 +16,13 @@ module.exports = {
const { list, get } = api.users; const { list, get } = api.users;
return args.id return args.id
? get(args).then(user => [user]).then(user => ? get(args)
Object.assign(user, { .then(user => [user])
isUser: true .then(user =>
}) Object.assign(user, {
) isUser: true
})
)
: list(); : list();
} }
}; };

View File

@ -17,9 +17,7 @@ class App extends Component {
render() { render() {
return ( return (
<ApolloProvider client={client} store={store}> <ApolloProvider client={client} store={store}>
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>{Router}</ThemeProvider>
{Router}
</ThemeProvider>
</ApolloProvider> </ApolloProvider>
); );
} }

View File

@ -6,7 +6,7 @@ const DeploymentGroupDelete = ({
deploymentGroup, deploymentGroup,
onCancelClick = () => {}, onCancelClick = () => {},
onConfirmClick = () => {} onConfirmClick = () => {}
}) => }) => (
<div> <div>
<ModalHeading> <ModalHeading>
Deleting a deployment group: <br /> {deploymentGroup.name} Deleting a deployment group: <br /> {deploymentGroup.name}
@ -20,7 +20,8 @@ const DeploymentGroupDelete = ({
Cancel Cancel
</Button> </Button>
<Button onClick={onConfirmClick}>Delete deployment group</Button> <Button onClick={onConfirmClick}>Delete deployment group</Button>
</div>; </div>
);
DeploymentGroupDelete.propTypes = { DeploymentGroupDelete.propTypes = {
deploymentGroup: PropTypes.object.isRequired, deploymentGroup: PropTypes.object.isRequired,

View File

@ -3,9 +3,10 @@ import React from 'react';
import { Col, Row } from 'react-styled-flexboxgrid'; import { Col, Row } from 'react-styled-flexboxgrid';
import { P } from 'joyent-ui-toolkit'; import { P } from 'joyent-ui-toolkit';
export default () => export default () => (
<Row> <Row>
<Col xs={12}> <Col xs={12}>
<P>You don't have any instances</P> <P>You don't have any instances</P>
</Col> </Col>
</Row>; </Row>
);

View File

@ -65,7 +65,16 @@ const StyledCard = Card.extend`
background-color: ${props => props.theme.white}; background-color: ${props => props.theme.white};
${isOr('stopping', 'stopped', 'offline', 'destroyed', 'failed', 'deleted', 'incomplete', 'unknown')` ${isOr(
'stopping',
'stopped',
'offline',
'destroyed',
'failed',
'deleted',
'incomplete',
'unknown'
)`
background-color: ${props => props.theme.background}; background-color: ${props => props.theme.background};
& [name="card-options"] > button { & [name="card-options"] > button {
@ -80,7 +89,6 @@ const InstanceCard = ({
onStatusMouseOver = () => {}, onStatusMouseOver = () => {},
onMouseOut = () => {} onMouseOut = () => {}
}) => { }) => {
const statusProps = STATUSES.reduce( const statusProps = STATUSES.reduce(
(acc, name) => (acc, name) =>
Object.assign(acc, { Object.assign(acc, {
@ -92,42 +100,34 @@ const InstanceCard = ({
const label = (instance.healthy || 'UNKNOWN').toLowerCase(); const label = (instance.healthy || 'UNKNOWN').toLowerCase();
const icon = <HealthyIcon healthy={instance.healthy} />; const icon = <HealthyIcon healthy={instance.healthy} />;
const handleHealthMouseOver = (evt) => { const handleHealthMouseOver = evt => {
onHealthMouseOver(evt, instance); onHealthMouseOver(evt, instance);
} };
const handleStatusMouseOver = (evt) => { const handleStatusMouseOver = evt => {
onStatusMouseOver(evt, instance); onStatusMouseOver(evt, instance);
} };
const handleMouseOut = (evt) => { const handleMouseOut = evt => {
onMouseOut(evt); onMouseOut(evt);
} };
return ( return (
<StyledCard collapsed={true} key={instance.uuid} {...statusProps}> <StyledCard collapsed={true} key={instance.uuid} {...statusProps}>
<CardView> <CardView>
<CardTitle> <CardTitle>{instance.name}</CardTitle>
{instance.name}
</CardTitle>
<CardDescription> <CardDescription>
<div <div onMouseOver={handleHealthMouseOver} onMouseOut={handleMouseOut}>
onMouseOver={handleHealthMouseOver}
onMouseOut={handleMouseOut}
>
<CardInfo <CardInfo
icon={icon} icon={icon}
iconPosition='left' iconPosition="left"
label={label} label={label}
color='dark' color="dark"
/> />
</div> </div>
</CardDescription> </CardDescription>
<CardDescription> <CardDescription>
<div <div onMouseOver={handleStatusMouseOver} onMouseOut={handleMouseOut}>
onMouseOver={handleStatusMouseOver}
onMouseOut={handleMouseOut}
>
<Label> <Label>
<Dot {...statusProps} /> <Dot {...statusProps} />
{titleCase(instance.status)} {titleCase(instance.status)}
@ -136,7 +136,7 @@ const InstanceCard = ({
</CardDescription> </CardDescription>
</CardView> </CardView>
</StyledCard> </StyledCard>
) );
}; };
InstanceCard.propTypes = { InstanceCard.propTypes = {

View File

@ -143,31 +143,29 @@ class ManifestEditorBundle extends Component {
const { ManifestEditor } = this.state; const { ManifestEditor } = this.state;
const { children, ...rest } = this.props; const { children, ...rest } = this.props;
return ( return <ManifestEditor {...rest}>{children}</ManifestEditor>;
<ManifestEditor {...rest}>
{children}
</ManifestEditor>
);
} }
} }
const MEditor = ({ input, defaultValue, readOnly }) => const MEditor = ({ input, defaultValue, readOnly }) => (
<ManifestEditorBundle <ManifestEditorBundle
mode="yaml" mode="yaml"
{...input} {...input}
value={input.value || defaultValue} value={input.value || defaultValue}
readOnly={readOnly} readOnly={readOnly}
/>; />
);
const EEditor = ({ input, defaultValue, readOnly }) => const EEditor = ({ input, defaultValue, readOnly }) => (
<ManifestEditorBundle <ManifestEditorBundle
mode="ini" mode="ini"
{...input} {...input}
value={input.value || defaultValue} value={input.value || defaultValue}
readOnly={readOnly} readOnly={readOnly}
/>; />
);
export const Name = ({ handleSubmit, onCancel, dirty }) => export const Name = ({ handleSubmit, onCancel, dirty }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Row> <Row>
<Col xs={12} md={3} lg={3}> <Col xs={12} md={3} lg={3}>
@ -185,7 +183,8 @@ export const Name = ({ handleSubmit, onCancel, dirty }) =>
Next Next
</Button> </Button>
</ButtonsRow> </ButtonsRow>
</form>; </form>
);
export const Manifest = ({ export const Manifest = ({
handleSubmit, handleSubmit,
@ -193,7 +192,7 @@ export const Manifest = ({
dirty, dirty,
defaultValue = '', defaultValue = '',
loading loading
}) => }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<Field name="manifest" defaultValue={defaultValue} component={MEditor} /> <Field name="manifest" defaultValue={defaultValue} component={MEditor} />
<ButtonsRow> <ButtonsRow>
@ -208,30 +207,31 @@ export const Manifest = ({
Environment Environment
</Button> </Button>
</ButtonsRow> </ButtonsRow>
</form>; </form>
);
const File = ({ id, name, value, onRemoveFile, readOnly }) => { const File = ({ id, name, value, onRemoveFile, readOnly }) => {
const removeButton = !readOnly const removeButton = !readOnly ? (
? <FilenameRemove type="button" onClick={onRemoveFile} secondary> <FilenameRemove type="button" onClick={onRemoveFile} secondary>
Remove Remove
</FilenameRemove> </FilenameRemove>
: null; ) : null;
const fileEditor = !readOnly const fileEditor = !readOnly ? (
? <Field <Field name={`file-value-${id}`} defaultValue={value} component={EEditor} />
name={`file-value-${id}`} ) : (
defaultValue={value} <EEditor input={{ value }} readOnly />
component={EEditor} );
/>
: <EEditor input={{ value }} readOnly />;
const input = !readOnly const input = !readOnly ? (
? <FilenameInput type="text" placeholder="Filename including extension…" /> <FilenameInput type="text" placeholder="Filename including extension…" />
: <FilenameInput ) : (
type="text" <FilenameInput
placeholder="Filename including extension…" type="text"
value={name} placeholder="Filename including extension…"
/>; value={name}
/>
);
return ( return (
<FileCard> <FileCard>
@ -247,15 +247,15 @@ const File = ({ id, name, value, onRemoveFile, readOnly }) => {
}; };
const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => { const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => {
const footer = !readOnly const footer = !readOnly ? (
? <Button type="button" onClick={onAddFile} secondary> <Button type="button" onClick={onAddFile} secondary>
Create new .env file Create new .env file
</Button> </Button>
: null; ) : null;
return ( return (
<div> <div>
{files.map(({ id, ...rest }) => {files.map(({ id, ...rest }) => (
<File <File
key={id} key={id}
id={id} id={id}
@ -263,7 +263,7 @@ const Files = ({ files, onAddFile, onRemoveFile, readOnly }) => {
readOnly={readOnly} readOnly={readOnly}
{...rest} {...rest}
/> />
)} ))}
{footer} {footer}
</div> </div>
); );
@ -280,30 +280,28 @@ export const Environment = ({
readOnly = false, readOnly = false,
loading loading
}) => { }) => {
const envEditor = !readOnly const envEditor = !readOnly ? (
? <Field <Field name="environment" defaultValue={defaultValue} component={EEditor} />
name="environment" ) : (
defaultValue={defaultValue} <EEditor input={{ value: defaultValue }} readOnly />
component={EEditor} );
/>
: <EEditor input={{ value: defaultValue }} readOnly />;
const footerDivider = !readOnly ? <EnvironmentDivider /> : null; const footerDivider = !readOnly ? <EnvironmentDivider /> : null;
const footer = !readOnly const footer = !readOnly ? (
? <ButtonsRow> <ButtonsRow>
<Button type="button" onClick={onCancel} secondary> <Button type="button" onClick={onCancel} secondary>
Cancel Cancel
</Button> </Button>
<Button <Button
disabled={!(dirty || !loading || defaultValue.length)} disabled={!(dirty || !loading || defaultValue.length)}
loading={loading} loading={loading}
type="submit" type="submit"
> >
Continue Continue
</Button> </Button>
</ButtonsRow> </ButtonsRow>
: null; ) : null;
return ( return (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
@ -348,18 +346,18 @@ export const Review = ({
environmentToggles, environmentToggles,
...state ...state
}) => { }) => {
const serviceList = forceArray(state.services).map(({ name, config }) => const serviceList = forceArray(state.services).map(({ name, config }) => (
<ServiceCard key={name}> <ServiceCard key={name}>
<ServiceName> <ServiceName>{name}</ServiceName>
{name}
</ServiceName>
<Dl> <Dl>
<dt> <dt>
<ImageTitle>Image:</ImageTitle> <Image>{config.image}</Image> <ImageTitle>Image:</ImageTitle> <Image>{config.image}</Image>
</dt> </dt>
</Dl> </Dl>
{config.environment && config.environment.length ? <ServiceDivider /> : null} {config.environment && config.environment.length ? (
{config.environment && config.environment.length ? <ServiceDivider />
) : null}
{config.environment && config.environment.length ? (
<ServiceEnvironmentTitle <ServiceEnvironmentTitle
expanded={environmentToggles[name]} expanded={environmentToggles[name]}
onClick={() => onEnvironmentToggle(name)} onClick={() => onEnvironmentToggle(name)}
@ -370,12 +368,14 @@ export const Review = ({
up={environmentToggles[name]} up={environmentToggles[name]}
/> />
</ServiceEnvironmentTitle> </ServiceEnvironmentTitle>
: null} ) : null}
{config.environment && config.environment.length && environmentToggles[name] {config.environment &&
? <EnvironmentReview environment={config.environment} /> config.environment.length &&
: null} environmentToggles[name] ? (
<EnvironmentReview environment={config.environment} />
) : null}
</ServiceCard> </ServiceCard>
); ));
return ( return (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
@ -396,18 +396,18 @@ export const Progress = ({ stage, create, edit }) => {
const _nameCompleted = stage !== 'name'; const _nameCompleted = stage !== 'name';
const _nameActive = stage === 'name'; const _nameActive = stage === 'name';
const _name = !create const _name = !create ? null : (
? null <ProgressbarItem>
: <ProgressbarItem> <ProgressbarButton
<ProgressbarButton zIndex="10"
zIndex="10" completed={_nameCompleted}
completed={_nameCompleted} active={_nameActive}
active={_nameActive} first
first >
> Name the group
Name the group </ProgressbarButton>
</ProgressbarButton> </ProgressbarItem>
</ProgressbarItem>; );
const _manifestCompleted = ['environment', 'review'].indexOf(stage) >= 0; const _manifestCompleted = ['environment', 'review'].indexOf(stage) >= 0;
const _manifestActive = create ? stage === 'manifest' : stage === 'edit'; const _manifestActive = create ? stage === 'manifest' : stage === 'edit';

View File

@ -2,8 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Message } from 'joyent-ui-toolkit'; import { Message } from 'joyent-ui-toolkit';
const ErrorMessage = ({ title, message = "Ooops, there's been an error" }) => const ErrorMessage = ({ title, message = "Ooops, there's been an error" }) => (
<Message title={title} message={message} type="ERROR" />; <Message title={title} message={message} type="ERROR" />
);
ErrorMessage.propTypes = { ErrorMessage.propTypes = {
title: PropTypes.string, title: PropTypes.string,

View File

@ -27,10 +27,9 @@ const Msg = P.extend`
margin-bottom: 0; margin-bottom: 0;
`; `;
export default ({ msg }) => export default ({ msg }) => (
<Container> <Container>
<Loader /> <Loader />
<Msg> <Msg>{msg || 'Loading...'}</Msg>
{msg || 'Loading...'} </Container>
</Msg> );
</Container>;

View File

@ -7,13 +7,15 @@ const StyledHeading = styled(ModalHeading)`
color: ${props => props.theme.red}; color: ${props => props.theme.red};
`; `;
const ModalErrorMessage = ({ title, message, onCloseClick }) => const ModalErrorMessage = ({ title, message, onCloseClick }) => (
<div> <div>
<StyledHeading>{title}</StyledHeading> <StyledHeading>{title}</StyledHeading>
<ModalText marginBottom="3">{message} <ModalText marginBottom="3">{message}</ModalText>
</ModalText> <Button onClick={onCloseClick} secondary>
<Button onClick={onCloseClick} secondary>Close </Button> Close{' '}
</div>; </Button>
</div>
);
ModalErrorMessage.propTypes = { ModalErrorMessage.propTypes = {
title: PropTypes.string, title: PropTypes.string,

View File

@ -2,8 +2,9 @@ import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Message } from 'joyent-ui-toolkit'; import { Message } from 'joyent-ui-toolkit';
const WarningMessage = ({ title, message }) => const WarningMessage = ({ title, message }) => (
<Message title={title} message={message} type="WARNING" />; <Message title={title} message={message} type="WARNING" />
);
WarningMessage.propTypes = { WarningMessage.propTypes = {
title: PropTypes.string, title: PropTypes.string,

View File

@ -24,27 +24,22 @@ const BreadcrumbContainer = styled.div`
const getBreadcrumbItems = (...links) => const getBreadcrumbItems = (...links) =>
forceArray(links).map(({ pathname, name }, i) => { forceArray(links).map(({ pathname, name }, i) => {
const item = const item =
i + 1 >= links.length i + 1 >= links.length ? (
? name name
: <BreadcrumbLink to={pathname}> ) : (
{name} <BreadcrumbLink to={pathname}>{name}</BreadcrumbLink>
</BreadcrumbLink>; );
return ( return <BreadcrumbItem key={name}>{item}</BreadcrumbItem>;
<BreadcrumbItem key={name}>
{item}
</BreadcrumbItem>
);
}); });
const NavBreadcrumb = ({ links = [] }) => const NavBreadcrumb = ({ links = [] }) => (
<BreadcrumbContainer> <BreadcrumbContainer>
<Grid> <Grid>
<Breadcrumb> <Breadcrumb>{getBreadcrumbItems(...links)}</Breadcrumb>
{getBreadcrumbItems(...links)}
</Breadcrumb>
</Grid> </Grid>
</BreadcrumbContainer>; </BreadcrumbContainer>
);
NavBreadcrumb.propTypes = { NavBreadcrumb.propTypes = {
links: PropTypes.arrayOf( links: PropTypes.arrayOf(

View File

@ -12,20 +12,17 @@ const StyledLogo = Img.extend`
height: ${remcalc(25)}; height: ${remcalc(25)};
`; `;
const NavHeader = ({ datacenter, username }) => const NavHeader = ({ datacenter, username }) => (
<Header> <Header>
<HeaderBrand> <HeaderBrand>
<Link to="/"> <Link to="/">
<StyledLogo src={Logo} /> <StyledLogo src={Logo} />
</Link> </Link>
</HeaderBrand> </HeaderBrand>
<HeaderItem> <HeaderItem>{datacenter}</HeaderItem>
{datacenter} <HeaderItem>{username}</HeaderItem>
</HeaderItem> </Header>
<HeaderItem> );
{username}
</HeaderItem>
</Header>;
NavHeader.propTypes = { NavHeader.propTypes = {
datacenter: PropTypes.string, datacenter: PropTypes.string,

View File

@ -11,20 +11,19 @@ import {
} from 'joyent-ui-toolkit'; } from 'joyent-ui-toolkit';
const getMenuItems = (...links) => const getMenuItems = (...links) =>
forceArray(links).map(({ pathname, name }) => forceArray(links).map(({ pathname, name }) => (
<SectionListItem key={pathname}> <SectionListItem key={pathname}>
<SectionListNavLink activeClassName="active" to={pathname}> <SectionListNavLink activeClassName="active" to={pathname}>
{name} {name}
</SectionListNavLink> </SectionListNavLink>
</SectionListItem> </SectionListItem>
); ));
const Menu = ({ links = [] }) => const Menu = ({ links = [] }) => (
<LayoutContainer plain> <LayoutContainer plain>
<SectionList> <SectionList>{getMenuItems(...links)}</SectionList>
{getMenuItems(...links)} </LayoutContainer>
</SectionList> );
</LayoutContainer>;
Menu.propTypes = { Menu.propTypes = {
links: PropTypes.arrayOf( links: PropTypes.arrayOf(

View File

@ -39,6 +39,6 @@ NotFound.propTypes = {
message: PropTypes.string, message: PropTypes.string,
link: PropTypes.string, link: PropTypes.string,
to: PropTypes.string to: PropTypes.string
} };
export default NotFound; export default NotFound;

View File

@ -6,7 +6,7 @@ const ServiceDelete = ({
service, service,
onCancelClick = () => {}, onCancelClick = () => {},
onConfirmClick = () => {} onConfirmClick = () => {}
}) => }) => (
<div> <div>
<ModalHeading> <ModalHeading>
Deleting a service: <br /> {service.name} Deleting a service: <br /> {service.name}
@ -19,7 +19,8 @@ const ServiceDelete = ({
Cancel Cancel
</Button> </Button>
<Button onClick={onConfirmClick}>Delete service</Button> <Button onClick={onConfirmClick}>Delete service</Button>
</div>; </div>
);
ServiceDelete.propTypes = { ServiceDelete.propTypes = {
service: PropTypes.object.isRequired, service: PropTypes.object.isRequired,

View File

@ -1,3 +1,3 @@
export { default as ServiceScale } from './scale'; export { default as ServiceScale } from './scale';
export { default as ServiceDelete } from './delete'; export { default as ServiceDelete } from './delete';
export { default as ServiceMetrics} from './metrics'; export { default as ServiceMetrics } from './metrics';

View File

@ -2,13 +2,9 @@ import React, { Component } from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { MetricGraph } from 'joyent-ui-toolkit'; import { MetricGraph } from 'joyent-ui-toolkit';
const ServiceMetrics = ({ const ServiceMetrics = ({ metricsData, graphDurationSeconds }) => {
metricsData,
graphDurationSeconds
}) => {
// metricsData should prob be an array rather than an object // metricsData should prob be an array rather than an object
const metricGraphs = Object.keys(metricsData).map((key) => ( const metricGraphs = Object.keys(metricsData).map(key => (
// should also have a header, w metric name and number of instances (omit everything else from design for copilot) // should also have a header, w metric name and number of instances (omit everything else from design for copilot)
<MetricGraph <MetricGraph
key={key} key={key}
@ -17,19 +13,15 @@ const ServiceMetrics = ({
height={292} height={292}
graphDurationSeconds={graphDurationSeconds} graphDurationSeconds={graphDurationSeconds}
/> />
)) ));
// This needs layout!!! // This needs layout!!!
return ( return <div>{metricGraphs}</div>;
<div> };
{metricGraphs}
</div>
)
}
ServiceMetrics.propTypes = { ServiceMetrics.propTypes = {
// metricsData should prob be an array rather than an object // metricsData should prob be an array rather than an object
metricsData: PropTypes.object.isRequired, metricsData: PropTypes.object.isRequired,
graphDurationSeconds: PropTypes.number.isRequired graphDurationSeconds: PropTypes.number.isRequired
} };
export default ServiceMetrics; export default ServiceMetrics;

View File

@ -15,7 +15,7 @@ const ServiceScale = ({
onCancelClick = () => {}, onCancelClick = () => {},
invalid, invalid,
pristine pristine
}) => }) => (
<form onSubmit={handleSubmit}> <form onSubmit={handleSubmit}>
<ModalHeading> <ModalHeading>
Scaling a service: <br /> Scaling a service: <br />
@ -38,7 +38,8 @@ const ServiceScale = ({
<Button type="submit" disabled={pristine || invalid} secondary> <Button type="submit" disabled={pristine || invalid} secondary>
Scale Scale
</Button> </Button>
</form>; </form>
);
ServiceScale.propTypes = { ServiceScale.propTypes = {
service: PropTypes.object.isRequired, service: PropTypes.object.isRequired,

View File

@ -16,7 +16,7 @@ const StyledBox = styled.div`
} }
`; `;
export default () => export default () => (
<LayoutContainer> <LayoutContainer>
<Row> <Row>
<Col> <Col>
@ -56,4 +56,5 @@ export default () =>
</Row> </Row>
</Col> </Col>
</Row> </Row>
</LayoutContainer>; </LayoutContainer>
);

View File

@ -57,27 +57,27 @@ const ServiceListItem = ({
: service.instances.length; : service.instances.length;
const childrenItems = children.length const childrenItems = children.length
? children.map(service => ? children.map(service => (
<ServiceListItem <ServiceListItem
key={service.id} key={service.id}
deploymentGroup={deploymentGroup} deploymentGroup={deploymentGroup}
service={service} service={service}
isChild isChild
/> />
) : null; ))
: null;
const title = isChild ? (
const title = isChild <CardTitle>{service.name}</CardTitle>
? <CardTitle> ) : (
{service.name} <CardTitle>
</CardTitle> <TitleInnerContainer>
: <CardTitle> <StyledAnchor to={to} secondary active={service.instancesActive}>
<TitleInnerContainer> {service.name}
<StyledAnchor to={to} secondary active={service.instancesActive}> </StyledAnchor>
{service.name} </TitleInnerContainer>
</StyledAnchor> </CardTitle>
</TitleInnerContainer> );
</CardTitle>;
const subtitle = ( const subtitle = (
<CardSubTitle> <CardSubTitle>
@ -86,52 +86,47 @@ const ServiceListItem = ({
</CardSubTitle> </CardSubTitle>
); );
const header = !isChild const header = !isChild ? (
? <StyledCardHeader> <StyledCardHeader>
{title} {title}
<CardDescription> <CardDescription>
<CardInfo <CardInfo
icon={<InstancesIcon />} icon={<InstancesIcon />}
iconPosition="left" iconPosition="left"
label={`${instancesCount} ${instancesCount > 1 label={`${instancesCount} ${instancesCount > 1
? 'instances' ? 'instances'
: 'instance'}`} : 'instance'}`}
color={!service.instancesActive ? 'disabled' : 'light'} color={!service.instancesActive ? 'disabled' : 'light'}
/> />
</CardDescription> </CardDescription>
<CardOptions onClick={handleCardOptionsClick} /> <CardOptions onClick={handleCardOptionsClick} />
</StyledCardHeader> </StyledCardHeader>
: null; ) : null;
let healthyInfo = null; let healthyInfo = null;
if(service.instancesActive) { if (service.instancesActive) {
const { total, healthy } = service.instancesHealthy; const { total, healthy } = service.instancesHealthy;
const iconHealthy = total === healthy ? 'HEALTHY' : 'NOT HEALTHY'; const iconHealthy = total === healthy ? 'HEALTHY' : 'NOT HEALTHY';
const icon = <HealthyIcon healthy={iconHealthy} />; const icon = <HealthyIcon healthy={iconHealthy} />;
const label = `${healthy} of ${total} healthy`; const label = `${healthy} of ${total} healthy`;
healthyInfo = ( healthyInfo = (
<CardInfo <CardInfo icon={icon} iconPosition="left" label={label} color="dark" />
icon={icon} );
iconPosition='left'
label={label}
color='dark'
/>
)
} }
const view = children.length const view = children.length ? (
? <CardGroupView> <CardGroupView>{childrenItems}</CardGroupView>
{childrenItems} ) : (
</CardGroupView> <CardView>
: <CardView> {isChild && title}
{isChild && title} {isChild && subtitle}
{isChild && subtitle} <CardDescription>
<CardDescription> <Status service={service} />
<Status service={service} /> {healthyInfo}
{healthyInfo} </CardDescription>
</CardDescription> </CardView>
</CardView>; );
return ( return (
<Card <Card

View File

@ -1,6 +1,11 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Tooltip, TooltipButton, TooltipDivider, TooltipList } from 'joyent-ui-toolkit'; import {
Tooltip,
TooltipButton,
TooltipDivider,
TooltipList
} from 'joyent-ui-toolkit';
const ServicesQuickActions = ({ const ServicesQuickActions = ({
show, show,
@ -11,7 +16,7 @@ const ServicesQuickActions = ({
onStopClick = () => {}, onStopClick = () => {},
onStartClick = () => {}, onStartClick = () => {},
onScaleClick = () => {}, onScaleClick = () => {},
onDeleteClick = () => {}, onDeleteClick = () => {}
}) => { }) => {
if (!show) { if (!show) {
return null; return null;
@ -46,22 +51,22 @@ const ServicesQuickActions = ({
}, null); }, null);
const startService = const startService =
status === 'RUNNING' status === 'RUNNING' ? null : (
? null <li>
: <li> <TooltipButton onClick={handleStartClick} disabled={disabled}>
<TooltipButton onClick={handleStartClick} disabled={disabled}> Start
Start </TooltipButton>
</TooltipButton> </li>
</li>; );
const stopService = const stopService =
status === 'STOPPED' status === 'STOPPED' ? null : (
? null <li>
: <li> <TooltipButton onClick={handleStopClick} disabled={disabled}>
<TooltipButton onClick={handleStopClick} disabled={disabled}> Stop
Stop </TooltipButton>
</TooltipButton> </li>
</li>; );
return ( return (
<Tooltip {...position} onBlur={onBlur}> <Tooltip {...position} onBlur={onBlur}>

View File

@ -37,16 +37,18 @@ const ServiceStatus = ({ service }) => {
); );
}); });
return service.transitionalStatus return service.transitionalStatus ? (
? <StyledStatusContainer> <StyledStatusContainer>
<StatusLoader /> <StatusLoader />
<StyledTransitionalStatus> <StyledTransitionalStatus>
{service.status ? service.status.toLowerCase() : ''} {service.status ? service.status.toLowerCase() : ''}
</StyledTransitionalStatus> </StyledTransitionalStatus>
</StyledStatusContainer> </StyledStatusContainer>
: <StyledStatusContainer> ) : (
{getInstanceStatuses(service.instanceStatuses)} <StyledStatusContainer>
</StyledStatusContainer>; {getInstanceStatuses(service.instanceStatuses)}
</StyledStatusContainer>
);
}; };
ServiceStatus.propTypes = { ServiceStatus.propTypes = {

View File

@ -5,7 +5,7 @@ import DeploymentGroupDeleteMutation from '@graphql/DeploymentGroupDeleteMutatio
import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql'; import DeploymentGroupQuery from '@graphql/DeploymentGroup.gql';
import { Loader, ModalErrorMessage } 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 } from 'joyent-ui-toolkit' import { Modal } from 'joyent-ui-toolkit';
import { withNotFound, GqlPaths } from '@containers/navigation'; import { withNotFound, GqlPaths } from '@containers/navigation';
export class DeploymentGroupDelete extends Component { export class DeploymentGroupDelete extends Component {
@ -21,7 +21,10 @@ export class DeploymentGroupDelete extends Component {
const { history, match, loading, error } = this.props; const { 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('/');
history.replace(closeUrl); history.replace(closeUrl);
}; };
@ -37,24 +40,21 @@ export class DeploymentGroupDelete extends Component {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalErrorMessage <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} onCloseClick={handleCloseClick}
/> />
</Modal> </Modal>
); );
} }
const { const { deploymentGroup, deleteDeploymentGroup } = this.props;
deploymentGroup,
deleteDeploymentGroup
} = this.props;
if (this.state.error) { if (this.state.error) {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalErrorMessage <ModalErrorMessage
title='Ooops!' title="Ooops!"
message={`An error occurred while attempting to delete the ${deploymentGroup.name} deployment group.`} message={`An error occurred while attempting to delete the ${deploymentGroup.name} deployment group.`}
onCloseClick={handleCloseClick} onCloseClick={handleCloseClick}
/> />
@ -117,7 +117,7 @@ const DeploymentGroupGql = graphql(DeploymentGroupQuery, {
const DeploymentGroupDeleteWithData = compose( const DeploymentGroupDeleteWithData = compose(
DeleteDeploymentGroupGql, DeleteDeploymentGroupGql,
DeploymentGroupGql, DeploymentGroupGql,
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ]) withNotFound([GqlPaths.DEPLOYMENT_GROUP])
)(DeploymentGroupDelete); )(DeploymentGroupDelete);
export default DeploymentGroupDeleteWithData; export default DeploymentGroupDeleteWithData;

View File

@ -5,9 +5,10 @@ import { Progress } from '@components/manifest/edit-or-create';
import { LayoutContainer } from '@components/layout'; import { LayoutContainer } from '@components/layout';
import { Title } from '@components/navigation'; import { Title } from '@components/navigation';
export default ({ match }) => export default ({ match }) => (
<LayoutContainer> <LayoutContainer>
<Title>Creating deployment group</Title> <Title>Creating deployment group</Title>
<Progress stage={match.params.stage} create /> <Progress stage={match.params.stage} create />
<ManifestEditOrCreate create /> <ManifestEditOrCreate create />
</LayoutContainer>; </LayoutContainer>
);

View File

@ -140,20 +140,18 @@ export const DeploymentGroupList = ({
); );
} }
const groups = forceArray(deploymentGroups).map(({ slug, name }) => const groups = forceArray(deploymentGroups).map(({ slug, name }) => (
<Col xs={12} sm={4} md={3} lg={3} key={slug}> <Col xs={12} sm={4} md={3} lg={3} key={slug}>
<Box> <Box>
<StyledLink to={`${match.path}/${slug}`}> <StyledLink to={`${match.path}/${slug}`}>
<ServiceTitle> <ServiceTitle>{name}</ServiceTitle>
{name}
</ServiceTitle>
</StyledLink> </StyledLink>
<StyledIconButton to={`${match.url}/${slug}/delete`}> <StyledIconButton to={`${match.url}/${slug}/delete`}>
<BinIcon /> <BinIcon />
</StyledIconButton> </StyledIconButton>
</Box> </Box>
</Col> </Col>
); ));
const create = [ const create = [
<Col xs={12} sm={4} md={3} lg={3} key="~create"> <Col xs={12} sm={4} md={3} lg={3} key="~create">
@ -165,18 +163,16 @@ export const DeploymentGroupList = ({
</BoxCreate> </BoxCreate>
</Col> </Col>
].concat( ].concat(
forceArray(importable).map(({ slug, name }) => forceArray(importable).map(({ slug, name }) => (
<Col xs={12} sm={4} md={3} lg={3} key={slug}> <Col xs={12} sm={4} md={3} lg={3} key={slug}>
<BoxCreate> <BoxCreate>
<StyledCreateLink to={`${match.path}/~import/${slug}`}> <StyledCreateLink to={`${match.path}/~import/${slug}`}>
<Oval>&#10549;</Oval> <Oval>&#10549;</Oval>
<CreateTitle> <CreateTitle>{name}</CreateTitle>
{name}
</CreateTitle>
</StyledCreateLink> </StyledCreateLink>
</BoxCreate> </BoxCreate>
</Col> </Col>
) ))
); );
return ( return (
@ -218,5 +214,5 @@ export default compose(
importable: importableDeploymentGroups importable: importableDeploymentGroups
}) })
}), }),
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ]) withNotFound([GqlPaths.DEPLOYMENT_GROUP])
)(DeploymentGroupList); )(DeploymentGroupList);

View File

@ -20,7 +20,6 @@ export const InstanceList = ({
instancesTooltip, instancesTooltip,
toggleInstancesTooltip toggleInstancesTooltip
}) => { }) => {
const _title = <Title>Instances</Title>; const _title = <Title>Instances</Title>;
if (loading && !forceArray(instances).length) { if (loading && !forceArray(instances).length) {
@ -62,15 +61,12 @@ export const InstanceList = ({
}; };
const handleMouseOver = (evt, instance, type) => { const handleMouseOver = (evt, instance, type) => {
const label = evt.currentTarget; const label = evt.currentTarget;
const labelRect = label.getBoundingClientRect(); const labelRect = label.getBoundingClientRect();
const offset = type === 'healthy' const offset = type === 'healthy' ? 48 : type === 'status' ? 36 : 0;
? 48 : type === 'status' ? 36 : 0;
const position = { const position = {
left: left: `${window.scrollX + labelRect.left + offset}px`,
`${window.scrollX + labelRect.left + offset}px`,
top: `${window.scrollY + labelRect.bottom}px` top: `${window.scrollY + labelRect.bottom}px`
}; };
@ -78,16 +74,16 @@ export const InstanceList = ({
instance, instance,
position, position,
type type
} };
toggleInstancesTooltip(tooltipData); toggleInstancesTooltip(tooltipData);
}; };
const handleMouseOut = (evt) => { const handleMouseOut = evt => {
toggleInstancesTooltip({ show: false }); toggleInstancesTooltip({ show: false });
}; };
const instanceList = instances.map((instance, index) => const instanceList = instances.map((instance, index) => (
<InstanceListItem <InstanceListItem
instance={instance} instance={instance}
key={instance.id} key={instance.id}
@ -95,7 +91,7 @@ export const InstanceList = ({
onStatusMouseOver={handleStatusMouseOver} onStatusMouseOver={handleStatusMouseOver}
onMouseOut={handleMouseOut} onMouseOut={handleMouseOut}
/> />
); ));
const _instances = !instanceList.length ? <EmptyInstances /> : instanceList; const _instances = !instanceList.length ? <EmptyInstances /> : instanceList;
@ -105,7 +101,7 @@ export const InstanceList = ({
{_instances} {_instances}
</LayoutContainer> </LayoutContainer>
); );
} };
const mapStateToProps = (state, ownProps) => ({ const mapStateToProps = (state, ownProps) => ({
instancesTooltip: state.ui.instances.tooltip instancesTooltip: state.ui.instances.tooltip
@ -131,7 +127,7 @@ const InstanceListGql = graphql(InstancesQuery, {
} }
}; };
}, },
props: ({ data: { deploymentGroup, loading, error }}) => ({ props: ({ data: { deploymentGroup, loading, error } }) => ({
deploymentGroup, deploymentGroup,
instances: sortBy( instances: sortBy(
forceArray( forceArray(
@ -151,8 +147,5 @@ const InstanceListGql = graphql(InstancesQuery, {
export default compose( export default compose(
UiConnect, UiConnect,
InstanceListGql, InstanceListGql,
withNotFound([ withNotFound([GqlPaths.DEPLOYMENT_GROUP, GqlPaths.SERVICES])
GqlPaths.DEPLOYMENT_GROUP,
GqlPaths.SERVICES
])
)(InstanceList); )(InstanceList);

View File

@ -12,37 +12,34 @@ const StyledContainer = styled.div`
const healthMessages = { const healthMessages = {
healthy: 'Your instance is operating as expected', healthy: 'Your instance is operating as expected',
unhealthy: 'Your instance is not operating as expected', unhealthy: 'Your instance is not operating as expected',
maintenance: 'You\'ve set your instance to this manually, use the Container Pilot CLI to change', maintenance:
unknown: 'We\'ve connected to your instance but we have no health information', "You've set your instance to this manually, use the Container Pilot CLI to change",
unavailable: 'We cannot connect to your instance', unknown: "We've connected to your instance but we have no health information",
unavailable: 'We cannot connect to your instance'
}; };
const statusMessages = { const statusMessages = {
running: 'Your instance is operating', running: 'Your instance is operating',
provisioning: 'Your instance is downloading dependencies and compiling', provisioning: 'Your instance is downloading dependencies and compiling',
ready: 'Your instance finished provisioning and is ready to be run, it\'ll be running soon', ready:
"Your instance finished provisioning and is ready to be run, it'll be running soon",
stopping: 'Your instance is going to be stopped soon', stopping: 'Your instance is going to be stopped soon',
stopped: 'Your instance isn\'t doing anything, you can start it', stopped: "Your instance isn't doing anything, you can start it",
offline: 'We have no idea what this means, do you??????', offline: 'We have no idea what this means, do you??????',
failed: 'Your instance has crashed', failed: 'Your instance has crashed',
unknown: 'We cannot work out what status your instance is in', unknown: 'We cannot work out what status your instance is in'
}; };
export const InstancesTooltip = ({ export const InstancesTooltip = ({ instancesTooltip }) => {
instancesTooltip if (instancesTooltip.show) {
}) => { const { type, instance } = instancesTooltip;
if(instancesTooltip.show) { const message =
const { type === 'healthy'
type, ? healthMessages[instance.healthy.toLowerCase()]
instance : type === 'status'
} = instancesTooltip; ? statusMessages[instance.status.toLowerCase()]
: '';
const message = type === 'healthy'
? healthMessages[instance.healthy.toLowerCase()]
: type === 'status'
? statusMessages[instance.status.toLowerCase()]
: '';
return ( return (
<StyledContainer> <StyledContainer>
@ -50,7 +47,7 @@ export const InstancesTooltip = ({
<TooltipLabel>{message}</TooltipLabel> <TooltipLabel>{message}</TooltipLabel>
</Tooltip> </Tooltip>
</StyledContainer> </StyledContainer>
) );
} }
return null; return null;

View File

@ -175,7 +175,9 @@ class DeploymentGroupEditOrCreate extends Component {
name.replace(/^\$/, '') name.replace(/^\$/, '')
); );
const vars = uniq(names).map(name => `\n${name}=`).join(''); const vars = uniq(names)
.map(name => `\n${name}=`)
.join('');
return `# define your interpolatable variables here\n${vars}`; return `# define your interpolatable variables here\n${vars}`;
} }

View File

@ -47,12 +47,12 @@ const Manifest = ({
} }
const _notice = const _notice =
deploymentGroup && deploymentGroup.imported && !manifest deploymentGroup && deploymentGroup.imported && !manifest ? (
? <WarningMessage <WarningMessage
title="Be aware" title="Be aware"
message="Since this Deployment Group was imported, it doesn&#x27;t have the initial manifest." message="Since this Deployment Group was imported, it doesn&#x27;t have the initial manifest."
/> />
: null; ) : null;
return ( return (
<LayoutContainer> <LayoutContainer>

View File

@ -11,25 +11,16 @@ export const MetricNames = [
export const withServiceMetricsPolling = ({ export const withServiceMetricsPolling = ({
pollingInterval = 1000 // in milliseconds pollingInterval = 1000 // in milliseconds
}) => { }) => {
return (WrappedComponent) => { return WrappedComponent => {
return class extends Component { return class extends Component {
componentDidMount() { componentDidMount() {
this._poll = setInterval(() => { this._poll = setInterval(() => {
const { const { loading, error, service, fetchMoreMetrics } = this.props;
loading,
error,
service,
fetchMoreMetrics
} = this.props;
if(!loading && !error && service) { if (!loading && !error && service) {
const previousEnd = service.instances[0].metrics[0].end; const previousEnd = service.instances[0].metrics[0].end;
fetchMoreMetrics(previousEnd); fetchMoreMetrics(previousEnd);
} }
}, pollingInterval); // TODO this is the polling interval - think about amount is the todo I guess... }, pollingInterval); // TODO this is the polling interval - think about amount is the todo I guess...
} }
@ -38,24 +29,28 @@ export const withServiceMetricsPolling = ({
} }
render() { render() {
return <WrappedComponent {...this.props} /> return <WrappedComponent {...this.props} />;
} }
} };
} };
} };
export const withServiceMetricsGql = ({ export const withServiceMetricsGql = ({
gqlQuery, gqlQuery,
graphDurationSeconds, graphDurationSeconds,
updateIntervalSeconds updateIntervalSeconds
}) => { }) => {
const getPreviousMetrics = (
const getPreviousMetrics = (previousResult, serviceId, instanceId, metricName) => { previousResult,
serviceId,
instanceId,
metricName
) => {
return previousResult.deploymentGroup.services return previousResult.deploymentGroup.services
.find(s => s.id === serviceId).instances .find(s => s.id === serviceId)
.find(i => i.id === instanceId).metrics .instances.find(i => i.id === instanceId)
.find(m => m.name === metricName).metrics; .metrics.find(m => m.name === metricName).metrics;
} };
const getNextResult = (previousResult, fetchNextResult) => { const getNextResult = (previousResult, fetchNextResult) => {
const deploymentGroup = fetchNextResult.deploymentGroup; const deploymentGroup = fetchNextResult.deploymentGroup;
@ -69,18 +64,18 @@ export const withServiceMetricsGql = ({
metrics: instance.metrics.map(metric => ({ metrics: instance.metrics.map(metric => ({
...metric, ...metric,
metrics: getPreviousMetrics( metrics: getPreviousMetrics(
previousResult, previousResult,
service.id, service.id,
instance.id, instance.id,
metric.name metric.name
).concat(metric.metrics) ).concat(metric.metrics)
})) }))
})) }))
})) }))
} }
} };
return nextResult; return nextResult;
} };
return graphql(gqlQuery, { return graphql(gqlQuery, {
options(props) { options(props) {
@ -90,7 +85,10 @@ export const withServiceMetricsGql = ({
// this is potentially prone to overfetching if we already have data within timeframe and we leave the page then come back to it // this is potentially prone to overfetching if we already have data within timeframe and we leave the page then come back to it
const end = moment(); const end = moment();
const start = moment(end).subtract(graphDurationSeconds + updateIntervalSeconds, 'seconds'); // TODO initial amount of data we wanna get - should be the same as what we display + 15 secs const start = moment(end).subtract(
graphDurationSeconds + updateIntervalSeconds,
'seconds'
); // TODO initial amount of data we wanna get - should be the same as what we display + 15 secs
return { return {
variables: { variables: {
@ -102,27 +100,34 @@ export const withServiceMetricsGql = ({
} }
}; };
}, },
props: ({ data: { deploymentGroup, loading, error, variables, fetchMore }}) => { props: ({
data: { deploymentGroup, loading, error, variables, fetchMore }
const fetchMoreMetrics = (previousEnd) => { }) => {
const fetchMoreMetrics = previousEnd => {
fetchMore({ fetchMore({
variables: { variables: {
...variables, ...variables,
start: previousEnd, start: previousEnd,
end: moment().utc().format() end: moment()
.utc()
.format()
}, },
updateQuery: (previousResult, { fetchMoreResult, queryVariables }) => { updateQuery: (
previousResult,
{ fetchMoreResult, queryVariables }
) => {
return getNextResult(previousResult, fetchMoreResult); return getNextResult(previousResult, fetchMoreResult);
} }
}); });
} };
return ({ return {
deploymentGroup, deploymentGroup,
service: !loading && deploymentGroup ? deploymentGroup.services[0] : null, service:
!loading && deploymentGroup ? deploymentGroup.services[0] : null,
loading, loading,
error, error,
fetchMoreMetrics fetchMoreMetrics
}) };
} }
}); });
} };

View File

@ -9,8 +9,11 @@ import {
serviceBySlugSelector serviceBySlugSelector
} from '@root/state/selectors'; } from '@root/state/selectors';
export const Breadcrumb = ({ deploymentGroup, service, location: { pathname }}) => { export const Breadcrumb = ({
deploymentGroup,
service,
location: { pathname }
}) => {
const path = pathname.split('/'); const path = pathname.split('/');
const links = [ const links = [
@ -41,7 +44,7 @@ Breadcrumb.propTypes = {
deploymentGroup: PropTypes.object, deploymentGroup: PropTypes.object,
service: PropTypes.object, service: PropTypes.object,
location: PropTypes.object location: PropTypes.object
} };
const connectBreadcrumb = connect( const connectBreadcrumb = connect(
(state, ownProps) => { (state, ownProps) => {
@ -59,7 +62,4 @@ const connectBreadcrumb = connect(
dispatch => ({}) dispatch => ({})
); );
export default compose( export default compose(connectBreadcrumb, withNotFound())(Breadcrumb);
connectBreadcrumb,
withNotFound()
)(Breadcrumb);

View File

@ -5,8 +5,9 @@ import get from 'lodash.get';
import PortalQuery from '@graphql/Portal.gql'; import PortalQuery from '@graphql/Portal.gql';
import { Header as HeaderComponent } from '@components/navigation'; import { Header as HeaderComponent } from '@components/navigation';
export const Header = ({ datacenter, username }) => export const Header = ({ datacenter, username }) => (
<HeaderComponent datacenter={datacenter} username={username} />; <HeaderComponent datacenter={datacenter} username={username} />
);
const HeaderWithData = graphql(PortalQuery, { const HeaderWithData = graphql(PortalQuery, {
props: ({ data: { portal = {} } }) => ({ props: ({ data: { portal = {} } }) => ({

View File

@ -5,7 +5,6 @@ import withNotFound from './not-found-hoc';
import { Menu as MenuComponent } from '@components/navigation'; import { Menu as MenuComponent } from '@components/navigation';
export const Menu = ({ location, match, sections }) => { export const Menu = ({ location, match, sections }) => {
if (!sections || !sections.length) { if (!sections || !sections.length) {
return null; return null;
} }
@ -40,7 +39,4 @@ const connectMenu = connect(
dispatch => ({}) dispatch => ({})
); );
export default compose( export default compose(connectMenu, withNotFound())(Menu);
connectMenu,
withNotFound()
)(Menu);

View File

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

View File

@ -21,7 +21,10 @@ export class ServiceDelete extends Component {
const { loading, error, match, history } = this.props; const { loading, error, match, history } = 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('/');
history.replace(closeUrl); history.replace(closeUrl);
}; };
@ -37,8 +40,8 @@ export class ServiceDelete extends Component {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalErrorMessage <ModalErrorMessage
title='Ooops!' title="Ooops!"
message='An error occurred while loading your service.' message="An error occurred while loading your service."
onCloseClick={handleCloseClick} onCloseClick={handleCloseClick}
/> />
</Modal> </Modal>
@ -51,7 +54,7 @@ export class ServiceDelete extends Component {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalErrorMessage <ModalErrorMessage
title='Ooops!' title="Ooops!"
message={`An error occurred while attempting to delete your ${service.name} service.`} message={`An error occurred while attempting to delete your ${service.name} service.`}
onCloseClick={handleCloseClick} onCloseClick={handleCloseClick}
/> />
@ -60,9 +63,11 @@ export class ServiceDelete extends Component {
} }
const handleConfirmClick = evt => { const handleConfirmClick = evt => {
deleteServices(service.id).then(() => handleCloseClick()).catch(err => { deleteServices(service.id)
this.setState({ error: err }); .then(() => handleCloseClick())
}); .catch(err => {
this.setState({ error: err });
});
}; };
return ( return (
@ -98,5 +103,5 @@ const DeleteServicesGql = graphql(ServicesDeleteMutation, {
export default compose( export default compose(
DeleteServicesGql, DeleteServicesGql,
ServiceGql, ServiceGql,
withNotFound([ GqlPaths.SERVICES ]) withNotFound([GqlPaths.SERVICES])
)(ServiceDelete); )(ServiceDelete);

View File

@ -10,17 +10,16 @@ import { ServiceMetrics as ServiceMetricsComponent } from '@components/service';
import { Button } from 'joyent-ui-toolkit'; import { Button } from 'joyent-ui-toolkit';
import { Loader, ErrorMessage } from '@components/messaging'; import { Loader, ErrorMessage } from '@components/messaging';
import { withServiceMetricsPolling, withServiceMetricsGql } from '@containers/metrics'; import {
withServiceMetricsPolling,
withServiceMetricsGql
} from '@containers/metrics';
// 'width' of graph, i.e. total duration of time it'll display and truncate data to // 'width' of graph, i.e. total duration of time it'll display and truncate data to
// amount of data we'll need to initially fetch // amount of data we'll need to initially fetch
const GraphDurationSeconds = 90; const GraphDurationSeconds = 90;
const ServiceMetrics = ({ const ServiceMetrics = ({ service, loading, error }) => {
service,
loading,
error
}) => {
if (loading || !service) { if (loading || !service) {
return ( return (
<LayoutContainer center> <LayoutContainer center>
@ -43,8 +42,8 @@ const ServiceMetrics = ({
// metricsData should prob be an array rather than an object // metricsData should prob be an array rather than an object
const metricsData = service.instances.reduce((metrics, instance) => { const metricsData = service.instances.reduce((metrics, instance) => {
// gather metrics of instances according to type // gather metrics of instances according to type
instance.metrics.forEach((instanceMetrics) => { instance.metrics.forEach(instanceMetrics => {
if(!metrics[instanceMetrics.name]) { if (!metrics[instanceMetrics.name]) {
metrics[instanceMetrics.name] = []; metrics[instanceMetrics.name] = [];
} }
metrics[instanceMetrics.name].push(instanceMetrics); metrics[instanceMetrics.name].push(instanceMetrics);
@ -69,10 +68,7 @@ export default compose(
updateIntervalSeconds: 15 updateIntervalSeconds: 15
}), }),
withServiceMetricsPolling({ pollingInterval: 1000 }), withServiceMetricsPolling({ pollingInterval: 1000 }),
withNotFound([ withNotFound([GqlPaths.DEPLOYMENT_GROUP, GqlPaths.SERVICES])
GqlPaths.DEPLOYMENT_GROUP,
GqlPaths.SERVICES
])
)(ServiceMetrics); )(ServiceMetrics);
/* /*

View File

@ -22,7 +22,10 @@ export class ServiceScale extends Component {
const { loading, error, match, history } = this.props; const { loading, error, match, history } = 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('/');
history.replace(closeUrl); history.replace(closeUrl);
}; };
@ -38,8 +41,8 @@ export class ServiceScale extends Component {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalErrorMessage <ModalErrorMessage
title='Ooops!' title="Ooops!"
message='An error occurred while loading your service.' message="An error occurred while loading your service."
onCloseClick={handleCloseClick} onCloseClick={handleCloseClick}
/> />
</Modal> </Modal>
@ -52,7 +55,7 @@ export class ServiceScale extends Component {
return ( return (
<Modal width={460} onCloseClick={handleCloseClick}> <Modal width={460} onCloseClick={handleCloseClick}>
<ModalErrorMessage <ModalErrorMessage
title='Ooops!' title="Ooops!"
message={`An error occurred while attempting to scale your ${service.name} service.`} message={`An error occurred while attempting to scale your ${service.name} service.`}
onCloseClick={handleCloseClick} onCloseClick={handleCloseClick}
/> />
@ -70,9 +73,11 @@ export class ServiceScale extends Component {
}; };
const handleSubmitClick = values => { const handleSubmitClick = values => {
scale(service.id, values.replicas).then(handleCloseClick).catch(err => { scale(service.id, values.replicas)
this.setState({ error: err }); .then(handleCloseClick)
}); .catch(err => {
this.setState({ error: err });
});
}; };
if (!service) { if (!service) {
@ -126,5 +131,5 @@ const ServiceScaleGql = graphql(ServiceScaleMutation, {
export default compose( export default compose(
ServiceScaleGql, ServiceScaleGql,
ServiceGql, ServiceGql,
withNotFound([ GqlPaths.SERVICES ]) withNotFound([GqlPaths.SERVICES])
)(ServiceScale); )(ServiceScale);

View File

@ -34,7 +34,7 @@ export class ServiceList extends Component {
services, services,
loading, loading,
error, error,
toggleServicesQuickActions, toggleServicesQuickActions
} = this.props; } = this.props;
if (loading && !forceArray(services).length) { if (loading && !forceArray(services).length) {
@ -73,8 +73,9 @@ export class ServiceList extends Component {
const buttonRect = button.getBoundingClientRect(); const buttonRect = button.getBoundingClientRect();
const position = { const position = {
left: left: `${buttonRect.left +
`${buttonRect.left + window.scrollX + (buttonRect.right - buttonRect.left) / 2}px`, window.scrollX +
(buttonRect.right - buttonRect.left) / 2}px`,
top: `${buttonRect.bottom + window.scrollY}px` top: `${buttonRect.bottom + window.scrollY}px`
}; };
@ -120,9 +121,7 @@ export class ServiceList extends Component {
return ( return (
<LayoutContainer> <LayoutContainer>
{renderedError} {renderedError}
<StyledContainer> <StyledContainer>{serviceList}</StyledContainer>
{serviceList}
</StyledContainer>
</LayoutContainer> </LayoutContainer>
); );
} }
@ -134,7 +133,7 @@ ServiceList.propTypes = {
loading: PropTypes.bool, loading: PropTypes.bool,
error: PropTypes.bool, error: PropTypes.bool,
toggleServicesQuickActions: PropTypes.func toggleServicesQuickActions: PropTypes.func
} };
const mapStateToProps = (state, ownProps) => ({}); const mapStateToProps = (state, ownProps) => ({});
@ -153,7 +152,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)
@ -166,7 +165,7 @@ const ServicesGql = graphql(ServicesQuery, {
const ServiceListWithData = compose( const ServiceListWithData = compose(
ServicesGql, ServicesGql,
UiConnect, UiConnect,
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ]) withNotFound([GqlPaths.DEPLOYMENT_GROUP])
)(ServiceList); )(ServiceList);
export default ServiceListWithData; export default ServiceListWithData;

View File

@ -20,11 +20,8 @@ const PaddedRow = Row.extend`
margin-bottom: ${remcalc(18)} margin-bottom: ${remcalc(18)}
`; `;
export const ServicesMenu = ({
location: { pathname },
history: { push }
}) => {
export const ServicesMenu = ({ location: { pathname }, history: { push } }) => {
const toggleValue = pathname.split('-').pop(); const toggleValue = pathname.split('-').pop();
const handleToggle = evt => { const handleToggle = evt => {

View File

@ -17,13 +17,12 @@ const StyledContainer = styled.div`
`; `;
export class ServicesQuickActions extends Component { export class ServicesQuickActions extends Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = { this.state = {
errors: {} errors: {}
} };
} }
render() { render() {
@ -53,20 +52,20 @@ export class ServicesQuickActions extends Component {
? 'An error occurred while attempting to restart your service.' ? 'An error occurred while attempting to restart your service.'
: ''; : '';
errorMessage = ( errorMessage = (
<LayoutContainer> <LayoutContainer>
<ErrorMessage title="Ooops!" message={message} /> <ErrorMessage title="Ooops!" message={message} />
</LayoutContainer> </LayoutContainer>
); );
} }
if(servicesQuickActions.show) { if (servicesQuickActions.show) {
const handleTooltipBlur = evt => { const handleTooltipBlur = evt => {
toggleServicesQuickActions({ show: false }); toggleServicesQuickActions({ show: false });
}; };
const handleRestartClick = (evt, service) => { const handleRestartClick = (evt, service) => {
this.setState({errors: {}}); this.setState({ errors: {} });
toggleServicesQuickActions({ show: false }); toggleServicesQuickActions({ show: false });
restartServices(service.id).catch(err => { restartServices(service.id).catch(err => {
this.setState({ errors: { restart: err } }); this.setState({ errors: { restart: err } });
@ -74,7 +73,7 @@ export class ServicesQuickActions extends Component {
}; };
const handleStopClick = (evt, service) => { const handleStopClick = (evt, service) => {
this.setState({errors: {}}); this.setState({ errors: {} });
toggleServicesQuickActions({ show: false }); toggleServicesQuickActions({ show: false });
stopServices(service.id).catch(err => { stopServices(service.id).catch(err => {
this.setState({ errors: { stop: err } }); this.setState({ errors: { stop: err } });
@ -82,7 +81,7 @@ export class ServicesQuickActions extends Component {
}; };
const handleStartClick = (evt, service) => { const handleStartClick = (evt, service) => {
this.setState({errors: {}}); this.setState({ errors: {} });
toggleServicesQuickActions({ show: false }); toggleServicesQuickActions({ show: false });
startServices(service.id).catch(err => { startServices(service.id).catch(err => {
this.setState({ errors: { start: err } }); this.setState({ errors: { start: err } });
@ -90,13 +89,13 @@ export class ServicesQuickActions extends Component {
}; };
const handleScaleClick = (evt, service) => { const handleScaleClick = (evt, service) => {
this.setState({errors: {}}); this.setState({ errors: {} });
toggleServicesQuickActions({ show: false }); toggleServicesQuickActions({ show: false });
push(`${url}/${service.slug}/scale`); push(`${url}/${service.slug}/scale`);
}; };
const handleDeleteClick = (evt, service) => { const handleDeleteClick = (evt, service) => {
this.setState({errors: {}}); this.setState({ errors: {} });
toggleServicesQuickActions({ show: false }); toggleServicesQuickActions({ show: false });
push(`${url}/${service.slug}/delete`); push(`${url}/${service.slug}/delete`);
}; };
@ -115,16 +114,16 @@ export class ServicesQuickActions extends Component {
onDeleteClick={handleDeleteClick} onDeleteClick={handleDeleteClick}
/> />
</StyledContainer> </StyledContainer>
) );
} }
if(quickActions || errorMessage) { if (quickActions || errorMessage) {
return ( return (
<div> <div>
{errorMessage} {errorMessage}
{quickActions} {quickActions}
</div> </div>
) );
} }
return null; return null;

View File

@ -47,7 +47,7 @@ export class ServicesTopology extends Component {
services, services,
loading, loading,
error, error,
toggleServicesQuickActions, toggleServicesQuickActions
} = this.props; } = this.props;
if (loading && !forceArray(services).length) { if (loading && !forceArray(services).length) {
@ -85,18 +85,27 @@ export class ServicesTopology extends Component {
const container = this._refs.container; const container = this._refs.container;
const containerRect = container.getBoundingClientRect(); const containerRect = container.getBoundingClientRect();
const position = { const position = {
top: `${containerRect.top + window.scrollY + tooltipData.position.top}px`, top: `${containerRect.top +
left: `${containerRect.left + window.scrollX + tooltipData.position.left}px` window.scrollY +
} tooltipData.position.top}px`,
left: `${containerRect.left +
window.scrollX +
tooltipData.position.left}px`
};
const data = { const data = {
...tooltipData, ...tooltipData,
position position
} };
toggleServicesQuickActions(data); toggleServicesQuickActions(data);
}; };
const handleNodeTitleClick = (evt, { service }) => { const handleNodeTitleClick = (evt, { service }) => {
push(`${url.split('/').slice(0, 3).join('/')}/services/${service.slug}`); push(
`${url
.split('/')
.slice(0, 3)
.join('/')}/services/${service.slug}`
);
}; };
let renderedError = null; let renderedError = null;
@ -173,7 +182,7 @@ const ServicesGql = graphql(ServicesQuery, {
const ServicesTopologyWithData = compose( const ServicesTopologyWithData = compose(
ServicesGql, ServicesGql,
UiConnect, UiConnect,
withNotFound([ GqlPaths.DEPLOYMENT_GROUP ]) withNotFound([GqlPaths.DEPLOYMENT_GROUP])
)(ServicesTopology); )(ServicesTopology);
export default ServicesTopologyWithData; export default ServicesTopologyWithData;

View File

@ -1,8 +1,8 @@
query Instances( query Instances(
$deploymentGroupSlug: String!, $deploymentGroupSlug: String!
$serviceSlug: String!, $serviceSlug: String!
$metricNames: [MetricName]!, $metricNames: [MetricName]!
$start: String!, $start: String!
$end: String! $end: String!
) { ) {
deploymentGroup(slug: $deploymentGroupSlug) { deploymentGroup(slug: $deploymentGroupSlug) {

View File

@ -25,10 +25,7 @@ import {
ServiceMetrics ServiceMetrics
} from '@containers/service'; } from '@containers/service';
import { import { InstanceList, InstancesTooltip } from '@containers/instances';
InstanceList,
InstancesTooltip
} from '@containers/instances';
import { DeploymentGroupDelete } from '@containers/deployment-group'; import { DeploymentGroupDelete } from '@containers/deployment-group';
@ -43,23 +40,27 @@ const Container = styled.div`
const rootRedirect = p => <Redirect to="/deployment-groups" />; const rootRedirect = p => <Redirect to="/deployment-groups" />;
const servicesListRedirect = 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 => const servicesTopologyRedirect = p => (
<Redirect <Redirect
to={`/deployment-groups/${p.match.params.deploymentGroup}/services-topology`} 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`}
/>; />
);
const App = p => const App = p => (
<div> <div>
<Switch> <Switch>
<Route <Route
@ -222,7 +223,8 @@ const App = p =>
component={servicesTopologyRedirect} component={servicesTopologyRedirect}
/> />
</Switch> </Switch>
</div>; </div>
);
const Router = ( const Router = (
<BrowserRouter> <BrowserRouter>

View File

@ -1,5 +1,8 @@
import { handleActions } from 'redux-actions'; import { handleActions } from 'redux-actions';
import { toggleServicesQuickActions, toggleInstancesTooltip } from '@state/actions'; import {
toggleServicesQuickActions,
toggleInstancesTooltip
} from '@state/actions';
export const _toggleServicesQuickActions = (state, action) => { export const _toggleServicesQuickActions = (state, action) => {
const { position, service, show } = action.payload; const { position, service, show } = action.payload;

View File

@ -104,10 +104,10 @@ const getInstancesHealthy = instances => {
return instances.reduce( return instances.reduce(
(healthy, instance) => ({ (healthy, instance) => ({
total: healthy.total + 1, total: healthy.total + 1,
healthy: instance.healthy === 'HEALTHY' ? healthy:
healthy.healthy + 1 : healthy.healthy instance.healthy === 'HEALTHY' ? healthy.healthy + 1 : healthy.healthy
}), }),
{total: 0, healthy: 0} { total: 0, healthy: 0 }
); );
}; };
@ -174,6 +174,8 @@ const processServicesForTopology = services => {
})); }));
}; };
/* ,
instancesByServiceId */
export { export {
deploymentGroupBySlug as deploymentGroupBySlugSelector, deploymentGroupBySlug as deploymentGroupBySlugSelector,
serviceBySlug as serviceBySlugSelector, serviceBySlug as serviceBySlugSelector,
@ -182,6 +184,5 @@ export {
getInstancesHealthy, getInstancesHealthy,
getService, getService,
processServices, processServices,
processServicesForTopology /* , processServicesForTopology
instancesByServiceId */
}; };

View File

@ -30,8 +30,11 @@ export const client = new ApolloClient({
? o.timestamp ? o.timestamp
: o.name && o.instance : o.name && o.instance
? `${o.name}-${o.instance}` ? `${o.name}-${o.instance}`
: o.name ? o.name : o.time && o.value : o.name
? `${o.time}-${o.value}` : 'apollo-cache-key-not-defined'; ? o.name
: o.time && o.value
? `${o.time}-${o.value}`
: 'apollo-cache-key-not-defined';
return `${o.__typename}:${id}`; return `${o.__typename}:${id}`;
}, },
networkInterface: createNetworkInterface({ networkInterface: createNetworkInterface({

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -12,9 +11,7 @@ import { deploymentGroup } from '../../mocks';
it('renders <DeploymentGroupDelete /> without throwing', () => { it('renders <DeploymentGroupDelete /> without throwing', () => {
const tree = renderer const tree = renderer
.create( .create(<DeploymentGroupDelete deploymentGroup={deploymentGroup} />)
<DeploymentGroupDelete deploymentGroup={deploymentGroup} />
)
.toJSON(); .toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -10,10 +9,6 @@ import 'jest-styled-components';
import EmtpyInstances from '@components/instances/empty.js'; import EmtpyInstances from '@components/instances/empty.js';
it('renders <EmtpyInstances /> without throwing', () => { it('renders <EmtpyInstances /> without throwing', () => {
const tree = renderer const tree = renderer.create(<EmtpyInstances />).toJSON();
.create(
<EmtpyInstances />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -11,10 +10,6 @@ import InstanceCard from '@components/instances/list-item.js';
import { instance } from '../../mocks'; import { instance } from '../../mocks';
it('renders <InstanceCard /> without throwing', () => { it('renders <InstanceCard /> without throwing', () => {
const tree = renderer const tree = renderer.create(<InstanceCard instance={instance} />).toJSON();
.create(
<InstanceCard instance={instance} />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -9,10 +9,6 @@ import 'jest-styled-components';
import Container from '@components/layout/container'; import Container from '@components/layout/container';
it('renders <Container /> without throwing', () => { it('renders <Container /> without throwing', () => {
const tree = renderer const tree = renderer.create(<Container />).toJSON();
.create(
<Container />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -9,10 +9,6 @@ import 'jest-styled-components';
import Error from '@components/messaging/error'; import Error from '@components/messaging/error';
it('renders <Error /> without throwing', () => { it('renders <Error /> without throwing', () => {
const tree = renderer const tree = renderer.create(<Error />).toJSON();
.create(
<Error />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -9,10 +9,6 @@ import 'jest-styled-components';
import Loader from '@components/messaging/loader'; import Loader from '@components/messaging/loader';
it('renders <Loader /> without throwing', () => { it('renders <Loader /> without throwing', () => {
const tree = renderer const tree = renderer.create(<Loader />).toJSON();
.create(
<Loader />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -11,7 +11,7 @@ import ModalError from '@components/messaging/modal-error';
it('renders <ModalError /> without throwing', () => { it('renders <ModalError /> without throwing', () => {
const tree = renderer const tree = renderer
.create( .create(
<ModalError message='Modal error message' onCloseClick={() => {}}/> <ModalError message="Modal error message" onCloseClick={() => {}} />
) )
.toJSON(); .toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();

View File

@ -9,10 +9,6 @@ import 'jest-styled-components';
import Warning from '@components/messaging/warning'; import Warning from '@components/messaging/warning';
it('renders <Warning /> without throwing', () => { it('renders <Warning /> without throwing', () => {
const tree = renderer const tree = renderer.create(<Warning message="Warning message" />).toJSON();
.create(
<Warning message='Warning message'/>
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -9,10 +9,6 @@ import 'jest-styled-components';
import Title from '@components/navigation/title'; import Title from '@components/navigation/title';
it('renders <Title /> without throwing', () => { it('renders <Title /> without throwing', () => {
const tree = renderer const tree = renderer.create(<Title />).toJSON();
.create(
<Title />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -10,10 +10,6 @@ import { service } from '../../mocks';
import Delete from '@components/service/delete'; import Delete from '@components/service/delete';
it('renders <Delete /> without throwing', () => { it('renders <Delete /> without throwing', () => {
const tree = renderer const tree = renderer.create(<Delete service={service} />).toJSON();
.create(
<Delete service={service} />
)
.toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -23,9 +22,7 @@ it('renders <ServiceListItem /> without throwing', () => {
it('renders child <ServiceListItem /> without throwing', () => { it('renders child <ServiceListItem /> without throwing', () => {
const tree = renderer const tree = renderer
.create( .create(<ServiceListItem service={service} isChild />)
<ServiceListItem service={service} isChild />
)
.toJSON(); .toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -12,9 +11,7 @@ import { service } from '../../mocks';
xit('renders <ServiceStatus /> without throwing', () => { xit('renders <ServiceStatus /> without throwing', () => {
const tree = renderer const tree = renderer
.create( .create(<ServiceStatus instanceStatuses={service.instanceStatuses} />)
<ServiceStatus instanceStatuses={service.instanceStatuses} />
)
.toJSON(); .toJSON();
expect(tree).toMatchSnapshot(); expect(tree).toMatchSnapshot();
}); });

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -17,12 +16,12 @@ it('renders <DeploymentGroupCreate /> without throwing', () => {
stage: '' stage: ''
} }
} }
} };
const tree = renderer const tree = renderer
.create( .create(
<Store> <Store>
<Router> <Router>
<DeploymentGroupCreate { ...props } /> <DeploymentGroupCreate {...props} />
</Router> </Router>
</Store> </Store>
) )

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -15,12 +14,12 @@ it('renders <Breadcrumb /> without throwing', () => {
location: { location: {
pathname: '' pathname: ''
} }
} };
const tree = renderer const tree = renderer
.create( .create(
<Store> <Store>
<Router> <Router>
<Breadcrumb { ...props } /> <Breadcrumb {...props} />
</Router> </Router>
</Store> </Store>
) )

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -15,10 +14,7 @@ it('renders <ServiceList /> without throwing', () => {
.create( .create(
<Store> <Store>
<Router> <Router>
<ServiceList <ServiceList deploymentGroup={deploymentGroup} services={services} />
deploymentGroup={deploymentGroup}
services={services}
/>
</Router> </Router>
</Store> </Store>
) )

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -18,12 +17,12 @@ it('renders <ServicesMenu /> without throwing', () => {
history: { history: {
push: () => {} push: () => {}
} }
} };
const tree = renderer const tree = renderer
.create( .create(
<Store> <Store>
<Router> <Router>
<ServicesMenu { ...props } /> <ServicesMenu {...props} />
</Router> </Router>
</Store> </Store>
) )

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */

View File

@ -1,4 +1,3 @@
/** /**
* @jest-environment jsdom * @jest-environment jsdom
*/ */
@ -15,9 +14,7 @@ it('renders <ServicesTopology /> without throwing', () => {
.create( .create(
<Store> <Store>
<Router> <Router>
<ServicesTopology <ServicesTopology services={services} />
services={services}
/>
</Router> </Router>
</Store> </Store>
) )

View File

@ -1,37 +1,31 @@
export const instance = { export const instance = {
"id": "309ecd9f-ac03-474b-aff7-4bd2e743296c", id: '309ecd9f-ac03-474b-aff7-4bd2e743296c',
"name": "wordpress_01", name: 'wordpress_01',
"serviceId": "be227788-74f1-4e5b-a85f-b5c71cbae8d8", serviceId: 'be227788-74f1-4e5b-a85f-b5c71cbae8d8',
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", deploymentGroupId: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
"machineId": "011f7479-2d45-442d-99bf-7f6216954cc8", machineId: '011f7479-2d45-442d-99bf-7f6216954cc8',
"status": "RUNNING", status: 'RUNNING',
"healthy": "HEALTHY" healthy: 'HEALTHY'
}; };
export const service = { export const service = {
"id": "081a792c-47e0-4439-924b-2efa9788ae9e", id: '081a792c-47e0-4439-924b-2efa9788ae9e',
"slug": "nginx", slug: 'nginx',
"name": "Nginx", name: 'Nginx',
"deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", deploymentGroupId: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
"connections": ["be227788-74f1-4e5b-a85f-b5c71cbae8d8"], connections: ['be227788-74f1-4e5b-a85f-b5c71cbae8d8'],
"instances": [instance], instances: [instance],
"instanceStatuses": [{ status: "RUNNING", count: 1 }] instanceStatuses: [{ status: 'RUNNING', count: 1 }]
}; };
export const deploymentGroup = { export const deploymentGroup = {
"id": "e0ea0c02-55cc-45fe-8064-3e5176a59401", id: 'e0ea0c02-55cc-45fe-8064-3e5176a59401',
"slug": "wordpress-blog-example", slug: 'wordpress-blog-example',
"name": "Wordpress Blog Example" name: 'Wordpress Blog Example'
}; };
export const services = [ export const services = [service];
service
];
export const instances = [ export const instances = [instance];
instance
];
export const deploymentGroups = [ export const deploymentGroups = [deploymentGroup];
deploymentGroup
]

View File

@ -1,7 +1,4 @@
import React from 'react'; import React from 'react';
import { MemoryRouter } from 'react-router-dom'; import { MemoryRouter } from 'react-router-dom';
export default ({ children }) => export default ({ children }) => <MemoryRouter>{children}</MemoryRouter>;
<MemoryRouter>
{children}
</MemoryRouter>;

View File

@ -2,7 +2,8 @@ import React from 'react';
import { client, store } from '@state/store'; import { client, store } from '@state/store';
import { ApolloProvider } from 'react-apollo'; import { ApolloProvider } from 'react-apollo';
export default ({ children }) => export default ({ children }) => (
<ApolloProvider client={client} store={store}> <ApolloProvider client={client} store={store}>
{children} {children}
</ApolloProvider>; </ApolloProvider>
);

View File

@ -2,7 +2,6 @@ import React from 'react';
import { ThemeProvider } from 'styled-components'; import { ThemeProvider } from 'styled-components';
import { theme } from 'joyent-ui-toolkit'; import { theme } from 'joyent-ui-toolkit';
export default ({ children }) => export default ({ children }) => (
<ThemeProvider theme={theme}> <ThemeProvider theme={theme}>{children}</ThemeProvider>
{children} );
</ThemeProvider>;

View File

@ -5,7 +5,6 @@ import {
import state from '@state/state'; import state from '@state/state';
describe('ui reducer', () => { describe('ui reducer', () => {
it('toggleServicesQuickActions shows correctly', () => { it('toggleServicesQuickActions shows correctly', () => {
const uiState = state.ui; const uiState = state.ui;
const expectedUiState = { const expectedUiState = {
@ -23,17 +22,19 @@ describe('ui reducer', () => {
} }
} }
} }
} };
const action = { payload: { const action = {
show: true, payload: {
position: { show: true,
top: 10, position: {
left: 10 top: 10,
}, left: 10
service: { },
id: 'service-id' service: {
id: 'service-id'
}
} }
}}; };
const result = _toggleServicesQuickActions(uiState, action); const result = _toggleServicesQuickActions(uiState, action);
expect(result).toEqual(expectedUiState); expect(result).toEqual(expectedUiState);
}); });
@ -48,8 +49,8 @@ describe('ui reducer', () => {
show: false show: false
} }
} }
} };
const action = { payload: { show: false }}; const action = { payload: { show: false } };
const result = _toggleServicesQuickActions(uiState, action); const result = _toggleServicesQuickActions(uiState, action);
expect(result).toEqual(expectedUiState); expect(result).toEqual(expectedUiState);
}); });
@ -72,18 +73,20 @@ describe('ui reducer', () => {
type: 'healthy' type: 'healthy'
} }
} }
} };
const action = { payload: { const action = {
show: true, payload: {
position: { show: true,
top: 10, position: {
left: 10 top: 10,
}, left: 10
instance: { },
id: 'instance-id' instance: {
}, id: 'instance-id'
type: 'healthy' },
}}; type: 'healthy'
}
};
const result = _toggleInstancesTooltip(uiState, action); const result = _toggleInstancesTooltip(uiState, action);
expect(result).toEqual(expectedUiState); expect(result).toEqual(expectedUiState);
}); });
@ -98,9 +101,9 @@ describe('ui reducer', () => {
show: false show: false
} }
} }
} };
const action = { payload: { show: false }}; const action = { payload: { show: false } };
const result = _toggleInstancesTooltip(uiState, action); const result = _toggleInstancesTooltip(uiState, action);
expect(result).toEqual(expectedUiState); expect(result).toEqual(expectedUiState);
}); });
}) });

View File

@ -10,48 +10,45 @@ import {
} from '@state/selectors'; } from '@state/selectors';
describe('Redux selectors and Apollo helpers', () => { describe('Redux selectors and Apollo helpers', () => {
describe('getInstanceStatuses', () => { describe('getInstanceStatuses', () => {
it('gathers instance statuses correctly', () => { it('gathers instance statuses correctly', () => {
const service = { const service = {
instances: [ instances: [
{ status: 'RUNNING' }, { status: 'RUNNING' },
{ status: 'RUNNING' }, { status: 'RUNNING' },
{ status: 'READY' }, { status: 'READY' },
{ status: 'RUNNING' }, { status: 'RUNNING' },
{ status: 'INCOMPLETE' }, { status: 'INCOMPLETE' },
{ status: 'READY' }, { status: 'READY' },
{ status: 'OFFLINE' }, { status: 'OFFLINE' },
{ status: 'STOPPED' }, { status: 'STOPPED' },
{ status: 'STOPPED' }, { status: 'STOPPED' },
{ status: 'RUNNING' } { status: 'RUNNING' }
] ]
}; };
const expectedResult = [ const expectedResult = [
{ status: 'RUNNING', count: 4 }, { status: 'RUNNING', count: 4 },
{ status: 'READY', count: 2 }, { status: 'READY', count: 2 },
{ status: 'INCOMPLETE', count: 1 }, { status: 'INCOMPLETE', count: 1 },
{ status: 'OFFLINE', count: 1 }, { status: 'OFFLINE', count: 1 },
{ status: 'STOPPED', count: 2 } { status: 'STOPPED', count: 2 }
]; ];
const result = getInstanceStatuses(service); const result = getInstanceStatuses(service);
expect(result).toEqual(expectedResult); expect(result).toEqual(expectedResult);
}); });
it('does not throw a hissy fit if there are no instances', () => { it('does not throw a hissy fit if there are no instances', () => {
const service = { const service = {
instances: [] instances: []
}; };
const expectedResult = []; const expectedResult = [];
const result = getInstanceStatuses(service); const result = getInstanceStatuses(service);
expect(result).toEqual(expectedResult); expect(result).toEqual(expectedResult);
}); });
}); });
describe('getInstancesActive', () => { describe('getInstancesActive', () => {
it("returns true if all instances' status is active", () => {
it('returns true if all instances\' status is active', () => {
const statuses = [ const statuses = [
{ status: 'RUNNING' }, { status: 'RUNNING' },
{ status: 'READY' }, { status: 'READY' },
@ -63,7 +60,7 @@ describe('Redux selectors and Apollo helpers', () => {
expect(result).toEqual(expectedResult); expect(result).toEqual(expectedResult);
}); });
it('returns false if no instances\' status is active', () => { it("returns false if no instances' status is active", () => {
const statuses = [ const statuses = [
{ status: 'STOPPING' }, { status: 'STOPPING' },
{ status: 'FAILED' }, { status: 'FAILED' },
@ -75,7 +72,7 @@ describe('Redux selectors and Apollo helpers', () => {
expect(result).toEqual(expectedResult); expect(result).toEqual(expectedResult);
}); });
it('returns true if some instances\' status is active', () => { it("returns true if some instances' status is active", () => {
const statuses = [ const statuses = [
{ status: 'STOPPING' }, { status: 'STOPPING' },
{ status: 'FAILED' }, { status: 'FAILED' },
@ -89,7 +86,6 @@ describe('Redux selectors and Apollo helpers', () => {
}); });
describe('getInstancesHealthy', () => { describe('getInstancesHealthy', () => {
it('returns the number of healthy instances correctly', () => { it('returns the number of healthy instances correctly', () => {
const instances = [ const instances = [
{ healthy: 'HEALTHY' }, { healthy: 'HEALTHY' },
@ -106,39 +102,31 @@ describe('Redux selectors and Apollo helpers', () => {
}); });
describe('getService', () => { describe('getService', () => {
it('returns the service decorated with details for display correctly', () => { it('returns the service decorated with details for display correctly', () => {
const result = getService(nginxService, 0); const result = getService(nginxService, 0);
expect(result).toEqual(nginxExpectedResult); expect(result).toEqual(nginxExpectedResult);
}); });
it('returns the consul service decorated with details for display correctly', () => { it('returns the consul service decorated with details for display correctly', () => {
const result = getService(consulService, 1); const result = getService(consulService, 1);
expect(result).toEqual(consulExpectedResult); expect(result).toEqual(consulExpectedResult);
}); });
}); });
describe('processServices', () => { describe('processServices', () => {
it('returns the services decorated with details for display correctly', () => { it('returns the services decorated with details for display correctly', () => {
const services = [ const services = [nginxService, consulService];
nginxService, const expectedResult = [nginxExpectedResult, consulExpectedResult];
consulService
];
const expectedResult = [
nginxExpectedResult,
consulExpectedResult
];
const result = processServices(services); const result = processServices(services);
expect(result).toEqual(expectedResult); expect(result).toEqual(expectedResult);
}); });
it('removes deleted services', () => { it('removes deleted services', () => {
const services = [{ const services = [
status: 'DELETED' {
}]; status: 'DELETED'
}
];
const expectedResult = []; const expectedResult = [];
const result = processServices(services); const result = processServices(services);
expect(result).toEqual(expectedResult); expect(result).toEqual(expectedResult);
@ -146,16 +134,12 @@ describe('Redux selectors and Apollo helpers', () => {
}); });
describe('processServicesForTopology', () => { describe('processServicesForTopology', () => {
it('returns the services decorated with details for display correctly', () => { it('returns the services decorated with details for display correctly', () => {
const services = [ const services = [
{ {
...nginxService, ...nginxService,
id: 'nginx-service-0', id: 'nginx-service-0',
connections: [ connections: ['consul-service-0', 'consul-service-1']
'consul-service-0',
'consul-service-1'
]
}, },
{ {
...nginxService, ...nginxService,
@ -164,9 +148,7 @@ describe('Redux selectors and Apollo helpers', () => {
{ {
...consulService, ...consulService,
id: 'consul-service-0', id: 'consul-service-0',
connections: [ connections: ['consul-service-1']
'consul-service-1'
]
}, },
{ {
...consulService, ...consulService,
@ -177,10 +159,7 @@ describe('Redux selectors and Apollo helpers', () => {
{ {
...nginxExpectedResult, ...nginxExpectedResult,
id: 'nginx-service-0', id: 'nginx-service-0',
connections: [ connections: ['consul-service-0', 'consul-service-1'],
'consul-service-0',
'consul-service-1'
],
connected: true, connected: true,
index: 0 index: 0
}, },
@ -193,9 +172,7 @@ describe('Redux selectors and Apollo helpers', () => {
{ {
...consulExpectedResult, ...consulExpectedResult,
id: 'consul-service-0', id: 'consul-service-0',
connections: [ connections: ['consul-service-1'],
'consul-service-1'
],
connected: true, connected: true,
index: 2 index: 2
}, },

View File

@ -48,12 +48,8 @@
"tap-xunit": "^1.7.0" "tap-xunit": "^1.7.0"
}, },
"ava": { "ava": {
"files": [ "files": ["test/*.js"],
"test/*.js" "source": ["src/*.js"],
],
"source": [
"src/*.js"
],
"failFast": true "failFast": true
} }
} }

View File

@ -268,8 +268,10 @@
}, },
{ {
"id": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4", "id": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
"slug": "extra service reported by containerpilot: redbaloonservice-https", "slug":
"name": "Extra service reported by ContainerPilot: RedBaloonService-HTTPS", "extra service reported by containerpilot: redbaloonservice-https",
"name":
"Extra service reported by ContainerPilot: RedBaloonService-HTTPS",
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"status": "ACTIVE" "status": "ACTIVE"
}, },
@ -578,7 +580,8 @@
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3", "serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
"machineId": "c9a49cff-4438-460f-bc46-610bfecbddca", "machineId": "c9a49cff-4438-460f-bc46-610bfecbddca",
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-0" "name":
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-0"
}, },
{ {
"id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-1", "id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-1",
@ -587,7 +590,8 @@
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3", "serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
"machineId": "4252ddb5-e9b4-4d1e-aa53-6e1bdcdeab30", "machineId": "4252ddb5-e9b4-4d1e-aa53-6e1bdcdeab30",
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-1" "name":
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-1"
}, },
{ {
"id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-2", "id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-2",
@ -596,7 +600,8 @@
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3", "serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
"machineId": "783c5eb2-2145-4fd2-a22f-274bec3b2ffc", "machineId": "783c5eb2-2145-4fd2-a22f-274bec3b2ffc",
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-2" "name":
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-2"
}, },
{ {
"id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-3", "id": "instance-31663285-2b58-4f92-b6f5-3ef34591c3a3-3",
@ -605,7 +610,8 @@
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3", "serviceId": "31663285-2b58-4f92-b6f5-3ef34591c3a3",
"machineId": "f8a54a11-93f4-4b0a-9c80-be6542393448", "machineId": "f8a54a11-93f4-4b0a-9c80-be6542393448",
"name": "instance-Extra service reported by ContainerPilot: CartService-HTTPS-3" "name":
"instance-Extra service reported by ContainerPilot: CartService-HTTPS-3"
}, },
{ {
"id": "instance-3b954132-49fc-405c-9d91-c59b8953d7b8-0", "id": "instance-3b954132-49fc-405c-9d91-c59b8953d7b8-0",
@ -1747,7 +1753,8 @@
"healthy": "UNKNOWN", "healthy": "UNKNOWN",
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4", "serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-0", "name":
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-0",
"machineId": "20f5bf63-c7d7-4b80-bbe0-63dd744f1b72" "machineId": "20f5bf63-c7d7-4b80-bbe0-63dd744f1b72"
}, },
{ {
@ -1756,7 +1763,8 @@
"healthy": "UNKNOWN", "healthy": "UNKNOWN",
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4", "serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-1", "name":
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-1",
"machineId": "bf10d972-17e0-4267-908e-4a8184d7c164" "machineId": "bf10d972-17e0-4267-908e-4a8184d7c164"
}, },
{ {
@ -1765,7 +1773,8 @@
"healthy": "UNKNOWN", "healthy": "UNKNOWN",
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4", "serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-2", "name":
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-2",
"machineId": "d7204a39-5005-4925-a2d5-02afbb2457db" "machineId": "d7204a39-5005-4925-a2d5-02afbb2457db"
}, },
{ {
@ -1774,7 +1783,8 @@
"healthy": "UNKNOWN", "healthy": "UNKNOWN",
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4", "serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-3", "name":
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-3",
"machineId": "80dac6f7-e459-4f42-b824-5abdd6f13d41" "machineId": "80dac6f7-e459-4f42-b824-5abdd6f13d41"
}, },
{ {
@ -1783,7 +1793,8 @@
"healthy": "UNKNOWN", "healthy": "UNKNOWN",
"deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f", "deploymentGroupId": "ba217234-9b1b-41a7-8079-08f9a4aadb0f",
"serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4", "serviceId": "1ad6dd4e-15bd-4ea5-a0db-bf071d7958c4",
"name": "instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-4", "name":
"instance-Extra service reported by ContainerPilot: RedBaloonService-HTTPS-4",
"machineId": "dbb819d5-9df2-48e1-8412-7cd48ca4c186" "machineId": "dbb819d5-9df2-48e1-8412-7cd48ca4c186"
}, },
{ {

View File

@ -53,4 +53,4 @@
} }
] ]
} }
] ]

View File

@ -34466,4 +34466,4 @@
} }
] ]
} }
] ]

View File

@ -34490,4 +34490,4 @@
} }
] ]
} }
] ]

View File

@ -22993,4 +22993,4 @@
} }
] ]
} }
] ]

View File

@ -48,17 +48,11 @@ const cleanQuery = (q = {}) => JSON.parse(JSON.stringify(q));
let metricDataIndex = 0; let metricDataIndex = 0;
const getMetrics = query => { const getMetrics = query => {
const { const { names, start, end, instanceId } = query;
names,
start,
end,
instanceId
} = query;
const metrics = names.reduce((metrics, name) => { const metrics = names.reduce((metrics, name) => {
// pick one of the three metric data jsons, so there's variety // pick one of the three metric data jsons, so there's variety
const index = metricDataIndex%metricData.length; const index = metricDataIndex % metricData.length;
metricDataIndex++; metricDataIndex++;
const md = metricData[index].find(md => md.name === name); const md = metricData[index].find(md => md.name === name);
@ -69,17 +63,20 @@ const getMetrics = query => {
// how many records do we need? // how many records do we need?
const duration = e.diff(s); // duration for which we need data const duration = e.diff(s); // duration for which we need data
const records = Math.floor(duration/15000); // new metric record every 15 secs const records = Math.floor(duration / 15000); // new metric record every 15 secs
const requiredMetrics = []; const requiredMetrics = [];
let i = 0; let i = 0;
const time = moment(s); const time = moment(s);
// start at a random point within the dataset for variety // start at a random point within the dataset for variety
const randomIndex = Math.round(Math.random() * m.length); const randomIndex = Math.round(Math.random() * m.length);
while(i < records) { while (i < records) {
const index = (randomIndex + i)%m.length; // loop if not enough data const index = (randomIndex + i) % m.length; // loop if not enough data
const requiredMetric = m[index]; const requiredMetric = m[index];
requiredMetric.time = time.add(15, 'seconds').utc().format(); // we should have a new record every 15 secs requiredMetric.time = time
.add(15, 'seconds')
.utc()
.format(); // we should have a new record every 15 secs
requiredMetrics.push(requiredMetric); requiredMetrics.push(requiredMetric);
i++; i++;
} }
@ -90,13 +87,13 @@ const getMetrics = query => {
start: s.utc().format(), start: s.utc().format(),
end: time.utc().format(), // this will be used by the frontend for the next fetch end: time.utc().format(), // this will be used by the frontend for the next fetch
metrics: requiredMetrics metrics: requiredMetrics
} };
metrics.push(requiredMetricData); metrics.push(requiredMetricData);
return metrics; return metrics;
}, []); }, []);
return Promise.resolve(metrics); return Promise.resolve(metrics);
} };
const getInstances = query => { const getInstances = query => {
const metricsResolver = ({ id }) => query => const metricsResolver = ({ id }) => query =>
@ -175,13 +172,12 @@ const getServices = query => {
return ret; return ret;
}); });
return Promise.resolve(services) return Promise.resolve(services).then(services => {
.then((services) => { if (!services || !services.length) {
if(!services || !services.length) { throw Boom.notFound();
throw Boom.notFound(); }
} return services;
return services; });
});
}; };
const getDeploymentGroups = query => { const getDeploymentGroups = query => {
@ -199,8 +195,8 @@ 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) => { ).then(deploymentGroups => {
if(!deploymentGroups || !deploymentGroups.length) { if (!deploymentGroups || !deploymentGroups.length) {
throw Boom.notFound(); throw Boom.notFound();
} }
return deploymentGroups; return deploymentGroups;
@ -406,11 +402,16 @@ const updateServiceAndInstancesStatus = (
instancesStatus instancesStatus
) => { ) => {
return Promise.all([ return Promise.all([
getServices({ id: serviceId })/* , getServices({
id: serviceId
}) /* ,
getServices({ parentId: serviceId }) */ getServices({ parentId: serviceId }) */
]) ])
.then(services => { .then(services => {
return services.reduce((services, service) => services.concat(service), []) return services.reduce(
(services, service) => services.concat(service),
[]
);
}) })
.then(services => { .then(services => {
updateServiceStatus(services, serviceStatus); updateServiceStatus(services, serviceStatus);
@ -431,7 +432,9 @@ const updateServiceAndInstancesStatus = (
}) })
.then(() => .then(() =>
Promise.all([ Promise.all([
getUnfilteredServices({ id: serviceId })/* , getUnfilteredServices({
id: serviceId
}) /* ,
getUnfilteredServices({ parentId: serviceId }) */ getUnfilteredServices({ parentId: serviceId }) */
]) ])
) )
@ -536,12 +539,7 @@ const parseEnvVars = (str = '') =>
const findEnvInterpolation = (str = '') => const findEnvInterpolation = (str = '') =>
uniq(str.match(INTERPOLATE_REGEX).map(name => name.replace(/^\$/, ''))); uniq(str.match(INTERPOLATE_REGEX).map(name => name.replace(/^\$/, '')));
const config = ({ const config = ({ environment = '', files = [], raw = '', _plain = false }) => {
environment = '',
files = [],
raw = '',
_plain = false
}) => {
const interpolatableNames = findEnvInterpolation(raw); const interpolatableNames = findEnvInterpolation(raw);
const interpolatableEnv = parseEnvVars(environment); const interpolatableEnv = parseEnvVars(environment);
@ -588,11 +586,10 @@ const config = ({
config: Object.assign(service.config, { config: Object.assign(service.config, {
id: hasha(JSON.stringify(service.config)), id: hasha(JSON.stringify(service.config)),
environment: Object.keys(service.config.environment).map(name => ({ environment: Object.keys(service.config.environment).map(name => ({
name, name,
id: hasha(JSON.stringify(service.config.environment[name])), id: hasha(JSON.stringify(service.config.environment[name])),
value: service.config.environment[name] value: service.config.environment[name]
}) }))
)
}) })
}) })
); );

View File

@ -203,7 +203,6 @@ type Datacenter {
region: String! region: String!
} }
# we probably wont use some of these queries or arguments # we probably wont use some of these queries or arguments
# but this way we expose the entire db through gql # but this way we expose the entire db through gql
type Query { type Query {
@ -265,7 +264,13 @@ type Query {
): [Service] ): [Service]
importableDeploymentGroups: [DeploymentGroup] importableDeploymentGroups: [DeploymentGroup]
# start and end should be .toISOString() date strings # start and end should be .toISOString() date strings
metrics(deploymentGroupId: ID!, names: [MetricName]!, instances: [ID]!, start: String!, end: String!): [InstanceMetric] metrics(
deploymentGroupId: ID!
names: [MetricName]!
instances: [ID]!
start: String!
end: String!
): [InstanceMetric]
} }
type Mutation { type Mutation {

View File

@ -152,17 +152,17 @@ export const Button = styled.button`
appearance: button; appearance: button;
&::-moz-focus-inner, &::-moz-focus-inner,
&[type="button"]::-moz-focus-inner, &[type='button']::-moz-focus-inner,
&[type="reset"]::-moz-focus-inner, &[type='reset']::-moz-focus-inner,
&[type="submit"]::-moz-focus-inner { &[type='submit']::-moz-focus-inner {
border-style: none; border-style: none;
padding: 0; padding: 0;
} }
&:-moz-focusring, &:-moz-focusring,
&[type="button"]:-moz-focusring, &[type='button']:-moz-focusring,
&[type="reset"]:-moz-focusring, &[type='reset']:-moz-focusring,
&[type="submit"]:-moz-focusring { &[type='submit']:-moz-focusring {
outline: ${remcalc(1)} dotted ButtonText; outline: ${remcalc(1)} dotted ButtonText;
} }
`; `;
@ -174,24 +174,24 @@ export const Input = styled.input`
margin: 0; margin: 0;
overflow: visible; overflow: visible;
&[type="checkbox"], &[type='checkbox'],
&[type="radio"] { &[type='radio'] {
box-sizing: border-box; box-sizing: border-box;
padding: 0; padding: 0;
} }
&[type="number"]::-webkit-inner-spin-button, &[type='number']::-webkit-inner-spin-button,
&[type="number"]::-webkit-outer-spin-button { &[type='number']::-webkit-outer-spin-button {
height: auto; height: auto;
} }
&[type="search"] { &[type='search'] {
appearance: textfield; appearance: textfield;
outline-offset: ${remcalc(-2)}; outline-offset: ${remcalc(-2)};
} }
&[type="search"]::-webkit-search-cancel-button, &[type='search']::-webkit-search-cancel-button,
&[type="search"]::-webkit-search-decoration { &[type='search']::-webkit-search-decoration {
appearance: none; appearance: none;
} }

View File

@ -36,11 +36,7 @@ const Anchor = ({ children, ...rest }) => {
const Views = [() => (to ? StyledLink : null), () => StyledAnchor]; const Views = [() => (to ? StyledLink : null), () => StyledAnchor];
const View = Views.reduce((sel, view) => (sel ? sel : view()), null); const View = Views.reduce((sel, view) => (sel ? sel : view()), null);
return ( return <View {...rest}>{children}</View>;
<View {...rest}>
{children}
</View>
);
}; };
Anchor.propTypes = { Anchor.propTypes = {

View File

@ -21,6 +21,6 @@ export default css`
margin: 0; margin: 0;
padding: 0; padding: 0;
background: ${theme.background}; background: ${theme.background};
font-family: 'Libre Franklin' font-family: 'Libre Franklin';
} }
`; `;

View File

@ -18,4 +18,6 @@ export default Component =>
? Component.extend` ? Component.extend`
${alignsFromProps}; ${alignsFromProps};
` `
: styled(Component)`${alignsFromProps}`; : styled(Component)`
${alignsFromProps};
`;

View File

@ -33,4 +33,6 @@ export default Component =>
? Component.extend` ? Component.extend`
${unitsFromProps}; ${unitsFromProps};
` `
: styled(Component)`${unitsFromProps}`; : styled(Component)`
${unitsFromProps};
`;

View File

@ -11,9 +11,9 @@ export const tooltipShadow = `0 ${remcalc(2)} ${remcalc(6)} ${remcalc(
export const modalShadow = `0 0 ${remcalc(6)} ${remcalc(1)} rgba(0, 0, 0, 0.1)`; export const modalShadow = `0 0 ${remcalc(6)} ${remcalc(1)} rgba(0, 0, 0, 0.1)`;
export const border = { export const border = {
checked: css`${remcalc(1)} solid ${props => props.theme.primary}`, checked: css`${remcalc(1)} solid ${props => props.theme.primary};`,
unchecked: css`${remcalc(1)} solid ${props => props.theme.grey}`, unchecked: css`${remcalc(1)} solid ${props => props.theme.grey};`,
confirmed: css`${remcalc(1)} solid ${props => props.theme.grey}`, confirmed: css`${remcalc(1)} solid ${props => props.theme.grey};`,
error: css`${remcalc(1)} solid ${props => props.theme.red}`, error: css`${remcalc(1)} solid ${props => props.theme.red};`,
secondary: css`${remcalc(1)} solid ${props => props.theme.secondaryActive}`, secondary: css`${remcalc(1)} solid ${props => props.theme.secondaryActive};`
}; };

View File

@ -4,9 +4,10 @@ import { Row } from 'react-styled-flexboxgrid';
/** /**
* @example ./usage.md * @example ./usage.md
*/ */
export default ({ children, ...rest }) => export default ({ children, ...rest }) => (
<Row name="breadcrum" {...rest}> <Row name="breadcrum" {...rest}>
{children} {children}
</Row>; </Row>
);
export { default as Item } from './item'; export { default as Item } from './item';

View File

@ -21,10 +21,11 @@ const Arrow = styled.div`
margin: ${remcalc(3)} ${remcalc(10)} ${remcalc(3)} ${remcalc(10)}; margin: ${remcalc(3)} ${remcalc(10)} ${remcalc(3)} ${remcalc(10)};
`; `;
export default ({ children, ...rest }) => export default ({ children, ...rest }) => (
<div> <div>
<Name name="breadcrum-item" {...rest}> <Name name="breadcrum-item" {...rest}>
{children} {children}
</Name> </Name>
<Arrow /> <Arrow />
</div>; </div>
);

Some files were not shown because too many files have changed in this diff Show More