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
use std::error::Error;
use std::fmt::Display;
use actix_web::{http::StatusCode, HttpResponseBuilder, ResponseError};
use serde::Serialize;
use serde_json::json;
use crate::types::EdgeToken;
#[derive(Debug)]
pub enum FeatureError {
AccessDenied,
Retriable,
}
#[derive(Debug, Serialize)]
pub struct FrontendHydrationMissing {
pub project: String,
pub environment: String,
}
impl From<&EdgeToken> for FrontendHydrationMissing {
fn from(value: &EdgeToken) -> Self {
Self {
project: value.projects.join(","),
environment: value
.environment
.clone()
.unwrap_or_else(|| "default".into()), }
}
}
#[derive(Debug)]
pub enum EdgeError {
AuthorizationDenied,
AuthorizationPending,
ClientFeaturesFetchError(FeatureError),
ClientFeaturesParseError,
ClientRegisterError,
FrontendNotYetHydrated(FrontendHydrationMissing),
PersistenceError(String),
EdgeMetricsError,
EdgeTokenError,
EdgeTokenParseError,
InvalidBackupFile(String, String),
InvalidServerUrl(String),
JsonParseError(String),
NoFeaturesFile,
NoTokenProvider,
TlsError,
TokenParseError,
}
impl Error for EdgeError {}
impl Display for EdgeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EdgeError::InvalidBackupFile(path, why_invalid) => {
write!(f, "file at path: {path} was invalid due to {why_invalid}")
}
EdgeError::TlsError => write!(f, "Could not configure TLS"),
EdgeError::NoFeaturesFile => write!(f, "No features file located"),
EdgeError::AuthorizationDenied => write!(f, "Not allowed to access"),
EdgeError::NoTokenProvider => write!(f, "Could not get a TokenProvider"),
EdgeError::TokenParseError => write!(f, "Could not parse edge token"),
EdgeError::PersistenceError(msg) => write!(f, "{msg}"),
EdgeError::JsonParseError(msg) => write!(f, "{msg}"),
EdgeError::ClientFeaturesFetchError(fe) => match fe {
FeatureError::Retriable => write!(f, "Could not fetch client features. Will retry"),
FeatureError::AccessDenied => write!(
f,
"Could not fetch client features because api key was not allowed"
),
},
EdgeError::ClientFeaturesParseError => {
write!(f, "Failed to parse client features")
}
EdgeError::ClientRegisterError => {
write!(f, "Failed to register client")
}
EdgeError::InvalidServerUrl(msg) => write!(f, "Failed to parse server url: [{msg}]"),
EdgeError::EdgeTokenError => write!(f, "Edge token error"),
EdgeError::EdgeTokenParseError => write!(f, "Failed to parse token response"),
EdgeError::AuthorizationPending => {
write!(f, "No validation for token has happened yet")
}
EdgeError::EdgeMetricsError => write!(f, "Edge metrics error"),
EdgeError::FrontendNotYetHydrated(hydration_info) => {
write!(f, "Edge not yet hydrated for {hydration_info:?}")
}
}
}
}
impl ResponseError for EdgeError {
fn status_code(&self) -> actix_web::http::StatusCode {
match self {
EdgeError::InvalidBackupFile(_, _) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::TlsError => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::NoFeaturesFile => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::AuthorizationDenied => StatusCode::FORBIDDEN,
EdgeError::NoTokenProvider => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::TokenParseError => StatusCode::FORBIDDEN,
EdgeError::ClientFeaturesParseError => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::ClientFeaturesFetchError(_) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::InvalidServerUrl(_) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::PersistenceError(_) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::JsonParseError(_) => StatusCode::INTERNAL_SERVER_ERROR,
EdgeError::EdgeTokenError => StatusCode::BAD_REQUEST,
EdgeError::EdgeTokenParseError => StatusCode::BAD_REQUEST,
EdgeError::AuthorizationPending => StatusCode::UNAUTHORIZED,
EdgeError::EdgeMetricsError => StatusCode::BAD_REQUEST,
EdgeError::ClientRegisterError => StatusCode::BAD_REQUEST,
EdgeError::FrontendNotYetHydrated(_) => StatusCode::NETWORK_AUTHENTICATION_REQUIRED,
}
}
fn error_response(&self) -> actix_web::HttpResponse<actix_web::body::BoxBody> {
match self {
EdgeError::FrontendNotYetHydrated(hydration_info) => {
HttpResponseBuilder::new(self.status_code()).json(json!({
"explanation": "Edge does not yet have data for this token. Please make a call against /api/client/features with a client token that has the same access as your token",
"access": hydration_info
}))
},
_ => HttpResponseBuilder::new(self.status_code()).finish()
}
}
}
impl From<serde_json::Error> for EdgeError {
fn from(value: serde_json::Error) -> Self {
EdgeError::JsonParseError(value.to_string())
}
}
#[cfg(test)]
mod tests {}