mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
Merge pull request #63 from prymitive/proptypes-strict
feat(tests): strict validation for props
This commit is contained in:
@@ -5,10 +5,12 @@ import { observer } from "mobx-react";
|
||||
|
||||
import Favico from "favico.js";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
|
||||
const FaviconBadge = observer(
|
||||
class FaviconBadge extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
||||
@@ -3,11 +3,13 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { inject } from "mobx-react";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
|
||||
const FetchPauser = inject("alertStore")(
|
||||
class FetchPauser extends Component {
|
||||
static propTypes = {
|
||||
children: PropTypes.any,
|
||||
alertStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
|
||||
@@ -15,6 +15,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCaretDown } from "@fortawesome/free-solid-svg-icons/faCaretDown";
|
||||
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
|
||||
import { APIAlert, APIGroup } from "Models/API";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { FetchPauser } from "Components/FetchPauser";
|
||||
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
||||
|
||||
@@ -71,17 +73,17 @@ MenuContent.propTypes = {
|
||||
popperPlacement: PropTypes.string,
|
||||
popperRef: PropTypes.func,
|
||||
popperStyle: PropTypes.object,
|
||||
group: PropTypes.object.isRequired,
|
||||
alert: PropTypes.object.isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
alert: APIAlert.isRequired,
|
||||
afterClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const AlertMenu = observer(
|
||||
class AlertMenu extends Component {
|
||||
static propTypes = {
|
||||
group: PropTypes.object.isRequired,
|
||||
alert: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
group: APIGroup.isRequired,
|
||||
alert: APIAlert.isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
collapse = observable(
|
||||
|
||||
@@ -42,7 +42,7 @@ exports[`<Alert /> matches snapshot with showAlertmanagers=false showReceiver=fa
|
||||
hidden
|
||||
</div>
|
||||
</div>
|
||||
<span class=\\"components-label-with-hover text-nowrap text-truncate px-1 mr-1 badge badge-secondary cursor-pointer components-grid-alert-undefined-0988cb349635341c67d91bfe3454d2b3178c443c\\"
|
||||
<span class=\\"components-label-with-hover text-nowrap text-truncate px-1 mr-1 badge badge-secondary cursor-pointer components-grid-alert-099c5ca6d1c92f615b13056b935d0c8dee70f18c-0988cb349635341c67d91bfe3454d2b3178c443c\\"
|
||||
data-toggle=\\"dropdown\\"
|
||||
>
|
||||
<svg aria-hidden=\\"true\\"
|
||||
|
||||
@@ -3,6 +3,8 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { APIAlert, APIGroup } from "Models/API";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { GetLabelColorClass } from "Common/Colors";
|
||||
import { StaticLabels } from "Common/Query";
|
||||
import { FilteringLabel } from "Components/Labels/FilteringLabel";
|
||||
@@ -15,12 +17,12 @@ import "./index.css";
|
||||
const Alert = observer(
|
||||
class Alert extends Component {
|
||||
static propTypes = {
|
||||
group: PropTypes.object.isRequired,
|
||||
alert: PropTypes.object.isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
alert: APIAlert.isRequired,
|
||||
showAlertmanagers: PropTypes.bool.isRequired,
|
||||
showReceiver: PropTypes.bool.isRequired,
|
||||
afterUpdate: PropTypes.func.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -8,7 +8,7 @@ import { advanceTo, clear } from "jest-date-mock";
|
||||
|
||||
import toDiffableHtml from "diffable-html";
|
||||
|
||||
import { MockAlert, MockAnnotation } from "__mocks__/Alerts.js";
|
||||
import { MockAlert, MockAnnotation, MockAlertGroup } from "__mocks__/Alerts.js";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Alert } from ".";
|
||||
@@ -41,12 +41,12 @@ const MockedAlert = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const MountedAlert = (alert, showAlertmanagers, showReceiver) => {
|
||||
const MountedAlert = (alert, group, showAlertmanagers, showReceiver) => {
|
||||
return mount(
|
||||
<Provider alertStore={alertStore}>
|
||||
<Alert
|
||||
alert={alert}
|
||||
group={{}}
|
||||
group={group}
|
||||
showAlertmanagers={showAlertmanagers}
|
||||
showReceiver={showReceiver}
|
||||
afterUpdate={MockAfterUpdate}
|
||||
@@ -59,13 +59,15 @@ const MountedAlert = (alert, showAlertmanagers, showReceiver) => {
|
||||
describe("<Alert />", () => {
|
||||
it("matches snapshot with showAlertmanagers=false showReceiver=false", () => {
|
||||
const alert = MockedAlert();
|
||||
const tree = MountedAlert(alert, false, false);
|
||||
const group = MockAlertGroup({}, [alert], [], {});
|
||||
const tree = MountedAlert(alert, group, false, false);
|
||||
expect(toDiffableHtml(tree.html())).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders @alertmanager label with showAlertmanagers=true", () => {
|
||||
const alert = MockedAlert();
|
||||
const tree = MountedAlert(alert, true, false);
|
||||
const group = MockAlertGroup({}, [alert], [], {});
|
||||
const tree = MountedAlert(alert, group, true, false);
|
||||
const label = tree
|
||||
.find("FilteringLabel")
|
||||
.filterWhere(elem => elem.props().name === "@alertmanager");
|
||||
@@ -74,7 +76,8 @@ describe("<Alert />", () => {
|
||||
|
||||
it("renders @receiver label with showReceiver=true", () => {
|
||||
const alert = MockedAlert();
|
||||
const tree = MountedAlert(alert, false, true);
|
||||
const group = MockAlertGroup({}, [alert], [], {});
|
||||
const tree = MountedAlert(alert, group, false, true);
|
||||
const label = tree
|
||||
.find("FilteringLabel")
|
||||
.filterWhere(elem => elem.props().name === "@receiver");
|
||||
@@ -84,7 +87,8 @@ describe("<Alert />", () => {
|
||||
it("renders a silence if alert is silenced", () => {
|
||||
const alert = MockedAlert();
|
||||
alert.alertmanager[0].silencedBy = ["silence123456789"];
|
||||
const tree = MountedAlert(alert, false, false);
|
||||
const group = MockAlertGroup({}, [alert], [], {});
|
||||
const tree = MountedAlert(alert, group, false, false);
|
||||
const silence = tree.find("Silence");
|
||||
expect(silence).toHaveLength(1);
|
||||
expect(silence.html()).toMatch(/silence123456789/);
|
||||
|
||||
@@ -11,13 +11,15 @@ import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalL
|
||||
import { faSearchPlus } from "@fortawesome/free-solid-svg-icons/faSearchPlus";
|
||||
import { faSearchMinus } from "@fortawesome/free-solid-svg-icons/faSearchMinus";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
|
||||
import "./index.css";
|
||||
|
||||
const RenderNonLinkAnnotation = inject("alertStore")(
|
||||
observer(
|
||||
class RenderNonLinkAnnotation extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
visible: PropTypes.bool.isRequired,
|
||||
|
||||
@@ -3,6 +3,7 @@ import PropTypes from "prop-types";
|
||||
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { StaticLabels } from "Common/Query";
|
||||
import { FilteringLabel } from "Components/Labels/FilteringLabel";
|
||||
import { RenderNonLinkAnnotation, RenderLinkAnnotation } from "../Annotation";
|
||||
@@ -10,7 +11,7 @@ import { RenderNonLinkAnnotation, RenderLinkAnnotation } from "../Annotation";
|
||||
const GroupFooter = observer(
|
||||
class GroupFooter extends Component {
|
||||
static propTypes = {
|
||||
group: PropTypes.object.isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
alertmanagers: PropTypes.arrayOf(PropTypes.string).isRequired,
|
||||
afterUpdate: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -14,7 +14,9 @@ import { faEllipsisV } from "@fortawesome/free-solid-svg-icons/faEllipsisV";
|
||||
import { faShareSquare } from "@fortawesome/free-solid-svg-icons/faShareSquare";
|
||||
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { FormatAPIFilterQuery } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { QueryOperators, StaticLabels, FormatQuery } from "Common/Query";
|
||||
import { DropdownSlide } from "Components/Animations/DropdownSlide";
|
||||
import { FetchPauser } from "Components/FetchPauser";
|
||||
@@ -76,15 +78,15 @@ MenuContent.propTypes = {
|
||||
popperPlacement: PropTypes.string,
|
||||
popperRef: PropTypes.func,
|
||||
popperStyle: PropTypes.object,
|
||||
group: PropTypes.object.isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
afterClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
const GroupMenu = observer(
|
||||
class GroupMenu extends Component {
|
||||
static propTypes = {
|
||||
group: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
group: APIGroup.isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
collapse = observable(
|
||||
|
||||
@@ -7,6 +7,8 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
|
||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { FilteringLabel } from "Components/Labels/FilteringLabel";
|
||||
import { FilteringCounterBadge } from "Components/Labels/FilteringCounterBadge";
|
||||
import { GroupMenu } from "./GroupMenu";
|
||||
@@ -14,9 +16,12 @@ import { GroupMenu } from "./GroupMenu";
|
||||
const GroupHeader = observer(
|
||||
class GroupHeader extends Component {
|
||||
static propTypes = {
|
||||
collapseStore: PropTypes.object.isRequired,
|
||||
group: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
collapseStore: PropTypes.shape({
|
||||
value: PropTypes.bool.isRequired,
|
||||
toggle: PropTypes.func.isRequired
|
||||
}).isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -14,6 +14,12 @@ import { faExternalLinkAlt } from "@fortawesome/free-solid-svg-icons/faExternalL
|
||||
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
|
||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||
|
||||
import {
|
||||
APIAlertAlertmanagerState,
|
||||
APIAlertmanagerUpstream,
|
||||
APISilence
|
||||
} from "Models/API";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { StaticLabels, QueryOperators } from "Common/Query";
|
||||
import { FilteringLabel } from "Components/Labels/FilteringLabel";
|
||||
|
||||
@@ -31,7 +37,7 @@ const SilenceComment = ({ silence }) => {
|
||||
return silence.comment;
|
||||
};
|
||||
SilenceComment.propTypes = {
|
||||
silence: PropTypes.object.isRequired
|
||||
silence: APISilence.isRequired
|
||||
};
|
||||
|
||||
const SilenceExpiryBadgeWithProgress = ({ silence, progress }) => {
|
||||
@@ -70,7 +76,7 @@ const SilenceExpiryBadgeWithProgress = ({ silence, progress }) => {
|
||||
);
|
||||
};
|
||||
SilenceExpiryBadgeWithProgress.propTypes = {
|
||||
silence: PropTypes.object.isRequired,
|
||||
silence: APISilence.isRequired,
|
||||
progress: PropTypes.number.isRequired
|
||||
};
|
||||
|
||||
@@ -118,8 +124,8 @@ const SilenceDetails = ({ alertmanager, silence }) => {
|
||||
);
|
||||
};
|
||||
SilenceDetails.propTypes = {
|
||||
alertmanager: PropTypes.object.isRequired,
|
||||
silence: PropTypes.object.isRequired
|
||||
alertmanager: APIAlertmanagerUpstream.isRequired,
|
||||
silence: APISilence.isRequired
|
||||
};
|
||||
|
||||
//
|
||||
@@ -141,8 +147,8 @@ const Silence = inject("alertStore")(
|
||||
observer(
|
||||
class Silence extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
alertmanagerState: PropTypes.object.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
alertmanagerState: APIAlertAlertmanagerState.isRequired,
|
||||
silenceID: PropTypes.string.isRequired,
|
||||
afterUpdate: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -16,6 +16,7 @@ const mockAfterUpdate = jest.fn();
|
||||
|
||||
const alertmanager = {
|
||||
name: "default",
|
||||
uri: "http://localhost",
|
||||
state: "suppressed",
|
||||
startsAt: "2000-01-01T10:00:00Z",
|
||||
endsAt: "0001-01-01T00:00:00Z",
|
||||
|
||||
@@ -10,6 +10,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faPlus } from "@fortawesome/free-solid-svg-icons/faPlus";
|
||||
import { faMinus } from "@fortawesome/free-solid-svg-icons/faMinus";
|
||||
|
||||
import { APIGroup } from "Models/API";
|
||||
import { MountFade } from "Components/Animations/MountFade";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
@@ -25,7 +26,7 @@ const LoadButton = ({ icon, action }) => {
|
||||
);
|
||||
};
|
||||
LoadButton.propTypes = {
|
||||
icon: PropTypes.object.isRequired,
|
||||
icon: FontAwesomeIcon.propTypes.icon.isRequired,
|
||||
action: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -42,7 +43,7 @@ const AlertGroup = observer(
|
||||
class AlertGroup extends Component {
|
||||
static propTypes = {
|
||||
afterUpdate: PropTypes.func.isRequired,
|
||||
group: PropTypes.object.isRequired,
|
||||
group: APIGroup.isRequired,
|
||||
showAlertmanagers: PropTypes.bool.isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
|
||||
@@ -36,7 +36,7 @@ beforeEach(() => {
|
||||
|
||||
const MockAlerts = alertCount => {
|
||||
for (let i = 1; i <= alertCount; i++) {
|
||||
let alert = MockAlert([], { instance: `instance${i}` });
|
||||
let alert = MockAlert([], { instance: `instance${i}` }, "active");
|
||||
const startsAt = moment().toISOString();
|
||||
alert.startsAt = startsAt;
|
||||
alert.alertmanager[0].startsAt = startsAt;
|
||||
|
||||
@@ -31,7 +31,7 @@ const ShallowAlertGrid = () => {
|
||||
const MockGroup = (groupName, alertCount) => {
|
||||
let alerts = [];
|
||||
for (let i = 1; i <= alertCount; i++) {
|
||||
alerts.push(MockAlert([], { instance: `instance${i}` }));
|
||||
alerts.push(MockAlert([], { instance: `instance${i}` }, "active"));
|
||||
}
|
||||
const group = MockAlertGroup(
|
||||
{ alertname: "Fake Alert", group: groupName },
|
||||
|
||||
@@ -7,7 +7,7 @@ import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
||||
import { faPauseCircle } from "@fortawesome/free-regular-svg-icons/faPauseCircle";
|
||||
|
||||
import { AlertStoreStatuses } from "Stores/AlertStore";
|
||||
import { AlertStore, AlertStoreStatuses } from "Stores/AlertStore";
|
||||
|
||||
const FetchIcon = ({ icon, color, visible, spin }) => (
|
||||
<FontAwesomeIcon
|
||||
@@ -19,7 +19,7 @@ const FetchIcon = ({ icon, color, visible, spin }) => (
|
||||
/>
|
||||
);
|
||||
FetchIcon.propTypes = {
|
||||
icon: PropTypes.object.isRequired,
|
||||
icon: FontAwesomeIcon.propTypes.icon.isRequired,
|
||||
color: PropTypes.string,
|
||||
visible: PropTypes.bool,
|
||||
spin: PropTypes.bool
|
||||
@@ -33,7 +33,7 @@ FetchIcon.defaultProps = {
|
||||
const FetchIndicator = observer(
|
||||
class FetchIndicator extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -54,7 +54,7 @@ const ActionButton = ({ color, icon, title, action, afterClick }) => (
|
||||
ActionButton.propTypes = {
|
||||
color: PropTypes.string.isRequired,
|
||||
title: PropTypes.node.isRequired,
|
||||
icon: PropTypes.object.isRequired,
|
||||
icon: FontAwesomeIcon.propTypes.icon.isRequired,
|
||||
action: PropTypes.func.isRequired,
|
||||
afterClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
@@ -6,6 +6,8 @@ import { observer } from "mobx-react";
|
||||
|
||||
import ReactSelect from "react-select";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { MultiSelect, ReactSelectStyles } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
|
||||
@@ -18,8 +20,8 @@ const AlertmanagerInstancesToOptions = instances =>
|
||||
const AlertManagerInput = observer(
|
||||
class AlertManagerInput extends MultiSelect {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
|
||||
@@ -21,7 +21,7 @@ const IconTd = ({ icon, onClick }) => (
|
||||
</td>
|
||||
);
|
||||
IconTd.propTypes = {
|
||||
icon: PropTypes.object.isRequired,
|
||||
icon: FontAwesomeIcon.propTypes.icon.isRequired,
|
||||
onClick: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import moment from "moment";
|
||||
|
||||
import DatePicker from "react-datepicker";
|
||||
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Duration } from "./Duration";
|
||||
import { HourMinute } from "./HourMinute";
|
||||
|
||||
@@ -161,13 +162,13 @@ const TabContentDuration = observer(({ silenceFormStore }) => {
|
||||
);
|
||||
});
|
||||
TabContentDuration.propTypes = {
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
const DateTimeSelect = observer(
|
||||
class DateTimeSelect extends Component {
|
||||
static propTypes = {
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
tab = observable(
|
||||
|
||||
@@ -4,6 +4,7 @@ import PropTypes from "prop-types";
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { SilenceFormMatcher } from "Models/SilenceForm";
|
||||
import { MultiSelect } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
import { FormatBackendURI } from "Stores/AlertStore";
|
||||
@@ -11,7 +12,7 @@ import { FormatBackendURI } from "Stores/AlertStore";
|
||||
const LabelNameInput = observer(
|
||||
class LabelNameInput extends MultiSelect {
|
||||
static propTypes = {
|
||||
matcher: PropTypes.object.isRequired,
|
||||
matcher: SilenceFormMatcher.isRequired,
|
||||
isValid: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -4,13 +4,14 @@ import PropTypes from "prop-types";
|
||||
import { action } from "mobx";
|
||||
import { observer } from "mobx-react";
|
||||
|
||||
import { SilenceFormMatcher } from "Models/SilenceForm";
|
||||
import { MultiSelect } from "Components/MultiSelect";
|
||||
import { ValidationError } from "Components/MultiSelect/ValidationError";
|
||||
|
||||
const LabelValueInput = observer(
|
||||
class LabelValueInput extends MultiSelect {
|
||||
static propTypes = {
|
||||
matcher: PropTypes.object.isRequired,
|
||||
matcher: SilenceFormMatcher.isRequired,
|
||||
isValid: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,9 @@ import { faSave } from "@fortawesome/free-regular-svg-icons/faSave";
|
||||
import { faChevronUp } from "@fortawesome/free-solid-svg-icons/faChevronUp";
|
||||
import { faChevronDown } from "@fortawesome/free-solid-svg-icons/faChevronDown";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { AlertManagerInput } from "./AlertManagerInput";
|
||||
import { SilenceMatch } from "./SilenceMatch";
|
||||
import { DateTimeSelect } from "./DateTimeSelect";
|
||||
@@ -45,7 +48,7 @@ const IconInput = ({
|
||||
IconInput.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
autoComplete: PropTypes.string.isRequired,
|
||||
icon: PropTypes.object.isRequired,
|
||||
icon: FontAwesomeIcon.propTypes.icon.isRequired,
|
||||
placeholder: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
onChange: PropTypes.func.isRequired
|
||||
@@ -54,9 +57,9 @@ IconInput.propTypes = {
|
||||
const SilenceForm = observer(
|
||||
class SilenceForm extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired,
|
||||
settingsStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
};
|
||||
|
||||
// store preview visibility state here, by default preview is collapsed
|
||||
|
||||
@@ -161,7 +161,7 @@ describe("<SilenceForm />", () => {
|
||||
it("calling submit marks form as in progress when form is valid", () => {
|
||||
const matcher = NewEmptyMatcher();
|
||||
matcher.name = "job";
|
||||
matcher.values = ["node_exporter"];
|
||||
matcher.values = [{ label: "node_exporter", value: "node_exporter" }];
|
||||
silenceFormStore.data.matchers = [matcher];
|
||||
silenceFormStore.data.alertmanagers = [
|
||||
{ label: "am1", value: "http://example.com" }
|
||||
|
||||
@@ -7,18 +7,14 @@ import { observer } from "mobx-react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faTrash } from "@fortawesome/free-solid-svg-icons/faTrash";
|
||||
|
||||
import { SilenceFormMatcher } from "Models/SilenceForm";
|
||||
import { LabelNameInput } from "./LabelNameInput";
|
||||
import { LabelValueInput } from "./LabelValueInput";
|
||||
|
||||
const SilenceMatch = observer(
|
||||
class SilenceMatch extends Component {
|
||||
static propTypes = {
|
||||
matcher: PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
values: PropTypes.array.isRequired,
|
||||
isRegex: PropTypes.bool.isRequired
|
||||
}),
|
||||
matcher: SilenceFormMatcher.isRequired,
|
||||
showDelete: PropTypes.bool.isRequired,
|
||||
onDelete: PropTypes.func.isRequired,
|
||||
isValid: PropTypes.bool.isRequired
|
||||
|
||||
@@ -6,15 +6,18 @@ import { observer } from "mobx-react";
|
||||
|
||||
import { disableBodyScroll, enableBodyScroll } from "body-scroll-lock";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { SilenceForm } from "./SilenceForm";
|
||||
import { SilenceSubmitController } from "./SilenceSubmitController";
|
||||
|
||||
const SilenceModalContent = observer(
|
||||
class SilenceModalContent extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired,
|
||||
settingsStore: PropTypes.object.isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired,
|
||||
onHide: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ import { observer } from "mobx-react";
|
||||
import JSONPretty from "react-json-pretty";
|
||||
import "react-json-pretty/src/JSONPretty.monikai.css";
|
||||
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
|
||||
const SilencePreview = observer(
|
||||
class SilencePreview extends Component {
|
||||
static propTypes = {
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -4,12 +4,14 @@ import PropTypes from "prop-types";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faArrowLeft } from "@fortawesome/free-solid-svg-icons/faArrowLeft";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { SilenceSubmitProgress } from "./SilenceSubmitProgress";
|
||||
|
||||
class SilenceSubmitController extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
|
||||
@@ -9,6 +9,9 @@ import { faCircleNotch } from "@fortawesome/free-solid-svg-icons/faCircleNotch";
|
||||
import { faCheckCircle } from "@fortawesome/free-regular-svg-icons/faCheckCircle";
|
||||
import { faExclamationCircle } from "@fortawesome/free-solid-svg-icons/faExclamationCircle";
|
||||
|
||||
import { APISilenceMatcher } from "Models/API";
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
|
||||
const SubmitState = Object.freeze({
|
||||
InProgress: "InProgress",
|
||||
Done: "Done",
|
||||
@@ -46,8 +49,14 @@ const SilenceSubmitProgress = observer(
|
||||
static propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
uri: PropTypes.string.isRequired,
|
||||
payload: PropTypes.object.isRequired,
|
||||
alertStore: PropTypes.object.isRequired
|
||||
payload: PropTypes.exact({
|
||||
matchers: PropTypes.arrayOf(APISilenceMatcher).isRequired,
|
||||
startsAt: PropTypes.string.isRequired,
|
||||
endsAt: PropTypes.string.isRequired,
|
||||
createdBy: PropTypes.string.isRequired,
|
||||
comment: PropTypes.string.isRequired
|
||||
}).isRequired,
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired
|
||||
};
|
||||
|
||||
submitState = observable(
|
||||
|
||||
@@ -28,7 +28,13 @@ const MountedSilenceSubmitProgress = () => {
|
||||
<SilenceSubmitProgress
|
||||
name="mockAlertmanager"
|
||||
uri="http://localhost/mock"
|
||||
payload={{ foo: "bar" }}
|
||||
payload={{
|
||||
matchers: [],
|
||||
startsAt: "now",
|
||||
endsAt: "later",
|
||||
createdBy: "me@example.com",
|
||||
comment: "fake payload"
|
||||
}}
|
||||
alertStore={alertStore}
|
||||
/>
|
||||
);
|
||||
@@ -52,7 +58,13 @@ describe("<SilenceSubmitProgress />", () => {
|
||||
expect(payload).toMatchObject({
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ foo: "bar" })
|
||||
body: JSON.stringify({
|
||||
matchers: [],
|
||||
startsAt: "now",
|
||||
endsAt: "later",
|
||||
createdBy: "me@example.com",
|
||||
comment: "fake payload"
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -6,6 +6,9 @@ import { observer } from "mobx-react";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import { faBellSlash } from "@fortawesome/free-solid-svg-icons/faBellSlash";
|
||||
|
||||
import { AlertStore } from "Stores/AlertStore";
|
||||
import { SilenceFormStore } from "Stores/SilenceFormStore";
|
||||
import { Settings } from "Stores/Settings";
|
||||
import { MountFade } from "Components/Animations/MountFade";
|
||||
import { SilenceModalContent } from "./SilenceModalContent";
|
||||
|
||||
@@ -14,9 +17,9 @@ import "./index.css";
|
||||
const SilenceModal = observer(
|
||||
class SilenceModal extends Component {
|
||||
static propTypes = {
|
||||
alertStore: PropTypes.object.isRequired,
|
||||
silenceFormStore: PropTypes.object.isRequired,
|
||||
settingsStore: PropTypes.object.isRequired
|
||||
alertStore: PropTypes.instanceOf(AlertStore).isRequired,
|
||||
silenceFormStore: PropTypes.instanceOf(SilenceFormStore).isRequired,
|
||||
settingsStore: PropTypes.instanceOf(Settings).isRequired
|
||||
};
|
||||
|
||||
toggleModal = () => {
|
||||
|
||||
82
ui/src/Models/API.js
Normal file
82
ui/src/Models/API.js
Normal file
@@ -0,0 +1,82 @@
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const AlertState = PropTypes.oneOf(["unprocessed", "active", "suppressed"]);
|
||||
|
||||
const Annotation = PropTypes.exact({
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
visible: PropTypes.bool.isRequired,
|
||||
isLink: PropTypes.bool.isRequired
|
||||
});
|
||||
|
||||
const APIAlertAlertmanagerState = PropTypes.exact({
|
||||
name: PropTypes.string.isRequired,
|
||||
uri: PropTypes.string.isRequired,
|
||||
state: AlertState.isRequired,
|
||||
startsAt: PropTypes.string.isRequired,
|
||||
endsAt: PropTypes.string.isRequired,
|
||||
source: PropTypes.string.isRequired,
|
||||
silencedBy: PropTypes.arrayOf(PropTypes.string).isRequired
|
||||
});
|
||||
|
||||
const APIAlert = PropTypes.exact({
|
||||
annotations: PropTypes.arrayOf(Annotation).isRequired,
|
||||
labels: PropTypes.object.isRequired,
|
||||
startsAt: PropTypes.string.isRequired,
|
||||
endsAt: PropTypes.string.isRequired,
|
||||
state: AlertState.isRequired,
|
||||
alertmanager: PropTypes.arrayOf(APIAlertAlertmanagerState).isRequired,
|
||||
receiver: PropTypes.string.isRequired
|
||||
});
|
||||
|
||||
const APIGroup = PropTypes.exact({
|
||||
receiver: PropTypes.string.isRequired,
|
||||
labels: PropTypes.object.isRequired,
|
||||
alerts: PropTypes.arrayOf(APIAlert),
|
||||
id: PropTypes.string.isRequired,
|
||||
hash: PropTypes.string.isRequired,
|
||||
alertmanagerCount: PropTypes.objectOf(PropTypes.number).isRequired,
|
||||
stateCount: PropTypes.exact({
|
||||
active: PropTypes.number.isRequired,
|
||||
suppressed: PropTypes.number.isRequired,
|
||||
unprocessed: PropTypes.number.isRequired
|
||||
}),
|
||||
shared: PropTypes.exact({
|
||||
annotations: PropTypes.arrayOf(Annotation).isRequired,
|
||||
labels: PropTypes.object.isRequired
|
||||
}).isRequired
|
||||
});
|
||||
|
||||
const APISilenceMatcher = PropTypes.exact({
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
isRegex: PropTypes.bool.isRequired
|
||||
});
|
||||
|
||||
const APISilence = PropTypes.exact({
|
||||
id: PropTypes.string.isRequired,
|
||||
matchers: PropTypes.arrayOf(APISilenceMatcher).isRequired,
|
||||
startsAt: PropTypes.string.isRequired,
|
||||
endsAt: PropTypes.string.isRequired,
|
||||
createdAt: PropTypes.string.isRequired,
|
||||
createdBy: PropTypes.string.isRequired,
|
||||
comment: PropTypes.string.isRequired,
|
||||
jiraID: PropTypes.string.isRequired,
|
||||
jiraURL: PropTypes.string.isRequired
|
||||
});
|
||||
|
||||
const APIAlertmanagerUpstream = PropTypes.exact({
|
||||
name: PropTypes.string.isRequired,
|
||||
uri: PropTypes.string.isRequired,
|
||||
publicURI: PropTypes.string.isRequired,
|
||||
error: PropTypes.string.isRequired
|
||||
});
|
||||
|
||||
export {
|
||||
APIAlert,
|
||||
APIGroup,
|
||||
APISilence,
|
||||
APISilenceMatcher,
|
||||
APIAlertAlertmanagerState,
|
||||
APIAlertmanagerUpstream
|
||||
};
|
||||
19
ui/src/Models/SilenceForm.js
Normal file
19
ui/src/Models/SilenceForm.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const SilenceFormSuggestion = PropTypes.shape({
|
||||
label: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired
|
||||
});
|
||||
|
||||
const SilenceFormMatcher = PropTypes.exact({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
values: PropTypes.arrayOf(SilenceFormSuggestion).isRequired,
|
||||
suggestions: PropTypes.exact({
|
||||
names: PropTypes.arrayOf(SilenceFormSuggestion).isRequired,
|
||||
values: PropTypes.arrayOf(SilenceFormSuggestion).isRequired
|
||||
}).isRequired,
|
||||
isRegex: PropTypes.bool.isRequired
|
||||
});
|
||||
|
||||
export { SilenceFormMatcher, SilenceFormSuggestion };
|
||||
Reference in New Issue
Block a user