From f69ad34d0a0638969213aa7ee3b43a96b7c6b117 Mon Sep 17 00:00:00 2001 From: Shahar Glazner Date: Sun, 27 Oct 2024 20:10:25 +0200 Subject: [PATCH 1/9] feat(ui): add azure svg to signin (#2313) --- keycloak/docker-compose.yaml | 4 ++-- keycloak/themes/keep.jar | Bin 249908 -> 251710 bytes 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/keycloak/docker-compose.yaml b/keycloak/docker-compose.yaml index e5afd6108..c03bf1380 100644 --- a/keycloak/docker-compose.yaml +++ b/keycloak/docker-compose.yaml @@ -1,4 +1,4 @@ -version: '3.9' +version: "3.9" services: mysql: @@ -20,7 +20,7 @@ services: - keycloak-and-mysql-network keycloak: - image: quay.io/phasetwo/phasetwo-keycloak:latest + image: us-central1-docker.pkg.dev/keephq/keep/keep-keycloak ports: - 8181:8080 restart: unless-stopped diff --git a/keycloak/themes/keep.jar b/keycloak/themes/keep.jar index 89177367a6670dc1bde920189c92718402ffe692..401c62aaca8e9748229bf149cca9d7efd63f76b3 100644 GIT binary patch delta 4299 zcmYk82|QHY8^>qnT1R$cUz2?&LY63dvSu6E*A&WD#K=;%3KJ(K`;sLQ#!j{@LqjPc zRHSUts;K<$n78-+-}%hvKELmI&U4Ol&bjxw4F$}dHOy3&W)KzVZh<&6N;&b~NX`u2 z)VlFVG*x6c))Bv{cK}${LA3Zsoap)lhHey9U<{1cQvSm@_sFpG-ts=7XAiZjmG0;QJJI4sZ*!S zx0fm(tekTr(Gu=&hiA?Hxxj-B$6Cw=5MwqT6Y{Qn(#GOE)_iui2VL5}Z?0|J)P#4N|4#Vfb+aMw7PnOug5XRF z+vL%C=9%be!mO*@VBJM^fhcwUA9@lAX-e7pS*DmH96MXMUSwTsK@lcJ(p^ zd~eiaZ(F{Ln=Pn#ujt+yx{6f2?`U8rUFlU?eXo)|}?f4X5mbr?+~E4!v?h7dk337zRHF#`Six;cP-N19w&BNcw>5^G4-za<{nl zv!0OU()1RlI9jeW-^3TrJAP*a-3;X#Jj;{NS~Nj9cCik=U-S*+OH*!5gd8^#Or+2I z?WQe56*0_*Cn5r(1RO^>w$$z4#qkK<{~~VhEaf}luRWkDE8mhi&Z{|~E}~lCv=fW) z(UPV*h^tYOCb;JXh)Xlap9E@cWt~Z=Wu@<=!fDs`W-oZrL+3*!xW}HrTvEj=wQ(wbN zO20fnm;GT?Dp>Z)A~vD48NAMpZxfYt2rBXAwJ(Pnu@2lGy|T>Hrs~8VUcnWK8RHQ%UN0H`JUMkoM^;vm zd8zc~v=O_wmtHC|h3V&CY#;P?qDNn-+HZ=u`=MB(DKb!$Mp6N_b)lAjeWeI_-K(2D zCDP?gyTy}&0bUyPZ>y(eZ*rj_rn$^hB3iY^slPOZ847I;HveFL(-~vA8#!4% zsna2@!!;c6QB-{UN68;$-8G?Q?Gr)wROP?yI<4XmmS(hc!$)aqI;jwdP@^OYw+0AFfxJ<=DngG*MeZqhUaDr7{1qQh0jZWRJSl&bNNq z>Iy~Ujxe<#&ZEA8LFaSz?o@Ugacxa@i&AvGBp{xWwQhs+=Ydu12e|Xw~ml7}UuXU-`0hpwzkRnX4I_ zmiSsKwEVS!%zE+qvk5Pkr`(0~Tbk5Uq{?EiK(q+oXvUO8U&EZm$*`T0*DhD=!y1=6 z_J`=Qnr9f$4xRO%d-p6gPjdJo&d6BFDNZ^+?y}*4;KyG)*>ywWbLH)i+LEI3-sZiR z`rs$x*l8f3jGIGA&eTUwnFQM`WN!vsqz^-yA0XD;=kcQx7;>#Ci_Fd6_%q{Yt0PW` z`9(@G?+to|1-qt`8hsTp)SB{lBl?R!H(sJ0=fWuC@?<1Fe|(4I6_* zAbjh`HA<|hsBv(83PK*h*P1Zk=Wn0^-y;Zv>u@j+nNefu*s&M3Wg0?ezxBD4E>`7# z*FVHW+5G(H3(CUu*7tuq+Ht++*b))LQtbYjE?di)*{kMH*~LrabM%y(E5SMLzn6FAxJ`unpy<|Qjt zakq0KHVIx$_g*7fMx3s>iW*?|CzlyNJA4g89h*4_>Ew_8da4#uNvFNrdGcH8zT7hh zpV9KS_c%{fcjqqN^i|jp7~@E<*0l+yb5_f9Ppk3ktYQt27HK-BGA^8U|JJ$V%TPJ7 z+N<*2qN33?zk5bqkvjvRYpk3U^ylZ=ngveXMw8~YkJOLUF{6AN+QUt^#TgS+ik4U` z>E2A#XoYn0(QoudJuKC3@zTy<@|nxhe$$*gIPDdk(JsNm+40KbbZ^Mz=CS$CE@h2S ze^ra26E8{Zp-Q7b?AA2fy+WIM{SJoP&)sly9soa?dqTX?G77RYlpfCf_=D(VNZ0q1eT1apuI+ zHSgd)4wBVPe~{X%uOa)U(I<8UnM?Q9pjpF!?(~zhzc#{ zMCW)t{_Ai&LdJT`IcZeAuRxpbp-*H7aR8|~TNoT1m39T1br05`@JwwWw8p zo3RC1M<}+ceY%hpz7l^#BS5VoA;7Vr!M$|(7o(h^;7zwP>9klH4uj;=2YkwP=)USL z2#)Q$-nPm2;`Y0l$UI!o>@dr3&09GB`WAlmRDtTZ5@GIm zYSP!)?&X&;!iq_H>~YEQM}<;G=Fz&{f3A%TJbEV@;96H!60wyriA&!n&f0@B+!XCP zl(yzjx!`ZL8RS;~c;jXbpHi4W&&tg}yQ-|GnW|xyH$rC(O*G#w8P&d@_gw8V(x}Cu zrGMuq)a7-{`dogZXFm`| zJk$h!cihMfFsaHra?6^HC)&^5gBanIDt>xfFOXr9l$RB$ZDe_PebykIPRz4D^Zcqj z$WA(K}uh>M?FR44ta4%2$@(qY>~U3Cj9v~(d-Q6=WRqjHMaLY4N9rQDeWJucyoN<5Cy z&wEpTXTANbORf}?gf-6-aHUvdAy!L^W9(}!Bf2nE4k-9Wh>s%9mBXa84)5D0m&Q4gt6 zkw3Npk^o`A+j@u>b&?%?B0v)O7$!zw*9Wlx{RWuABkoaIRtAvX07;|lc_<*3V;|Tc zgF-$E80JKSSMU4Y>@D_e-457C$#T!W-2u3a@dU2u{X8E_gRz@KB0B%2mXAyVnZ(uxM=05P7%wFcej1%Z~!x`4! z!HZ-Gf$VOW>)s`E{1HG8-jH?VU(EtkdSKoDR|}C(2MZ|afwM@?`==8JSm}Y}QIh$4 z!4-QULlkQP81IAl@pp2VDS6lTLcB;c80&)sz-lj~gUTt}^8iMDkOpd^f+F(z;C(8p zCJQ?hQw{kph`(Kq2H}t4QQiB=qt;Sfcl#j@aK9hkO5VnQi4p1tfg-#fLt3cEEqelQ zSqf<1&evv5m2W{B1MYBT^JmWC?LYD53mCs(Z6c3tcVhw%}!s+;$XM zB1!grnX>OoNYQG!Zd&xO-1Cmh{mUOPt^oC+7ocUb-z!$leN{1Ay!7CxLo$3rAoFu4MVMkeA_2cmW9meX^>maR6_ zNxk!&$$d=U^wU0K#^=6%Hq0`8tnc>j5Z9>W(Ur2Ywl*o9d^L8jv~pIR$GxEWP>#?bR4{bJu2g zV8@j`oTTgk^~iQ#Z8j@twk^YhwtLe`tD{=#$r=6L5{Z&(UbeHIwN(9kK0kV#HV7w5 zD%7=Vh?qRE%j>5UeNS?o+}LA2>$2VV&1vpb`=h|X2Rgyo$x%m}Z`yk4JbQnTK7IH0 zf!$ckw^tS;?a!8cBx&Om|Ru#Tgx5T&uV zU@(|WB1s97NO-5CKE6RyEZ<^-x3j>wpR79`LRNwF2EdLiXr^#`%UrS-s!0_1~nP@+?cxo@efkm$FWO z8!P2%0;OCv#n@kF2Tm%h6wFyBIUd#D^twrDU17XXt$B8>+4ByWx_4TDS*qz^A z==wXvM#-eqe(4*caP3}6*%Wkq+P|aH;>M{VwK_;v)oq*G-O-IIoS*!dzNS|tu^n%4hdx)s`=Gqr=?uou(4J| zv0s{jNhF2aE~O7RUrQ-#-az{F4_bTffNsI5ZdRFMGmEP|kn3@4gCM=r;F8_j5AhAv zI-$vxa?AVXawE0LpPXru(lYDVNhV&M&hy>PY88j_Bd;g#mH&8A_L@V!;;3fOM0;68 z5$B4@%qpAQ`^yIBn?0Mfij(TM*@fOdB39cq%XK@R_4v8Vcb0i&@~s+%X*NjTtW$0(Lu}HtLG^9(3B^(aR>e&Y?JDN_=^IdsIGW%lc*+~XP1t0%PG)a&7hanUQ~DSG(bBPE|RM~Jgec|>!0ijhc$%W*P8 zK01(lHjqn3U#u{Y3oa``!$P1!CP9xvupZJ10hRnaR44?}kXZ#7h1^201tTsr_`C>Y z!Q2KQ04IupEWB3?SWu?~7-4Ve_#p{(c)0`^Vy2S(c(Men!Ok-Hv0W)Jz_>E}ID?3< ztsr9jtFkbpmjO0*NQtjoR7K%ILVr->Ye!8H*h=UgHosQ69IdrWlkb)4QQ-M<ssLtpKaB?DhQ4au#B6 zUnS~XZN}HSmdjxsq0`LaOeL_zUYNtUDxePis}NMRAcP0zvxyJ!R%;5BuSQ0Ie4baB~tQXw#HRy>>*eo=fa1e!`YEUE1tqZPAElL%% zT@b0YXw>Qd2&@jQCO?B!HK?I?9m?`{TG}KIegD@O>ld-n0bh z(3FP&6)t8-LY#+&F}MUQFiz5ZAZTDTix2*~F8qDCw%bI~CENnujIVfilY{9nX@(Xnf z#bH!4DrDwEpBAtJpDz-Jg9wl*(5V^esZw$H^Aogfs}{8Fna4zyGgerNs#$Qj1yxR! zqgW2AwgPR;wg$yA@IWhiO#3<*=K+|wPLnWv+KLkA8VR5TrQ47&kgm^ z)KtlEW);~0W_BRIQhmYyQxEO2P@7DH5}gQ>S1n-)7)FpW9fByrmNjHa*h>&n)B-uy z1%#ni7tq2Qw!^$`fDXVJlyin4doC^!Ds)UhchFE5+MeCD1+3T&EU@q!gkZv>-RNJb zNFjs`=9WhMO2f5TfCiO&P|X0JFknRwI$gm%z=SLYbBPoL)?=5l7CQ1>`CcGN-AUR> O>Q^L@ta1T*4gLn@5jSA~ From ab5afc7258269c7f8f8cc4bc30b2f08e2f541773 Mon Sep 17 00:00:00 2001 From: Kirill Chernakov Date: Mon, 28 Oct 2024 15:15:22 +0400 Subject: [PATCH 2/9] fix: hovering long dashboard names stretch NavBar (#2317) --- keep-ui/components/navbar/DashboardLink.tsx | 51 ++++++++++++-------- keep-ui/components/navbar/DashboardLinks.tsx | 5 +- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/keep-ui/components/navbar/DashboardLink.tsx b/keep-ui/components/navbar/DashboardLink.tsx index cc046de60..80016e0bf 100644 --- a/keep-ui/components/navbar/DashboardLink.tsx +++ b/keep-ui/components/navbar/DashboardLink.tsx @@ -1,8 +1,8 @@ -import { useSortable } from '@dnd-kit/sortable'; +import { useSortable } from "@dnd-kit/sortable"; import { Subtitle } from "@tremor/react"; import { FiLayout } from "react-icons/fi"; import { LinkWithIcon } from "components/LinkWithIcon"; // Ensure you import this correctly -import classNames from 'classnames'; +import { clsx } from "clsx"; interface Dashboard { id: string; @@ -14,31 +14,40 @@ type DashboardLinkProps = { dashboard: Dashboard; pathname: string | null; deleteDashboard: (id: string) => void; + titleClassName?: string; }; -export const DashboardLink = ({ dashboard, pathname, deleteDashboard }: DashboardLinkProps) => { +export const DashboardLink = ({ + dashboard, + pathname, + deleteDashboard, + titleClassName, +}: DashboardLinkProps) => { const href = `/dashboard/${dashboard.dashboard_name}`; - const isActive = decodeURIComponent(pathname|| "") === href; - - const { isDragging } = - useSortable({ - id: dashboard.id, - }); + const isActive = decodeURIComponent(pathname || "") === href; + const { isDragging } = useSortable({ + id: dashboard.id, + }); return ( deleteDashboard(dashboard.id)} - > - - {dashboard.dashboard_name} - - + href={href} + icon={FiLayout} + isDeletable={true} + onDelete={() => deleteDashboard(dashboard.id)} + > + + {dashboard.dashboard_name} + + ); }; diff --git a/keep-ui/components/navbar/DashboardLinks.tsx b/keep-ui/components/navbar/DashboardLinks.tsx index cf0cab3d4..80fcc9c35 100644 --- a/keep-ui/components/navbar/DashboardLinks.tsx +++ b/keep-ui/components/navbar/DashboardLinks.tsx @@ -13,7 +13,7 @@ import { DashboardLink } from "./DashboardLink"; import { Subtitle, Button, Badge, Text } from "@tremor/react"; import { Disclosure } from "@headlessui/react"; import { IoChevronUp } from "react-icons/io5"; -import classNames from "classnames"; +import clsx from "clsx"; import { useDashboards } from "utils/hooks/useDashboards"; import { useApiUrl } from "utils/hooks/useConfig"; @@ -105,7 +105,7 @@ export const DashboardLinks = ({ session }: DashboardProps) => { Beta { dashboard={dashboard} pathname={pathname} deleteDashboard={deleteDashboard} + titleClassName="max-w-[150px] overflow-hidden overflow-ellipsis" /> )) ) : ( From ebbb889f627dea2899e9dc828eb20da99e567760 Mon Sep 17 00:00:00 2001 From: Matvey Kukuy Date: Mon, 28 Oct 2024 18:14:20 +0400 Subject: [PATCH 3/9] fix: keycloak docs (#2309) --- .../authentication/keycloak-auth.mdx | 5 ++-- .../keycloak/keycloak_authverifier.py | 7 ++++++ keycloak/keycloak_entrypoint.sh | 4 ++-- keycloak/readme.md | 24 +++++++++++++++---- 4 files changed, 32 insertions(+), 8 deletions(-) diff --git a/docs/deployment/authentication/keycloak-auth.mdx b/docs/deployment/authentication/keycloak-auth.mdx index 255e67ed2..eb821e7a6 100644 --- a/docs/deployment/authentication/keycloak-auth.mdx +++ b/docs/deployment/authentication/keycloak-auth.mdx @@ -32,6 +32,7 @@ To start Keep with Keycloak authentication, set the following environment variab | AUTH_TYPE | Set to 'KEYCLOAK' for Keycloak authentication | Yes | - | | KEYCLOAK_ID | Your Keycloak client ID (e.g. keep) | Yes | - | | KEYCLOAK_ISSUER | Full URL to Your Keycloak issuer URL e.g. http://localhost:8181/auth/realms/keep | Yes | - | +| KEYCLOAK_SECRET | Your Keycloak client secret | Yes | keep-keycloak-secret | #### Backend Environment Variables @@ -50,5 +51,5 @@ To start Keep with Keycloak authentication, set the following environment variab ### Example configuration To get a better understanding on how to use Keep together with Keycloak, you can: -- See [Keycloak](https://github.com/keephq/keep/tree/main/tests) directory for configuration, realm.json, etc -- See Keep + Keycloak [docker-compose example](https://github.com/keephq/keep/blob/main/keycloak/docker-compose.yml) +- See [Keycloak](https://github.com/keephq/keep/tree/main/keycloak) directory for configuration, realm.json, etc +- See Keep + Keycloak [docker-compose example](https://github.com/keephq/keep/blob/main/keycloak/docker-compose.yaml) diff --git a/ee/identitymanager/identity_managers/keycloak/keycloak_authverifier.py b/ee/identitymanager/identity_managers/keycloak/keycloak_authverifier.py index 1daf5bb6a..1a2fd7516 100644 --- a/ee/identitymanager/identity_managers/keycloak/keycloak_authverifier.py +++ b/ee/identitymanager/identity_managers/keycloak/keycloak_authverifier.py @@ -1,4 +1,5 @@ import os +import logging from fastapi import Depends, HTTPException @@ -8,6 +9,8 @@ from keycloak.keycloak_uma import KeycloakUMA from keycloak.uma_permissions import UMAPermission +logger = logging.getLogger(__name__) + class KeycloakAuthVerifier(AuthVerifierBase): """Handles authentication and authorization for Keycloak""" @@ -55,6 +58,10 @@ def _verify_bearer_token( email = payload.get("preferred_username") org_id = payload.get("active_organization", {}).get("id") org_realm = payload.get("active_organization", {}).get("name") + if org_id is None or org_realm is None: + logger.warning( + "Invalid Keycloak configuration - no org information for user. Check organization mapper: https://github.com/keephq/keep/blob/main/keycloak/keep-realm.json#L93" + ) role = ( payload.get("resource_access", {}) .get(self.keycloak_client_id, {}) diff --git a/keycloak/keycloak_entrypoint.sh b/keycloak/keycloak_entrypoint.sh index d8bfa81e2..74a95fce9 100755 --- a/keycloak/keycloak_entrypoint.sh +++ b/keycloak/keycloak_entrypoint.sh @@ -28,7 +28,7 @@ fi # Start Keycloak in the background echo "Starting Keycloak" -/opt/keycloak/bin/kc.sh start-dev --features=preview --import-realm -Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.migration.strategy=OVERWRITE_EXISTIN & +/opt/keycloak/bin/kc.sh start-dev --log-level=DEBUG --features=preview --import-realm -Dkeycloak.profile.feature.scripts=enabled -Dkeycloak.migration.strategy=OVERWRITE_EXISTIN & echo "Keycloak started" # Try to connect to Keycloak - wait until Keycloak is ready or timeout echo "Waiting for Keycloak to be ready" @@ -62,7 +62,7 @@ echo "Event listener 'last_login' configured" # Configure Content-Security-Policy and X-Frame-Options # So that the SSO connect works with the Keep UI echo "Configuring Content-Security-Policy and X-Frame-Options" -/opt/keycloak/bin/kcadm.sh update realms/${KEEP_REALM} -s 'browserSecurityHeaders.contentSecurityPolicy="frame-src '\''self'\'' '"$KEEP_URL"'; frame-ancestors '\''self'\'' '"$KEEP_URL"'; object-src '\''none'\'';"' +/opt/keycloak/bin/kcadm.sh update realms/${KEEP_REALM} -s 'browserSecurityHeaders.contentSecurityPolicy="frame-src '\''self'\'' '${KEEP_URL}'; frame-ancestors '\''self'\'' '${KEEP_URL}'; object-src '\''none'\'';"' /opt/keycloak/bin/kcadm.sh update realms/${KEEP_REALM} -s 'browserSecurityHeaders.xFrameOptions="ALLOW"' echo "Content-Security-Policy and X-Frame-Options configured" diff --git a/keycloak/readme.md b/keycloak/readme.md index a7f980b42..910bc3a8c 100644 --- a/keycloak/readme.md +++ b/keycloak/readme.md @@ -1,17 +1,33 @@ +# Docker-compose example: +``` +docker-compose -f keycloak/docker-compose.yaml up +``` +Keycloak: http://localhost:8181/auth/ (keep_kc:keep_kc) +Keep login page: http://localhost:3000/ +## For Azure: +Instructions: +1. https://rahulroyz.medium.com/using-keycloak-as-idp-for-azure-ad-sso-authentication-role-authorization-0b309c15eadc +2. https://rahulroyz.medium.com/using-keycloak-as-idp-for-azure-ad-role-authorization-part-2-map-ad-groups-to-keycloak-roles-9850d4acd536 + +Set email, first name & last name for keep_admin user: http://localhost:8181/auth/admin/master/console/#/keep/users +Also please assign admin role for keep_admin. + +# Development + +``` docker run --name phasetwo_test --rm -p 8181:8080 \ -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin \ quay.io/phasetwo/phasetwo-keycloak:latest \ start-dev - - +``` +``` http://localhost:8181/realms/keep/portal/ http://localhost:8181/realms/keep/portal/ - https://euc1.auth.ac/auth/realms/keep/portal - +``` # delete realm to refresh 1. delete the realm from the UI From d101552291ffa35d5d611bb1fd7457a3f76299ea Mon Sep 17 00:00:00 2001 From: Vladimir Filonov Date: Mon, 28 Oct 2024 23:08:23 +0400 Subject: [PATCH 4/9] fix: incident.alerts_count should return count of unique fingerprints instead of alerts (#2217) --- keep/api/core/db.py | 30 ++++++++++---- keep/api/tasks/process_event_task.py | 5 ++- tests/test_incidents.py | 59 ++++++++++++++++++++++------ 3 files changed, 74 insertions(+), 20 deletions(-) diff --git a/keep/api/core/db.py b/keep/api/core/db.py index 0d1c0e3c5..9d6bc7380 100644 --- a/keep/api/core/db.py +++ b/keep/api/core/db.py @@ -2950,7 +2950,9 @@ def get_all_same_alert_ids( def get_alerts_data_for_incident( - alert_ids: List[str | UUID], session: Optional[Session] = None + alert_ids: List[str | UUID], + existed_fingerprints: Optional[List[str]] = None, + session: Optional[Session] = None ) -> dict: """ Function to prepare aggregated data for incidents from the given list of alert_ids @@ -2962,12 +2964,14 @@ def get_alerts_data_for_incident( Returns: dict {sources: list[str], services: list[str], count: int} """ + existed_fingerprints = existed_fingerprints or [] with existed_or_new_session(session) as session: fields = ( get_json_extract_field(session, Alert.event, "service"), Alert.provider_type, + Alert.fingerprint, get_json_extract_field(session, Alert.event, "severity"), ) @@ -2980,8 +2984,9 @@ def get_alerts_data_for_incident( sources = [] services = [] severities = [] + fingerprints = set() - for service, source, severity in alerts_data: + for service, source, fingerprint, severity in alerts_data: if source: sources.append(source) if service: @@ -2991,12 +2996,14 @@ def get_alerts_data_for_incident( severities.append(IncidentSeverity.from_number(severity)) else: severities.append(IncidentSeverity(severity)) + if fingerprint and fingerprint not in existed_fingerprints: + fingerprints.add(fingerprint) return { "sources": set(sources), "services": set(services), "max_severity": max(severities), - "count": len(alerts_data), + "count": len(fingerprints), } @@ -3047,6 +3054,17 @@ def add_alerts_to_incident( ) ).all() ) + existing_fingerprints = set( + session.exec( + select(Alert.fingerprint) + .join(AlertToIncident, AlertToIncident.alert_id == Alert.id) + .where( + AlertToIncident.deleted_at == NULL_FOR_DELETED_AT, + AlertToIncident.tenant_id == tenant_id, + AlertToIncident.incident_id == incident.id, + ) + ).all() + ) new_alert_ids = [ alert_id for alert_id in all_alert_ids if alert_id not in existing_alert_ids @@ -3055,9 +3073,7 @@ def add_alerts_to_incident( if not new_alert_ids: return incident - alerts_data_for_incident = get_alerts_data_for_incident( - new_alert_ids, session - ) + alerts_data_for_incident = get_alerts_data_for_incident(new_alert_ids, existing_fingerprints, session) incident.sources = list( set(incident.sources if incident.sources else []) | set(alerts_data_for_incident["sources"]) @@ -3177,7 +3193,7 @@ def remove_alerts_to_incident_by_incident_id( session.commit() # Getting aggregated data for incidents for alerts which just was removed - alerts_data_for_incident = get_alerts_data_for_incident(all_alert_ids, session) + alerts_data_for_incident = get_alerts_data_for_incident(all_alert_ids, session=session) service_field = get_json_extract_field(session, Alert.event, "service") diff --git a/keep/api/tasks/process_event_task.py b/keep/api/tasks/process_event_task.py index 5e2070743..9347fe49b 100644 --- a/keep/api/tasks/process_event_task.py +++ b/keep/api/tasks/process_event_task.py @@ -527,7 +527,10 @@ def process_event( and isinstance(event, dict) or isinstance(event, FormData) ): - provider_class = ProvidersFactory.get_provider_class(provider_type) + try: + provider_class = ProvidersFactory.get_provider_class(provider_type) + except Exception: + provider_class = ProvidersFactory.get_provider_class("keep") event = provider_class.format_alert( tenant_id=tenant_id, event=event, diff --git a/tests/test_incidents.py b/tests/test_incidents.py index 5cd78fbd2..b9758b638 100644 --- a/tests/test_incidents.py +++ b/tests/test_incidents.py @@ -2,7 +2,7 @@ from itertools import cycle import pytest -from sqlalchemy import func +from sqlalchemy import func, distinct from sqlalchemy.orm.exc import DetachedInstanceError from keep.api.core.db import ( @@ -23,24 +23,40 @@ IncidentSeverity, IncidentStatus, ) -from keep.api.models.db.alert import Alert +from keep.api.models.db.alert import Alert, AlertToIncident from keep.api.utils.enrichment_helpers import convert_db_alerts_to_dto_alerts from tests.fixtures.client import client, test_app # noqa +def test_get_alerts_data_for_incident(db_session, create_alert): + for i in range(100): + create_alert( + f"alert-test-{i % 10}", + AlertStatus.FIRING, + datetime.utcnow(), + { + "source": [f"source_{i % 10}"], + "service": f"service_{i % 10}", + } + ) + + alerts = db_session.query(Alert).all() + + unique_fingerprints = db_session.query(func.count(distinct(Alert.fingerprint))).scalar() -def test_get_alerts_data_for_incident(db_session, setup_stress_alerts_no_elastic): - alerts = setup_stress_alerts_no_elastic(100) assert 100 == db_session.query(func.count(Alert.id)).scalar() + assert 10 == unique_fingerprints data = get_alerts_data_for_incident([a.id for a in alerts]) - assert data["sources"] == set(["source_{}".format(i) for i in range(10)]) - assert data["services"] == set(["service_{}".format(i) for i in range(10)]) - assert data["count"] == 100 + assert data["sources"] == set([f"source_{i}" for i in range(10)]) + assert data["services"] == set([f"service_{i}" for i in range(10)]) + assert data["count"] == unique_fingerprints def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elastic): alerts = setup_stress_alerts_no_elastic(100) + # Adding 10 non-unique fingerprints + alerts.extend(setup_stress_alerts_no_elastic(10)) incident = create_incident_from_dict( SINGLE_TENANT_UUID, {"user_generated_name": "test", "user_summary": "test"} ) @@ -53,7 +69,10 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti incident = get_incident_by_id(SINGLE_TENANT_UUID, incident.id) - assert len(incident.alerts) == 100 + # 110 alerts + assert len(incident.alerts) == 110 + # But 100 unique fingerprints + assert incident.alerts_count == 100 assert sorted(incident.affected_services) == sorted( ["service_{}".format(i) for i in range(10)] @@ -66,6 +85,18 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti service_0 = db_session.query(Alert.id).filter(service_field == "service_0").all() + # Testing unique fingerprints + more_alerts_with_same_fingerprints = setup_stress_alerts_no_elastic(10) + + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident.id, [a.id for a in more_alerts_with_same_fingerprints] + ) + + incident = get_incident_by_id(SINGLE_TENANT_UUID, incident.id) + + assert incident.alerts_count == 100 + assert db_session.query(func.count(AlertToIncident.alert_id)).scalar() == 120 + remove_alerts_to_incident_by_incident_id( SINGLE_TENANT_UUID, incident.id, @@ -76,7 +107,8 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti incident = get_incident_by_id(SINGLE_TENANT_UUID, incident.id) - assert len(incident.alerts) == 99 + # 117 because we removed multiple alerts with service_0 + assert len(incident.alerts) == 117 assert "service_0" in incident.affected_services assert len(incident.affected_services) == 10 assert sorted(incident.affected_services) == sorted( @@ -92,11 +124,12 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti incident_id=incident.id, tenant_id=incident.tenant_id, include_unlinked=True - )[0]) == 100 + )[0]) == 120 incident = get_incident_by_id(SINGLE_TENANT_UUID, incident.id) - assert len(incident.alerts) == 90 + # 108 because we removed multiple alert with same fingerprints + assert len(incident.alerts) == 108 assert "service_0" not in incident.affected_services assert len(incident.affected_services) == 9 assert sorted(incident.affected_services) == sorted( @@ -117,7 +150,7 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti incident = get_incident_by_id(SINGLE_TENANT_UUID, incident.id) - assert len(incident.alerts) == 89 + assert len(incident.alerts) == 105 assert "source_1" in incident.sources # source_0 was removed together with service_0 assert len(incident.sources) == 9 @@ -137,6 +170,8 @@ def test_add_remove_alert_to_incidents(db_session, setup_stress_alerts_no_elasti ) + + def test_get_last_incidents(db_session, create_alert): severity_cycle = cycle([s.order for s in IncidentSeverity]) From 3f24f578a406b69c05d136878b2a632013d2fab6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:09:03 +0000 Subject: [PATCH 5/9] chore(deps): bump mysql-connector-python from 8.4.0 to 9.1.0 (#2296) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Tal --- poetry.lock | 67 +++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 35 insertions(+), 34 deletions(-) diff --git a/poetry.lock b/poetry.lock index 563eb33e4..6e253ac55 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2541,44 +2541,45 @@ files = [ [[package]] name = "mysql-connector-python" -version = "8.4.0" -description = "MySQL driver written in Python" +version = "9.1.0" +description = "A self-contained Python driver for communicating with MySQL servers, using an API that is compliant with the Python Database API Specification v2.0 (PEP 249)." optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "mysql-connector-python-8.4.0.tar.gz", hash = "sha256:42542d131d63c78416d410fdc9e84b9acb960d715c2e7b28c57ac9577c6d8165"}, - {file = "mysql_connector_python-8.4.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:c0a2688d95d53cfbea9352ed61926b47bc9042570570fb8fe0a8d19b1e20f1c4"}, - {file = "mysql_connector_python-8.4.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:276bae0d5d44abb7ba1205003b55628e4e6f1d399f1825d518bc607320997b1f"}, - {file = "mysql_connector_python-8.4.0-cp310-cp310-manylinux_2_17_aarch64.whl", hash = "sha256:a6d24ea29b3c2bdbba6861590de557665420bfb938f74b5cecc630bac5457d35"}, - {file = "mysql_connector_python-8.4.0-cp310-cp310-manylinux_2_17_x86_64.whl", hash = "sha256:b7876358d9e51f25edc492088c4ce16cd14c2db87c279a965b0f9c327723359c"}, - {file = "mysql_connector_python-8.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:085024bf12d15f9b428938fdbeb50bd9b15dda9c4d3a474e6df061cb08713e6a"}, - {file = "mysql_connector_python-8.4.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:4e83fc8ed95005b171ffa36a289dac48625048263b09b56718e8395539ea07d9"}, - {file = "mysql_connector_python-8.4.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:cd89d1c8c2d1e33e5ac2d4eac5813422c150a8427fb60a16c59be18c29dd9a94"}, - {file = "mysql_connector_python-8.4.0-cp311-cp311-manylinux_2_17_aarch64.whl", hash = "sha256:76c13fde35a038afe50550a9af7b31b28ca3a04cce06f3030980afb20460d28c"}, - {file = "mysql_connector_python-8.4.0-cp311-cp311-manylinux_2_17_x86_64.whl", hash = "sha256:accf10425c6af39a9595a47e7119ebcbcd7351f7df28755dbee01bca5a605b7c"}, - {file = "mysql_connector_python-8.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:cda868bb4e1641362d148f5b0d2a86188cffa2f7188831589781b13f2df6f51a"}, - {file = "mysql_connector_python-8.4.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:3ae951f2e16d089975cb9f05b3f3e58807dc33a2e5a627047bba1c8ad5439d82"}, - {file = "mysql_connector_python-8.4.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:af40b5bdd91547d3dbf5fa62bde37e9e840bd7cba3b9246b55c09e6a1cde536f"}, - {file = "mysql_connector_python-8.4.0-cp312-cp312-manylinux_2_17_aarch64.whl", hash = "sha256:bb4f3edab78f3fd6f80c6c0a9e5a533704044fc01bfb9e8736e1a993f74aa42d"}, - {file = "mysql_connector_python-8.4.0-cp312-cp312-manylinux_2_17_x86_64.whl", hash = "sha256:e549674c72b596a7386f4a76bbac2ee9581f6632e6713618a70468713b162964"}, - {file = "mysql_connector_python-8.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:aed505adc76b58282c76e6cbf3da195be0f84029a41f05c470be977481896074"}, - {file = "mysql_connector_python-8.4.0-cp38-cp38-macosx_13_0_x86_64.whl", hash = "sha256:d343a4a8133ae9561bd537fc8cdbcab74a0607a5f40698569010fa3c7d4a048f"}, - {file = "mysql_connector_python-8.4.0-cp38-cp38-manylinux_2_17_aarch64.whl", hash = "sha256:74b1759d8bd9ccd4296dc2e5abe22ec7efbd1ad12a9032c2cb4d17fa5d0ca6e0"}, - {file = "mysql_connector_python-8.4.0-cp38-cp38-manylinux_2_17_x86_64.whl", hash = "sha256:e6d5a418ef124dd1b18a73fd89431a1862ce7bf68f61275c7d006e8e2f8afcd2"}, - {file = "mysql_connector_python-8.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:427a84027b8314c73f5ff3eb1abdc709a8201b44a491d7b580bdf430b4820a16"}, - {file = "mysql_connector_python-8.4.0-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:44a99d44a925ea29c2e423e6d8b1d97ce740c3078d8b41923a81bbcd0a821972"}, - {file = "mysql_connector_python-8.4.0-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:ed276c4e7907da0ad95a9ad122004294d6fb425127064af2ae880033b8e72166"}, - {file = "mysql_connector_python-8.4.0-cp39-cp39-manylinux_2_17_aarch64.whl", hash = "sha256:2b5c6fea6513cf208c7116a4a5e36b3ae54e0d37f324a7cfe43fb01cfdf03be6"}, - {file = "mysql_connector_python-8.4.0-cp39-cp39-manylinux_2_17_x86_64.whl", hash = "sha256:651c7824af57eb50f4a79ea04bf6f453b24381e1bb56eee45c0035b4c0c624c0"}, - {file = "mysql_connector_python-8.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:655dccbdc0e2943e62cf69e10a024329248b17b58bfac59c60fd2103db3ba0b0"}, - {file = "mysql_connector_python-8.4.0-py2.py3-none-any.whl", hash = "sha256:35939c4ff28f395a5550bae67bafa4d1658ea72ea3206f457fff64a0fbec17e4"}, + {file = "mysql-connector-python-9.1.0.tar.gz", hash = "sha256:346261a2aeb743a39cf66ba8bde5e45931d313b76ce0946a69a6d1187ec7d279"}, + {file = "mysql_connector_python-9.1.0-cp310-cp310-macosx_13_0_arm64.whl", hash = "sha256:dcdcf380d07b9ca6f18a95e9516a6185f2ab31a53d290d5e698e77e59c043c9e"}, + {file = "mysql_connector_python-9.1.0-cp310-cp310-macosx_13_0_x86_64.whl", hash = "sha256:948ef0c7da87901176d4320e0f40a3277ee06fe6f58ce151c1e60d8d50fdeaf4"}, + {file = "mysql_connector_python-9.1.0-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:abf16fc1155ebeba5558e5702dd7210d634ac8da484eca05a640b68a548dc7cf"}, + {file = "mysql_connector_python-9.1.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:aceaab679b852c0a2ec0eed9eb2a490171b3493484f1881b605cbf2f9c5fde6d"}, + {file = "mysql_connector_python-9.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:72dcce5f2e4f5910d65f02eb318c1e4622464da007a3ae5e9ccd64169d8efac3"}, + {file = "mysql_connector_python-9.1.0-cp311-cp311-macosx_13_0_arm64.whl", hash = "sha256:9b23a8e2acee91b5120febe00c53e7f472b9b6d49618e39fa1af86cdc1f0ade8"}, + {file = "mysql_connector_python-9.1.0-cp311-cp311-macosx_13_0_x86_64.whl", hash = "sha256:e15153cb8ab5fcec00b99077de536489d22d4809fc28f633850398fef0560b1f"}, + {file = "mysql_connector_python-9.1.0-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:fec943d333851c4b5e57cd0b04dde36e6817f0d4d62b2a58ce028a82be444866"}, + {file = "mysql_connector_python-9.1.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c36a9b9ebf9587aaa5d7928468fefe8faf6fc993a03cb242bb160ede9cf75b2d"}, + {file = "mysql_connector_python-9.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:7b2eb48518b8c2bc9636883d264b291e5c93824fc6b61823ca9cf396a09474ad"}, + {file = "mysql_connector_python-9.1.0-cp312-cp312-macosx_13_0_arm64.whl", hash = "sha256:f67b22e3eaf5b03ffac97232d3dd67b56abcacad907ad4391c847bad5ba58f0e"}, + {file = "mysql_connector_python-9.1.0-cp312-cp312-macosx_13_0_x86_64.whl", hash = "sha256:c75f674a52b8820c90d466183b2bb59f89bcf09d17ebe9b391313d89565c8896"}, + {file = "mysql_connector_python-9.1.0-cp312-cp312-manylinux_2_28_aarch64.whl", hash = "sha256:e75ecb3df2c2cbe4d92d5dd58a318fa708edebc0fa2d850fc2a9d42481dbb808"}, + {file = "mysql_connector_python-9.1.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7d99c0a841a2c2a0e4d5b28376c1bfac794ec3821b66eb6fa2f7702cec820ee8"}, + {file = "mysql_connector_python-9.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:30a8f0ba84f8adf15a4877e80b3f97f786ce35616d918b9310578a2bd22952d5"}, + {file = "mysql_connector_python-9.1.0-cp313-cp313-macosx_13_0_arm64.whl", hash = "sha256:d627ebafc0327b935d8783454e7a4b5c32324ed39a2a1589239490ab850bf7d7"}, + {file = "mysql_connector_python-9.1.0-cp313-cp313-macosx_13_0_x86_64.whl", hash = "sha256:e26a08a9500407fa8f4a6504f7077d1312bec4fa52cb0a58c1ad324ca1f3eeaa"}, + {file = "mysql_connector_python-9.1.0-cp313-cp313-manylinux_2_28_aarch64.whl", hash = "sha256:109e17a4ada1442e3881a51e2bbabcb336ad229a619ac61e9ad24bd6b9b117bd"}, + {file = "mysql_connector_python-9.1.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:4f102452c64332b7e042fa37b84d4f15332bd639e479d15035f2a005fb9fbb34"}, + {file = "mysql_connector_python-9.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:25e261f3260ec798c48cb910862a299e565548a1b5421dec84315ddbc9ef28c4"}, + {file = "mysql_connector_python-9.1.0-cp39-cp39-macosx_13_0_arm64.whl", hash = "sha256:ec4386b2426bfb07f83455bf895d8a7e2d6c067343ac05be5511083ca2424991"}, + {file = "mysql_connector_python-9.1.0-cp39-cp39-macosx_13_0_x86_64.whl", hash = "sha256:28fd99ee464ac3b02d1e2a71a63ca4f25c6110e4414a46a5b64631e6d2096899"}, + {file = "mysql_connector_python-9.1.0-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:e2f0876e1efd76e05853cb0a623dba2746ee70686c043019d811737dd5c3d871"}, + {file = "mysql_connector_python-9.1.0-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:6d7d5d458d0d600bbbebd9f2bce551e386b359bcce6026f7369b57922d26f13a"}, + {file = "mysql_connector_python-9.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:c350b1aaf257b1b778f44b8bfaeda07751f55e150f5a7464342f36e4aac8e805"}, + {file = "mysql_connector_python-9.1.0-py2.py3-none-any.whl", hash = "sha256:dacf1aa84dc7dd8ae908626c3ae50fce956d0105130c7465fd248a4f035d50b1"}, ] [package.extras] -dns-srv = ["dnspython (>=1.16.0,<=2.3.0)"] +dns-srv = ["dnspython (==2.6.1)"] fido2 = ["fido2 (==1.1.2)"] -gssapi = ["gssapi (>=1.6.9,<=1.8.2)"] -opentelemetry = ["Deprecated (>=1.2.6)", "typing-extensions (>=3.7.4)", "zipp (>=0.5)"] +gssapi = ["gssapi (==1.8.3)"] +telemetry = ["opentelemetry-api (==1.18.0)", "opentelemetry-exporter-otlp-proto-http (==1.18.0)", "opentelemetry-sdk (==1.18.0)"] [[package]] name = "ndg-httpsclient" @@ -5298,4 +5299,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "a94488bcf0d635773f1dc9d235a623e6818ed8f7681f4128a7e214c4304e005a" +content-hash = "ca7f650988117bd132b35001a4e3fa4c46e08d76ef0e2545fb481cacb8e2c1c5" diff --git a/pyproject.toml b/pyproject.toml index 6d44fbede..329a61b2e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,7 +19,7 @@ pygithub = "^1.57" sentry-sdk = "^1.15.0" pydantic = "^1.10.4" datefinder = "^0.7.3" -mysql-connector-python = "^8.0.32" +mysql-connector-python = "^9.1.0" logmine = "^0.4.1" astunparse = "^1.6.3" python-json-logger = "^2.0.6" From a37ec4f29685d8325e759817aec2c0de7925e0eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 09:15:18 +0000 Subject: [PATCH 6/9] chore(deps): bump snowflake-connector-python from 3.12.1 to 3.12.3 (#2329) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- poetry.lock | 56 +++++++++++++++++++++++++------------------------- pyproject.toml | 2 +- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6e253ac55..1a4527b81 100644 --- a/poetry.lock +++ b/poetry.lock @@ -4589,37 +4589,37 @@ files = [ [[package]] name = "snowflake-connector-python" -version = "3.12.1" +version = "3.12.3" description = "Snowflake Connector for Python" optional = false python-versions = ">=3.8" files = [ - {file = "snowflake_connector_python-3.12.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c0979324bd96019f500f6c987d4720c9e4d7176df54b1b5aa96875be8c8ff57b"}, - {file = "snowflake_connector_python-3.12.1-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:c889a85966ec6a3384799e594e97301a4be0705d7763a5177104866b75383d8c"}, - {file = "snowflake_connector_python-3.12.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4bfb5fe8db051771480059ffddd5127653f4ac1168c76293655da33c2a2904d7"}, - {file = "snowflake_connector_python-3.12.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1061af4a3a3e66b0c99ab0f8bae5eda28e6324618143b3f5b2d81d1649b8557"}, - {file = "snowflake_connector_python-3.12.1-cp310-cp310-win_amd64.whl", hash = "sha256:3edcf3591b6071ddb02413a0000dea42ee6fe811693d176915edb8687b03ce89"}, - {file = "snowflake_connector_python-3.12.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:226a714eb68bbae328fe49b705ecb304fbd44ea6a7afbb329ba3c389ac9111bc"}, - {file = "snowflake_connector_python-3.12.1-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:7319f63c09efed853d7652cbb38ecc23068e86dbce8340444056787993a854d9"}, - {file = "snowflake_connector_python-3.12.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f86b42a076e14900dc6af2f096343ccf4314d324e7e1153b667d6ee53c60334b"}, - {file = "snowflake_connector_python-3.12.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d231f0d5fb8d7a96b9ab5e9500035bd9f259c80d4b3c482163d156928fb0e546"}, - {file = "snowflake_connector_python-3.12.1-cp311-cp311-win_amd64.whl", hash = "sha256:d9f1bc6b35344b170e2fb30314aa64709b28539084be88e95aacf094e13259eb"}, - {file = "snowflake_connector_python-3.12.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0114370c274ed64fe4aee2333b01e9ff88272837bdaa65fb3a3ee4820dca61b4"}, - {file = "snowflake_connector_python-3.12.1-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:dadd262196cce0132ca7e766f055e00c00497a88fdf83fd48143eb4a469a4527"}, - {file = "snowflake_connector_python-3.12.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:473642c0e628b8b9f264cbf31c7f4de44974373db43052b6542a66e751159caf"}, - {file = "snowflake_connector_python-3.12.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bddc4cdcd991f9538726a7c293d2637bb5aed43db68246e06c92c49a6df2b692"}, - {file = "snowflake_connector_python-3.12.1-cp312-cp312-win_amd64.whl", hash = "sha256:b06c63ec0381df1f4da6c4326330a1a40c8fc21fd3dcc2f58df4de395d676893"}, - {file = "snowflake_connector_python-3.12.1-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3c24119ad64c20a8a691760c81e7d846feea4a6103ba84470116c60f7f31a1b8"}, - {file = "snowflake_connector_python-3.12.1-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:a8ba32c91ebf4de6d3f981cfd6324fb4b833696b639c350f5e5984371957e6f9"}, - {file = "snowflake_connector_python-3.12.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cde5643d8237fc109fed68c6a806297ebe3adeb56ac6865430a78fcaba27f2ef"}, - {file = "snowflake_connector_python-3.12.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a4bc4212db73feab5a79ad28b1d03743cbe48df1e346d219747afde5425c35d"}, - {file = "snowflake_connector_python-3.12.1-cp38-cp38-win_amd64.whl", hash = "sha256:7e5d7a0f1b827304b3ba250fa98c25385a7158ea5333e7857cda2ea91433a354"}, - {file = "snowflake_connector_python-3.12.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:a56f9df9db2b03caf9bc7a45f51d7cdfe307b5e2cde7edaa93b67c2d81789db6"}, - {file = "snowflake_connector_python-3.12.1-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a1ead374d96cf21cb249bf91fe814ab1e1baaa3c3f2391116ccefab8bfa36374"}, - {file = "snowflake_connector_python-3.12.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:38698260175321ddef5504170ac1f9e5e92b897844d55ac2fc77bf0783435299"}, - {file = "snowflake_connector_python-3.12.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7f8699ff60924105253e465a54ad150469ddf65082ce029387d65ca404a46cc"}, - {file = "snowflake_connector_python-3.12.1-cp39-cp39-win_amd64.whl", hash = "sha256:93e79497ae0f0be1a10cf2649900db0011e391ede47cbef2803814c32e1d63d6"}, - {file = "snowflake_connector_python-3.12.1.tar.gz", hash = "sha256:e43b7d4b4488ecd97b5bf62539cc502d7e84d8215c547eaeb4dd928c0b7212b9"}, + {file = "snowflake_connector_python-3.12.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:497a096fc379ef0846b2f1cf11a8d7620f0d090f08a77d9e93473845014d57d1"}, + {file = "snowflake_connector_python-3.12.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:055c5808d524497213e4cc9ae91ec3e46cb8342b314e78bc3e139d733dc16741"}, + {file = "snowflake_connector_python-3.12.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4a5dc512d62ef693041ed2ad82931231caddc16e14ffc2842da3e3dd4240b83d"}, + {file = "snowflake_connector_python-3.12.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a46448f7279d444084eb84a9cddea67662e80ccfaddf41713b9e9aab2b1242e9"}, + {file = "snowflake_connector_python-3.12.3-cp310-cp310-win_amd64.whl", hash = "sha256:821b774b77129ce9f03729456ac1f21d69fedb50e5ce957178131c7bb3d8279f"}, + {file = "snowflake_connector_python-3.12.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:82290134978d11628026b447052219ce8d880e36937204f1f0332dfc3f2e92e9"}, + {file = "snowflake_connector_python-3.12.3-cp311-cp311-macosx_11_0_x86_64.whl", hash = "sha256:20b5c8000ee9cee11b0f9a6ae26640f0d498ce77f7e2ec649a2f0d306523792d"}, + {file = "snowflake_connector_python-3.12.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca6500d16bdbd37da88e589cc3e82b90272471d3aabfe4a79ec1cf4696675acf"}, + {file = "snowflake_connector_python-3.12.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b455ba117a68da436e253899674fae1a93669eaefdde8a903c03eb65b7e87c86"}, + {file = "snowflake_connector_python-3.12.3-cp311-cp311-win_amd64.whl", hash = "sha256:205219fcaeee2d33db5d0d023d60518e3bd8272ce1679be2199d7f362d255054"}, + {file = "snowflake_connector_python-3.12.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3d830ca32c864b730cba5d92900d850752199635c4fb0ae0a70ee677f62aee70"}, + {file = "snowflake_connector_python-3.12.3-cp312-cp312-macosx_11_0_x86_64.whl", hash = "sha256:597b0c74ec57ba693191ae2de8db9536e349ee32cab152df657473e498b6fd87"}, + {file = "snowflake_connector_python-3.12.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2215d8a4c5e25ea0d2183fe693c3fdf058cd6035e5c84710d532dc04ab4ffd31"}, + {file = "snowflake_connector_python-3.12.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8ba9c261904c1ba7cae6035c7881224cf979da39c8b7c7cb10236fdfc57e505"}, + {file = "snowflake_connector_python-3.12.3-cp312-cp312-win_amd64.whl", hash = "sha256:f0d0fcb948ef0812ab162ec9767622f345554043a07439c0c1a9474c86772320"}, + {file = "snowflake_connector_python-3.12.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fe742a0b2fb1c79a21e95b97c49a05783bc00314d1184d227c5fe5b57688af12"}, + {file = "snowflake_connector_python-3.12.3-cp38-cp38-macosx_11_0_x86_64.whl", hash = "sha256:a8584a44a6bb41d2056cf1b833e629c76e28c5303d2c875c1a23bda46a1cd43a"}, + {file = "snowflake_connector_python-3.12.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd990db8e4886c32ba5c63758e8dc4814e2e75f5fd3fe79d43f7e5ee0fc46793"}, + {file = "snowflake_connector_python-3.12.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e4fe7f91f6e44bda877e77403a586d7487ca2c52dc1a32a705b2fea33f9c763a"}, + {file = "snowflake_connector_python-3.12.3-cp38-cp38-win_amd64.whl", hash = "sha256:4994e95eff593dc44c28243ef0ae8d27b8b1aeb96dd64cbcea5bcf0e4dfb77fb"}, + {file = "snowflake_connector_python-3.12.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ac33a7dd54b35f94c4b91369971dbd6467a914dff4b01c46e77e7e6901d7eca4"}, + {file = "snowflake_connector_python-3.12.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:a26876322811fe2b93f6d814dcfe016f1df680a12624026ecf57a6bcdf20f969"}, + {file = "snowflake_connector_python-3.12.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6c0bb390be2e15b6b7cccab7fbe1ef94e1e9ab13790c974aa44761298cdc2641"}, + {file = "snowflake_connector_python-3.12.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e7340f73af4ae72e6af8fe28a1b8e196a0c99943071afc96ce419efb4da80035"}, + {file = "snowflake_connector_python-3.12.3-cp39-cp39-win_amd64.whl", hash = "sha256:c314749bd0151218b654a7d4646a39067ab650bdc86dfebb1884b056b0bdb4b4"}, + {file = "snowflake_connector_python-3.12.3.tar.gz", hash = "sha256:02873c7f7a3b10322e28dddc2be6907f8ab8ecad93d6d6af14c77c2f53091b88"}, ] [package.dependencies] @@ -5299,4 +5299,4 @@ test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", [metadata] lock-version = "2.0" python-versions = ">=3.11,<3.12" -content-hash = "ca7f650988117bd132b35001a4e3fa4c46e08d76ef0e2545fb481cacb8e2c1c5" +content-hash = "c021924b92984309ec0904f525c5287b51ff74b2a51017a48c714f85b802f92e" diff --git a/pyproject.toml b/pyproject.toml index 329a61b2e..bfbb2421d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ google-cloud-secret-manager = "^2.16.1" python-jose = "^3.3.0" jwcrypto = "^1.5.6" sqlalchemy = "1.4.41" -snowflake-connector-python = "3.12.1" +snowflake-connector-python = "3.12.3" openai = "1.37.1" opentelemetry-sdk = ">=1.20.0,<1.22" opentelemetry-instrumentation-fastapi = "^0.41b0" From f9358bea286529649992dc0aa802ade8e814fd3e Mon Sep 17 00:00:00 2001 From: Shahar Glazner Date: Tue, 29 Oct 2024 11:29:49 +0200 Subject: [PATCH 7/9] fix(workflows): email template (#2328) --- keep/api/routes/workflows.py | 2 +- keep/iohandler/iohandler.py | 5 +++-- pyproject.toml | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/keep/api/routes/workflows.py b/keep/api/routes/workflows.py index d4acac775..ae8a837e0 100644 --- a/keep/api/routes/workflows.py +++ b/keep/api/routes/workflows.py @@ -187,7 +187,7 @@ def run_workflow( else: event_class = IncidentDto - event_body = body.get("body", {}) + event_body = body.get("body", {}) or body # if its event that was triggered by the UI with the Modal if "test-workflow" in event_body.get("fingerprint", "") or not body: diff --git a/keep/iohandler/iohandler.py b/keep/iohandler/iohandler.py index 244ad561f..ea7467582 100644 --- a/keep/iohandler/iohandler.py +++ b/keep/iohandler/iohandler.py @@ -1,5 +1,6 @@ import ast import copy +import html # TODO: fix this! It screws up the eval statement if these are not imported import inspect @@ -311,8 +312,6 @@ def _parse(self, tree): # this is happens when libraries such as datadog api client # HTML escapes the string and then ast.parse fails () # https://github.com/keephq/keep/issues/137 - import html - try: unescaped_token = html.unescape( token.replace("\r\n", "").replace("\n", "") @@ -376,6 +375,8 @@ def _render(self, key: str, safe=False, default=""): return default if const_rendering: + # https://github.com/keephq/keep/issues/2326 + rendered = html.unescape(rendered) return self._render(rendered, safe, default) return rendered diff --git a/pyproject.toml b/pyproject.toml index bfbb2421d..758f57d9e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "keep" -version = "0.27.6" +version = "0.27.7" description = "Alerting. for developers, by developers." authors = ["Keep Alerting LTD"] readme = "README.md" From ab8c6ff2c34c691952ced03f1aa2a46caa3c384c Mon Sep 17 00:00:00 2001 From: Jay Kumar <70096901+35C4n0r@users.noreply.github.com> Date: Tue, 29 Oct 2024 15:40:17 +0530 Subject: [PATCH 8/9] feat: add Graylog Provider (#2305) Signed-off-by: 35C4n0r Co-authored-by: Shahar Glazner Co-authored-by: Tal --- README.md | 2 + docs/mint.json | 1 + .../documentation/graylog-provider.mdx | 59 ++ docs/providers/overview.mdx | 8 + keep-ui/public/icons/graylog-icon.png | Bin 0 -> 9285 bytes keep/providers/graylog_provider/README.md | 132 ++++ keep/providers/graylog_provider/__init__.py | 0 .../providers/graylog_provider/alerts_mock.py | 37 + .../graylog_provider/docker-compose.yml | 102 +++ .../graylog_provider/graylog_provider.py | 646 ++++++++++++++++++ 10 files changed, 987 insertions(+) create mode 100644 docs/providers/documentation/graylog-provider.mdx create mode 100644 keep-ui/public/icons/graylog-icon.png create mode 100644 keep/providers/graylog_provider/README.md create mode 100644 keep/providers/graylog_provider/__init__.py create mode 100644 keep/providers/graylog_provider/alerts_mock.py create mode 100644 keep/providers/graylog_provider/docker-compose.yml create mode 100644 keep/providers/graylog_provider/graylog_provider.py diff --git a/README.md b/README.md index a9864afe0..39bb76779 100644 --- a/README.md +++ b/README.md @@ -115,6 +115,8 @@ Workflow triggers can either be executed manually when an alert is activated or                       + +                       diff --git a/docs/mint.json b/docs/mint.json index 89f69ba23..d0bcdc482 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -140,6 +140,7 @@ "providers/documentation/grafana-provider", "providers/documentation/grafana_incident-provider", "providers/documentation/grafana_oncall-provider", + "providers/documentation/graylog-provider", "providers/documentation/http-provider", "providers/documentation/ilert-provider", "providers/documentation/incidentio-provider", diff --git a/docs/providers/documentation/graylog-provider.mdx b/docs/providers/documentation/graylog-provider.mdx new file mode 100644 index 000000000..9f6d9a160 --- /dev/null +++ b/docs/providers/documentation/graylog-provider.mdx @@ -0,0 +1,59 @@ +--- +title: "Graylog Provider" +sidebarTitle: "Graylog Provider" +description: "The Graylog provider enables webhook installations for receiving alerts in Keep" +--- + +## Overview + +The **Graylog Provider** facilitates receiving alerts from Graylog by setting up Webhook connections. It allows seamless integration with Graylog to receive notifications about events and alerts through Keep. + +## Authentication Parameters + +- **Username** (required): Username for authenticating with Graylog's API. +- **Graylog Access Token** (required): Access token for authenticating with Graylog's API. +- **Deployment Url** (required): Deployment URL for connecting to the Graylog instance (e.g., `http://localhost:9000`). + +## Scopes + +- **authenticated**: Mandatory for all operations, ensures the user is authenticated. +- **authorized**: Mandatory for querying incidents and managing resources, ensures the user has `Admin` privileges. + +## Connecting with the Provider + +1. Obtain the **username** and **access token** from your Graylog instance by following [Graylog's API Access Documentation](https://go2docs.graylog.org/current/setting_up_graylog/rest_api_access_tokens.htm?tocpath=Set%20up%20Graylog%7CGet%20Started%20with%20Graylog%7CREST%C2%A0API%7C_____3#CreateanAccessToken). +2. Set the **deployment URL** to your Graylog instance's base URL (e.g., `http://127.0.0.1:9000`). +3. Ensure the user has the **Admin** role in Graylog. + +## Features + +The **Graylog Provider** supports the following key features: + +- **Webhook Setup**: Configures webhooks to send alerts to Keep. +- **Alerts Retrieval**: Fetches and formats alerts from Graylog based on specified search parameters (only a maximum of 10000 most recent alerts) + +## Inputs for Query +- **events_search_parameters**: Takes in a python dict +Example: +``` +{ + "filter": {"alerts": "only"}, + "page": 1, + "per_page": 1000, + "query": "", + "timerange": {"range": 86400, "type": "relative"}, +} +``` +- You can modify this to fetch either alerts, events or both. + +--- + +**Note**: Ensure that the product of `page` and `per_page` does not exceed 10,000. + +--- + +## Useful Links + +- [Graylog API Documentation](https://go2docs.graylog.org/current/what_is_graylog/what_is_graylog.htm?tocpath=What%20Is%20Graylog%253F%7C_____0) +- [Graylog Access Token](https://go2docs.graylog.org/current/setting_up_graylog/rest_api_access_tokens.htm?tocpath=Set%20up%20Graylog%7CGet%20Started%20with%20Graylog%7CREST%C2%A0API%7C_____3#CreateanAccessToken) +- [Quick Setup for Graylog & Integration with Keep](https://github.com/keephq/keep/keep/providers/graylog_provider/README.md) diff --git a/docs/providers/overview.mdx b/docs/providers/overview.mdx index 088806517..c6ae298c0 100644 --- a/docs/providers/overview.mdx +++ b/docs/providers/overview.mdx @@ -268,6 +268,14 @@ By leveraging Keep Providers, users are able to deeply integrate Keep with the t } > + + } +> + qpiM=E6r!REc|%2YNP$9Dsi=I!si@YiDXfoFR2&|e4f=8v zhn$|4kt(IV*mm~6J~z709uJ=-FQ2vUoUQHdpPilAPM#Hi{TdP!WNK{O)71rj^hn?) zU)i_{l%l8gHqbGo;#Kp?`L%hYBbb6*u+>yoVGOO-x1$(z-p{mE=~n5v=x#Fk*nHHW zK)1bBO}*`HyrHsoUQkLy^-%nwgxCXdv4>Jd;$Yc_GO`aJii(TNiiwFZA^tnbE=H>=<_okwZ zHJ=^K9S-&KhT8KXoV*?R9KF3gUdw_&P~LgHxL49>mlY16Z3F?WnN0^qQqc1 zk6{RN@J85pL8<aU*lo2SS8Z>}j_{{OugJn}~(kwHO0e%V(seZR|_j*oeFNeTf$ zgwv{YGX8m&PNg|Eo4)4#)82v62TlX7UnkbyVec%Wcn#aXTreNQak|B@8Mdj!2|p;u zw%I0CkVS8&`y!okrwp$&4ZgYZ3kTy6^YOROh*)d+@@~S=ya|i4HWN?pDphS3W8Zpo zNsHd|*7WC)wdN&eru~eM+)zdu{ z!~0~-y>=tYRY3H^At_z_`edMVlh4mTG7(Ofm-BIIFY-F{$De}YpWgz==#MNaW)ARky%fMI&F!I5;+?&v0Y~&80 z<Gs-~BzjUiZBn4s6bxXc5jFc|Nza!W=pCko|2V!FpUQ zY6#D!#W-RO-%{T5Po)=s{4az@8SsZkuK6j7mRnfYlPm5%fC zt>X1b@rSD|KeRt^rcCb8;;amrGpATRfj4T`1N{B{gM+P5n-!C%VIbZU z<)3^LK9|DZDjdgr9C}KpE7oRP=`(qE2omE|Am%K-WB$~Infx;=^5Gxp;mdb3bM%0InhYy zwA*sM$;F@}Q%*DS@N&jTPlS`;GWwx`-a zzgQtb(H%v#|F*1ua2(cLzQ#SpH|c^{)BQnrQoC=J;6{Z~uW|cTgIkrK{B2cI_o;L8 z#JBpzZ#%-1$sk?dzS4wR^O^`Z(HFngQN<@!-bMKZ@IcDSA(&5s5FH~CuYiFn@>w@#MDKyx+FI{Yz$HF69?r;3P z_Zf-y?rU>=M7r8gbC-CSn)hnG*PsZOba8zCwnvuxK%omO;4LF!Fq$dr1rxn0wP0J| z3wqv?iRHmUJqeEo+m|CER6nGKA6InNn}-B!kvtr)^Ww&nQu%oU(7?&|sG8G_;xU2X zCRBBvc7Un5nWg~C!a7NG@A*|1i_5x7NLQlsh>(1#r%^`q-*Mc~Amv^kos#_I_C)`n z)b-QO${CbmgZJv~y&hV~3wxhZ`;Mt+e^LRM(i_gqPx1(=e^T9i{XXU0v?0R>_j;r- zqm<_ax%(3`)ZCjQ&6ghmJDlU2eCH{7L9m3d6qxSlmm$A^skFv<4R09!n; z_q^7AwCviub!~3{+vX3QO&Fh%S4h1y%G5QY(YeSGTDq1&MN+Il5#dYAQ*jABbnE33 zFx2CkD_4=F=hA4#&YJ`a$2lF-3qQ0yTj%)Lx!4(q`!ct~esk|!qN^NKoE>|rgIx;? zpH8QBHi=0cpNX}9#l56Ny{TQqk9Z_v;*mrT7%dapG+5AP<8G(mSH20rze*c=Bpqh! zp?jj`2U^TLy)=w57CJ?OHKiJcd(X99Fe1}b#jW>x`-CJ=`e^NI2C#SO+Ky&+LxZ#w zxT8;q>>=!2$7~RhUbA)U+9#Z;goJXDmxg%tl^G6)p}}i;J^ZHy^yyCVUp>tPyw!w{ z`-BWr#ENDLFJ}$KKt6PHf}(0VyBOVCP$q>!ixoKT^gJ=RrO=Mp9K4k(c^n-rR~^F% zqAtJmuo7HB?isab#0Yh@6Y6oIRXNseF6~io_9JJEqR%Ne1S-KLXA=lT+i9ep5}+^>cE) z2-fX5A}Q=Yr-^dJiCSasD7PJIp|fbBtS!q!whI{nUtdxrJ3WX}@|4_2zy zaj{DVN0S#2F{so7PG5^&vlJ94QCUqB>tVUlB&Hl5IQhv{Zilv#AbA(E?{-73tN_*R zz7eTh0I9#f`=lyQ{-y0APvUv01S4tQiMd_-n0SPbI{JYD$Q9c-kVYX8PTC#y*}IVQ z+7X+{04+dVd#Atf4m<^EndrJ&pElvE4sWY0<(M(@y*bf5nl$NqHiLKPEyizmMJBiu z$M5fk`r4U-J|RyMT<73oHQNNSp2KI@AayBki|U|PMt=6^*|o6lSJlFb`Rlfy#A3`F zOPe}b^oZn7z(LRc!$AGzO}p)&v+7d;O)A53{CN?SxHe0q4rJB}ZN*Z5K*@ZIyYzc< zzA$&d3#_%XMnD3>Qty37C1lH@d|)~RTiDJaLi=0*#!9?2>*$h2xTppA5x+m=UN1}} ze*{vh-s{;)sOkc?BU|FyZ@#A9i`{|TAn3FEeKrNv7gfAim$Y{7nn__}gL;AF_QFSAVoXwd^CU)0aH@YQ z4c|lcmb7!Ygf=3n3kX+Lun;8>t6wz&;FAn~Lskk5PZ?w&MUH%pj7mm)zqFTw%wwxe z1Uz8z+eHA3RbO5sMjX=e=5fSgPE&X^+?L{#kh6op3C&&piIe&F)Yc=?Tq$lAzv|h%5CIqs zQMKpc)jW=a`QD(Yd^ng}h^(|Pj!KPFB^(nab43_zur11<@A(y{gm2-h1gc_=Zor-k zy*)(J>b>#JC`Nm%#!ClayGB}hhp!=&bdprarzyG_2 zNlUax?O|tU9a$g`VRH|%q*-^`_)X9Tvw`Bcf~}oYU6E8HC~Y6sLT>$?)N$>?q&?X= zWfK7&n}deWfo4qFnRVM@mwBQ4EG~{4R*^NHbV8}b5*PsHZbP==03p63Im>LZ=K*Ch z?20YzWil0ZVq@^<8Sw@fjftcuJM{4`kLAoMJrg6+esSG$6=Iowi>V3QbHzscQXmx! zZuT|wg#dcqY8zP#v9_&d1^uq^oYQ2bEXt4L_Y(i&;=pIhJ+}nQVZ=*t*R%!HS;@!; zfR}2zKT(w_ZNiDesi*1#0Gf<2{4i5U>S=ftq}CJCIP(BS;)|Qyc)V0 zE?wu#;NyELqj0?xKTk(@L0mM0`;WnOLpd#Bx!T47(GuNc69}P2hqPNwY5qM+`c$v zL+0sFnD{-W)o>*313o^Ibcx+J2fI;;N@bi>SZ&j5BJz<8yojI^U~jq6Xpc#M;2p@F zFHaa2-h1IcH3FaT?Te7N6^!qkMqY=V%-GB%e%9+wSpF8;s&HC8W)ie5>pjX4jdxHu zHfVY=G+O3s&&dJY`8~^`f3Wy^{a{!h_FWiErR%0QT9)RYY6k0fH$92No4~N_t_i6> zUreq$M%XzTIh;hBZ0e;O%w=t5>!QH{ zF>pLudC|fwa7B`9yrc9yhecj!}kAtN{|yns>^Kl}fKOLpv{w z!BIbP{$TI39KKLOgtn~h1`3n&xn4zzN$%lP&nQgj1gHGGR~fT<~hLP&MTp6i$3 zZH8w&*A~oc-P^ydU=Cr2;m^MQvoqQl$|thrjE5u^^5+0h9j`+1P@XoRfh{dp(1_%D4EPIyY#ueI0tzLg7`IMJNje4_A{l-L!9U->eY&B zWtqm%%ob=;A!6sqMivyi7wPtshls+010rh4cU{NBV(>E)k;W-OX9?t1^n9@bZOxWz zcvCe%TWRy(W2=KA?l~*tn9ky=^*$FtNYrn?jHO7>mM%zE``e*ikq2`T!Ox9pMcSLN zL-$tj?aT6Z_kSUUWM_-uO<}23OKNAYc!zeRD!i>}_2s78BCSK9A-gsvwk`@V;1$OP zef&3wad|+wY|*mW`(QfKI4&K4Tvb}T^st6w;m;4i(8KZ-W%W-*`-SA^YJ{3kqjRSUDG3C{;AI4CPjguw9PD(?{b&he%ffSum4r?>taVXa(| zIFDg1m&oBt{P@HRiiPfuIhg5^?`(7Joh3BoeB%AavT=afPyjeCyt)vmbLet7j8ssE zzU~|q7P}IVsUJ5$y}mXQViCkN2B2AH6Rg)hdK_$V+MK(Pzxd21>IahCFd6d4g?oM5`fFKn!L6d8sIU{c zjA+ZFg0sF@xRj9U#}11p+wy*9$@XB7a#Z-^rRiziK&B@EnXXiPu8^5bGQo_XOm+w! zDi>q-iyQeoE&i5$<)*gMJ_NiJ1XX>mcv_7Xc|oaS_`U#Sz;Yr@yK2jkC<<8|LBOVO6<9ESx?H8B$CUP@p@b5IW~(o>P@>#&F7F<$gEGBHgV>yQ`PQ_ zxRI3U7$(V=hc|Zm5kHxo(5M0QOQueSFZ55PYCpq9ESeGW8J&7cogqsCNk5hc=|F?fWdZl zYi)i9EDog1%qMA@?aHYrRC^g4IM0?tcPjzY7267jglC(qoZOtc$xPI?c+Z~Cw2KazzKFdF&}{=uYX?sREX(@6vsF5@3T{5_YP3gnNg&iiSP zpGaPw(QZl~H!(AI1-hX^wIV{F)SrQb>sDbhPimf;_|7;eI%Q-G(& z7Tmwkz)pkksBnOSV!5D}dl6;a(KbacTi5+srmrmrC~M<0C>q$0oY9aps7(o>8;9;T zfBzIAa?A=1ZU%VfPJ1*FvCJ{M8<8Jwz}x(DtL}BSsl&Ct6EMjf$nogU`L0w9SWmCE z_R)+wkv4a8Y#W;!`t`uHAy7l|&o^7iU_Bxs%?dK?9+U;`0uZXw)!T&4G z?HzeVtS9%8%r67#273mHD$2v;BnXl!^D`?awLo-olik9|h4Gm2`V}W^EnP3bv)1)` zDHx_7iNzBPs55~JrZCU-nJ@8}-0cQUfTR0mi7Ga$Vhu{CJ0)S3ZB!qD3*Vs~IN&S# zELvQOj26JdJ^FR?yc{`Uzmbv%&N*!7thmn_I$1?FN#1r4>uJvJv%-s0twbu~%+rw7 zYLG!+`9fN0@V%;+uw4A32)2LV(%#XK?6|k1X{n|#Tt4HU?trXFxr-~2_P7km9a~t{ z$ho`E0s9Qmmb;C74Dh4r1B9}(U81Z(w!0UEAmzmuFj47!$P_gKSPzr!4#^Iuqyyi* zX$Bk4XB;h?9i>WiO)mw%T2hV26YHg@USNj|H$9Cp&l-Sg@pz|hG~J zzgaI8vJyFm%aGs6?vCe>d^8bJIx+7Ug7xau9l$aGx3q^pamFDo#*U~OLR@X&yr{gYn>7Ih5nOlrqq()n*8f`00ASH=?Q*| zv;S4+LgzsQ7=4&wfpw}cH%q=`T|)py4&y9u?qV;#kA`ABgZ7?> zx>Q|ToMif!O8aTT{R<%=nIi`$_ayg^R6Uutq_?ai0|sWq+k3v{P%vkAazIm#(_$Y6 znC%8-|6!d*Qj;d6CImr^<_z{20m`&Hpta8ZauO;JAOj&)-$Z_aCWv|PvRC3r=p}>T4Iy zCBRkwmfU%T=}z)xz&6WfNHn=qW-;kn=p(?>coX~ny@u%P{v-U%zRE&`3!&GU@|e^e zl;^aRD+~c&HB3Qve+$~tVk$0m@+U$(uFQ&ANan0`S(EPZi6*RPMU(Ob58 z+$BU~F8=jv68l43@AwpVax;)}!qVk>m(xE(@Vc+vhHyf(>)r7^DK#~;{{D4!`%*-A z9_qG0C>XQ-d<^;)E0%l(lk8GOEs#Nf)5|fsD><_3DCWA#`a>qS{%gY<0-@(LBbXo| zqEEMZYOy4>$t%Kex5E|-^H$&eAv#U4l9LM(-Gv~1>V5c zWg}ja3f;RCqB|i3RFTTYpMwOZiBT+9t2Q`w$j1CzGY#mEAs(yj#1aOpZM} z>6kmBI_OH;_+eiVebtSzaervg9YIlU=aEw5H+hx>l|N9dgS`0oP0j_xbE$?$OjGyiSbNti2|n~Tli=3poPa#)yVZWV6h z&3~$f*CKyZ{8P1g@RW3Ou!tM-^5CXk`Y7b+K9`Q30jM9Lc$;K^RyU%r4&CTC>4p1) zx^?soVZT?tLD8cdS7du*d3Z9)Kat>8TiKsITK_1P6Kzv{wz3IjCvr;vUNXJ%o;c{? zmHi4=*Z)gk;&A7R;tkG&mk<6W{GtV;YG`U6$9Ap}1bLuz1#Vkcd2;5kQN#KFq99W4 zB{89z^ZMS{x;Vnq-^b(3hZ`cg4Q0ts4%Z`ROb$0J-(wNL!0Ww;0wCOK4SiM51U8w{ zDB9t$Pj|RM?ftbDvtx{~JaoUXPRP)2g!R0hqzbGHp(PwOXx*NrEz_J8`x3e@5Mo=L;Op z2$qspeZQh*(KRBRdxMCt`INtlVtwc9j7NFqvU1kG8^J0T*C)TiiYznJ)&nh7DreIQ zk9z!D#~kJ3?cY$XA;#{nZOm7zmN=C{HkaFu)Kda!YDASkx9-1*Fga-iuY2pxn*s7W zsz^%Ma?RjFo?2#7a6++VQei>c=G010Fd}=30Dt*tZoCN5b@l#X6gd35BB^I6!1c&@ zYo~uSWY!$PAnajuJ-_=#_k&Bo8(TenUWhH$bWV~co#6!_!+xq{xQI~8zCp9LKs)+} z#|=b5`pZkRkJ3umk53&wdg}V-!3!!G8=Dvl>y5VNP_y>|lNA}_8+QkTtRA%TRdwu; zNS4hJCgC$1&KAPM(CM+3C;uRo+!7Xo3u8yQW*m3s89OZ{Eof%V@1;$NKUn?eo;!ZJ zxS!~zXo~cl5FOvRyC5Vxy_DOHdJp<>K*Us9Pa7|HTY1+D(h@<|TyGiI>*JY*Z0sFF zHdn8)*D|_Av+{&Be{=5lsI2Is5w3}pE)Ae=dxjEw@=JCpB*pBtQmR8j`fGznAuFO6 z-0+t_f#}`vOrJosZg|#DF!}G6ug4Eww7TOj{0VT$CCP@aJiakm#w%T~9TxscRJuO3 zt$mKkAxh~Zr(EkFJ*w;BhU1Ur!hZkcZd>)g8fp1k{4k-lg2>ymFEpUV$UNtn_srpU4}0C4j`d?i&Ue+GR(Ge&AIL;FRmTDlvlmC^4C1Slg}oV`$3Mvi2F0@+NVl)8Lm#GmE0z! z%abzeZ6APVozn7e-yC1}660&)N%RQKyc8zSC=j{ UU!JABmqPVSRY#>v$vW&m0ES^KZ2$lO literal 0 HcmV?d00001 diff --git a/keep/providers/graylog_provider/README.md b/keep/providers/graylog_provider/README.md new file mode 100644 index 000000000..f1d327f7c --- /dev/null +++ b/keep/providers/graylog_provider/README.md @@ -0,0 +1,132 @@ +# Instructions for a quick setup + +## Setting up Graylog + +### Installation + +1. Spin up Graylog, [docs](https://go2docs.graylog.org/6-0/downloading_and_installing_graylog/docker_installation.htm) + ```bash + cd keep/providers/graylog_provider + docker compose up + ``` +2. Once the containers are up and running, go to [http://localhost:9000](http://localhost:9000) and sign in with + username `admin` & password `admin`. + +### Getting Access Token + +1. Navigate to System > Users and Teams to view the Users Overview page. +2. For the user `Admin`, select Edit tokens from the More drop-down menu. +3. Enter a token name, then click Create Token. + +### Setting up Inputs and Event Definition + + ```python +import requests + +auth = ("YOUR_ACCESS_TOKEN", "token") # from the previous step +headers = { + "Accept": "application/json", + "X-Requested-By": "Keep", + "Content-Type": "application/json", +} + +input_data = { + 'type': 'org.graylog2.inputs.raw.tcp.RawTCPInput', + 'configuration': { + 'bind_address': '0.0.0.0', + 'port': 5044, + 'recv_buffer_size': 1048576, + 'number_worker_threads': 3, + 'tls_cert_file': '', + 'tls_key_file': '', + 'tls_enable': False, + 'tls_key_password': '', + 'tls_client_auth': 'disabled', + 'tls_client_auth_cert_file': '', + 'tcp_keepalive': False, + 'use_null_delimiter': False, + 'max_message_size': 2097152, + 'override_source': None, + 'charset_name': 'UTF-8', + }, + 'title': 'Keep-Input', + 'global': True, +} + +input_response = requests.post( + url="http://127.0.0.1:9000/api/system/inputs", + headers=headers, + json=input_data, + auth=auth, +) + +print(input_response.text) + +event_data = { + 'title': 'Keep-Event', + 'description': 'This is an event for Keep', + 'priority': 3, + 'config': { + 'query': 'source:*', + 'query_parameters': [], + 'streams': [], + 'filters': [], + 'search_within_ms': 86400000, + 'execute_every_ms': 60000, + 'event_limit': 100, + 'group_by': [], + 'series': [], + 'conditions': {}, + 'type': 'aggregation-v1', + }, + 'field_spec': {}, + 'key_spec': [], + 'notification_settings': { + 'grace_period_ms': 300000, + 'backlog_size': None, + }, + 'notifications': [], + 'alert': True, +} + +event_response = requests.post( + url="http://127.0.0.1:9000/api/events/definitions", + headers=headers, + json=event_data, + auth=auth, +) + +print(event_response.text) + ``` + +### Sending a log + +1. After that you can send a plain text message to the Graylog raw/plaintext TCP input running on port 5555 using the + following command: + ```bash + echo 'First log message' | nc localhost 5555 + ``` + +## Setup Keep to receive from Graylog + +--- + +### **Note** + +1. Run without `NGROK` +2. After Step 2, do this: + - Go to Alerts > Notifications + - Click the `title` of the newly create notification > `Edit Notification` > Replace `0.0.0.0` with your ip + address > Click `Add to URL whitelist ` > Fill in the `Title` > `Update Configuration` > `Update Notification` + +--- + +1. Go to `Providers` > search for `Graylog` > + - Username: `admin` + - Graylog Access Token: Access tokens from previous steps + - Deployment Url: http://localhost:9000 + - Install webhook: True + +2. This will create a new notification and install that notification in the existing events. +3. Send a log to `Graylog`, this will trigger an alert. +4. Check your feed. \ No newline at end of file diff --git a/keep/providers/graylog_provider/__init__.py b/keep/providers/graylog_provider/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/keep/providers/graylog_provider/alerts_mock.py b/keep/providers/graylog_provider/alerts_mock.py new file mode 100644 index 000000000..ff3f0ebb7 --- /dev/null +++ b/keep/providers/graylog_provider/alerts_mock.py @@ -0,0 +1,37 @@ +ALERTS = { + "event_definition_id": "671a28a03696bb3801a7a9f1", + "event_definition_type": "aggregation-v1", + "event_definition_title": "Event - 1", + "event_definition_description": ".", + "job_definition_id": "671a97cc3696bb3801a846a6", + "job_trigger_id": "671a9dfe3696bb3801a8536d", + "event": { + "id": "01JAZZJAKS82TDZAE82E0WAENT", + "event_definition_type": "aggregation-v1", + "event_definition_id": "671a28a03696bb3801a7a9f1", + "origin_context": "urn:graylog:message:es:graylog_0:d0a9a7a0-91f1-11ef-9a79-0242ac170004", + "timestamp": "2024-10-24T10:22:04.556Z", + "timestamp_processing": "2024-10-24T19:20:30.585Z", + "timerange_start": None, + "timerange_end": None, + "streams": [], + "source_streams": ["000000000000000000000001"], + "message": "Event - 1", + "source": "server", + "key_tuple": [], + "key": "", + "priority": 3, + "scores": {}, + "alert": True, + "fields": {}, + "group_by_fields": {}, + "replay_info": { + "timerange_start": "2024-10-23T19:20:29.706Z", + "timerange_end": "2024-10-24T19:20:29.706Z", + "query": "source:172.23.0.1", + "streams": ["000000000000000000000001"], + "filters": [], + }, + }, + "backlog": [], +} diff --git a/keep/providers/graylog_provider/docker-compose.yml b/keep/providers/graylog_provider/docker-compose.yml new file mode 100644 index 000000000..7380508d4 --- /dev/null +++ b/keep/providers/graylog_provider/docker-compose.yml @@ -0,0 +1,102 @@ +version: '3' + +services: + # MongoDB: https://hub.docker.com/_/mongo/ + mongodb: + image: "mongo:6.0.18" + ports: + - "27017:27017" + restart: "on-failure" + networks: + - graylog + volumes: + - "mongodb_data:/data/db" + + opensearch: + image: "opensearchproject/opensearch:2.15.0" + environment: + - "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g" + - "bootstrap.memory_lock=true" + - "discovery.type=single-node" + - "action.auto_create_index=false" + - "plugins.security.ssl.http.enabled=false" + - "plugins.security.disabled=true" + # Can generate a password for `OPENSEARCH_INITIAL_ADMIN_PASSWORD` using a linux device via: + # tr -dc A-Z-a-z-0-9_@#%^-_=+ < /dev/urandom | head -c${1:-32} + - "OPENSEARCH_INITIAL_ADMIN_PASSWORD=+_8r#wliY3Pv5-HMIf4qzXImYzZf-M=M" + ulimits: + memlock: + hard: -1 + soft: -1 + nofile: + soft: 65536 + hard: 65536 + ports: + - "9203:9200" + - "9303:9300" + restart: "on-failure" + networks: + - graylog + volumes: + - "opensearch:/usr/share/opensearch/data" + + # Graylog: https://hub.docker.com/r/graylog/graylog/ + graylog: + hostname: "server" + image: "graylog/graylog:6.0" + # To install Graylog Open: "graylog/graylog:6.0" + depends_on: + mongodb: + condition: "service_started" + opensearch: + condition: "service_started" + entrypoint: "/usr/bin/tini -- wait-for-it opensearch:9200 -- /docker-entrypoint.sh" + environment: + GRAYLOG_NODE_ID_FILE: "/usr/share/graylog/data/config/node-id" + GRAYLOG_HTTP_BIND_ADDRESS: "0.0.0.0:9000" + GRAYLOG_ELASTICSEARCH_HOSTS: "http://opensearch:9200" + GRAYLOG_MONGODB_URI: "mongodb://mongodb:27017/graylog" + # To make reporting (headless_shell) work inside a Docker container + GRAYLOG_REPORT_DISABLE_SANDBOX: "true" + # CHANGE ME (must be at least 16 characters)! + GRAYLOG_PASSWORD_SECRET: "somepasswordpepper" + # Password: "admin" + GRAYLOG_ROOT_PASSWORD_SHA2: "8c6976e5b5410415bde908bd4dee15dfb167a9c873fc4bb8a81f6f2ab448a918" + GRAYLOG_HTTP_EXTERNAL_URI: "http://127.0.0.1:9000/" + ports: + # Graylog web interface and REST API + - "9000:9000/tcp" + # Beats + - "5044:5044/tcp" + # Exposing for TCP Ingestion + - "5555:5555/tcp" + # Syslog TCP + - "5140:5140/tcp" + # Syslog UDP + - "5140:5140/udp" + # GELF TCP + - "12201:12201/tcp" + # GELF UDP + - "12201:12201/udp" + # Forwarder data + - "13301:13301/tcp" + # Forwarder config + - "13302:13302/tcp" + restart: "on-failure" + networks: + - graylog + volumes: + - "graylog_data:/usr/share/graylog/data/data" + - "graylog_config:/usr/share/graylog/data/config" + - "graylog_journal:/usr/share/graylog/data/journal" + +networks: + graylog: + driver: "bridge" + +volumes: + mongodb_data: + opensearch: + graylog_data: + graylog_config: + graylog_journal: \ No newline at end of file diff --git a/keep/providers/graylog_provider/graylog_provider.py b/keep/providers/graylog_provider/graylog_provider.py new file mode 100644 index 000000000..20ad94d4c --- /dev/null +++ b/keep/providers/graylog_provider/graylog_provider.py @@ -0,0 +1,646 @@ +""" +Graylog Provider is a class that allows to install webhooks in Graylog. +""" + +import dataclasses +import math +import uuid +from datetime import datetime, timezone, timedelta +from typing import List +from urllib.parse import urlencode, urljoin, urlparse + +import pydantic +import requests + +from keep.api.models.alert import AlertDto, AlertSeverity, AlertStatus +from keep.contextmanager.contextmanager import ContextManager +from keep.providers.base.base_provider import BaseProvider +from keep.providers.models.provider_config import ProviderConfig, ProviderScope + + +class ResourceAlreadyExists(Exception): + def __init__(self, *args): + super().__init__(*args) + + +@pydantic.dataclasses.dataclass +class GraylogProviderAuthConfig: + """ + Graylog authentication configuration. + """ + + graylog_user_name: str = dataclasses.field( + metadata={ + "required": True, + "description": "Username", + "hint": "Your Username associated with the Access Token", + }, + ) + graylog_access_token: str = dataclasses.field( + metadata={ + "required": True, + "description": "Graylog Access Token", + "hint": "Graylog Access Token ", + "sensitive": True, + }, + ) + deployment_url: str = dataclasses.field( + metadata={ + "required": True, + "description": "Deployment Url", + "hint": "Example: http://127.0.0.1:9000", + }, + ) + + +class GraylogProvider(BaseProvider): + """Install Webhooks and receive alerts from Graylog.""" + + webhook_description = "" + webhook_template = "" + webhook_markdown = """ +💡 For more details on how to configure Graylog to send alerts to Keep, see the [Keep documentation](https://docs.keephq.dev/providers/documentation/graylog-provider). 💡 + +To send alerts from Graylog to Keep, Use the following webhook url to configure Graylog send alerts to Keep: + +1. In Graylog, from the Topbar, go to `Alerts` > `Notifications`. +2. Click "Create Notification". +3. In the New Notification form, configure: + +- **Display Name**: keep-graylog-webhook-integration +- **Title**: keep-graylog-webhook-integration +- **Notification Type**: Custom HTTP Notification +- **URL**: {keep_webhook_api_url} # Whitelist this URL +- **Headers**: X-API-KEY:{api_key} +4. Erase the Body Template. +5. Click on "Create Notification". +6. Go the the `Event Definitions` tab, and select the Event Definition that will trigger the alert you want to send to Keep and click on More > Edit. +7. Go to "Notifications" tab. +8. Click on "Add Notification" and select the "keep-graylog-webhook-integration" that you created in step 3. +9. Click on "Add Notification". +10. Click `Next` > `Update` event definition +""" + PROVIDER_DISPLAY_NAME = "Graylog" + PROVIDER_SCOPES = [ + ProviderScope( + name="authenticated", + description="User is Authorized", + mandatory=True, + mandatory_for_webhook=True, + alias="Rules Reader", + ), + ProviderScope( + name="authorized", + description="Required privileges", + mandatory=True, + mandatory_for_webhook=True, + alias="Rules Reader", + ), + ] + + FINGERPRINT_FIELDS = ["event_definition_id"] + + def __init__( + self, context_manager: ContextManager, provider_id: str, config: ProviderConfig + ): + super().__init__(context_manager, provider_id, config) + self._host = None + + def dispose(self): + """ + Dispose the provider. + """ + pass + + def validate_config(self): + """ + Validates required configuration for Graylog provider. + """ + self.logger.debug("Validating configuration for Graylog provider") + self.authentication_config = GraylogProviderAuthConfig( + **self.config.authentication + ) + + @property + def graylog_host(self): + self.logger.debug("Fetching Graylog host") + if self._host: + self.logger.debug("Returning cached Graylog host") + return self._host + + # Handle host determination logic with logging + if self.authentication_config.deployment_url.startswith( + "http://" + ) or self.authentication_config.deployment_url.startswith("https://"): + self.logger.info("Using supplied Graylog host with protocol") + self._host = self.authentication_config.deployment_url + return self._host + + # Otherwise, attempt to use https + try: + self.logger.debug( + f"Trying HTTPS for {self.authentication_config.deployment_url}" + ) + requests.get( + f"https://{self.authentication_config.deployment_url}", + verify=False, + ) + self.logger.info("HTTPS protocol confirmed") + self._host = f"https://{self.authentication_config.deployment_url}" + except requests.exceptions.SSLError: + self.logger.warning("SSL error encountered, falling back to HTTP") + self._host = f"http://{self.authentication_config.deployment_url}" + except Exception as e: + self.logger.error( + "Failed to determine Graylog host", extra={"exception": str(e)} + ) + self._host = self.authentication_config.deployment_url.rstrip("/") + + return self._host + + @property + def _headers(self): + return { + "Accept": "application/json", + "X-Requested-By": "Keep", + } + + @property + def _auth(self): + return self.authentication_config.graylog_access_token, "token" + + def __get_url(self, paths: List[str] = [], query_params: dict = None, **kwargs): + """ + Helper method to build the url for Graylog api requests. + """ + host = self.graylog_host.rstrip("/").rstrip() + "/api/" + self.logger.info(f"Building URL with host: {host}") + url = urljoin( + host, + "/".join(str(path) for path in paths), + ) + + # add query params + if query_params: + url = f"{url}?{urlencode(query_params)}" + + self.logger.debug(f"Constructed URL: {url}") + return url + + def validate_scopes(self) -> dict[str, bool | str]: + self.logger.info("Validating user scopes for Graylog provider") + required_role = "Admin" + + try: + user_response = requests.get( + url=self.__get_url( + paths=["users", self.authentication_config.graylog_user_name] + ), + headers=self._headers, + auth=self._auth, + ) + self.logger.debug("User information request sent") + if user_response.status_code != 200: + raise Exception(user_response.text) + + authenticated = True + user_response = user_response.json() + if required_role in user_response["roles"]: + self.logger.info("User has required admin privileges") + authorized = True + else: + self.logger.warning("User lacks required admin privileges") + authorized = "Missing admin Privileges" + + except Exception as e: + self.logger.error( + "Error while validating user scopes", extra={"exception": str(e)} + ) + authenticated = str(e) + authorized = False + + return { + "authenticated": authenticated, + "authorized": authorized, + } + + def __get_url_whitelist(self): + try: + self.logger.info("Fetching URL Whitelist") + whitelist_response = requests.get( + url=self.__get_url(paths=["system/urlwhitelist"]), + headers=self._headers, + auth=self._auth, + timeout=10, + ) + if whitelist_response.status_code != 200: + raise Exception(whitelist_response.text) + self.logger.info("Successfully retrieved URL Whitelist") + return whitelist_response.json() + except Exception as e: + self.logger.error( + "Error while fetching URL whitelist", extra={"exception": str(e)} + ) + raise e + + def __update_url_whitelist(self, whitelist): + try: + self.logger.info("Updating URL whitelist") + whitelist_response = requests.put( + url=self.__get_url(paths=["system/urlwhitelist"]), + headers=self._headers, + auth=self._auth, + json=whitelist, + ) + if whitelist_response.status_code != 204: + raise Exception(whitelist_response.text) + self.logger.info("Successfully updated URL whitelist") + except Exception as e: + self.logger.error( + "Error while updating URL whitelist", extra={"exception": str(e)} + ) + raise e + + def __get_events(self, page: int, per_page: int): + self.logger.info( + f"Fetching events from Graylog (page: {page}, per_page: {per_page})" + ) + try: + events_response = requests.get( + url=self.__get_url(paths=["events", "definitions"]), + headers=self._headers, + auth=self._auth, + params={"page": page, "per_page": per_page}, + ) + + if events_response.status_code != 200: + raise Exception(events_response.text) + + events_response = events_response.json() + self.logger.info("Successfully fetched events from Graylog") + return events_response + + except Exception as e: + self.logger.error( + "Error while fetching events", extra={"exception": str(e)} + ) + raise e + + def __update_event(self, event): + try: + self.logger.info(f"Updating event with ID: {event['id']}") + event_update_response = requests.put( + url=self.__get_url(paths=["events", "definitions", event["id"]]), + timeout=10, + json=event, + auth=self._auth, + headers=self._headers, + ) + + if event_update_response.status_code != 200: + raise Exception(event_update_response.text) + + self.logger.info(f"Successfully updated event with ID: {event['id']}") + + except Exception as e: + self.logger.error( + f"Error while updating event with ID: {event['id']}", + extra={"exception": str(e)}, + ) + raise e + + def __get_notification(self, page: int, per_page: int, notification_name: str): + try: + self.logger.info(f"Fetching notification: {notification_name}") + notifications_response = requests.get( + url=self.__get_url(paths=["events", "notifications"]), + params={ + "page": page, + "per_page": per_page, + "query": f"title:{notification_name}", + }, + auth=self._auth, + headers=self._headers, + timeout=10, + ) + if notifications_response.status_code != 200: + raise Exception(notifications_response.text) + self.logger.info(f"Successfully fetched notification: {notification_name}") + return notifications_response.json() + except Exception as e: + self.logger.error( + f"Error while fetching notification {notification_name}", + extra={"exception": str(e)}, + ) + raise e + + def __delete_notification(self, notification_id: str): + try: + self.logger.info( + f"Attempting to delete notification with ID: {notification_id}" + ) + notification_delete_response = requests.delete( + url=self.__get_url(paths=["events", "notifications", notification_id]), + auth=self._auth, + headers=self._headers, + ) + if notification_delete_response.status_code != 204: + raise Exception(notification_delete_response.text) + + self.logger.info( + f"Successfully deleted notification with ID: {notification_id}" + ) + + except Exception as e: + self.logger.error( + f"Error while deleting notification with ID {notification_id}", + extra={"exception": str(e)}, + ) + raise e + + def __create_notification(self, notification_name: str, notification_body): + try: + self.logger.info(f"Attempting to create notification: {notification_name}") + notification_creation_response = requests.post( + url=self.__get_url(paths=["events", "notifications"]), + headers=self._headers, + auth=self._auth, + timeout=10, + json=notification_body, + ) + if notification_creation_response.status_code != 200: + raise Exception(notification_creation_response.text) + + self.logger.info(f"Successfully created notification: {notification_name}") + return notification_creation_response.json() + except Exception as e: + self.logger.error( + f"Error while creating notification {notification_name}", + extra={"exception": str(e)}, + ) + raise e + + def __update_notification(self, notification_id: str, notification_body): + try: + self.logger.info( + f"Attempting to update notification with ID: {notification_id}" + ) + notification_update_response = requests.put( + url=self.__get_url(paths=["events", "notifications", notification_id]), + headers=self._headers, + auth=self._auth, + timeout=10, + json=notification_body, + ) + if notification_update_response.status_code != 200: + raise Exception(notification_update_response.text) + + self.logger.info( + f"Successfully updated notification with ID: {notification_id}" + ) + return notification_update_response.json() + except Exception as e: + self.logger.error( + f"Error while updating notification with ID {notification_id}", + extra={"exception": str(e)}, + ) + raise e + + def setup_webhook( + self, tenant_id: str, keep_api_url: str, api_key: str, setup_alerts: bool = True + ): + self.logger.info("Setting up webhook in Graylog") + try: + event_definitions = [] + events_1 = self.__get_events(page=1, per_page=100) + event_definitions.extend(events_1["event_definitions"]) + total_pages = math.ceil(int(events_1["total"]) / 100) + + for page in range(2, total_pages): + self.logger.debug(f"Fetching events page: {page}") + event_definitions.extend( + self.__get_events(page=page, per_page=100)["event_definitions"] + ) + + # Extracting provider_id from the keep_api_url + parsed_url = urlparse(keep_api_url) + query_params = parsed_url.query + provider_id = query_params.split("provider_id=")[-1] + notification_name = f"Keep-{provider_id}" + + # Whitelist URL + url_whitelist = self.__get_url_whitelist() + url_found = False + for entry in url_whitelist["entries"]: + if entry["value"] == keep_api_url: + self.logger.info("URL already whitelisted") + url_found = True + break + if not url_found: + self.logger.info("Adding URL to whitelist") + url_whitelist["entries"].append( + { + "id": str(uuid.uuid4()), + "title": notification_name, + "value": keep_api_url, + "type": "literal", + } + ) + self.__update_url_whitelist(url_whitelist) + + # Create notification + notification = self.__get_notification( + page=1, per_page=1, notification_name=notification_name + ) + if int(notification["count"]) > 0: + self.logger.info("Notification already exists, deleting it") + self.__delete_notification( + notification_id=notification["notifications"][0]["id"] + ) + + self.logger.info("Creating new notification") + notification_body = { + "title": notification_name, + "description": "Hello, this Notification is created by Keep, please do not change the title.", + "config": { + "type": "http-notification-v2", + "basic_auth": None, + "api_key_as_header": False, + "api_key": "", + "api_secret": None, + "url": keep_api_url, + "skip_tls_verification": True, + "method": "POST", + "time_zone": "UTC", + "content_type": "JSON", + "headers": f"X-API-KEY:{api_key}", + "body_template": "", + }, + } + new_notification = self.__create_notification( + notification_name=notification_name, notification_body=notification_body + ) + + for event_definition in event_definitions: + if event_definition["_scope"] == "SYSTEM_NOTIFICATION_EVENT": + self.logger.info("Skipping SYSTEM_NOTIFICATION_EVENT") + continue + self.logger.info(f"Updating event with ID: {event_definition['id']}") + event_definition["notifications"].append( + {"notification_id": new_notification["id"]} + ) + self.__update_event(event=event_definition) + + self.logger.info("Webhook setup completed successfully") + except Exception as e: + self.logger.error( + "Error while setting up webhook", extra={"exception": str(e)} + ) + raise e + + @staticmethod + def __map_event_to_alert(event: dict) -> AlertDto: + alert = AlertDto( + id=event["event"]["id"], + name=event.get("event_definition_title", event["event"]["message"]), + severity=[AlertSeverity.LOW, AlertSeverity.WARNING, AlertSeverity.HIGH][ + int(event["event"]["priority"]) - 1 + ], + description=event.get("event_definition_description", None), + event_definition_id=event["event"]["event_definition_id"], + origin_context=event["event"].get("origin_context", None), + status=AlertStatus.FIRING, + lastReceived=datetime.fromisoformat( + event["event"]["timestamp"].replace("z", "") + ) + .replace(tzinfo=timezone.utc) + .isoformat(), + message=event["event"].get("message", None), + source=["graylog"], + ) + + alert.fingerprint = GraylogProvider.get_alert_fingerprint( + alert, GraylogProvider.FINGERPRINT_FIELDS + ) + + return alert + + @staticmethod + def _format_alert(event: dict, provider_instance: BaseProvider) -> AlertDto: + return GraylogProvider.__map_event_to_alert(event=event) + + @classmethod + def simulate_alert(cls) -> dict: + from keep.providers.graylog_provider.alerts_mock import ALERTS + import random + import string + + # Use the provided ALERTS structure + alert_data = ALERTS.copy() + + # Start with the base event payload + simulated_alert = alert_data["event"] + + alert_data["event_definition_title"] = random.choice( + [ + "EventDefinition - 1", + "EventDefinition - 2", + "EventDefinition - 3", + ] + ) + + alert_data["event_definition_description"] = random.choice( + [ + "Description - add", + "Description - commit", + "Description - push", + ] + ) + + # Apply variability to the event message and priority + simulated_alert["message"] = alert_data["event_definition_title"] + simulated_alert["priority"] = random.choice([1, 2, 3]) + chars = string.ascii_uppercase + string.digits + # Generate a random ID of specified length + random_id = "".join(random.choice(chars) for _ in range(25)) + simulated_alert["id"] = random_id + + simulated_alert["event_definition_id"] = alert_data["event_definition_id"] = ( + "".join( + random.choice(string.ascii_lowercase + string.digits) for _ in range(24) + ) + ) + + # Set the current timestamp + simulated_alert["timestamp"] = datetime.now().isoformat() + + # Apply variability to replay_info + replay_info = simulated_alert.get("replay_info", {}) + replay_info["timerange_start"] = ( + datetime.now() - timedelta(hours=1) + ).isoformat() + replay_info["timerange_end"] = datetime.now().isoformat() + + simulated_alert["replay_info"] = replay_info + + return alert_data + + def __get_alerts(self, json_data: dict): + try: + self.logger.info( + f"Fetching alerts (page: {json_data['page']}, per_page: {json_data['per_page']})" + ) + alert_response = requests.post( + url=self.__get_url(paths=["events", "search"]), + headers=self._headers, + auth=self._auth, + timeout=10, + json=json_data, + ) + + if alert_response.status_code != 200: + raise Exception(alert_response.text) + + self.logger.info("Successfully fetched alerts") + return alert_response.json() + + except Exception as e: + self.logger.error( + "Error while fetching alerts", extra={"exception": str(e)} + ) + raise e + + def _get_alerts(self) -> list[AlertDto]: + self.logger.info("Getting alerts from Graylog") + json_data = { + "query": "", + "page": 1, + "per_page": 1000, + "filter": { + "alerts": "only", + }, + "timerange": { + "range": 1 * 24 * 60 * 60, + "type": "relative", + }, + } + all_alerts = [] + alerts_1 = self.__get_alerts(json_data=json_data) + all_alerts.extend(alerts_1["events"]) + total_events = max(10, math.ceil(alerts_1["total_events"] / 1000)) + + for page in range(2, total_events + 1): + self.logger.debug(f"Fetching alerts page: {page}") + json_data["page"] = page + alerts = self.__get_alerts(json_data=json_data) + all_alerts.extend(alerts["events"]) + + self.logger.info("Successfully fetched all alerts") + return [ + GraylogProvider.__map_event_to_alert(event=event) for event in all_alerts + ] + + def _query(self, events_search_parameters: dict, **kwargs: dict): + self.logger.info("Querying Graylog with specified parameters") + alerts = self.__get_alerts(json_data=events_search_parameters)["events"] + return [GraylogProvider.__map_event_to_alert(event=event) for event in alerts] From f549b053b30a5104ad7e4cc88ba8c6606e1ae2a2 Mon Sep 17 00:00:00 2001 From: Kirill Chernakov Date: Tue, 29 Oct 2024 14:59:28 +0400 Subject: [PATCH 9/9] feat: merge incidents (#2274) Signed-off-by: Kirill Chernakov --- docs/api-ref/alerts/get-alert-quality.mdx | 3 + docs/api-ref/incidents/add-comment.mdx | 3 + .../get-future-incidents-for-an-incident.mdx | 3 + .../incidents/get-incident-workflows.mdx | 3 + docs/api-ref/incidents/get-incidents-meta.mdx | 3 + docs/api-ref/incidents/merge-incidents.mdx | 3 + docs/mint.json | 10 +- docs/openapi.json | 2 +- keep-ui/app/alerts/alert-pagination.tsx | 121 ++++--- keep-ui/app/globals.css | 42 +++ .../app/incidents/[id]/incident-activity.tsx | 16 +- .../incidents/[id]/incident-alert-menu.tsx | 6 +- .../app/incidents/[id]/incident-alerts.tsx | 151 ++++---- keep-ui/app/incidents/[id]/incident-chat.tsx | 45 +-- .../app/incidents/[id]/incident-header.tsx | 172 +++++++++ ...ncident-info.tsx => incident-overview.tsx} | 208 ++--------- .../app/incidents/[id]/incident-timeline.tsx | 5 +- .../[id]/incident-workflow-table.tsx | 109 +++--- keep-ui/app/incidents/[id]/incident.tsx | 146 ++++---- .../incidents/create-or-update-incident.tsx | 2 + .../incidents/incident-candidate-actions.tsx | 39 +- .../incident-change-status-modal.tsx | 14 +- .../app/incidents/incident-dropdown-menu.tsx | 58 +++ keep-ui/app/incidents/incident-list-error.tsx | 24 ++ ...lder.tsx => incident-list-placeholder.tsx} | 8 +- .../{incident.tsx => incident-list.tsx} | 81 +++-- .../app/incidents/incident-merge-modal.tsx | 204 +++++++++++ keep-ui/app/incidents/incident-pagination.tsx | 18 +- .../incidents/incident-table-component.tsx | 102 +++--- .../incident-table-filters-context.tsx | 99 ++++-- keep-ui/app/incidents/incidents-table.tsx | 335 ++++++++++++------ keep-ui/app/incidents/models.ts | 7 +- keep-ui/app/incidents/page.tsx | 7 +- .../app/incidents/react-quill-override.css | 3 + keep-ui/app/incidents/statuses.tsx | 52 ++- .../app/incidents/vertical-rounded-list.css | 19 + .../ui/DropdownMenu/DropdownMenu.css | 73 ++++ .../ui/DropdownMenu/DropdownMenu.tsx | 285 +++++++++++++++ keep-ui/components/ui/DropdownMenu/index.ts | 1 + keep-ui/components/ui/table/utils.ts | 33 ++ .../incidents/model/useIncidentActions.tsx | 60 ++++ .../features/change-incident-status/index.ts | 1 + .../ui/incident-change-status-select.tsx | 90 +++++ keep-ui/package-lock.json | 8 +- keep-ui/package.json | 2 +- keep-ui/tailwind.config.js | 8 + keep-ui/tsconfig.json | 2 + keep-ui/utils/hooks/useIncidents.ts | 9 +- keep/api/core/db.py | 78 +++- keep/api/models/alert.py | 29 +- keep/api/models/db/alert.py | 33 +- .../versions/2024-10-23-15-21_89b4d3905d26.py | 50 +++ keep/api/routes/incidents.py | 54 +++ keep/api/utils/pluralize.py | 26 ++ tests/test_incidents.py | 314 ++++++++++++++-- 55 files changed, 2514 insertions(+), 765 deletions(-) create mode 100644 docs/api-ref/alerts/get-alert-quality.mdx create mode 100644 docs/api-ref/incidents/add-comment.mdx create mode 100644 docs/api-ref/incidents/get-future-incidents-for-an-incident.mdx create mode 100644 docs/api-ref/incidents/get-incident-workflows.mdx create mode 100644 docs/api-ref/incidents/get-incidents-meta.mdx create mode 100644 docs/api-ref/incidents/merge-incidents.mdx create mode 100644 keep-ui/app/incidents/[id]/incident-header.tsx rename keep-ui/app/incidents/[id]/{incident-info.tsx => incident-overview.tsx} (55%) create mode 100644 keep-ui/app/incidents/incident-dropdown-menu.tsx create mode 100644 keep-ui/app/incidents/incident-list-error.tsx rename keep-ui/app/incidents/{IncidentPlaceholder.tsx => incident-list-placeholder.tsx} (91%) rename keep-ui/app/incidents/{incident.tsx => incident-list.tsx} (71%) create mode 100644 keep-ui/app/incidents/incident-merge-modal.tsx create mode 100644 keep-ui/app/incidents/react-quill-override.css create mode 100644 keep-ui/app/incidents/vertical-rounded-list.css create mode 100644 keep-ui/components/ui/DropdownMenu/DropdownMenu.css create mode 100644 keep-ui/components/ui/DropdownMenu/DropdownMenu.tsx create mode 100644 keep-ui/components/ui/DropdownMenu/index.ts create mode 100644 keep-ui/components/ui/table/utils.ts create mode 100644 keep-ui/entities/incidents/model/useIncidentActions.tsx create mode 100644 keep-ui/features/change-incident-status/index.ts create mode 100644 keep-ui/features/change-incident-status/ui/incident-change-status-select.tsx create mode 100644 keep/api/models/db/migrations/versions/2024-10-23-15-21_89b4d3905d26.py create mode 100644 keep/api/utils/pluralize.py diff --git a/docs/api-ref/alerts/get-alert-quality.mdx b/docs/api-ref/alerts/get-alert-quality.mdx new file mode 100644 index 000000000..277322d33 --- /dev/null +++ b/docs/api-ref/alerts/get-alert-quality.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /alerts/quality/metrics +--- \ No newline at end of file diff --git a/docs/api-ref/incidents/add-comment.mdx b/docs/api-ref/incidents/add-comment.mdx new file mode 100644 index 000000000..2c27f1c25 --- /dev/null +++ b/docs/api-ref/incidents/add-comment.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /incidents/{incident_id}/comment +--- \ No newline at end of file diff --git a/docs/api-ref/incidents/get-future-incidents-for-an-incident.mdx b/docs/api-ref/incidents/get-future-incidents-for-an-incident.mdx new file mode 100644 index 000000000..078b4d930 --- /dev/null +++ b/docs/api-ref/incidents/get-future-incidents-for-an-incident.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /incidents/{incident_id}/future_incidents +--- \ No newline at end of file diff --git a/docs/api-ref/incidents/get-incident-workflows.mdx b/docs/api-ref/incidents/get-incident-workflows.mdx new file mode 100644 index 000000000..012ed48b9 --- /dev/null +++ b/docs/api-ref/incidents/get-incident-workflows.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /incidents/{incident_id}/workflows +--- \ No newline at end of file diff --git a/docs/api-ref/incidents/get-incidents-meta.mdx b/docs/api-ref/incidents/get-incidents-meta.mdx new file mode 100644 index 000000000..52e7a6b62 --- /dev/null +++ b/docs/api-ref/incidents/get-incidents-meta.mdx @@ -0,0 +1,3 @@ +--- +openapi: get /incidents/meta +--- \ No newline at end of file diff --git a/docs/api-ref/incidents/merge-incidents.mdx b/docs/api-ref/incidents/merge-incidents.mdx new file mode 100644 index 000000000..21d697a6c --- /dev/null +++ b/docs/api-ref/incidents/merge-incidents.mdx @@ -0,0 +1,3 @@ +--- +openapi: post /incidents/merge +--- \ No newline at end of file diff --git a/docs/mint.json b/docs/mint.json index d0bcdc482..fe36ffb2d 100644 --- a/docs/mint.json +++ b/docs/mint.json @@ -331,7 +331,8 @@ "api-ref/alerts/unenrich-alert", "api-ref/alerts/search-alerts", "api-ref/alerts/get-alert-audit", - "api-ref/alerts/get-alerts" + "api-ref/alerts/get-alerts", + "api-ref/alerts/get-alert-quality" ] }, { @@ -366,7 +367,12 @@ "api-ref/incidents/get-incident-alerts", "api-ref/incidents/add-alerts-to-incident", "api-ref/incidents/delete-alerts-from-incident", - "api-ref/incidents/confirm-incident" + "api-ref/incidents/confirm-incident", + "api-ref/incidents/add-comment", + "api-ref/incidents/get-future-incidents-for-an-incident", + "api-ref/incidents/get-incident-workflows", + "api-ref/incidents/get-incidents-meta", + "api-ref/incidents/merge-incidents" ] }, { diff --git a/docs/openapi.json b/docs/openapi.json index 5df0d0f5f..54ad8d432 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -1 +1 @@ -{"openapi": "3.0.2", "info": {"title": "Keep API", "description": "Rest API powering https://platform.keephq.dev and friends \ud83c\udfc4\u200d\u2640\ufe0f", "version": "0.1.0"}, "paths": {"/providers": {"get": {"tags": ["providers"], "summary": "Get Providers", "operationId": "get_providers_providers_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/export": {"get": {"tags": ["providers"], "summary": "Get Installed Providers", "description": "export all installed providers", "operationId": "get_installed_providers_providers_export_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}/configured-alerts": {"get": {"tags": ["providers"], "summary": "Get Alerts Configuration", "description": "Get alerts configuration from a provider", "operationId": "get_alerts_configuration_providers__provider_type___provider_id__configured_alerts_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {}, "type": "array", "title": "Response Get Alerts Configuration Providers Provider Type Provider Id Configured Alerts Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}/logs": {"get": {"tags": ["providers"], "summary": "Get Logs", "description": "Get logs from a provider", "operationId": "get_logs_providers__provider_type___provider_id__logs_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 5}, "name": "limit", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {}, "type": "array", "title": "Response Get Logs Providers Provider Type Provider Id Logs Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/schema": {"get": {"tags": ["providers"], "summary": "Get Alerts Schema", "description": "Get the provider's API schema used to push alerts configuration", "operationId": "get_alerts_schema_providers__provider_type__schema_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Get Alerts Schema Providers Provider Type Schema Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/providers/{provider_type}/{provider_id}/alerts/count": {"get": {"tags": ["providers"], "summary": "Get Alert Count", "description": "Get number of alerts a specific provider has received (in a specific time time period or ever)", "operationId": "get_alert_count_providers__provider_type___provider_id__alerts_count_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": true, "schema": {"type": "boolean", "title": "Ever"}, "name": "ever", "in": "query"}, {"required": false, "schema": {"type": "string", "format": "date-time", "title": "Start Time"}, "name": "start_time", "in": "query"}, {"required": false, "schema": {"type": "string", "format": "date-time", "title": "End Time"}, "name": "end_time", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}/alerts": {"post": {"tags": ["providers"], "summary": "Add Alert", "description": "Push new alerts to the provider", "operationId": "add_alert_providers__provider_type___provider_id__alerts_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": false, "schema": {"type": "string", "title": "Alert Id"}, "name": "alert_id", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Alert"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/test": {"post": {"tags": ["providers"], "summary": "Test Provider", "description": "Test a provider's alert retrieval", "operationId": "test_provider_providers_test_post", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Provider Info"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}": {"delete": {"tags": ["providers"], "summary": "Delete Provider", "operationId": "delete_provider_providers__provider_type___provider_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_id}/scopes": {"post": {"tags": ["providers"], "summary": "Validate Provider Scopes", "description": "Validate provider scopes", "operationId": "validate_provider_scopes_providers__provider_id__scopes_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"anyOf": [{"type": "boolean"}, {"type": "string"}]}, "type": "object", "title": "Response Validate Provider Scopes Providers Provider Id Scopes Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_id}": {"put": {"tags": ["providers"], "summary": "Update Provider", "description": "Update provider", "operationId": "update_provider_providers__provider_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/install": {"post": {"tags": ["providers"], "summary": "Install Provider", "operationId": "install_provider_providers_install_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/install/oauth2/{provider_type}": {"post": {"tags": ["providers"], "summary": "Install Provider Oauth2", "operationId": "install_provider_oauth2_providers_install_oauth2__provider_type__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Provider Info"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_id}/invoke/{method}": {"post": {"tags": ["providers"], "summary": "Invoke Provider Method", "description": "Invoke provider special method", "operationId": "invoke_provider_method_providers__provider_id__invoke__method__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Method"}, "name": "method", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Method Params"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/install/webhook/{provider_type}/{provider_id}": {"post": {"tags": ["providers"], "summary": "Install Provider Webhook", "operationId": "install_provider_webhook_providers_install_webhook__provider_type___provider_id__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/webhook": {"get": {"tags": ["providers"], "summary": "Get Webhook Settings", "operationId": "get_webhook_settings_providers__provider_type__webhook_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProviderWebhookSettings"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/actions": {"get": {"tags": ["actions"], "summary": "Get Actions", "description": "Get all actions", "operationId": "get_actions_actions_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["actions"], "summary": "Create Actions", "description": "Create new actions by uploading a file", "operationId": "create_actions_actions_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_create_actions_actions_post"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/actions/{action_id}": {"put": {"tags": ["actions"], "summary": "Put Action", "description": "Update an action", "operationId": "put_action_actions__action_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Action Id"}, "name": "action_id", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_put_action_actions__action_id__put"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["actions"], "summary": "Delete Action", "description": "Delete an action", "operationId": "delete_action_actions__action_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Action Id"}, "name": "action_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/healthcheck": {"get": {"tags": ["healthcheck"], "summary": "Healthcheck", "description": "simple healthcheck endpoint", "operationId": "healthcheck_healthcheck_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Healthcheck Healthcheck Get"}}}}}}}, "/alerts": {"get": {"tags": ["alerts"], "summary": "Get All Alerts", "description": "Get last alerts occurrence", "operationId": "get_all_alerts_alerts_get", "parameters": [{"required": false, "schema": {"type": "integer", "title": "Limit", "default": 1000}, "name": "limit", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Get All Alerts Alerts Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["alerts"], "summary": "Delete Alert", "description": "Delete alert by finerprint and last received time", "operationId": "delete_alert_alerts_delete", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeleteRequestBody"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Delete Alert Alerts Delete"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}/history": {"get": {"tags": ["alerts"], "summary": "Get Alert History", "description": "Get alert history", "operationId": "get_alert_history_alerts__fingerprint__history_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Get Alert History Alerts Fingerprint History Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}/assign/{last_received}": {"post": {"tags": ["alerts"], "summary": "Assign Alert", "description": "Assign alert to user", "operationId": "assign_alert_alerts__fingerprint__assign__last_received__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Last Received"}, "name": "last_received", "in": "path"}, {"required": false, "schema": {"type": "boolean", "title": "Unassign", "default": false}, "name": "unassign", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Assign Alert Alerts Fingerprint Assign Last Received Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/event": {"post": {"tags": ["alerts"], "summary": "Receive Generic Event", "description": "Receive a generic alert event", "operationId": "receive_generic_event_alerts_event_post", "parameters": [{"required": false, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/AlertDto"}, {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array"}, {"type": "object"}], "title": "Event"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/AlertDto"}, {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array"}], "title": "Response Receive Generic Event Alerts Event Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/event/netdata": {"get": {"tags": ["alerts"], "summary": "Webhook Challenge", "description": "Helper function to complete Netdata webhook challenge", "operationId": "webhook_challenge_alerts_event_netdata_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/alerts/event/{provider_type}": {"post": {"tags": ["alerts"], "summary": "Receive Event", "description": "Receive an alert event from a provider", "operationId": "receive_event_alerts_event__provider_type__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": false, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"anyOf": [{"type": "object"}, {"type": "string", "format": "binary"}], "title": "Event"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Receive Event Alerts Event Provider Type Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}": {"get": {"tags": ["alerts"], "summary": "Get Alert", "description": "Get alert by fingerprint", "operationId": "get_alert_alerts__fingerprint__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AlertDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/enrich": {"post": {"tags": ["alerts"], "summary": "Enrich Alert", "description": "Enrich an alert", "operationId": "enrich_alert_alerts_enrich_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EnrichAlertRequestBody"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Enrich Alert Alerts Enrich Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/unenrich": {"post": {"tags": ["alerts"], "summary": "Unenrich Alert", "description": "Un-Enrich an alert", "operationId": "unenrich_alert_alerts_unenrich_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UnEnrichAlertRequestBody"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Unenrich Alert Alerts Unenrich Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/search": {"post": {"tags": ["alerts"], "summary": "Search Alerts", "description": "Search alerts", "operationId": "search_alerts_alerts_search_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchAlertsRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Search Alerts Alerts Search Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/audit": {"post": {"tags": ["alerts"], "summary": "Get Multiple Fingerprint Alert Audit", "description": "Get alert timeline audit trail for multiple fingerprints", "operationId": "get_multiple_fingerprint_alert_audit_alerts_audit_post", "requestBody": {"content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Fingerprints"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertAuditDto"}, "type": "array", "title": "Response Get Multiple Fingerprint Alert Audit Alerts Audit Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}/audit": {"get": {"tags": ["alerts"], "summary": "Get Alert Audit", "description": "Get alert timeline audit trail", "operationId": "get_alert_audit_alerts__fingerprint__audit_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertAuditDto"}, "type": "array", "title": "Response Get Alert Audit Alerts Fingerprint Audit Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents": {"get": {"tags": ["incidents"], "summary": "Get All Incidents", "description": "Get last incidents", "operationId": "get_all_incidents_incidents_get", "parameters": [{"required": false, "schema": {"type": "boolean", "title": "Confirmed", "default": true}, "name": "confirmed", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}, {"required": false, "schema": {"allOf": [{"$ref": "#/components/schemas/IncidentSorting"}], "default": "creation_time"}, "name": "sorting", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentsPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["incidents"], "summary": "Create Incident Endpoint", "description": "Create new incident", "operationId": "create_incident_endpoint_incidents_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDtoIn"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}": {"get": {"tags": ["incidents"], "summary": "Get Incident", "description": "Get incident by id", "operationId": "get_incident_incidents__incident_id__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "put": {"tags": ["incidents"], "summary": "Update Incident", "description": "Update incident by id", "operationId": "update_incident_incidents__incident_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["incidents"], "summary": "Delete Incident", "description": "Delete incident by incident id", "operationId": "delete_incident_incidents__incident_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/alerts": {"get": {"tags": ["incidents"], "summary": "Get Incident Alerts", "description": "Get incident alerts by incident incident id", "operationId": "get_incident_alerts_incidents__incident_id__alerts_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AlertPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["incidents"], "summary": "Add Alerts To Incident", "description": "Add alerts to incident", "operationId": "add_alerts_to_incident_incidents__incident_id__alerts_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Alert Ids"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Add Alerts To Incident Incidents Incident Id Alerts Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["incidents"], "summary": "Delete Alerts From Incident", "description": "Delete alerts from incident", "operationId": "delete_alerts_from_incident_incidents__incident_id__alerts_delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Alert Ids"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Delete Alerts From Incident Incidents Incident Id Alerts Delete"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/confirm": {"post": {"tags": ["incidents"], "summary": "Confirm Incident", "description": "Confirm predicted incident by id", "operationId": "confirm_incident_incidents__incident_id__confirm_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/status": {"post": {"tags": ["incidents"], "summary": "Change Incident Status", "description": "Change incident status", "operationId": "change_incident_status_incidents__incident_id__status_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentStatusChangeDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/webhook": {"get": {"tags": ["settings"], "summary": "Webhook Settings", "description": "Get details about the webhook endpoint (e.g. the API url and an API key)", "operationId": "webhook_settings_settings_webhook_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookSettings"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/users": {"get": {"tags": ["settings"], "summary": "Get Users", "description": "Get all users", "operationId": "get_users_settings_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/User"}, "type": "array", "title": "Response Get Users Settings Users Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["settings"], "summary": "Create User", "description": "Create a user", "operationId": "create_user_settings_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/keep__api__routes__settings__CreateUserRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/users/{user_email}": {"delete": {"tags": ["settings"], "summary": "Delete User", "description": "Delete a user", "operationId": "delete_user_settings_users__user_email__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "User Email"}, "name": "user_email", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/smtp": {"get": {"tags": ["settings"], "summary": "Get Smtp Settings", "description": "Get SMTP settings", "operationId": "get_smtp_settings_settings_smtp_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["settings"], "summary": "Update Smtp Settings", "description": "Install or update SMTP settings", "operationId": "update_smtp_settings_settings_smtp_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SMTPSettings"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["settings"], "summary": "Delete Smtp Settings", "description": "Delete SMTP settings", "operationId": "delete_smtp_settings_settings_smtp_delete", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/smtp/test": {"post": {"tags": ["settings"], "summary": "Test Smtp Settings", "description": "Test SMTP settings", "operationId": "test_smtp_settings_settings_smtp_test_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SMTPSettings"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/apikey": {"put": {"tags": ["settings"], "summary": "Update Api Key", "description": "Update API key secret", "operationId": "update_api_key_settings_apikey_put", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["settings"], "summary": "Create Key", "description": "Create API key", "operationId": "create_key_settings_apikey_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/apikeys": {"get": {"tags": ["settings"], "summary": "Get Keys", "description": "Get API keys", "operationId": "get_keys_settings_apikeys_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/apikey/{keyId}": {"delete": {"tags": ["settings"], "summary": "Delete Api Key", "description": "Delete API key", "operationId": "delete_api_key_settings_apikey__keyId__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Keyid"}, "name": "keyId", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/sso": {"get": {"tags": ["settings"], "summary": "Get Sso Settings", "operationId": "get_sso_settings_settings_sso_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflows", "description": "Get workflows", "operationId": "get_workflows_workflows_get", "parameters": [{"required": false, "schema": {"type": "boolean", "title": "Is V2", "default": false}, "name": "is_v2", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"anyOf": [{"items": {"$ref": "#/components/schemas/WorkflowDTO"}, "type": "array"}, {"items": {"type": "object"}, "type": "array"}], "title": "Response Get Workflows Workflows Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["workflows", "alerts"], "summary": "Create Workflow", "description": "Create or update a workflow", "operationId": "create_workflow_workflows_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_create_workflow_workflows_post"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowCreateOrUpdateDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/export": {"get": {"tags": ["workflows", "alerts"], "summary": "Export Workflows", "description": "export all workflow Yamls", "operationId": "export_workflows_workflows_export_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Response Export Workflows Workflows Export Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}/run": {"post": {"tags": ["workflows", "alerts"], "summary": "Run Workflow", "description": "Run a workflow", "operationId": "run_workflow_workflows__workflow_id__run_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Body"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Run Workflow Workflows Workflow Id Run Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/test": {"post": {"tags": ["workflows", "alerts"], "summary": "Run Workflow From Definition", "description": "Test run a workflow from a definition", "operationId": "run_workflow_from_definition_workflows_test_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_run_workflow_from_definition_workflows_test_post"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Run Workflow From Definition Workflows Test Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/json": {"post": {"tags": ["workflows", "alerts"], "summary": "Create Workflow From Body", "description": "Create or update a workflow", "operationId": "create_workflow_from_body_workflows_json_post", "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowCreateOrUpdateDTO"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/random-templates": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Random Workflow Templates", "description": "Get random workflow templates", "operationId": "get_random_workflow_templates_workflows_random_templates_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "object"}, "type": "array", "title": "Response Get Random Workflow Templates Workflows Random Templates Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow By Id", "description": "Get workflow executions by ID", "operationId": "get_workflow_by_id_workflows__workflow_id__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Tab", "default": 1}, "name": "tab", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Status"}, "name": "status", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Trigger"}, "name": "trigger", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Execution Id"}, "name": "execution_id", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowExecutionsPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "put": {"tags": ["workflows", "alerts"], "summary": "Update Workflow By Id", "description": "Update a workflow", "operationId": "update_workflow_by_id_workflows__workflow_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowCreateOrUpdateDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["workflows", "alerts"], "summary": "Delete Workflow By Id", "description": "Delete workflow", "operationId": "delete_workflow_by_id_workflows__workflow_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}/raw": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Raw Workflow By Id", "description": "Get workflow executions by ID", "operationId": "get_raw_workflow_by_id_workflows__workflow_id__raw_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "string", "title": "Response Get Raw Workflow By Id Workflows Workflow Id Raw Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/executions": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow Executions By Alert Fingerprint", "description": "Get workflow executions by alert fingerprint", "operationId": "get_workflow_executions_by_alert_fingerprint_workflows_executions_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/WorkflowToAlertExecutionDTO"}, "type": "array", "title": "Response Get Workflow Executions By Alert Fingerprint Workflows Executions Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}/runs/{workflow_execution_id}": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow Execution Status", "description": "Get a workflow execution status", "operationId": "get_workflow_execution_status_workflows__workflow_id__runs__workflow_execution_id__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Execution Id"}, "name": "workflow_execution_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowExecutionDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/executions/list": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow Executions", "description": "List last workflow executions", "operationId": "get_workflow_executions_workflows_executions_list_get", "parameters": [{"description": "Workflow execution ID", "required": false, "schema": {"type": "string", "title": "Workflow Execution Id", "description": "Workflow execution ID"}, "name": "workflow_execution_id", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/WorkflowExecutionDTO"}, "type": "array", "title": "Response Get Workflow Executions Workflows Executions List Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/whoami": {"get": {"tags": ["whoami"], "summary": "Get Tenant Id", "description": "Get tenant id", "operationId": "get_tenant_id_whoami_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Get Tenant Id Whoami Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/pusher/auth": {"post": {"tags": ["pusher"], "summary": "Pusher Authentication", "description": "Authenticate a user to a private channel\n\nArgs:\n request (Request): The request object\n tenant_id (str, optional): The tenant ID. Defaults to Depends(verify_bearer_token).\n pusher_client (Pusher, optional): Pusher client. Defaults to Depends(get_pusher_client).\n\nRaises:\n HTTPException: 403 if the user is not allowed to access the channel.\n\nReturns:\n dict: The authentication response.", "operationId": "pusher_authentication_pusher_auth_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_pusher_authentication_pusher_auth_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Pusher Authentication Pusher Auth Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/status": {"get": {"tags": ["status"], "summary": "Status", "description": "simple status endpoint", "operationId": "status_status_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Status Status Get"}}}}}}}, "/rules": {"get": {"tags": ["rules"], "summary": "Get Rules", "description": "Get Rules", "operationId": "get_rules_rules_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["rules"], "summary": "Create Rule", "description": "Create Rule", "operationId": "create_rule_rules_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RuleCreateDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/rules/{rule_id}": {"put": {"tags": ["rules"], "summary": "Update Rule", "description": "Update Rule", "operationId": "update_rule_rules__rule_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["rules"], "summary": "Delete Rule", "description": "Delete Rule", "operationId": "delete_rule_rules__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset": {"get": {"tags": ["preset"], "summary": "Get Presets", "description": "Get all presets for tenant", "operationId": "get_presets_preset_get", "parameters": [{"required": false, "schema": {"type": "string", "title": "Time Stamp"}, "name": "time_stamp", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/PresetDto"}, "type": "array", "title": "Response Get Presets Preset Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["preset"], "summary": "Create Preset", "description": "Create a preset for tenant", "operationId": "create_preset_preset_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdatePresetDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PresetDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{uuid}": {"put": {"tags": ["preset"], "summary": "Update Preset", "description": "Update a preset for tenant", "operationId": "update_preset_preset__uuid__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Uuid"}, "name": "uuid", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdatePresetDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PresetDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["preset"], "summary": "Delete Preset", "description": "Delete a preset for tenant", "operationId": "delete_preset_preset__uuid__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Uuid"}, "name": "uuid", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{preset_name}/alerts": {"get": {"tags": ["preset"], "summary": "Get Preset Alerts", "description": "Get a preset for tenant", "operationId": "get_preset_alerts_preset__preset_name__alerts_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Preset Name"}, "name": "preset_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {}, "type": "array", "title": "Response Get Preset Alerts Preset Preset Name Alerts Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{preset_id}/tab": {"post": {"tags": ["preset"], "summary": "Create Preset Tab", "description": "Create a tab for a preset", "operationId": "create_preset_tab_preset__preset_id__tab_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Preset Id"}, "name": "preset_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreatePresetTab"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{preset_id}/tab/{tab_id}": {"delete": {"tags": ["preset"], "summary": "Delete Tab", "description": "Delete a tab from a preset", "operationId": "delete_tab_preset__preset_id__tab__tab_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Preset Id"}, "name": "preset_id", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Tab Id"}, "name": "tab_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/mapping": {"get": {"tags": ["enrichment", "mapping"], "summary": "Get Rules", "description": "Get all mapping rules", "operationId": "get_rules_mapping_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MappingRuleDtoOut"}, "type": "array", "title": "Response Get Rules Mapping Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["enrichment", "mapping"], "summary": "Create Rule", "description": "Create a new mapping rule", "operationId": "create_rule_mapping_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRuleDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRule"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/mapping/{rule_id}": {"put": {"tags": ["enrichment", "mapping"], "summary": "Update Rule", "description": "Update an existing rule", "operationId": "update_rule_mapping__rule_id__put", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRuleDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRuleDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["enrichment", "mapping"], "summary": "Delete Rule", "description": "Delete a mapping rule", "operationId": "delete_rule_mapping__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/groups": {"get": {"tags": ["auth", "groups"], "summary": "Get Groups", "description": "Get all groups", "operationId": "get_groups_auth_groups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Group"}, "type": "array", "title": "Response Get Groups Auth Groups Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "groups"], "summary": "Create Group", "description": "Create a group", "operationId": "create_group_auth_groups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdateGroupRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/groups/{group_name}": {"put": {"tags": ["auth", "groups"], "summary": "Update Group", "description": "Update a group", "operationId": "update_group_auth_groups__group_name__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Group Name"}, "name": "group_name", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdateGroupRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["auth", "groups"], "summary": "Delete Group", "description": "Delete a group", "operationId": "delete_group_auth_groups__group_name__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Group Name"}, "name": "group_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/permissions": {"get": {"tags": ["auth", "permissions"], "summary": "Get Permissions", "description": "Get resources permissions", "operationId": "get_permissions_auth_permissions_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ResourcePermission"}, "type": "array", "title": "Response Get Permissions Auth Permissions Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "permissions"], "summary": "Create Permissions", "description": "Create permissions for resources", "operationId": "create_permissions_auth_permissions_post", "requestBody": {"content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ResourcePermission"}, "type": "array", "title": "Resource Permissions", "description": "List of resource permissions"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/permissions/scopes": {"get": {"tags": ["auth", "permissions"], "summary": "Get Scopes", "description": "Get all resources types", "operationId": "get_scopes_auth_permissions_scopes_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Response Get Scopes Auth Permissions Scopes Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/roles": {"get": {"tags": ["auth", "roles"], "summary": "Get Roles", "description": "Get roles", "operationId": "get_roles_auth_roles_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Role"}, "type": "array", "title": "Response Get Roles Auth Roles Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "roles"], "summary": "Create Role", "description": "Create role", "operationId": "create_role_auth_roles_post", "requestBody": {"content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/CreateOrUpdateRole"}], "title": "Role", "description": "Role"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/roles/{role_id}": {"put": {"tags": ["auth", "roles"], "summary": "Update Role", "description": "Update role", "operationId": "update_role_auth_roles__role_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Role Id"}, "name": "role_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/CreateOrUpdateRole"}], "title": "Role", "description": "Role"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["auth", "roles"], "summary": "Delete Role", "description": "Delete role", "operationId": "delete_role_auth_roles__role_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Role Id"}, "name": "role_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/users": {"get": {"tags": ["auth", "users"], "summary": "Get Users", "description": "Get all users", "operationId": "get_users_auth_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/User"}, "type": "array", "title": "Response Get Users Auth Users Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "users"], "summary": "Create User", "description": "Create a user", "operationId": "create_user_auth_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/keep__api__routes__auth__users__CreateUserRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/users/{user_email}": {"put": {"tags": ["auth", "users"], "summary": "Update User", "description": "Update a user", "operationId": "update_user_auth_users__user_email__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "User Email"}, "name": "user_email", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateUserRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["auth", "users"], "summary": "Delete User", "description": "Delete a user", "operationId": "delete_user_auth_users__user_email__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "User Email"}, "name": "user_email", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/metrics": {"get": {"tags": ["metrics"], "summary": "Get Metrics", "description": "This endpoint is used by Prometheus to scrape such metrics from the application:\n- alerts_total {incident_name, incident_id} - The total number of alerts per incident.\n- open_incidents_total - The total number of open incidents\n\nPlease note that those metrics are per-tenant and are not designed to be used for the monitoring of the application itself.\n\nExample prometheus configuration:\n```\nscrape_configs:\n- job_name: \"scrape_keep\"\n scrape_interval: 5m # It's important to scrape not too often to avoid rate limiting.\n static_configs:\n - targets: [\"https://api.keephq.dev\"] # Or your own domain.\n authorization:\n type: Bearer\n credentials: \"{Your API Key}\"\n\n # Optional, you can add labels to exported incidents. \n # Label values will be equal to the last incident's alert payload value matching the label.\n # Attention! Don't add \"flaky\" labels which could change from alert to alert within the same incident.\n # Good labels: ['labels.department', 'labels.team'], bad labels: ['labels.severity', 'labels.pod_id']\n # Check Keep -> Feed -> \"extraPayload\" column, it will help in writing labels.\n\n params:\n labels: ['labels.service', 'labels.queue']\n # Will resuld as: \"labels_service\" and \"labels_queue\".\n```", "operationId": "get_metrics_metrics_get", "parameters": [{"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Labels"}, "name": "labels", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/extraction": {"get": {"tags": ["enrichment", "extraction"], "summary": "Get Extraction Rules", "description": "Get all extraction rules", "operationId": "get_extraction_rules_extraction_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ExtractionRuleDtoOut"}, "type": "array", "title": "Response Get Extraction Rules Extraction Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["enrichment", "extraction"], "summary": "Create Extraction Rule", "description": "Create a new extraction rule", "operationId": "create_extraction_rule_extraction_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/extraction/{rule_id}": {"put": {"tags": ["enrichment", "extraction"], "summary": "Update Extraction Rule", "description": "Update an existing extraction rule", "operationId": "update_extraction_rule_extraction__rule_id__put", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["enrichment", "extraction"], "summary": "Delete Extraction Rule", "description": "Delete an extraction rule", "operationId": "delete_extraction_rule_extraction__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/dashboard": {"get": {"tags": ["dashboard"], "summary": "Read Dashboards", "operationId": "read_dashboards_dashboard_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/DashboardResponseDTO"}, "type": "array", "title": "Response Read Dashboards Dashboard Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["dashboard"], "summary": "Create Dashboard", "operationId": "create_dashboard_dashboard_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardCreateDTO"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardResponseDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/dashboard/{dashboard_id}": {"put": {"tags": ["dashboard"], "summary": "Update Dashboard", "operationId": "update_dashboard_dashboard__dashboard_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Dashboard Id"}, "name": "dashboard_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardUpdateDTO"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardResponseDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["dashboard"], "summary": "Delete Dashboard", "operationId": "delete_dashboard_dashboard__dashboard_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Dashboard Id"}, "name": "dashboard_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/tags": {"get": {"tags": ["tags"], "summary": "Get Tags", "description": "get tags", "operationId": "get_tags_tags_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "object"}, "type": "array", "title": "Response Get Tags Tags Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/maintenance": {"get": {"tags": ["maintenance"], "summary": "Get Maintenance Rules", "description": "Get all maintenance rules", "operationId": "get_maintenance_rules_maintenance_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MaintenanceRuleRead"}, "type": "array", "title": "Response Get Maintenance Rules Maintenance Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["maintenance"], "summary": "Create Maintenance Rule", "description": "Create a new maintenance rule", "operationId": "create_maintenance_rule_maintenance_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleRead"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/maintenance/{rule_id}": {"put": {"tags": ["maintenance"], "summary": "Update Maintenance Rule", "description": "Update an existing maintenance rule", "operationId": "update_maintenance_rule_maintenance__rule_id__put", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleRead"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["maintenance"], "summary": "Delete Maintenance Rule", "description": "Delete a maintenance rule", "operationId": "delete_maintenance_rule_maintenance__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/topology": {"get": {"tags": ["topology"], "summary": "Get Topology Data", "description": "Get all topology data", "operationId": "get_topology_data_topology_get", "parameters": [{"required": false, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Service Id"}, "name": "service_id", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Environment"}, "name": "environment", "in": "query"}, {"required": false, "schema": {"type": "boolean", "title": "Include Empty Deps", "default": false}, "name": "include_empty_deps", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/TopologyServiceDtoOut"}, "type": "array", "title": "Response Get Topology Data Topology Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/topology/applications": {"get": {"tags": ["topology"], "summary": "Get Applications", "description": "Get all applications", "operationId": "get_applications_topology_applications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/TopologyApplicationDtoOut"}, "type": "array", "title": "Response Get Applications Topology Applications Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["topology"], "summary": "Create Application", "description": "Create a new application", "operationId": "create_application_topology_applications_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/topology/applications/{application_id}": {"put": {"tags": ["topology"], "summary": "Update Application", "description": "Update an application", "operationId": "update_application_topology_applications__application_id__put", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Application Id"}, "name": "application_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["topology"], "summary": "Delete Application", "description": "Delete an application", "operationId": "delete_application_topology_applications__application_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Application Id"}, "name": "application_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/deduplications": {"get": {"tags": ["deduplications"], "summary": "Get Deduplications", "description": "Get Deduplications", "operationId": "get_deduplications_deduplications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["deduplications"], "summary": "Create Deduplication Rule", "description": "Create Deduplication Rule", "operationId": "create_deduplication_rule_deduplications_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeduplicationRuleRequestDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/deduplications/fields": {"get": {"tags": ["deduplications"], "summary": "Get Deduplication Fields", "description": "Get Optional Fields For Deduplications", "operationId": "get_deduplication_fields_deduplications_fields_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "type": "object", "title": "Response Get Deduplication Fields Deduplications Fields Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/deduplications/{rule_id}": {"put": {"tags": ["deduplications"], "summary": "Update Deduplication Rule", "description": "Update Deduplication Rule", "operationId": "update_deduplication_rule_deduplications__rule_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeduplicationRuleRequestDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["deduplications"], "summary": "Delete Deduplication Rule", "description": "Delete Deduplication Rule", "operationId": "delete_deduplication_rule_deduplications__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}}, "components": {"schemas": {"AlertActionType": {"enum": ["alert was triggered", "alert acknowledged", "alert automatically resolved", "alert automatically resolved by API", "alert manually resolved", "alert status manually changed", "alert status changed by API", "alert status undone", "alert enriched by workflow", "alert enriched by mapping rule", "alert was deduplicated", "alert was assigned with ticket", "alert was unassigned from ticket", "alert ticket was updated", "alert enrichments disposed", "alert deleted", "alert enriched", "alert un-enriched", "a comment was added to the alert", "a comment was removed from the alert", "Alert is in maintenance window"], "title": "AlertActionType", "description": "An enumeration."}, "AlertAuditDto": {"properties": {"id": {"type": "string", "title": "Id"}, "timestamp": {"type": "string", "format": "date-time", "title": "Timestamp"}, "fingerprint": {"type": "string", "title": "Fingerprint"}, "action": {"$ref": "#/components/schemas/AlertActionType"}, "user_id": {"type": "string", "title": "User Id"}, "description": {"type": "string", "title": "Description"}}, "type": "object", "required": ["id", "timestamp", "fingerprint", "action", "user_id", "description"], "title": "AlertAuditDto"}, "AlertDto": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "status": {"$ref": "#/components/schemas/AlertStatus"}, "severity": {"$ref": "#/components/schemas/AlertSeverity"}, "lastReceived": {"type": "string", "title": "Lastreceived"}, "firingStartTime": {"type": "string", "title": "Firingstarttime"}, "environment": {"type": "string", "title": "Environment", "default": "undefined"}, "isFullDuplicate": {"type": "boolean", "title": "Isfullduplicate", "default": false}, "isPartialDuplicate": {"type": "boolean", "title": "Ispartialduplicate", "default": false}, "duplicateReason": {"type": "string", "title": "Duplicatereason"}, "service": {"type": "string", "title": "Service"}, "source": {"items": {"type": "string"}, "type": "array", "title": "Source", "default": []}, "apiKeyRef": {"type": "string", "title": "Apikeyref"}, "message": {"type": "string", "title": "Message"}, "description": {"type": "string", "title": "Description"}, "pushed": {"type": "boolean", "title": "Pushed", "default": false}, "event_id": {"type": "string", "title": "Event Id"}, "url": {"type": "string", "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Url"}, "labels": {"type": "object", "title": "Labels", "default": {}}, "fingerprint": {"type": "string", "title": "Fingerprint"}, "deleted": {"type": "boolean", "title": "Deleted", "default": false}, "dismissUntil": {"type": "string", "title": "Dismissuntil"}, "dismissed": {"type": "boolean", "title": "Dismissed", "default": false}, "assignee": {"type": "string", "title": "Assignee"}, "providerId": {"type": "string", "title": "Providerid"}, "providerType": {"type": "string", "title": "Providertype"}, "note": {"type": "string", "title": "Note"}, "startedAt": {"type": "string", "title": "Startedat"}, "isNoisy": {"type": "boolean", "title": "Isnoisy", "default": false}, "enriched_fields": {"items": {}, "type": "array", "title": "Enriched Fields", "default": []}}, "type": "object", "required": ["name", "status", "severity", "lastReceived"], "title": "AlertDto", "example": {"id": "1234", "name": "Alert name", "status": "firing", "lastReceived": "2021-01-01T00:00:00.000Z", "environment": "production", "service": "backend", "source": ["keep"], "message": "Keep: Alert message", "description": "Keep: Alert description", "severity": "critical", "pushed": true, "event_id": "1234", "url": "https://www.keephq.dev?alertId=1234", "labels": {"key": "value"}, "ticket_url": "https://www.keephq.dev?enrichedTicketId=456", "fingerprint": "1234"}}, "AlertPaginatedResultsDto": {"properties": {"limit": {"type": "integer", "title": "Limit", "default": 25}, "offset": {"type": "integer", "title": "Offset", "default": 0}, "count": {"type": "integer", "title": "Count"}, "items": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Items"}}, "type": "object", "required": ["count", "items"], "title": "AlertPaginatedResultsDto"}, "AlertSeverity": {"enum": ["critical", "high", "warning", "info", "low"], "title": "AlertSeverity", "description": "An enumeration."}, "AlertStatus": {"enum": ["firing", "resolved", "acknowledged", "suppressed", "pending"], "title": "AlertStatus", "description": "An enumeration."}, "Body_create_actions_actions_post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "title": "Body_create_actions_actions_post"}, "Body_create_workflow_workflows_post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_create_workflow_workflows_post"}, "Body_pusher_authentication_pusher_auth_post": {"properties": {"channel_name": {"title": "Channel Name"}, "socket_id": {"title": "Socket Id"}}, "type": "object", "required": ["channel_name", "socket_id"], "title": "Body_pusher_authentication_pusher_auth_post"}, "Body_put_action_actions__action_id__put": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_put_action_actions__action_id__put"}, "Body_run_workflow_from_definition_workflows_test_post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "title": "Body_run_workflow_from_definition_workflows_test_post"}, "CreateOrUpdateGroupRequest": {"properties": {"name": {"type": "string", "title": "Name"}, "roles": {"items": {"type": "string"}, "type": "array", "title": "Roles"}, "members": {"items": {"type": "string"}, "type": "array", "title": "Members"}}, "type": "object", "required": ["name", "roles", "members"], "title": "CreateOrUpdateGroupRequest"}, "CreateOrUpdatePresetDto": {"properties": {"name": {"type": "string", "title": "Name"}, "options": {"items": {"$ref": "#/components/schemas/PresetOption"}, "type": "array", "title": "Options"}, "is_private": {"type": "boolean", "title": "Is Private", "default": false}, "is_noisy": {"type": "boolean", "title": "Is Noisy", "default": false}, "tags": {"items": {"$ref": "#/components/schemas/TagDto"}, "type": "array", "title": "Tags", "default": []}}, "type": "object", "required": ["options"], "title": "CreateOrUpdatePresetDto"}, "CreateOrUpdateRole": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "scopes": {"items": {"type": "string"}, "type": "array", "uniqueItems": true, "title": "Scopes"}}, "type": "object", "title": "CreateOrUpdateRole"}, "CreatePresetTab": {"properties": {"name": {"type": "string", "title": "Name"}, "filter": {"type": "string", "title": "Filter"}}, "type": "object", "required": ["name", "filter"], "title": "CreatePresetTab"}, "DashboardCreateDTO": {"properties": {"dashboard_name": {"type": "string", "title": "Dashboard Name"}, "dashboard_config": {"type": "object", "title": "Dashboard Config"}}, "type": "object", "required": ["dashboard_name", "dashboard_config"], "title": "DashboardCreateDTO"}, "DashboardResponseDTO": {"properties": {"id": {"type": "string", "title": "Id"}, "dashboard_name": {"type": "string", "title": "Dashboard Name"}, "dashboard_config": {"type": "object", "title": "Dashboard Config"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}}, "type": "object", "required": ["id", "dashboard_name", "dashboard_config", "created_at", "updated_at"], "title": "DashboardResponseDTO"}, "DashboardUpdateDTO": {"properties": {"dashboard_config": {"type": "object", "title": "Dashboard Config"}, "dashboard_name": {"type": "string", "title": "Dashboard Name"}}, "type": "object", "title": "DashboardUpdateDTO"}, "DeduplicationRuleRequestDto": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "provider_type": {"type": "string", "title": "Provider Type"}, "provider_id": {"type": "string", "title": "Provider Id"}, "fingerprint_fields": {"items": {"type": "string"}, "type": "array", "title": "Fingerprint Fields"}, "full_deduplication": {"type": "boolean", "title": "Full Deduplication", "default": false}, "ignore_fields": {"items": {"type": "string"}, "type": "array", "title": "Ignore Fields"}}, "type": "object", "required": ["name", "provider_type", "fingerprint_fields"], "title": "DeduplicationRuleRequestDto"}, "DeleteRequestBody": {"properties": {"fingerprint": {"type": "string", "title": "Fingerprint"}, "lastReceived": {"type": "string", "title": "Lastreceived"}, "restore": {"type": "boolean", "title": "Restore", "default": false}}, "type": "object", "required": ["fingerprint", "lastReceived"], "title": "DeleteRequestBody"}, "EnrichAlertRequestBody": {"properties": {"enrichments": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Enrichments"}, "fingerprint": {"type": "string", "title": "Fingerprint"}}, "type": "object", "required": ["enrichments", "fingerprint"], "title": "EnrichAlertRequestBody"}, "ExtractionRuleDtoBase": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "attribute": {"type": "string", "title": "Attribute"}, "condition": {"type": "string", "title": "Condition"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "regex": {"type": "string", "title": "Regex"}, "pre": {"type": "boolean", "title": "Pre", "default": false}}, "type": "object", "required": ["name", "regex"], "title": "ExtractionRuleDtoBase"}, "ExtractionRuleDtoOut": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "attribute": {"type": "string", "title": "Attribute"}, "condition": {"type": "string", "title": "Condition"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "regex": {"type": "string", "title": "Regex"}, "pre": {"type": "boolean", "title": "Pre", "default": false}, "id": {"type": "integer", "title": "Id"}, "created_by": {"type": "string", "title": "Created By"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "updated_by": {"type": "string", "title": "Updated By"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}}, "type": "object", "required": ["name", "regex", "id", "created_at"], "title": "ExtractionRuleDtoOut"}, "Group": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "roles": {"items": {"type": "string"}, "type": "array", "title": "Roles", "default": []}, "members": {"items": {"type": "string"}, "type": "array", "title": "Members", "default": []}, "memberCount": {"type": "integer", "title": "Membercount", "default": 0}}, "type": "object", "required": ["id", "name"], "title": "Group"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "IncidentDto": {"properties": {"user_generated_name": {"type": "string", "title": "User Generated Name"}, "assignee": {"type": "string", "title": "Assignee"}, "user_summary": {"type": "string", "title": "User Summary"}, "id": {"type": "string", "format": "uuid", "title": "Id"}, "start_time": {"type": "string", "format": "date-time", "title": "Start Time"}, "last_seen_time": {"type": "string", "format": "date-time", "title": "Last Seen Time"}, "end_time": {"type": "string", "format": "date-time", "title": "End Time"}, "alerts_count": {"type": "integer", "title": "Alerts Count"}, "alert_sources": {"items": {"type": "string"}, "type": "array", "title": "Alert Sources"}, "severity": {"$ref": "#/components/schemas/IncidentSeverity"}, "status": {"allOf": [{"$ref": "#/components/schemas/IncidentStatus"}], "default": "firing"}, "services": {"items": {"type": "string"}, "type": "array", "title": "Services"}, "is_predicted": {"type": "boolean", "title": "Is Predicted"}, "is_confirmed": {"type": "boolean", "title": "Is Confirmed"}, "generated_summary": {"type": "string", "title": "Generated Summary"}, "ai_generated_name": {"type": "string", "title": "Ai Generated Name"}, "rule_fingerprint": {"type": "string", "title": "Rule Fingerprint"}}, "type": "object", "required": ["id", "alerts_count", "alert_sources", "severity", "services", "is_predicted", "is_confirmed"], "title": "IncidentDto", "example": {"id": "c2509cb3-6168-4347-b83b-a41da9df2d5b", "name": "Incident name", "user_summary": "Keep: Incident description", "status": "firing"}}, "IncidentDtoIn": {"properties": {"user_generated_name": {"type": "string", "title": "User Generated Name"}, "assignee": {"type": "string", "title": "Assignee"}, "user_summary": {"type": "string", "title": "User Summary"}}, "type": "object", "title": "IncidentDtoIn", "example": {"id": "c2509cb3-6168-4347-b83b-a41da9df2d5b", "name": "Incident name", "user_summary": "Keep: Incident description", "status": "firing"}}, "IncidentSeverity": {"enum": ["critical", "high", "warning", "info", "low"], "title": "IncidentSeverity", "description": "An enumeration."}, "IncidentSorting": {"enum": ["creation_time", "start_time", "last_seen_time", "severity", "status", "alerts_count", "-creation_time", "-start_time", "-last_seen_time", "-severity", "-status", "-alerts_count"], "title": "IncidentSorting", "description": "An enumeration."}, "IncidentStatus": {"enum": ["firing", "resolved", "acknowledged"], "title": "IncidentStatus", "description": "An enumeration."}, "IncidentStatusChangeDto": {"properties": {"status": {"$ref": "#/components/schemas/IncidentStatus"}, "comment": {"type": "string", "title": "Comment"}}, "type": "object", "required": ["status"], "title": "IncidentStatusChangeDto"}, "IncidentsPaginatedResultsDto": {"properties": {"limit": {"type": "integer", "title": "Limit", "default": 25}, "offset": {"type": "integer", "title": "Offset", "default": 0}, "count": {"type": "integer", "title": "Count"}, "items": {"items": {"$ref": "#/components/schemas/IncidentDto"}, "type": "array", "title": "Items"}}, "type": "object", "required": ["count", "items"], "title": "IncidentsPaginatedResultsDto"}, "MaintenanceRuleCreate": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "cel_query": {"type": "string", "title": "Cel Query"}, "start_time": {"type": "string", "format": "date-time", "title": "Start Time"}, "duration_seconds": {"type": "integer", "title": "Duration Seconds"}, "suppress": {"type": "boolean", "title": "Suppress", "default": false}, "enabled": {"type": "boolean", "title": "Enabled", "default": true}}, "type": "object", "required": ["name", "cel_query", "start_time"], "title": "MaintenanceRuleCreate"}, "MaintenanceRuleRead": {"properties": {"id": {"type": "integer", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "created_by": {"type": "string", "title": "Created By"}, "cel_query": {"type": "string", "title": "Cel Query"}, "start_time": {"type": "string", "format": "date-time", "title": "Start Time"}, "end_time": {"type": "string", "format": "date-time", "title": "End Time"}, "duration_seconds": {"type": "integer", "title": "Duration Seconds"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}, "suppress": {"type": "boolean", "title": "Suppress", "default": false}, "enabled": {"type": "boolean", "title": "Enabled", "default": true}}, "type": "object", "required": ["id", "name", "created_by", "cel_query", "start_time", "end_time"], "title": "MaintenanceRuleRead"}, "MappingRule": {"properties": {"id": {"type": "integer", "title": "Id"}, "tenant_id": {"type": "string", "title": "Tenant Id"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "name": {"type": "string", "maxLength": 255, "title": "Name"}, "description": {"type": "string", "maxLength": 2048, "title": "Description"}, "file_name": {"type": "string", "maxLength": 255, "title": "File Name"}, "created_by": {"type": "string", "maxLength": 255, "title": "Created By"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "override": {"type": "boolean", "title": "Override", "default": true}, "condition": {"type": "string", "maxLength": 2000, "title": "Condition"}, "type": {"type": "string", "maxLength": 255, "title": "Type"}, "matchers": {"items": {"type": "string"}, "type": "array", "title": "Matchers"}, "rows": {"items": {"type": "object"}, "type": "array", "title": "Rows"}, "updated_by": {"type": "string", "maxLength": 255, "title": "Updated By"}, "last_updated_at": {"type": "string", "format": "date-time", "title": "Last Updated At"}}, "type": "object", "required": ["tenant_id", "name", "type", "matchers"], "title": "MappingRule"}, "MappingRuleDtoIn": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "file_name": {"type": "string", "title": "File Name"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "matchers": {"items": {"type": "string"}, "type": "array", "title": "Matchers"}, "type": {"type": "string", "enum": ["csv", "topology"], "title": "Type", "default": "csv"}, "rows": {"items": {"type": "object"}, "type": "array", "title": "Rows"}}, "type": "object", "required": ["name", "matchers"], "title": "MappingRuleDtoIn"}, "MappingRuleDtoOut": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "file_name": {"type": "string", "title": "File Name"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "matchers": {"items": {"type": "string"}, "type": "array", "title": "Matchers"}, "type": {"type": "string", "enum": ["csv", "topology"], "title": "Type", "default": "csv"}, "id": {"type": "integer", "title": "Id"}, "created_by": {"type": "string", "title": "Created By"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "attributes": {"items": {"type": "string"}, "type": "array", "title": "Attributes", "default": []}, "updated_by": {"type": "string", "title": "Updated By"}, "last_updated_at": {"type": "string", "format": "date-time", "title": "Last Updated At"}}, "type": "object", "required": ["name", "matchers", "id", "created_at"], "title": "MappingRuleDtoOut"}, "PermissionEntity": {"properties": {"id": {"type": "string", "title": "Id"}, "type": {"type": "string", "title": "Type"}}, "type": "object", "required": ["id", "type"], "title": "PermissionEntity"}, "PresetDto": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "options": {"items": {}, "type": "array", "title": "Options", "default": []}, "created_by": {"type": "string", "title": "Created By"}, "is_private": {"type": "boolean", "title": "Is Private", "default": false}, "is_noisy": {"type": "boolean", "title": "Is Noisy", "default": false}, "should_do_noise_now": {"type": "boolean", "title": "Should Do Noise Now", "default": false}, "alerts_count": {"type": "integer", "title": "Alerts Count", "default": 0}, "static": {"type": "boolean", "title": "Static", "default": false}, "tags": {"items": {"$ref": "#/components/schemas/TagDto"}, "type": "array", "title": "Tags", "default": []}}, "type": "object", "required": ["id", "name"], "title": "PresetDto"}, "PresetOption": {"properties": {"label": {"type": "string", "title": "Label"}, "value": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Value"}}, "type": "object", "required": ["label", "value"], "title": "PresetOption"}, "PresetSearchQuery": {"properties": {"cel_query": {"type": "string", "minLength": 1, "title": "Cel Query"}, "sql_query": {"type": "object", "title": "Sql Query"}, "limit": {"type": "integer", "minimum": 0.0, "title": "Limit", "default": 1000}, "timeframe": {"type": "integer", "minimum": 0.0, "title": "Timeframe", "default": 0}}, "type": "object", "required": ["cel_query", "sql_query"], "title": "PresetSearchQuery"}, "ProviderDTO": {"properties": {"type": {"type": "string", "title": "Type"}, "id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "installed": {"type": "boolean", "title": "Installed"}}, "type": "object", "required": ["type", "name", "installed"], "title": "ProviderDTO"}, "ProviderWebhookSettings": {"properties": {"webhookDescription": {"type": "string", "title": "Webhookdescription"}, "webhookTemplate": {"type": "string", "title": "Webhooktemplate"}, "webhookMarkdown": {"type": "string", "title": "Webhookmarkdown"}}, "type": "object", "required": ["webhookTemplate"], "title": "ProviderWebhookSettings"}, "ResourcePermission": {"properties": {"resource_id": {"type": "string", "title": "Resource Id"}, "resource_name": {"type": "string", "title": "Resource Name"}, "resource_type": {"type": "string", "title": "Resource Type"}, "permissions": {"items": {"$ref": "#/components/schemas/PermissionEntity"}, "type": "array", "title": "Permissions"}}, "type": "object", "required": ["resource_id", "resource_name", "resource_type", "permissions"], "title": "ResourcePermission"}, "Role": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "scopes": {"items": {"type": "string"}, "type": "array", "uniqueItems": true, "title": "Scopes"}, "predefined": {"type": "boolean", "title": "Predefined", "default": true}}, "type": "object", "required": ["id", "name", "description", "scopes"], "title": "Role"}, "RuleCreateDto": {"properties": {"ruleName": {"type": "string", "title": "Rulename"}, "sqlQuery": {"type": "object", "title": "Sqlquery"}, "celQuery": {"type": "string", "title": "Celquery"}, "timeframeInSeconds": {"type": "integer", "title": "Timeframeinseconds"}, "timeUnit": {"type": "string", "title": "Timeunit"}, "groupingCriteria": {"items": {}, "type": "array", "title": "Groupingcriteria", "default": []}, "groupDescription": {"type": "string", "title": "Groupdescription"}, "requireApprove": {"type": "boolean", "title": "Requireapprove", "default": false}}, "type": "object", "required": ["ruleName", "sqlQuery", "celQuery", "timeframeInSeconds", "timeUnit"], "title": "RuleCreateDto"}, "SMTPSettings": {"properties": {"host": {"type": "string", "title": "Host"}, "port": {"type": "integer", "title": "Port"}, "from_email": {"type": "string", "title": "From Email"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "format": "password", "title": "Password", "writeOnly": true}, "secure": {"type": "boolean", "title": "Secure", "default": true}, "to_email": {"type": "string", "title": "To Email", "default": "keep@example.com"}}, "type": "object", "required": ["host", "port", "from_email"], "title": "SMTPSettings", "example": {"host": "smtp.example.com", "port": 587, "username": "user@example.com", "password": "password", "secure": true, "from_email": "noreply@example.com", "to_email": ""}}, "SearchAlertsRequest": {"properties": {"query": {"$ref": "#/components/schemas/PresetSearchQuery"}, "timeframe": {"type": "integer", "title": "Timeframe"}}, "type": "object", "required": ["query", "timeframe"], "title": "SearchAlertsRequest"}, "TagDto": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "TagDto"}, "TopologyApplicationDtoIn": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "services": {"items": {"$ref": "#/components/schemas/TopologyServiceDtoIn"}, "type": "array", "title": "Services", "default": []}}, "type": "object", "required": ["name"], "title": "TopologyApplicationDtoIn"}, "TopologyApplicationDtoOut": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "services": {"items": {"$ref": "#/components/schemas/TopologyApplicationServiceDto"}, "type": "array", "title": "Services", "default": []}}, "type": "object", "required": ["id", "name"], "title": "TopologyApplicationDtoOut"}, "TopologyApplicationServiceDto": {"properties": {"id": {"type": "integer", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "service": {"type": "string", "title": "Service"}}, "type": "object", "required": ["id", "name", "service"], "title": "TopologyApplicationServiceDto"}, "TopologyServiceDependencyDto": {"properties": {"serviceId": {"type": "integer", "title": "Serviceid"}, "serviceName": {"type": "string", "title": "Servicename"}, "protocol": {"type": "string", "title": "Protocol", "default": "unknown"}}, "type": "object", "required": ["serviceId", "serviceName"], "title": "TopologyServiceDependencyDto"}, "TopologyServiceDtoIn": {"properties": {"id": {"type": "integer", "title": "Id"}}, "type": "object", "required": ["id"], "title": "TopologyServiceDtoIn"}, "TopologyServiceDtoOut": {"properties": {"source_provider_id": {"type": "string", "title": "Source Provider Id"}, "repository": {"type": "string", "title": "Repository"}, "tags": {"items": {"type": "string"}, "type": "array", "title": "Tags"}, "service": {"type": "string", "title": "Service"}, "display_name": {"type": "string", "title": "Display Name"}, "environment": {"type": "string", "title": "Environment", "default": "unknown"}, "description": {"type": "string", "title": "Description"}, "team": {"type": "string", "title": "Team"}, "email": {"type": "string", "title": "Email"}, "slack": {"type": "string", "title": "Slack"}, "ip_address": {"type": "string", "title": "Ip Address"}, "mac_address": {"type": "string", "title": "Mac Address"}, "category": {"type": "string", "title": "Category"}, "manufacturer": {"type": "string", "title": "Manufacturer"}, "id": {"type": "integer", "title": "Id"}, "dependencies": {"items": {"$ref": "#/components/schemas/TopologyServiceDependencyDto"}, "type": "array", "title": "Dependencies"}, "application_ids": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Application Ids"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}}, "type": "object", "required": ["service", "display_name", "id", "dependencies", "application_ids"], "title": "TopologyServiceDtoOut"}, "UnEnrichAlertRequestBody": {"properties": {"enrichments": {"items": {"type": "string"}, "type": "array", "title": "Enrichments"}, "fingerprint": {"type": "string", "title": "Fingerprint"}}, "type": "object", "required": ["enrichments", "fingerprint"], "title": "UnEnrichAlertRequestBody"}, "UpdateUserRequest": {"properties": {"username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}, "role": {"type": "string", "title": "Role"}, "groups": {"items": {"type": "string"}, "type": "array", "title": "Groups"}}, "type": "object", "title": "UpdateUserRequest"}, "User": {"properties": {"email": {"type": "string", "title": "Email"}, "name": {"type": "string", "title": "Name"}, "role": {"type": "string", "title": "Role"}, "picture": {"type": "string", "title": "Picture"}, "created_at": {"type": "string", "title": "Created At"}, "last_login": {"type": "string", "title": "Last Login"}, "ldap": {"type": "boolean", "title": "Ldap", "default": false}, "groups": {"items": {"$ref": "#/components/schemas/Group"}, "type": "array", "title": "Groups", "default": []}}, "type": "object", "required": ["email", "name", "created_at"], "title": "User"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}, "WebhookSettings": {"properties": {"webhookApi": {"type": "string", "title": "Webhookapi"}, "apiKey": {"type": "string", "title": "Apikey"}, "modelSchema": {"type": "object", "title": "Modelschema"}}, "type": "object", "required": ["webhookApi", "apiKey", "modelSchema"], "title": "WebhookSettings"}, "WorkflowCreateOrUpdateDTO": {"properties": {"workflow_id": {"type": "string", "title": "Workflow Id"}, "status": {"type": "string", "enum": ["created", "updated"], "title": "Status"}, "revision": {"type": "integer", "title": "Revision", "default": 1}}, "type": "object", "required": ["workflow_id", "status"], "title": "WorkflowCreateOrUpdateDTO"}, "WorkflowDTO": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name", "default": "Workflow file doesn't contain name"}, "description": {"type": "string", "title": "Description", "default": "Workflow file doesn't contain description"}, "created_by": {"type": "string", "title": "Created By"}, "creation_time": {"type": "string", "format": "date-time", "title": "Creation Time"}, "triggers": {"items": {"type": "object"}, "type": "array", "title": "Triggers"}, "interval": {"type": "integer", "title": "Interval"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "last_execution_time": {"type": "string", "format": "date-time", "title": "Last Execution Time"}, "last_execution_status": {"type": "string", "title": "Last Execution Status"}, "providers": {"items": {"$ref": "#/components/schemas/ProviderDTO"}, "type": "array", "title": "Providers"}, "workflow_raw": {"type": "string", "title": "Workflow Raw"}, "revision": {"type": "integer", "title": "Revision", "default": 1}, "last_updated": {"type": "string", "format": "date-time", "title": "Last Updated"}, "invalid": {"type": "boolean", "title": "Invalid", "default": false}, "last_executions": {"items": {"type": "object"}, "type": "array", "title": "Last Executions"}, "last_execution_started": {"type": "string", "format": "date-time", "title": "Last Execution Started"}, "provisioned": {"type": "boolean", "title": "Provisioned", "default": false}, "provisioned_file": {"type": "string", "title": "Provisioned File"}}, "type": "object", "required": ["id", "created_by", "creation_time", "providers", "workflow_raw"], "title": "WorkflowDTO"}, "WorkflowExecutionDTO": {"properties": {"id": {"type": "string", "title": "Id"}, "workflow_id": {"type": "string", "title": "Workflow Id"}, "started": {"type": "string", "format": "date-time", "title": "Started"}, "triggered_by": {"type": "string", "title": "Triggered By"}, "status": {"type": "string", "title": "Status"}, "logs": {"items": {"$ref": "#/components/schemas/WorkflowExecutionLogsDTO"}, "type": "array", "title": "Logs"}, "error": {"type": "string", "title": "Error"}, "execution_time": {"type": "number", "title": "Execution Time"}, "results": {"type": "object", "title": "Results"}}, "type": "object", "required": ["id", "workflow_id", "started", "triggered_by", "status"], "title": "WorkflowExecutionDTO"}, "WorkflowExecutionLogsDTO": {"properties": {"id": {"type": "integer", "title": "Id"}, "timestamp": {"type": "string", "format": "date-time", "title": "Timestamp"}, "message": {"type": "string", "title": "Message"}, "context": {"type": "object", "title": "Context"}}, "type": "object", "required": ["id", "timestamp", "message"], "title": "WorkflowExecutionLogsDTO"}, "WorkflowExecutionsPaginatedResultsDto": {"properties": {"limit": {"type": "integer", "title": "Limit", "default": 25}, "offset": {"type": "integer", "title": "Offset", "default": 0}, "count": {"type": "integer", "title": "Count"}, "items": {"items": {"$ref": "#/components/schemas/WorkflowExecutionDTO"}, "type": "array", "title": "Items"}, "passCount": {"type": "integer", "title": "Passcount", "default": 0}, "avgDuration": {"type": "number", "title": "Avgduration", "default": 0.0}, "workflow": {"$ref": "#/components/schemas/WorkflowDTO"}, "failCount": {"type": "integer", "title": "Failcount", "default": 0}}, "type": "object", "required": ["count", "items"], "title": "WorkflowExecutionsPaginatedResultsDto"}, "WorkflowToAlertExecutionDTO": {"properties": {"workflow_id": {"type": "string", "title": "Workflow Id"}, "workflow_execution_id": {"type": "string", "title": "Workflow Execution Id"}, "alert_fingerprint": {"type": "string", "title": "Alert Fingerprint"}, "workflow_status": {"type": "string", "title": "Workflow Status"}, "workflow_started": {"type": "string", "format": "date-time", "title": "Workflow Started"}}, "type": "object", "required": ["workflow_id", "workflow_execution_id", "alert_fingerprint", "workflow_status", "workflow_started"], "title": "WorkflowToAlertExecutionDTO"}, "keep__api__routes__auth__users__CreateUserRequest": {"properties": {"username": {"type": "string", "title": "Username"}, "name": {"type": "string", "title": "Name"}, "password": {"type": "string", "title": "Password"}, "role": {"type": "string", "title": "Role"}, "groups": {"items": {"type": "string"}, "type": "array", "title": "Groups"}}, "type": "object", "required": ["username"], "title": "CreateUserRequest"}, "keep__api__routes__settings__CreateUserRequest": {"properties": {"username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}, "role": {"type": "string", "title": "Role"}}, "type": "object", "required": ["username", "role"], "title": "CreateUserRequest"}}, "securitySchemes": {"API Key": {"type": "apiKey", "in": "header", "name": "X-API-KEY"}, "HTTPBasic": {"type": "http", "scheme": "basic"}, "OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "token"}}}}}} \ No newline at end of file +{"openapi": "3.0.2", "info": {"title": "Keep API", "description": "Rest API powering https://platform.keephq.dev and friends \ud83c\udfc4\u200d\u2640\ufe0f", "version": "0.1.0"}, "paths": {"/providers": {"get": {"tags": ["providers"], "summary": "Get Providers", "operationId": "get_providers_providers_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/export": {"get": {"tags": ["providers"], "summary": "Get Installed Providers", "description": "export all installed providers", "operationId": "get_installed_providers_providers_export_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}/configured-alerts": {"get": {"tags": ["providers"], "summary": "Get Alerts Configuration", "description": "Get alerts configuration from a provider", "operationId": "get_alerts_configuration_providers__provider_type___provider_id__configured_alerts_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {}, "type": "array", "title": "Response Get Alerts Configuration Providers Provider Type Provider Id Configured Alerts Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}/logs": {"get": {"tags": ["providers"], "summary": "Get Logs", "description": "Get logs from a provider", "operationId": "get_logs_providers__provider_type___provider_id__logs_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 5}, "name": "limit", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {}, "type": "array", "title": "Response Get Logs Providers Provider Type Provider Id Logs Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/schema": {"get": {"tags": ["providers"], "summary": "Get Alerts Schema", "description": "Get the provider's API schema used to push alerts configuration", "operationId": "get_alerts_schema_providers__provider_type__schema_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Get Alerts Schema Providers Provider Type Schema Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}}}, "/providers/{provider_type}/{provider_id}/alerts/count": {"get": {"tags": ["providers"], "summary": "Get Alert Count", "description": "Get number of alerts a specific provider has received (in a specific time time period or ever)", "operationId": "get_alert_count_providers__provider_type___provider_id__alerts_count_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": true, "schema": {"type": "boolean", "title": "Ever"}, "name": "ever", "in": "query"}, {"required": false, "schema": {"type": "string", "format": "date-time", "title": "Start Time"}, "name": "start_time", "in": "query"}, {"required": false, "schema": {"type": "string", "format": "date-time", "title": "End Time"}, "name": "end_time", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}/alerts": {"post": {"tags": ["providers"], "summary": "Add Alert", "description": "Push new alerts to the provider", "operationId": "add_alert_providers__provider_type___provider_id__alerts_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": false, "schema": {"type": "string", "title": "Alert Id"}, "name": "alert_id", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Alert"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/test": {"post": {"tags": ["providers"], "summary": "Test Provider", "description": "Test a provider's alert retrieval", "operationId": "test_provider_providers_test_post", "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Provider Info"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/{provider_id}": {"delete": {"tags": ["providers"], "summary": "Delete Provider", "operationId": "delete_provider_providers__provider_type___provider_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_id}/scopes": {"post": {"tags": ["providers"], "summary": "Validate Provider Scopes", "description": "Validate provider scopes", "operationId": "validate_provider_scopes_providers__provider_id__scopes_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"anyOf": [{"type": "boolean"}, {"type": "string"}]}, "type": "object", "title": "Response Validate Provider Scopes Providers Provider Id Scopes Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_id}": {"put": {"tags": ["providers"], "summary": "Update Provider", "description": "Update provider", "operationId": "update_provider_providers__provider_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/install": {"post": {"tags": ["providers"], "summary": "Install Provider", "operationId": "install_provider_providers_install_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/install/oauth2/{provider_type}": {"post": {"tags": ["providers"], "summary": "Install Provider Oauth2", "operationId": "install_provider_oauth2_providers_install_oauth2__provider_type__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Provider Info"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_id}/invoke/{method}": {"post": {"tags": ["providers"], "summary": "Invoke Provider Method", "description": "Invoke provider special method", "operationId": "invoke_provider_method_providers__provider_id__invoke__method__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Method"}, "name": "method", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Method Params"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/install/webhook/{provider_type}/{provider_id}": {"post": {"tags": ["providers"], "summary": "Install Provider Webhook", "operationId": "install_provider_webhook_providers_install_webhook__provider_type___provider_id__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/providers/{provider_type}/webhook": {"get": {"tags": ["providers"], "summary": "Get Webhook Settings", "operationId": "get_webhook_settings_providers__provider_type__webhook_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ProviderWebhookSettings"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/actions": {"get": {"tags": ["actions"], "summary": "Get Actions", "description": "Get all actions", "operationId": "get_actions_actions_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["actions"], "summary": "Create Actions", "description": "Create new actions by uploading a file", "operationId": "create_actions_actions_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_create_actions_actions_post"}}}}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/actions/{action_id}": {"put": {"tags": ["actions"], "summary": "Put Action", "description": "Update an action", "operationId": "put_action_actions__action_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Action Id"}, "name": "action_id", "in": "path"}], "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_put_action_actions__action_id__put"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["actions"], "summary": "Delete Action", "description": "Delete an action", "operationId": "delete_action_actions__action_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Action Id"}, "name": "action_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/healthcheck": {"get": {"tags": ["healthcheck"], "summary": "Healthcheck", "description": "simple healthcheck endpoint", "operationId": "healthcheck_healthcheck_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Healthcheck Healthcheck Get"}}}}}}}, "/alerts": {"get": {"tags": ["alerts"], "summary": "Get All Alerts", "description": "Get last alerts occurrence", "operationId": "get_all_alerts_alerts_get", "parameters": [{"required": false, "schema": {"type": "integer", "title": "Limit", "default": 1000}, "name": "limit", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Get All Alerts Alerts Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["alerts"], "summary": "Delete Alert", "description": "Delete alert by finerprint and last received time", "operationId": "delete_alert_alerts_delete", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeleteRequestBody"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Delete Alert Alerts Delete"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}/history": {"get": {"tags": ["alerts"], "summary": "Get Alert History", "description": "Get alert history", "operationId": "get_alert_history_alerts__fingerprint__history_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Get Alert History Alerts Fingerprint History Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}/assign/{last_received}": {"post": {"tags": ["alerts"], "summary": "Assign Alert", "description": "Assign alert to user", "operationId": "assign_alert_alerts__fingerprint__assign__last_received__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Last Received"}, "name": "last_received", "in": "path"}, {"required": false, "schema": {"type": "boolean", "title": "Unassign", "default": false}, "name": "unassign", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Assign Alert Alerts Fingerprint Assign Last Received Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/event": {"post": {"tags": ["alerts"], "summary": "Receive Generic Event", "description": "Receive a generic alert event", "operationId": "receive_generic_event_alerts_event_post", "parameters": [{"required": false, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/AlertDto"}, {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array"}, {"type": "object"}], "title": "Event"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"anyOf": [{"$ref": "#/components/schemas/AlertDto"}, {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array"}], "title": "Response Receive Generic Event Alerts Event Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/event/netdata": {"get": {"tags": ["alerts"], "summary": "Webhook Challenge", "description": "Helper function to complete Netdata webhook challenge", "operationId": "webhook_challenge_alerts_event_netdata_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}}}, "/alerts/event/{provider_type}": {"post": {"tags": ["alerts"], "summary": "Receive Event", "description": "Receive an alert event from a provider", "operationId": "receive_event_alerts_event__provider_type__post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Provider Type"}, "name": "provider_type", "in": "path"}, {"required": false, "schema": {"type": "string", "title": "Provider Id"}, "name": "provider_id", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "query"}], "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Receive Event Alerts Event Provider Type Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}": {"get": {"tags": ["alerts"], "summary": "Get Alert", "description": "Get alert by fingerprint", "operationId": "get_alert_alerts__fingerprint__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AlertDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/enrich": {"post": {"tags": ["alerts"], "summary": "Enrich Alert", "description": "Enrich an alert", "operationId": "enrich_alert_alerts_enrich_post", "parameters": [{"description": "Dispose on new alert", "required": false, "schema": {"type": "boolean", "title": "Dispose On New Alert", "description": "Dispose on new alert", "default": false}, "name": "dispose_on_new_alert", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/EnrichAlertRequestBody"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Enrich Alert Alerts Enrich Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/unenrich": {"post": {"tags": ["alerts"], "summary": "Unenrich Alert", "description": "Un-Enrich an alert", "operationId": "unenrich_alert_alerts_unenrich_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UnEnrichAlertRequestBody"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Response Unenrich Alert Alerts Unenrich Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/search": {"post": {"tags": ["alerts"], "summary": "Search Alerts", "description": "Search alerts", "operationId": "search_alerts_alerts_search_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SearchAlertsRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Search Alerts Alerts Search Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/audit": {"post": {"tags": ["alerts"], "summary": "Get Multiple Fingerprint Alert Audit", "description": "Get alert timeline audit trail for multiple fingerprints", "operationId": "get_multiple_fingerprint_alert_audit_alerts_audit_post", "requestBody": {"content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Fingerprints"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertAuditDto"}, "type": "array", "title": "Response Get Multiple Fingerprint Alert Audit Alerts Audit Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/{fingerprint}/audit": {"get": {"tags": ["alerts"], "summary": "Get Alert Audit", "description": "Get alert timeline audit trail", "operationId": "get_alert_audit_alerts__fingerprint__audit_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Fingerprint"}, "name": "fingerprint", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertAuditDto"}, "type": "array", "title": "Response Get Alert Audit Alerts Fingerprint Audit Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/alerts/quality/metrics": {"get": {"tags": ["alerts"], "summary": "Get Alert Quality", "description": "Get alert quality", "operationId": "get_alert_quality_alerts_quality_metrics_get", "parameters": [{"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Fields", "default": []}, "name": "fields", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Time Stamp"}, "name": "time_stamp", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents": {"get": {"tags": ["incidents"], "summary": "Get All Incidents", "description": "Get last incidents", "operationId": "get_all_incidents_incidents_get", "parameters": [{"required": false, "schema": {"type": "boolean", "title": "Confirmed", "default": true}, "name": "confirmed", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}, {"required": false, "schema": {"allOf": [{"$ref": "#/components/schemas/IncidentSorting"}], "default": "creation_time"}, "name": "sorting", "in": "query"}, {"required": false, "schema": {"items": {"$ref": "#/components/schemas/IncidentStatus"}, "type": "array"}, "name": "status", "in": "query"}, {"required": false, "schema": {"items": {"$ref": "#/components/schemas/IncidentSeverity"}, "type": "array"}, "name": "severity", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Assignees"}, "name": "assignees", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Sources"}, "name": "sources", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Affected Services"}, "name": "affected_services", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentsPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["incidents"], "summary": "Create Incident Endpoint", "description": "Create new incident", "operationId": "create_incident_endpoint_incidents_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDtoIn"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/meta": {"get": {"tags": ["incidents"], "summary": "Get Incidents Meta", "description": "Get incidents' metadata for filtering", "operationId": "get_incidents_meta_incidents_meta_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentListFilterParamsDto"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}": {"get": {"tags": ["incidents"], "summary": "Get Incident", "description": "Get incident by id", "operationId": "get_incident_incidents__incident_id__get", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "put": {"tags": ["incidents"], "summary": "Update Incident", "description": "Update incident by id", "operationId": "update_incident_incidents__incident_id__put", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}, {"description": "Whether the incident update request was generated by AI", "required": false, "schema": {"type": "boolean", "title": "Generatedbyai", "description": "Whether the incident update request was generated by AI", "default": false}, "name": "generatedByAi", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["incidents"], "summary": "Delete Incident", "description": "Delete incident by incident id", "operationId": "delete_incident_incidents__incident_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/merge": {"post": {"tags": ["incidents"], "summary": "Merge Incidents", "description": "Merge incidents", "operationId": "merge_incidents_incidents_merge_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MergeIncidentsRequestDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MergeIncidentsResponseDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/alerts": {"get": {"tags": ["incidents"], "summary": "Get Incident Alerts", "description": "Get incident alerts by incident incident id", "operationId": "get_incident_alerts_incidents__incident_id__alerts_get", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}, {"required": false, "schema": {"type": "boolean", "title": "Include Unlinked", "default": false}, "name": "include_unlinked", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AlertWithIncidentLinkMetadataPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["incidents"], "summary": "Add Alerts To Incident", "description": "Add alerts to incident", "operationId": "add_alerts_to_incident_incidents__incident_id__alerts_post", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}, {"required": false, "schema": {"type": "boolean", "title": "Is Created By Ai", "default": false}, "name": "is_created_by_ai", "in": "query"}], "requestBody": {"content": {"application/json": {"schema": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Alert Ids"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Add Alerts To Incident Incidents Incident Id Alerts Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["incidents"], "summary": "Delete Alerts From Incident", "description": "Delete alerts from incident", "operationId": "delete_alerts_from_incident_incidents__incident_id__alerts_delete", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Alert Ids"}}}, "required": true}, "responses": {"202": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/AlertDto"}, "type": "array", "title": "Response Delete Alerts From Incident Incidents Incident Id Alerts Delete"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/future_incidents": {"get": {"tags": ["incidents"], "summary": "Get Future Incidents For An Incident", "description": "Get same incidents linked to this one", "operationId": "get_future_incidents_for_an_incident_incidents__incident_id__future_incidents_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Incident Id"}, "name": "incident_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentsPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/workflows": {"get": {"tags": ["incidents"], "summary": "Get Incident Workflows", "description": "Get incident workflows by incident id", "operationId": "get_incident_workflows_incidents__incident_id__workflows_get", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowExecutionsPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/confirm": {"post": {"tags": ["incidents"], "summary": "Confirm Incident", "description": "Confirm predicted incident by id", "operationId": "confirm_incident_incidents__incident_id__confirm_post", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/status": {"post": {"tags": ["incidents"], "summary": "Change Incident Status", "description": "Change incident status", "operationId": "change_incident_status_incidents__incident_id__status_post", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentStatusChangeDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/incidents/{incident_id}/comment": {"post": {"tags": ["incidents"], "summary": "Add Comment", "description": "Add incident audit activity", "operationId": "add_comment_incidents__incident_id__comment_post", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Incident Id"}, "name": "incident_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/IncidentStatusChangeDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/AlertAudit"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/webhook": {"get": {"tags": ["settings"], "summary": "Webhook Settings", "description": "Get details about the webhook endpoint (e.g. the API url and an API key)", "operationId": "webhook_settings_settings_webhook_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WebhookSettings"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/smtp": {"get": {"tags": ["settings"], "summary": "Get Smtp Settings", "description": "Get SMTP settings", "operationId": "get_smtp_settings_settings_smtp_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["settings"], "summary": "Update Smtp Settings", "description": "Install or update SMTP settings", "operationId": "update_smtp_settings_settings_smtp_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SMTPSettings"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["settings"], "summary": "Delete Smtp Settings", "description": "Delete SMTP settings", "operationId": "delete_smtp_settings_settings_smtp_delete", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/smtp/test": {"post": {"tags": ["settings"], "summary": "Test Smtp Settings", "description": "Test SMTP settings", "operationId": "test_smtp_settings_settings_smtp_test_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/SMTPSettings"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/apikey": {"put": {"tags": ["settings"], "summary": "Update Api Key", "description": "Update API key secret", "operationId": "update_api_key_settings_apikey_put", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["settings"], "summary": "Create Key", "description": "Create API key", "operationId": "create_key_settings_apikey_post", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/apikeys": {"get": {"tags": ["settings"], "summary": "Get Keys", "description": "Get API keys", "operationId": "get_keys_settings_apikeys_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/apikey/{keyId}": {"delete": {"tags": ["settings"], "summary": "Delete Api Key", "description": "Delete API key", "operationId": "delete_api_key_settings_apikey__keyId__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Keyid"}, "name": "keyId", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/settings/sso": {"get": {"tags": ["settings"], "summary": "Get Sso Settings", "operationId": "get_sso_settings_settings_sso_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflows", "description": "Get workflows", "operationId": "get_workflows_workflows_get", "parameters": [{"required": false, "schema": {"type": "boolean", "title": "Is V2", "default": false}, "name": "is_v2", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"anyOf": [{"items": {"$ref": "#/components/schemas/WorkflowDTO"}, "type": "array"}, {"items": {"type": "object"}, "type": "array"}], "title": "Response Get Workflows Workflows Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["workflows", "alerts"], "summary": "Create Workflow", "description": "Create or update a workflow", "operationId": "create_workflow_workflows_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_create_workflow_workflows_post"}}}, "required": true}, "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowCreateOrUpdateDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/export": {"get": {"tags": ["workflows", "alerts"], "summary": "Export Workflows", "description": "export all workflow Yamls", "operationId": "export_workflows_workflows_export_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Response Export Workflows Workflows Export Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}/run": {"post": {"tags": ["workflows", "alerts"], "summary": "Run Workflow", "description": "Run a workflow", "operationId": "run_workflow_workflows__workflow_id__run_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"type": "object", "title": "Body"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Run Workflow Workflows Workflow Id Run Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/test": {"post": {"tags": ["workflows", "alerts"], "summary": "Run Workflow From Definition", "description": "Test run a workflow from a definition", "operationId": "run_workflow_from_definition_workflows_test_post", "requestBody": {"content": {"multipart/form-data": {"schema": {"$ref": "#/components/schemas/Body_run_workflow_from_definition_workflows_test_post"}}}}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Run Workflow From Definition Workflows Test Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/json": {"post": {"tags": ["workflows", "alerts"], "summary": "Create Workflow From Body", "description": "Create or update a workflow", "operationId": "create_workflow_from_body_workflows_json_post", "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowCreateOrUpdateDTO"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/random-templates": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Random Workflow Templates", "description": "Get random workflow templates", "operationId": "get_random_workflow_templates_workflows_random_templates_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "object"}, "type": "array", "title": "Response Get Random Workflow Templates Workflows Random Templates Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow By Id", "description": "Get workflow executions by ID", "operationId": "get_workflow_by_id_workflows__workflow_id__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}, {"required": false, "schema": {"type": "integer", "title": "Tab", "default": 1}, "name": "tab", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Limit", "default": 25}, "name": "limit", "in": "query"}, {"required": false, "schema": {"type": "integer", "title": "Offset", "default": 0}, "name": "offset", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Status"}, "name": "status", "in": "query"}, {"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Trigger"}, "name": "trigger", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Execution Id"}, "name": "execution_id", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowExecutionsPaginatedResultsDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "put": {"tags": ["workflows", "alerts"], "summary": "Update Workflow By Id", "description": "Update a workflow", "operationId": "update_workflow_by_id_workflows__workflow_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "responses": {"201": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowCreateOrUpdateDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["workflows", "alerts"], "summary": "Delete Workflow By Id", "description": "Delete workflow", "operationId": "delete_workflow_by_id_workflows__workflow_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}/raw": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Raw Workflow By Id", "description": "Get workflow executions by ID", "operationId": "get_raw_workflow_by_id_workflows__workflow_id__raw_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Id"}, "name": "workflow_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "string", "title": "Response Get Raw Workflow By Id Workflows Workflow Id Raw Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/executions": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow Executions By Alert Fingerprint", "description": "Get workflow executions by alert fingerprint", "operationId": "get_workflow_executions_by_alert_fingerprint_workflows_executions_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/WorkflowToAlertExecutionDTO"}, "type": "array", "title": "Response Get Workflow Executions By Alert Fingerprint Workflows Executions Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/workflows/{workflow_id}/runs/{workflow_execution_id}": {"get": {"tags": ["workflows", "alerts"], "summary": "Get Workflow Execution Status", "description": "Get a workflow execution status", "operationId": "get_workflow_execution_status_workflows__workflow_id__runs__workflow_execution_id__get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Workflow Execution Id"}, "name": "workflow_execution_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/WorkflowExecutionDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/whoami": {"get": {"tags": ["whoami"], "summary": "Get Tenant Id", "description": "Get tenant id", "operationId": "get_tenant_id_whoami_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Get Tenant Id Whoami Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/pusher/auth": {"post": {"tags": ["pusher"], "summary": "Pusher Authentication", "description": "Authenticate a user to a private channel\n\nArgs:\n request (Request): The request object\n tenant_id (str, optional): The tenant ID. Defaults to Depends(verify_bearer_token).\n pusher_client (Pusher, optional): Pusher client. Defaults to Depends(get_pusher_client).\n\nRaises:\n HTTPException: 403 if the user is not allowed to access the channel.\n\nReturns:\n dict: The authentication response.", "operationId": "pusher_authentication_pusher_auth_post", "requestBody": {"content": {"application/x-www-form-urlencoded": {"schema": {"$ref": "#/components/schemas/Body_pusher_authentication_pusher_auth_post"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Pusher Authentication Pusher Auth Post"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/status": {"get": {"tags": ["status"], "summary": "Status", "description": "simple status endpoint", "operationId": "status_status_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"type": "object", "title": "Response Status Status Get"}}}}}}}, "/rules": {"get": {"tags": ["rules"], "summary": "Get Rules", "description": "Get Rules", "operationId": "get_rules_rules_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["rules"], "summary": "Create Rule", "description": "Create Rule", "operationId": "create_rule_rules_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/RuleCreateDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/rules/{rule_id}": {"put": {"tags": ["rules"], "summary": "Update Rule", "description": "Update Rule", "operationId": "update_rule_rules__rule_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["rules"], "summary": "Delete Rule", "description": "Delete Rule", "operationId": "delete_rule_rules__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset": {"get": {"tags": ["preset"], "summary": "Get Presets", "description": "Get all presets for tenant", "operationId": "get_presets_preset_get", "parameters": [{"required": false, "schema": {"type": "string", "title": "Time Stamp"}, "name": "time_stamp", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/PresetDto"}, "type": "array", "title": "Response Get Presets Preset Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["preset"], "summary": "Create Preset", "description": "Create a preset for tenant", "operationId": "create_preset_preset_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdatePresetDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PresetDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{uuid}": {"put": {"tags": ["preset"], "summary": "Update Preset", "description": "Update a preset for tenant", "operationId": "update_preset_preset__uuid__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Uuid"}, "name": "uuid", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdatePresetDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/PresetDto"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["preset"], "summary": "Delete Preset", "description": "Delete a preset for tenant", "operationId": "delete_preset_preset__uuid__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Uuid"}, "name": "uuid", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{preset_name}/alerts": {"get": {"tags": ["preset"], "summary": "Get Preset Alerts", "description": "Get a preset for tenant", "operationId": "get_preset_alerts_preset__preset_name__alerts_get", "parameters": [{"required": true, "schema": {"type": "string", "title": "Preset Name"}, "name": "preset_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {}, "type": "array", "title": "Response Get Preset Alerts Preset Preset Name Alerts Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{preset_id}/tab": {"post": {"tags": ["preset"], "summary": "Create Preset Tab", "description": "Create a tab for a preset", "operationId": "create_preset_tab_preset__preset_id__tab_post", "parameters": [{"required": true, "schema": {"type": "string", "title": "Preset Id"}, "name": "preset_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreatePresetTab"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/preset/{preset_id}/tab/{tab_id}": {"delete": {"tags": ["preset"], "summary": "Delete Tab", "description": "Delete a tab from a preset", "operationId": "delete_tab_preset__preset_id__tab__tab_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Preset Id"}, "name": "preset_id", "in": "path"}, {"required": true, "schema": {"type": "string", "title": "Tab Id"}, "name": "tab_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/mapping": {"get": {"tags": ["enrichment", "mapping"], "summary": "Get Rules", "description": "Get all mapping rules", "operationId": "get_rules_mapping_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MappingRuleDtoOut"}, "type": "array", "title": "Response Get Rules Mapping Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["enrichment", "mapping"], "summary": "Create Rule", "description": "Create a new mapping rule", "operationId": "create_rule_mapping_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRuleDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRule"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/mapping/{rule_id}": {"put": {"tags": ["enrichment", "mapping"], "summary": "Update Rule", "description": "Update an existing rule", "operationId": "update_rule_mapping__rule_id__put", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRuleDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MappingRuleDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["enrichment", "mapping"], "summary": "Delete Rule", "description": "Delete a mapping rule", "operationId": "delete_rule_mapping__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/groups": {"get": {"tags": ["auth", "groups"], "summary": "Get Groups", "description": "Get all groups", "operationId": "get_groups_auth_groups_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Group"}, "type": "array", "title": "Response Get Groups Auth Groups Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "groups"], "summary": "Create Group", "description": "Create a group", "operationId": "create_group_auth_groups_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdateGroupRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/groups/{group_name}": {"put": {"tags": ["auth", "groups"], "summary": "Update Group", "description": "Update a group", "operationId": "update_group_auth_groups__group_name__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Group Name"}, "name": "group_name", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateOrUpdateGroupRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["auth", "groups"], "summary": "Delete Group", "description": "Delete a group", "operationId": "delete_group_auth_groups__group_name__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Group Name"}, "name": "group_name", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/permissions": {"get": {"tags": ["auth", "permissions"], "summary": "Get Permissions", "description": "Get resources permissions", "operationId": "get_permissions_auth_permissions_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ResourcePermission"}, "type": "array", "title": "Response Get Permissions Auth Permissions Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "permissions"], "summary": "Create Permissions", "description": "Create permissions for resources", "operationId": "create_permissions_auth_permissions_post", "requestBody": {"content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ResourcePermission"}, "type": "array", "title": "Resource Permissions", "description": "List of resource permissions"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/permissions/scopes": {"get": {"tags": ["auth", "permissions"], "summary": "Get Scopes", "description": "Get all resources types", "operationId": "get_scopes_auth_permissions_scopes_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "string"}, "type": "array", "title": "Response Get Scopes Auth Permissions Scopes Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/roles": {"get": {"tags": ["auth", "roles"], "summary": "Get Roles", "description": "Get roles", "operationId": "get_roles_auth_roles_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/Role"}, "type": "array", "title": "Response Get Roles Auth Roles Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "roles"], "summary": "Create Role", "description": "Create role", "operationId": "create_role_auth_roles_post", "requestBody": {"content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/CreateOrUpdateRole"}], "title": "Role", "description": "Role"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/roles/{role_id}": {"put": {"tags": ["auth", "roles"], "summary": "Update Role", "description": "Update role", "operationId": "update_role_auth_roles__role_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Role Id"}, "name": "role_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"allOf": [{"$ref": "#/components/schemas/CreateOrUpdateRole"}], "title": "Role", "description": "Role"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["auth", "roles"], "summary": "Delete Role", "description": "Delete role", "operationId": "delete_role_auth_roles__role_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Role Id"}, "name": "role_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/users": {"get": {"tags": ["auth", "users"], "summary": "Get Users", "description": "Get all users", "operationId": "get_users_auth_users_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/User"}, "type": "array", "title": "Response Get Users Auth Users Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["auth", "users"], "summary": "Create User", "description": "Create a user", "operationId": "create_user_auth_users_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/CreateUserRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/auth/users/{user_email}": {"put": {"tags": ["auth", "users"], "summary": "Update User", "description": "Update a user", "operationId": "update_user_auth_users__user_email__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "User Email"}, "name": "user_email", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/UpdateUserRequest"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["auth", "users"], "summary": "Delete User", "description": "Delete a user", "operationId": "delete_user_auth_users__user_email__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "User Email"}, "name": "user_email", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/metrics": {"get": {"tags": ["metrics"], "summary": "Get Metrics", "description": "This endpoint is used by Prometheus to scrape such metrics from the application:\n- alerts_total {incident_name, incident_id} - The total number of alerts per incident.\n- open_incidents_total - The total number of open incidents\n\nPlease note that those metrics are per-tenant and are not designed to be used for the monitoring of the application itself.\n\nExample prometheus configuration:\n```\nscrape_configs:\n- job_name: \"scrape_keep\"\n scrape_interval: 5m # It's important to scrape not too often to avoid rate limiting.\n static_configs:\n - targets: [\"https://api.keephq.dev\"] # Or your own domain.\n authorization:\n type: Bearer\n credentials: \"{Your API Key}\"\n\n # Optional, you can add labels to exported incidents. \n # Label values will be equal to the last incident's alert payload value matching the label.\n # Attention! Don't add \"flaky\" labels which could change from alert to alert within the same incident.\n # Good labels: ['labels.department', 'labels.team'], bad labels: ['labels.severity', 'labels.pod_id']\n # Check Keep -> Feed -> \"extraPayload\" column, it will help in writing labels.\n\n params:\n labels: ['labels.service', 'labels.queue']\n # Will resuld as: \"labels_service\" and \"labels_queue\".\n```", "operationId": "get_metrics_metrics_get", "parameters": [{"required": false, "schema": {"items": {"type": "string"}, "type": "array", "title": "Labels"}, "name": "labels", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/extraction": {"get": {"tags": ["enrichment", "extraction"], "summary": "Get Extraction Rules", "description": "Get all extraction rules", "operationId": "get_extraction_rules_extraction_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/ExtractionRuleDtoOut"}, "type": "array", "title": "Response Get Extraction Rules Extraction Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["enrichment", "extraction"], "summary": "Create Extraction Rule", "description": "Create a new extraction rule", "operationId": "create_extraction_rule_extraction_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/extraction/{rule_id}": {"put": {"tags": ["enrichment", "extraction"], "summary": "Update Extraction Rule", "description": "Update an existing extraction rule", "operationId": "update_extraction_rule_extraction__rule_id__put", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoBase"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/ExtractionRuleDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["enrichment", "extraction"], "summary": "Delete Extraction Rule", "description": "Delete an extraction rule", "operationId": "delete_extraction_rule_extraction__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/dashboard": {"get": {"tags": ["dashboard"], "summary": "Read Dashboards", "operationId": "read_dashboards_dashboard_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/DashboardResponseDTO"}, "type": "array", "title": "Response Read Dashboards Dashboard Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["dashboard"], "summary": "Create Dashboard", "operationId": "create_dashboard_dashboard_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardCreateDTO"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardResponseDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/dashboard/{dashboard_id}": {"put": {"tags": ["dashboard"], "summary": "Update Dashboard", "operationId": "update_dashboard_dashboard__dashboard_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Dashboard Id"}, "name": "dashboard_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardUpdateDTO"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/DashboardResponseDTO"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["dashboard"], "summary": "Delete Dashboard", "operationId": "delete_dashboard_dashboard__dashboard_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Dashboard Id"}, "name": "dashboard_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/tags": {"get": {"tags": ["tags"], "summary": "Get Tags", "description": "get tags", "operationId": "get_tags_tags_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"type": "object"}, "type": "array", "title": "Response Get Tags Tags Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/maintenance": {"get": {"tags": ["maintenance"], "summary": "Get Maintenance Rules", "description": "Get all maintenance rules", "operationId": "get_maintenance_rules_maintenance_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/MaintenanceRuleRead"}, "type": "array", "title": "Response Get Maintenance Rules Maintenance Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["maintenance"], "summary": "Create Maintenance Rule", "description": "Create a new maintenance rule", "operationId": "create_maintenance_rule_maintenance_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleRead"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/maintenance/{rule_id}": {"put": {"tags": ["maintenance"], "summary": "Update Maintenance Rule", "description": "Update an existing maintenance rule", "operationId": "update_maintenance_rule_maintenance__rule_id__put", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleCreate"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/MaintenanceRuleRead"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["maintenance"], "summary": "Delete Maintenance Rule", "description": "Delete a maintenance rule", "operationId": "delete_maintenance_rule_maintenance__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "integer", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/topology": {"get": {"tags": ["topology"], "summary": "Get Topology Data", "description": "Get all topology data", "operationId": "get_topology_data_topology_get", "parameters": [{"required": false, "schema": {"type": "string", "title": "Provider Ids"}, "name": "provider_ids", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Services"}, "name": "services", "in": "query"}, {"required": false, "schema": {"type": "string", "title": "Environment"}, "name": "environment", "in": "query"}, {"required": false, "schema": {"type": "boolean", "title": "Include Empty Deps", "default": false}, "name": "include_empty_deps", "in": "query"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/TopologyServiceDtoOut"}, "type": "array", "title": "Response Get Topology Data Topology Get"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/topology/applications": {"get": {"tags": ["topology"], "summary": "Get Applications", "description": "Get all applications", "operationId": "get_applications_topology_applications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"items": {"$ref": "#/components/schemas/TopologyApplicationDtoOut"}, "type": "array", "title": "Response Get Applications Topology Applications Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["topology"], "summary": "Create Application", "description": "Create a new application", "operationId": "create_application_topology_applications_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/topology/applications/{application_id}": {"put": {"tags": ["topology"], "summary": "Update Application", "description": "Update an application", "operationId": "update_application_topology_applications__application_id__put", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Application Id"}, "name": "application_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoIn"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/TopologyApplicationDtoOut"}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["topology"], "summary": "Delete Application", "description": "Delete an application", "operationId": "delete_application_topology_applications__application_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "format": "uuid", "title": "Application Id"}, "name": "application_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/deduplications": {"get": {"tags": ["deduplications"], "summary": "Get Deduplications", "description": "Get Deduplications", "operationId": "get_deduplications_deduplications_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "post": {"tags": ["deduplications"], "summary": "Create Deduplication Rule", "description": "Create Deduplication Rule", "operationId": "create_deduplication_rule_deduplications_post", "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeduplicationRuleRequestDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/deduplications/fields": {"get": {"tags": ["deduplications"], "summary": "Get Deduplication Fields", "description": "Get Optional Fields For Deduplications", "operationId": "get_deduplication_fields_deduplications_fields_get", "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {"additionalProperties": {"items": {"type": "string"}, "type": "array"}, "type": "object", "title": "Response Get Deduplication Fields Deduplications Fields Get"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}, "/deduplications/{rule_id}": {"put": {"tags": ["deduplications"], "summary": "Update Deduplication Rule", "description": "Update Deduplication Rule", "operationId": "update_deduplication_rule_deduplications__rule_id__put", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "requestBody": {"content": {"application/json": {"schema": {"$ref": "#/components/schemas/DeduplicationRuleRequestDto"}}}, "required": true}, "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}, "delete": {"tags": ["deduplications"], "summary": "Delete Deduplication Rule", "description": "Delete Deduplication Rule", "operationId": "delete_deduplication_rule_deduplications__rule_id__delete", "parameters": [{"required": true, "schema": {"type": "string", "title": "Rule Id"}, "name": "rule_id", "in": "path"}], "responses": {"200": {"description": "Successful Response", "content": {"application/json": {"schema": {}}}}, "422": {"description": "Validation Error", "content": {"application/json": {"schema": {"$ref": "#/components/schemas/HTTPValidationError"}}}}}, "security": [{"API Key": []}, {"HTTPBasic": []}, {"OAuth2PasswordBearer": []}]}}}, "components": {"schemas": {"AlertActionType": {"enum": ["alert was triggered", "alert acknowledged", "alert automatically resolved", "alert automatically resolved by API", "alert manually resolved", "alert status manually changed", "alert status changed by API", "alert status undone", "alert enriched by workflow", "alert enriched by mapping rule", "alert was deduplicated", "alert was assigned with ticket", "alert was unassigned from ticket", "alert ticket was updated", "alert enrichments disposed", "alert deleted", "alert enriched", "alert un-enriched", "a comment was added to the alert", "a comment was removed from the alert", "Alert is in maintenance window", "A comment was added to the incident"], "title": "AlertActionType", "description": "An enumeration."}, "AlertAudit": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "fingerprint": {"type": "string", "title": "Fingerprint"}, "tenant_id": {"type": "string", "title": "Tenant Id"}, "timestamp": {"type": "string", "format": "date-time", "title": "Timestamp"}, "user_id": {"type": "string", "title": "User Id"}, "action": {"type": "string", "title": "Action"}, "description": {"type": "string", "title": "Description"}}, "type": "object", "required": ["fingerprint", "tenant_id", "user_id", "action", "description"], "title": "AlertAudit"}, "AlertAuditDto": {"properties": {"id": {"type": "string", "title": "Id"}, "timestamp": {"type": "string", "format": "date-time", "title": "Timestamp"}, "fingerprint": {"type": "string", "title": "Fingerprint"}, "action": {"$ref": "#/components/schemas/AlertActionType"}, "user_id": {"type": "string", "title": "User Id"}, "description": {"type": "string", "title": "Description"}}, "type": "object", "required": ["id", "timestamp", "fingerprint", "action", "user_id", "description"], "title": "AlertAuditDto"}, "AlertDto": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "status": {"$ref": "#/components/schemas/AlertStatus"}, "severity": {"$ref": "#/components/schemas/AlertSeverity"}, "lastReceived": {"type": "string", "title": "Lastreceived"}, "firingStartTime": {"type": "string", "title": "Firingstarttime"}, "environment": {"type": "string", "title": "Environment", "default": "undefined"}, "isFullDuplicate": {"type": "boolean", "title": "Isfullduplicate", "default": false}, "isPartialDuplicate": {"type": "boolean", "title": "Ispartialduplicate", "default": false}, "duplicateReason": {"type": "string", "title": "Duplicatereason"}, "service": {"type": "string", "title": "Service"}, "source": {"items": {"type": "string"}, "type": "array", "title": "Source", "default": []}, "apiKeyRef": {"type": "string", "title": "Apikeyref"}, "message": {"type": "string", "title": "Message"}, "description": {"type": "string", "title": "Description"}, "pushed": {"type": "boolean", "title": "Pushed", "default": false}, "event_id": {"type": "string", "title": "Event Id"}, "url": {"type": "string", "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Url"}, "labels": {"type": "object", "title": "Labels", "default": {}}, "fingerprint": {"type": "string", "title": "Fingerprint"}, "deleted": {"type": "boolean", "title": "Deleted", "default": false}, "dismissUntil": {"type": "string", "title": "Dismissuntil"}, "dismissed": {"type": "boolean", "title": "Dismissed", "default": false}, "assignee": {"type": "string", "title": "Assignee"}, "providerId": {"type": "string", "title": "Providerid"}, "providerType": {"type": "string", "title": "Providertype"}, "note": {"type": "string", "title": "Note"}, "startedAt": {"type": "string", "title": "Startedat"}, "isNoisy": {"type": "boolean", "title": "Isnoisy", "default": false}, "enriched_fields": {"items": {}, "type": "array", "title": "Enriched Fields", "default": []}, "incident": {"type": "string", "title": "Incident"}}, "type": "object", "required": ["name", "status", "severity", "lastReceived"], "title": "AlertDto", "example": {"id": "1234", "name": "Alert name", "status": "firing", "lastReceived": "2021-01-01T00:00:00.000Z", "environment": "production", "service": "backend", "source": ["keep"], "message": "Keep: Alert message", "description": "Keep: Alert description", "severity": "critical", "pushed": true, "event_id": "1234", "url": "https://www.keephq.dev?alertId=1234", "labels": {"key": "value"}, "ticket_url": "https://www.keephq.dev?enrichedTicketId=456", "fingerprint": "1234"}}, "AlertSeverity": {"enum": ["critical", "high", "warning", "info", "low"], "title": "AlertSeverity", "description": "An enumeration."}, "AlertStatus": {"enum": ["firing", "resolved", "acknowledged", "suppressed", "pending"], "title": "AlertStatus", "description": "An enumeration."}, "AlertWithIncidentLinkMetadataDto": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "status": {"$ref": "#/components/schemas/AlertStatus"}, "severity": {"$ref": "#/components/schemas/AlertSeverity"}, "lastReceived": {"type": "string", "title": "Lastreceived"}, "firingStartTime": {"type": "string", "title": "Firingstarttime"}, "environment": {"type": "string", "title": "Environment", "default": "undefined"}, "isFullDuplicate": {"type": "boolean", "title": "Isfullduplicate", "default": false}, "isPartialDuplicate": {"type": "boolean", "title": "Ispartialduplicate", "default": false}, "duplicateReason": {"type": "string", "title": "Duplicatereason"}, "service": {"type": "string", "title": "Service"}, "source": {"items": {"type": "string"}, "type": "array", "title": "Source", "default": []}, "apiKeyRef": {"type": "string", "title": "Apikeyref"}, "message": {"type": "string", "title": "Message"}, "description": {"type": "string", "title": "Description"}, "pushed": {"type": "boolean", "title": "Pushed", "default": false}, "event_id": {"type": "string", "title": "Event Id"}, "url": {"type": "string", "maxLength": 65536, "minLength": 1, "format": "uri", "title": "Url"}, "labels": {"type": "object", "title": "Labels", "default": {}}, "fingerprint": {"type": "string", "title": "Fingerprint"}, "deleted": {"type": "boolean", "title": "Deleted", "default": false}, "dismissUntil": {"type": "string", "title": "Dismissuntil"}, "dismissed": {"type": "boolean", "title": "Dismissed", "default": false}, "assignee": {"type": "string", "title": "Assignee"}, "providerId": {"type": "string", "title": "Providerid"}, "providerType": {"type": "string", "title": "Providertype"}, "note": {"type": "string", "title": "Note"}, "startedAt": {"type": "string", "title": "Startedat"}, "isNoisy": {"type": "boolean", "title": "Isnoisy", "default": false}, "enriched_fields": {"items": {}, "type": "array", "title": "Enriched Fields", "default": []}, "incident": {"type": "string", "title": "Incident"}, "is_created_by_ai": {"type": "boolean", "title": "Is Created By Ai", "default": false}}, "type": "object", "required": ["name", "status", "severity", "lastReceived"], "title": "AlertWithIncidentLinkMetadataDto", "example": {"id": "1234", "name": "Alert name", "status": "firing", "lastReceived": "2021-01-01T00:00:00.000Z", "environment": "production", "service": "backend", "source": ["keep"], "message": "Keep: Alert message", "description": "Keep: Alert description", "severity": "critical", "pushed": true, "event_id": "1234", "url": "https://www.keephq.dev?alertId=1234", "labels": {"key": "value"}, "ticket_url": "https://www.keephq.dev?enrichedTicketId=456", "fingerprint": "1234"}}, "AlertWithIncidentLinkMetadataPaginatedResultsDto": {"properties": {"limit": {"type": "integer", "title": "Limit", "default": 25}, "offset": {"type": "integer", "title": "Offset", "default": 0}, "count": {"type": "integer", "title": "Count"}, "items": {"items": {"$ref": "#/components/schemas/AlertWithIncidentLinkMetadataDto"}, "type": "array", "title": "Items"}}, "type": "object", "required": ["count", "items"], "title": "AlertWithIncidentLinkMetadataPaginatedResultsDto"}, "Body_create_actions_actions_post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "title": "Body_create_actions_actions_post"}, "Body_create_workflow_workflows_post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_create_workflow_workflows_post"}, "Body_pusher_authentication_pusher_auth_post": {"properties": {"channel_name": {"title": "Channel Name"}, "socket_id": {"title": "Socket Id"}}, "type": "object", "required": ["channel_name", "socket_id"], "title": "Body_pusher_authentication_pusher_auth_post"}, "Body_put_action_actions__action_id__put": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "required": ["file"], "title": "Body_put_action_actions__action_id__put"}, "Body_run_workflow_from_definition_workflows_test_post": {"properties": {"file": {"type": "string", "format": "binary", "title": "File"}}, "type": "object", "title": "Body_run_workflow_from_definition_workflows_test_post"}, "CreateOrUpdateGroupRequest": {"properties": {"name": {"type": "string", "title": "Name"}, "roles": {"items": {"type": "string"}, "type": "array", "title": "Roles"}, "members": {"items": {"type": "string"}, "type": "array", "title": "Members"}}, "type": "object", "required": ["name", "roles", "members"], "title": "CreateOrUpdateGroupRequest"}, "CreateOrUpdatePresetDto": {"properties": {"name": {"type": "string", "title": "Name"}, "options": {"items": {"$ref": "#/components/schemas/PresetOption"}, "type": "array", "title": "Options"}, "is_private": {"type": "boolean", "title": "Is Private", "default": false}, "is_noisy": {"type": "boolean", "title": "Is Noisy", "default": false}, "tags": {"items": {"$ref": "#/components/schemas/TagDto"}, "type": "array", "title": "Tags", "default": []}}, "type": "object", "required": ["options"], "title": "CreateOrUpdatePresetDto"}, "CreateOrUpdateRole": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "scopes": {"items": {"type": "string"}, "type": "array", "uniqueItems": true, "title": "Scopes"}}, "type": "object", "title": "CreateOrUpdateRole"}, "CreatePresetTab": {"properties": {"name": {"type": "string", "title": "Name"}, "filter": {"type": "string", "title": "Filter"}}, "type": "object", "required": ["name", "filter"], "title": "CreatePresetTab"}, "CreateUserRequest": {"properties": {"username": {"type": "string", "title": "Username"}, "name": {"type": "string", "title": "Name"}, "password": {"type": "string", "title": "Password"}, "role": {"type": "string", "title": "Role"}, "groups": {"items": {"type": "string"}, "type": "array", "title": "Groups"}}, "type": "object", "required": ["username"], "title": "CreateUserRequest"}, "DashboardCreateDTO": {"properties": {"dashboard_name": {"type": "string", "title": "Dashboard Name"}, "dashboard_config": {"type": "object", "title": "Dashboard Config"}}, "type": "object", "required": ["dashboard_name", "dashboard_config"], "title": "DashboardCreateDTO"}, "DashboardResponseDTO": {"properties": {"id": {"type": "string", "title": "Id"}, "dashboard_name": {"type": "string", "title": "Dashboard Name"}, "dashboard_config": {"type": "object", "title": "Dashboard Config"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}}, "type": "object", "required": ["id", "dashboard_name", "dashboard_config", "created_at", "updated_at"], "title": "DashboardResponseDTO"}, "DashboardUpdateDTO": {"properties": {"dashboard_config": {"type": "object", "title": "Dashboard Config"}, "dashboard_name": {"type": "string", "title": "Dashboard Name"}}, "type": "object", "title": "DashboardUpdateDTO"}, "DeduplicationRuleRequestDto": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "provider_type": {"type": "string", "title": "Provider Type"}, "provider_id": {"type": "string", "title": "Provider Id"}, "fingerprint_fields": {"items": {"type": "string"}, "type": "array", "title": "Fingerprint Fields"}, "full_deduplication": {"type": "boolean", "title": "Full Deduplication", "default": false}, "ignore_fields": {"items": {"type": "string"}, "type": "array", "title": "Ignore Fields"}}, "type": "object", "required": ["name", "provider_type", "fingerprint_fields"], "title": "DeduplicationRuleRequestDto"}, "DeleteRequestBody": {"properties": {"fingerprint": {"type": "string", "title": "Fingerprint"}, "lastReceived": {"type": "string", "title": "Lastreceived"}, "restore": {"type": "boolean", "title": "Restore", "default": false}}, "type": "object", "required": ["fingerprint", "lastReceived"], "title": "DeleteRequestBody"}, "EnrichAlertRequestBody": {"properties": {"enrichments": {"additionalProperties": {"type": "string"}, "type": "object", "title": "Enrichments"}, "fingerprint": {"type": "string", "title": "Fingerprint"}}, "type": "object", "required": ["enrichments", "fingerprint"], "title": "EnrichAlertRequestBody"}, "ExtractionRuleDtoBase": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "attribute": {"type": "string", "title": "Attribute"}, "condition": {"type": "string", "title": "Condition"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "regex": {"type": "string", "title": "Regex"}, "pre": {"type": "boolean", "title": "Pre", "default": false}}, "type": "object", "required": ["name", "regex"], "title": "ExtractionRuleDtoBase"}, "ExtractionRuleDtoOut": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "attribute": {"type": "string", "title": "Attribute"}, "condition": {"type": "string", "title": "Condition"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "regex": {"type": "string", "title": "Regex"}, "pre": {"type": "boolean", "title": "Pre", "default": false}, "id": {"type": "integer", "title": "Id"}, "created_by": {"type": "string", "title": "Created By"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "updated_by": {"type": "string", "title": "Updated By"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}}, "type": "object", "required": ["name", "regex", "id", "created_at"], "title": "ExtractionRuleDtoOut"}, "Group": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "roles": {"items": {"type": "string"}, "type": "array", "title": "Roles", "default": []}, "members": {"items": {"type": "string"}, "type": "array", "title": "Members", "default": []}, "memberCount": {"type": "integer", "title": "Membercount", "default": 0}}, "type": "object", "required": ["id", "name"], "title": "Group"}, "HTTPValidationError": {"properties": {"detail": {"items": {"$ref": "#/components/schemas/ValidationError"}, "type": "array", "title": "Detail"}}, "type": "object", "title": "HTTPValidationError"}, "IncidentDto": {"properties": {"user_generated_name": {"type": "string", "title": "User Generated Name"}, "assignee": {"type": "string", "title": "Assignee"}, "user_summary": {"type": "string", "title": "User Summary"}, "same_incident_in_the_past_id": {"type": "string", "format": "uuid", "title": "Same Incident In The Past Id"}, "id": {"type": "string", "format": "uuid", "title": "Id"}, "start_time": {"type": "string", "format": "date-time", "title": "Start Time"}, "last_seen_time": {"type": "string", "format": "date-time", "title": "Last Seen Time"}, "end_time": {"type": "string", "format": "date-time", "title": "End Time"}, "alerts_count": {"type": "integer", "title": "Alerts Count"}, "alert_sources": {"items": {"type": "string"}, "type": "array", "title": "Alert Sources"}, "severity": {"$ref": "#/components/schemas/IncidentSeverity"}, "status": {"allOf": [{"$ref": "#/components/schemas/IncidentStatus"}], "default": "firing"}, "services": {"items": {"type": "string"}, "type": "array", "title": "Services"}, "is_predicted": {"type": "boolean", "title": "Is Predicted"}, "is_confirmed": {"type": "boolean", "title": "Is Confirmed"}, "generated_summary": {"type": "string", "title": "Generated Summary"}, "ai_generated_name": {"type": "string", "title": "Ai Generated Name"}, "rule_fingerprint": {"type": "string", "title": "Rule Fingerprint"}, "merged_into_incident_id": {"type": "string", "format": "uuid", "title": "Merged Into Incident Id"}, "merged_by": {"type": "string", "title": "Merged By"}, "merged_at": {"type": "string", "format": "date-time", "title": "Merged At"}}, "type": "object", "required": ["id", "alerts_count", "alert_sources", "severity", "services", "is_predicted", "is_confirmed"], "title": "IncidentDto", "example": {"id": "c2509cb3-6168-4347-b83b-a41da9df2d5b", "name": "Incident name", "user_summary": "Keep: Incident description", "status": "firing"}}, "IncidentDtoIn": {"properties": {"user_generated_name": {"type": "string", "title": "User Generated Name"}, "assignee": {"type": "string", "title": "Assignee"}, "user_summary": {"type": "string", "title": "User Summary"}, "same_incident_in_the_past_id": {"type": "string", "format": "uuid", "title": "Same Incident In The Past Id"}}, "type": "object", "title": "IncidentDtoIn", "example": {"id": "c2509cb3-6168-4347-b83b-a41da9df2d5b", "name": "Incident name", "user_summary": "Keep: Incident description", "status": "firing"}}, "IncidentListFilterParamsDto": {"properties": {"statuses": {"items": {"$ref": "#/components/schemas/IncidentStatus"}, "type": "array", "default": ["firing", "resolved", "acknowledged", "merged"]}, "severities": {"items": {"$ref": "#/components/schemas/IncidentSeverity"}, "type": "array", "default": ["critical", "high", "warning", "info", "low"]}, "assignees": {"items": {"type": "string"}, "type": "array", "title": "Assignees"}, "services": {"items": {"type": "string"}, "type": "array", "title": "Services"}, "sources": {"items": {"type": "string"}, "type": "array", "title": "Sources"}}, "type": "object", "required": ["assignees", "services", "sources"], "title": "IncidentListFilterParamsDto"}, "IncidentSeverity": {"enum": ["critical", "high", "warning", "info", "low"], "title": "IncidentSeverity", "description": "An enumeration."}, "IncidentSorting": {"enum": ["creation_time", "start_time", "last_seen_time", "severity", "status", "alerts_count", "-creation_time", "-start_time", "-last_seen_time", "-severity", "-status", "-alerts_count"], "title": "IncidentSorting", "description": "An enumeration."}, "IncidentStatus": {"enum": ["firing", "resolved", "acknowledged", "merged"], "title": "IncidentStatus", "description": "An enumeration."}, "IncidentStatusChangeDto": {"properties": {"status": {"$ref": "#/components/schemas/IncidentStatus"}, "comment": {"type": "string", "title": "Comment"}}, "type": "object", "required": ["status"], "title": "IncidentStatusChangeDto"}, "IncidentsPaginatedResultsDto": {"properties": {"limit": {"type": "integer", "title": "Limit", "default": 25}, "offset": {"type": "integer", "title": "Offset", "default": 0}, "count": {"type": "integer", "title": "Count"}, "items": {"items": {"$ref": "#/components/schemas/IncidentDto"}, "type": "array", "title": "Items"}}, "type": "object", "required": ["count", "items"], "title": "IncidentsPaginatedResultsDto"}, "MaintenanceRuleCreate": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "cel_query": {"type": "string", "title": "Cel Query"}, "start_time": {"type": "string", "format": "date-time", "title": "Start Time"}, "duration_seconds": {"type": "integer", "title": "Duration Seconds"}, "suppress": {"type": "boolean", "title": "Suppress", "default": false}, "enabled": {"type": "boolean", "title": "Enabled", "default": true}}, "type": "object", "required": ["name", "cel_query", "start_time"], "title": "MaintenanceRuleCreate"}, "MaintenanceRuleRead": {"properties": {"id": {"type": "integer", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "created_by": {"type": "string", "title": "Created By"}, "cel_query": {"type": "string", "title": "Cel Query"}, "start_time": {"type": "string", "format": "date-time", "title": "Start Time"}, "end_time": {"type": "string", "format": "date-time", "title": "End Time"}, "duration_seconds": {"type": "integer", "title": "Duration Seconds"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}, "suppress": {"type": "boolean", "title": "Suppress", "default": false}, "enabled": {"type": "boolean", "title": "Enabled", "default": true}}, "type": "object", "required": ["id", "name", "created_by", "cel_query", "start_time", "end_time"], "title": "MaintenanceRuleRead"}, "MappingRule": {"properties": {"id": {"type": "integer", "title": "Id"}, "tenant_id": {"type": "string", "title": "Tenant Id"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "name": {"type": "string", "maxLength": 255, "title": "Name"}, "description": {"type": "string", "maxLength": 2048, "title": "Description"}, "file_name": {"type": "string", "maxLength": 255, "title": "File Name"}, "created_by": {"type": "string", "maxLength": 255, "title": "Created By"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "override": {"type": "boolean", "title": "Override", "default": true}, "condition": {"type": "string", "maxLength": 2000, "title": "Condition"}, "type": {"type": "string", "maxLength": 255, "title": "Type"}, "matchers": {"items": {"type": "string"}, "type": "array", "title": "Matchers"}, "rows": {"items": {"type": "object"}, "type": "array", "title": "Rows"}, "updated_by": {"type": "string", "maxLength": 255, "title": "Updated By"}, "last_updated_at": {"type": "string", "format": "date-time", "title": "Last Updated At"}}, "type": "object", "required": ["tenant_id", "name", "type", "matchers"], "title": "MappingRule"}, "MappingRuleDtoIn": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "file_name": {"type": "string", "title": "File Name"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "matchers": {"items": {"type": "string"}, "type": "array", "title": "Matchers"}, "type": {"type": "string", "enum": ["csv", "topology"], "title": "Type", "default": "csv"}, "rows": {"items": {"type": "object"}, "type": "array", "title": "Rows"}}, "type": "object", "required": ["name", "matchers"], "title": "MappingRuleDtoIn"}, "MappingRuleDtoOut": {"properties": {"name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "file_name": {"type": "string", "title": "File Name"}, "priority": {"type": "integer", "title": "Priority", "default": 0}, "matchers": {"items": {"type": "string"}, "type": "array", "title": "Matchers"}, "type": {"type": "string", "enum": ["csv", "topology"], "title": "Type", "default": "csv"}, "id": {"type": "integer", "title": "Id"}, "created_by": {"type": "string", "title": "Created By"}, "created_at": {"type": "string", "format": "date-time", "title": "Created At"}, "attributes": {"items": {"type": "string"}, "type": "array", "title": "Attributes", "default": []}, "updated_by": {"type": "string", "title": "Updated By"}, "last_updated_at": {"type": "string", "format": "date-time", "title": "Last Updated At"}}, "type": "object", "required": ["name", "matchers", "id", "created_at"], "title": "MappingRuleDtoOut"}, "MergeIncidentsRequestDto": {"properties": {"source_incident_ids": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Source Incident Ids"}, "destination_incident_id": {"type": "string", "format": "uuid", "title": "Destination Incident Id"}}, "type": "object", "required": ["source_incident_ids", "destination_incident_id"], "title": "MergeIncidentsRequestDto"}, "MergeIncidentsResponseDto": {"properties": {"merged_incident_ids": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Merged Incident Ids"}, "skipped_incident_ids": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Skipped Incident Ids"}, "failed_incident_ids": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Failed Incident Ids"}, "destination_incident_id": {"type": "string", "format": "uuid", "title": "Destination Incident Id"}, "message": {"type": "string", "title": "Message"}}, "type": "object", "required": ["merged_incident_ids", "skipped_incident_ids", "failed_incident_ids", "destination_incident_id", "message"], "title": "MergeIncidentsResponseDto"}, "PermissionEntity": {"properties": {"id": {"type": "string", "title": "Id"}, "type": {"type": "string", "title": "Type"}}, "type": "object", "required": ["id", "type"], "title": "PermissionEntity"}, "PresetDto": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "options": {"items": {}, "type": "array", "title": "Options", "default": []}, "created_by": {"type": "string", "title": "Created By"}, "is_private": {"type": "boolean", "title": "Is Private", "default": false}, "is_noisy": {"type": "boolean", "title": "Is Noisy", "default": false}, "should_do_noise_now": {"type": "boolean", "title": "Should Do Noise Now", "default": false}, "alerts_count": {"type": "integer", "title": "Alerts Count", "default": 0}, "static": {"type": "boolean", "title": "Static", "default": false}, "tags": {"items": {"$ref": "#/components/schemas/TagDto"}, "type": "array", "title": "Tags", "default": []}}, "type": "object", "required": ["id", "name"], "title": "PresetDto"}, "PresetOption": {"properties": {"label": {"type": "string", "title": "Label"}, "value": {"anyOf": [{"type": "string"}, {"type": "object"}], "title": "Value"}}, "type": "object", "required": ["label", "value"], "title": "PresetOption"}, "PresetSearchQuery": {"properties": {"cel_query": {"type": "string", "minLength": 1, "title": "Cel Query"}, "sql_query": {"type": "object", "title": "Sql Query"}, "limit": {"type": "integer", "minimum": 0.0, "title": "Limit", "default": 1000}, "timeframe": {"type": "integer", "minimum": 0.0, "title": "Timeframe", "default": 0}}, "type": "object", "required": ["cel_query", "sql_query"], "title": "PresetSearchQuery"}, "ProviderDTO": {"properties": {"type": {"type": "string", "title": "Type"}, "id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "installed": {"type": "boolean", "title": "Installed"}}, "type": "object", "required": ["type", "name", "installed"], "title": "ProviderDTO"}, "ProviderWebhookSettings": {"properties": {"webhookDescription": {"type": "string", "title": "Webhookdescription"}, "webhookTemplate": {"type": "string", "title": "Webhooktemplate"}, "webhookMarkdown": {"type": "string", "title": "Webhookmarkdown"}}, "type": "object", "required": ["webhookTemplate"], "title": "ProviderWebhookSettings"}, "ResourcePermission": {"properties": {"resource_id": {"type": "string", "title": "Resource Id"}, "resource_name": {"type": "string", "title": "Resource Name"}, "resource_type": {"type": "string", "title": "Resource Type"}, "permissions": {"items": {"$ref": "#/components/schemas/PermissionEntity"}, "type": "array", "title": "Permissions"}}, "type": "object", "required": ["resource_id", "resource_name", "resource_type", "permissions"], "title": "ResourcePermission"}, "Role": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "scopes": {"items": {"type": "string"}, "type": "array", "uniqueItems": true, "title": "Scopes"}, "predefined": {"type": "boolean", "title": "Predefined", "default": true}}, "type": "object", "required": ["id", "name", "description", "scopes"], "title": "Role"}, "RuleCreateDto": {"properties": {"ruleName": {"type": "string", "title": "Rulename"}, "sqlQuery": {"type": "object", "title": "Sqlquery"}, "celQuery": {"type": "string", "title": "Celquery"}, "timeframeInSeconds": {"type": "integer", "title": "Timeframeinseconds"}, "timeUnit": {"type": "string", "title": "Timeunit"}, "groupingCriteria": {"items": {}, "type": "array", "title": "Groupingcriteria", "default": []}, "groupDescription": {"type": "string", "title": "Groupdescription"}, "requireApprove": {"type": "boolean", "title": "Requireapprove", "default": false}, "resolveOn": {"type": "string", "title": "Resolveon", "default": "never"}}, "type": "object", "required": ["ruleName", "sqlQuery", "celQuery", "timeframeInSeconds", "timeUnit"], "title": "RuleCreateDto"}, "SMTPSettings": {"properties": {"host": {"type": "string", "title": "Host"}, "port": {"type": "integer", "title": "Port"}, "from_email": {"type": "string", "title": "From Email"}, "username": {"type": "string", "title": "Username"}, "password": {"type": "string", "format": "password", "title": "Password", "writeOnly": true}, "secure": {"type": "boolean", "title": "Secure", "default": true}, "to_email": {"type": "string", "title": "To Email", "default": "keep@example.com"}}, "type": "object", "required": ["host", "port", "from_email"], "title": "SMTPSettings", "example": {"host": "smtp.example.com", "port": 587, "username": "user@example.com", "password": "password", "secure": true, "from_email": "noreply@example.com", "to_email": ""}}, "SearchAlertsRequest": {"properties": {"query": {"$ref": "#/components/schemas/PresetSearchQuery"}, "timeframe": {"type": "integer", "title": "Timeframe"}}, "type": "object", "required": ["query", "timeframe"], "title": "SearchAlertsRequest"}, "TagDto": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name"}}, "type": "object", "required": ["name"], "title": "TagDto"}, "TopologyApplicationDtoIn": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "services": {"items": {"$ref": "#/components/schemas/TopologyServiceDtoIn"}, "type": "array", "title": "Services", "default": []}}, "type": "object", "required": ["name"], "title": "TopologyApplicationDtoIn"}, "TopologyApplicationDtoOut": {"properties": {"id": {"type": "string", "format": "uuid", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "description": {"type": "string", "title": "Description"}, "services": {"items": {"$ref": "#/components/schemas/TopologyApplicationServiceDto"}, "type": "array", "title": "Services", "default": []}}, "type": "object", "required": ["id", "name"], "title": "TopologyApplicationDtoOut"}, "TopologyApplicationServiceDto": {"properties": {"id": {"type": "integer", "title": "Id"}, "name": {"type": "string", "title": "Name"}, "service": {"type": "string", "title": "Service"}}, "type": "object", "required": ["id", "name", "service"], "title": "TopologyApplicationServiceDto"}, "TopologyServiceDependencyDto": {"properties": {"serviceId": {"type": "integer", "title": "Serviceid"}, "serviceName": {"type": "string", "title": "Servicename"}, "protocol": {"type": "string", "title": "Protocol", "default": "unknown"}}, "type": "object", "required": ["serviceId", "serviceName"], "title": "TopologyServiceDependencyDto"}, "TopologyServiceDtoIn": {"properties": {"id": {"type": "integer", "title": "Id"}}, "type": "object", "required": ["id"], "title": "TopologyServiceDtoIn"}, "TopologyServiceDtoOut": {"properties": {"source_provider_id": {"type": "string", "title": "Source Provider Id"}, "repository": {"type": "string", "title": "Repository"}, "tags": {"items": {"type": "string"}, "type": "array", "title": "Tags"}, "service": {"type": "string", "title": "Service"}, "display_name": {"type": "string", "title": "Display Name"}, "environment": {"type": "string", "title": "Environment", "default": "unknown"}, "description": {"type": "string", "title": "Description"}, "team": {"type": "string", "title": "Team"}, "email": {"type": "string", "title": "Email"}, "slack": {"type": "string", "title": "Slack"}, "ip_address": {"type": "string", "title": "Ip Address"}, "mac_address": {"type": "string", "title": "Mac Address"}, "category": {"type": "string", "title": "Category"}, "manufacturer": {"type": "string", "title": "Manufacturer"}, "id": {"type": "integer", "title": "Id"}, "dependencies": {"items": {"$ref": "#/components/schemas/TopologyServiceDependencyDto"}, "type": "array", "title": "Dependencies"}, "application_ids": {"items": {"type": "string", "format": "uuid"}, "type": "array", "title": "Application Ids"}, "updated_at": {"type": "string", "format": "date-time", "title": "Updated At"}}, "type": "object", "required": ["service", "display_name", "id", "dependencies", "application_ids"], "title": "TopologyServiceDtoOut"}, "UnEnrichAlertRequestBody": {"properties": {"enrichments": {"items": {"type": "string"}, "type": "array", "title": "Enrichments"}, "fingerprint": {"type": "string", "title": "Fingerprint"}}, "type": "object", "required": ["enrichments", "fingerprint"], "title": "UnEnrichAlertRequestBody"}, "UpdateUserRequest": {"properties": {"username": {"type": "string", "title": "Username"}, "password": {"type": "string", "title": "Password"}, "role": {"type": "string", "title": "Role"}, "groups": {"items": {"type": "string"}, "type": "array", "title": "Groups"}}, "type": "object", "title": "UpdateUserRequest"}, "User": {"properties": {"email": {"type": "string", "title": "Email"}, "name": {"type": "string", "title": "Name"}, "role": {"type": "string", "title": "Role"}, "picture": {"type": "string", "title": "Picture"}, "created_at": {"type": "string", "title": "Created At"}, "last_login": {"type": "string", "title": "Last Login"}, "ldap": {"type": "boolean", "title": "Ldap", "default": false}, "groups": {"items": {"$ref": "#/components/schemas/Group"}, "type": "array", "title": "Groups", "default": []}}, "type": "object", "required": ["email", "name", "created_at"], "title": "User"}, "ValidationError": {"properties": {"loc": {"items": {"anyOf": [{"type": "string"}, {"type": "integer"}]}, "type": "array", "title": "Location"}, "msg": {"type": "string", "title": "Message"}, "type": {"type": "string", "title": "Error Type"}}, "type": "object", "required": ["loc", "msg", "type"], "title": "ValidationError"}, "WebhookSettings": {"properties": {"webhookApi": {"type": "string", "title": "Webhookapi"}, "apiKey": {"type": "string", "title": "Apikey"}, "modelSchema": {"type": "object", "title": "Modelschema"}}, "type": "object", "required": ["webhookApi", "apiKey", "modelSchema"], "title": "WebhookSettings"}, "WorkflowCreateOrUpdateDTO": {"properties": {"workflow_id": {"type": "string", "title": "Workflow Id"}, "status": {"type": "string", "enum": ["created", "updated"], "title": "Status"}, "revision": {"type": "integer", "title": "Revision", "default": 1}}, "type": "object", "required": ["workflow_id", "status"], "title": "WorkflowCreateOrUpdateDTO"}, "WorkflowDTO": {"properties": {"id": {"type": "string", "title": "Id"}, "name": {"type": "string", "title": "Name", "default": "Workflow file doesn't contain name"}, "description": {"type": "string", "title": "Description", "default": "Workflow file doesn't contain description"}, "created_by": {"type": "string", "title": "Created By"}, "creation_time": {"type": "string", "format": "date-time", "title": "Creation Time"}, "triggers": {"items": {"type": "object"}, "type": "array", "title": "Triggers"}, "interval": {"type": "integer", "title": "Interval"}, "disabled": {"type": "boolean", "title": "Disabled", "default": false}, "last_execution_time": {"type": "string", "format": "date-time", "title": "Last Execution Time"}, "last_execution_status": {"type": "string", "title": "Last Execution Status"}, "providers": {"items": {"$ref": "#/components/schemas/ProviderDTO"}, "type": "array", "title": "Providers"}, "workflow_raw": {"type": "string", "title": "Workflow Raw"}, "revision": {"type": "integer", "title": "Revision", "default": 1}, "last_updated": {"type": "string", "format": "date-time", "title": "Last Updated"}, "invalid": {"type": "boolean", "title": "Invalid", "default": false}, "last_executions": {"items": {"type": "object"}, "type": "array", "title": "Last Executions"}, "last_execution_started": {"type": "string", "format": "date-time", "title": "Last Execution Started"}, "provisioned": {"type": "boolean", "title": "Provisioned", "default": false}, "provisioned_file": {"type": "string", "title": "Provisioned File"}}, "type": "object", "required": ["id", "created_by", "creation_time", "providers", "workflow_raw"], "title": "WorkflowDTO"}, "WorkflowExecutionDTO": {"properties": {"id": {"type": "string", "title": "Id"}, "workflow_id": {"type": "string", "title": "Workflow Id"}, "started": {"type": "string", "format": "date-time", "title": "Started"}, "triggered_by": {"type": "string", "title": "Triggered By"}, "status": {"type": "string", "title": "Status"}, "workflow_name": {"type": "string", "title": "Workflow Name"}, "logs": {"items": {"$ref": "#/components/schemas/WorkflowExecutionLogsDTO"}, "type": "array", "title": "Logs"}, "error": {"type": "string", "title": "Error"}, "execution_time": {"type": "number", "title": "Execution Time"}, "results": {"type": "object", "title": "Results"}}, "type": "object", "required": ["id", "workflow_id", "started", "triggered_by", "status"], "title": "WorkflowExecutionDTO"}, "WorkflowExecutionLogsDTO": {"properties": {"id": {"type": "integer", "title": "Id"}, "timestamp": {"type": "string", "format": "date-time", "title": "Timestamp"}, "message": {"type": "string", "title": "Message"}, "context": {"type": "object", "title": "Context"}}, "type": "object", "required": ["id", "timestamp", "message"], "title": "WorkflowExecutionLogsDTO"}, "WorkflowExecutionsPaginatedResultsDto": {"properties": {"limit": {"type": "integer", "title": "Limit", "default": 25}, "offset": {"type": "integer", "title": "Offset", "default": 0}, "count": {"type": "integer", "title": "Count"}, "items": {"items": {"$ref": "#/components/schemas/WorkflowExecutionDTO"}, "type": "array", "title": "Items"}, "passCount": {"type": "integer", "title": "Passcount", "default": 0}, "avgDuration": {"type": "number", "title": "Avgduration", "default": 0.0}, "workflow": {"$ref": "#/components/schemas/WorkflowDTO"}, "failCount": {"type": "integer", "title": "Failcount", "default": 0}}, "type": "object", "required": ["count", "items"], "title": "WorkflowExecutionsPaginatedResultsDto"}, "WorkflowToAlertExecutionDTO": {"properties": {"workflow_id": {"type": "string", "title": "Workflow Id"}, "workflow_execution_id": {"type": "string", "title": "Workflow Execution Id"}, "alert_fingerprint": {"type": "string", "title": "Alert Fingerprint"}, "workflow_status": {"type": "string", "title": "Workflow Status"}, "workflow_started": {"type": "string", "format": "date-time", "title": "Workflow Started"}}, "type": "object", "required": ["workflow_id", "workflow_execution_id", "alert_fingerprint", "workflow_status", "workflow_started"], "title": "WorkflowToAlertExecutionDTO"}}, "securitySchemes": {"API Key": {"type": "apiKey", "in": "header", "name": "X-API-KEY"}, "HTTPBasic": {"type": "http", "scheme": "basic"}, "OAuth2PasswordBearer": {"type": "oauth2", "flows": {"password": {"scopes": {}, "tokenUrl": "token"}}}}}} \ No newline at end of file diff --git a/keep-ui/app/alerts/alert-pagination.tsx b/keep-ui/app/alerts/alert-pagination.tsx index fe45aec93..659f4405d 100644 --- a/keep-ui/app/alerts/alert-pagination.tsx +++ b/keep-ui/app/alerts/alert-pagination.tsx @@ -5,10 +5,15 @@ import { ChevronLeftIcon, ChevronRightIcon, TableCellsIcon, -} from "@heroicons/react/24/outline"; +} from "@heroicons/react/16/solid"; import { Button, Text } from "@tremor/react"; -import { StylesConfig, SingleValueProps, components, GroupBase } from 'react-select'; -import Select from 'react-select'; +import { + StylesConfig, + SingleValueProps, + components, + GroupBase, +} from "react-select"; +import Select from "react-select"; import { AlertDto } from "./models"; import { Table } from "@tanstack/react-table"; import { useAlerts } from "utils/hooks/useAlerts"; @@ -24,39 +29,46 @@ interface OptionType { label: string; } - const customStyles: StylesConfig> = { - control: (provided, state) => ({ - ...provided, - borderColor: state.isFocused ? 'orange' : provided.borderColor, - '&:hover': { borderColor: 'orange' }, - boxShadow: state.isFocused ? '0 0 0 1px orange' : provided.boxShadow, - }), - singleValue: (provided) => ({ - ...provided, - display: 'flex', - alignItems: 'center', - }), - menu: (provided) => ({ - ...provided, - color: 'orange', - }), - option: (provided, state) => ({ - ...provided, - backgroundColor: state.isSelected ? 'orange' : provided.backgroundColor, - '&:hover': { backgroundColor: state.isSelected ? 'orange' : '#f5f5f5' }, - color: state.isSelected ? 'white' : provided.color, - }), - }; - - const SingleValue = ({ children, ...props }: SingleValueProps>) => ( - - {children} - - - ); +const customStyles: StylesConfig> = { + control: (provided, state) => ({ + ...provided, + borderColor: state.isFocused ? "orange" : "rgb(229 231 235)", + borderRadius: "0.5rem", + "&:hover": { borderColor: "orange" }, + boxShadow: state.isFocused ? "0 0 0 1px orange" : provided.boxShadow, + }), + singleValue: (provided) => ({ + ...provided, + display: "flex", + alignItems: "center", + }), + menu: (provided) => ({ + ...provided, + color: "orange", + }), + option: (provided, state) => ({ + ...provided, + backgroundColor: state.isSelected ? "orange" : provided.backgroundColor, + "&:hover": { backgroundColor: state.isSelected ? "orange" : "#f5f5f5" }, + color: state.isSelected ? "white" : provided.color, + }), +}; +const SingleValue = ({ + children, + ...props +}: SingleValueProps>) => ( + + {children} + + +); -export default function AlertPagination({ presetName, table, isRefreshAllowed }: Props) { +export default function AlertPagination({ + presetName, + table, + isRefreshAllowed, +}: Props) { const { usePresetAlerts } = useAlerts(); const { mutate, isLoading: isValidating } = usePresetAlerts(presetName); @@ -69,50 +81,59 @@ export default function AlertPagination({ presetName, table, isRefreshAllowed }: Showing {pageCount === 0 ? 0 : pageIndex + 1} of {pageCount}
- + table.setPageSize(Number(selectedOption!.value)) + } + options={[ + { value: "10", label: "10" }, + { value: "20", label: "20" }, + { value: "50", label: "50" }, + { value: "100", label: "100" }, + ]} + menuPlacement="top" />
diff --git a/keep-ui/app/globals.css b/keep-ui/app/globals.css index a05e00ebf..2f68f0d9e 100644 --- a/keep-ui/app/globals.css +++ b/keep-ui/app/globals.css @@ -49,3 +49,45 @@ /* original value is ridiculous 15.000, overflowing toasts*/ @apply z-[1000] !important; } + +@keyframes scroll-shadow-left { + 0% { + filter: none + } + 25%, 100% { + filter: drop-shadow(rgba(0, 0, 0, 0.07) -2px 9px 5px) + } +} + +@keyframes scroll-shadow-right { + 0%, 75% { + filter: drop-shadow(rgba(0, 0, 0, 0.07) 2px 9px 5px) + } + 99% { + filter: none + } +} + +.pagination-button { + @apply shadow-tremor-input border-tremor-border bg-tremor-background ; + + &:not(:first-child) { + @apply -ml-px; + } + + &:first-child:not(:last-child) { + @apply rounded-r-none; + } + + &:last-child:not(:first-child) { + @apply rounded-l-none; + } + + &:not(:first-child):not(:last-child) { + @apply rounded-l-none rounded-r-none; + } + + &:not(:disabled):hover { + @apply bg-slate-100; + } +} diff --git a/keep-ui/app/incidents/[id]/incident-activity.tsx b/keep-ui/app/incidents/[id]/incident-activity.tsx index 9cdf91f26..2ab50d2d9 100644 --- a/keep-ui/app/incidents/[id]/incident-activity.tsx +++ b/keep-ui/app/incidents/[id]/incident-activity.tsx @@ -40,12 +40,13 @@ export function IncidentActivityChronoItem({ activity }: { activity: any }) { ". "; return (
- {activity.type === "alert" && ( - - )} + {activity.type === "alert" && + (activity.initiator as AlertDto)?.severity && ( + + )} {title} {subTitle} @@ -214,6 +215,7 @@ export default function IncidentActivity({ ) ); const chronoIcons = activities?.map((activity, index) => { + console.log("activity", activity); if (activity.type === "comment" || activity.type === "newcomment") { const user = users?.find((user) => user.email === activity.initiator); return ( @@ -224,7 +226,7 @@ export default function IncidentActivity({ /> ); } else { - const source = (activity.initiator as AlertDto).source[0]; + const source = (activity.initiator as AlertDto)?.source?.[0]; const imagePath = `/icons/${source}-icon.png`; return ( diff --git a/keep-ui/app/incidents/[id]/incident-alerts.tsx b/keep-ui/app/incidents/[id]/incident-alerts.tsx index 0177cc705..773a6e492 100644 --- a/keep-ui/app/incidents/[id]/incident-alerts.tsx +++ b/keep-ui/app/incidents/[id]/incident-alerts.tsx @@ -6,6 +6,7 @@ import { } from "@tanstack/react-table"; import { Callout, + Card, Table, TableBody, TableCell, @@ -29,6 +30,7 @@ import IncidentAlertMenu from "./incident-alert-menu"; import IncidentPagination from "../incident-pagination"; import React, { useEffect, useState } from "react"; import { IncidentDto } from "../models"; +import { getCommonPinningStylesAndClassNames } from "@/components/ui/table/utils"; interface Props { incident: IncidentDto; @@ -85,8 +87,12 @@ export default function IncidentAlerts({ incident }: Props) { columnHelper.display({ id: "name", header: "Name", - minSize: 330, - cell: (context) => , + minSize: 100, + cell: (context) => ( +
+ +
+ ), }), columnHelper.accessor("description", { id: "description", @@ -158,7 +164,12 @@ export default function IncidentAlerts({ incident }: Props) { const table = useReactTable({ columns: columns, manualPagination: true, - state: { pagination }, + state: { + pagination, + columnPinning: { + right: ["remove"], + }, + }, rowCount: alerts ? alerts.count : 0, onPaginationChange: setTablePagination, data: alerts?.items ?? [], @@ -166,69 +177,87 @@ export default function IncidentAlerts({ incident }: Props) { }); return ( <> - {!isLoading && (alerts?.items ?? []).length === 0 && ( - - Alerts will show up here as they are correlated into this incident. - - )} - - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header, index) => { - return ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ); - })} - - ))} - - {alerts && alerts?.items?.length > 0 && ( - - {table.getRowModel().rows.map((row, index) => ( - - {row.getVisibleCells().map((cell, index) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - - ))} + + {!isLoading && (alerts?.items ?? []).length === 0 && ( + + Alerts will show up here as they are correlated into this incident. + + )} +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header, index) => { + const { style, className } = + getCommonPinningStylesAndClassNames(header.column); + return ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + + ); + })} ))} - - )} - { - // Skeleton - (isLoading || (alerts?.items ?? []).length === 0) && ( + + {alerts && alerts?.items?.length > 0 && ( - {Array(pagination.pageSize) - .fill("") - .map((index, rowIndex) => ( - - {columns.map((c, cellIndex) => ( - - + {table.getRowModel().rows.map((row, index) => ( + + {row.getVisibleCells().map((cell, index) => { + const { style, className } = + getCommonPinningStylesAndClassNames(cell.column); + return ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} - ))} - - ))} + ); + })} + + ))} - ) - } -
+ )} + { + // Skeleton + (isLoading || (alerts?.items ?? []).length === 0) && ( + + {Array(pagination.pageSize) + .fill("") + .map((index, rowIndex) => ( + + {columns.map((c, cellIndex) => ( + + + + ))} + + ))} + + ) + } + +
diff --git a/keep-ui/app/incidents/[id]/incident-chat.tsx b/keep-ui/app/incidents/[id]/incident-chat.tsx index 31634f2f8..942ba125d 100644 --- a/keep-ui/app/incidents/[id]/incident-chat.tsx +++ b/keep-ui/app/incidents/[id]/incident-chat.tsx @@ -19,6 +19,7 @@ import { useSession } from "next-auth/react"; import { toast } from "react-toastify"; import "@copilotkit/react-ui/styles.css"; import "./incident-chat.css"; +import { Card } from "@tremor/react"; export default function IncidentChat({ incident }: { incident: IncidentDto }) { const router = useRouter(); @@ -109,32 +110,34 @@ export default function IncidentChat({ incident }: { incident: IncidentDto }) { ); return ( -
- +
+ -
+ labels={{ + title: "Incident Assitant", + initial: + "Hi! 👋 Lets work together to resolve this incident! Ask me anything", + placeholder: + "For example: What do you think the root cause of this incident might be?", + }} + /> +
+ ); } diff --git a/keep-ui/app/incidents/[id]/incident-header.tsx b/keep-ui/app/incidents/[id]/incident-header.tsx new file mode 100644 index 000000000..da202ee15 --- /dev/null +++ b/keep-ui/app/incidents/[id]/incident-header.tsx @@ -0,0 +1,172 @@ +import { IncidentDto } from "@/app/incidents/models"; +import { Badge, Button, Icon, Subtitle, Title } from "@tremor/react"; +import { Link } from "@/components/ui"; +import { ArrowRightIcon } from "@heroicons/react/16/solid"; +import { MdBlock, MdDone, MdModeEdit, MdPlayArrow } from "react-icons/md"; +import React, { useState } from "react"; +import { + deleteIncident, + handleConfirmPredictedIncident, +} from "@/app/incidents/incident-candidate-actions"; +import { useRouter } from "next/navigation"; +import { useSession } from "next-auth/react"; +import { useApiUrl } from "@/utils/hooks/useConfig"; +import ManualRunWorkflowModal from "@/app/workflows/manual-run-workflow-modal"; +import CreateOrUpdateIncident from "@/app/incidents/create-or-update-incident"; +import Modal from "@/components/ui/Modal"; +import { KeyedMutator } from "swr"; + +function SeverityBadge({ severity }: { severity: IncidentDto["severity"] }) { + let severityColor; + if (severity === "critical") { + severityColor = "red"; + } else if (severity === "info") { + severityColor = "blue"; + } else if (severity === "warning") { + severityColor = "yellow"; + } + return ( + + {severity} + + ); +} + +export function IncidentHeader({ + incident, + mutate, +}: { + incident: IncidentDto; + mutate: KeyedMutator; +}) { + const router = useRouter(); + const { data: session } = useSession(); + const apiUrl = useApiUrl(); + + const [isFormOpen, setIsFormOpen] = useState(false); + + const [runWorkflowModalIncident, setRunWorkflowModalIncident] = + useState(); + + const handleCloseForm = () => { + setIsFormOpen(false); + }; + + const handleFinishEdit = () => { + setIsFormOpen(false); + mutate(); + }; + const handleRunWorkflow = () => { + setRunWorkflowModalIncident(incident); + mutate(); + }; + + const handleStartEdit = () => { + setIsFormOpen(true); + }; + + return ( + <> +
+ + All Incidents{" "} + {" "} + {incident.is_confirmed ? "⚔️ " : "Possible "}Incident Details + +
+ + <SeverityBadge severity={incident.severity} /> + <span> + {incident.user_generated_name || incident.ai_generated_name} + </span> + + +
+ )} +
+ + + + + setRunWorkflowModalIncident(null)} + /> + + ); +} diff --git a/keep-ui/app/incidents/[id]/incident-info.tsx b/keep-ui/app/incidents/[id]/incident-overview.tsx similarity index 55% rename from keep-ui/app/incidents/[id]/incident-info.tsx rename to keep-ui/app/incidents/[id]/incident-overview.tsx index cc484e06a..db1d95b38 100644 --- a/keep-ui/app/incidents/[id]/incident-info.tsx +++ b/keep-ui/app/incidents/[id]/incident-overview.tsx @@ -1,33 +1,21 @@ -import { Badge, Button, Icon, Title } from "@tremor/react"; import { IncidentDto } from "../models"; -import CreateOrUpdateIncident from "../create-or-update-incident"; import Modal from "@/components/ui/Modal"; import React, { useState } from "react"; -import { MdBlock, MdDone, MdModeEdit, MdPlayArrow } from "react-icons/md"; import { useIncident, useIncidentFutureIncidents, } from "@/utils/hooks/useIncidents"; - -import { - deleteIncident, - handleConfirmPredictedIncident, -} from "../incident-candidate-actions"; -import { useSession } from "next-auth/react"; -import { useRouter } from "next/navigation"; -import { useApiUrl } from "utils/hooks/useConfig"; import { format } from "date-fns"; -import { ArrowUturnLeftIcon } from "@heroicons/react/24/outline"; import { Disclosure } from "@headlessui/react"; import classNames from "classnames"; import { IoChevronDown } from "react-icons/io5"; -import IncidentChangeStatusModal from "@/app/incidents/incident-change-status-modal"; import ChangeSameIncidentInThePast from "@/app/incidents/incident-change-same-in-the-past"; -import { STATUS_ICONS } from "@/app/incidents/statuses"; import remarkRehype from "remark-rehype"; import rehypeRaw from "rehype-raw"; import Markdown from "react-markdown"; -import ManualRunWorkflowModal from "@/app/workflows/manual-run-workflow-modal"; +import { Callout } from "@tremor/react"; +import { Link } from "@/components/ui"; +import { IncidentChangeStatusSelect } from "@/features/change-incident-status"; interface Props { incident: IncidentDto; @@ -91,45 +79,36 @@ function Summary({ ); } -export default function IncidentInformation({ incident }: Props) { - const router = useRouter(); - const { data: session } = useSession(); - const { mutate } = useIncident(incident.id); - const [isFormOpen, setIsFormOpen] = useState(false); - const apiUrl = useApiUrl(); - - const [runWorkflowModalIncident, setRunWorkflowModalIncident] = - useState(); - - const handleCloseForm = () => { - setIsFormOpen(false); - }; +function MergedCallout({ + merged_into_incident_id, +}: { + merged_into_incident_id: string; +}) { + const { data: merged_incident } = useIncident(merged_into_incident_id); - const handleStartEdit = () => { - setIsFormOpen(true); - }; + if (!merged_incident) { + return null; + } - const handleFinishEdit = () => { - setIsFormOpen(false); - mutate(); - }; - const handleRunWorkflow = () => { - setRunWorkflowModalIncident(incident); - mutate(); - }; + return ( + +

+ This incident was merged into{" "} + + {merged_incident?.user_generated_name || + merged_incident?.ai_generated_name} + +

+
+ ); +} - const [changeStatusIncident, setChangeStatusIncident] = - useState(); +export default function IncidentOverview({ incident }: Props) { + const { mutate } = useIncident(incident.id); const [changeSameIncidentInThePast, setChangeSameIncidentInThePast] = useState(); - const handleChangeStatus = (e: React.MouseEvent, incident: IncidentDto) => { - e.preventDefault(); - e.stopPropagation(); - setChangeStatusIncident(incident); - }; - const handleChangeSameIncidentInThePast = ( e: React.MouseEvent, incident: IncidentDto @@ -148,114 +127,25 @@ export default function IncidentInformation({ incident }: Props) { incident.id ); - const severity = incident.severity; - let severityColor; - if (severity === "critical") severityColor = "red"; - else if (severity === "info") severityColor = "blue"; - else if (severity === "warning") severityColor = "yellow"; - return (
-
- - {incident.is_confirmed ? "⚔️ " : "Possible "}Incident - - -
- )} - router.back()} + {incident.merged_into_incident_id && ( + -
-
- - {incident.severity} - - - {incident.user_generated_name || incident.ai_generated_name} - -
+ )} + {/*TODO: use this magic property to treat children like a children of a parent flex container */}

Status

-
handleChangeStatus(e, incident)} - className="capitalize flex-grow-0 inline-flex items-center cursor-pointer" - > - {STATUS_ICONS[incident.status]} {incident.status} -
+ { + mutate(); + }} + />
@@ -360,18 +250,6 @@ export default function IncidentInformation({ incident }: Props) { )}
- - - - {changeSameIncidentInThePast ? ( ) : null} - - setChangeStatusIncident(null)} - /> - setRunWorkflowModalIncident(null)} - />
); } diff --git a/keep-ui/app/incidents/[id]/incident-timeline.tsx b/keep-ui/app/incidents/[id]/incident-timeline.tsx index 94b3b5c5f..d865573a5 100644 --- a/keep-ui/app/incidents/[id]/incident-timeline.tsx +++ b/keep-ui/app/incidents/[id]/incident-timeline.tsx @@ -10,6 +10,7 @@ import Image from "next/image"; import AlertSeverity from "app/alerts/alert-severity"; import { EmptyStateCard } from "@/components/ui/EmptyStateCard"; import { useRouter } from "next/navigation"; +import { Card } from "@tremor/react"; const severityColors = { critical: "bg-red-300", @@ -359,7 +360,7 @@ export default function IncidentTimeline({ ); return ( -
+
{/* Time labels */} @@ -431,6 +432,6 @@ export default function IncidentTimeline({ />
)} -
+
); } diff --git a/keep-ui/app/incidents/[id]/incident-workflow-table.tsx b/keep-ui/app/incidents/[id]/incident-workflow-table.tsx index 8998333b7..f0eaf5641 100644 --- a/keep-ui/app/incidents/[id]/incident-workflow-table.tsx +++ b/keep-ui/app/incidents/[id]/incident-workflow-table.tsx @@ -15,6 +15,7 @@ import { TableRow, Button, Badge, + Card, } from "@tremor/react"; import { ExclamationTriangleIcon } from "@radix-ui/react-icons"; import Skeleton from "react-loading-skeleton"; @@ -186,65 +187,69 @@ export default function IncidentWorkflowTable({ incident }: Props) { return ( <> - {!isLoading && (workflows?.items ?? []).length === 0 && ( - - No workflows have been executed for this incident yet. - - )} - - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => ( - - {flexRender( - header.column.columnDef.header, - header.getContext() - )} - - ))} - - ))} - - {workflows && workflows.items.length > 0 && ( - - {table.getRowModel().rows.map((row) => ( - handleRowClick(row.original)} - > - {row.getVisibleCells().map((cell) => ( - - {flexRender(cell.column.columnDef.cell, cell.getContext())} - + + {!isLoading && (workflows?.items ?? []).length === 0 && ( + + No workflows have been executed for this incident yet. + + )} +
+ + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {flexRender( + header.column.columnDef.header, + header.getContext() + )} + ))} ))} - - )} - {(isLoading || (workflows?.items ?? []).length === 0) && ( - - {Array(pagination.pageSize) - .fill("") - .map((_, index) => ( - - {columns.map((_, cellIndex) => ( - - + + {workflows && workflows.items.length > 0 && ( + + {table.getRowModel().rows.map((row) => ( + handleRowClick(row.original)} + > + {row.getVisibleCells().map((cell) => ( + + {flexRender( + cell.column.columnDef.cell, + cell.getContext() + )} ))} ))} - - )} -
+ + )} + {(isLoading || (workflows?.items ?? []).length === 0) && ( + + {Array(pagination.pageSize) + .fill("") + .map((_, index) => ( + + {columns.map((_, cellIndex) => ( + + + + ))} + + ))} + + )} + +
diff --git a/keep-ui/app/incidents/[id]/incident.tsx b/keep-ui/app/incidents/[id]/incident.tsx index a83360b73..43b657dba 100644 --- a/keep-ui/app/incidents/[id]/incident.tsx +++ b/keep-ui/app/incidents/[id]/incident.tsx @@ -1,7 +1,6 @@ "use client"; -import Loading from "app/loading"; -import { useIncident } from "utils/hooks/useIncidents"; -import IncidentInformation from "./incident-info"; +import { useState } from "react"; +import { FiActivity } from "react-icons/fi"; import { Badge, Card, @@ -12,18 +11,20 @@ import { TabPanels, Title, } from "@tremor/react"; -import IncidentAlerts from "./incident-alerts"; -import IncidentTimeline from "./incident-timeline"; import { CiBellOn, CiChat2, CiViewTimeline } from "react-icons/ci"; import { IoIosGitNetwork } from "react-icons/io"; -import IncidentChat from "./incident-chat"; import { Workflows } from "components/icons"; -import IncidentWorkflowTable from "./incident-workflow-table"; +import { useIncident } from "utils/hooks/useIncidents"; import { TopologyMap } from "@/app/topology/ui/map"; import { TopologySearchProvider } from "@/app/topology/TopologySearchContext"; -import { useState } from "react"; -import { FiActivity } from "react-icons/fi"; +import Loading from "app/loading"; +import IncidentWorkflowTable from "./incident-workflow-table"; +import IncidentOverview from "./incident-overview"; +import IncidentAlerts from "./incident-alerts"; +import IncidentTimeline from "./incident-timeline"; +import IncidentChat from "./incident-chat"; import IncidentActivity from "./incident-activity"; +import { IncidentHeader } from "./incident-header"; interface Props { incidentId: string; @@ -31,80 +32,73 @@ interface Props { // TODO: generate metadata with incident name export default function IncidentView({ incidentId }: Props) { - const { data: incident, isLoading, error } = useIncident(incidentId); + const { data: incident, mutate, isLoading, error } = useIncident(incidentId); const [index, setIndex] = useState(0); if (isLoading || !incident) return ; if (error) return Incident does not exist.; return ( - <> -
- -
- - + + + {/* Compensating for page-container padding, TODO: more robust solution */} + - {/* Compensating for page-container padding, TODO: more robust solution */} - - - Activity - - New - - - Alerts - Timeline - Topology - Workflows - - Chat - - New - - - - - + Overview and Alerts + + Activity + + New + + + Timeline + Topology + Workflows + Chat + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + +
); } diff --git a/keep-ui/app/incidents/create-or-update-incident.tsx b/keep-ui/app/incidents/create-or-update-incident.tsx index dfaf52940..2755b6746 100644 --- a/keep-ui/app/incidents/create-or-update-incident.tsx +++ b/keep-ui/app/incidents/create-or-update-incident.tsx @@ -21,6 +21,7 @@ import { useUsers } from "utils/hooks/useUsers"; const ReactQuill = typeof window === "object" ? require("react-quill") : () => false; import "react-quill/dist/quill.snow.css"; +import "./react-quill-override.css"; interface Props { incidentToEdit: IncidentDto | null; @@ -215,6 +216,7 @@ export default function CreateOrUpdateIncident({ modules={modules} formats={formats} // Add formats placeholder="What happened?" + className="border border-tremor-border rounded-tremor-default shadow-tremor-input" required={false} onValueChange={setIncidentUserSummary} /> diff --git a/keep-ui/app/incidents/incident-candidate-actions.tsx b/keep-ui/app/incidents/incident-candidate-actions.tsx index 89dbb53a1..cb5d536fa 100644 --- a/keep-ui/app/incidents/incident-candidate-actions.tsx +++ b/keep-ui/app/incidents/incident-candidate-actions.tsx @@ -37,22 +37,29 @@ export const deleteIncident = async ({ mutate, session, apiUrl, -}: Props) => { - if (confirm("Are you sure you want to delete this incident?")) { - const response = await fetch(`${apiUrl}/incidents/${incidentId}`, { - method: "DELETE", - headers: { - Authorization: `Bearer ${session?.accessToken}`, - }, - }); + skipConfirmation = false, +}: Props & { + skipConfirmation?: boolean; +}) => { + if ( + !skipConfirmation && + !confirm("Are you sure you want to delete this incident?") + ) { + return; + } + const response = await fetch(`${apiUrl}/incidents/${incidentId}`, { + method: "DELETE", + headers: { + Authorization: `Bearer ${session?.accessToken}`, + }, + }); - if (response.ok) { - await mutate(); - toast.success("Incident deleted successfully"); - return true; - } else { - toast.error("Failed to delete incident, contact us if this persists"); - return false; - } + if (response.ok) { + await mutate(); + toast.success("Incident deleted successfully"); + return true; + } else { + toast.error("Failed to delete incident, contact us if this persists"); + return false; } }; diff --git a/keep-ui/app/incidents/incident-change-status-modal.tsx b/keep-ui/app/incidents/incident-change-status-modal.tsx index b8c205c05..7c3d701f5 100644 --- a/keep-ui/app/incidents/incident-change-status-modal.tsx +++ b/keep-ui/app/incidents/incident-change-status-modal.tsx @@ -11,17 +11,7 @@ import { IncidentDto, Status } from "./models"; import { useApiUrl } from "utils/hooks/useConfig"; import { useSession } from "next-auth/react"; import { toast } from "react-toastify"; -import { - CheckCircleIcon, - ExclamationCircleIcon, - PauseIcon, -} from "@heroicons/react/24/outline"; - -const statusIcons = { - [Status.Firing]: , - [Status.Resolved]: , - [Status.Acknowledged]: , -}; +import { STATUS_ICONS } from "@/app/incidents/statuses"; const customSelectStyles = { control: ( @@ -81,7 +71,7 @@ export default function IncidentChangeStatusModal({ value: status, label: (
- {statusIcons[status]} + {STATUS_ICONS[status]} {status.charAt(0).toUpperCase() + status.slice(1)}
), diff --git a/keep-ui/app/incidents/incident-dropdown-menu.tsx b/keep-ui/app/incidents/incident-dropdown-menu.tsx new file mode 100644 index 000000000..5117168f5 --- /dev/null +++ b/keep-ui/app/incidents/incident-dropdown-menu.tsx @@ -0,0 +1,58 @@ +import { + ChevronDoubleRightIcon, + EllipsisHorizontalIcon, + PencilIcon, + PlayIcon, + TrashIcon, +} from "@heroicons/react/24/outline"; +import { DropdownMenu } from "@/components/ui/DropdownMenu"; +import { IncidentDto } from "./models"; + +interface Props { + incident: IncidentDto; + handleEdit: (incident: IncidentDto) => void; + handleRunWorkflow: (incident: IncidentDto) => void; + handleDelete: (incident: IncidentDto) => void; +} + +export function IncidentDropdownMenu({ + incident, + handleEdit, + handleRunWorkflow, + handleDelete, +}: Props) { + return ( + <> + + { + e.preventDefault(); + e.stopPropagation(); + handleEdit(incident); + }} + /> + { + e.preventDefault(); + e.stopPropagation(); + handleRunWorkflow(incident); + }} + /> + { + e.preventDefault(); + e.stopPropagation(); + handleDelete(incident); + }} + /> + + + ); +} diff --git a/keep-ui/app/incidents/incident-list-error.tsx b/keep-ui/app/incidents/incident-list-error.tsx new file mode 100644 index 000000000..2bf10c51a --- /dev/null +++ b/keep-ui/app/incidents/incident-list-error.tsx @@ -0,0 +1,24 @@ +import { Fragment } from "react"; +import { Button, Subtitle, Title } from "@tremor/react"; + +export const IncidentListError = () => { + return ( + +
+
+ Failed to load incidents + + Please try again. If the issue persists, contact us + + +
+
+
+ ); +}; diff --git a/keep-ui/app/incidents/IncidentPlaceholder.tsx b/keep-ui/app/incidents/incident-list-placeholder.tsx similarity index 91% rename from keep-ui/app/incidents/IncidentPlaceholder.tsx rename to keep-ui/app/incidents/incident-list-placeholder.tsx index ff5a4ff8d..3ed86d8f9 100644 --- a/keep-ui/app/incidents/IncidentPlaceholder.tsx +++ b/keep-ui/app/incidents/incident-list-placeholder.tsx @@ -1,16 +1,11 @@ import { Fragment } from "react"; import { Button, Subtitle, Title } from "@tremor/react"; - interface Props { setIsFormOpen: (value: boolean) => void; } - -export const IncidentPlaceholder = ({ - setIsFormOpen, -}: Props) => { - +export const IncidentListPlaceholder = ({ setIsFormOpen }: Props) => { const onCreateButtonClick = () => { setIsFormOpen(true); }; @@ -32,7 +27,6 @@ export const IncidentPlaceholder = ({ Create Incident
- ); }; diff --git a/keep-ui/app/incidents/incident.tsx b/keep-ui/app/incidents/incident-list.tsx similarity index 71% rename from keep-ui/app/incidents/incident.tsx rename to keep-ui/app/incidents/incident-list.tsx index 4fc8efaa2..783e2e517 100644 --- a/keep-ui/app/incidents/incident.tsx +++ b/keep-ui/app/incidents/incident-list.tsx @@ -1,18 +1,19 @@ "use client"; import { Card, Title, Subtitle, Button, Badge } from "@tremor/react"; import Loading from "app/loading"; -import { useState } from "react"; +import React, { useState } from "react"; import { IncidentDto } from "./models"; import CreateOrUpdateIncident from "./create-or-update-incident"; import IncidentsTable from "./incidents-table"; import { useIncidents, usePollIncidents } from "utils/hooks/useIncidents"; -import { IncidentPlaceholder } from "./IncidentPlaceholder"; +import { IncidentListPlaceholder } from "./incident-list-placeholder"; import Modal from "@/components/ui/Modal"; import { PlusCircleIcon } from "@heroicons/react/24/outline"; import PredictedIncidentsTable from "./predicted-incidents-table"; import { SortingState } from "@tanstack/react-table"; import { IncidentTableFilters } from "./incident-table-filters"; import { useIncidentFilterContext } from "./incident-table-filters-context"; +import { IncidentListError } from "@/app/incidents/incident-list-error"; interface Pagination { limit: number; @@ -27,7 +28,7 @@ interface Filters { affected_services: string[]; } -export default function Incident() { +export default function IncidentList() { const [incidentsPagination, setIncidentsPagination] = useState({ limit: 20, offset: 0, @@ -37,8 +38,14 @@ export default function Incident() { { id: "creation_time", desc: true }, ]); - const { statuses, severities, assignees, services, sources } = - useIncidentFilterContext(); + const { + statuses, + severities, + assignees, + services, + sources, + areFiltersApplied, + } = useIncidentFilterContext(); const filters: Filters = { status: statuses, @@ -52,6 +59,7 @@ export default function Incident() { data: incidents, isLoading, mutate: mutateIncidents, + error: incidentsError, } = useIncidents( true, incidentsPagination.limit, @@ -74,6 +82,7 @@ export default function Incident() { const handleCloseForm = () => { setIsFormOpen(false); + setIncidentToEdit(null); }; const handleStartEdit = (incident: IncidentDto) => { @@ -86,6 +95,50 @@ export default function Incident() { setIsFormOpen(false); }; + console.log({ + incidents, + filters, + }); + + function renderIncidents() { + if (incidentsError) { + return ( + + + + ); + } + + if (isLoading) { + // TODO: only show this on the initial load + return ( + + + + ); + } + + if (incidents && (incidents.items.length > 0 || areFiltersApplied)) { + return ( + + ); + } + + // This is shown on the cold page load. FIXME + return ( + + + + ); + } + return (
@@ -127,23 +180,9 @@ export default function Incident() {
+ {/* Filters are placed here so the table could be in loading/not-found state without affecting the controls */} - - {isLoading ? ( - - ) : incidents && incidents.items.length > 0 ? ( - - ) : ( - - )} - + {renderIncidents()}
+
{STATUS_ICONS[incident.status]}
+
+
{incident.user_generated_name}
+
+ + ); +} + +interface Props { + incidents: IncidentDto[]; + mutate: () => void; + handleClose: () => void; + onSuccess?: () => void; +} + +interface OptionType { + value: string; + label: JSX.Element; +} + +// TODO: unify all selects into components/ui/Select.tsx +const customSelectStyles: StylesConfig< + OptionType, + false, + GroupBase +> = { + control: (provided, state) => ({ + ...provided, + borderColor: state.isFocused ? "orange" : "rgb(229 231 235)", + borderRadius: "0.5rem", + "&:hover": { borderColor: "orange" }, + boxShadow: state.isFocused ? "0 0 0 1px orange" : provided.boxShadow, + }), + singleValue: (provided) => ({ + ...provided, + display: "flex", + alignItems: "center", + }), + menu: (provided) => ({ + ...provided, + color: "orange", + }), + option: (provided, state) => ({ + ...provided, + backgroundColor: state.isSelected ? "orange" : provided.backgroundColor, + "&:hover": { backgroundColor: state.isSelected ? "orange" : "#f5f5f5" }, + color: state.isSelected ? "white" : "black", + }), +}; + +export default function IncidentMergeModal({ + incidents, + mutate, + handleClose, + onSuccess, +}: Props) { + const { data: session } = useSession(); + const apiUrl = useApiUrl(); + + const [destinationIncidentId, setDestinationIncidentId] = useState( + incidents[0].id + ); + const destinationIncident = incidents.find( + (incident) => incident.id === destinationIncidentId + ); + const sourceIncidents = incidents.filter( + (incident) => incident.id !== destinationIncidentId + ); + + const incidentOptions = useMemo(() => { + return incidents.map((incident) => ({ + value: incident.id, + label: , + })); + }, [incidents]); + + const selectValue = useMemo(() => { + return { + value: destinationIncidentId, + label: , + }; + }, [destinationIncidentId, destinationIncident]); + + const errors = useMemo(() => { + const errorDict: Record = {}; + if (sourceIncidents.every((i) => i.status === Status.Merged)) { + errorDict["alreadyMerged"] = true; + } + return errorDict; + }, [sourceIncidents]); + + const handleMerge = async () => { + if (!sourceIncidents.length || !destinationIncident) { + toast.error("Please select incidents to merge."); + return; + } + + try { + const response = await fetch(`${apiUrl}/incidents/merge`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Authorization: `Bearer ${session?.accessToken}`, + }, + body: JSON.stringify({ + source_incident_ids: sourceIncidents.map((incident) => incident.id), + destination_incident_id: destinationIncident.id, + }), + }); + + if (response.ok) { + toast.success("Incidents merged successfully!"); + onSuccess?.(); + mutate(); + handleClose(); + } else { + toast.error("Failed to merge incidents."); + } + } catch (error) { + toast.error("An error occurred while merging incidents."); + } + }; + + return ( + +
+
+ Merge Incidents + + Alerts from the following incidents will be moved into the + destination incident and the source incidents would be marked as{" "} + Merged + +
+
+
+ Source Incidents + {errors.alreadyMerged && ( +

+ These incidents were already merged +

+ )} +
+
+ {sourceIncidents.map((incident) => ( + + ))} +
+
+
+
+ Destination Incident +
+ + ); +} diff --git a/keep-ui/package-lock.json b/keep-ui/package-lock.json index ab6a71033..51d1bda23 100644 --- a/keep-ui/package-lock.json +++ b/keep-ui/package-lock.json @@ -25,7 +25,7 @@ "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.14", - "@heroicons/react": "^2.0.18", + "@heroicons/react": "^2.1.5", "@mui/material": "^5.15.18", "@radix-ui/react-icons": "^1.3.0", "@svgr/webpack": "^8.0.1", @@ -4007,9 +4007,9 @@ } }, "node_modules/@heroicons/react": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.3.tgz", - "integrity": "sha512-fEcPfo4oN345SoqdlCDdSa4ivjaKbk0jTd+oubcgNxnNgAfzysfwWfQUr+51wigiWHQQRiZNd1Ao0M5Y3M2EGg==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.5.tgz", + "integrity": "sha512-FuzFN+BsHa+7OxbvAERtgBTNeZpUjgM/MIizfVkSCL2/edriN0Hx/DWRCR//aPYwO5QX/YlgLGXk+E3PcfZwjA==", "peerDependencies": { "react": ">= 16" } diff --git a/keep-ui/package.json b/keep-ui/package.json index 7efcbc8e8..089b83050 100644 --- a/keep-ui/package.json +++ b/keep-ui/package.json @@ -26,7 +26,7 @@ "@fortawesome/free-solid-svg-icons": "^6.4.0", "@fortawesome/react-fontawesome": "^0.2.0", "@headlessui/react": "^1.7.14", - "@heroicons/react": "^2.0.18", + "@heroicons/react": "^2.1.5", "@mui/material": "^5.15.18", "@radix-ui/react-icons": "^1.3.0", "@svgr/webpack": "^8.0.1", diff --git a/keep-ui/tailwind.config.js b/keep-ui/tailwind.config.js index 595f66d20..3e6a5b5f5 100644 --- a/keep-ui/tailwind.config.js +++ b/keep-ui/tailwind.config.js @@ -3,6 +3,8 @@ module.exports = { content: [ "./app/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", + "./entities/**/*.{js,ts,jsx,tsx}", + "./features/**/*.{js,ts,jsx,tsx}", "./node_modules/@tremor/**/*.{js,ts,jsx,tsx}", ], darkMode: "class", @@ -102,6 +104,12 @@ module.exports = { "tremor-title": ["1.125rem", { lineHeight: "1.75rem" }], "tremor-metric": ["1.875rem", { lineHeight: "2.25rem" }], }, + animation: { + "scroll-shadow-left": + "auto linear 0s 1 normal none running scroll-shadow-left", + "scroll-shadow-right": + "auto linear 0s 1 normal none running scroll-shadow-right", + }, }, }, safelist: [ diff --git a/keep-ui/tsconfig.json b/keep-ui/tsconfig.json index 2f8f41709..6d7822423 100644 --- a/keep-ui/tsconfig.json +++ b/keep-ui/tsconfig.json @@ -26,6 +26,8 @@ "@/app/*": ["./app/*"], "@/pages/*": ["./pages/*"], "@/utils/*": ["./utils/*"], + "@/entities/*": ["./entities/*"], + "@/features/*": ["./features/*"] } }, "include": [ diff --git a/keep-ui/utils/hooks/useIncidents.ts b/keep-ui/utils/hooks/useIncidents.ts index b3ba58a26..4c4e8f25d 100644 --- a/keep-ui/utils/hooks/useIncidents.ts +++ b/keep-ui/utils/hooks/useIncidents.ts @@ -36,7 +36,7 @@ export const useIncidents = ( } ) => { const apiUrl = useApiUrl(); - const { data: session } = useSession(); + const { data: session, status: sessionStatus } = useSession(); const filtersParams = new URLSearchParams(); @@ -50,7 +50,7 @@ export const useIncidents = ( } }); - return useSWR( + const swrValue = useSWR( () => session ? `${apiUrl}/incidents?confirmed=${confirmed}&limit=${limit}&offset=${offset}&sorting=${ @@ -60,6 +60,11 @@ export const useIncidents = ( (url) => fetcher(url, session?.accessToken), options ); + + return { + ...swrValue, + isLoading: swrValue.isLoading || sessionStatus === "loading", + }; }; export const useIncidentAlerts = ( diff --git a/keep/api/core/db.py b/keep/api/core/db.py index 9d6bc7380..503e0e018 100644 --- a/keep/api/core/db.py +++ b/keep/api/core/db.py @@ -3081,6 +3081,8 @@ def add_alerts_to_incident( incident.affected_services = list( set(incident.affected_services if incident.affected_services else []) | set(alerts_data_for_incident["services"]) ) + # If incident has alerts already, use the max severity between existing and new alerts, otherwise use the new alerts max severity + incident.severity = max(incident.severity, alerts_data_for_incident["max_severity"].order) if incident.alerts_count else alerts_data_for_incident["max_severity"].order incident.alerts_count += alerts_data_for_incident["count"] alert_to_incident_entries = [ @@ -3114,7 +3116,6 @@ def add_alerts_to_incident( incident.start_time = started_at incident.last_seen_time = last_seen_at - incident.severity = alerts_data_for_incident["max_severity"].order session.add(incident) session.commit() @@ -3255,6 +3256,7 @@ def remove_alerts_to_incident_by_incident_id( ] incident.alerts_count -= alerts_data_for_incident["count"] + incident.severity = alerts_data_for_incident["max_severity"].order incident.start_time = started_at incident.last_seen_time = last_seen_at @@ -3264,6 +3266,80 @@ def remove_alerts_to_incident_by_incident_id( return deleted +class DestinationIncidentNotFound(Exception): + pass + + +def merge_incidents_to_id( + tenant_id: str, + source_incident_ids: List[UUID], + # Maybe to add optional destionation_incident_dto to merge to + destination_incident_id: UUID, + merged_by: str | None = None, +) -> Tuple[List[UUID], List[UUID], List[UUID]]: + with Session(engine) as session: + destination_incident = session.exec( + select(Incident) + .where( + Incident.tenant_id == tenant_id, Incident.id == destination_incident_id + ) + .options(joinedload(Incident.alerts)) + ).first() + + if not destination_incident: + raise DestinationIncidentNotFound( + f"Destination incident with id {destination_incident_id} not found" + ) + + source_incidents = session.exec( + select(Incident).filter( + Incident.tenant_id == tenant_id, + Incident.id.in_(source_incident_ids), + ) + ).all() + + merged_incident_ids = [] + skipped_incident_ids = [] + failed_incident_ids = [] + for source_incident in source_incidents: + source_incident_alerts_ids = [alert.id for alert in source_incident.alerts] + if not source_incident_alerts_ids: + logger.info(f"Source incident {source_incident.id} doesn't have alerts") + skipped_incident_ids.append(source_incident.id) + continue + source_incident.merged_into_incident_id = destination_incident.id + source_incident.merged_at = datetime.now(tz=timezone.utc) + source_incident.status = IncidentStatus.MERGED.value + source_incident.merged_by = merged_by + try: + remove_alerts_to_incident_by_incident_id( + tenant_id, + source_incident.id, + [alert.id for alert in source_incident.alerts], + ) + except OperationalError as e: + logger.error( + f"Error removing alerts to incident {source_incident.id}: {e}" + ) + try: + add_alerts_to_incident( + tenant_id, + destination_incident, + source_incident_alerts_ids, + session=session, + ) + merged_incident_ids.append(source_incident.id) + except OperationalError as e: + logger.error( + f"Error adding alerts to incident {destination_incident.id} from {source_incident.id}: {e}" + ) + failed_incident_ids.append(source_incident.id) + + session.commit() + session.refresh(destination_incident) + return merged_incident_ids, skipped_incident_ids, failed_incident_ids + + def get_alerts_count( tenant_id: str, ) -> int: diff --git a/keep/api/models/alert.py b/keep/api/models/alert.py index c49a89bfb..c9de2b43f 100644 --- a/keep/api/models/alert.py +++ b/keep/api/models/alert.py @@ -4,7 +4,7 @@ import logging import uuid from enum import Enum -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, TYPE_CHECKING from uuid import UUID import pytz @@ -19,6 +19,9 @@ from sqlalchemy import desc from sqlmodel import col +if TYPE_CHECKING: + from keep.api.models.db.alert import Incident + logger = logging.getLogger(__name__) @@ -108,6 +111,8 @@ class IncidentStatus(Enum): RESOLVED = "resolved" # Incident has been acknowledged but not resolved ACKNOWLEDGED = "acknowledged" + # Incident was merged with another incident + MERGED = "merged" class IncidentSeverity(SeverityBaseInterface): @@ -409,6 +414,10 @@ class IncidentDto(IncidentDtoIn): same_incident_in_the_past_id: UUID | None + merged_into_incident_id: UUID | None + merged_by: str | None + merged_at: datetime.datetime | None + _tenant_id: str = PrivateAttr() def __str__(self) -> str: @@ -455,7 +464,7 @@ def set_default_values(cls, values: Dict[str, Any]) -> Dict[str, Any]: return values @classmethod - def from_db_incident(cls, db_incident): + def from_db_incident(cls, db_incident: "Incident"): severity = ( IncidentSeverity.from_number(db_incident.severity) @@ -483,6 +492,9 @@ def from_db_incident(cls, db_incident): services=db_incident.affected_services or [], rule_fingerprint=db_incident.rule_fingerprint, same_incident_in_the_past_id=db_incident.same_incident_in_the_past_id, + merged_into_incident_id=db_incident.merged_into_incident_id, + merged_by=db_incident.merged_by, + merged_at=db_incident.merged_at, ) # This field is required for getting alerts when required @@ -490,6 +502,19 @@ def from_db_incident(cls, db_incident): return dto +class MergeIncidentsRequestDto(BaseModel): + source_incident_ids: list[UUID] + destination_incident_id: UUID + + +class MergeIncidentsResponseDto(BaseModel): + merged_incident_ids: list[UUID] + skipped_incident_ids: list[UUID] + failed_incident_ids: list[UUID] + destination_incident_id: UUID + message: str + + class DeduplicationRuleDto(BaseModel): id: str | None # UUID name: str diff --git a/keep/api/models/db/alert.py b/keep/api/models/db/alert.py index b46690734..473574e0a 100644 --- a/keep/api/models/db/alert.py +++ b/keep/api/models/db/alert.py @@ -126,7 +126,7 @@ class Incident(SQLModel, table=True): rule_id: UUID | None = Field( sa_column=Column( UUIDType(binary=False), - ForeignKey("rule.id", use_alter=False, ondelete="CASCADE"), + ForeignKey("rule.id", ondelete="CASCADE"), nullable=True, ), ) @@ -137,7 +137,7 @@ class Incident(SQLModel, table=True): same_incident_in_the_past_id: UUID | None = Field( sa_column=Column( UUIDType(binary=False), - ForeignKey("incident.id", use_alter=False, ondelete="SET NULL"), + ForeignKey("incident.id", ondelete="SET NULL"), nullable=True, ), ) @@ -146,11 +146,38 @@ class Incident(SQLModel, table=True): back_populates="same_incidents_in_the_future", sa_relationship_kwargs=dict( remote_side="Incident.id", + foreign_keys="[Incident.same_incident_in_the_past_id]", ), ) - same_incidents_in_the_future: list["Incident"] = Relationship( + same_incidents_in_the_future: List["Incident"] = Relationship( back_populates="same_incident_in_the_past", + sa_relationship_kwargs=dict( + foreign_keys="[Incident.same_incident_in_the_past_id]", + ), + ) + + merged_into_incident_id: UUID | None = Field( + sa_column=Column( + UUIDType(binary=False), + ForeignKey("incident.id", ondelete="SET NULL"), + nullable=True, + ), + ) + merged_at: datetime | None = Field(default=None) + merged_by: str | None = Field(default=None) + merged_into: Optional["Incident"] = Relationship( + back_populates="merged_incidents", + sa_relationship_kwargs=dict( + remote_side="Incident.id", + foreign_keys="[Incident.merged_into_incident_id]", + ), + ) + merged_incidents: List["Incident"] = Relationship( + back_populates="merged_into", + sa_relationship_kwargs=dict( + foreign_keys="[Incident.merged_into_incident_id]", + ), ) def __init__(self, **kwargs): diff --git a/keep/api/models/db/migrations/versions/2024-10-23-15-21_89b4d3905d26.py b/keep/api/models/db/migrations/versions/2024-10-23-15-21_89b4d3905d26.py new file mode 100644 index 000000000..e34207606 --- /dev/null +++ b/keep/api/models/db/migrations/versions/2024-10-23-15-21_89b4d3905d26.py @@ -0,0 +1,50 @@ +"""Merge Incidents + +Revision ID: 89b4d3905d26 +Revises: 8438f041ee0e +Create Date: 2024-10-21 20:48:40.151171 + +""" + +import sqlalchemy as sa +import sqlalchemy_utils +import sqlmodel +from alembic import op + +# revision identifiers, used by Alembic. +revision = "89b4d3905d26" +down_revision = "8438f041ee0e" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + with op.batch_alter_table("incident", schema=None) as batch_op: + batch_op.add_column( + sa.Column( + "merged_into_incident_id", + sqlalchemy_utils.types.uuid.UUIDType(binary=False), + nullable=True, + ) + ) + batch_op.add_column(sa.Column("merged_at", sa.DateTime(), nullable=True)) + batch_op.add_column( + sa.Column("merged_by", sqlmodel.sql.sqltypes.AutoString(), nullable=True) + ) + batch_op.create_foreign_key( + "fk_incident_merged_into_incident_id", + "incident", + ["merged_into_incident_id"], + ["id"], + ondelete="SET NULL", + ) + + +def downgrade() -> None: + with op.batch_alter_table("incident", schema=None) as batch_op: + batch_op.drop_constraint( + "fk_incident_merged_into_incident_id", type_="foreignkey" + ) + batch_op.drop_column("merged_by") + batch_op.drop_column("merged_at") + batch_op.drop_column("merged_into_incident_id") diff --git a/keep/api/routes/incidents.py b/keep/api/routes/incidents.py index bf2921bd8..ed87d105a 100644 --- a/keep/api/routes/incidents.py +++ b/keep/api/routes/incidents.py @@ -28,6 +28,9 @@ get_workflow_executions_for_incident_or_alert, remove_alerts_to_incident_by_incident_id, update_incident_from_dto_by_id, + get_incidents_meta_for_tenant, + merge_incidents_to_id, + DestinationIncidentNotFound, ) from keep.api.core.dependencies import get_pusher_client from keep.api.core.elastic import ElasticClient @@ -37,10 +40,12 @@ IncidentDto, IncidentDtoIn, IncidentListFilterParamsDto, + MergeIncidentsRequestDto, IncidentSeverity, IncidentSorting, IncidentStatus, IncidentStatusChangeDto, + MergeIncidentsResponseDto, ) from keep.api.models.db.alert import AlertActionType, AlertAudit from keep.api.routes.alerts import _enrich_alert @@ -51,6 +56,7 @@ IncidentsPaginatedResultsDto, WorkflowExecutionsPaginatedResultsDto, ) +from keep.api.utils.pluralize import pluralize from keep.identitymanager.authenticatedentity import AuthenticatedEntity from keep.identitymanager.identitymanagerfactory import IdentityManagerFactory from keep.workflowmanager.workflowmanager import WorkflowManager @@ -348,6 +354,54 @@ def delete_incident( return Response(status_code=202) +@router.post( + "/merge", description="Merge incidents", response_model=MergeIncidentsResponseDto +) +def merge_incidents( + command: MergeIncidentsRequestDto, + authenticated_entity: AuthenticatedEntity = Depends( + IdentityManagerFactory.get_auth_verifier(["write:incident"]) + ), +) -> MergeIncidentsResponseDto: + tenant_id = authenticated_entity.tenant_id + logger.info( + "Merging incidents", + extra={ + "source_incident_ids": command.source_incident_ids, + "destination_incident_id": command.destination_incident_id, + "tenant_id": tenant_id, + }, + ) + + try: + merged_ids, skipped_ids, failed_ids = merge_incidents_to_id( + tenant_id, + command.source_incident_ids, + command.destination_incident_id, + authenticated_entity.email, + ) + + if not merged_ids: + message = "No incidents merged" + else: + message = f"{pluralize(len(merged_ids), 'incident')} merged into {command.destination_incident_id} successfully" + + if skipped_ids: + message += f", {pluralize(len(skipped_ids), 'incident')} were skipped" + if failed_ids: + message += f", {pluralize(len(failed_ids), 'incident')} failed to merge" + + return MergeIncidentsResponseDto( + merged_incident_ids=merged_ids, + skipped_incident_ids=skipped_ids, + failed_incident_ids=failed_ids, + destination_incident_id=command.destination_incident_id, + message=message, + ) + except DestinationIncidentNotFound as e: + raise HTTPException(status_code=400, detail=str(e)) + + @router.get( "/{incident_id}/alerts", description="Get incident alerts by incident incident id", diff --git a/keep/api/utils/pluralize.py b/keep/api/utils/pluralize.py new file mode 100644 index 000000000..7083ca931 --- /dev/null +++ b/keep/api/utils/pluralize.py @@ -0,0 +1,26 @@ +# Maybe to use 'pluralize' from 'inflect' library in the future +def pluralize(count: int, singular: str, plural: str | None = None, include_count: bool = True) -> str: + """ + Returns a string with the correct plural or singular form based on count. + + Args: + count: The number of items + singular: The singular form of the word + plural: The plural form of the word. If None, appends 's' to singular form + include_count: Whether to include the count in the returned string + + Examples: + >>> pluralize(1, "incident") + "1 incident" + >>> pluralize(2, "incident") + "2 incidents" + >>> pluralize(2, "category", "categories") + "2 categories" + >>> pluralize(1, "incident", include_count=False) + "incident" + """ + if plural is None: + plural = singular + 's' + + word = plural if count != 1 else singular + return f"{count} {word}" if include_count else word \ No newline at end of file diff --git a/tests/test_incidents.py b/tests/test_incidents.py index b9758b638..984da9473 100644 --- a/tests/test_incidents.py +++ b/tests/test_incidents.py @@ -13,7 +13,9 @@ get_incident_by_id, get_last_incidents, remove_alerts_to_incident_by_incident_id, - get_incident_alerts_by_incident_id + get_incident_alerts_by_incident_id, + merge_incidents_to_id, + create_alert, ) from keep.api.core.db_utils import get_json_extract_field from keep.api.core.dependencies import SINGLE_TENANT_UUID @@ -22,6 +24,7 @@ AlertStatus, IncidentSeverity, IncidentStatus, + IncidentDto, ) from keep.api.models.db.alert import Alert, AlertToIncident from keep.api.utils.enrichment_helpers import convert_db_alerts_to_dto_alerts @@ -178,30 +181,36 @@ def test_get_last_incidents(db_session, create_alert): status_cycle = cycle([s.value for s in IncidentStatus]) services_cycle = cycle(["keep", None]) - for i in range(50): + for i in range(60): severity = next(severity_cycle) status = next(status_cycle) - incident = create_incident_from_dict(SINGLE_TENANT_UUID, { - "user_generated_name": f"test-{i}", - "user_summary": f"test-{i}", - "is_confirmed": True, - "severity": severity, - "status": status, - }) - create_alert( - f"alert-test-{i}", - AlertStatus(status), - datetime.utcnow(), + service = next(services_cycle) + incident = create_incident_from_dict( + SINGLE_TENANT_UUID, { - "severity": AlertSeverity.from_number(severity), - "service": next(services_cycle), - } - ) - alert = db_session.query(Alert).order_by(Alert.timestamp.desc()).first() - - add_alerts_to_incident_by_incident_id( - SINGLE_TENANT_UUID, incident.id, [alert.id] + "user_generated_name": f"test-{i}", + "user_summary": f"test-{i}", + "is_confirmed": True, + "severity": severity, + "status": status, + }, ) + # Merged incidents don't have alerts + if status != IncidentStatus.MERGED.value: + create_alert( + f"alert-test-{i}", + AlertStatus(status), + datetime.utcnow(), + { + "severity": AlertSeverity.from_number(severity), + "service": service, + }, + ) + alert = db_session.query(Alert).order_by(Alert.timestamp.desc()).first() + + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident.id, [alert.id] + ) incidents_default, incidents_default_count = get_last_incidents(SINGLE_TENANT_UUID) assert len(incidents_default) == 0 @@ -211,7 +220,7 @@ def test_get_last_incidents(db_session, create_alert): SINGLE_TENANT_UUID, is_confirmed=True ) assert len(incidents_confirmed) == 25 - assert incidents_confirmed_count == 50 + assert incidents_confirmed_count == 60 for i in range(25): assert incidents_confirmed[i].user_generated_name == f"test-{i}" @@ -219,7 +228,7 @@ def test_get_last_incidents(db_session, create_alert): SINGLE_TENANT_UUID, is_confirmed=True, limit=5 ) assert len(incidents_limit_5) == 5 - assert incidents_count_limit_5 == 50 + assert incidents_count_limit_5 == 60 for i in range(5): assert incidents_limit_5[i].user_generated_name == f"test-{i}" @@ -228,7 +237,7 @@ def test_get_last_incidents(db_session, create_alert): ) assert len(incidents_limit_5_page_2) == 5 - assert incidents_count_limit_5_page_2 == 50 + assert incidents_count_limit_5_page_2 == 60 for i, j in enumerate(range(5, 10)): assert incidents_limit_5_page_2[i].user_generated_name == f"test-{j}" @@ -241,7 +250,10 @@ def test_get_last_incidents(db_session, create_alert): SINGLE_TENANT_UUID, is_confirmed=True, with_alerts=True ) for i in range(25): - assert len(incidents_with_alerts[i].alerts) == 1 + if incidents_with_alerts[i].status == IncidentStatus.MERGED.value: + assert len(incidents_with_alerts[i].alerts) == 0 + else: + assert len(incidents_with_alerts[i].alerts) == 1 # Test sorting @@ -255,26 +267,40 @@ def test_get_last_incidents(db_session, create_alert): # Test filters filters_1 = {"severity": [1]} - incidents_with_filters_1, _ = get_last_incidents(SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_1, limit=100) - assert len(incidents_with_filters_1) == 10 + incidents_with_filters_1, _ = get_last_incidents( + SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_1, limit=100 + ) + assert len(incidents_with_filters_1) == 12 assert all([i.severity == 1 for i in incidents_with_filters_1]) filters_2 = {"status": ["firing", "acknowledged"]} - incidents_with_filters_2, _ = get_last_incidents(SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_2, limit=100) - assert len(incidents_with_filters_2) == 17 + 16 - assert all([i.status in ["firing", "acknowledged"] for i in incidents_with_filters_2]) + incidents_with_filters_2, _ = get_last_incidents( + SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_2, limit=100 + ) + assert ( + len(incidents_with_filters_2) == 15 + 15 + ) # 15 confirmed, 15 acknowledged because 60 incidents with cycled status + assert all( + [i.status in ["firing", "acknowledged"] for i in incidents_with_filters_2] + ) filters_3 = {"sources": ["keep"]} - incidents_with_filters_3, _ = get_last_incidents(SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_3, limit=100) - assert len(incidents_with_filters_3) == 50 + incidents_with_filters_3, _ = get_last_incidents( + SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_3, limit=100 + ) + assert len(incidents_with_filters_3) == 45 # 60 minus 15 merged with no alerts assert all(["keep" in i.sources for i in incidents_with_filters_3]) filters_4 = {"sources": ["grafana"]} - incidents_with_filters_4, _ = get_last_incidents(SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_4, limit=100) + incidents_with_filters_4, _ = get_last_incidents( + SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_4, limit=100 + ) assert len(incidents_with_filters_4) == 0 filters_5 = {"affected_services": "keep"} - incidents_with_filters_5, _ = get_last_incidents(SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_5, limit=100) - assert len(incidents_with_filters_5) == 25 + incidents_with_filters_5, _ = get_last_incidents( + SINGLE_TENANT_UUID, is_confirmed=True, filters=filters_5, limit=100 + ) + assert len(incidents_with_filters_5) == 30 # half of incidents assert all(["keep" in i.affected_services for i in incidents_with_filters_5]) @@ -460,3 +486,221 @@ def test_add_alerts_with_same_fingerprint_to_incident(db_session, create_alert): assert len(incident.alerts) == 0 +def test_merge_incidents(db_session, create_alert, setup_stress_alerts_no_elastic): + incident_1 = create_incident_from_dict( + SINGLE_TENANT_UUID, + { + "user_generated_name": "Incident with info severity (destination)", + "user_summary": "Incident with info severity (destination)", + }, + ) + create_alert( + "fp1", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.INFO.value}, + ) + create_alert( + f"fp1", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.INFO.value}, + ) + create_alert( + f"fp2", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.INFO.value}, + ) + alerts_1 = db_session.query(Alert).all() + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident_1.id, [a.id for a in alerts_1] + ) + incident_2 = create_incident_from_dict( + SINGLE_TENANT_UUID, + { + "user_generated_name": "Incident with critical severity", + "user_summary": "Incident with critical severity", + }, + ) + create_alert( + "fp20", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.CRITICAL.value}, + ) + create_alert( + f"fp20", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.CRITICAL.value}, + ) + create_alert( + f"fp20", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.CRITICAL.value}, + ) + alerts_2 = db_session.query(Alert).filter(Alert.fingerprint.startswith("fp20")).all() + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident_2.id, [a.id for a in alerts_2] + ) + incident_3 = create_incident_from_dict( + SINGLE_TENANT_UUID, + { + "user_generated_name": "Incident with warning severity", + "user_summary": "Incident with warning severity", + }, + ) + create_alert( + "fp30", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.WARNING.value}, + ) + create_alert( + f"fp30", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.WARNING.value}, + ) + create_alert( + f"fp30", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.INFO.value}, + ) + alerts_3 = db_session.query(Alert).filter(Alert.fingerprint.startswith("fp30")).all() + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident_3.id, [a.id for a in alerts_3] + ) + + # before merge + incident_1 = get_incident_by_id(SINGLE_TENANT_UUID, incident_1.id) + assert incident_1.severity == IncidentSeverity.INFO.order + incident_2 = get_incident_by_id(SINGLE_TENANT_UUID, incident_2.id) + assert incident_2.severity == IncidentSeverity.CRITICAL.order + incident_3 = get_incident_by_id(SINGLE_TENANT_UUID, incident_3.id) + assert incident_3.severity == IncidentSeverity.WARNING.order + + merge_incidents_to_id( + SINGLE_TENANT_UUID, + [incident_2.id, incident_3.id], + incident_1.id, + "test-user-email", + ) + + incident_1 = get_incident_by_id(SINGLE_TENANT_UUID, incident_1.id, with_alerts=True) + assert len(incident_1.alerts) == 9 + assert incident_1.severity == IncidentSeverity.CRITICAL.order + + incident_2 = get_incident_by_id(SINGLE_TENANT_UUID, incident_2.id, with_alerts=True) + assert len(incident_2.alerts) == 0 + assert incident_2.status == IncidentStatus.MERGED.value + assert incident_2.merged_into_incident_id == incident_1.id + assert incident_2.merged_at is not None + assert incident_2.merged_by == "test-user-email" + + incident_3 = get_incident_by_id(SINGLE_TENANT_UUID, incident_3.id, with_alerts=True) + assert len(incident_3.alerts) == 0 + assert incident_3.status == IncidentStatus.MERGED.value + assert incident_3.merged_into_incident_id == incident_1.id + assert incident_3.merged_at is not None + assert incident_3.merged_by == "test-user-email" + + +@pytest.mark.parametrize("test_app", ["NO_AUTH"], indirect=True) +def test_merge_incidents_app( + db_session, client, test_app, setup_stress_alerts_no_elastic, create_alert +): + incident_1 = create_incident_from_dict( + SINGLE_TENANT_UUID, + {"user_generated_name": "Incident with info severity (destination)", "user_summary": "Incident with info severity (destination)"}, + ) + for i in range(50): + create_alert( + f"alert-1-{i}", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.INFO.value}, + ) + alerts_1 = db_session.query(Alert).filter(Alert.fingerprint.startswith("alert-1-")).all() + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident_1.id, [a.id for a in alerts_1] + ) + incident_2 = create_incident_from_dict( + SINGLE_TENANT_UUID, + {"user_generated_name": "Incident with critical severity", "user_summary": "Incident with critical severity"}, + ) + for i in range(50): + create_alert( + f"alert-2-{i}", + AlertStatus.FIRING, + datetime.utcnow(), + {"severity": AlertSeverity.CRITICAL.value, "service": "second-service"}, + ) + alerts_2 = ( + db_session.query(Alert).filter(Alert.fingerprint.startswith("alert-2-")).all() + ) + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident_2.id, [a.id for a in alerts_2] + ) + incident_3 = create_incident_from_dict( + SINGLE_TENANT_UUID, + {"user_generated_name": "test-3", "user_summary": "test-3"}, + ) + alerts_3 = setup_stress_alerts_no_elastic(50) + add_alerts_to_incident_by_incident_id( + SINGLE_TENANT_UUID, incident_3.id, [a.id for a in alerts_3] + ) + empty_incident = create_incident_from_dict( + SINGLE_TENANT_UUID, {"user_generated_name": "test-4", "user_summary": "test-4"} + ) + + incident_1_before_via_api = client.get( + f"/incidents/{incident_1.id}", headers={"x-api-key": "some-key"} + ).json() + assert incident_1_before_via_api["severity"] == IncidentSeverity.INFO.value + assert incident_1_before_via_api["alerts_count"] == 50 + assert "second-service" not in incident_1_before_via_api["services"] + + response = client.post( + "/incidents/merge", + headers={"x-api-key": "some-key"}, + json={ + "source_incident_ids": [ + str(incident_2.id), + str(incident_3.id), + str(empty_incident.id), + ], + "destination_incident_id": str(incident_1.id), + }, + ) + + assert response.status_code == 200 + result = response.json() + assert set(result["merged_incident_ids"]) == {str(incident_2.id), str(incident_3.id)} + assert result["skipped_incident_ids"] == [str(empty_incident.id)] + assert result["failed_incident_ids"] == [] + + incident_1_via_api = client.get( + f"/incidents/{incident_1.id}", headers={"x-api-key": "some-key"} + ).json() + + assert incident_1_via_api["id"] == str(incident_1.id) + assert incident_1_via_api["severity"] == IncidentSeverity.CRITICAL.value + assert incident_1_via_api["alerts_count"] == 150 + assert "second-service" in incident_1_via_api["services"] + + incident_2_via_api = client.get( + f"/incidents/{incident_2.id}", headers={"x-api-key": "some-key"} + ).json() + assert incident_2_via_api["status"] == IncidentStatus.MERGED.value + assert incident_2_via_api["merged_into_incident_id"] == str(incident_1.id) + + incident_3_via_api = client.get( + f"/incidents/{incident_3.id}", + headers={"x-api-key": "some-key"}, + ).json() + assert incident_3_via_api["status"] == IncidentStatus.MERGED.value + assert incident_3_via_api["merged_into_incident_id"] == str(incident_1.id)