Merge pull request #1383 from prymitive/ro-menu

fix(ui): disable silence menu actions when all alertmanagers are read-only
This commit is contained in:
Łukasz Mierzwa
2020-01-29 11:03:59 +00:00
committed by GitHub
8 changed files with 98 additions and 5 deletions
@@ -44,6 +44,9 @@ const MenuContent = onClickOutside(
alertStore,
silenceFormStore
}) => {
const isReadOnly =
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0;
return (
<FetchPauser alertStore={alertStore}>
<div
@@ -68,8 +71,10 @@ const MenuContent = onClickOutside(
))}
<div className="dropdown-divider" />
<div
className="dropdown-item cursor-pointer"
className={`dropdown-item cursor-pointer ${isReadOnly &&
"disabled"}`}
onClick={() =>
isReadOnly ||
onSilenceClick(alertStore, silenceFormStore, group, alert)
}
>
@@ -17,6 +17,23 @@ beforeEach(() => {
silenceFormStore = new SilenceFormStore();
alert = MockAlert([], { foo: "bar" }, "active");
group = MockAlertGroup({ alertname: "Fake Alert" }, [alert], [], {}, {});
alertStore.data.upstreams = {
clusters: { default: ["am1"] },
instances: [
{
name: "am1",
uri: "http://localhost:8080",
publicURI: "http://example.com",
readonly: false,
headers: {},
error: "",
version: "0.15.0",
cluster: "default",
clusterMembers: ["am1"]
}
]
};
});
const MockAfterClick = jest.fn();
@@ -82,6 +99,15 @@ describe("<MenuContent />", () => {
expect(silenceFormStore.toggle.visible).toBe(true);
});
it("'Silence' menu entry is disabled when all Alertmanager instances are read-only", () => {
alertStore.data.upstreams.instances[0].readonly = true;
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
expect(button.hasClass("disabled")).toBe(true);
button.simulate("click");
expect(silenceFormStore.toggle.visible).toBe(false);
});
it("source link points at alert source", () => {
const tree = MountedMenuContent(group);
const link = tree.find("a.dropdown-item[href='localhost/prometheus']");
@@ -56,6 +56,9 @@ const MenuContent = onClickOutside(
].join("");
const groupLink = `${baseURL}?${FormatAlertsQ(groupFilters)}`;
const isReadOnly =
Object.keys(alertStore.data.clustersWithoutReadOnly).length === 0;
return (
<FetchPauser alertStore={alertStore}>
<div
@@ -74,8 +77,11 @@ const MenuContent = onClickOutside(
<FontAwesomeIcon icon={faShareSquare} /> Copy link to this group
</div>
<div
className="dropdown-item cursor-pointer"
onClick={() => onSilenceClick(alertStore, silenceFormStore, group)}
className={`dropdown-item cursor-pointer ${isReadOnly &&
"disabled"}`}
onClick={() =>
isReadOnly || onSilenceClick(alertStore, silenceFormStore, group)
}
>
<FontAwesomeIcon icon={faBellSlash} /> Silence this group
</div>
@@ -15,6 +15,23 @@ let silenceFormStore;
beforeEach(() => {
alertStore = new AlertStore([]);
silenceFormStore = new SilenceFormStore();
alertStore.data.upstreams = {
clusters: { default: ["am1"] },
instances: [
{
name: "am1",
uri: "http://localhost:8080",
publicURI: "http://example.com",
readonly: false,
headers: {},
error: "",
version: "0.15.0",
cluster: "default",
clusterMembers: ["am1"]
}
]
};
});
const MockAfterClick = jest.fn();
@@ -91,4 +108,15 @@ describe("<MenuContent />", () => {
button.simulate("click");
expect(silenceFormStore.toggle.visible).toBe(true);
});
it("'Silence' menu entry is disabled when all Alertmanager instances are read-only", () => {
alertStore.data.upstreams.instances[0].readonly = true;
const group = MockAlertGroup({ alertname: "Fake Alert" }, [], [], {}, {});
const tree = MountedMenuContent(group);
const button = tree.find(".dropdown-item").at(1);
expect(button.hasClass("disabled")).toBe(true);
button.simulate("click");
expect(silenceFormStore.toggle.visible).toBe(false);
});
});
@@ -306,7 +306,9 @@ const DeleteSilence = observer(
<button
className="btn btn-danger btn-sm"
disabled={members.length === 0}
onClick={members.length && this.toggle.toggle}
onClick={() => {
members.length > 0 && this.toggle.toggle();
}}
>
<FontAwesomeIcon
className="mr-1 d-none d-sm-inline-block"
@@ -107,6 +107,18 @@ describe("<DeleteSilence />", () => {
.simulate("click");
expect(tree.find(".modal-body")).toHaveLength(1);
});
it("button is disabled when all alertmanager instances are read-only", () => {
alertStore.data.upstreams.instances[0].readonly = true;
const tree = MountedDeleteSilence();
expect(tree.find("button").prop("disabled")).toBe(true);
tree
.find("button")
.at(0)
.simulate("click");
expect(tree.find(".modal-body")).toHaveLength(0);
});
});
describe("<DeleteSilenceModalContent />", () => {
@@ -159,7 +159,9 @@ const SilenceDetails = ({
<button
className="btn btn-primary btn-sm mb-lg-2 mb-0"
disabled={isReadOnly}
onClick={!isReadOnly && onEditSilence}
onClick={() => {
!isReadOnly && onEditSilence();
}}
>
<FontAwesomeIcon
className="mr-1 d-none d-sm-inline-block"
@@ -95,4 +95,16 @@ describe("<SilenceDetails />", () => {
expect(copy).toHaveBeenCalledTimes(1);
expect(copy).toHaveBeenCalledWith(silence.id);
});
it("Edit silence button is disabled when all alertmanager instances are read-only", () => {
alertStore.data.upstreams.instances[0].readonly = true;
const tree = MountedSilenceDetails();
expect(tree.find("button").prop("disabled")).toBe(true);
tree
.find("button")
.at(0)
.simulate("click");
expect(tree.find(".modal-body")).toHaveLength(0);
});
});