Skip to content

Commit 13e854f

Browse files
Matvey-KukDMMutua
authored andcommitted
Merge branch 'main' into icinga2_provider and add Automatic snippet to provider doc
2 parents df383be + cb3d633 commit 13e854f

File tree

107 files changed

+4579
-2358
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

107 files changed

+4579
-2358
lines changed

docs/providers/documentation/icinga2-provider.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
---
22
title: "Icinga2 Provider"
33
sidebarTitle: "Icinga2"
4+
description: "Icinga2 Provider Allows Reception of Push Alerts from Icinga2 to Keep."
45
---
6+
import AutoGeneratedSnippet from '/snippets/providers/icinga2-snippet-autogenerated.mdx';
7+
8+
<AutoGeneratedSnippet />
59

610
import ProviderLogo from '@components/ProviderLogo';
711

docs/snippets/providers/grafana_loki-snippet-autogenerated.mdx

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
{/* This snippet is automatically generated using scripts/docs_render_provider_snippets.py
1+
{/* This snippet is automatically generated using scripts/docs_render_provider_snippets.py
22
Do not edit it manually, as it will be overwritten */}
33

44
## Authentication
55
This provider requires authentication.
66
- **host_url**: Grafana Loki Host URL (required: True, sensitive: False)
7+
- **verify**: Enable SSL verification (required: False, sensitive: False)
78
- **authentication_type**: Authentication Type (required: True, sensitive: False)
89
- **username**: HTTP basic authentication - Username (required: False, sensitive: False)
910
- **password**: HTTP basic authentication - Password (required: False, sensitive: True)
1011
- **x_scope_orgid**: X-Scope-OrgID Header Authentication (required: False, sensitive: False)
1112

1213
Certain scopes may be required to perform specific actions or queries via the provider. Below is a summary of relevant scopes and their use cases:
13-
- **authenticated**: Instance is valid and user is authenticated
14+
- **authenticated**: Instance is valid and user is authenticated
1415

1516

1617

