diff --git a/frontend/src/components/instances/empty.js b/frontend/src/components/instances/empty.js new file mode 100644 index 00000000..e5954fc1 --- /dev/null +++ b/frontend/src/components/instances/empty.js @@ -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 () => ( + + +

You don't have any instances

+
+
+); diff --git a/frontend/src/components/instances/index.js b/frontend/src/components/instances/index.js new file mode 100644 index 00000000..f2137111 --- /dev/null +++ b/frontend/src/components/instances/index.js @@ -0,0 +1,2 @@ +export { default as EmptyInstances } from './empty'; +export { default as InstanceListItem } from './list-item'; diff --git a/frontend/src/components/instances/list-item.js b/frontend/src/components/instances/list-item.js new file mode 100644 index 00000000..d7bfb2ee --- /dev/null +++ b/frontend/src/components/instances/list-item.js @@ -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 +}) => ( + + + + {instance.name} + + + + +); + +InstanceListItem.propTypes = { + instance: PropTypes.object, + onOptionsClick: React.PropTypes.func, + toggleCollapsed: React.PropTypes.func +}; + +export default InstanceListItem; diff --git a/frontend/src/components/navigation/breadcrumb.js b/frontend/src/components/navigation/breadcrumb.js index 9ed94870..24a29bd2 100644 --- a/frontend/src/components/navigation/breadcrumb.js +++ b/frontend/src/components/navigation/breadcrumb.js @@ -39,20 +39,11 @@ function getBreadcrumbLinks(links) { / ); } - if(index < links.length - 1) { - breadcrumb.push( - - {link.name} - - ); - } - else { - breadcrumb.push( - - {link.name} - - ); - } + breadcrumb.push( + + {link.name} + + ); return breadcrumb; }, []); } diff --git a/frontend/src/components/services/list-item.js b/frontend/src/components/services/list-item.js index 5b0efbfa..9f76b9de 100644 --- a/frontend/src/components/services/list-item.js +++ b/frontend/src/components/services/list-item.js @@ -5,7 +5,6 @@ import styled from 'styled-components'; // import ItemMetricGroup from '@components/item-metric-group'; import { Link } from '@ui/components/anchor'; -import { Checkbox, FormGroup } from '@ui/components/form'; import { DataCentersIcon, HealthyIcon, @@ -25,10 +24,6 @@ import { ListItemInfo } from '@ui/components/list'; -const StyledFormGroup = styled(FormGroup)` - width: auto; -`; - const TitleInnerContainer = styled.div` display: flex; flex-direction: row; @@ -52,7 +47,7 @@ const ServiceListItem = ({ /> )) : null; - const to = `/deploymentGroups/${deploymentGroup}/services/${service.id}`; + const to = `/deployment-groups/${deploymentGroup}/services/${service.slug}`; const title = service.parent ? ( @@ -61,9 +56,6 @@ const ServiceListItem = ({ ) : ( - - - {service.name} diff --git a/frontend/src/containers/instances/list.js b/frontend/src/containers/instances/list.js index ebb51bd4..5996570a 100644 --- a/frontend/src/containers/instances/list.js +++ b/frontend/src/containers/instances/list.js @@ -1,7 +1,13 @@ 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 { LayoutContainer } from '@components/layout'; +import { Loader, ErrorMessage } from '@components/messaging'; +import { InstanceListItem, EmptyInstances } from '@components/instances'; + class InstanceList extends Component { render() { @@ -11,30 +17,59 @@ class InstanceList extends Component { loading, error } = this.props; + console.log('instances = ', instances); + console.log('loading = ', loading); + console.log('error = ', error); + if(loading) { + return ( + + + + ) + } + else if(error) { + return ( + + + + ) + } - const instanceList = - loading ?

Loading...

: - error ?

Error!!!

