From a373e4efa2707ebe8553270b58a17afe3a191819 Mon Sep 17 00:00:00 2001 From: JUDIT GRESKOVITS Date: Mon, 26 Jun 2017 13:25:46 +0100 Subject: [PATCH] feat(cp-frontend, cp-gql-mock-server): add stop, start, delete to resolvers --- .../src/components/services/list-item.js | 9 +- .../src/components/services/quick-actions.js | 25 +++- .../src/containers/service/delete.js | 28 +++- .../src/containers/services/list.js | 20 +-- .../cp-frontend/src/graphql/Instances.gql | 1 + packages/cp-frontend/src/graphql/Services.gql | 1 + .../src/graphql/ServicesDeleteMutation.gql | 4 + .../src/graphql/ServicesStartMutation.gql | 4 + .../src/graphql/ServicesStopMutation.gql | 4 + packages/cp-frontend/src/state/selectors.js | 28 ++-- packages/cp-gql-mock-server/src/data.json | 37 +++-- packages/cp-gql-mock-server/src/resolvers.js | 128 ++++++++++++++++-- 12 files changed, 233 insertions(+), 56 deletions(-) diff --git a/packages/cp-frontend/src/components/services/list-item.js b/packages/cp-frontend/src/components/services/list-item.js index 254f07e3..ca2a91a0 100644 --- a/packages/cp-frontend/src/components/services/list-item.js +++ b/packages/cp-frontend/src/components/services/list-item.js @@ -63,12 +63,18 @@ const ServiceListItem = ({ ; - const subtitle = {service.instances} instances; + const subtitle = ( + {service.instances.length} instances + ); const handleCardOptionsClick = evt => { onQuickActionsClick(evt, service); }; + const statuses = service.instances.map(instance => +

1 instance {instance.status}

+ ); + const header = isChild ? null : @@ -99,6 +105,7 @@ const ServiceListItem = ({ {isChild && subtitle} {/* } label="Healthy" /> */} + {statuses} {/* { - onStopClick(evt, service); - }; - const handleStartClick = evt => { onStartClick(evt, service); }; - // TODO we'll need to check for service status and diplay start or restart & stop accordingly + const handleStopClick = evt => { + onStopClick(evt, service); + }; + + const status = service.instances.reduce((status, instance) => { + return status + ? instance.status === status ? status : 'MIXED' + : instance.status; + }, null); + + const startService = status === 'RUNNING' + ? null + : Start; + + const stopService = status === 'STOPPED' + ? null + : Stop; return ( Scale Restart - Stop + {startService} + {stopService} Delete diff --git a/packages/cp-frontend/src/containers/service/delete.js b/packages/cp-frontend/src/containers/service/delete.js index a2a0c8d3..2e138f0b 100644 --- a/packages/cp-frontend/src/containers/service/delete.js +++ b/packages/cp-frontend/src/containers/service/delete.js @@ -1,4 +1,4 @@ -import React, { PureComponent } from 'react'; +import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { compose, graphql, gql } from 'react-apollo'; import ServicesDeleteMutation from '@graphql/ServicesDeleteMutation.gql'; @@ -7,7 +7,7 @@ import { ServiceDelete as ServiceDeleteComponent } from '@components/service'; import { Modal } from 'joyent-ui-toolkit'; import ServiceGql from './service-gql'; -class ServiceDelete extends PureComponent { +class ServiceDelete extends Component { render() { if (this.props.loading) { return ; @@ -26,7 +26,7 @@ class ServiceDelete extends PureComponent { }; const handleConfirmClick = evt => { - deleteServices(service.id); + deleteServices(service.id).then(() => handleCloseClick()); }; return ( @@ -49,7 +49,27 @@ ServiceDelete.propTypes = { const DeleteServicesGql = graphql(ServicesDeleteMutation, { props: ({ mutate }) => ({ - deleteServices: serviceId => mutate({ variables: { ids: [serviceId] } }) + deleteServices: serviceId => + mutate({ + variables: { ids: [serviceId] }, + updateQueries: { + Services: (prev, { mutationResult }) => { + const deletedService = mutationResult.data.deleteServices[0]; + const prevServices = prev.deploymentGroup.services; + const services = prevServices.filter( + service => + service.id !== deletedService.id && + service.parent !== deletedService.id + ); + return { + deploymentGroup: { + ...prev.deploymentGroup, + services + } + }; + } + } + }) }) }); diff --git a/packages/cp-frontend/src/containers/services/list.js b/packages/cp-frontend/src/containers/services/list.js index 5c59d99a..a5885f5d 100644 --- a/packages/cp-frontend/src/containers/services/list.js +++ b/packages/cp-frontend/src/containers/services/list.js @@ -57,7 +57,7 @@ class ServiceList extends Component { ); } - const handleQuickActionsClick = (evt, { service }) => { + const handleQuickActionsClick = (evt, service) => { const list = this._refs.container; const listRect = list.getBoundingClientRect(); const button = evt.currentTarget; @@ -93,14 +93,16 @@ class ServiceList extends Component { toggleServicesQuickActions({ show: false }); }; - const serviceList = services.map(service => - - ); + const serviceList = services.map(service => { + return ( + + ); + }); return ( diff --git a/packages/cp-frontend/src/graphql/Instances.gql b/packages/cp-frontend/src/graphql/Instances.gql index 26025f11..3def1dd3 100644 --- a/packages/cp-frontend/src/graphql/Instances.gql +++ b/packages/cp-frontend/src/graphql/Instances.gql @@ -9,6 +9,7 @@ query Instances($deploymentGroupSlug: String!, $serviceSlug: String) { instances { id name + status } } } diff --git a/packages/cp-frontend/src/graphql/Services.gql b/packages/cp-frontend/src/graphql/Services.gql index 47cdb866..09ffa6c1 100644 --- a/packages/cp-frontend/src/graphql/Services.gql +++ b/packages/cp-frontend/src/graphql/Services.gql @@ -9,6 +9,7 @@ query Services($deploymentGroupSlug: String!){ parent instances { id + status } } } diff --git a/packages/cp-frontend/src/graphql/ServicesDeleteMutation.gql b/packages/cp-frontend/src/graphql/ServicesDeleteMutation.gql index 0790ea31..b4ef3a26 100644 --- a/packages/cp-frontend/src/graphql/ServicesDeleteMutation.gql +++ b/packages/cp-frontend/src/graphql/ServicesDeleteMutation.gql @@ -2,5 +2,9 @@ mutation DeleteServices($ids: [ID]!) { deleteServices(ids: $ids) { id slug + instances { + id + status + } } } diff --git a/packages/cp-frontend/src/graphql/ServicesStartMutation.gql b/packages/cp-frontend/src/graphql/ServicesStartMutation.gql index a47e541c..e854e34e 100644 --- a/packages/cp-frontend/src/graphql/ServicesStartMutation.gql +++ b/packages/cp-frontend/src/graphql/ServicesStartMutation.gql @@ -2,5 +2,9 @@ mutation StartServices($ids: [ID]!) { startServices(ids: $ids) { id slug + instances { + id + status + } } } diff --git a/packages/cp-frontend/src/graphql/ServicesStopMutation.gql b/packages/cp-frontend/src/graphql/ServicesStopMutation.gql index 1ef7ebe1..f2778ba7 100644 --- a/packages/cp-frontend/src/graphql/ServicesStopMutation.gql +++ b/packages/cp-frontend/src/graphql/ServicesStopMutation.gql @@ -2,5 +2,9 @@ mutation StopServices($ids: [ID]!) { stopServices(ids: $ids) { id slug + instances { + id + status + } } } diff --git a/packages/cp-frontend/src/state/selectors.js b/packages/cp-frontend/src/state/selectors.js index e78d0df1..ca307a99 100644 --- a/packages/cp-frontend/src/state/selectors.js +++ b/packages/cp-frontend/src/state/selectors.js @@ -71,29 +71,35 @@ const getService = (service, index, datacenter) => ({ name: `${m}`, value: `${m}` })), - instances: service.instances.length, + // instances: service.instances.length, datacenter }); const processServices = (services, datacenter) => { - console.log('services = ', services); return forceArray(services).reduce((ss, s, i) => { - // Check whether it exits in thing, if so, add as child - // if not, create and add as child - if (s.parent) { - let parent = findService(ss, s.parent); - if (!parent) { - parent = { uuid: s.parent }; + const parents = ss.filter(parentS => parentS.id === s.parent); + let parent; + if (parents.length) { + parent = parents[0]; + } else { + parent = { id: s.parent }; ss.push(parent); } if (!parent.children) { parent.children = []; } parent.children.push(getService(s, i, datacenter)); - } - if (!s.parent) { - ss.push(getService(s, i, datacenter)); + } else { + const serviceIndex = ss.findIndex(existingS => existingS.id === s.id); + if (serviceIndex == -1) { + ss.push(getService(s, i, datacenter)); + } else { + ss.splice(serviceIndex, 1, { + ...ss[serviceIndex], + ...getService(s, i, datacenter) + }); + } } return ss; }, []); diff --git a/packages/cp-gql-mock-server/src/data.json b/packages/cp-gql-mock-server/src/data.json index f5a71c9c..7849d3fc 100644 --- a/packages/cp-gql-mock-server/src/data.json +++ b/packages/cp-gql-mock-server/src/data.json @@ -112,67 +112,78 @@ "id": "309ecd9f-ac03-474b-aff7-4bd2e743296c", "name": "wordpress_01", "serviceId": "be227788-74f1-4e5b-a85f-b5c71cbae8d8", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "0db6db53-de6f-4378-839e-5d5b452fbaf2", "name": "nfs_01", "serviceId": "6a0eee76-c019-413b-9d5f-44712b55b993", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "250c8a6c-7d02-49a9-8abd-e1c22773041d", "name": "consul", "serviceId": "97c68055-db88-45c9-ad49-f26da4264777", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "2c921f3a-8bc3-4f57-9cd7-789ebae72061", "name": "memcache_01", "serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "68d3046e-8e34-4f5d-a0e5-db3795a250fd", "name": "memcache_02", "serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "2ea99763-3b44-4179-8393-d66d94961051", "name": "memcache_03", "serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "25f6bc62-63b8-4959-908e-1f6d7ff6341d", "name": "memcache_04", "serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "8be01042-0281-4a77-a357-25979e87bf3d", "name": "memcache_05", "serviceId": "6d31aff4-de1e-4042-a983-fbd23d5c530c", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "3d652e9d-73e8-4a6f-8171-84fa83740662", "name": "nginx", "serviceId": "081a792c-47e0-4439-924b-2efa9788ae9e", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "c3ec7633-a02b-4615-86a0-9e6faeaae94b", "name": "percona-primary", - "serviceId": "4ee4103e-1a52-4099-a48e-01588f597c70", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "serviceId": "9572d367-c4ae-4fb1-8ad5-f5e3830e7034", + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" }, { "id": "c2b5fec2-31e2-41a7-b7fc-cd0bb1822e76", "name": "percona-secondary", - "serviceId": "4ee4103e-1a52-4099-a48e-01588f597c70", - "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401" + "serviceId": "c8411ef0-ab39-42cb-a704-d20b170eff31", + "deploymentGroupId": "e0ea0c02-55cc-45fe-8064-3e5176a59401", + "status": "RUNNING" } ] } diff --git a/packages/cp-gql-mock-server/src/resolvers.js b/packages/cp-gql-mock-server/src/resolvers.js index 13764483..72be229e 100644 --- a/packages/cp-gql-mock-server/src/resolvers.js +++ b/packages/cp-gql-mock-server/src/resolvers.js @@ -16,10 +16,11 @@ const find = (query = {}) => item => const cleanQuery = (q = {}) => JSON.parse(JSON.stringify(q)); -const getInstances = query => - Promise.resolve(instances.filter(find(cleanQuery(query)))); +const getInstances = query => { + return Promise.resolve(instances.filter(find(cleanQuery(query)))); +}; -const getServices = query => { +const getUnfilteredServices = query => { const instancesResolver = ({ id }) => query => getInstances( Object.assign({}, query, { @@ -37,6 +38,74 @@ const getServices = query => { ); }; +const getServices = query => { + const services = getUnfilteredServices(query) + .then(services => { + // loop through services and for each of them get all services that's parent id is the service + // once done - this will be a Promise all - need to remove any duplicates from the services list - the original + // then we can do below... + return ( + Promise.all( + services.map(service => getUnfilteredServices({ parent: service.id })) + ) + // this is going to be an array of arrays of services + .then(childServices => { + return childServices.reduce((childServices, childService) => { + return childServices.concat(childService); + }, []); + }) + // now it's at least flat + .then(childServices => { + return services.concat( + childServices.reduce((childServices, childService) => { + const exists = services.filter( + service => service.id === childService.id + ).length; + if (!exists) { + childServices.push(childService); + } + return childServices; + }, []) + ); + }) + .then(services => { + return Promise.all( + services.map(service => service.instances()) + ).then(instances => { + return { services, instances }; + }); + }) + ); + }) + .then(({ services, instances }) => { + const activeServices = services.reduce((services, service, index) => { + const active = instances[index].filter( + instance => + instance.status !== 'DELETED' && instance.status !== 'EXITED' + ).length; + if (active) { + services.push(service); + } + return services; + }, []); + const allServices = activeServices.reduce((allServices, service) => { + if (service.parent) { + const parentService = services.filter(s => s.id === service.parent); + const exists = allServices.filter(s => s.id === service.parent) + .length; + if (!exists && parentService) { + allServices.push(parentService[0]); + } + } + allServices.push(service); + return allServices; + }, []); + return allServices; + }); + + return Promise.resolve(services); +}; + const getDeploymentGroups = query => { const servicesResolver = ({ id }) => query => getServices( @@ -100,11 +169,6 @@ const createServicesFromManifest = ({ deploymentGroupId, raw }) => { return Promise.resolve(undefined); }; -const deleteServices = options => { - const service = getServices({ id: options.ids[0] }); - return service; -}; - const scale = options => { const service = getServices({ id: options.serviceId })[0]; @@ -124,14 +188,54 @@ const restartServices = options => { return service; }; +const updateInstancesStatus = (is, status) => { + is.forEach(i => { + const instance = instances.filter(instance => instance.id === i.id)[0]; + instance.status = status; + }); + + return Promise.resolve(instances); +}; + +const updateServiceStatus = (serviceId, status) => { + return getServices({ id: serviceId }) + .then(services => services.shift().instances()) + .then(instances => updateInstancesStatus(instances, status)) + .then(instances => getServices({ id: serviceId })); +}; + +const deleteServices = options => { + const serviceId = options.ids[0]; + const deleteService = getServices({ id: serviceId }).then(services => { + const service = services.shift(); + return service.instances().then(instances => { + if (instances.length) { + updateInstancesStatus(instances, 'DELETED'); + return [service]; + } + + return getUnfilteredServices({ parent: serviceId }).then(services => { + return Promise.all( + services.map(service => service.instances()) + ).then(instances => { + const is = instances.reduce((is, i) => is.concat(i), []); + updateInstancesStatus(is, 'DELETED'); + return [service]; + }); + }); + }); + }); + return Promise.resolve(deleteService); +}; + const stopServices = options => { - const service = getServices({ id: options.ids[0] }); - return service; + const stopService = updateServiceStatus(options.ids[0], 'STOPPED'); + return Promise.resolve(stopService); }; const startServices = options => { - const service = getServices({ id: options.ids[0] }); - return service; + const startService = updateServiceStatus(options.ids[0], 'RUNNING'); + return Promise.resolve(startService); }; module.exports = {