Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: showErrorToast function, KeepApiReadOnlyError, read-only friendly errors #2653

Merged
merged 7 commits into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 47 additions & 48 deletions keep-ui/app/(keep)/ai/ai.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import { Card, Title, Subtitle } from "@tremor/react";
import { useAIStats, UseAIActions } from "utils/hooks/useAI";
import { toast } from "react-toastify";
import { useEffect, useState, useMemo } from "react";
import { useEffect, useMemo, useState } from "react";
import Image from "next/image";
import debounce from "lodash.debounce";

Expand All @@ -29,7 +29,7 @@ function RangeInputWithLabel({
};
}, [debouncedOnChange]);

return (
return (
<div>
<p>Value: {value}</p>
<input
Expand All @@ -40,7 +40,10 @@ function RangeInputWithLabel({
max={setting.max}
value={value}
onChange={(e) => {
const newValue = setting.type === "float" ? parseFloat(e.target.value) : parseInt(e.target.value, 10);
const newValue =
setting.type === "float"
? parseFloat(e.target.value)
: parseInt(e.target.value, 10);
setValue(newValue);
debouncedOnChange(newValue);
}}
Expand All @@ -67,9 +70,7 @@ export default function Ai() {
<div className="flex justify-between items-center">
<div>
<Title>AI Plugins</Title>
<Subtitle>
For correlation, summarization, and enrichment
</Subtitle>
<Subtitle>For correlation, summarization, and enrichment</Subtitle>
</div>
</div>
<Card className="mt-10 p-4 md:p-10 mx-auto">
Expand All @@ -91,17 +92,17 @@ export default function Ai() {
<div>
<Title>No AI enabled for this tenant</Title>
<p className="pt-2">
AI plugins can correlate, enrich, or summarize
your alerts and incidents by leveraging the
the context within Keep allowing
you to gain deeper insights and respond more effectively.
AI plugins can correlate, enrich, or summarize your alerts
and incidents by leveraging the the context within Keep
allowing you to gain deeper insights and respond more
effectively.
</p>
<p className="pt-2">
By the way, AI plugins are designed to work even in air-gapped environments.
You can train models using your data, so
there is no need to share information with third-party
providers like OpenAI. Keep your data secure and
private.
By the way, AI plugins are designed to work even in
air-gapped environments. You can train models using your
data, so there is no need to share information with
third-party providers like OpenAI. Keep your data secure
and private.
</p>
<p className="pt-2">
<a
Expand Down Expand Up @@ -155,40 +156,38 @@ export default function Ai() {
/>
) : null}
{setting.type === "float" ? (

<RangeInputWithLabel
key={setting.value}
setting={setting}
onChange={(newValue) => {
setting.value = newValue;
algorithm_config.settings_proposed_by_algorithm =
null;
updateAISettings(
algorithm_config.algorithm_id,
algorithm_config
);
toast.success("Settings updated successfully!");
refetchAIStats();
}}
/>

<RangeInputWithLabel
key={setting.value}
setting={setting}
onChange={(newValue) => {
setting.value = newValue;
algorithm_config.settings_proposed_by_algorithm =
null;
updateAISettings(
algorithm_config.algorithm_id,
algorithm_config
);
toast.success("Settings updated successfully!");
refetchAIStats();
}}
/>
) : null}
{setting.type === "int" ? (
<RangeInputWithLabel
key={setting.value}
setting={setting}
onChange={(newValue) => {
setting.value = newValue;
algorithm_config.settings_proposed_by_algorithm =
null;
updateAISettings(
algorithm_config.algorithm_id,
algorithm_config
);
toast.success("Settings updated successfully!");
refetchAIStats();
}}
/>
<RangeInputWithLabel
key={setting.value}
setting={setting}
onChange={(newValue) => {
setting.value = newValue;
algorithm_config.settings_proposed_by_algorithm =
null;
updateAISettings(
algorithm_config.algorithm_id,
algorithm_config
);
toast.success("Settings updated successfully!");
refetchAIStats();
}}
/>
) : null}
</div>
))}
Expand Down Expand Up @@ -253,4 +252,4 @@ export default function Ai() {
</Card>
</main>
);
}
}
71 changes: 28 additions & 43 deletions keep-ui/app/(keep)/alerts/EnrichAlertSidePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {AlertDto} from "./models";
import {Dialog, Transition} from "@headlessui/react";
import React, {Fragment, useEffect, useState} from "react";
import {Button, TextInput} from "@tremor/react";
import {useSession} from "next-auth/react";
import {useApiUrl} from "utils/hooks/useConfig";
import {toast} from "react-toastify";
import SidePanel from "@/components/SidePanel"
import { AlertDto } from "./models";
import { Dialog, Transition } from "@headlessui/react";
import React, { Fragment, useEffect, useState } from "react";
import { Button, TextInput } from "@tremor/react";
import { toast } from "react-toastify";
import SidePanel from "@/components/SidePanel";
import { showErrorToast } from "@/shared/ui/utils/showErrorToast";
import { useApi } from "@/shared/lib/hooks/useApi";

