Skip to content

Commit

Permalink
Managing redactors updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jgruica committed Jul 22, 2020
1 parent 69664c3 commit dbfe3ce
Show file tree
Hide file tree
Showing 10 changed files with 134 additions and 65 deletions.
2 changes: 1 addition & 1 deletion kotsadm/pkg/handlers/redact.go
Expand Up @@ -309,7 +309,7 @@ func SetRedactMetadataAndYaml(w http.ResponseWriter, r *http.Request) {
newRedactor, err := redact.SetRedactYaml(redactorSlug, updateRedactRequest.Description, updateRedactRequest.Enabled, updateRedactRequest.New, []byte(updateRedactRequest.Redactor))
if err != nil {
logger.Error(err)
metadataResponse.Error = "failed to update redactor"
metadataResponse.Error = err.Error()
JSON(w, 400, metadataResponse)
return
}
Expand Down
20 changes: 8 additions & 12 deletions kotsadm/pkg/redact/redact.go
Expand Up @@ -5,11 +5,10 @@ import (
"encoding/json"
"fmt"
"os"
"regexp"
"sort"
"strings"
"time"

"github.com/gosimple/slug"
"github.com/pkg/errors"
"github.com/replicatedhq/kots/pkg/util"
"github.com/replicatedhq/troubleshoot/pkg/apis/troubleshoot/v1beta1"
Expand Down Expand Up @@ -245,19 +244,19 @@ func setRedactYaml(slug, description string, enabled, newRedact bool, currentTim

redactorEntry := RedactorMetadata{}
redactString, ok := data[slug]

if !ok || newRedact {
// if name is not set in yaml, autogenerate a name
// if name is not set in yaml throw error
// if name is set, create the slug from the name
if newRedactorSpec.Name == "" {
newRedactorSpec.Name = fmt.Sprintf("redactor-%d", len(data)+1)
slug = getSlug(newRedactorSpec.Name)
return nil, nil, fmt.Errorf("failed to create new redact spec: name can't be empty")
} else {
slug = getSlug(newRedactorSpec.Name)
}

if _, ok := data[slug]; ok {
// the target slug already exists - this is an error
return nil, nil, fmt.Errorf("refusing to create new redact spec with name %s - slug %s already exists", newRedactorSpec.Name, slug)
return nil, nil, fmt.Errorf("failed to create new redact spec: name %s - slug %s already exists", newRedactorSpec.Name, slug)
}

// create the new redactor
Expand All @@ -278,7 +277,7 @@ func setRedactYaml(slug, description string, enabled, newRedact bool, currentTim

if _, ok := data[getSlug(newRedactorSpec.Name)]; ok {
// the target slug already exists - this is an error
return nil, nil, fmt.Errorf("refusing to change slug from %s to %s as that already exists", slug, getSlug(newRedactorSpec.Name))
return nil, nil, fmt.Errorf("failed to update redact spec: refusing to change slug from %s to %s as that already exists", slug, getSlug(newRedactorSpec.Name))
}

delete(data, slug)
Expand All @@ -288,8 +287,7 @@ func setRedactYaml(slug, description string, enabled, newRedact bool, currentTim
}

if newRedactorSpec.Name == "" {
newRedactorSpec.Name = slug
redactorEntry.Metadata.Name = slug
return nil, nil, fmt.Errorf("failed to update redact spec: name can't be empty")
}
}

Expand Down Expand Up @@ -386,9 +384,7 @@ func writeConfigmap(configMap *v1.ConfigMap) (*v1.ConfigMap, error) {
}

func getSlug(name string) string {
name = strings.ReplaceAll(name, " ", "-")

name = regexp.MustCompile(`[^\w\d-_]`).ReplaceAllString(name, "")
name = slug.Make(name)

if name == "kotsadm-redact" {
name = "kotsadm-redact-metadata"
Expand Down
6 changes: 3 additions & 3 deletions kotsadm/pkg/redact/redact_test.go
Expand Up @@ -21,7 +21,7 @@ func Test_getSlug(t *testing.T) {
{
name: "all alphanumeric",
input: "aBC123",
want: "aBC123",
want: "abc123",
},
{
name: "dashes",
Expand All @@ -34,9 +34,9 @@ func Test_getSlug(t *testing.T) {
want: "abc-123",
},
{
name: "aymbols",
name: "symbols",
input: "abc%^123!@#",
want: "abc123",
want: "abc-123-at",
},
{
name: "kotsadm-redact",
Expand Down
70 changes: 44 additions & 26 deletions kotsadm/web/src/components/redactors/EditRedactor.jsx
Expand Up @@ -10,6 +10,8 @@ import "brace/theme/chrome";
import Loader from "../shared/Loader";
import { Utilities } from "../../utilities/utilities";

import "../../scss/components/redactors/EditRedactor.scss"

class EditRedactor extends Component {
state = {
redactorEnabled: false,
Expand Down Expand Up @@ -71,7 +73,6 @@ class EditRedactor extends Component {
this.setState({ editingRedactor: true, editingErrMsg: "" });

const payload = {
slug: slug,
enabled: enabled,
redactor: yaml
}
Expand Down Expand Up @@ -106,6 +107,7 @@ class EditRedactor extends Component {
setTimeout(() => {
this.setState({ editConfirm: false })
}, 3000);
this.props.history.replace(`/app/${this.props.appSlug}/troubleshoot/redactors`)
} else {
this.setState({
editingRedactor: false,
Expand All @@ -121,6 +123,22 @@ class EditRedactor extends Component {
});
}

getEmptyNameLine = (redactorYaml) => {
const splittedYaml = redactorYaml.split("\n");
let metadataFound = false;
let namePosition;
for(let i=0; i<splittedYaml.length; ++i) {
if (splittedYaml[i] === "metadata:") {
metadataFound = true;
}
if (metadataFound && splittedYaml[i].includes("name:")) {
namePosition = i + 1;
break;
}
}
return namePosition;
}

createRedactor = (enabled, newRedactor, yaml) => {
this.setState({ creatingRedactor: true, createErrMsg: "" });

Expand All @@ -144,8 +162,10 @@ class EditRedactor extends Component {
this.setState({
creatingRedactor: false,
createErrMsg: createResponse.error
})
return;
});
const editor = this.aceEditor.editor;
editor.scrollToLine(this.getEmptyNameLine(this.state.redactorYaml), true, true);
editor.gotoLine(this.getEmptyNameLine(this.state.redactorYaml), 1, true);
}

if (createResponse.success) {
Expand All @@ -160,7 +180,7 @@ class EditRedactor extends Component {
setTimeout(() => {
this.setState({ createConfirm: false })
}, 3000);
this.props.history.replace(`/app/${this.props.appSlug}/troubleshoot/redactors/${createResponse.redactorMetadata.slug}`)
this.props.history.replace(`/app/${this.props.appSlug}/troubleshoot/redactors`)
} else {
this.setState({
creatingRedactor: false,
Expand Down Expand Up @@ -189,7 +209,7 @@ class EditRedactor extends Component {
const defaultYaml = `kind: Redactor
apiVersion: troubleshoot.replicated.com/v1beta1
metadata:
name: kotsadm-redact
name:
spec:
redactors:
- name: myredactor
Expand All @@ -199,7 +219,7 @@ spec:
removals:
values:
- "removethis"`
this.setState({ redactorEnabled: false, redactorYaml: defaultYaml, redactorName: "New redactor" });
this.setState({ redactorEnabled: true, redactorYaml: defaultYaml, redactorName: "New redactor" });
}
}

Expand Down Expand Up @@ -233,39 +253,38 @@ spec:
<title>Redactors</title>
</Helmet>
<div className="Redactors--wrapper flex1 flex-column u-width--full">
{(createErrMsg || editingErrMsg) && <p className="ErrorToast flex justifyContent--center alignItems--center">{createErrMsg ? createErrMsg : editingErrMsg}</p>}
<div className="u-fontSize--small u-fontWeight--medium u-color--dustyGray u-marginBottom--20">
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors`} className="replicated-link u-marginRight--5">Redactors</Link> > <span className="u-marginLeft--5">{this.state.redactorName}</span>
</div>
<div className="flex flex-auto alignItems--flexStart justifyContent--spaceBetween">
<div className="flex flex1 alignItems--center">
<p className="u-fontWeight--bold u-color--tuna u-fontSize--jumbo u-lineHeight--normal u-marginRight--10">{this.state.redactorName}</p>
</div>
{!this.props.isNew &&
<div className="flex justifyContent--flexEnd">
<div className="toggle flex flex1">
<div className="flex flex1">
<div className={`Checkbox--switch ${this.state.redactorEnabled ? "is-checked" : "is-notChecked"}`}>
<input
type="checkbox"
className="Checkbox-toggle"
name="isRedactorEnabled"
checked={this.state.redactorEnabled}
onChange={(e) => { this.handleEnableRedactor(e) }}
/>
</div>
</div>
<div className="flex flex1 u-marginLeft--5">
<p className="u-fontWeight--medium u-color--tundora u-fontSize--large alignSelf--center">{this.state.redactorEnabled ? "Enabled" : "Disabled"}</p>
<div className="flex justifyContent--flexEnd">
<div className="toggle flex flex1">
<div className="flex flex1">
<div className={`Checkbox--switch ${this.state.redactorEnabled ? "is-checked" : "is-notChecked"}`}>
<input
type="checkbox"
className="Checkbox-toggle"
name="isRedactorEnabled"
checked={this.state.redactorEnabled}
onChange={(e) => { this.handleEnableRedactor(e) }}
/>
</div>
</div>
<div className="flex flex1 u-marginLeft--5">
<p className="u-fontWeight--medium u-color--tundora u-fontSize--large alignSelf--center">{this.state.redactorEnabled ? "Enabled" : "Disabled"}</p>
</div>
</div>
}
</div>
</div>
<p className="u-fontSize--normal u-color--dustyGray u-fontWeight--medium u-lineHeight--normal u-marginTop--small">For more information about creating redactors,
<a href="https://troubleshoot.sh/reference/redactors/overview/" target="_blank" rel="noopener noreferrer" className="replicated-link"> check out our docs</a>.</p>
<div className="flex1 u-marginTop--30 u-border--gray">
<AceEditor
ref={(input) => this.refAceEditor = input}
ref={el => (this.aceEditor = el)}
mode="yaml"
theme="chrome"
className="flex1 flex"
Expand All @@ -287,7 +306,7 @@ spec:
</div>
<div className="flex u-marginTop--20 justifyContent--spaceBetween">
<div className="flex">
<Link to="/redactors" className="btn secondary"> Cancel </Link>
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors`} className="btn secondary"> Cancel </Link>
</div>
<div className="flex alignItems--center">
{createConfirm || editConfirm &&
Expand All @@ -296,7 +315,6 @@ spec:
<span className="u-marginLeft--5 u-fontSize--small u-fontWeight--medium u-color--chateauGreen">{createConfirm ? "Redactor created" : "Redactor updated"}</span>
</div>
}
{(createErrMsg || editingErrMsg) && <p className="u-color--chestnut u-fontSize--normal u-fontWeight--medium u-lineHeight--normal u-marginRight--10">{createErrMsg ? createErrMsg : editingErrMsg}</p>}
<button type="button" className="btn primary blue" onClick={this.onSaveRedactor} disabled={creatingRedactor || editingRedactor}>{(creatingRedactor || editingRedactor) ? "Saving" : "Save redactor"} </button>
</div>
</div>
Expand Down
10 changes: 6 additions & 4 deletions kotsadm/web/src/components/redactors/RedactorRow.jsx
Expand Up @@ -2,14 +2,16 @@ import React from "react";
import dayjs from "dayjs";
import { Link } from "react-router-dom"

import { Utilities } from "../../utilities/utilities";

class RedactorRow extends React.Component {
state = {
redactorEnabled: false
redactorEnabled: false,
};

handleEnableRedactor = () => {
this.setState({
redactorEnabled: !this.state.redactorEnabled,
this.setState({ redactorEnabled: !this.state.redactorEnabled }, () => {
this.props.handleSetRedactEnabled(this.props.redactor, this.state.redactorEnabled);
});
}

Expand Down Expand Up @@ -37,7 +39,7 @@ class RedactorRow extends React.Component {
<p className="u-fontSize--small u-fontWeight--medium u-lineHeight--normal u-color--nevada u-marginLeft--10"> {redactor?.description} </p>
</div>
<div className="flex alignItems--center">
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors/${redactor?.slug}`} className="u-fontSize--normal u-fontWeight--medium u-color--royalBlue u-textDecoration--underlineOnHover u-marginRight--20">Edit redactor</Link>
<Link to={`/app/${this.props.appSlug}/troubleshoot/redactors/${redactor?.slug}`} className="u-fontSize--normal u-fontWeight--medium u-color--royalBlue u-textDecoration--underlineOnHover u-marginRight--20">Edit</Link>
<span className="u-fontSize--normal u-fontWeight--medium u-color--chestnut u-textDecoration--underlineOnHover u-marginRight--20" onClick={() => this.handleDeleteClick(redactor)}>Delete</span>
<div className={`Checkbox--switch ${this.state.redactorEnabled ? "is-checked" : "is-notChecked"}`}>
<input
Expand Down
54 changes: 48 additions & 6 deletions kotsadm/web/src/components/redactors/Redactors.jsx
Expand Up @@ -11,21 +11,21 @@ import Loader from "../shared/Loader";

import { Utilities } from "../../utilities/utilities";


class Redactors extends Component {
state = {
redactors: [],
sortedRedactors: [],
selectedOption: {
value: "createdAt",
label: "Sort by: Created At"
value: "enabled",
label: "Sort by: Status"
},
deleteRedactorModal: false,
redactorToDelete: {},
isLoadingRedactors: false,
redactorsErrMsg: "",
deletingRedactor: false,
deleteErrMsg: ""
deleteErrMsg: "",
enablingRedactorMsg: ""
};

getRedactors = () => {
Expand Down Expand Up @@ -81,8 +81,10 @@ class Redactors extends Component {
sortRedactors = value => {
if (value === "createdAt") {
this.setState({ sortedRedactors: this.state.redactors.sort((a, b) => dayjs(b.createdAt) - dayjs(a.createdAt)) });
} else {
} else if (value === "updatedAt") {
this.setState({ sortedRedactors: this.state.redactors.sort((a, b) => dayjs(b.updatedAt) - dayjs(a.updatedAt)) });
} else {
this.setState({ sortedRedactors: this.state.redactors.sort((a, b) => (a.enabled === b.enabled) ? 0 : a.enabled ? -1 : 1 )});
}
}

Expand Down Expand Up @@ -121,9 +123,42 @@ class Redactors extends Component {
});
}

handleSetRedactEnabled = (redactor, redactorEnabled) => {
const payload = {
enabled: redactorEnabled
}
this.setState({ enablingRedactorMsg: "" });
fetch(`${window.env.API_ENDPOINT}/redact/enabled/${redactor.slug}`, {
method: "POST",
headers: {
"Authorization": Utilities.getToken(),
"Content-Type": "application/json",
},
body: JSON.stringify(payload)
})
.then(async (res) => {
const response = await res.json();
if (!res.ok) {
this.setState({ enablingRedactorMsg: response.error });
return;
}
if (response.success) {
this.setState({ enablingRedactorMsg: "" });
} else {
this.setState({ enablingRedactorMsg: response.error });
}
})
.catch((err) => {
this.setState({
enablingRedactorMsg: err.message ? err.message : "Something went wrong, please try again!"
});
});
}



render() {
const { sortedRedactors, selectedOption, deleteRedactorModal, isLoadingRedactors } = this.state;
const { sortedRedactors, selectedOption, deleteRedactorModal, isLoadingRedactors, enablingRedactorMsg } = this.state;

if (isLoadingRedactors) {
return (
Expand All @@ -134,6 +169,10 @@ class Redactors extends Component {
}

const selectOptions = [
{
value: "enabled",
label: "Sort by: Status"
},
{
value: "createdAt",
label: "Sort by: Created At"
Expand Down Expand Up @@ -189,12 +228,15 @@ class Redactors extends Component {
</div>
<p className="u-fontSize--normal u-color--dustyGray u-fontWeight--medium u-lineHeight--normal u-marginTop--20 u-marginBottom--30">Define custom rules for sensitive values you need to be redacted when gathering a support bundle. This might include things like Secrets or IP addresses. For help with creating custom redactors,
<a href="https://troubleshoot.sh/reference/redactors/overview/" target="_blank" rel="noopener noreferrer" className="replicated-link"> check out our docs</a>.</p>
{enablingRedactorMsg && <p className="u-color--chestnut u-fontSize--normal u-fontWeight--medium u-lineHeight--normal u-marginBottom--10 flex justifyContent--center alignItems--center">{enablingRedactorMsg}</p>}
{sortedRedactors?.map((redactor) => (
<RedactorRow
key={`redactor-${redactor.slug}`}
redactor={redactor}
appSlug={this.props.appSlug}
toggleConfirmDeleteModal={this.toggleConfirmDeleteModal}
handleSetRedactEnabled={this.handleSetRedactEnabled}

/>
))}
</div>
Expand Down

0 comments on commit dbfe3ce

Please sign in to comment.