Skip to content

Commit

Permalink
feat(api): enrichments - Mapping (#840)
Browse files Browse the repository at this point in the history
Signed-off-by: asharonbaltazar <[email protected]>
Signed-off-by: talboren <[email protected]>
Signed-off-by: talboren <[email protected]>
Co-authored-by: asharonbaltazar <[email protected]>
Co-authored-by: asharonbaltazar <[email protected]>
  • Loading branch information
3 people authored Feb 20, 2024
1 parent 8df72c3 commit a2d2baa
Show file tree
Hide file tree
Showing 23 changed files with 815 additions and 50 deletions.
Binary file added docs/images/rule-creation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/images/rule-table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
12 changes: 6 additions & 6 deletions docs/mint.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@
"overview/keyconcepts",
"overview/usecases",
"overview/ruleengine",
{
"group": "Enrichments",
"pages": ["overview/enrichment/mapping"]
},
"overview/examples",
"overview/comparison"
]
Expand Down Expand Up @@ -168,7 +172,7 @@
"pages": ["workflows/examples/multi-step-alert"]
},
"workflows/state"
]
]
},
{
"group": "Keep API",
Expand Down Expand Up @@ -246,18 +250,14 @@
"cli/commands/workflow-run",
{
"group": "keep workflow runs",
"pages": [
"cli/commands/runs-logs",
"cli/commands/runs-list"
]
"pages": ["cli/commands/runs-logs", "cli/commands/runs-list"]
}
]
},
"cli/commands/cli-api",
"cli/commands/cli-config",
"cli/commands/cli-version",
"cli/commands/cli-whoami"

]
}
]
Expand Down
50 changes: 50 additions & 0 deletions docs/overview/enrichment/mapping.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
---
title: "Alert Enrichment - Mapping"
---

# Alert Mapping and Enrichment

Keep's Alert Mapping and Enrichment feature provides a powerful mechanism for dynamically enhancing alert data by leveraging external data sources, such as CSV files. This feature allows for the matching of incoming alerts to specific records in a CSV file based on predefined attributes (matchers) and enriching those alerts with additional information from the matched records.

## Introduction

In complex monitoring environments, the need to enrich alert data with additional context is critical for effective alert analysis and response. Keep's Alert Mapping and Enrichment enables users to define rules that match alerts to rows in a CSV file, appending or modifying alert attributes with the values from matching rows. This process adds significant value to each alert, providing deeper insights and enabling more precise and informed decision-making.

## How It Works

1. **Rule Definition**: Users define mapping rules that specify which alert attributes (matchers) should be used for matching alerts to rows in a CSV file.
2. **CSV File Specification**: A CSV file is associated with each mapping rule. This file contains additional data that should be added to alerts matching the rule.
3. **Alert Matching**: When an alert is received, the system checks if it matches the conditions of any mapping rule based on the specified matchers.
4. **Data Enrichment**: If a match is found, the alert is enriched with additional data from the corresponding row in the CSV file.

## Core Concepts

- **Matchers**: Attributes within the alert used to identify matching rows within the CSV file. Common matchers include identifiers like `service` or `region`.
- **CSV File**: A structured file containing rows of data. Each column represents a potential attribute that can be added to an alert.
- **Enrichment**: The process of adding new attributes or modifying existing ones in an alert based on the data from a matching CSV row.

## Creating a Mapping Rule

To create an alert mapping and enrichment rule:

<Frame width="100" height="200">
<img height="10" src="/images/rule-creation.png" />
</Frame>

1. **Define the Matchers**: Specify which alert attributes will be used to match rows in the CSV file.
2. **Upload the CSV File**: Provide the CSV file containing the data for enrichment.
3. **Configure the Rule**: Set additional parameters, such as whether the rule should override existing alert attributes.

## Practical Example

