cx_sdk/client/
alerts.rs

1// Copyright 2024 Coralogix Ltd.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::error::{
16    SdkApiError,
17    SdkError,
18};
19use crate::{
20    CoralogixRegion,
21    auth::AuthContext,
22    error::Result,
23    metadata::CallProperties,
24    util::make_request_with_metadata,
25};
26use crate::{
27    SDK_VERSION,
28    SDK_VERSION_HEADER_NAME,
29};
30use cx_api::proto::com::coralogixapis::alerts::v3::alert_defs_service_client::AlertDefsServiceClient;
31use cx_api::proto::com::coralogixapis::alerts::v3::{
32    CreateAlertDefRequest,
33    CreateAlertDefResponse,
34    DeleteAlertDefRequest,
35    GetAlertDefRequest,
36    GetAlertDefResponse,
37    ListAlertDefsRequest,
38    ListAlertDefsResponse,
39    ReplaceAlertDefRequest,
40    ReplaceAlertDefResponse,
41    SetActiveRequest,
42};
43
44use std::collections::HashMap;
45use std::str::FromStr;
46use tokio::sync::Mutex;
47use tonic::{
48    metadata::MetadataMap,
49    transport::{
50        Channel,
51        ClientTlsConfig,
52        Endpoint,
53    },
54};
55
56pub use cx_api::proto::com::coralogixapis::alerts::v3::{
57    ActivitySchedule,
58    ActivitySchedule as AlertDefActivitySchedule,
59    AlertDef,
60    AlertDefIncidentSettings,
61    AlertDefNotificationGroup,
62    AlertDefOverride,
63    AlertDefPriority,
64    AlertDefProperties,
65    AlertDefType,
66    AlertDefWebhooksSettings,
67    AlertsOp,
68    AutoRetireTimeframe,
69    DayOfWeek as AlertDayOfWeek,
70    DayOfWeek,
71    FlowStages,
72    FlowStagesGroup,
73    FlowStagesGroups,
74    FlowStagesGroupsAlertDefs,
75    FlowType,
76    IntegrationType,
77    LabelFilterType,
78    LabelFilters,
79    LogFilterOperationType,
80    LogSeverity,
81    LogsAnomalyCondition,
82    LogsAnomalyConditionType,
83    LogsAnomalyRule,
84    LogsAnomalyType,
85    LogsFilter,
86    LogsImmediateType,
87    LogsNewValueCondition,
88    LogsNewValueRule,
89    LogsNewValueTimeWindow,
90    LogsNewValueTimeWindowValue,
91    LogsNewValueType,
92    LogsRatioCondition,
93    LogsRatioRules,
94    LogsRatioThresholdType,
95    LogsRatioTimeWindow,
96    LogsSimpleFilter,
97    LogsThresholdCondition,
98    LogsThresholdConditionType,
99    LogsThresholdRule,
100    LogsThresholdType,
101    LogsTimeRelativeCondition,
102    LogsTimeRelativeConditionType,
103    LogsTimeRelativeRule,
104    LogsTimeRelativeThresholdType,
105    LogsTimeWindow,
106    LogsTimeWindowValue,
107    LogsUniqueCountCondition,
108    LogsUniqueCountRule,
109    LogsUniqueCountType,
110    MetricAnomalyCondition,
111    MetricAnomalyRule,
112    MetricAnomalyType,
113    MetricMissingValues,
114    MetricThresholdType,
115    MetricTimeWindow,
116    MetricTimeWindowValue,
117    NextOp,
118    NotificationDestination,
119    NotificationRouter,
120    NotifyOn,
121    Recipients,
122    TimeOfDay,
123    TimeframeType,
124    TracingFilter,
125    TracingFilterOperationType,
126    TracingImmediateType,
127    TracingSimpleFilter,
128    TracingThresholdCondition,
129    TracingThresholdRule,
130    TracingThresholdType,
131    TracingTimeWindow,
132    TracingTimeWindowValue,
133    UndetectedValuesManagement,
134    alert_def_properties::{
135        Schedule,
136        TypeDefinition,
137    },
138    alert_def_webhooks_settings::*,
139    integration_type,
140    logs_filter::FilterType,
141    logs_time_window::Type as LogsTimeWindowType,
142    metric_missing_values::MissingValues,
143};
144
145const ALERTS_FEATURE_GROUP_ID: &str = "alerts";
146
147/// Labels added by default to each Create/Replace request
148pub enum DefaultLabels {
149    /// SDK version under the key `x-cx-sdk-version``
150    SdkVersion,
151    /// Custom labels that are added by key-value
152    Custom(HashMap<String, String>),
153}
154
155/// The Alerts API client.
156/// Read more at [https://coralogix.com/docs/coralogix-user-defined-alerts/]()
157///
158pub struct AlertsClient {
159    metadata_map: MetadataMap,
160    service_client: Mutex<AlertDefsServiceClient<Channel>>,
161    default_labels: HashMap<String, String>,
162}
163
164impl AlertsClient {
165    /// Creates a new client for the Alerts API.
166    /// # Arguments
167    /// * `auth_context` - The API key to use for authentication.
168    /// * `region` - The region to connect to.
169    /// * `default_labels` - Labels to add to each request. Overwrites keys if they already exist.
170    pub fn new(
171        region: CoralogixRegion,
172        auth_context: AuthContext,
173        default_labels: Option<DefaultLabels>,
174    ) -> Result<Self> {
175        let channel: Channel = Endpoint::from_str(&region.grpc_endpoint())?
176            .tls_config(ClientTlsConfig::new().with_native_roots())?
177            .connect_lazy();
178        let request_metadata: CallProperties = (&auth_context.team_level_api_key).into();
179
180        let default_labels: HashMap<String, String> = match default_labels.as_ref() {
181            Some(labels) => match labels {
182                DefaultLabels::SdkVersion => default_label_hashmap(),
183                DefaultLabels::Custom(hash_map) => hash_map.clone(),
184            },
185            None => HashMap::new(),
186        };
187
188        Ok(Self {
189            metadata_map: request_metadata.to_metadata_map(),
190            service_client: Mutex::new(AlertDefsServiceClient::new(channel)),
191            default_labels,
192        })
193    }
194
195    /// Get an alert definition by ID.
196    ///
197    /// # Arguments
198    /// * `alert_id` - The ID of the alert definition to get.
199    pub async fn get(&self, alert_id: String) -> Result<GetAlertDefResponse> {
200        let request = make_request_with_metadata(
201            GetAlertDefRequest { id: Some(alert_id) },
202            &self.metadata_map,
203        );
204        {
205            let mut client = self.service_client.lock().await.clone();
206
207            client
208                .get_alert_def(request)
209                .await
210                .map(|r| r.into_inner())
211                .map_err(|status| {
212                    SdkError::ApiError(SdkApiError {
213                        status,
214                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/GetAlertDef"
215                            .into(),
216                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
217                    })
218                })
219        }
220    }
221
222    /// List all alert definitions.
223    pub async fn list(&self) -> Result<ListAlertDefsResponse> {
224        let request = make_request_with_metadata(ListAlertDefsRequest {}, &self.metadata_map);
225        {
226            let mut client = self.service_client.lock().await.clone();
227
228            client
229                .list_alert_defs(request)
230                .await
231                .map(|r| r.into_inner())
232                .map_err(|status| {
233                    SdkError::ApiError(SdkApiError {
234                        status,
235                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/ListAlertDefs"
236                            .into(),
237                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
238                    })
239                })
240        }
241    }
242
243    /// Create a new alert definition.
244    /// # Arguments
245    ///
246    /// * `alert` - The [`AlertDef`] to create.
247    pub async fn create(&self, alert: AlertDef) -> Result<CreateAlertDefResponse> {
248        let request = make_request_with_metadata(
249            CreateAlertDefRequest {
250                alert_def_properties: alert.alert_def_properties.clone(),
251            },
252            &self.metadata_map,
253        );
254        {
255            let mut client = self.service_client.lock().await.clone();
256            let mut alert = alert;
257            if let Some(alert_def) = &mut alert.alert_def_properties {
258                let _ = self
259                    .default_labels
260                    .clone()
261                    .into_iter()
262                    .map(|(k, v)| alert_def.entity_labels.insert(k, v));
263            }
264            client
265                .create_alert_def(request)
266                .await
267                .map(|r| r.into_inner())
268                .map_err(|status| {
269                    SdkError::ApiError(SdkApiError {
270                        status,
271                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/CreateAlertDef"
272                            .into(),
273                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
274                    })
275                })
276        }
277    }
278
279    /// Replace an existing alert definition.
280    /// # Arguments
281    /// * `alert` - The [`AlertDef`] to replace.
282    pub async fn replace(&self, alert: AlertDef) -> Result<ReplaceAlertDefResponse> {
283        let mut alert = alert;
284        if let Some(alert_def) = &mut alert.alert_def_properties {
285            let _ = self
286                .default_labels
287                .clone()
288                .into_iter()
289                .map(|(k, v)| alert_def.entity_labels.insert(k, v));
290        }
291
292        let request = make_request_with_metadata(
293            ReplaceAlertDefRequest {
294                alert_def_properties: alert.alert_def_properties,
295                id: alert.id,
296            },
297            &self.metadata_map,
298        );
299        {
300            let mut client = self.service_client.lock().await.clone();
301
302            client
303                .replace_alert_def(request)
304                .await
305                .map(|r| r.into_inner())
306                .map_err(|status| {
307                    SdkError::ApiError(SdkApiError {
308                        status,
309                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/ReplaceAlertDef"
310                            .into(),
311                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
312                    })
313                })
314        }
315    }
316
317    /// Delete an alert definition by ID.
318    /// # Arguments
319    /// * `alert_id` - The ID of the alert definition to delete.
320    pub async fn delete(&self, alert_id: String) -> Result<()> {
321        let request = make_request_with_metadata(
322            DeleteAlertDefRequest { id: Some(alert_id) },
323            &self.metadata_map,
324        );
325        {
326            let mut client = self.service_client.lock().await.clone();
327            client
328                .delete_alert_def(request)
329                .await
330                .map(|_| ())
331                .map_err(|status| {
332                    SdkError::ApiError(SdkApiError {
333                        status,
334                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/DeleteAlertDef"
335                            .into(),
336                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
337                    })
338                })
339        }
340    }
341
342    /// Set the active status of an alert definition.
343    /// # Arguments
344    /// * `id` - The ID of the alert definition to set the active status of.
345    /// * `active` - The active status to set.
346    pub async fn set(&self, id: String, active: bool) -> Result<()> {
347        let request = make_request_with_metadata(
348            SetActiveRequest {
349                id: Some(id),
350                active: Some(active),
351            },
352            &self.metadata_map,
353        );
354        {
355            let mut client = self.service_client.lock().await.clone();
356            client
357                .set_active(request)
358                .await
359                .map(|_| ())
360                .map_err(|status| {
361                    SdkError::ApiError(SdkApiError {
362                        status,
363                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/SetActive".into(),
364                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
365                    })
366                })
367        }
368    }
369}
370
371fn default_label_hashmap() -> HashMap<String, String> {
372    let mut hm = HashMap::new();
373    hm.insert(SDK_VERSION_HEADER_NAME.to_string(), SDK_VERSION.to_string());
374    hm
375}