relay_server/endpoints/
monitor.rs

1use crate::constants::DEFAULT_CHECK_IN_CLIENT;
2use axum::extract::{DefaultBodyLimit, Path, Query, Request};
3use axum::http::StatusCode;
4use axum::response::IntoResponse;
5use axum::routing::{MethodFilter, MethodRouter, on};
6use axum::{Json, RequestExt};
7use relay_config::Config;
8use relay_event_schema::protocol::EventId;
9use relay_monitors::{CheckIn, CheckInStatus};
10use serde::Deserialize;
11use uuid::Uuid;
12
13use crate::endpoints::common::{self, BadStoreRequest};
14use crate::envelope::{ContentType, Envelope, Item, ItemType};
15use crate::extractors::{RawContentType, RequestMeta};
16use crate::service::ServiceState;
17
18#[derive(Debug, Deserialize)]
19struct MonitorPath {
20    monitor_slug: String,
21}
22
23#[derive(Debug, Deserialize)]
24struct MonitorQuery {
25    status: CheckInStatus,
26    check_in_id: Option<Uuid>,
27    environment: Option<String>,
28    duration: Option<f64>,
29}
30
31async fn handle(
32    state: ServiceState,
33    content_type: RawContentType,
34    mut meta: RequestMeta,
35    Path(path): Path<MonitorPath>,
36    request: Request,
37) -> axum::response::Result<impl IntoResponse> {
38    let check_in = if content_type.as_ref().starts_with("application/json") {
39        let Json(mut check_in): Json<CheckIn> = request.extract().await?;
40        check_in.monitor_slug = path.monitor_slug;
41        check_in
42    } else {
43        let Query(query): Query<MonitorQuery> = request.extract().await?;
44        CheckIn {
45            check_in_id: query.check_in_id.map(EventId).unwrap_or_else(EventId::nil),
46            monitor_slug: path.monitor_slug,
47            status: query.status,
48            environment: query.environment,
49            duration: query.duration,
50            monitor_config: None,
51            contexts: None,
52        }
53    };
54
55    let json = serde_json::to_vec(&check_in).map_err(BadStoreRequest::InvalidJson)?;
56
57    // In case the `client` was not specified in the `RequestMeta` we mark the client as the Relay
58    // HTTP endpoint since we don't know from which client the request came from.
59    if meta.client().is_none() {
60        meta.set_client(DEFAULT_CHECK_IN_CLIENT.to_owned());
61    }
62
63    let mut envelope = Envelope::from_request(Some(EventId::new()), meta);
64    let mut item = Item::new(ItemType::CheckIn);
65    item.set_payload(ContentType::Json, json);
66    envelope.add_item(item);
67
68    // Never respond with a 429
69    match common::handle_envelope(&state, envelope).await {
70        Ok(_) | Err(BadStoreRequest::RateLimited(_)) => (),
71        Err(error) => return Err(error.into()),
72    };
73
74    // Event will be processed by Sentry, respond with a 202
75    Ok(StatusCode::ACCEPTED)
76}
77
78pub fn route(config: &Config) -> MethodRouter<ServiceState> {
79    on(MethodFilter::GET.or(MethodFilter::POST), handle)
80        .route_layer(DefaultBodyLimit::max(config.max_event_size()))
81}