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    AlertDefStatus,
66    AlertDefStatusFilter,
67    AlertDefType,
68    AlertDefWebhooksSettings,
69    AlertsOp,
70    AutoRetireTimeframe,
71    BurnRateThreshold,
72    BurnRateTypeDual,
73    BurnRateTypeSingle,
74    DayOfWeek as AlertDayOfWeek,
75    DayOfWeek,
76    DurationUnit,
77    ErrorBudgetThreshold,
78    FlowStages,
79    FlowStagesGroup,
80    FlowStagesGroups,
81    FlowStagesGroupsAlertDefs,
82    FlowType,
83    IntegrationType,
84    LabelFilterType,
85    LabelFilters,
86    LogFilterOperationType,
87    LogSeverity,
88    LogsAnomalyCondition,
89    LogsAnomalyConditionType,
90    LogsAnomalyRule,
91    LogsAnomalyType,
92    LogsFilter,
93    LogsImmediateType,
94    LogsNewValueCondition,
95    LogsNewValueRule,
96    LogsNewValueTimeWindow,
97    LogsNewValueTimeWindowValue,
98    LogsNewValueType,
99    LogsRatioCondition,
100    LogsRatioRules,
101    LogsRatioThresholdType,
102    LogsRatioTimeWindow,
103    LogsSimpleFilter,
104    LogsThresholdCondition,
105    LogsThresholdConditionType,
106    LogsThresholdRule,
107    LogsThresholdType,
108    LogsTimeRelativeCondition,
109    LogsTimeRelativeConditionType,
110    LogsTimeRelativeRule,
111    LogsTimeRelativeThresholdType,
112    LogsTimeWindow,
113    LogsTimeWindowValue,
114    LogsUniqueCountCondition,
115    LogsUniqueCountRule,
116    LogsUniqueCountType,
117    MetricAnomalyCondition,
118    MetricAnomalyRule,
119    MetricAnomalyType,
120    MetricMissingValues,
121    MetricThresholdType,
122    MetricTimeWindow,
123    MetricTimeWindowValue,
124    NextOp,
125    NotificationDestination,
126    NotificationRouter,
127    NotifyOn,
128    Recipients,
129    SloDefinition,
130    SloThresholdCondition,
131    SloThresholdRule,
132    SloThresholdType,
133    TimeDuration,
134    TimeOfDay,
135    TimeframeType,
136    TracingFilter,
137    TracingFilterOperationType,
138    TracingImmediateType,
139    TracingSimpleFilter,
140    TracingThresholdCondition,
141    TracingThresholdRule,
142    TracingThresholdType,
143    TracingTimeWindow,
144    TracingTimeWindowValue,
145    UndetectedValuesManagement,
146    alert_def_properties::{
147        Schedule,
148        TypeDefinition,
149    },
150    alert_def_webhooks_settings::*,
151    burn_rate_threshold::Type as BurnRateThresholdType,
152    integration_type,
153    logs_filter::FilterType,
154    logs_time_window::Type as LogsTimeWindowType,
155    metric_missing_values::MissingValues,
156    slo_threshold_type::Threshold,
157};
158
159const ALERTS_FEATURE_GROUP_ID: &str = "alerts";
160
161/// Labels added by default to each Create/Replace request
162pub enum DefaultLabels {
163    /// SDK version under the key `x-cx-sdk-version``
164    SdkVersion,
165    /// Custom labels that are added by key-value
166    Custom(HashMap<String, String>),
167}
168
169/// The Alerts API client.
170/// Read more at [https://coralogix.com/docs/coralogix-user-defined-alerts/]()
171///
172pub struct AlertsClient {
173    metadata_map: MetadataMap,
174    service_client: Mutex<AlertDefsServiceClient<Channel>>,
175    default_labels: HashMap<String, String>,
176}
177
178impl AlertsClient {
179    /// Creates a new client for the Alerts API.
180    /// # Arguments
181    /// * `auth_context` - The API key to use for authentication.
182    /// * `region` - The region to connect to.
183    /// * `default_labels` - Labels to add to each request. Overwrites keys if they already exist.
184    pub fn new(
185        region: CoralogixRegion,
186        auth_context: AuthContext,
187        default_labels: Option<DefaultLabels>,
188    ) -> Result<Self> {
189        let channel: Channel = Endpoint::from_str(&region.grpc_endpoint())?
190            .tls_config(ClientTlsConfig::new().with_native_roots())?
191            .connect_lazy();
192        let request_metadata: CallProperties = (&auth_context.team_level_api_key).into();
193
194        let default_labels: HashMap<String, String> = match default_labels.as_ref() {
195            Some(labels) => match labels {
196                DefaultLabels::SdkVersion => default_label_hashmap(),
197                DefaultLabels::Custom(hash_map) => hash_map.clone(),
198            },
199            None => HashMap::new(),
200        };
201
202        Ok(Self {
203            metadata_map: request_metadata.to_metadata_map(),
204            service_client: Mutex::new(AlertDefsServiceClient::new(channel)),
205            default_labels,
206        })
207    }
208
209    /// Get an alert definition by ID.
210    ///
211    /// # Arguments
212    /// * `alert_id` - The ID of the alert definition to get.
213    pub async fn get(&self, alert_id: String) -> Result<GetAlertDefResponse> {
214        let request = make_request_with_metadata(
215            GetAlertDefRequest { id: Some(alert_id) },
216            &self.metadata_map,
217        );
218        {
219            let mut client = self.service_client.lock().await.clone();
220
221            client
222                .get_alert_def(request)
223                .await
224                .map(|r| r.into_inner())
225                .map_err(|status| {
226                    SdkError::ApiError(SdkApiError {
227                        status,
228                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/GetAlertDef"
229                            .into(),
230                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
231                    })
232                })
233        }
234    }
235
236    /// List all alert definitions.
237    pub async fn list(&self) -> Result<ListAlertDefsResponse> {
238        let request = make_request_with_metadata(
239            ListAlertDefsRequest {
240                pagination: None,
241                query_filter: None,
242                ..Default::default()
243            },
244            &self.metadata_map,
245        );
246        {
247            let mut client = self.service_client.lock().await.clone();
248
249            client
250                .list_alert_defs(request)
251                .await
252                .map(|r| r.into_inner())
253                .map_err(|status| {
254                    SdkError::ApiError(SdkApiError {
255                        status,
256                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/ListAlertDefs"
257                            .into(),
258                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
259                    })
260                })
261        }
262    }
263
264    /// Create a new alert definition.
265    /// # Arguments
266    ///
267    /// * `alert` - The [`AlertDef`] to create.
268    pub async fn create(&self, alert: AlertDef) -> Result<CreateAlertDefResponse> {
269        let request = make_request_with_metadata(
270            CreateAlertDefRequest {
271                alert_def_properties: alert.alert_def_properties.clone(),
272            },
273            &self.metadata_map,
274        );
275        {
276            let mut client = self.service_client.lock().await.clone();
277            let mut alert = alert;
278            if let Some(alert_def) = &mut alert.alert_def_properties {
279                let _ = self
280                    .default_labels
281                    .clone()
282                    .into_iter()
283                    .map(|(k, v)| alert_def.entity_labels.insert(k, v));
284            }
285            client
286                .create_alert_def(request)
287                .await
288                .map(|r| r.into_inner())
289                .map_err(|status| {
290                    SdkError::ApiError(SdkApiError {
291                        status,
292                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/CreateAlertDef"
293                            .into(),
294                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
295                    })
296                })
297        }
298    }
299
300    /// Replace an existing alert definition.
301    /// # Arguments
302    /// * `alert` - The [`AlertDef`] to replace.
303    pub async fn replace(&self, alert: AlertDef) -> Result<ReplaceAlertDefResponse> {
304        let mut alert = alert;
305        if let Some(alert_def) = &mut alert.alert_def_properties {
306            let _ = self
307                .default_labels
308                .clone()
309                .into_iter()
310                .map(|(k, v)| alert_def.entity_labels.insert(k, v));
311        }
312
313        let request = make_request_with_metadata(
314            ReplaceAlertDefRequest {
315                alert_def_properties: alert.alert_def_properties,
316                id: alert.id,
317            },
318            &self.metadata_map,
319        );
320        {
321            let mut client = self.service_client.lock().await.clone();
322
323            client
324                .replace_alert_def(request)
325                .await
326                .map(|r| r.into_inner())
327                .map_err(|status| {
328                    SdkError::ApiError(SdkApiError {
329                        status,
330                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/ReplaceAlertDef"
331                            .into(),
332                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
333                    })
334                })
335        }
336    }
337
338    /// Delete an alert definition by ID.
339    /// # Arguments
340    /// * `alert_id` - The ID of the alert definition to delete.
341    pub async fn delete(&self, alert_id: String) -> Result<()> {
342        let request = make_request_with_metadata(
343            DeleteAlertDefRequest { id: Some(alert_id) },
344            &self.metadata_map,
345        );
346        {
347            let mut client = self.service_client.lock().await.clone();
348            client
349                .delete_alert_def(request)
350                .await
351                .map(|_| ())
352                .map_err(|status| {
353                    SdkError::ApiError(SdkApiError {
354                        status,
355                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/DeleteAlertDef"
356                            .into(),
357                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
358                    })
359                })
360        }
361    }
362
363    /// Set the active status of an alert definition.
364    /// # Arguments
365    /// * `id` - The ID of the alert definition to set the active status of.
366    /// * `active` - The active status to set.
367    pub async fn set(&self, id: String, active: bool) -> Result<()> {
368        let request = make_request_with_metadata(
369            SetActiveRequest {
370                id: Some(id),
371                active: Some(active),
372            },
373            &self.metadata_map,
374        );
375        {
376            let mut client = self.service_client.lock().await.clone();
377            client
378                .set_active(request)
379                .await
380                .map(|_| ())
381                .map_err(|status| {
382                    SdkError::ApiError(SdkApiError {
383                        status,
384                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/SetActive".into(),
385                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
386                    })
387                })
388        }
389    }
390}
391
392fn default_label_hashmap() -> HashMap<String, String> {
393    let mut hm = HashMap::new();
394    hm.insert(SDK_VERSION_HEADER_NAME.to_string(), SDK_VERSION.to_string());
395    hm
396}