Skip to content

Commit

Permalink
feat: merge from main
Browse files Browse the repository at this point in the history
  • Loading branch information
shahargl committed Sep 23, 2024
2 parents 8493436 + dda0ac7 commit 14eeb68
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 64 deletions.
19 changes: 18 additions & 1 deletion keep-ui/app/incidents/[id]/incident-timeline.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { useIncidentAlerts } from "utils/hooks/useIncidents";
import { IncidentDto } from "../models";
import Image from "next/image";
import AlertSeverity from "app/alerts/alert-severity";
import { EmptyStateCard } from "@/components/ui/EmptyStateCard";
import { useRouter } from "next/navigation";

const severityColors = {
critical: "bg-red-300",
Expand Down Expand Up @@ -237,6 +239,20 @@ const AlertBar: React.FC<AlertBarProps> = ({
);
};

const IncidentTimelineNoAlerts: React.FC = () => {
const router = useRouter();
return (
<div className="h-80">
<EmptyStateCard
title="Timeline not available"
description="No alerts found for this incident. Go to the alerts feed and assign alerts to view the timeline."
buttonText="Assign alerts to this incident"
onClick={() => router.push("/alerts/feed")}
/>
</div>
);
};

export default function IncidentTimeline({
incident,
}: {
Expand Down Expand Up @@ -313,7 +329,8 @@ export default function IncidentTimeline({
return {};
}, [auditEvents, alerts]);

if (auditEventsLoading || !auditEvents || alertsLoading) return <>No Data</>;
if (auditEventsLoading || !auditEvents || alertsLoading)
return <IncidentTimelineNoAlerts />;

const {
startTime,
Expand Down
12 changes: 11 additions & 1 deletion keep-ui/app/incidents/[id]/incident.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useRouter } from "next/navigation";
import IncidentTimeline from "./incident-timeline";
import { CiBellOn, CiViewTimeline } from "react-icons/ci";
import { IoIosGitNetwork } from "react-icons/io";
import { EmptyStateCard } from "@/components/ui/EmptyStateCard";

interface Props {
incidentId: string;
Expand Down Expand Up @@ -72,7 +73,16 @@ export default function IncidentView({ incidentId }: Props) {
<TabPanel>
<IncidentTimeline incident={incident} />
</TabPanel>
<TabPanel>Coming Soon...</TabPanel>
<TabPanel>
<div className="h-80">
<EmptyStateCard
title="Coming Soon..."
description="Topology view of the incident is coming soon."
buttonText="Go to Topology"
onClick={() => router.push("/topology")}
/>
</div>
</TabPanel>
</TabPanels>
</TabGroup>
</div>
Expand Down
60 changes: 29 additions & 31 deletions keep-ui/app/incidents/create-or-update-incident.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ import { useIncidents } from "utils/hooks/useIncidents";

interface Props {
incidentToEdit: IncidentDto | null;
createCallback?: (id: string) => void
exitCallback?: () => void
createCallback?: (id: string) => void;
exitCallback?: () => void;
}

export default function CreateOrUpdateIncident({
incidentToEdit,
createCallback,
exitCallback
exitCallback,
}: Props) {
const { data: session } = useSession();
const { mutate } = useIncidents(true, 20);
Expand All @@ -34,12 +34,18 @@ export default function CreateOrUpdateIncident({
const editMode = incidentToEdit !== null;

// Display cancel btn if editing or we need to cancel for another reason (eg. going one step back in the modal etc.)
const cancellable = editMode || exitCallback
const cancellable = editMode || exitCallback;

useEffect(() => {
if (incidentToEdit) {
setIncidentName(incidentToEdit.user_generated_name ?? incidentToEdit.ai_generated_name ?? "");
setIncidentUserSummary(incidentToEdit.user_summary ?? incidentToEdit.generated_summary ?? "" );
setIncidentName(
incidentToEdit.user_generated_name ??
incidentToEdit.ai_generated_name ??
""
);
setIncidentUserSummary(
incidentToEdit.user_summary ?? incidentToEdit.generated_summary ?? ""
);
setIncidentAssignee(incidentToEdit.assignee ?? "");
}
}, [incidentToEdit]);
Expand Down Expand Up @@ -70,8 +76,8 @@ export default function CreateOrUpdateIncident({
await mutate();
toast.success("Incident created successfully");

const created = await response.json()
createCallback?.(created.id) // close the modal and associate the alert incident
const created = await response.json();
createCallback?.(created.id); // close the modal and associate the alert incident
} else {
toast.error(
"Failed to create incident, please contact us if this issue persists."
Expand All @@ -83,21 +89,18 @@ export default function CreateOrUpdateIncident({
const updateIncident = async (e: FormEvent) => {
e.preventDefault();
const apiUrl = getApiURL();
const response = await fetch(
`${apiUrl}/incidents/${incidentToEdit?.id}`,
{
method: "PUT",
headers: {
Authorization: `Bearer ${session?.accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: incidentName,
user_summary: incidentUserSummary,
assignee: incidentAssignee,
}),
}
);
const response = await fetch(`${apiUrl}/incidents/${incidentToEdit?.id}`, {
method: "PUT",
headers: {
Authorization: `Bearer ${session?.accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
user_generated_name: incidentName,
user_summary: incidentUserSummary,
assignee: incidentAssignee,
}),
});
if (response.ok) {
exitEditMode();
await mutate();
Expand All @@ -111,21 +114,16 @@ export default function CreateOrUpdateIncident({

// If the Incident is successfully updated or the user cancels the update we exit the editMode and set the editRule in the incident.tsx to null.
const exitEditMode = () => {
exitCallback?.()
exitCallback?.();
clearForm();
};

const submitEnabled = (): boolean => {
return (
!!incidentName
);
return !!incidentName;
};

return (
<form
className="py-2"
onSubmit={editMode ? updateIncident : addIncident}
>
<form className="py-2" onSubmit={editMode ? updateIncident : addIncident}>
<Subtitle>Incident Metadata</Subtitle>
<div className="mt-2.5">
<Text>
Expand Down
2 changes: 1 addition & 1 deletion keep-ui/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
/// <reference types="next/navigation-types/compat/navigation" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
8 changes: 4 additions & 4 deletions keep-ui/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion keep-ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
"postcss-nested": "^6.0.1",
"postcss-selector-parser": "^6.0.12",
"postcss-value-parser": "^4.2.0",
"posthog-js": "^1.161.6",
"posthog-js": "^1.163.0",
"posthog-node": "^3.1.1",
"preact-render-to-string": "^5.2.6",
"prelude-ls": "^1.2.1",
Expand Down
13 changes: 3 additions & 10 deletions keep/api/core/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -2569,16 +2569,9 @@ def update_incident_from_dto_by_id(
if not incident:
return None

session.query(Incident).filter(
Incident.tenant_id == tenant_id,
Incident.id == incident_id,
).update(
{
"user_generated_name": updated_incident_dto.user_generated_name,
"user_summary": updated_incident_dto.user_summary,
"assignee": updated_incident_dto.assignee,
}
)
incident.user_generated_name = updated_incident_dto.user_generated_name
incident.user_summary = updated_incident_dto.user_summary
incident.assignee = updated_incident_dto.assignee

session.commit()
session.refresh(incident)
Expand Down
46 changes: 31 additions & 15 deletions keep/api/observability.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
import os
from urllib.parse import urlparse

from fastapi import FastAPI, Request
from opentelemetry import metrics, trace
Expand All @@ -22,37 +23,52 @@
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor

def get_protocol_from_endpoint(endpoint):
parsed_url = urlparse(endpoint)
if parsed_url.scheme == "http":
return HTTPOTLPSpanExporter
elif parsed_url.scheme == "grpc":
return GRPCOTLPSpanExporter
else:
raise ValueError(f"Unsupported protocol: {parsed_url.scheme}")

def setup(app: FastAPI):
logger = logging.getLogger(__name__)
# Configure the OpenTelemetry SDK
service_name = os.environ.get("SERVICE_NAME", "keep-api")
otlp_collector_endpoint = os.environ.get("OTLP_ENDPOINT", False)
service_name = os.environ.get("OTEL_SERVICE_NAME", os.environ.get("SERVICE_NAME", "keep-api"))
otlp_collector_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_ENDPOINT", os.environ.get("OTLP_ENDPOINT", False))
otlp_traces_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_TRACES_ENDPOINT", None)
otlp_logs_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_LOGS_ENDPOINT", None)
otlp_metrics_endpoint = os.environ.get("OTEL_EXPORTER_OTLP_METRICS_ENDPOINT", None)
enable_cloud_trace_exporeter = os.environ.get("CLOUD_TRACE_ENABLED", False)
metrics_enabled = os.environ.get("METRIC_OTEL_ENABLED", "")
# to support both grpc and http - for example dynatrace doesn't support grpc
http_or_grpc = os.environ.get("OTLP_SPAN_EXPORTER", "grpc")
if http_or_grpc == "grpc":
OTLPSpanExporter = GRPCOTLPSpanExporter
else:
OTLPSpanExporter = HTTPOTLPSpanExporter

resource = Resource.create({"service.name": service_name})
provider = TracerProvider(resource=resource)

if otlp_collector_endpoint:

logger.info(f"OTLP endpoint set to {otlp_collector_endpoint}")
processor = BatchSpanProcessor(
OTLPSpanExporter(endpoint=otlp_collector_endpoint)
)
provider.add_span_processor(processor)
if metrics_enabled.lower() == "true":
logger.info("Metrics enabled.")

if otlp_traces_endpoint:
logger.info(f"OTLP Traces endpoint set to {otlp_traces_endpoint}")
SpanExporter = get_protocol_from_endpoint(otlp_traces_endpoint)
processor = BatchSpanProcessor(
SpanExporter(endpoint=otlp_traces_endpoint)
)
provider.add_span_processor(processor)

if metrics_enabled.lower() == "true" and otlp_metrics_endpoint:
logger.info(f"Metrics enabled. OTLP Metrics endpoint set to {otlp_metrics_endpoint}")
reader = PeriodicExportingMetricReader(
OTLPMetricExporter(endpoint=otlp_collector_endpoint)
OTLPMetricExporter(endpoint=otlp_metrics_endpoint)
)
metric_provider = MeterProvider(resource=resource, metric_readers=[reader])
metrics.set_meter_provider(metric_provider)

if otlp_logs_endpoint:
logger.info(f"OTLP Logs endpoint set to {otlp_logs_endpoint}")

if enable_cloud_trace_exporeter:
logger.info("Cloud Trace exporter enabled.")
processor = BatchSpanProcessor(CloudTraceSpanExporter())
Expand Down

0 comments on commit 14eeb68

Please sign in to comment.