mirror of
https://github.com/yldio/copilot.git
synced 2024-11-28 06:00:06 +02:00
feat(cp-frontend): add and edit env_file's
This commit is contained in:
parent
0f74cb5453
commit
f68c2ae78a
@ -30,6 +30,7 @@
|
||||
"joyent-ui-toolkit": "^1.1.0",
|
||||
"lodash.get": "^4.4.2",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.remove": "^4.7.0",
|
||||
"normalized-styled-components": "^1.0.8",
|
||||
"param-case": "^2.1.1",
|
||||
"prop-types": "^15.5.10",
|
||||
@ -46,7 +47,7 @@
|
||||
"redux": "^3.6.0",
|
||||
"redux-actions": "^2.0.3",
|
||||
"redux-batched-actions": "^0.2.0",
|
||||
"redux-form": "^6.8.0",
|
||||
"redux-form": "^7.0.0",
|
||||
"remcalc": "^1.0.8",
|
||||
"reselect": "^3.0.1",
|
||||
"simple-statistics": "^4.1.0",
|
||||
@ -54,7 +55,8 @@
|
||||
"styled-is": "^1.0.11",
|
||||
"styled-text-spinners": "^1.0.1",
|
||||
"title-case": "^2.1.1",
|
||||
"unitcalc": "^1.0.8"
|
||||
"unitcalc": "^1.0.8",
|
||||
"uuid": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"apr-for-each": "^1.0.6",
|
||||
|
@ -49,6 +49,30 @@ const ButtonsRow = Row.extend`
|
||||
margin-bottom: ${remcalc(60)};
|
||||
`;
|
||||
|
||||
const FilenameContainer = styled.span`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
justify-content: space-between;
|
||||
align-content: stretch;
|
||||
align-items: stretch;
|
||||
`;
|
||||
|
||||
const FilenameInput = styled(Input)`
|
||||
order: 0;
|
||||
flex: 1 1 auto;
|
||||
align-self: stretch;
|
||||
`;
|
||||
|
||||
const FilenameRemove = Button.extend`
|
||||
order: 0;
|
||||
flex: 0 1 auto;
|
||||
align-self: auto;
|
||||
margin: ${remcalc(8)};
|
||||
margin-right: 0;
|
||||
height: ${remcalc(48)};
|
||||
`;
|
||||
|
||||
const MEditor = ManifestEditor => ({ input, defaultValue }) =>
|
||||
<ManifestEditor mode="yaml" {...input} value={input.value || defaultValue} />;
|
||||
|
||||
@ -71,7 +95,13 @@ export const Name = ({ handleSubmit, onCancel, dirty }) =>
|
||||
</ButtonsRow>
|
||||
</form>;
|
||||
|
||||
export const Manifest = ({ handleSubmit, onCancel, dirty, defaultValue }) =>
|
||||
export const Manifest = ({
|
||||
handleSubmit,
|
||||
onCancel,
|
||||
dirty,
|
||||
defaultValue = '',
|
||||
loading
|
||||
}) =>
|
||||
<form onSubmit={handleSubmit}>
|
||||
<Bundle load={() => import('joyent-manifest-editor')}>
|
||||
{ManifestEditor =>
|
||||
@ -85,17 +115,67 @@ export const Manifest = ({ handleSubmit, onCancel, dirty, defaultValue }) =>
|
||||
</Bundle>
|
||||
<ButtonsRow>
|
||||
<Button onClick={onCancel} secondary>Cancel</Button>
|
||||
<Button disabled={!dirty} type="submit">
|
||||
<Button
|
||||
disabled={!(dirty || !loading || defaultValue.length)}
|
||||
type="submit"
|
||||
>
|
||||
Environment
|
||||
</Button>
|
||||
</ButtonsRow>
|
||||
</form>;
|
||||
|
||||
const Filename = ({ name, onRemoveFile }) =>
|
||||
<FilenameContainer>
|
||||
<FilenameInput
|
||||
type="text"
|
||||
placeholder="Filename including extension…"
|
||||
defaultValue={name}
|
||||
/>
|
||||
<FilenameRemove type="button" onClick={onRemoveFile} secondary>
|
||||
Remove
|
||||
</FilenameRemove>
|
||||
</FilenameContainer>;
|
||||
|
||||
export const Files = ({ loading, files, onRemoveFile }) => {
|
||||
if (loading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const _files = files.map(({ id, name, value }) =>
|
||||
<div key={id}>
|
||||
<FormGroup name={`file-name-${id}`} reduxForm>
|
||||
<FormMeta left />
|
||||
<Filename name={name} onRemoveFile={() => onRemoveFile(id)} />
|
||||
</FormGroup>
|
||||
<Bundle load={() => import('joyent-manifest-editor')}>
|
||||
{ManifestEditor =>
|
||||
ManifestEditor
|
||||
? <Field
|
||||
name={`file-value-${id}`}
|
||||
defaultValue={value}
|
||||
component={EEditor(ManifestEditor)}
|
||||
/>
|
||||
: <Dots2 />}
|
||||
</Bundle>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<H3>Files:</H3>
|
||||
{_files}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const Environment = ({
|
||||
handleSubmit,
|
||||
onCancel,
|
||||
onAddFile,
|
||||
onRemoveFile,
|
||||
dirty,
|
||||
defaultValue,
|
||||
defaultValue = '',
|
||||
files = [],
|
||||
loading
|
||||
}) =>
|
||||
<form onSubmit={handleSubmit}>
|
||||
@ -109,9 +189,14 @@ export const Environment = ({
|
||||
/>
|
||||
: <Dots2 />}
|
||||
</Bundle>
|
||||
<Files files={files} onRemoveFile={onRemoveFile} loading={loading} />
|
||||
<ButtonsRow>
|
||||
<Button onClick={onCancel} secondary>Cancel</Button>
|
||||
<Button disabled={loading || !dirty} type="submit">
|
||||
<Button type="button" onClick={onAddFile} secondary>Add File</Button>
|
||||
<Button
|
||||
disabled={!(dirty || !loading || defaultValue.length)}
|
||||
type="submit"
|
||||
>
|
||||
{loading ? <Dots2 /> : 'Review'}
|
||||
</Button>
|
||||
</ButtonsRow>
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React from 'react';
|
||||
|
||||
import DeploymentGroupEditOrCreate from './edit-or-create';
|
||||
import ManifestEditOrCreate from '@containers/manifest/edit-or-create';
|
||||
import { Progress } from '@components/deployment-groups/create';
|
||||
import { LayoutContainer } from '@components/layout';
|
||||
import { DeploymentGroupsLoading } from '@components/deployment-groups';
|
||||
@ -21,7 +21,7 @@ export default ({
|
||||
{loading && <DeploymentGroupsLoading />}
|
||||
{error && <span>{error.toString()}</span>}
|
||||
<Progress stage={stage} create />
|
||||
<DeploymentGroupEditOrCreate
|
||||
<ManifestEditOrCreate
|
||||
create
|
||||
manifest={manifest}
|
||||
deploymentGroup={deploymentGroup}
|
||||
|
@ -1,4 +1,3 @@
|
||||
export { default as DeploymentGroupList } from './list';
|
||||
export { default as DeploymentGroupCreate } from './create';
|
||||
export { default as DeploymentGroupImport } from './import';
|
||||
export { default as DeploymentGroupManifest } from './manifest';
|
||||
|
@ -5,6 +5,8 @@ import { withRouter } from 'react-router';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import intercept from 'apr-intercept';
|
||||
import paramCase from 'param-case';
|
||||
import remove from 'lodash.remove';
|
||||
import uuid from 'uuid/v4';
|
||||
|
||||
import DeploymentGroupBySlugQuery from '@graphql/DeploymentGroupBySlug.gql';
|
||||
import DeploymentGroupCreateMutation from '@graphql/DeploymentGroupCreate.gql';
|
||||
@ -25,7 +27,7 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { create, edit } = props;
|
||||
const { create, edit, files = [] } = props;
|
||||
const type = create ? 'create' : 'edit';
|
||||
|
||||
const NameForm =
|
||||
@ -68,12 +70,21 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
forceUnregisterOnUnmount: true
|
||||
})(Review);
|
||||
|
||||
if (!files.length) {
|
||||
files.push({
|
||||
id: uuid(),
|
||||
name: '',
|
||||
value: '#'
|
||||
});
|
||||
}
|
||||
|
||||
this.state = {
|
||||
defaultStage: create ? 'name' : 'edit',
|
||||
manifestStage: create ? 'manifest' : 'edit',
|
||||
name: '',
|
||||
manifest: '',
|
||||
environment: '',
|
||||
files,
|
||||
services: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
@ -97,6 +108,8 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
this.handleEnvironmentSubmit = this.handleEnvironmentSubmit.bind(this);
|
||||
this.handleReviewSubmit = this.handleReviewSubmit.bind(this);
|
||||
this.handleCancel = this.handleCancel.bind(this);
|
||||
this.handleFileAdd = this.handleFileAdd.bind(this);
|
||||
this.handleRemoveFile = this.handleRemoveFile.bind(this);
|
||||
|
||||
if (edit) {
|
||||
setTimeout(this.getDeploymentGroup, 16);
|
||||
@ -132,7 +145,7 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
};
|
||||
|
||||
provision = async deploymentGroupId => {
|
||||
const { manifest, environment } = this.state;
|
||||
const { manifest, environment, files } = this.state;
|
||||
const { provisionManifest } = this.props;
|
||||
|
||||
const [err] = await intercept(
|
||||
@ -141,6 +154,7 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
type: 'COMPOSE',
|
||||
format: 'YAML',
|
||||
environment,
|
||||
files,
|
||||
raw: manifest
|
||||
})
|
||||
);
|
||||
@ -161,15 +175,39 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
}
|
||||
|
||||
handleManifestSubmit({ manifest = '' }) {
|
||||
this.setState({ manifest }, () => {
|
||||
this.setState({ manifest: manifest || this.props.manifest }, () => {
|
||||
this.redirect({ stage: 'environment', prog: true });
|
||||
});
|
||||
}
|
||||
|
||||
handleEnvironmentSubmit({ environment = '' }) {
|
||||
handleEnvironmentSubmit(change) {
|
||||
const { environment = '' } = change;
|
||||
const { name, manifest } = this.state;
|
||||
|
||||
const files = Object.values(
|
||||
Object.keys(change).reduce((acc, key) => {
|
||||
const match = key.match(/file-(name|value)-(.*)/);
|
||||
|
||||
if (!match) {
|
||||
return acc;
|
||||
}
|
||||
|
||||
const [_, type, id] = match;
|
||||
|
||||
if (!acc[id]) {
|
||||
acc[id] = {
|
||||
id
|
||||
};
|
||||
}
|
||||
|
||||
acc[id][type] = change[key];
|
||||
return acc;
|
||||
}, {})
|
||||
);
|
||||
|
||||
const getConfig = async () => {
|
||||
const { environment } = this.state;
|
||||
|
||||
const [err, conf] = await intercept(
|
||||
client.query({
|
||||
query: DeploymentGroupConfigQuery,
|
||||
@ -179,6 +217,7 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
type: 'COMPOSE',
|
||||
format: 'YAML',
|
||||
environment,
|
||||
files,
|
||||
raw: manifest
|
||||
}
|
||||
})
|
||||
@ -193,12 +232,15 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
const { data } = conf;
|
||||
const { config: services } = data;
|
||||
|
||||
this.setState({ loading: false, services }, () => {
|
||||
this.setState({ loading: false, services, files }, () => {
|
||||
this.redirect({ stage: 'review', prog: true });
|
||||
});
|
||||
};
|
||||
|
||||
this.setState({ environment, loading: true }, getConfig);
|
||||
this.setState(
|
||||
{ environment: environment || this.props.environment, loading: true },
|
||||
getConfig
|
||||
);
|
||||
}
|
||||
|
||||
handleReviewSubmit() {
|
||||
@ -229,6 +271,24 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
history.push(create ? '/' : `/deployment-groups/${deploymentGroup.slug}`);
|
||||
}
|
||||
|
||||
handleFileAdd() {
|
||||
this.setState({
|
||||
files: this.state.files.concat([
|
||||
{
|
||||
id: uuid(),
|
||||
name: '',
|
||||
value: '#'
|
||||
}
|
||||
])
|
||||
});
|
||||
}
|
||||
|
||||
handleRemoveFile(fileId) {
|
||||
this.setState({
|
||||
files: remove(this.state.files, ({ id }) => id !== fileId)
|
||||
});
|
||||
}
|
||||
|
||||
redirect({ stage = 'name', prog = false }) {
|
||||
const { match, history, create } = this.props;
|
||||
|
||||
@ -271,8 +331,11 @@ class DeploymentGroupEditOrCreate extends Component {
|
||||
return (
|
||||
<EnvironmentForm
|
||||
defaultValue={this.props.environment}
|
||||
files={this.state.files}
|
||||
onSubmit={this.handleEnvironmentSubmit}
|
||||
onCancel={this.handleCancel}
|
||||
onAddFile={this.handleFileAdd}
|
||||
onRemoveFile={this.handleRemoveFile}
|
||||
loading={this.state.loading}
|
||||
/>
|
||||
);
|
@ -5,7 +5,7 @@ import get from 'lodash.get';
|
||||
import ManifestQuery from '@graphql/Manifest.gql';
|
||||
import DeploymentGroupBySlugQuery from '@graphql/DeploymentGroupBySlug.gql';
|
||||
|
||||
import DeploymentGroupEditOrCreate from './edit-or-create';
|
||||
import ManifestEditOrCreate from '@containers/manifest/edit-or-create';
|
||||
import { Progress } from '@components/deployment-groups/create';
|
||||
import { LayoutContainer } from '@components/layout';
|
||||
import { DeploymentGroupsLoading } from '@components/deployment-groups';
|
||||
@ -15,6 +15,7 @@ const Manifest = ({
|
||||
loading,
|
||||
error,
|
||||
manifest = '',
|
||||
environment = '',
|
||||
deploymentGroup = null,
|
||||
match
|
||||
}) => {
|
||||
@ -24,9 +25,10 @@ const Manifest = ({
|
||||
|
||||
const _view = loading || !deploymentGroup
|
||||
? null
|
||||
: <DeploymentGroupEditOrCreate
|
||||
: <ManifestEditOrCreate
|
||||
edit
|
||||
manifest={manifest}
|
||||
environment={environment}
|
||||
deploymentGroup={deploymentGroup}
|
||||
/>;
|
||||
|
||||
@ -36,7 +38,8 @@ const Manifest = ({
|
||||
deploymentGroup.imported &&
|
||||
!manifest
|
||||
? <span>
|
||||
Since this DeploymentGroup was imported, it doesn't have the initial
|
||||
Since this DeploymentGroup was imported, it doesn't have the
|
||||
initial
|
||||
manifest
|
||||
</span>
|
||||
: null;
|
||||
@ -62,6 +65,7 @@ export default compose(
|
||||
}),
|
||||
props: ({ data: { deploymentGroup, loading, error } }) => ({
|
||||
manifest: get(deploymentGroup, 'version.manifest.raw', ''),
|
||||
environment: get(deploymentGroup, 'version.manifest.environment', ''),
|
||||
loading,
|
||||
error
|
||||
})
|
1
packages/cp-frontend/src/containers/rollback/index.js
Normal file
1
packages/cp-frontend/src/containers/rollback/index.js
Normal file
@ -0,0 +1 @@
|
||||
export default null;
|
@ -1,7 +1,7 @@
|
||||
#import "./ServiceInfo.gql"
|
||||
|
||||
query config($deploymentGroupName: String!, $type: ManifestType!, $format: ManifestFormat!, $environment: String!, $raw: String!) {
|
||||
config(deploymentGroupName: $deploymentGroupName, type: $type, format: $format, environment: $environment, raw: $raw) {
|
||||
query config($deploymentGroupName: String!, $type: ManifestType!, $format: ManifestFormat!, $environment: String!, $files: [KeyValueInput]!, $raw: String!) {
|
||||
config(deploymentGroupName: $deploymentGroupName, type: $type, format: $format, environment: $environment, files: $files, raw: $raw) {
|
||||
...ServiceInfo
|
||||
config {
|
||||
id
|
||||
|
@ -1,5 +1,5 @@
|
||||
mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $format: ManifestFormat!, $environment: String!, $raw: String!) {
|
||||
provisionManifest(deploymentGroupId: $deploymentGroupId, type: $type, format: $format, environment: $environment, raw: $raw) {
|
||||
mutation provisionManifest($deploymentGroupId: ID!, $type: ManifestType!, $format: ManifestFormat!, $environment: String!, $files: [KeyValueInput]!, $raw: String!) {
|
||||
provisionManifest(deploymentGroupId: $deploymentGroupId, type: $type, format: $format, environment: $environment, files: $files, raw: $raw) {
|
||||
scale {
|
||||
serviceName
|
||||
replicas
|
||||
|
@ -4,6 +4,7 @@ query ManifestById($deploymentGroupSlug: String!) {
|
||||
manifest {
|
||||
id
|
||||
type
|
||||
environment
|
||||
format
|
||||
raw
|
||||
}
|
||||
|
@ -5,12 +5,13 @@ import styled from 'styled-components';
|
||||
import { Header, Breadcrumb, Menu } from '@containers/navigation';
|
||||
import { ServiceScale, ServiceDelete } from '@containers/service';
|
||||
import { InstanceList } from '@containers/instances';
|
||||
import Manifest from '@containers/manifest';
|
||||
import Rollback from '@containers/rollback';
|
||||
|
||||
import {
|
||||
DeploymentGroupList,
|
||||
DeploymentGroupCreate,
|
||||
DeploymentGroupImport,
|
||||
DeploymentGroupManifest
|
||||
DeploymentGroupImport
|
||||
} from '@containers/deployment-groups';
|
||||
|
||||
import {
|
||||
@ -116,7 +117,13 @@ const Router = (
|
||||
<Route
|
||||
path="/deployment-groups/:deploymentGroup/manifest/:stage?"
|
||||
exact
|
||||
component={DeploymentGroupManifest}
|
||||
component={Manifest}
|
||||
/>
|
||||
|
||||
<Route
|
||||
path="/deployment-groups/:deploymentGroup/rollback"
|
||||
exact
|
||||
component={Rollback}
|
||||
/>
|
||||
|
||||
<Route
|
||||
|
@ -13,6 +13,10 @@ const state = {
|
||||
{
|
||||
pathname: 'manifest',
|
||||
name: 'Manifest'
|
||||
},
|
||||
{
|
||||
pathname: 'rollback',
|
||||
name: 'Rollback'
|
||||
}
|
||||
],
|
||||
services: [
|
||||
|
Loading…
Reference in New Issue
Block a user