Skip to content

Commit

Permalink
feat(ui): show simplified status for single cluster silence submits
Browse files Browse the repository at this point in the history
  • Loading branch information
prymitive committed Jun 25, 2020
1 parent aab937a commit bdf835a
Show file tree
Hide file tree
Showing 3 changed files with 230 additions and 72 deletions.
188 changes: 128 additions & 60 deletions ui/src/Components/SilenceModal/SilenceSubmit/SilenceSubmitController.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,76 +5,144 @@ 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 { faCheckCircle } from "@fortawesome/free-solid-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 = ({ silenceFormStore, alertStore }) => {
const SingleClusterStatus = ({ silenceFormStore, alertStore }) => {
const clusterRequest = Object.values(
silenceFormStore.data.requestsByCluster
)[0];

return useObserver(() => (
<React.Fragment>
<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%" }}>
<div className="text-center">
<div className="display-1 mb-3">
{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}
/>
)}
</div>
<div className="badge badge-primary">{clusterRequest.cluster}</div>
{clusterRequest.isDone ? (
<p
className={`mt-2 rounded text-center ${
clusterRequest.isDone && clusterRequest.error ? "bg-light" : ""
}`}
>
{clusterRequest.error ? (
clusterRequest.error
) : (
<a
href={clusterRequest.silenceLink}
target="_blank"
rel="noopener noreferrer"
>
{clusterRequest.silenceID}
</a>
)}
</p>
) : null}
</div>
));
};

const MultiClusterStatus = ({ silenceFormStore, alertStore }) => {
return useObserver(() => (
<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 ? (
<FontAwesomeIcon
icon={faExclamationCircle}
className="text-danger"
/>
clusterRequest.error
) : (
<FontAwesomeIcon
icon={faCheckCircle}
className="text-success"
/>
<a
href={clusterRequest.silenceLink}
target="_blank"
rel="noopener noreferrer"
>
{clusterRequest.silenceID}
</a>
)
) : (
<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>
) : null}
</div>
</td>
</tr>
)
)}
</tbody>
</table>
</div>
));
};

const SilenceSubmitController = ({ silenceFormStore, alertStore }) => {
return useObserver(() => (
<React.Fragment>
{Object.keys(silenceFormStore.data.requestsByCluster).length === 1 ? (
<SingleClusterStatus
silenceFormStore={silenceFormStore}
alertStore={alertStore}
/>
) : (
<MultiClusterStatus
silenceFormStore={silenceFormStore}
alertStore={alertStore}
/>
)}
<div className="d-flex flex-row-reverse">
<button
type="button"
Expand All @@ -93,4 +161,4 @@ SilenceSubmitController.propTypes = {
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
};

export { SilenceSubmitController };
export { SilenceSubmitController, MultiClusterStatus, SingleClusterStatus };
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ import {
SilenceFormStage,
NewClusterRequest,
} from "Stores/SilenceFormStore";
import { SilenceSubmitController } from "./SilenceSubmitController";
import {
SilenceSubmitController,
MultiClusterStatus,
SingleClusterStatus,
} from "./SilenceSubmitController";

let alertStore;
let silenceFormStore;
Expand Down Expand Up @@ -61,25 +65,71 @@ beforeEach(() => {
});

describe("<SilenceSubmitController />", () => {
it("renders all passed SilenceSubmitProgress", () => {
it("renders MultiClusterStatus when multiple clusters are used", () => {
silenceFormStore.data.requestsByCluster = {
ha: NewClusterRequest("ha", ["am1", "am2"]),
single: NewClusterRequest("single", ["single"]),
};

const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("MultiClusterStatus")).toHaveLength(1);
expect(tree.find("SingleClusterStatus")).toHaveLength(0);
});

it("renders SingleClusterStatus when multiple clusters are used", () => {
silenceFormStore.data.requestsByCluster = {
ha: NewClusterRequest("ha", ["am1", "am2"]),
};

const tree = shallow(
<SilenceSubmitController
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("MultiClusterStatus")).toHaveLength(0);
expect(tree.find("SingleClusterStatus")).toHaveLength(1);
});

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

describe("<MultiClusterStatus />", () => {
it("renders all passed SilenceSubmitProgress", () => {
silenceFormStore.data.requestsByCluster = {
ha: NewClusterRequest("ha", ["am1", "am2"]),
single: NewClusterRequest("single", ["single"]),
};
const tree = shallow(
<MultiClusterStatus
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
<MultiClusterStatus
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
Expand All @@ -96,7 +146,7 @@ describe("<SilenceSubmitController />", () => {
single.error = "fake error";
silenceFormStore.data.requestsByCluster = { single: single };
const tree = shallow(
<SilenceSubmitController
<MultiClusterStatus
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
Expand All @@ -114,7 +164,7 @@ describe("<SilenceSubmitController />", () => {
single.silenceLink = "http://localhost";
silenceFormStore.data.requestsByCluster = { single: single };
const tree = shallow(
<SilenceSubmitController
<MultiClusterStatus
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
Expand All @@ -127,17 +177,57 @@ describe("<SilenceSubmitController />", () => {
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;
describe("<SingleClusterStatus />", () => {
it("renders spinner for pending requests", () => {
const single = NewClusterRequest("single", ["single"]);
silenceFormStore.data.requestsByCluster = { single: single };
const tree = shallow(
<SilenceSubmitController
<SingleClusterStatus
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
const button = tree.find("button");
button.simulate("click");
expect(silenceFormStore.data.currentStage).toBe(SilenceFormStage.UserInput);
expect(tree.find("div.display-1").at(0).html()).toMatch(/fa-circle-notch/);
expect(tree.find("div.badge.badge-primary").text()).toBe("single");
expect(tree.find("p")).toHaveLength(0);
});

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(
<SingleClusterStatus
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);
expect(tree.find("div.display-1").at(0).html()).toMatch(
/fa-exclamation-circle/
);
expect(tree.find("div.badge.badge-primary").text()).toBe("single");
expect(tree.find("p").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(
<SingleClusterStatus
alertStore={alertStore}
silenceFormStore={silenceFormStore}
/>
);

expect(tree.find("div.display-1").at(0).html()).toMatch(/fa-check-circle/);
expect(tree.find("div.badge.badge-primary").text()).toBe("single");
expect(tree.find("p").text()).toBe("123456789");
expect(tree.find("p").find('a[href="http://localhost"]')).toHaveLength(1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ const SilenceSubmitProgress = ({
}
}, [cluster, error, inProgress, publicURIs, response, responseURI]); // eslint-disable-line react-hooks/exhaustive-deps

return <FontAwesomeIcon icon={faCircleNotch} spin />;
return <FontAwesomeIcon className="text-muted" icon={faCircleNotch} spin />;
};
SilenceSubmitProgress.propTypes = {
cluster: PropTypes.string.isRequired,
Expand Down

0 comments on commit bdf835a

Please sign in to comment.