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