Imagine you have a CSV file with columns representing different aspects of your infrastructure, such as `region`, `responsible_team`, and `severity_override`. By creating a mapping rule that matches alerts based on `service` and `region`, you can automatically enrich alerts with the responsible team and adjust severity based on the matched row in the CSV file.

## Best Practices

- **Keep CSV Files Updated**: Regularly update the CSV files to reflect the current state of your infrastructure and operational data.
- **Use Specific Matchers**: Define matchers that are unique and relevant to ensure accurate matching.
- **Monitor Rule Performance**: Review the application of mapping rules to ensure they are working as expected and adjust them as necessary.

<Frame width="100" height="200">
<img height="10" src="/images/rule-table.png" />
</Frame>
193 changes: 193 additions & 0 deletions keep-ui/app/mapping/create-new-mapping.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
"use client";

import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
import {
TextInput,
Textarea,
Divider,
Subtitle,
Text,
MultiSelect,
MultiSelectItem,
Badge,
Button,
} from "@tremor/react";
import { useSession } from "next-auth/react";
import { ChangeEvent, FormEvent, useMemo, useState } from "react";
import { usePapaParse } from "react-papaparse";
import { toast } from "react-toastify";
import { getApiURL } from "utils/apiUrl";
import { useMappings } from "utils/hooks/useMappingRules";

export default function CreateNewMapping() {
const { data: session } = useSession();
const { mutate } = useMappings();
const [mapName, setMapName] = useState<string>("");
const [fileName, setFileName] = useState<string>("");
const [mapDescription, setMapDescription] = useState<string>("");
const [selectedAttributes, setSelectedAttributes] = useState<string[]>([]);

/** This is everything related with the uploaded CSV file */
const [parsedData, setParsedData] = useState<any[] | null>(null);
const attributes = useMemo(() => {
if (parsedData) {
return Object.keys(parsedData[0]);
}
return [];
}, [parsedData]);
const { readString } = usePapaParse();

const readFile = (event: ChangeEvent<HTMLInputElement>) => {
const file = event.target.files?.[0];
setFileName(file?.name || "");
const reader = new FileReader();
reader.onload = (e) => {
const text = e.target?.result;
if (typeof text === "string") {
readString(text, {
header: true,
complete: (results) => {
if (results.data.length > 0) setParsedData(results.data);
},
});
}
};
if (file) reader.readAsText(file);
};

const clearForm = () => {
setMapName("");
setMapDescription("");
setParsedData(null);
setSelectedAttributes([]);
};

const addRule = async (e: FormEvent) => {
e.preventDefault();
const apiUrl = getApiURL();
const response = await fetch(`${apiUrl}/mapping`, {
method: "POST",
headers: {
Authorization: `Bearer ${session?.accessToken}`,
"Content-Type": "application/json",
},
body: JSON.stringify({
name: mapName,
description: mapDescription,
file_name: fileName,
matchers: selectedAttributes,
rows: parsedData,
}),
});
if (response.ok) {
clearForm();
mutate();
toast.success("Mapping created successfully");
} else {
toast.error(
"Failed to create mapping, please contact us if this issue persists."
);
}
};

const submitEnabled = (): boolean => {
return (
!!mapName &&
selectedAttributes.length > 0 &&
!!parsedData &&
attributes.filter(
(attribute) => selectedAttributes.includes(attribute) === false
).length > 0
);
};

return (
<form className="max-w-lg py-2" onSubmit={addRule}>
<Subtitle>Mapping Metadata</Subtitle>
<div className="mt-2.5">
<Text>
Name<span className="text-red-500 text-xs">*</span>
</Text>
<TextInput
placeholder="Map Name"
required={true}
value={mapName}
onValueChange={setMapName}
/>
</div>
<div className="mt-2.5">
<Text>Description</Text>
<Textarea
placeholder="Map Description"
value={mapDescription}
onValueChange={setMapDescription}
/>
</div>
<Divider />
<div>
<input
type="file"
accept=".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"
onChange={readFile}
required={true}
/>
{!parsedData && (
<Text className="text-xs text-red-500">
* Upload a CSV file to begin with creating a new mapping
</Text>
)}
</div>
<Subtitle className="mt-2.5">Mapping Schema</Subtitle>
<div className="mt-2.5">
<Text>Alert lookup attributes to match against the uploaded CSV</Text>
<Text className="text-xs">
(E.g. the attributes that we will try to match before enriching)
</Text>
<MultiSelect
className="mt-1"
value={selectedAttributes}
onValueChange={setSelectedAttributes}
disabled={!parsedData}
icon={MagnifyingGlassIcon}
>
{attributes &&
attributes.map((attribute) => (
<MultiSelectItem key={attribute} value={attribute}>
{attribute}
</MultiSelectItem>
))}
</MultiSelect>
</div>
<div className="mt-2.5">
<Text>Result attributes</Text>
<Text className="text-xs">
(E.g. attributes that will be added to matching incoming alerts)
</Text>
<div className="flex flex-col gap-1 py-1">
{selectedAttributes.length === 0 ? (
<Badge color="gray">...</Badge>
) : (
attributes
.filter(
(attribute) => selectedAttributes.includes(attribute) === false
)
.map((attribute) => (
<Badge key={attribute} color="orange">
{attribute}
</Badge>
))
)}
</div>
</div>
<Button
disabled={!submitEnabled()}
color="orange"
size="xs"
className="float-right"
type="submit"
>
Create
</Button>
</form>
);
}
5 changes: 5 additions & 0 deletions keep-ui/app/mapping/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { Title, Subtitle } from "@tremor/react";

