relay_server/endpoints/
monitor.rs1use 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 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 match common::handle_envelope(&state, envelope).await {
70 Ok(_) | Err(BadStoreRequest::RateLimited(_)) => (),
71 Err(error) => return Err(error.into()),
72 };
73
74 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}