: - instances.map((instance, index) => -

{instance.name}

); + const instanceList = instances ? instances.map((instance, index) => ( + null} + /> + )) : ( + + ); return ( -
+

Instance List

{ instanceList } -
+ ); } } -const InstanceListWithData = graphql(InstancesQuery, { +const PortalGql = graphql(PortalQuery, {}); + +const InstanceListGql = graphql(InstancesQuery, { options(props) { + const params = props.match.params; + const deploymentGroupSlug = params.deploymentGroup; + const serviceSlug = params.service; return { variables: { - deploymentGroupSlug: props.match.params.deploymentGroup - } + deploymentGroupSlug, + serviceSlug + } }; }, props: ({ data: { deploymentGroup, loading, error } }) => ({ @@ -44,6 +79,11 @@ const InstanceListWithData = graphql(InstancesQuery, { loading, error }) -})(InstanceList) +}); + +const InstanceListWithData = compose( + PortalGql, + InstanceListGql +)(InstanceList); export default InstanceListWithData; diff --git a/frontend/src/containers/navigation/breadcrumb.js b/frontend/src/containers/navigation/breadcrumb.js index 42c497aa..db1a91d1 100644 --- a/frontend/src/containers/navigation/breadcrumb.js +++ b/frontend/src/containers/navigation/breadcrumb.js @@ -1,6 +1,7 @@ import React from 'react'; import { connect } from 'react-redux'; import { Breadcrumb as BreadcrumbComponent } from '@components/navigation'; +import { deploymentGroupBySlugSelector, serviceBySlugSelector} from '@root/state/selectors'; const Breadcrumb = ({ deploymentGroup, @@ -36,32 +37,12 @@ const Breadcrumb = ({ const ConnectedBreadcrumb = connect( (state, ownProps) => { - const params = ownProps.match.params; const deploymentGroupSlug = params.deploymentGroup; 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 { - deploymentGroup, - service, + deploymentGroup: deploymentGroupBySlugSelector(deploymentGroupSlug)(state), + service: serviceBySlugSelector(serviceSlug)(state), location: ownProps.location }; }, diff --git a/frontend/src/containers/services/list.js b/frontend/src/containers/services/list.js index 779987f9..83f7efc4 100644 --- a/frontend/src/containers/services/list.js +++ b/frontend/src/containers/services/list.js @@ -9,6 +9,7 @@ import ServicesQuery from '@graphql/Services.gql'; import { processServices } from '@root/state/selectors'; import { LayoutContainer } from '@components/layout'; +import { Loader, ErrorMessage } from '@components/messaging'; import { ServiceListItem } from '@components/services'; const StyledContainer = styled.div` @@ -27,12 +28,21 @@ class ServiceList extends Component { error } = this.props; - console.log('services = ', services); - - if(loading || error) { + if(loading) { return ( -

Loading OR error

- ); + + + + ) + } + else if(error) { + return ( + + + + ) } const serviceList = services.map((service) => ( diff --git a/frontend/src/graphql/Instances.gql b/frontend/src/graphql/Instances.gql index 23ee6298..923e1520 100644 --- a/frontend/src/graphql/Instances.gql +++ b/frontend/src/graphql/Instances.gql @@ -1,10 +1,10 @@ #import "./DeploymentGroupInfo.gql" #import "./ServiceInfo.gql" -query Instances($deploymentGroupSlug: String!) { +query Instances($deploymentGroupSlug: String!, $serviceSlug: String) { deploymentGroup(slug: $deploymentGroupSlug) { ...DeploymentGroupInfo - services { + services(slug: $serviceSlug) { ...ServiceInfo instances { uuid diff --git a/frontend/src/router.js b/frontend/src/router.js index 0a8139b0..e7aa63a2 100644 --- a/frontend/src/router.js +++ b/frontend/src/router.js @@ -22,6 +22,10 @@ const deploymentGroupRedirect = (p) => ( ); +const serviceRedirect = (p) => ( + +); + const Router = (
@@ -51,6 +55,7 @@ const Router = ( +
diff --git a/frontend/src/state/selectors.js b/frontend/src/state/selectors.js index 231b685e..b8b082b4 100644 --- a/frontend/src/state/selectors.js +++ b/frontend/src/state/selectors.js @@ -6,18 +6,20 @@ const apollo = (state) => state.apollo; const deploymentGroupBySlug = (deploymentGroupSlug) => createSelector( [apollo], - (apollo) => apollo ? Object.keys(apollo).reduce((dg, k) => - apollo[k].__typename === 'DeploymentGroup' && - apollo[k].slug === deploymentGroupSlug ? - apollo[k] : dg, {}) : null + (apollo) => apollo && apollo.data ? + Object.keys(apollo.data).reduce((dg, k) => + apollo.data[k].__typename === 'DeploymentGroup' && + apollo.data[k].slug === deploymentGroupSlug ? + apollo.data[k] : dg, null) : null ); -const servicesBySlug = (serviceSlug) => createSelector( +const serviceBySlug = (serviceSlug) => createSelector( [apollo], - (apollo) => apollo ? Object.keys(apollo).reduce((s, k) => - apollo[k].__typename === 'Service' && - apollo[k].slug === serviceSlug ? - apollo[k] : s, {}) : null + (apollo) => apollo && apollo.data ? + Object.keys(apollo.data).reduce((s, k) => + apollo.data[k].__typename === 'Service' && + apollo.data[k].slug === serviceSlug ? + apollo.data[k] : s, null) : null ); // apollo gql utils // @@ -64,6 +66,6 @@ const processServices = (services, datacenter) => { export { deploymentGroupBySlug as deploymentGroupBySlugSelector, - servicesBySlug as servicesBySlugSelector, + serviceBySlug as serviceBySlugSelector, processServices as processServices } diff --git a/spikes/graphql/graphql-server/src/resolvers.js b/spikes/graphql/graphql-server/src/resolvers.js index e46f818c..5c751c6b 100644 --- a/spikes/graphql/graphql-server/src/resolvers.js +++ b/spikes/graphql/graphql-server/src/resolvers.js @@ -2,6 +2,7 @@ import { find, filter } from 'lodash'; import data from './mock-data'; import { normalMetricData, leakMetricData } from './mock-data/metrics'; +// TMP / Util const datacenter = { uuid: 'datacenter-uuid', region: 'us-east-1' @@ -26,140 +27,148 @@ const getInstanceMetricData = (dataset, type) => { const tick = setInterval(() => index++, 15*1000); -const resolveFunctions = { - Query: { - portal() { - return portal; - }, +// GraphQL - 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) { - 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 queries = { + portal() { + return portal; }, + + 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: { - deploymentGroups(portal) { + deploymentGroups(portal, args, context) { return deploymentGroups; } }, DeploymentGroup: { - services(deploymentGroup) { - return filter(services, { project: deploymentGroup.uuid }); - }, + services(deploymentGroup, args, context) { + const a = Object.assign({}, args, {deploymentGroupSlug: deploymentGroup.slug}) + return queries.services(null, a); + } }, Service: { - instances(service) { + instances(service, args, context) { return filter(instances, { service: service.uuid }); }, /*metrics(service) { diff --git a/spikes/graphql/graphql-server/src/schema.gql b/spikes/graphql/graphql-server/src/schema.gql index 33642c52..73cf8ef0 100644 --- a/spikes/graphql/graphql-server/src/schema.gql +++ b/spikes/graphql/graphql-server/src/schema.gql @@ -12,7 +12,7 @@ uuid: ID! name: String! slug: String! - services: [Service]! + services(slug: String): [Service]! version: Version! history: [Version]! }