Add instances list

This commit is contained in:
JUDIT GRESKOVITS 2017-05-22 18:13:24 +01:00 committed by Sérgio Ramos
parent db4ac69452
commit a8c34ee756
13 changed files with 278 additions and 200 deletions

View File

@ -0,0 +1,13 @@
import React from 'react';
import Column from '@ui/components/column';
import Row from '@ui/components/row';
import { P } from '@ui/components/base-elements';
export default () => (
<Row>
<Column xs={12}>
<P>You don't have any instances</P>
</Column>
</Row>
);

View File

@ -0,0 +1,2 @@
export { default as EmptyInstances } from './empty';
export { default as InstanceListItem } from './list-item';

View File

@ -0,0 +1,33 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
ListItem,
ListItemView,
ListItemMeta,
ListItemTitle,
ListItemOptions
} from '@ui/components/list';
const InstanceListItem = ({
instance = {},
onOptionsClick = () => null,
toggleCollapsed = () => null
}) => (
<ListItem collapsed={true} key={instance.uuid} >
<ListItemView>
<ListItemMeta onClick={toggleCollapsed}>
<ListItemTitle>{instance.name}</ListItemTitle>
</ListItemMeta>
</ListItemView>
<ListItemOptions onClick={onOptionsClick} />
</ListItem>
);
InstanceListItem.propTypes = {
instance: PropTypes.object,
onOptionsClick: React.PropTypes.func,
toggleCollapsed: React.PropTypes.func
};
export default InstanceListItem;

View File

@ -39,20 +39,11 @@ function getBreadcrumbLinks(links) {
<BreadcrumbSpan key={breadcrumb.length}> / </BreadcrumbSpan> <BreadcrumbSpan key={breadcrumb.length}> / </BreadcrumbSpan>
); );
} }
if(index < links.length - 1) { breadcrumb.push(
breadcrumb.push( <BreadcrumbLink key={breadcrumb.length} to={link.pathname}>
<BreadcrumbLink key={breadcrumb.length} to={link.pathname}> {link.name}
{link.name} </BreadcrumbLink>
</BreadcrumbLink> );
);
}
else {
breadcrumb.push(
<span key={breadcrumb.length}>
{link.name}
</span>
);
}
return breadcrumb; return breadcrumb;
}, []); }, []);
} }

View File

