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
use crate::error::ApiErrorKind;
use crate::routers::RouterError;
use actix_web::http::StatusCode;
use autopush_common::errors::ReportableError;
use std::io;

/// Errors that may occur in the Apple Push Notification Service router
#[derive(thiserror::Error, Debug)]
pub enum ApnsError {
    #[error("Failed to decode the channel settings")]
    ChannelSettingsDecode(#[from] serde_json::Error),

    #[error("IO Error: {0}")]
    Io(#[from] io::Error),

    #[error("Error while setting up APNS clients: {0}")]
    ApnsClient(#[source] a2::Error),

    #[error("Error while checking the message size limit: {0}")]
    SizeLimit(#[source] a2::Error),

    #[error("APNS error, {0}")]
    ApnsUpstream(#[source] a2::Error),

    /// Configuration error {Type of error}, {Error string}
    #[error("APNS config, {0}:{1}")]
    Config(String, String),

    #[error("No device token found for user")]
    NoDeviceToken,

    #[error("No release channel found for user")]
    NoReleaseChannel,

    #[error("Release channel is invalid")]
    InvalidReleaseChannel,

    #[error("Invalid APS data")]
    InvalidApsData,

    #[error("APNS recipient no longer available")]
    Unregistered,
}

impl ApnsError {
    /// Get the associated HTTP status code
    pub fn status(&self) -> StatusCode {
        match self {
            ApnsError::InvalidReleaseChannel
            | ApnsError::InvalidApsData
            | ApnsError::SizeLimit(_) => StatusCode::BAD_REQUEST,

            ApnsError::NoDeviceToken | ApnsError::NoReleaseChannel | ApnsError::Unregistered => {
                StatusCode::GONE
            }

            ApnsError::ChannelSettingsDecode(_)
            | ApnsError::Io(_)
            | ApnsError::ApnsClient(_)
            | ApnsError::Config(..) => StatusCode::INTERNAL_SERVER_ERROR,

            ApnsError::ApnsUpstream(_) => StatusCode::BAD_GATEWAY,
        }
    }

    /// Get the associated error number
    pub fn errno(&self) -> Option<usize> {
        match self {
            ApnsError::NoDeviceToken | ApnsError::NoReleaseChannel | ApnsError::Unregistered => {
                Some(106)
            }

            ApnsError::ChannelSettingsDecode(_)
            | ApnsError::Io(_)
            | ApnsError::ApnsClient(_)
            | ApnsError::ApnsUpstream(_)
            | ApnsError::InvalidReleaseChannel
            | ApnsError::InvalidApsData
            | ApnsError::Config(..)
            | ApnsError::SizeLimit(_) => None,
        }
    }
}

impl From<ApnsError> for ApiErrorKind {
    fn from(e: ApnsError) -> Self {
        ApiErrorKind::Router(RouterError::Apns(e))
    }
}

impl ReportableError for ApnsError {
    fn is_sentry_event(&self) -> bool {
        !matches!(self, ApnsError::SizeLimit(_) | ApnsError::Unregistered)
    }

    fn metric_label(&self) -> Option<&'static str> {
        match &self {
            ApnsError::SizeLimit(_) => Some("notification.bridge.error.apns.oversized"),
            ApnsError::ApnsUpstream(_) => Some("notification.bridge.error.apns.upstream"),
            _ => None,
        }
    }

    fn extras(&self) -> Vec<(&str, String)> {
        match self {
            ApnsError::ApnsUpstream(e) => vec![("error", e.to_string())],
            _ => vec![],
        }
    }
}