From 4e241191bf14c596a482e2477efb28102054e8c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Se=CC=81rgio=20Ramos?= Date: Tue, 27 Jun 2017 17:45:38 +0100 Subject: [PATCH] feat(cp-frontend): DeploymentGroup reprovision flow --- packages/cp-frontend/package.json | 1 + .../components/deployment-groups/create.js | 21 +- .../src/components/instances/list-item.js | 15 + .../containers/deployment-groups/create.js | 280 +--------------- .../deployment-groups/edit-or-create.js | 298 ++++++++++++++++++ .../containers/deployment-groups/import.js | 3 +- .../src/containers/deployment-groups/index.js | 1 + .../src/containers/deployment-groups/list.js | 4 +- .../containers/deployment-groups/manifest.js | 67 ++++ .../src/containers/instances/list.js | 4 +- .../src/graphql/DeploymentGroupBySlug.gql | 4 +- .../src/graphql/DeploymentGroupInfo.gql | 1 + .../src/graphql/DeploymentGroupProvision.gql | 1 - packages/cp-frontend/src/graphql/Manifest.gql | 12 + packages/cp-frontend/src/router.js | 60 ++-- yarn.lock | 111 ++++--- 16 files changed, 540 insertions(+), 343 deletions(-) create mode 100644 packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js create mode 100644 packages/cp-frontend/src/containers/deployment-groups/manifest.js create mode 100644 packages/cp-frontend/src/graphql/Manifest.gql diff --git a/packages/cp-frontend/package.json b/packages/cp-frontend/package.json index 2421ced9..c0a493ea 100644 --- a/packages/cp-frontend/package.json +++ b/packages/cp-frontend/package.json @@ -28,6 +28,7 @@ "jest-cli": "^20.0.4", "joyent-manifest-editor": "^1.0.0", "joyent-ui-toolkit": "^1.1.0", + "lodash.get": "^4.4.2", "lodash.isstring": "^4.0.1", "normalized-styled-components": "^1.0.8", "param-case": "^2.1.1", diff --git a/packages/cp-frontend/src/components/deployment-groups/create.js b/packages/cp-frontend/src/components/deployment-groups/create.js index 20e8b7db..2683a900 100644 --- a/packages/cp-frontend/src/components/deployment-groups/create.js +++ b/packages/cp-frontend/src/components/deployment-groups/create.js @@ -45,8 +45,8 @@ const ButtonsRow = Row.extend` margin-bottom: ${remcalc(60)}; `; -const Editor = ManifestEditor => ({ input }) => - ; +const Editor = ManifestEditor => ({ input, defaultValue }) => + ; export const Name = ({ handleSubmit, onCancel, dirty }) =>
@@ -64,12 +64,23 @@ export const Name = ({ handleSubmit, onCancel, dirty }) =>
; -export const Manifest = ({ handleSubmit, onCancel, dirty, mode, loading }) => +export const Manifest = ({ + handleSubmit, + onCancel, + dirty, + defaultValue, + mode, + loading +}) =>
import('joyent-manifest-editor')}> {ManifestEditor => ManifestEditor - ? + ? : } @@ -82,7 +93,7 @@ export const Manifest = ({ handleSubmit, onCancel, dirty, mode, loading }) => export const Review = ({ handleSubmit, onCancel, dirty, ...state }) => { const serviceList = forceArray(state.services).map(({ name, image }) => - +
{name}
Image: {image}
diff --git a/packages/cp-frontend/src/components/instances/list-item.js b/packages/cp-frontend/src/components/instances/list-item.js index 3068575a..b0343abd 100644 --- a/packages/cp-frontend/src/components/instances/list-item.js +++ b/packages/cp-frontend/src/components/instances/list-item.js @@ -18,11 +18,14 @@ import { const STATUSES = [ 'CREATED', 'RESTARTING', + 'PROVISIONING', + 'READY', 'RUNNING', 'PAUSED', 'EXITED', 'DELETED', 'STOPPED', + 'STOPPING', 'FAILED' ]; @@ -46,6 +49,14 @@ const Dot = styled.div` background-color: yellow; `}; + ${is('provisioning')` + background-color: yellow; + `}; + + ${is('ready')` + background-color: yellow; + `}; + ${is('running')` background-color: ${props => props.theme.green}; `}; @@ -62,6 +73,10 @@ const Dot = styled.div` background-color: ${props => props.theme.secondaryActive}; `}; + ${is('stopping')` + background-color: orange; + `}; + ${is('stopped')` background-color: ${props => props.theme.red}; `}; diff --git a/packages/cp-frontend/src/containers/deployment-groups/create.js b/packages/cp-frontend/src/containers/deployment-groups/create.js index 8cb38d1b..ae618139 100644 --- a/packages/cp-frontend/src/containers/deployment-groups/create.js +++ b/packages/cp-frontend/src/containers/deployment-groups/create.js @@ -1,270 +1,18 @@ -import React, { Component } from 'react'; -import { reduxForm } from 'redux-form'; -import { compose, graphql } from 'react-apollo'; -import { Redirect } from 'react-router-dom'; -import intercept from 'apr-intercept'; -import paramCase from 'param-case'; +import React from 'react'; -import DeploymentGroupBySlug from '@graphql/DeploymentGroupBySlug.gql'; -import DeploymentGroupCreateMutation from '@graphql/DeploymentGroupCreate.gql'; -import DeploymentGroupProvisionMutation from '@graphql/DeploymentGroupProvision.gql'; -import DeploymentGroupConfigQuery from '@graphql/DeploymentGroupConfig.gql'; - -import { client } from '@state/store'; +import DeploymentGroupEditOrCreate from './edit-or-create'; import { LayoutContainer } from '@components/layout'; -import { Name, Manifest, Review } from '@components/deployment-groups/create'; +import { DeploymentGroupsLoading } from '@components/deployment-groups'; import { H2 } from 'joyent-ui-toolkit'; -const validateName = async ({ name = '' }) => { - const { data } = await client.query({ - fetchPolicy: 'network-only', - query: DeploymentGroupBySlug, - variables: { - slug: paramCase(name.trim()) - } - }); - - if (data.deploymentGroups.length) { - // eslint-disable-next-line no-throw-literal - throw { name: `"${name}" already exists!` }; - } -}; - -const NameForm = reduxForm({ - form: 'create-deployment-group', - destroyOnUnmount: true, - forceUnregisterOnUnmount: true, - asyncValidate: validateName -})(Name); - -const ManifestForm = reduxForm({ - form: 'create-deployment-group', - destroyOnUnmount: true, - forceUnregisterOnUnmount: true -})(Manifest); - -const ReviewForm = reduxForm({ - form: 'create-deployment-group', - destroyOnUnmount: true, - forceUnregisterOnUnmount: true -})(Review); - -// TODO: move state to redux. why: because in redux we can cache transactional -// state between refreshes -class DeploymentGroupCreate extends Component { - state = { - name: '', - manifest: '', - services: [], - loading: false, - error: null - }; - - constructor() { - super(); - - this.stages = { - name: this.renderNameForm.bind(this), - manifest: this.renderManifestEditor.bind(this), - review: this.renderReview.bind(this) - }; - - this.handleNameSubmit = this.handleNameSubmit.bind(this); - this.handleManifestSubmit = this.handleManifestSubmit.bind(this); - this.handleReviewSubmit = this.handleReviewSubmit.bind(this); - this.handleCancel = this.handleCancel.bind(this); - } - - handleNameSubmit({ name = '' }) { - this.setState({ name }, () => - this.redirect({ stage: 'manifest', prog: true }) - ); - } - - handleManifestSubmit({ manifest = '' }) { - const { config } = this.props; - const { name } = this.state; - - const getConfig = async () => { - const [err, conf] = await intercept( - config({ - deploymentGroupName: name, - type: 'COMPOSE', - format: 'YAML', - raw: manifest - }) - ); - - if (err) { - return this.setState({ - error: err.message - }); - } - - const { data } = conf; - const { config: services } = data; - - this.setState({ loading: false, services }, () => { - this.redirect({ stage: 'review', prog: true }); - }); - }; - - this.setState({ manifest, loading: true }, getConfig); - } - - handleReviewSubmit() { - const { history } = this.props; - - const submit = async () => { - const { id, slug } = await this.createDeploymentGroup(); - - if (!id) { - return; - } - - const manifest = await this.provision(id); - - if (!manifest) { - return; - } - - history.push(`/deployment-groups/${slug}`); - }; - - this.setState({ loading: true }, submit); - } - - createDeploymentGroup = async () => { - const { name } = this.state; - const { createDeploymentGroup } = this.props; - - const [err, res] = await intercept(createDeploymentGroup({ name })); - - if (err) { - this.setState({ - error: err.message - }); - } - - return err ? null : res.data.createDeploymentGroup; - }; - - provision = async deploymentGroupId => { - const { manifest } = this.state; - const { provisionManifest } = this.props; - - const [err] = await intercept( - provisionManifest({ - deploymentGroupId, - type: 'COMPOSE', - format: 'YAML', - raw: manifest - }) - ); - - if (err) { - this.setState({ - error: err.message - }); - } - - return err ? null : true; - }; - - renderNameForm() { - return ( - - ); - } - - renderManifestEditor() { - return ( - - ); - } - - renderReview() { - return ( - - ); - } - - handleCancel() { - const { history } = this.props; - - history.push('/'); - } - - redirect({ stage = 'name', prog = false }) { - const { match, history } = this.props; - - const pathname = match.url.replace(/~create(.*)/, '~create'); - const to = `${pathname}/${stage}`; - - if (!prog) { - return ; - } - - history.push(to); - } - - render() { - const { err } = this.state; - const { match } = this.props; - const stage = match.params.stage; - - if (!stage) { - return this.redirect({ stage: 'name' }); - } - - if (!this.stages[stage]) { - return this.redirect({ stage: 'name' }); - } - - if (stage !== 'name' && !this.state.name) { - return this.redirect({ stage: 'name' }); - } - - if (stage === 'review' && !this.state.manifest) { - return this.redirect({ stage: 'manifest' }); - } - - const view = this.stages[stage](); - - const error = err ? {err} : null; - - return ( - -

Creating deployment group

- {error} - {view} -
- ); - } -} - -export default compose( - graphql(DeploymentGroupCreateMutation, { - props: ({ mutate }) => ({ - createDeploymentGroup: variables => mutate({ variables }) - }) - }), - graphql(DeploymentGroupProvisionMutation, { - props: ({ mutate }) => ({ - provisionManifest: variables => mutate({ variables }) - }) - }), - graphql(DeploymentGroupConfigQuery, { - props: ({ mutate }) => ({ - config: variables => mutate({ variables }) - }) - }) -)(DeploymentGroupCreate); +export default ({ loading, error, manifest = '', deploymentGroup = null }) => + +

