1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
use std::{error::Error, fmt::Display};
use serde::{Deserialize, Serialize, Serializer};
use uuid::Uuid;
use crate::crontab_validator;
/// Error type for errors with parsing a crontab schedule
#[derive(Debug)]
pub struct CrontabParseError {
invalid_crontab: String,
}
impl Display for CrontabParseError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"\"{}\" is not a valid crontab schedule.\n\t \
For help determining why this schedule is invalid, you can use this site: \
https://crontab.guru/#{}",
self.invalid_crontab,
self.invalid_crontab
.split_whitespace()
.collect::<Vec<_>>()
.join("_"),
)
}
}
impl Error for CrontabParseError {}
impl CrontabParseError {
/// Constructs a new CrontabParseError from a given invalid crontab string
///
/// ## Example
/// ```
/// use sentry_types::protocol::v7::CrontabParseError;
///
/// let error = CrontabParseError::new("* * * *");
/// ```
pub fn new(invalid_crontab: &str) -> Self {
Self {
invalid_crontab: String::from(invalid_crontab),
}
}
}
/// Represents the status of the monitor check-in
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MonitorCheckInStatus {
/// Check-in had no issues during execution.
Ok,
/// Check-in failed or otherwise had some issues.
Error,
/// Check-in is expectred to complete.
InProgress,
/// Monitor did not check in on time.
Missed,
/// No status was passed.
#[serde(other)]
Unknown,
}
/// Configuration object of the monitor schedule.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
#[serde(tag = "type")]
pub enum MonitorSchedule {
/// A Crontab schedule allows you to use a standard UNIX crontab style schedule string to
/// configure when a monitor check-in will be expected on Sentry.
Crontab {
/// The crontab syntax string defining the schedule.
value: String,
},
/// A Interval schedule allows you to configure a periodic check-in, that will occur at an
/// interval after the most recent check-in.
Interval {
/// The interval value.
value: u64,
/// The interval unit of the value.
unit: MonitorIntervalUnit,
},
}
impl MonitorSchedule {
/// Attempts to create a MonitorSchedule from a provided crontab_str. If the crontab_str is a
/// valid crontab schedule, we return a Result containing the MonitorSchedule; otherwise, we
/// return a Result containing a CrontabParseError.
///
/// ## Example with valid crontab
/// ```
/// use sentry_types::protocol::v7::MonitorSchedule;
///
/// // Create a crontab that runs every other day of the month at midnight.
/// let result = MonitorSchedule::from_crontab("0 0 */2 * *");
/// assert!(result.is_ok())
/// ```
///
/// ## Example with an invalid crontab
/// ```
/// use sentry_types::protocol::v7::MonitorSchedule;
///
/// // Invalid crontab.
/// let result = MonitorSchedule::from_crontab("invalid");
/// assert!(result.is_err());
/// ```
pub fn from_crontab(crontab_str: &str) -> Result<Self, CrontabParseError> {
if crontab_validator::validate(crontab_str) {
Ok(Self::Crontab {
value: String::from(crontab_str),
})
} else {
Err(CrontabParseError::new(crontab_str))
}
}
}
/// The unit for the interval schedule type
#[derive(Clone, Copy, Debug, PartialEq, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum MonitorIntervalUnit {
/// Year Interval.
Year,
/// Month Interval.
Month,
/// Week Interval.
Week,
/// Day Interval.
Day,
/// Hour Interval.
Hour,
/// Minute Interval.
Minute,
}
/// The monitor configuration playload for upserting monitors during check-in
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct MonitorConfig {
/// The monitor schedule configuration.
pub schedule: MonitorSchedule,
/// How long (in minutes) after the expected check-in time will we wait until we consider the
/// check-in to have been missed.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub checkin_margin: Option<u64>,
/// How long (in minutes) is the check-in allowed to run for in
/// [`MonitorCheckInStatus::InProgress`] before it is considered failed.in_rogress
#[serde(default, skip_serializing_if = "Option::is_none")]
pub max_runtime: Option<u64>,
/// tz database style timezone string
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timezone: Option<String>,
/// The number of consecutive failed/error check-ins that triggers issue creation.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub failure_issue_threshold: Option<u64>,
/// The number of consecutive successful check-ins that triggers issue resolution.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recovery_threshold: Option<u64>,
}
fn serialize_id<S: Serializer>(uuid: &Uuid, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_some(&uuid.as_simple())
}
/// The monitor check-in payload.
#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)]
pub struct MonitorCheckIn {
/// Unique identifier of this check-in.
#[serde(serialize_with = "serialize_id")]
pub check_in_id: Uuid,
/// Identifier of the monitor for this check-in.
pub monitor_slug: String,
/// Status of this check-in. Defaults to `"unknown"`.
pub status: MonitorCheckInStatus,
/// The environment to associate the check-in with.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub environment: Option<String>,
/// Duration of this check-in since it has started in seconds.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub duration: Option<f64>,
/// Monitor configuration to support upserts. When provided a monitor will be created on Sentry
/// upon receiving the first check-in.
///
/// If the monitor already exists the configuration will be updated with the values provided in
/// this object.
#[serde(default, skip_serializing_if = "Option::is_none")]
pub monitor_config: Option<MonitorConfig>,
}