|
|
@@ -6,6 +6,7 @@ import _ from "lodash";
|
|
|
|
|
|
import { ChartType, StorageType, ClusterType } from "shared/types";
|
|
|
import { Context } from "shared/Context";
|
|
|
+import { isAlphanumeric } from "shared/common";
|
|
|
import api from "shared/api";
|
|
|
|
|
|
import SaveButton from "components/SaveButton";
|
|
|
@@ -15,6 +16,7 @@ import TabRegion from "components/TabRegion";
|
|
|
import EnvGroupArray, { KeyValueType } from "./EnvGroupArray";
|
|
|
import Heading from "components/values-form/Heading";
|
|
|
import Helper from "components/values-form/Helper";
|
|
|
+import InputRow from "components/values-form/InputRow";
|
|
|
import { withAuth, WithAuthProps } from "shared/auth/AuthorizationHoc";
|
|
|
|
|
|
type PropsType = WithAuthProps & {
|
|
|
@@ -30,8 +32,15 @@ type StateType = {
|
|
|
showDeleteOverlay: boolean;
|
|
|
deleting: boolean;
|
|
|
saveValuesStatus: string | null;
|
|
|
- envVariables: KeyValueType[];
|
|
|
+ envGroup: EnvGroup;
|
|
|
tabOptions: { value: string; label: string }[];
|
|
|
+ newEnvGroupName: string;
|
|
|
+};
|
|
|
+
|
|
|
+type EnvGroup = {
|
|
|
+ name: string;
|
|
|
+ timestamp: string;
|
|
|
+ variables: KeyValueType[];
|
|
|
};
|
|
|
|
|
|
const tabOptions = [
|
|
|
@@ -46,29 +55,48 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
showDeleteOverlay: false,
|
|
|
deleting: false,
|
|
|
saveValuesStatus: null as string | null,
|
|
|
- envVariables: [] as KeyValueType[],
|
|
|
+ envGroup: {
|
|
|
+ name: null as string,
|
|
|
+ timestamp: null as string,
|
|
|
+ variables: [] as KeyValueType[],
|
|
|
+ },
|
|
|
tabOptions: [
|
|
|
{ value: "environment", label: "Environment Variables" },
|
|
|
{ value: "settings", label: "Settings" },
|
|
|
],
|
|
|
+ newEnvGroupName: null as string,
|
|
|
};
|
|
|
|
|
|
- componentDidMount() {
|
|
|
+ populateEnvGroup = (envGroup: any) => {
|
|
|
+ const {
|
|
|
+ metadata: { name, creationTimestamp: timestamp },
|
|
|
+ data,
|
|
|
+ } = envGroup;
|
|
|
// parse env group props into values type
|
|
|
- let envVariables = [] as KeyValueType[];
|
|
|
- let envGroupData = this.props.envGroup.data;
|
|
|
+ const variables = [] as KeyValueType[];
|
|
|
|
|
|
- for (const key in envGroupData) {
|
|
|
- envVariables.push({
|
|
|
+ for (const key in data) {
|
|
|
+ variables.push({
|
|
|
key: key,
|
|
|
- value: envGroupData[key],
|
|
|
- hidden: envGroupData[key].includes("PORTERSECRET"),
|
|
|
- locked: envGroupData[key].includes("PORTERSECRET"),
|
|
|
+ value: data[key],
|
|
|
+ hidden: data[key].includes("PORTERSECRET"),
|
|
|
+ locked: data[key].includes("PORTERSECRET"),
|
|
|
deleted: false,
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- this.setState({ envVariables });
|
|
|
+ this.setState({
|
|
|
+ envGroup: {
|
|
|
+ name,
|
|
|
+ timestamp,
|
|
|
+ variables,
|
|
|
+ },
|
|
|
+ newEnvGroupName: name,
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ componentDidMount() {
|
|
|
+ this.populateEnvGroup(this.props.envGroup);
|
|
|
|
|
|
// Filter the settings tab options as for now it only shows the delete button.
|
|
|
// In a future this should be removed and return to a constant if we want to show data
|
|
|
@@ -86,25 +114,49 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- handleUpdateValues = () => {
|
|
|
- let { envGroup } = this.props;
|
|
|
- let name = envGroup.metadata.name;
|
|
|
- let namespace = envGroup.metadata.namespace;
|
|
|
+ handleRename = () => {
|
|
|
+ const { namespace } = this.props;
|
|
|
+ const {
|
|
|
+ envGroup: { name },
|
|
|
+ newEnvGroupName: newName,
|
|
|
+ } = this.state;
|
|
|
|
|
|
- let apiEnvVariables: Record<string, string> = {};
|
|
|
- let secretEnvVariables: Record<string, string> = {};
|
|
|
+ api
|
|
|
+ .renameConfigMap(
|
|
|
+ "<token>",
|
|
|
+ {
|
|
|
+ name,
|
|
|
+ namespace,
|
|
|
+ new_name: newName,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ id: this.context.currentProject.id,
|
|
|
+ cluster_id: this.props.currentCluster.id,
|
|
|
+ }
|
|
|
+ )
|
|
|
+ .then((res) => {
|
|
|
+ this.populateEnvGroup(res.data);
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ handleUpdateValues = () => {
|
|
|
+ const { namespace } = this.props;
|
|
|
+ const {
|
|
|
+ envGroup: { name, variables: envVariables },
|
|
|
+ } = this.state;
|
|
|
|
|
|
- let envVariables = this.state.envVariables;
|
|
|
+ const apiEnvVariables: Record<string, string> = {};
|
|
|
+ const secretEnvVariables: Record<string, string> = {};
|
|
|
|
|
|
envVariables
|
|
|
.filter((envVar: KeyValueType, index: number, self: KeyValueType[]) => {
|
|
|
// remove any collisions that are marked as deleted and are duplicates, unless they are
|
|
|
// all delete collisions
|
|
|
- let numDeleteCollisions = self.reduce((n, _envVar: KeyValueType) => {
|
|
|
+ const numDeleteCollisions = self.reduce((n, _envVar: KeyValueType) => {
|
|
|
return n + (_envVar.key === envVar.key && envVar.deleted ? 1 : 0);
|
|
|
}, 0);
|
|
|
|
|
|
- let numCollisions = self.reduce((n, _envVar: KeyValueType) => {
|
|
|
+ const numCollisions = self.reduce((n, _envVar: KeyValueType) => {
|
|
|
return n + (_envVar.key === envVar.key ? 1 : 0);
|
|
|
}, 0);
|
|
|
|
|
|
@@ -171,9 +223,15 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
};
|
|
|
|
|
|
renderTabContents = () => {
|
|
|
- let currentTab = this.state.currentTab;
|
|
|
- let { envGroup, namespace } = this.props;
|
|
|
- let name = envGroup.metadata.name;
|
|
|
+ const { namespace } = this.props;
|
|
|
+ const {
|
|
|
+ envGroup: { name, variables },
|
|
|
+ newEnvGroupName: newName,
|
|
|
+ currentTab,
|
|
|
+ } = this.state;
|
|
|
+
|
|
|
+ const isEnvGroupNameValid = isAlphanumeric(newName) && newName !== "";
|
|
|
+ const isEnvGroupNameDifferent = newName !== name;
|
|
|
|
|
|
switch (currentTab) {
|
|
|
case "environment":
|
|
|
@@ -187,8 +245,12 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
</Helper>
|
|
|
<EnvGroupArray
|
|
|
namespace={namespace}
|
|
|
- values={this.state.envVariables}
|
|
|
- setValues={(x: any) => this.setState({ envVariables: x })}
|
|
|
+ values={variables}
|
|
|
+ setValues={(x: any) =>
|
|
|
+ this.setState((prevState) => ({
|
|
|
+ envGroup: { ...prevState.envGroup, variables: x },
|
|
|
+ }))
|
|
|
+ }
|
|
|
fileUpload={true}
|
|
|
secretOption={true}
|
|
|
disabled={
|
|
|
@@ -216,6 +278,29 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
<TabWrapper>
|
|
|
{this.props.isAuthorized("env_group", "", ["get", "delete"]) && (
|
|
|
<InnerWrapper full={true}>
|
|
|
+ <Heading>Name</Heading>
|
|
|
+ <Subtitle>
|
|
|
+ <Warning makeFlush={true} highlight={!isEnvGroupNameValid}>
|
|
|
+ Lowercase letters, numbers, and "-" only.
|
|
|
+ </Warning>
|
|
|
+ </Subtitle>
|
|
|
+ <DarkMatter antiHeight="-29px" />
|
|
|
+ <InputRow
|
|
|
+ type="text"
|
|
|
+ value={newName}
|
|
|
+ setValue={(x: string) =>
|
|
|
+ this.setState({ newEnvGroupName: x })
|
|
|
+ }
|
|
|
+ placeholder="ex: doctor-scientist"
|
|
|
+ width="100%"
|
|
|
+ />
|
|
|
+ <Button
|
|
|
+ color="#616FEEcc"
|
|
|
+ disabled={!(isEnvGroupNameDifferent && isEnvGroupNameValid)}
|
|
|
+ onClick={this.handleRename}
|
|
|
+ >
|
|
|
+ Rename {name}
|
|
|
+ </Button>
|
|
|
<Heading>Manage Environment Group</Heading>
|
|
|
<Helper>
|
|
|
Permanently delete this set of environment variables. This
|
|
|
@@ -235,9 +320,9 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
};
|
|
|
|
|
|
readableDate = (s: string) => {
|
|
|
- let ts = new Date(s);
|
|
|
- let date = ts.toLocaleDateString();
|
|
|
- let time = ts.toLocaleTimeString([], {
|
|
|
+ const ts = new Date(s);
|
|
|
+ const date = ts.toLocaleDateString();
|
|
|
+ const time = ts.toLocaleTimeString([], {
|
|
|
hour: "numeric",
|
|
|
minute: "2-digit",
|
|
|
});
|
|
|
@@ -245,9 +330,10 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
};
|
|
|
|
|
|
handleDeleteEnvGroup = () => {
|
|
|
- let { envGroup } = this.props;
|
|
|
- let name = envGroup.metadata.name;
|
|
|
- let namespace = envGroup.metadata.namespace;
|
|
|
+ const { namespace } = this.props;
|
|
|
+ const {
|
|
|
+ envGroup: { name },
|
|
|
+ } = this.state;
|
|
|
|
|
|
this.setState({ deleting: true });
|
|
|
api
|
|
|
@@ -280,11 +366,10 @@ class ExpandedEnvGroup extends Component<PropsType, StateType> {
|
|
|
};
|
|
|
|
|
|
render() {
|
|
|
- let { closeExpanded } = this.props;
|
|
|
- let { envGroup } = this.props;
|
|
|
- let name = envGroup.metadata.name;
|
|
|
- let timestamp = envGroup.metadata.creationTimestamp;
|
|
|
- let namespace = envGroup.metadata.namespace;
|
|
|
+ const { namespace, closeExpanded } = this.props;
|
|
|
+ const {
|
|
|
+ envGroup: { name, timestamp },
|
|
|
+ } = this.state;
|
|
|
|
|
|
return (
|
|
|
<>
|
|
|
@@ -571,3 +656,23 @@ const StyledExpandedChart = styled.div`
|
|
|
}
|
|
|
}
|
|
|
`;
|
|
|
+
|
|
|
+const DarkMatter = styled.div<{ antiHeight?: string }>`
|
|
|
+ width: 100%;
|
|
|
+ margin-top: ${(props) => props.antiHeight || "-15px"};
|
|
|
+`;
|
|
|
+
|
|
|
+const Warning = styled.span<{ highlight: boolean; makeFlush?: boolean }>`
|
|
|
+ color: ${(props) => (props.highlight ? "#f5cb42" : "")};
|
|
|
+ margin-left: ${(props) => (props.makeFlush ? "" : "5px")};
|
|
|
+`;
|
|
|
+
|
|
|
+const Subtitle = styled.div`
|
|
|
+ padding: 11px 0px 16px;
|
|
|
+ font-family: "Work Sans", sans-serif;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #aaaabb;
|
|
|
+ line-height: 1.6em;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+`;
|