interface EnrichAlertModalProps {
alert: AlertDto | null | undefined;
Expand All @@ -15,13 +15,12 @@ interface EnrichAlertModalProps {
}

const EnrichAlertSidePanel: React.FC<EnrichAlertModalProps> = ({
alert,
isOpen,
handleClose,
mutate,
}) => {
const { data: session } = useSession();
const apiUrl = useApiUrl();
alert,
isOpen,
handleClose,
mutate,
}) => {
const api = useApi();

const [customFields, setCustomFields] = useState<
{ key: string; value: string }[]
Expand All @@ -41,10 +40,10 @@ const EnrichAlertSidePanel: React.FC<EnrichAlertModalProps> = ({
const updateCustomField = (
index: number,
field: "key" | "value",
value: string,
value: string
) => {
setCustomFields((prev) =>
prev.map((item, i) => (i === index ? { ...item, [field]: value } : item)),
prev.map((item, i) => (i === index ? { ...item, [field]: value } : item))
);
};

Expand All @@ -67,7 +66,7 @@ const EnrichAlertSidePanel: React.FC<EnrichAlertModalProps> = ({
customFields.length === preEnrichedFields.length &&
customFields.every((field) => {
const matchingField = preEnrichedFields.find(
(preField) => preField.key === field.key,
(preField) => preField.key === field.key
);
return matchingField && matchingField.value === field.value;
});
Expand All @@ -92,7 +91,7 @@ const EnrichAlertSidePanel: React.FC<EnrichAlertModalProps> = ({
}
return acc;
},
{} as Record<string, string>,
{} as Record<string, string>
);
};
setFinalData(calculateFinalData());
Expand Down Expand Up @@ -121,38 +120,24 @@ const EnrichAlertSidePanel: React.FC<EnrichAlertModalProps> = ({
}
});

let fieldsUnEnrichedSuccessfully = true;
let fieldsUnEnrichedSuccessfully = unEnrichedFields.length === 0;

if (unEnrichedFields.length != 0) {
const unEnrichmentResponse = await fetch(`${apiUrl}/alerts/unenrich`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session?.accessToken}`,
},
body: JSON.stringify({
try {
if (unEnrichedFields.length != 0) {
const unEnrichmentResponse = await api.post("/alerts/unenrich", {
fingerprint: alert?.fingerprint,
enrichments: unEnrichedFields,
}),
});
fieldsUnEnrichedSuccessfully = unEnrichmentResponse.ok;
}
});
fieldsUnEnrichedSuccessfully = true;
}

const response = await fetch(`${apiUrl}/alerts/enrich`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${session?.accessToken}`,
},
body: JSON.stringify(requestData),
});
const response = await api.post("/alerts/enrich", requestData);

if (response.ok && fieldsUnEnrichedSuccessfully) {
toast.success("Alert enriched successfully");
await mutate();
handleClose();
} else {
toast.error("Failed to enrich alert");
} catch (error) {
showErrorToast(error, "Failed to enrich alert");
}
};

Expand Down
11 changes: 3 additions & 8 deletions keep-ui/app/(keep)/alerts/ViewAlertModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { XMarkIcon } from "@heroicons/react/24/outline";
import "./ViewAlertModal.css";
import React, { useState } from "react";
import { useApi } from "@/shared/lib/hooks/useApi";
import { KeepApiError } from "@/shared/lib/api/KeepApiError";
import { showErrorToast } from "@/shared/ui/utils/showErrorToast";