@@ -26,16 +27,16 @@ steps:
2627
provider: grafana_loki
2728
config: "{{ provider.my_provider_name }}"
2829
with:
29-
query: {value}
30-
limit: {value}
31-
time: {value}
32-
direction: {value}
33-
start: {value}
34-
end: {value}
35-
since: {value}
36-
step: {value}
37-
interval: {value}
38-
queryType: {value}
30+
query: {value}
31+
limit: {value}
32+
time: {value}
33+
direction: {value}
34+
start: {value}
35+
end: {value}
36+
since: {value}
37+
step: {value}
38+
interval: {value}
39+
queryType: {value}
3940
```
4041
4142

examples/workflows/planner_basic.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ workflow:
1313
with:
1414
title: "Keep HQ Task1"
1515
plan_id: "tAtCor_XPEmqTzVqTigCycgABz0K"
16-
on-failure:
17-
retry:
18-
count: 2
19-
interval: 2
16+
on-failure:
17+
retry:
18+
count: 2
19+
interval: 2
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
workflow:
2+
id: send-slack-message-on-failure
3+
name: Get alert root cause from OpenAI, notify if workflow fails
4+
description: Get alert root cause from OpenAI, notify if workflow fails
5+
triggers:
6+
- type: alert
7+
cel: alert.severity == "critical"
8+
on-failure:
9+
provider:
10+
type: slack
11+
config: "{{ providers.slack }}"
12+
with:
13+
channel: "<slack-channel-id>"
14+
# message will be injected from the workflow engine
15+
# e.g. "Workflow <workflow-id> failed with error: <error-message>"
16+
steps:
17+
- name: openai-step
18+
provider:
19+
config: "{{ providers.openai }}"
20+
type: openai
21+
with:
22+
prompt: |
23+
You are a very talented engineer that receives critical alert and reports back the root
24+
cause analysis. Here is the context: keep.json_dumps({{alert}}) (it is a JSON of the alert).
25+
In your answer, also provide the reason why you think it is the root cause and specify what your certainty level is that it is the root cause. (between 1-10, where 1 is low and 10 is high)
26+
actions:
27+
- name: slack-action
28+
provider:
29+
config: "{{ providers.slack }}"
30+
type: slack
31+
with:
32+
message: "{{steps.openai-step.results}}"

keep-ui/app/(keep)/alerts/[id]/ui/alerts.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -221,7 +221,7 @@ export default function Alerts({ presetName, initialFacets }: AlertsProps) {
221221
/>
222222
<ManualRunWorkflowModal
223223
alert={runWorkflowModalAlert}
224-
handleClose={() => setRunWorkflowModalAlert(null)}
224+
onClose={() => setRunWorkflowModalAlert(null)}
225225
/>
226226
<ViewAlertModal
227227
alert={viewAlertModal}

keep-ui/app/(keep)/incidents/[id]/incident-header.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ export function IncidentHeader({
184184
</Modal>
185185
<ManualRunWorkflowModal
186186
incident={runWorkflowModalIncident}
187-
handleClose={() => setRunWorkflowModalIncident(null)}
187+
onClose={() => setRunWorkflowModalIncident(null)}
188188
/>
189189
</CopilotKit>
190190
);

keep-ui/app/(keep)/incidents/[id]/workflows/incident-workflow-empty.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function IncidentWorkflowsEmptyState({
4141

4242
<ManualRunWorkflowModal
4343
incident={runWorkflowModalIncident}
44-
handleClose={() => setRunWorkflowModalIncident(null)}
44+
onClose={() => setRunWorkflowModalIncident(null)}
4545
/>
4646
</>
4747
);

keep-ui/app/(keep)/layout.tsx

Lines changed: 22 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { ThemeScript, WatchUpdateTheme } from "@/shared/ui";
1313
import "@/app/globals.css";
1414
import "react-toastify/dist/ReactToastify.css";
1515
import { PostHogPageView } from "@/shared/ui/PostHogPageView";
16+
import { WorkflowModalProvider } from "@/features/workflows/manual-run-workflow";
1617

1718
// If loading a variable font, you don't need to specify the font weight
1819
const mulish = Mulish({
@@ -37,26 +38,28 @@ export default async function RootLayout({ children }: RootLayoutProps) {
3738
<PHProvider>
3839
<NextAuthProvider session={session}>
3940
<TopologyPollingContextProvider>
40-
{/* @ts-ignore-error Server Component */}
41-
<PostHogPageView />
42-
<Navbar />
43-
{/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */}
44-
<main className="page-container flex flex-col col-start-3 overflow-auto">
45-
{/* Add the banner here, before the navbar */}
46-
{config.READ_ONLY && <ReadOnlyBanner />}
47-
<div className="flex-1">{children}</div>
48-
{/** footer */}
49-
{process.env.GIT_COMMIT_HASH &&
50-
process.env.SHOW_BUILD_INFO !== "false" && (
51-
<div className="pointer-events-none opacity-80 w-full p-2 text-slate-400 text-xs">
52-
<div className="w-full text-right">
53-
Version: {process.env.KEEP_VERSION} | Build:{" "}
54-
{process.env.GIT_COMMIT_HASH.slice(0, 6)}
41+
<WorkflowModalProvider>
42+
{/* @ts-ignore-error Server Component */}
43+
<PostHogPageView />
44+
<Navbar />
45+
{/* https://discord.com/channels/752553802359505017/1068089513253019688/1117731746922893333 */}
46+
<main className="page-container flex flex-col col-start-3 overflow-auto">
47+
{/* Add the banner here, before the navbar */}
48+
{config.READ_ONLY && <ReadOnlyBanner />}
49+
<div className="flex-1">{children}</div>
50+
{/** footer */}
51+
{process.env.GIT_COMMIT_HASH &&
52+
process.env.SHOW_BUILD_INFO !== "false" && (
53+
<div className="pointer-events-none opacity-80 w-full p-2 text-slate-400 text-xs">
54+
<div className="w-full text-right">
55+
Version: {process.env.KEEP_VERSION} | Build:{" "}
56+
{process.env.GIT_COMMIT_HASH.slice(0, 6)}
57+
</div>
5558
</div>
56-
</div>
57-
)}
58-
<ToastContainer />
59-
</main>
59+
)}
60+
<ToastContainer />
61+
</main>
62+
</WorkflowModalProvider>
6063
</TopologyPollingContextProvider>
6164
</NextAuthProvider>
6265
</PHProvider>

keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationForm.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,41 @@ export const CorrelationForm = ({
264264
)}
265265
/>
266266
</div>
267+
268+
<div>
269+
<label
270+
className="flex items-center text-tremor-default font-medium text-tremor-content-strong"
271+
htmlFor="threshold"
272+
>
273+
Alerts threshold{" "}
274+
</label>
275+
276+
<Controller
277+
control={control}
278+
name="threshold"
279+
render={({ field: { value, onChange } }) => (
280+
<TextInput
281+
type="number"
282+
placeholder="1"
283+
className="mt-2"
284+
{...register("threshold", {
285+
required: {
286+
message: "Threshold is required",
287+
value: false,
288+
},
289+
validate: (value) => {
290+
if (value <= 0) {
291+
return "Threshold should be positive";
292+
}
293+
return true;
294+
},
295+
})}
296+
error={isSubmitted && !!get(errors, "threshold.message")}
297+
errorMessage={isSubmitted && get(errors, "threshold.message")}
298+
/>
299+
)}
300+
/>
301+
</div>
267302
</fieldset>
268303

269304
<div className="flex items-center space-x-2">

keep-ui/app/(keep)/rules/CorrelationSidebar/CorrelationSidebarBody.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ export const CorrelationSidebarBody = ({
7777
incidentPrefix,
7878
multiLevel,
7979
multiLevelPropertyName,
80+
threshold,
8081
} = correlationFormData;
8182

8283
const body = {
@@ -94,6 +95,7 @@ export const CorrelationSidebarBody = ({
9495
incidentPrefix,
9596
multiLevel,
9697
multiLevelPropertyName,
98+
threshold,
9799
};
98100

99101
try {

keep-ui/app/(keep)/rules/CorrelationSidebar/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ export const DEFAULT_CORRELATION_FORM_VALUES: CorrelationFormType = {
2727
incidentPrefix: "",
2828
multiLevel: false,
2929
multiLevelPropertyName: "",
30+
threshold: 1,
3031
query: {
3132
combinator: "or",
3233
rules: [
@@ -92,6 +93,7 @@ export const CorrelationSidebar = ({
9293
incidentPrefix: selectedRule.incident_prefix || "",
9394
multiLevel: selectedRule.multi_level,
9495
multiLevelPropertyName: selectedRule.multi_level_property_name || "",
96+
threshold: selectedRule.threshold || 1,
9597
};
9698
}
9799

keep-ui/app/(keep)/rules/CorrelationSidebar/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,5 @@ export type CorrelationFormType = {
1414
incidentPrefix: string;
1515
multiLevel: boolean;
1616
multiLevelPropertyName?: string;
17+
threshold: number;
1718
};

keep-ui/app/(keep)/workflows/[workflow_id]/workflow-detail-header.tsx

Lines changed: 4 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,9 @@
22

33
import { useWorkflowDetail } from "@/entities/workflows/model/useWorkflowDetail";
44
import { Workflow } from "@/shared/api/workflows";
5-
import { useWorkflowRun } from "@/utils/hooks/useWorkflowRun";
5+
import { useWorkflowRun } from "@/features/workflows/manual-run-workflow/model/useWorkflowRun";
66
import { Button, Text } from "@tremor/react";
77
import Skeleton from "react-loading-skeleton";
8-
import AlertTriggerModal from "../workflow-run-with-alert-modal";
9-
import { ManualRunWorkflowModal } from "@/features/workflows/manual-run-workflow";
108

119
export default function WorkflowDetailHeader({
1210
workflowId: workflow_id,
@@ -19,14 +17,8 @@ export default function WorkflowDetailHeader({
1917
fallbackData: initialData,
2018
});
2119

22-
const {
23-
isRunning,
24-
handleRunClick,
25-
getTriggerModalProps,
26-
getManualInputModalProps,
27-
isRunButtonDisabled,
28-
message,
29-
} = useWorkflowRun(workflow as Workflow);
20+
const { isRunning, handleRunClick, isRunButtonDisabled, message } =
21+
useWorkflowRun(workflow as Workflow);
3022

3123
if (error) {
3224
return <div>Error loading workflow</div>;
@@ -78,27 +70,13 @@ export default function WorkflowDetailHeader({
7870
handleRunClick?.();
7971
}}
8072
tooltip={message}
73+
data-testid="wf-run-now-button"
8174
>
8275
{isRunning ? "Running..." : "Run now"}
8376
</Button>
8477
)}
8578
</div>
8679
</div>
87-
88-
{/* Alert Trigger Modal */}
89-
{!!workflow && !!getTriggerModalProps && (
90-
<AlertTriggerModal {...getTriggerModalProps()} />
91-
)}
92-
93-
{/* Manual Input Modal */}
94-
{!!workflow && !!getManualInputModalProps && (
95-
<ManualRunWorkflowModal
96-
workflow={workflow}
97-
handleClose={() => getManualInputModalProps().onClose()}
98-
isOpen={getManualInputModalProps().isOpen}
99-
onSubmit={getManualInputModalProps().onSubmit}
100-
/>
101-
)}
10280
</div>
10381
);
10482
}

keep-ui/app/(keep)/workflows/[workflow_id]/workflow-detail-page.tsx

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { WorkflowYAMLEditorStandalone } from "@/shared/ui/WorkflowYAMLEditor/ui/
2929
import { getOrderedWorkflowYamlString } from "@/entities/workflows/lib/yaml-utils";
3030
import { PiClockCounterClockwise } from "react-icons/pi";
3131
import { WorkflowVersions } from "./workflow-versions";
32+
import { useUIBuilderUnsavedChanges } from "@/entities/workflows/model/workflow-store";
33+
import { useWorkflowYAMLEditorStore } from "@/entities/workflows/model/workflow-yaml-editor-store";
3234

3335
const TABS_KEYS = ["overview", "builder", "yaml", "versions", "secrets"];
3436

@@ -54,6 +56,10 @@ export default function WorkflowDetailPage({
5456
);
5557
const router = useRouter();
5658

59+
const isUIBuilderUnsaved = useUIBuilderUnsavedChanges();
60+
const { hasUnsavedChanges: isYamlEditorUnsaved } =
61+
useWorkflowYAMLEditorStore();
62+
5763
// Set initial tab based on URL query param
5864
useEffect(() => {
5965
const tab = searchParams.get("tab");
@@ -88,8 +94,22 @@ export default function WorkflowDetailPage({
8894
<TabGroup index={tabIndex} onIndexChange={handleTabChange}>
8995
<TabList>
9096
<Tab icon={AiOutlineSwap}>Overview</Tab>
91-
<Tab icon={WrenchIcon}>Builder</Tab>
92-
<Tab icon={CodeBracketIcon}>YAML Definition</Tab>
97+
<Tab icon={WrenchIcon}>
98+
<div className="flex items-center gap-2">
99+
Builder{" "}
100+
{isUIBuilderUnsaved ? (
101+
<div className="inline-block text-xs size-1.5 rounded-full bg-yellow-500" />
102+
) : null}
103+
</div>
104+
</Tab>
105+
<Tab icon={CodeBracketIcon}>
106+
<div className="flex items-center gap-2">
107+
YAML Definition{" "}
108+
{isYamlEditorUnsaved ? (
109+
<div className="inline-block text-xs size-1.5 rounded-full bg-yellow-500" />
110+
) : null}
111+
</div>
112+
</Tab>
93113
<Tab icon={PiClockCounterClockwise}>Versions</Tab>
94114
<Tab icon={KeyIcon}>Secrets</Tab>
95115
<TabNavigationLink

keep-ui/app/(keep)/workflows/[workflow_id]/workflow-overview.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import WorkflowGraph from "../workflow-graph";
88
import { WorkflowExecutionsTable } from "./workflow-executions-table";
99
import { WorkflowOverviewSkeleton } from "./workflow-overview-skeleton";
1010
import { WorkflowProviders } from "./workflow-providers";
11-
import { WorkflowSteps } from "../workflows-templates";
11+
import { WorkflowSteps } from "../workflows-steps";
1212
import { parseWorkflowYamlStringToJSON } from "@/entities/workflows/lib/yaml-utils";
1313
interface Pagination {
1414
limit: number;
@@ -134,6 +134,7 @@ export default function WorkflowOverview({
134134
<Card>
135135
<Title>Executions Graph</Title>
136136
<WorkflowGraph
137+
full
137138
showLastExecutionStatus={false}
138139
workflow={workflow}
139140
limit={executionPagination.limit}

keep-ui/app/(keep)/workflows/[workflow_id]/workflow-providers.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/outline";
77
import { DynamicImageProviderIcon } from "@/components/ui";
88
import { useFetchProviders } from "../../providers/page.client";
99
import { useRevalidateMultiple } from "@/shared/lib/state-utils";
10-
import { checkProviderNeedsInstallation } from "@/entities/workflows/lib/validation";
10+
import { checkProviderNeedsInstallation } from "@/entities/workflows/lib/validate-definition";
1111
import { Drawer } from "@/shared/ui/Drawer";
1212

1313
export const ProvidersCarousel = ({

0 commit comments

Comments
 (0)