autoendpoint/routers/
mod.rs1use crate::error::ApiResult;
4use crate::extractors::notification::Notification;
5use crate::extractors::router_data_input::RouterDataInput;
6use crate::routers::apns::error::ApnsError;
7use crate::routers::fcm::error::FcmError;
8
9use autopush_common::db::error::DbError;
10
11use actix_web::http::StatusCode;
12use actix_web::HttpResponse;
13use async_trait::async_trait;
14use autopush_common::errors::ReportableError;
15use std::collections::HashMap;
16use thiserror::Error;
17
18#[cfg(feature = "stub")]
19use self::stub::error::StubError;
20pub mod apns;
21mod common;
22pub mod fcm;
23#[cfg(feature = "stub")]
24pub mod stub;
25pub mod webpush;
26
27#[async_trait(?Send)]
28pub trait Router {
29 fn register(
32 &self,
33 router_input: &RouterDataInput,
34 app_id: &str,
35 ) -> Result<HashMap<String, serde_json::Value>, RouterError>;
36
37 async fn route_notification(&self, notification: Notification) -> ApiResult<RouterResponse>;
39}
40
41#[derive(Debug, Eq, PartialEq)]
43pub struct RouterResponse {
44 pub status: StatusCode,
45 pub headers: HashMap<&'static str, String>,
46 pub body: Option<String>,
47}
48
49impl RouterResponse {
50 pub fn success(location: String, ttl: usize) -> Self {
53 RouterResponse {
54 status: StatusCode::CREATED,
55 headers: {
56 let mut map = HashMap::new();
57 map.insert("Location", location);
58 map.insert("TTL", ttl.to_string());
59 map
60 },
61 body: None,
62 }
63 }
64}
65
66impl From<RouterResponse> for HttpResponse {
67 fn from(router_response: RouterResponse) -> Self {
68 let mut builder = HttpResponse::build(router_response.status);
69
70 for (key, value) in router_response.headers {
71 builder.insert_header((key, value));
72 }
73
74 builder.body(router_response.body.unwrap_or_default())
75 }
76}
77
78#[derive(Debug, Error)]
79pub enum RouterError {
80 #[error(transparent)]
81 Apns(#[from] ApnsError),
82
83 #[error(transparent)]
84 Fcm(#[from] FcmError),
85
86 #[cfg(feature = "stub")]
87 #[error(transparent)]
88 Stub(#[from] StubError),
89
90 #[error("Database error while saving notification")]
91 SaveDb(#[source] DbError, Option<String>),
92
93 #[error("User was deleted during routing")]
94 UserWasDeleted,
95
96 #[error(
97 "This message is intended for a constrained device and is limited in \
98 size. Converted buffer is too long by {0} bytes"
99 )]
100 TooMuchData(usize),
101
102 #[error("Bridge authentication error")]
103 Authentication,
104
105 #[error("Bridge request timeout")]
106 RequestTimeout,
107
108 #[error("Error while connecting to bridge service")]
109 Connect(#[source] reqwest::Error),
110
111 #[error("Bridge reports user was not found")]
112 NotFound,
113}
114
115impl RouterError {
116 pub fn status(&self) -> StatusCode {
118 match self {
119 RouterError::Apns(e) => e.status(),
120 RouterError::Fcm(e) => StatusCode::from_u16(e.status().as_u16()).unwrap_or_default(),
121
122 RouterError::SaveDb(e, _) => e.status(),
123 #[cfg(feature = "stub")]
124 RouterError::Stub(e) => e.status(),
125
126 RouterError::UserWasDeleted | RouterError::NotFound => StatusCode::GONE,
127
128 RouterError::TooMuchData(_) => StatusCode::PAYLOAD_TOO_LARGE,
129
130 RouterError::Authentication | RouterError::RequestTimeout | RouterError::Connect(_) => {
131 StatusCode::BAD_GATEWAY
132 }
133 }
134 }
135
136 pub fn errno(&self) -> Option<usize> {
138 match self {
139 RouterError::Apns(e) => e.errno(),
140 RouterError::Fcm(e) => e.errno(),
141
142 #[cfg(feature = "stub")]
143 RouterError::Stub(e) => e.errno(),
144
145 RouterError::TooMuchData(_) => Some(104),
146
147 RouterError::UserWasDeleted => Some(105),
148
149 RouterError::NotFound => Some(106),
150
151 RouterError::SaveDb(_, _) => Some(201),
152
153 RouterError::Authentication => Some(901),
154
155 RouterError::Connect(_) => Some(902),
156
157 RouterError::RequestTimeout => Some(903),
158 }
159 }
160}
161
162impl ReportableError for RouterError {
163 fn reportable_source(&self) -> Option<&(dyn ReportableError + 'static)> {
164 match &self {
165 RouterError::Apns(e) => Some(e),
166 RouterError::Fcm(e) => Some(e),
167 RouterError::SaveDb(e, _) => Some(e),
168 _ => None,
169 }
170 }
171
172 fn is_sentry_event(&self) -> bool {
173 match self {
174 RouterError::Apns(e) => e.is_sentry_event(),
176 RouterError::Fcm(e) => e.is_sentry_event(),
177 RouterError::Authentication
179 | RouterError::Connect(_)
180 | RouterError::NotFound
181 | RouterError::RequestTimeout
182 | RouterError::TooMuchData(_) => false,
183 RouterError::SaveDb(e, _) => e.is_sentry_event(),
184 _ => true,
185 }
186 }
187
188 fn metric_label(&self) -> Option<&'static str> {
189 match self {
193 RouterError::Apns(e) => e.metric_label(),
194 RouterError::Fcm(e) => e.metric_label(),
195 RouterError::TooMuchData(_) => Some("notification.bridge.error.too_much_data"),
196 _ => None,
197 }
198 }
199
200 fn extras(&self) -> Vec<(&str, String)> {
201 match &self {
202 RouterError::Apns(e) => e.extras(),
203 RouterError::Fcm(e) => e.extras(),
204 RouterError::SaveDb(e, sub) => {
205 let mut extras = e.extras();
206 if let Some(sub) = sub {
207 extras.append(&mut vec![("sub", sub.clone())]);
208 };
209 extras
210 }
211 _ => vec![],
212 }
213 }
214}