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(ListAlertDefsRequest {}, &self.metadata_map);
237        {
238            let mut client = self.service_client.lock().await.clone();
239
240            client
241                .list_alert_defs(request)
242                .await
243                .map(|r| r.into_inner())
244                .map_err(|status| {
245                    SdkError::ApiError(SdkApiError {
246                        status,
247                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/ListAlertDefs"
248                            .into(),
249                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
250                    })
251                })
252        }
253    }
254
255    /// Create a new alert definition.
256    /// # Arguments
257    ///
258    /// * `alert` - The [`AlertDef`] to create.
259    pub async fn create(&self, alert: AlertDef) -> Result<CreateAlertDefResponse> {
260        let request = make_request_with_metadata(
261            CreateAlertDefRequest {
262                alert_def_properties: alert.alert_def_properties.clone(),
263            },
264            &self.metadata_map,
265        );
266        {
267            let mut client = self.service_client.lock().await.clone();
268            let mut alert = alert;
269            if let Some(alert_def) = &mut alert.alert_def_properties {
270                let _ = self
271                    .default_labels
272                    .clone()
273                    .into_iter()
274                    .map(|(k, v)| alert_def.entity_labels.insert(k, v));
275            }
276            client
277                .create_alert_def(request)
278                .await
279                .map(|r| r.into_inner())
280                .map_err(|status| {
281                    SdkError::ApiError(SdkApiError {
282                        status,
283                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/CreateAlertDef"
284                            .into(),
285                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
286                    })
287                })
288        }
289    }
290
291    /// Replace an existing alert definition.
292    /// # Arguments
293    /// * `alert` - The [`AlertDef`] to replace.
294    pub async fn replace(&self, alert: AlertDef) -> Result<ReplaceAlertDefResponse> {
295        let mut alert = alert;
296        if let Some(alert_def) = &mut alert.alert_def_properties {
297            let _ = self
298                .default_labels
299                .clone()
300                .into_iter()
301                .map(|(k, v)| alert_def.entity_labels.insert(k, v));
302        }
303
304        let request = make_request_with_metadata(
305            ReplaceAlertDefRequest {
306                alert_def_properties: alert.alert_def_properties,
307                id: alert.id,
308            },
309            &self.metadata_map,
310        );
311        {
312            let mut client = self.service_client.lock().await.clone();
313
314            client
315                .replace_alert_def(request)
316                .await
317                .map(|r| r.into_inner())
318                .map_err(|status| {
319                    SdkError::ApiError(SdkApiError {
320                        status,
321                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/ReplaceAlertDef"
322                            .into(),
323                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
324                    })
325                })
326        }
327    }
328
329    /// Delete an alert definition by ID.
330    /// # Arguments
331    /// * `alert_id` - The ID of the alert definition to delete.
332    pub async fn delete(&self, alert_id: String) -> Result<()> {
333        let request = make_request_with_metadata(
334            DeleteAlertDefRequest { id: Some(alert_id) },
335            &self.metadata_map,
336        );
337        {
338            let mut client = self.service_client.lock().await.clone();
339            client
340                .delete_alert_def(request)
341                .await
342                .map(|_| ())
343                .map_err(|status| {
344                    SdkError::ApiError(SdkApiError {
345                        status,
346                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/DeleteAlertDef"
347                            .into(),
348                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
349                    })
350                })
351        }
352    }
353
354    /// Set the active status of an alert definition.
355    /// # Arguments
356    /// * `id` - The ID of the alert definition to set the active status of.
357    /// * `active` - The active status to set.
358    pub async fn set(&self, id: String, active: bool) -> Result<()> {
359        let request = make_request_with_metadata(
360            SetActiveRequest {
361                id: Some(id),
362                active: Some(active),
363            },
364            &self.metadata_map,
365        );
366        {
367            let mut client = self.service_client.lock().await.clone();
368            client
369                .set_active(request)
370                .await
371                .map(|_| ())
372                .map_err(|status| {
373                    SdkError::ApiError(SdkApiError {
374                        status,
375                        endpoint: "/com.coralogixapis.alerts.v3.AlertDefsService/SetActive".into(),
376                        feature_group: ALERTS_FEATURE_GROUP_ID.into(),
377                    })
378                })
379        }
380    }
381}
382
383fn default_label_hashmap() -> HashMap<String, String> {
384    let mut hm = HashMap::new();
385    hm.insert(SDK_VERSION_HEADER_NAME.to_string(), SDK_VERSION.to_string());
386    hm
387}