Skip to content

Commit

Permalink
fix(ui): move silence request state off component
Browse files Browse the repository at this point in the history
Fixes #1878
  • Loading branch information
prymitive committed Jun 25, 2020
1 parent 0c65bc7 commit 8eae930
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 159 deletions.
3 changes: 2 additions & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@
"jest": {
"collectCoverageFrom": [
"src/**/*.{js,ts,tsx}",
"!src/**/*.stories.{js,ts,tsx}"
"!src/**/*.stories.{js,ts,tsx}",
"!src/react-app-env.d.ts"
]
},
"devDependencies": {
Expand Down
7 changes: 7 additions & 0 deletions ui/src/Components/SilenceModal/SilenceForm.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
SilenceFormStage,
NewEmptyMatcher,
MatcherValueToObject,
NewClusterRequest,
} from "Stores/SilenceFormStore";
import { Settings } from "Stores/Settings";
import { QueryOperators } from "Common/Query";
Expand Down Expand Up @@ -100,6 +101,12 @@ const SilenceForm = ({
const handleSubmit = (event) => {
event.preventDefault();

let rbc = {};
silenceFormStore.data.alertmanagers.forEach((am) => {
rbc[am.label] = NewClusterRequest(am.label, am.value);
});
silenceFormStore.data.requestsByCluster = rbc;

settingsStore.silenceFormConfig.saveAuthor(silenceFormStore.data.author);

if (silenceFormStore.data.isValid)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,79 @@
import React, { memo } from "react";
import React from "react";
import PropTypes from "prop-types";

import { useObserver } from "mobx-react-lite";

import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faArrowLeft } from "@fortawesome/free-solid-svg-icons/faArrowLeft";
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons/faCheckCircle";
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";

import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore } from "Stores/SilenceFormStore";
import { SilenceSubmitProgress } from "./SilenceSubmitProgress";

const SilenceSubmitController = memo(({ silenceFormStore, alertStore }) => {
return (
const SilenceSubmitController = ({ silenceFormStore, alertStore }) => {
return useObserver(() => (
<React.Fragment>
<div>
{silenceFormStore.data.alertmanagers.map((am) => (
<SilenceSubmitProgress
key={am.label}
cluster={am.label}
members={am.value}
payload={silenceFormStore.data.toAlertmanagerPayload}
alertStore={alertStore}
/>
))}
<div className="table-responsive">
<table className="table table-borderless">
<tbody>
{Object.values(silenceFormStore.data.requestsByCluster).map(
(clusterRequest) => (
<tr key={clusterRequest.cluster}>
<td className="align-middle" style={{ width: "1%" }}>
{clusterRequest.isDone ? (
clusterRequest.error ? (
<FontAwesomeIcon
icon={faExclamationCircle}
className="text-danger"
/>
) : (
<FontAwesomeIcon
icon={faCheckCircle}
className="text-success"
/>
)
) : (
<SilenceSubmitProgress
key={clusterRequest.cluster}
cluster={clusterRequest.cluster}
members={clusterRequest.members}
payload={silenceFormStore.data.toAlertmanagerPayload}
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
)}
</td>
<td className="align-middle">{clusterRequest.cluster}</td>
<td>
<div
className={`rounded text-center ${
clusterRequest.isDone && clusterRequest.error
? "bg-light"
: ""
}`}
>
{clusterRequest.isDone ? (
clusterRequest.error ? (
clusterRequest.error
) : (
<a
href={clusterRequest.silenceLink}
target="_blank"
rel="noopener noreferrer"
>
{clusterRequest.silenceID}
</a>
)
) : null}
</div>
</td>
</tr>
)
)}
</tbody>
</table>
</div>
<div className="d-flex flex-row-reverse">
<button
Expand All @@ -33,8 +86,8 @@ const SilenceSubmitController = memo(({ silenceFormStore, alertStore }) => {
</button>
</div>
</React.Fragment>
);
});
));
};
SilenceSubmitController.propTypes = {
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,11 @@ import React from "react";
import { shallow } from "enzyme";

import { AlertStore } from "Stores/AlertStore";
import { SilenceFormStore, SilenceFormStage } from "Stores/SilenceFormStore";
import {
SilenceFormStore,
SilenceFormStage,
NewClusterRequest,
} from "Stores/SilenceFormStore";
import { SilenceSubmitController } from "./SilenceSubmitController";

let alertStore;
Expand All @@ -12,31 +16,126 @@ let silenceFormStore;
beforeEach(() => {
alertStore = new AlertStore([]);
silenceFormStore = new SilenceFormStore();
});

const ShallowSilenceSubmitController = () => {
return shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
};
alertStore.data.upstreams = {
clusters: { ha: ["am1", "am2"], single: "single" },
instances: [
{
name: "am1",
uri: "http://am1.example.com",
publicURI: "http://am1.example.com",
readonly: false,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
clusterMembers: ["am1", "am2"],
},
{
name: "am2",
uri: "http://am2.example.com",
publicURI: "http://am2.example.com",
readonly: false,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
clusterMembers: ["am1", "am2"],
},
{
name: "single",
uri: "http://single.example.com",
publicURI: "http://single.example.com",
readonly: false,
headers: {},
corsCredentials: "include",
error: "",
version: "0.17.0",
cluster: "ha",
clusterMembers: ["single"],
},
],
};
});

describe("<SilenceSubmitController />", () => {
it("renders all passed SilenceSubmitProgress", () => {
silenceFormStore.data.alertmanagers.push({ label: "am1", value: ["am1"] });
silenceFormStore.data.alertmanagers.push({
label: "ha",
value: ["am2", "am3"],
});
const tree = ShallowSilenceSubmitController();
expect(tree.find("div").at(0).children()).toHaveLength(2);
silenceFormStore.data.requestsByCluster = {
ha: NewClusterRequest("ha", ["am1", "am2"]),
single: NewClusterRequest("single", ["single"]),
};
const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("tr")).toHaveLength(2);
});

it("renders spinner for pending requests", () => {
const single = NewClusterRequest("single", ["single"]);
silenceFormStore.data.requestsByCluster = { single: single };
const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("tr")).toHaveLength(1);
expect(tree.find("td").at(0).html()).toMatch(/fa-circle-notch/);
expect(tree.find("td").at(1).text()).toBe("single");
expect(tree.find("td").at(2).text()).toBe("");
});

it("renders error for failed requests", () => {
const single = NewClusterRequest("single", ["single"]);
single.isDone = true;
single.error = "fake error";
silenceFormStore.data.requestsByCluster = { single: single };
const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("tr")).toHaveLength(1);
expect(tree.find("td").at(0).html()).toMatch(/fa-exclamation-circle/);
expect(tree.find("td").at(1).text()).toBe("single");
expect(tree.find("td").at(2).text()).toBe("fake error");
});

it("renders silence link for completed requests", () => {
const single = NewClusterRequest("single", ["single"]);
single.isDone = true;
single.silenceID = "123456789";
single.silenceLink = "http://localhost";
silenceFormStore.data.requestsByCluster = { single: single };
const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("tr")).toHaveLength(1);
expect(tree.find("td").at(0).html()).toMatch(/fa-check-circle/);
expect(tree.find("td").at(1).text()).toBe("single");
expect(tree.find("td").at(2).text()).toBe("123456789");
expect(
tree.find("td").at(2).find('a[href="http://localhost"]')
).toHaveLength(1);
});

it("resets the form on 'Back' button click", () => {
silenceFormStore.data.currentStage = SilenceFormStage.Submit;
const tree = ShallowSilenceSubmitController();
const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
const button = tree.find("button");
button.simulate("click");
expect(silenceFormStore.data.currentStage).toBe(SilenceFormStage.UserInput);
Expand Down
Loading

0 comments on commit 8eae930

Please sign in to comment.