Creating deployment group

+ {loading && } + {error && {error.toString()}} + +
; diff --git a/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js b/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js new file mode 100644 index 00000000..bf80d5b5 --- /dev/null +++ b/packages/cp-frontend/src/containers/deployment-groups/edit-or-create.js @@ -0,0 +1,298 @@ +import React, { Component } from 'react'; +import { reduxForm } from 'redux-form'; +import { compose, graphql } from 'react-apollo'; +import { withRouter } from 'react-router'; +import { Redirect } from 'react-router-dom'; +import intercept from 'apr-intercept'; +import paramCase from 'param-case'; + +import DeploymentGroupBySlugQuery from '@graphql/DeploymentGroupBySlug.gql'; +import DeploymentGroupCreateMutation from '@graphql/DeploymentGroupCreate.gql'; +import DeploymentGroupProvisionMutation from '@graphql/DeploymentGroupProvision.gql'; +import DeploymentGroupConfigQuery from '@graphql/DeploymentGroupConfig.gql'; + +import { client } from '@state/store'; +import { Name, Manifest, Review } from '@components/deployment-groups/create'; + +// TODO: move state to redux. why: because in redux we can cache transactional +// state between refreshes +class DeploymentGroupEditOrCreate extends Component { + constructor(props) { + super(props); + + const { create, edit } = props; + const type = create ? 'create' : 'edit'; + + const NameForm = + create && + reduxForm({ + form: `${type}-deployment-group`, + destroyOnUnmount: true, + forceUnregisterOnUnmount: true, + asyncValidate: async ({ name = '' }) => { + const { data } = await client.query({ + fetchPolicy: 'network-only', + query: DeploymentGroupBySlugQuery, + variables: { + slug: paramCase(name.trim()) + } + }); + + if (data.deploymentGroups.length) { + // eslint-disable-next-line no-throw-literal + throw { name: `"${name}" already exists!` }; + } + } + })(Name); + + const ManifestForm = reduxForm({ + form: `${type}-deployment-group`, + destroyOnUnmount: true, + forceUnregisterOnUnmount: true + })(Manifest); + + const ReviewForm = reduxForm({ + form: `${type}-deployment-group`, + destroyOnUnmount: true, + forceUnregisterOnUnmount: true + })(Review); + + this.state = { + defaultStage: create ? 'name' : 'edit', + manifestStage: create ? 'manifest' : 'edit', + name: '', + manifest: '', + services: [], + loading: false, + error: null, + NameForm, + ManifestForm, + ReviewForm + }; + + this.stages = { + name: create && this.renderNameForm.bind(this), + [create ? 'manifest' : 'edit']: this.renderManifestEditor.bind(this), + review: this.renderReview.bind(this) + }; + + this.handleNameSubmit = + type === 'create' && this.handleNameSubmit.bind(this); + + this.handleManifestSubmit = this.handleManifestSubmit.bind(this); + this.handleReviewSubmit = this.handleReviewSubmit.bind(this); + this.handleCancel = this.handleCancel.bind(this); + + if (edit) { + setTimeout(this.getDeploymentGroup, 16); + } + } + + createDeploymentGroup = async () => { + const { createDeploymentGroup, deploymentGroup, edit } = this.props; + + if (edit && (!deploymentGroup || !deploymentGroup.id)) { + this.setState({ + error: 'Unexpected Error: Inexistent DeploymentGroup!' + }); + + return {}; + } + + if (deploymentGroup && deploymentGroup.id) { + return deploymentGroup; + } + + const { name } = this.state; + + const [err, res] = await intercept(createDeploymentGroup({ name })); + + if (err) { + this.setState({ + error: err.message + }); + } + + return err ? {} : res.data.createDeploymentGroup; + }; + + provision = async deploymentGroupId => { + const { manifest } = this.state; + const { provisionManifest } = this.props; + + const [err] = await intercept( + provisionManifest({ + deploymentGroupId, + type: 'COMPOSE', + format: 'YAML', + raw: manifest + }) + ); + + if (err) { + this.setState({ + error: err.message + }); + } + + return err ? null : true; + }; + + handleNameSubmit({ name = '' }) { + this.setState({ name }, () => + this.redirect({ stage: 'manifest', prog: true }) + ); + } + + handleManifestSubmit({ manifest = '' }) { + const { name } = this.state; + + const getConfig = async () => { + const [err, conf] = await intercept( + client.query({ + query: DeploymentGroupConfigQuery, + variables: { + deploymentGroupName: name, + type: 'COMPOSE', + format: 'YAML', + raw: manifest + } + }) + ); + + if (err) { + return this.setState({ + error: err.message + }); + } + + const { data } = conf; + const { config: services } = data; + + this.setState({ loading: false, services }, () => { + this.redirect({ stage: 'review', prog: true }); + }); + }; + + this.setState({ manifest, loading: true }, getConfig); + } + + handleReviewSubmit() { + const { history } = this.props; + + const submit = async () => { + const { id, slug } = await this.createDeploymentGroup(); + + if (!id) { + return; + } + + const manifest = await this.provision(id); + + if (!manifest) { + return; + } + + history.push(`/deployment-groups/${slug}`); + }; + + this.setState({ loading: true }, submit); + } + + handleCancel() { + const { history, create, deploymentGroup } = this.props; + + history.push(create ? '/' : `/deployment-groups/${deploymentGroup.slug}`); + } + + redirect({ stage = 'name', prog = false }) { + const { match, history, create } = this.props; + + const regex = create ? /\/~create(.*)/ : /\/manifest(.*)/; + const to = match.url.replace( + regex, + create ? `/~create/${stage}` : `/manifest/${stage}` + ); + + if (!prog) { + return ; + } + + history.push(to); + } + + renderNameForm() { + const { NameForm } = this.state; + + return ( + + ); + } + + renderManifestEditor() { + const { ManifestForm } = this.state; + + return ( + + ); + } + + renderReview() { + const { ReviewForm } = this.state; + + return ( + + ); + } + + render() { + const { error, defaultStage, manifestStage } = this.state; + + if (error) { + return {error}; + } + + const { match, create } = this.props; + const stage = match.params.stage; + + if (!stage) { + return this.redirect({ stage: defaultStage }); + } + + if (!this.stages[stage]) { + return this.redirect({ stage: defaultStage }); + } + + if (create && stage !== 'name' && !this.state.name) { + return this.redirect({ stage: defaultStage }); + } + + if (stage === 'review' && !this.state.manifest) { + return this.redirect({ stage: manifestStage }); + } + + return this.stages[stage](); + } +} + +export default compose( + graphql(DeploymentGroupCreateMutation, { + props: ({ mutate }) => ({ + createDeploymentGroup: variables => mutate({ variables }) + }) + }), + graphql(DeploymentGroupProvisionMutation, { + props: ({ mutate }) => ({ + provisionManifest: variables => mutate({ variables }) + }) + }) +)(withRouter(DeploymentGroupEditOrCreate)); diff --git a/packages/cp-frontend/src/containers/deployment-groups/import.js b/packages/cp-frontend/src/containers/deployment-groups/import.js index c36ec1a6..f81d4ad2 100644 --- a/packages/cp-frontend/src/containers/deployment-groups/import.js +++ b/packages/cp-frontend/src/containers/deployment-groups/import.js @@ -1,12 +1,11 @@ import React, { Component } from 'react'; -import { compose, graphql } from 'react-apollo'; +import { graphql } from 'react-apollo'; import intercept from 'apr-intercept'; import DeploymentGroupImportMutation from '@graphql/DeploymentGroupImport.gql'; import { LayoutContainer } from '@components/layout'; import { DeploymentGroupsLoading } from '@components/deployment-groups'; -import { Dots2 } from 'styled-text-spinners'; import { H2 } from 'joyent-ui-toolkit'; class DeploymentGroupImport extends Component { diff --git a/packages/cp-frontend/src/containers/deployment-groups/index.js b/packages/cp-frontend/src/containers/deployment-groups/index.js index 6f8944a3..853b018f 100644 --- a/packages/cp-frontend/src/containers/deployment-groups/index.js +++ b/packages/cp-frontend/src/containers/deployment-groups/index.js @@ -1,3 +1,4 @@ export { default as DeploymentGroupList } from './list'; export { default as DeploymentGroupCreate } from './create'; export { default as DeploymentGroupImport } from './import'; +export { default as DeploymentGroupManifest } from './manifest'; diff --git a/packages/cp-frontend/src/containers/deployment-groups/list.js b/packages/cp-frontend/src/containers/deployment-groups/list.js index 204ccedc..ffba064b 100644 --- a/packages/cp-frontend/src/containers/deployment-groups/list.js +++ b/packages/cp-frontend/src/containers/deployment-groups/list.js @@ -8,11 +8,11 @@ import forceArray from 'force-array'; import remcalc from 'remcalc'; import { LayoutContainer } from '@components/layout'; -import { Loader, ErrorMessage } from '@components/messaging'; +import { ErrorMessage } from '@components/messaging'; import { DeploymentGroupsLoading } from '@components/deployment-groups'; import DeploymentGroupsQuery from '@graphql/DeploymentGroups.gql'; import DeploymentGroupsImportableQuery from '@graphql/DeploymentGroupsImportable.gql'; -import { Button, H2, H3, Small } from 'joyent-ui-toolkit'; +import { H2, H3, Small } from 'joyent-ui-toolkit'; const Title = H2.extend` margin-top: ${remcalc(2)}; diff --git a/packages/cp-frontend/src/containers/deployment-groups/manifest.js b/packages/cp-frontend/src/containers/deployment-groups/manifest.js new file mode 100644 index 00000000..c4edeb91 --- /dev/null +++ b/packages/cp-frontend/src/containers/deployment-groups/manifest.js @@ -0,0 +1,67 @@ +import React from 'react'; +import { compose, graphql } from 'react-apollo'; +import get from 'lodash.get'; + +import ManifestQuery from '@graphql/Manifest.gql'; +import DeploymentGroupBySlugQuery from '@graphql/DeploymentGroupBySlug.gql'; + +import DeploymentGroupEditOrCreate from './edit-or-create'; +import { LayoutContainer } from '@components/layout'; +import { DeploymentGroupsLoading } from '@components/deployment-groups'; +import { H2 } from 'joyent-ui-toolkit'; + +const Manifest = ({ + loading, + error, + manifest = '', + deploymentGroup = null +}) => { + const _loading = !loading ? null : ; + const _error = !error ? null : {error.toString()}; + + const _view = (loading || !deploymentGroup) + ? null + : ; + + return ( + +