interface ViewAlertModalProps {
alert: AlertDto | null | undefined;
Expand Down Expand Up @@ -38,12 +38,7 @@ export const ViewAlertModal: React.FC<ViewAlertModalProps> = ({

toast.success(`${key} un-enriched successfully!`);
} catch (error) {
if (error instanceof KeepApiError) {
toast.error(error.message || `Failed to unenrich ${key}`);
} else {
// Handle unexpected error
toast.error(`Failed to unenrich ${key}: Unexpected error occurred`);
}
showErrorToast(error, `Failed to unenrich ${key}`);
} finally {
await mutate();
}
Expand Down Expand Up @@ -95,7 +90,7 @@ export const ViewAlertModal: React.FC<ViewAlertModalProps> = ({
await navigator.clipboard.writeText(JSON.stringify(alert, null, 2));
toast.success("Alert copied to clipboard!");
} catch (err) {
toast.error("Failed to copy alert.");
showErrorToast(err, "Failed to copy alert.");
}
}
};
Expand Down
4 changes: 3 additions & 1 deletion keep-ui/app/(keep)/alerts/alert-associate-incident-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import Loading from "@/app/(keep)/loading";
import { AlertDto } from "./models";
import { getIncidentName } from "@/entities/incidents/lib/utils";
import { useApi } from "@/shared/lib/hooks/useApi";
import { showErrorToast } from "@/shared/ui/utils/showErrorToast";

interface AlertAssociateIncidentModalProps {
isOpen: boolean;
Expand Down Expand Up @@ -47,7 +48,8 @@ const AlertAssociateIncidentModal = ({
await mutate();
toast.success("Alerts associated with incident successfully");
} catch (error) {
toast.error(
showErrorToast(
error,
"Failed to associated alerts with incident, please contact us if this issue persists."
);
}
Expand Down
5 changes: 3 additions & 2 deletions keep-ui/app/(keep)/alerts/alert-change-status-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
import { usePresets } from "utils/hooks/usePresets";
import { useAlerts } from "utils/hooks/useAlerts";
import { useApi } from "@/shared/lib/hooks/useApi";
import { showErrorToast } from "@/shared/ui/utils/showErrorToast";

const statusIcons = {
[Status.Firing]: <ExclamationCircleIcon className="w-4 h-4 mr-2" />,
Expand Down Expand Up @@ -103,7 +104,7 @@ export default function AlertChangeStatusModal({

const handleChangeStatus = async () => {
if (!selectedStatus) {
toast.error("Please select a new status.");
showErrorToast(new Error("Please select a new status."));
return;
}

Expand All @@ -127,7 +128,7 @@ export default function AlertChangeStatusModal({
await alertsMutator();
await presetsMutator();
} catch (error) {
toast.error("Failed to change alert status.");
showErrorToast(error, "Failed to change alert status.");
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import IncidentCard from "./alert-create-incident-ai-card";
import { useIncidents } from "utils/hooks/useIncidents";
import { useRouter } from "next/navigation";
import { useApi } from "@/shared/lib/hooks/useApi";
import { KeepApiError } from "@/shared/lib/api/KeepApiError";
import { KeepApiError } from "@/shared/api";

interface CreateIncidentWithAIModalProps {
isOpen: boolean;
Expand Down
8 changes: 2 additions & 6 deletions keep-ui/app/(keep)/alerts/alert-dismiss-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const ReactQuill =
typeof window === "object" ? require("react-quill") : () => false;
import "./alert-dismiss-modal.css";
import { useApi } from "@/shared/lib/hooks/useApi";
import { KeepApiError } from "@/shared/lib/api/KeepApiError";
import { showErrorToast } from "@/shared/ui/utils/showErrorToast";

interface Props {
preset: string;
Expand Down Expand Up @@ -107,11 +107,7 @@ export default function AlertDismissModal({
await alertsMutator();
await presetsMutator();
} catch (error) {
if (error instanceof KeepApiError) {
toast.error(error.message || "Failed to dismiss alerts");
} else {
toast.error("An unexpected error occurred");
}
showErrorToast(error, "Failed to dismiss alerts");
} finally {
clearAndClose();
}
Expand Down
Loading
Loading