@ -5,7 +5,6 @@ import styled from 'styled-components';
// import ItemMetricGroup from '@components/item-metric-group'; // import ItemMetricGroup from '@components/item-metric-group';
import { Link } from '@ui/components/anchor'; import { Link } from '@ui/components/anchor';
import { Checkbox, FormGroup } from '@ui/components/form';
import { import {
DataCentersIcon, DataCentersIcon,
HealthyIcon, HealthyIcon,
@ -25,10 +24,6 @@ import {
ListItemInfo ListItemInfo
} from '@ui/components/list'; } from '@ui/components/list';
const StyledFormGroup = styled(FormGroup)`
width: auto;
`;
const TitleInnerContainer = styled.div` const TitleInnerContainer = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
@ -52,7 +47,7 @@ const ServiceListItem = ({
/> />
)) : null; )) : null;
const to = `/deploymentGroups/${deploymentGroup}/services/${service.id}`; const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`;
const title = service.parent ? ( const title = service.parent ? (
<ListItemTitle> <ListItemTitle>
@ -61,9 +56,6 @@ const ServiceListItem = ({
) : ( ) : (
<ListItemTitle> <ListItemTitle>
<TitleInnerContainer> <TitleInnerContainer>
<StyledFormGroup>
<Checkbox />
</StyledFormGroup>
<Link secondary to={to}> <Link secondary to={to}>
{service.name} {service.name}
</Link> </Link>

View File

@ -1,7 +1,13 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { graphql } from 'react-apollo'; import PropTypes from 'prop-types';
import { compose, graphql } from 'react-apollo';
import PortalQuery from '@graphql/Portal.gql';
import InstancesQuery from '@graphql/Instances.gql'; import InstancesQuery from '@graphql/Instances.gql';
import { LayoutContainer } from '@components/layout';
import { Loader, ErrorMessage } from '@components/messaging';
import { InstanceListItem, EmptyInstances } from '@components/instances';
class InstanceList extends Component { class InstanceList extends Component {
render() { render() {
@ -11,30 +17,59 @@ class InstanceList extends Component {
loading, loading,
error error
} = this.props; } = this.props;
console.log('instances = ', instances);
console.log('loading = ', loading);
console.log('error = ', error);
if(loading) {
return (
<LayoutContainer>
<Loader />
</LayoutContainer>
)
}
else if(error) {
return (
<LayoutContainer>
<ErrorMessage
message='Oops, and error occured while loading your instances.'
/>
</LayoutContainer>
)
}
const instanceList = const instanceList = instances ? instances.map((instance, index) => (
loading ? <p>Loading...</p> : <InstanceListItem
error ? <p>Error!!!</p> : instance={instance}
instances.map((instance, index) => key={instance.uuid}
<p key={index}>{instance.name}</p>); toggleCollapsed={() => null}
/>
)) : (
<EmptyInstances />
);
return ( return (
<div> <LayoutContainer>
<div> <div>
<h2>Instance List</h2> <h2>Instance List</h2>
</div> </div>
{ instanceList } { instanceList }
</div> </LayoutContainer>
); );
} }
} }
const InstanceListWithData = graphql(InstancesQuery, { const PortalGql = graphql(PortalQuery, {});
const InstanceListGql = graphql(InstancesQuery, {
options(props) { options(props) {
const params = props.match.params;
const deploymentGroupSlug = params.deploymentGroup;
const serviceSlug = params.service;
return { return {
variables: { variables: {
deploymentGroupSlug: props.match.params.deploymentGroup deploymentGroupSlug,
} serviceSlug
}
}; };
}, },
props: ({ data: { deploymentGroup, loading, error } }) => ({ props: ({ data: { deploymentGroup, loading, error } }) => ({
@ -44,6 +79,11 @@ const InstanceListWithData = graphql(InstancesQuery, {
loading, loading,
error error
}) })
})(InstanceList) });
const InstanceListWithData = compose(
PortalGql,
InstanceListGql
)(InstanceList);
export default InstanceListWithData; export default InstanceListWithData;

View File

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Breadcrumb as BreadcrumbComponent } from '@components/navigation'; import { Breadcrumb as BreadcrumbComponent } from '@components/navigation';
import { deploymentGroupBySlugSelector, serviceBySlugSelector} from '@root/state/selectors';
const Breadcrumb = ({ const Breadcrumb = ({
deploymentGroup, deploymentGroup,
@ -36,32 +37,12 @@ const Breadcrumb = ({
const ConnectedBreadcrumb = connect( const ConnectedBreadcrumb = connect(
(state, ownProps) => { (state, ownProps) => {
const params = ownProps.match.params; const params = ownProps.match.params;
const deploymentGroupSlug = params.deploymentGroup; const deploymentGroupSlug = params.deploymentGroup;
const serviceSlug = params.service; const serviceSlug = params.service;
const apolloData = state.apollo.data;
const keys = Object.keys(apolloData);
let deploymentGroup, service;
if(keys.length) {
// These should be selectors
if(deploymentGroupSlug) {
deploymentGroup = keys.reduce((dg, k) =>
apolloData[k].__typename === 'DeploymentGroup' &&
apolloData[k].slug === deploymentGroupSlug ?
apolloData[k] : dg, {});
if(serviceSlug) {
service = keys.reduce((s, k) =>
apolloData[k].__typename === 'Service' &&
apolloData[k].slug === serviceSlug ?
apolloData[k] : s, {});
}
}
}
return { return {
deploymentGroup, deploymentGroup: deploymentGroupBySlugSelector(deploymentGroupSlug)(state),
service, service: serviceBySlugSelector(serviceSlug)(state),
location: ownProps.location location: ownProps.location
}; };
}, },

View File

@ -9,6 +9,7 @@ import ServicesQuery from '@graphql/Services.gql';
import { processServices } from '@root/state/selectors'; import { processServices } from '@root/state/selectors';
import { LayoutContainer } from '@components/layout'; import { LayoutContainer } from '@components/layout';
import { Loader, ErrorMessage } from '@components/messaging';
import { ServiceListItem } from '@components/services'; import { ServiceListItem } from '@components/services';
const StyledContainer = styled.div` const StyledContainer = styled.div`
@ -27,12 +28,21 @@ class ServiceList extends Component {
error error
} = this.props; } = this.props;
console.log('services = ', services); if(loading) {
if(loading || error) {
return ( return (
<p>Loading OR error</p> <LayoutContainer>
); <Loader />
</LayoutContainer>
)
}
else if(error) {
return (
<LayoutContainer>
<ErrorMessage
message='Oops, and error occured while loading your services.'
/>
</LayoutContainer>
)
} }
const serviceList = services.map((service) => ( const serviceList = services.map((service) => (

View File

@ -1,10 +1,10 @@
#import "./DeploymentGroupInfo.gql" #import "./DeploymentGroupInfo.gql"
#import "./ServiceInfo.gql" #import "./ServiceInfo.gql"
query Instances($deploymentGroupSlug: String!) { query Instances($deploymentGroupSlug: String!, $serviceSlug: String) {
deploymentGroup(slug: $deploymentGroupSlug) { deploymentGroup(slug: $deploymentGroupSlug) {
...DeploymentGroupInfo ...DeploymentGroupInfo
services { services(slug: $serviceSlug) {
...ServiceInfo ...ServiceInfo
instances { instances {
uuid uuid

View File

@ -22,6 +22,10 @@ const deploymentGroupRedirect = (p) => (
<Redirect to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`} /> <Redirect to={`/deployment-groups/${p.match.params.deploymentGroup}/services-list`} />
); );
const serviceRedirect = (p) => (
<Redirect to={`/deployment-groups/${p.match.params.deploymentGroup}/services/${p.match.params.service}/instances`} />
);
const Router = ( const Router = (
<BrowserRouter> <BrowserRouter>
<div> <div>
@ -51,6 +55,7 @@ const Router = (
<Route path='/deployment-groups/:deploymentGroup/services-topology' exact component={ServicesMenu} /> <Route path='/deployment-groups/:deploymentGroup/services-topology' exact component={ServicesMenu} />
<Route path='/deployment-groups/:deploymentGroup/services-topology' exact component={ServicesTopology} /> <Route path='/deployment-groups/:deploymentGroup/services-topology' exact component={ServicesTopology} />
<Route path='/deployment-groups/:deploymentGroup/services/:service' exact component={serviceRedirect} />
<Route path='/deployment-groups/:deploymentGroup/services/:service/instances' exact component={InstanceList} /> <Route path='/deployment-groups/:deploymentGroup/services/:service/instances' exact component={InstanceList} />
</div> </div>

View File

@ -6,18 +6,20 @@ const apollo = (state) => state.apollo;
const deploymentGroupBySlug = (deploymentGroupSlug) => createSelector( const deploymentGroupBySlug = (deploymentGroupSlug) => createSelector(
[apollo], [apollo],
(apollo) => apollo ? Object.keys(apollo).reduce((dg, k) => (apollo) => apollo && apollo.data ?
apollo[k].__typename === 'DeploymentGroup' && Object.keys(apollo.data).reduce((dg, k) =>
apollo[k].slug === deploymentGroupSlug ? apollo.data[k].__typename === 'DeploymentGroup' &&
apollo[k] : dg, {}) : null apollo.data[k].slug === deploymentGroupSlug ?
apollo.data[k] : dg, null) : null
); );
const servicesBySlug = (serviceSlug) => createSelector( const serviceBySlug = (serviceSlug) => createSelector(
[apollo], [apollo],
(apollo) => apollo ? Object.keys(apollo).reduce((s, k) => (apollo) => apollo && apollo.data ?
apollo[k].__typename === 'Service' && Object.keys(apollo.data).reduce((s, k) =>
apollo[k].slug === serviceSlug ? apollo.data[k].__typename === 'Service' &&
apollo[k] : s, {}) : null apollo.data[k].slug === serviceSlug ?
apollo.data[k] : s, null) : null
); );
// apollo gql utils // // apollo gql utils //
@ -64,6 +66,6 @@ const processServices = (services, datacenter) => {
export { export {
deploymentGroupBySlug as deploymentGroupBySlugSelector, deploymentGroupBySlug as deploymentGroupBySlugSelector,
servicesBySlug as servicesBySlugSelector, serviceBySlug as serviceBySlugSelector,
processServices as processServices processServices as processServices
} }

View File

@ -2,6 +2,7 @@ import { find, filter } from 'lodash';
import data from './mock-data'; import data from './mock-data';
import { normalMetricData, leakMetricData } from './mock-data/metrics'; import { normalMetricData, leakMetricData } from './mock-data/metrics';
// TMP / Util
const datacenter = { const datacenter = {
uuid: 'datacenter-uuid', uuid: 'datacenter-uuid',
region: 'us-east-1' region: 'us-east-1'
@ -26,140 +27,148 @@ const getInstanceMetricData = (dataset, type) => {
const tick = setInterval(() => index++, 15*1000); const tick = setInterval(() => index++, 15*1000);
const resolveFunctions = { // GraphQL
Query: {
portal() {
return portal;
},
deploymentGroups(_, { name, slug }) { const queries = {
return deploymentGroups; portal() {
}, return portal;
deploymentGroup(_, { uuid, name, slug }) {
if(uuid) {
return find(deploymentGroups, { uuid: uuid });
}
if(slug) {
return find(deploymentGroups, { slug: slug });
}
return null;
},
serviceScales(_, { serviceName, versionUuid }) { // : [ServiceScale]
return [];
},
serviceScale(_, { uuid }) { // : ServiceScale
return {};
},
convergenceActions(_, { type, service, versionUuid }) { // : [ConvergenceAction]
return [];
},
convergenceAction(uuid) { // : ConvergenceAction
return {};
},
stateConvergencePlans(_, { running, versionUuid }) { // : [StateConvergencePlan]
return [];
},
stateConvergencePlan(_, { uuid }) { // : StateConvergencePlan
return [];
},
versions(_, { manifestUuid, deploymentGroupUuid }) { // : [Version]
return [];
},
version(_, { uuid, manifestUuid }) { // : Version
return null;
},
manifests(_, { type, deploymentGroupUuid }) { // : [Manifest]
return [];
},
manifest(_, { uuid }) { // : Manifest
return null;
},
services(_, { name, slug, parentUuid, deploymentGroupUuid, deploymentGroupSlug }) { // }: [Service]
if(deploymentGroupUuid) {
return filter(services, { project: deploymentGroupUuid });
}
if(deploymentGroupSlug) {
const deploymentGroup = find(deploymentGroups, { slug: deploymentGroupSlug });
if(deploymentGroup) {
return filter(services, { project: deploymentGroup.uuid });
}
return null;
}
return services;
},
service(_, { uuid, hash }) { // : Service
if(uuid) {
return find(services, { uuid: uuid });
}
if(hash) {
return find(services, { hash: hash });
}
return null;
},
packages(_, { name, type, memory, disk, swap, lwps, vcpus, version, group }) { // : [Package]
return [];
},
package(_, { uuid }) { // : Package
return {};
},
instances(_, { name, machineId, status, serviceUuid, serviceSlug, deploymentGroupUuid, deploymentGroupSlug }) { // : [Instance]
if(serviceUuid) {
return filter(instances, { service: serviceUuid });
}
if(serviceSlug) {
const service = find(services, { slug: serviceSlug });
if(service) {
return filter(instances, { service: service.uuid });
}
return null;
}
return instances;
},
instance(_, { uuid }) { // : Instance
if(uuid) {
return find(instances, { uuid: uuid });
}
},
datacenter() {
return datacenter;
},
/*metricTypes() {
return metricTypes;
},
// tmp test
instanceMetric() {
return {
type: {
uuid: 'node_memory_rss_bytes',
id: 'node_memory_rss_bytes',
name: 'node_memory_rss_bytes',
},
data: getInstanceMetricData(leakMetricData, 'node_memory_rss_bytes')
};
}*/
}, },
deploymentGroups(_, { name, slug }) {
return deploymentGroups;
},
deploymentGroup(_, { uuid, name, slug }) {
if(uuid) {
return find(deploymentGroups, { uuid: uuid });
}
if(slug) {
return find(deploymentGroups, { slug: slug });
}
return null;
},
serviceScales(_, { serviceName, versionUuid }) { // : [ServiceScale]
return [];
},
serviceScale(_, { uuid }) { // : ServiceScale
return {};
},
convergenceActions(_, { type, service, versionUuid }) { // : [ConvergenceAction]
return [];
},
convergenceAction(uuid) { // : ConvergenceAction
return {};
},
stateConvergencePlans(_, { running, versionUuid }) { // : [StateConvergencePlan]
return [];
},
stateConvergencePlan(_, { uuid }) { // : StateConvergencePlan
return [];
},
versions(_, { manifestUuid, deploymentGroupUuid }) { // : [Version]
return [];
},
version(_, { uuid, manifestUuid }) { // : Version
return null;
},
manifests(_, { type, deploymentGroupUuid }) { // : [Manifest]
return [];
},
manifest(_, { uuid }) { // : Manifest
return null;
},
services(_, { name, slug, parentUuid, deploymentGroupUuid, deploymentGroupSlug }) { // }: [Service]
if(deploymentGroupUuid) {
return filter(services, { project: deploymentGroupUuid });
}
if(deploymentGroupSlug) {
const deploymentGroup = find(deploymentGroups, { slug: deploymentGroupSlug });
if(deploymentGroup) {
if(slug) {
return filter(services, { project: deploymentGroup.uuid, slug: slug });
}
return filter(services, { project: deploymentGroup.uuid });
}
return null;
}
return services;
},
service(_, { uuid, hash }) { // : Service
if(uuid) {
return find(services, { uuid: uuid });
}
if(hash) {
return find(services, { hash: hash });
}
return null;
},
packages(_, { name, type, memory, disk, swap, lwps, vcpus, version, group }) { // : [Package]
return [];
},
package(_, { uuid }) { // : Package
return {};
},
instances(_, { name, machineId, status, serviceUuid, serviceSlug, deploymentGroupUuid, deploymentGroupSlug }) { // : [Instance]
if(serviceUuid) {
return filter(instances, { service: serviceUuid });
}
if(serviceSlug) {
const service = find(services, { slug: serviceSlug });
if(service) {
return filter(instances, { service: service.uuid });
}
return null;
}
return instances;
},
instance(_, { uuid }) { // : Instance
if(uuid) {
return find(instances, { uuid: uuid });
}
},
datacenter() {
return datacenter;
},
/*metricTypes() {
return metricTypes;
},
// tmp test
instanceMetric() {
return {
type: {
uuid: 'node_memory_rss_bytes',
id: 'node_memory_rss_bytes',
name: 'node_memory_rss_bytes',
},
data: getInstanceMetricData(leakMetricData, 'node_memory_rss_bytes')
};
}*/
}
const resolveFunctions = {
Query: queries,
Portal: { Portal: {
deploymentGroups(portal) { deploymentGroups(portal, args, context) {
return deploymentGroups; return deploymentGroups;
} }
}, },
DeploymentGroup: { DeploymentGroup: {
services(deploymentGroup) { services(deploymentGroup, args, context) {
return filter(services, { project: deploymentGroup.uuid }); const a = Object.assign({}, args, {deploymentGroupSlug: deploymentGroup.slug})
}, return queries.services(null, a);
}
}, },
Service: { Service: {
instances(service) { instances(service, args, context) {
return filter(instances, { service: service.uuid }); return filter(instances, { service: service.uuid });
}, },
/*metrics(service) { /*metrics(service) {

View File

@ -12,7 +12,7 @@
uuid: ID! uuid: ID!
name: String! name: String!
slug: String! slug: String!
services: [Service]! services(slug: String): [Service]!
version: Version! version: Version!
history: [Version]! history: [Version]!
} }