Skip to content

Commit

Permalink
feat: add notification filter
Browse files Browse the repository at this point in the history
  • Loading branch information
moshloop committed Nov 11, 2024
1 parent c486d9d commit 4b67028
Show file tree
Hide file tree
Showing 7 changed files with 165 additions and 225 deletions.
2 changes: 2 additions & 0 deletions src/api/types/notifications.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export type NotificationRules = {

export type SilenceNotificationResponse = {
id: string;
filter?: string;
component_id?: string;
config_id?: string;
check_id?: string;
Expand Down Expand Up @@ -85,6 +86,7 @@ export type NotificationSilenceItem = {
id: string;
namespace: string;
description?: string;
filter?: string;
from: string;
until: string;
recursive?: boolean;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ export default function EditNotificationSilenceModal({
onUpdate = () => {}
}: EditNotificationSilenceModalProps) {
return (
<Modal
open={isOpen}
onClose={onClose}
title="Edit Notification Silence"
size="medium"
>
<Modal open={isOpen} onClose={onClose} title="Edit Silence" size="medium">
<div className="flex flex-col gap-4">
<NotificationSilenceForm
data={data}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FormikResourceSelectorDropdown from "@flanksource-ui/components/Forms/Formik/FormikResourceSelectorDropdown";
import { Label } from "@flanksource-ui/ui/FormControls/Label";
import { Switch } from "@flanksource-ui/ui/FormControls/Switch";
import { useFormikContext } from "formik";
import { useMemo, useState } from "react";
Expand Down Expand Up @@ -56,7 +57,7 @@ export default function FormikNotificationResourceField() {

return (
<div className="flex flex-col gap-2">
<label className={`form-label`}>Resource</label>
<Label label="Resource" required={true} />
<div>
<div className="flex w-full flex-row">
<Switch
Expand Down Expand Up @@ -96,16 +97,9 @@ export default function FormikNotificationResourceField() {
</div>

<FormikResourceSelectorDropdown
required
name={fieldName.name}
checkResourceSelector={
switchOption === "Check"
? [
{
id: check_id
}
]
: undefined
switchOption === "Check" ? [{ id: check_id }] : undefined
}
componentResourceSelector={
switchOption === "Component"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import FormikTextArea from "@flanksource-ui/components/Forms/Formik/FormikTextAr
import FormikNotificationResourceField from "@flanksource-ui/components/Notifications/SilenceNotificationForm/FormikNotificationField";
import { toastError } from "@flanksource-ui/components/Toast/toast";
import { Button } from "@flanksource-ui/ui/Buttons/Button";
import DeleteButton from "@flanksource-ui/ui/Buttons/DeleteButton";
import { parseDateMath } from "@flanksource-ui/ui/Dates/TimeRangePicker/parseDateMath";
import ErrorMessage from "@flanksource-ui/ui/FormControls/ErrorMessage";
import { useMutation } from "@tanstack/react-query";
import { AxiosError } from "axios";
import { Form, Formik } from "formik";
import { Formik, Form, FormikBag } from "formik";
import { FaCircleNotch } from "react-icons/fa";
import { useSearchParams } from "react-router-dom";

Expand Down Expand Up @@ -53,6 +55,7 @@ export default function NotificationSilenceForm({
if (data.id) {
return updateNotificationSilence({
id: data.id,
filter: data.filter,
updated_at: "now()",
source: data.source,
canary_id: data.canary_id,
Expand All @@ -68,12 +71,7 @@ export default function NotificationSilenceForm({
}
return silenceNotification(data);
},
onSuccess,
onError: (error: AxiosError) => {
// do something
console.error(error);
toastError(error.message);
}
onSuccess
});

const { mutate: deleteSilence, isLoading: isDeleting } = useMutation({
Expand All @@ -89,78 +87,135 @@ export default function NotificationSilenceForm({
}
});

const validate = (v: Partial<SilenceNotificationRequest>) => {
const errors: { [key: string]: string } = {};
if (
v.canary_id == null &&
v.check_id == null &&
v.component_id == null &&
v.config_id == null &&
v.filter == null
) {
errors.form = "Must specify either a resource and/or a filter";
}
if (v.until == null) {
errors.until = "Must specify a silence duration";
}
return errors;
};

const submit = (
v: Partial<SilenceNotificationRequest>,
formik: FormikBag
) => {
// Before submitting, we need to parse the date math expressions, if
// any are present in the from and until fields.
const { from, until } = v;
const fromTime = from?.includes("now") ? parseDateMath(from, false) : from;

const untilTime = until?.includes("now")
? parseDateMath(until, false)
: until;

return mutate(
{
...v,
from: fromTime,
until: untilTime
} as SilenceNotificationRequest,
{
onError(error) {
// @ts-ignore
formik.setErrors({ form: error });
}
}
);
};

return (
// @ts-ignore
<div className="flex flex-1 flex-col gap-2 overflow-auto">
<Formik<Partial<SilenceNotificationRequest>>
initialValues={initialValues}
onSubmit={(v) => {
// Before submitting, we need to parse the date math expressions, if
// any are present in the from and until fields.
const { from, until } = v;
const fromTime = from?.includes("now")
? parseDateMath(from, false)
: from;

const untilTime = until?.includes("now")
? parseDateMath(until, false)
: until;

return mutate({
...v,
from: fromTime,
until: untilTime
} as SilenceNotificationRequest);
}}
validateOnChange={false}
validateOnBlur={true}
validate={validate}
onSubmit={submit}
>
<Form className="flex flex-1 flex-col gap-2 overflow-y-auto">
<div
className={`flex flex-col gap-2 overflow-y-auto p-4 ${data?.id ? "flex-1" : ""}`}
>
<FormikNotificationResourceField />
<FormikCheckbox name="recursive" label="Recursive" />
<FormikDurationPicker
fieldNames={{
from: "from",
to: "until"
}}
label="Silence Duration"
/>
<FormikTextArea name="description" label="Reason" />
</div>
<div className={`${footerClassName}`}>
{data?.id && (
<>
<Button
onClick={() => {
onCancel();
{({ errors }) => {
return (
<Form className="flex flex-1 flex-col gap-2 overflow-y-auto">
<div
className={`flex flex-col gap-2 overflow-y-auto p-4 ${data?.id ? "flex-1" : ""}`}
>
<FormikNotificationResourceField />

<FormikCheckbox
checkboxStyle="toggle"
name="recursive"
label="Recursive"
hint="When selected, the silence will apply to all children of the item"
/>

<FormikDurationPicker
required
hint="Duration for which the silence will apply for, after which notifications will begin firing again"
fieldNames={{
from: "from",
to: "until"
}}
className="btn-secondary"
>
Cancel
</Button>
<div className="flex-1" />
<Button
onClick={() => deleteSilence(data.id)}
className="btn-danger"
label="Duration"
/>

<FormikTextArea
name="filter"
label="Filter"
hint="CEL expression for the silence to match against"
/>

<FormikTextArea name="description" label="Reason" />
<ErrorMessage
message={
// @ts-ignore
errors.form
}
/>
</div>
<div className={`${footerClassName}`}>
{data?.id && (
<>
<DeleteButton
title="Delete Silence"
description="Are you sure you want to delete this silence?"
yesLabel="Delete"
onConfirm={() => deleteSilence(data.id)}
/>

<div className="flex-1" />

<Button
onClick={() => {
onCancel();
}}
className="btn-secondary"
>
Cancel
</Button>
</>
)}

<button
type="submit"
className="btn btn-primary"
disabled={isLoading}
>
{isDeleting && (
<FaCircleNotch className="mr-2 animate-spin" />
)}
{isDeleting ? "Deleting" : "Delete"}
</Button>
</>
)}

<button
type="submit"
className="btn btn-primary"
disabled={isLoading}
>
{isLoading && <FaCircleNotch className="mr-2 animate-spin" />}
{data?.id ? "Update" : "Submit"}
</button>
</div>
</Form>
{isLoading && <FaCircleNotch className="mr-2 animate-spin" />}
{data?.id ? "Update" : "Submit"}
</button>
</div>
</Form>
);
}}
</Formik>
</div>
);
Expand Down
Loading

0 comments on commit 4b67028

Please sign in to comment.