Edit Manifest

+ {_error} + {_loading} + {_view} +
+ ); +}; + +export default compose( + graphql(ManifestQuery, { + options: props => ({ + variables: { + deploymentGroupSlug: props.match.params.deploymentGroup + } + }), + props: ({ data: { deploymentGroup, loading, error } }) => ({ + manifest: get(deploymentGroup, 'version.manifest.raw', ''), + loading, + error + }) + }), + graphql(DeploymentGroupBySlugQuery, { + options: props => ({ + variables: { + slug: props.match.params.deploymentGroup + } + }), + props: ({ data: { deploymentGroups, loading, error } }) => ({ + deploymentGroup: deploymentGroups && deploymentGroups.length + ? deploymentGroups[0] + : null, + loading, + error + }) + }) +)(Manifest); diff --git a/packages/cp-frontend/src/containers/instances/list.js b/packages/cp-frontend/src/containers/instances/list.js index c498bcf4..36308e43 100644 --- a/packages/cp-frontend/src/containers/instances/list.js +++ b/packages/cp-frontend/src/containers/instances/list.js @@ -6,7 +6,7 @@ import { Row } from 'react-styled-flexboxgrid'; import remcalc from 'remcalc'; import { LayoutContainer } from '@components/layout'; -import { Loader, ErrorMessage } from '@components/messaging'; +import { ErrorMessage } from '@components/messaging'; import { InstanceListItem, EmptyInstances } from '@components/instances'; import { DeploymentGroupsLoading } from '@components/deployment-groups'; import { H2 } from 'joyent-ui-toolkit'; @@ -40,6 +40,8 @@ class InstanceList extends Component { return ( Instances + {_error} + {_loading} {instanceList} ); diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupBySlug.gql b/packages/cp-frontend/src/graphql/DeploymentGroupBySlug.gql index 5a427b30..3692c2c6 100644 --- a/packages/cp-frontend/src/graphql/DeploymentGroupBySlug.gql +++ b/packages/cp-frontend/src/graphql/DeploymentGroupBySlug.gql @@ -1,5 +1,7 @@ +#import "./DeploymentGroupInfo.gql" + query DeploymentGroupBySlug($slug: String!) { deploymentGroups(slug: $slug) { - id + ...DeploymentGroupInfo } } diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql b/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql index b87e26bc..fe39b2f4 100644 --- a/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql +++ b/packages/cp-frontend/src/graphql/DeploymentGroupInfo.gql @@ -2,4 +2,5 @@ fragment DeploymentGroupInfo on DeploymentGroup { id name slug + imported } diff --git a/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql b/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql index 49bcc09d..2f1357e5 100644 --- a/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql +++ b/packages/cp-frontend/src/graphql/DeploymentGroupProvision.gql @@ -1,6 +1,5 @@ mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $format: ManifestFormat!, $raw: String!) { provisionManifest(deploymentGroupId: $deploymentGroupId, type: $type, format: $format, raw: $raw) { - manifestId scale { serviceName replicas diff --git a/packages/cp-frontend/src/graphql/Manifest.gql b/packages/cp-frontend/src/graphql/Manifest.gql new file mode 100644 index 00000000..25accbd7 --- /dev/null +++ b/packages/cp-frontend/src/graphql/Manifest.gql @@ -0,0 +1,12 @@ +query ManifestById($deploymentGroupSlug: String!) { + deploymentGroup(slug: $deploymentGroupSlug) { + version { + manifest { + id + type + format + raw + } + } + } +} diff --git a/packages/cp-frontend/src/router.js b/packages/cp-frontend/src/router.js index 57721826..41458742 100644 --- a/packages/cp-frontend/src/router.js +++ b/packages/cp-frontend/src/router.js @@ -9,7 +9,8 @@ import { InstanceList } from '@containers/instances'; import { DeploymentGroupList, DeploymentGroupCreate, - DeploymentGroupImport + DeploymentGroupImport, + DeploymentGroupManifest } from '@containers/deployment-groups'; import { @@ -66,6 +67,11 @@ const Router = ( + + + + + + + + + + + + - - - - - + - diff --git a/yarn.lock b/yarn.lock index ced82dfb..6179f187 100644 --- a/yarn.lock +++ b/yarn.lock @@ -186,6 +186,10 @@ ansi-regex@^2.0.0, ansi-regex@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" +ansi-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998" + ansi-styles@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-1.1.0.tgz#eaecbf66cd706882760b2f4691582b8f55d7a7de" @@ -216,8 +220,8 @@ anymatch@^1.3.0: micromatch "^2.1.5" apollo-client@^1.4.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.5.0.tgz#c0ce752f2fe1945dca2eaaa62c7907fdde384563" + version "1.6.0" + resolved "https://registry.yarnpkg.com/apollo-client/-/apollo-client-1.6.0.tgz#d2b831fd8e6e6c045ce91dc8b86a920d44fd77a9" dependencies: graphql "^0.10.0" graphql-anywhere "^3.0.1" @@ -493,6 +497,10 @@ ast-types@0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.0.tgz#c8721c8747ae4d5b29b929e99c5317b4e8745623" +ast-types@0.9.6: + version "0.9.6" + resolved "https://registry.yarnpkg.com/ast-types/-/ast-types-0.9.6.tgz#102c9e9e9005d3e7e3829bf0c4fa24ee862ee9b9" + async-each@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" @@ -1757,12 +1765,12 @@ camelcase@^4.0.0, camelcase@^4.1.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" caniuse-db@^1.0.30000187, caniuse-db@^1.0.30000634, caniuse-db@^1.0.30000639: - version "1.0.30000694" - resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000694.tgz#02009f4f82d2f0126e4c691b7cd5adb351935c01" + version "1.0.30000696" + resolved "https://registry.yarnpkg.com/caniuse-db/-/caniuse-db-1.0.30000696.tgz#e71f5c61e1f96c7a3af4e791ac5db55e11737604" caniuse-lite@^1.0.30000684: - version "1.0.30000694" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000694.tgz#1492dab7c10c608c9d37a723e6e3e7873e0ce94f" + version "1.0.30000696" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000696.tgz#30f2695d2a01a0dfd779a26ab83f4d134b3da5cc" capture-stack-trace@^1.0.0: version "1.0.0" @@ -3358,8 +3366,8 @@ eslint-plugin-import@^2.3.0: read-pkg-up "^2.0.0" eslint-plugin-jsx-a11y@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.0.3.tgz#4a939f76ec125010528823331bf948cc573380b6" + version "5.1.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-5.1.0.tgz#4a829634344e7a90391a9fb0fbd19810737d79c5" dependencies: aria-query "^0.5.0" array-includes "^3.0.3" @@ -3489,7 +3497,7 @@ esprima@^2.6.0, esprima@~2.7.1: version "2.7.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" -esprima@^3.1.1: +esprima@^3.1.1, esprima@~3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -4374,8 +4382,8 @@ hash-base@^2.0.0: inherits "^2.0.1" hash.js@^1.0.0, hash.js@^1.0.3: - version "1.1.1" - resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.1.tgz#5cb2e796499224e69fd0b00ed01d2d4a16e7a323" + version "1.1.2" + resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.2.tgz#bf5c887825cfe40b9efde7bf11bd2db26e6bf01b" dependencies: inherits "^2.0.3" minimalistic-assert "^1.0.0" @@ -4439,12 +4447,12 @@ home-or-tmp@^2.0.0: os-tmpdir "^1.0.1" hosted-git-info@^2.1.4: - version "2.4.2" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.4.2.tgz#0076b9f46a270506ddbaaea56496897460612a67" + version "2.5.0" + resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" -html-tags@^1.1.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-1.2.0.tgz#c78de65b5663aa597989dd2b7ab49200d7e4db98" +html-tags@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-2.0.0.tgz#10b30a386085f43cede353cc8fa7cb0deeea668b" http-errors@^1.3.0, http-errors@~1.6.1: version "1.6.1" @@ -4999,7 +5007,7 @@ istanbul-lib-hook@^1.0.7: dependencies: append-transform "^0.4.0" -istanbul-lib-instrument@^1.7.2: +istanbul-lib-instrument@^1.7.2, istanbul-lib-instrument@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-1.7.3.tgz#925b239163eabdd68cc4048f52c2fa4f899ecfa7" dependencies: @@ -5712,7 +5720,7 @@ lodash.flattendeep@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" -lodash.get@^4.1.2: +lodash.get@^4.1.2, lodash.get@^4.4.2: version "4.4.2" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" @@ -6344,8 +6352,8 @@ nopt@~1.0.10: abbrev "1" normalize-package-data@^2.0.0, normalize-package-data@^2.3.0, normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.3.5: - version "2.3.8" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.3.8.tgz#d819eda2a9dedbd1ffa563ea4071d936782295bb" + version "2.4.0" + resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.4.0.tgz#12f95a307d58352075a04907b84ac8be98ac012f" dependencies: hosted-git-info "^2.1.4" is-builtin-module "^1.0.0" @@ -6387,8 +6395,8 @@ npm-run-path@^2.0.0: path-key "^2.0.0" npmlog@^4.0.2, npmlog@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.0.tgz#dc59bee85f64f00ed424efb2af0783df25d1c0b5" + version "4.1.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.1.2.tgz#08a7f2a8bf734604779a9efa4ad5cc717abb954b" dependencies: are-we-there-yet "~1.1.2" console-control-strings "~1.1.0" @@ -6411,8 +6419,8 @@ number-is-nan@^1.0.0: resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" nyc@^11.0.2: - version "11.0.2" - resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.0.2.tgz#9e592a697186028253b668516c38f079c39c08f3" + version "11.0.3" + resolved "https://registry.yarnpkg.com/nyc/-/nyc-11.0.3.tgz#0c28bc669a851621709bf7a08503034bee3812b6" dependencies: archy "^1.0.0" arrify "^1.0.1" @@ -6426,7 +6434,7 @@ nyc@^11.0.2: glob "^7.0.6" istanbul-lib-coverage "^1.1.1" istanbul-lib-hook "^1.0.7" - istanbul-lib-instrument "^1.7.2" + istanbul-lib-instrument "^1.7.3" istanbul-lib-report "^1.1.1" istanbul-lib-source-maps "^1.2.1" istanbul-reports "^1.1.1" @@ -6437,7 +6445,7 @@ nyc@^11.0.2: resolve-from "^2.0.0" rimraf "^2.5.4" signal-exit "^3.0.1" - spawn-wrap "^1.3.6" + spawn-wrap "^1.3.7" test-exclude "^4.1.1" yargs "^8.0.1" yargs-parser "^5.0.0" @@ -7399,8 +7407,8 @@ read-installed@~4.0.3: graceful-fs "^4.1.2" read-package-json@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.5.tgz#f93a64e641529df68a08c64de46389e8a3f88845" + version "2.0.9" + resolved "https://registry.yarnpkg.com/read-package-json/-/read-package-json-2.0.9.tgz#fd3c21750d760a96c02abda60081e62133213a26" dependencies: glob "^7.1.1" json-parse-helpfulerror "^1.0.2" @@ -7517,7 +7525,7 @@ readline2@^1.0.1: is-fullwidth-code-point "^1.0.0" mute-stream "0.0.5" -recast@0.11.12, recast@^0.11.5: +recast@0.11.12: version "0.11.12" resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.12.tgz#a79e4d3f82d5d72a82ee177aeaa791e793bbe5d6" dependencies: @@ -7526,6 +7534,15 @@ recast@0.11.12, recast@^0.11.5: private "~0.1.5" source-map "~0.5.0" +recast@^0.11.5: + version "0.11.23" + resolved "https://registry.yarnpkg.com/recast/-/recast-0.11.23.tgz#451fd3004ab1e4df9b4e4b66376b2a21912462d3" + dependencies: + ast-types "0.9.6" + esprima "~3.1.0" + private "~0.1.5" + source-map "~0.5.0" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -8257,6 +8274,12 @@ sort-keys@^1.1.1, sort-keys@^1.1.2: dependencies: is-plain-obj "^1.0.0" +sort-keys@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-2.0.0.tgz#658535584861ec97d730d6cf41822e1f56684128" + dependencies: + is-plain-obj "^1.0.0" + source-list-map@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/source-list-map/-/source-list-map-1.1.2.tgz#9889019d1024cce55cdc069498337ef6186a11a1" @@ -8304,7 +8327,7 @@ spache@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/spache/-/spache-1.1.0.tgz#8c68ba807630f0199429c2035c82ed96f5438cd5" -spawn-wrap@^1.3.6: +spawn-wrap@^1.3.7: version "1.3.7" resolved "https://registry.yarnpkg.com/spawn-wrap/-/spawn-wrap-1.3.7.tgz#beb8bf4426d64b2b06871e0d7dee2643f1f8d1bc" dependencies: @@ -8492,11 +8515,11 @@ string-width@^1.0.0, string-width@^1.0.1, string-width@^1.0.2: strip-ansi "^3.0.0" string-width@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.0.0.tgz#635c5436cc72a6e0c387ceca278d4e2eec52687e" + version "2.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.0.tgz#030664561fc146c9423ec7d978fe2457437fe6d0" dependencies: is-fullwidth-code-point "^2.0.0" - strip-ansi "^3.0.0" + strip-ansi "^4.0.0" string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" @@ -8533,6 +8556,12 @@ strip-ansi@^0.3.0: dependencies: ansi-regex "^0.2.1" +strip-ansi@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f" + dependencies: + ansi-regex "^3.0.0" + strip-ansi@~0.1.0: version "0.1.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.1.1.tgz#39e8a98d044d150660abe4a6808acf70bb7bc991" @@ -8662,8 +8691,8 @@ stylelint-selector-no-utility@^1.5.0: stylelint "^7.0.0" stylelint@^7.0.0, stylelint@^7.0.3, stylelint@^7.11.1: - version "7.11.1" - resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-7.11.1.tgz#c816c658baf7d9e5d167d82273fead37c97ae49d" + version "7.12.0" + resolved "https://registry.yarnpkg.com/stylelint/-/stylelint-7.12.0.tgz#bf302c265d7c2d6fe79b154a9fd873a80f8b4aa4" dependencies: autoprefixer "^6.0.0" balanced-match "^0.4.0" @@ -8677,7 +8706,7 @@ stylelint@^7.0.0, stylelint@^7.0.3, stylelint@^7.11.1: get-stdin "^5.0.0" globby "^6.0.0" globjoin "^0.1.4" - html-tags "^1.1.1" + html-tags "^2.0.0" ignore "^3.2.0" imurmurhash "^0.1.4" known-css-properties "^0.2.0" @@ -9677,7 +9706,7 @@ write-file-stdout@0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/write-file-stdout/-/write-file-stdout-0.0.2.tgz#c252d7c7c5b1b402897630e3453c7bfe690d9ca1" -write-json-file@^2.0.0, write-json-file@^2.1.0: +write-json-file@^2.0.0, write-json-file@^2.1.0, write-json-file@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/write-json-file/-/write-json-file-2.2.0.tgz#51862506bbb3b619eefab7859f1fd6c6d0530876" dependencies: @@ -9696,11 +9725,11 @@ write-pkg@^2.0.0: write-json-file "^2.0.0" write-pkg@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-3.0.1.tgz#f95245805be6f6a4eb1d6c31c43b57226815e6e3" + version "3.1.0" + resolved "https://registry.yarnpkg.com/write-pkg/-/write-pkg-3.1.0.tgz#030a9994cc9993d25b4e75a9f1a1923607291ce9" dependencies: - sort-keys "^1.1.2" - write-json-file "^2.0.0" + sort-keys "^2.0.0" + write-json-file "^2.2.0" write@^0.2.1: version "0.2.1"