export default function Layout({ children }: { children: any }) {
return <main className="p-4 md:p-10 mx-auto max-w-full">{children}</main>;
}
51 changes: 51 additions & 0 deletions keep-ui/app/mapping/mapping.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"use client";
import { Badge, Callout, Card } from "@tremor/react";
import CreateNewMapping from "./create-new-mapping";
import { useMappings } from "utils/hooks/useMappingRules";
import RulesTable from "./rules-table";
import { MdWarning } from "react-icons/md";
import Loading from "app/loading";
export default function Mapping() {
const { data: mappings, isLoading } = useMappings();

return (
<Card className="p-4 md:p-10 mx-auto">
<Badge
color="orange"
size="xs"
tooltip="Slack us if something isn't working properly :)"
className="absolute top-[-10px] left-[-10px]"
>
Beta
</Badge>
<div className="flex divide-x p-2">
<div className="w-1/3 pr-2.5">
<h2 className="text-lg">Configure</h2>
<p className="text-slate-400">
Add dynamic context to your alerts with mapping rules
</p>
<CreateNewMapping />
</div>

<div className="w-2/3 pl-2.5">
{isLoading ? (
<Loading />
) : mappings && mappings.length > 0 ? (
<RulesTable mappings={mappings} />
) : (
<Callout
color="orange"
title="Mapping rules does not exist"
icon={MdWarning}
>
<p className="text-slate-400">No mapping rules found</p>
<p className="text-slate-400">
Configure new mapping rule using the mapping rules wizard.
</p>
</Callout>
)}
</div>
</div>
</Card>
);
}
16 changes: 16 additions & 0 deletions keep-ui/app/mapping/models.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export interface MappingRule {
id: number;
tenant_id: string;
priority: number;
name: string;
description?: string;
file_name?: string;
created_by?: string;
created_at: Date;
disabled: boolean;
override: boolean;
condition?: string;
matchers: string[];
rows: { [key: string]: any }[];
attributes?: string[];
}
10 changes: 10 additions & 0 deletions keep-ui/app/mapping/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Mapping from "./mapping";

export default function Page() {
return <Mapping />;
}

export const metadata = {
title: "Keep - Alert Mapping",
description: "Add dynamic context to your alerts with mapping",
};
Loading

0 comments on commit a2d2baa

Please sign in to comment.