|
| 1 | +//! Endpoint functions and base response and error types |
| 2 | +use std::collections::HashMap; |
| 3 | +use std::fmt; |
| 4 | + |
| 5 | +use reqwest; |
| 6 | +use reqwest::{RequestBuilder, Response, StatusCode}; |
| 7 | +use serde_derive::{Deserialize, Serialize}; |
| 8 | +use thiserror::Error; |
| 9 | +use validator::Validate; |
| 10 | + |
| 11 | +use crate::configuration::{ApiKey, Configuration}; |
| 12 | + |
| 13 | +#[cfg(feature = "sms")] |
| 14 | +pub mod sms; |
| 15 | + |
| 16 | +/// Holds the possible errors that can happen when calling the Infobip API. |
| 17 | +#[derive(Error, Debug)] |
| 18 | +pub enum SdkError { |
| 19 | + #[error("request body has field errors")] |
| 20 | + Validation(#[from] validator::ValidationErrors), |
| 21 | + |
| 22 | + #[error("client error calling endpoint")] |
| 23 | + Reqwest(#[from] reqwest::Error), |
| 24 | + |
| 25 | + #[error("serialization error")] |
| 26 | + Serde(#[from] serde_json::Error), |
| 27 | + |
| 28 | + #[error("api request error")] |
| 29 | + ApiRequestError(#[from] ApiError), |
| 30 | +} |
| 31 | + |
| 32 | +/// Holds the status code and error details when a 4xx or 5xx response is received. |
| 33 | +#[derive(Error, Clone, Debug)] |
| 34 | +pub struct ApiError { |
| 35 | + pub details: ApiErrorDetails, |
| 36 | + pub status: StatusCode, |
| 37 | +} |
| 38 | + |
| 39 | +impl fmt::Display for ApiError { |
| 40 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 41 | + write!( |
| 42 | + f, |
| 43 | + "API request error: status: {} {}", |
| 44 | + self.status, self.details |
| 45 | + ) |
| 46 | + } |
| 47 | +} |
| 48 | + |
| 49 | +/// Holds information about a server-side error. |
| 50 | +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] |
| 51 | +pub struct ServiceException { |
| 52 | + #[serde(rename = "messageId")] |
| 53 | + pub message_id: Option<String>, |
| 54 | + #[serde(rename = "text")] |
| 55 | + pub text: String, |
| 56 | + #[serde(rename = "validationErrors", skip_serializing_if = "Option::is_none")] |
| 57 | + pub validation_errors: Option<String>, |
| 58 | +} |
| 59 | + |
| 60 | +/// Holds the exception produced by a server-side error. |
| 61 | +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] |
| 62 | +pub struct RequestError { |
| 63 | + #[serde(rename = "serviceException")] |
| 64 | + pub service_exception: ServiceException, |
| 65 | +} |
| 66 | + |
| 67 | +/// Holds the details about a 4xx/5xx server-side error. |
| 68 | +#[derive(Clone, Debug, Error, PartialEq, Serialize, Deserialize)] |
| 69 | +pub struct ApiErrorDetails { |
| 70 | + #[serde(rename = "requestError")] |
| 71 | + pub request_error: RequestError, |
| 72 | +} |
| 73 | + |
| 74 | +impl fmt::Display for ApiErrorDetails { |
| 75 | + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
| 76 | + write!( |
| 77 | + f, |
| 78 | + "API request error: {}", |
| 79 | + serde_json::to_string(self).expect("error deserializing request error") |
| 80 | + ) |
| 81 | + } |
| 82 | +} |
| 83 | + |
| 84 | +/// Holds the status code and the response body of a successful API call. |
| 85 | +#[derive(Clone, Debug, PartialEq)] |
| 86 | +pub struct SdkResponse<T> { |
| 87 | + pub response_body: T, |
| 88 | + pub status: StatusCode, |
| 89 | +} |
| 90 | + |
| 91 | +fn get_api_key_authorization_value(api_key: &ApiKey) -> String { |
| 92 | + let key = api_key.key.to_owned(); |
| 93 | + let prefix = api_key |
| 94 | + .prefix |
| 95 | + .to_owned() |
| 96 | + .unwrap_or_else(|| "App".to_string()); |
| 97 | + |
| 98 | + format!("{} {}", prefix, key) |
| 99 | +} |
| 100 | + |
| 101 | +// Async version of add_auth, uses async request builder. |
| 102 | +fn add_auth(mut builder: RequestBuilder, configuration: &Configuration) -> RequestBuilder { |
| 103 | + if let Some(api_key) = &configuration.api_key { |
| 104 | + builder = builder.header("Authorization", get_api_key_authorization_value(api_key)); |
| 105 | + } else if let Some(basic_auth) = &configuration.basic_auth { |
| 106 | + builder = builder.basic_auth( |
| 107 | + basic_auth.username.to_owned(), |
| 108 | + basic_auth.password.to_owned(), |
| 109 | + ); |
| 110 | + } else if let Some(token) = &configuration.bearer_access_token { |
| 111 | + builder = builder.bearer_auth(token); |
| 112 | + }; |
| 113 | + |
| 114 | + builder |
| 115 | +} |
| 116 | + |
| 117 | +// Blocking version of add_auth, uses blocking request builder. |
| 118 | +fn add_auth_blocking( |
| 119 | + mut builder: reqwest::blocking::RequestBuilder, |
| 120 | + configuration: &Configuration, |
| 121 | +) -> reqwest::blocking::RequestBuilder { |
| 122 | + if let Some(api_key) = &configuration.api_key { |
| 123 | + builder = builder.header("Authorization", get_api_key_authorization_value(api_key)); |
| 124 | + } else if let Some(basic_auth) = &configuration.basic_auth { |
| 125 | + builder = builder.basic_auth( |
| 126 | + basic_auth.username.to_owned(), |
| 127 | + basic_auth.password.to_owned(), |
| 128 | + ); |
| 129 | + } else if let Some(token) = &configuration.bearer_access_token { |
| 130 | + builder = builder.bearer_auth(token); |
| 131 | + }; |
| 132 | + |
| 133 | + builder |
| 134 | +} |
| 135 | + |
| 136 | +fn build_api_error(status: StatusCode, text: &str) -> SdkError { |
| 137 | + match serde_json::from_str(text) { |
| 138 | + Ok(details) => SdkError::ApiRequestError(ApiError { details, status }), |
| 139 | + Err(e) => SdkError::Serde(e), |
| 140 | + } |
| 141 | +} |
| 142 | + |
| 143 | +async fn send_no_body_request( |
| 144 | + client: &reqwest::Client, |
| 145 | + configuration: &Configuration, |
| 146 | + query_parameters: HashMap<String, String>, |
| 147 | + method: reqwest::Method, |
| 148 | + path: &str, |
| 149 | +) -> Result<Response, SdkError> { |
| 150 | + let url = format!("{}{}", configuration.base_url, path); |
| 151 | + let mut builder = client.request(method, url).query(&query_parameters); |
| 152 | + |
| 153 | + builder = add_auth(builder, configuration); |
| 154 | + |
| 155 | + Ok(builder.send().await?) |
| 156 | +} |
| 157 | + |
| 158 | +async fn send_json_request<T: Validate + serde::Serialize>( |
| 159 | + client: &reqwest::Client, |
| 160 | + configuration: &Configuration, |
| 161 | + request_body: T, |
| 162 | + query_parameters: HashMap<String, String>, |
| 163 | + method: reqwest::Method, |
| 164 | + path: &str, |
| 165 | +) -> Result<Response, SdkError> { |
| 166 | + let url = format!("{}{}", configuration.base_url, path); |
| 167 | + let mut builder = client |
| 168 | + .request(method, url) |
| 169 | + .json(&request_body) |
| 170 | + .query(&query_parameters); |
| 171 | + |
| 172 | + builder = add_auth(builder, configuration); |
| 173 | + |
| 174 | + Ok(builder.send().await?) |
| 175 | +} |
| 176 | + |
| 177 | +async fn _send_multipart_request( |
| 178 | + client: &reqwest::Client, |
| 179 | + configuration: &Configuration, |
| 180 | + form: reqwest::multipart::Form, |
| 181 | + method: reqwest::Method, |
| 182 | + path: &str, |
| 183 | +) -> Result<Response, SdkError> { |
| 184 | + let url = format!("{}{}", configuration.base_url, path); |
| 185 | + let mut builder = client.request(method, url); |
| 186 | + |
| 187 | + builder = add_auth(builder, configuration); |
| 188 | + |
| 189 | + Ok(builder.multipart(form).send().await?) |
| 190 | +} |
| 191 | + |
| 192 | +fn send_blocking_json_request<T: Validate + serde::Serialize>( |
| 193 | + client: &reqwest::blocking::Client, |
| 194 | + configuration: &Configuration, |
| 195 | + request_body: T, |
| 196 | + method: reqwest::Method, |
| 197 | + path: &str, |
| 198 | +) -> Result<reqwest::blocking::Response, SdkError> { |
| 199 | + request_body.validate()?; |
| 200 | + |
| 201 | + let url = format!("{}{}", configuration.base_url, path); |
| 202 | + let mut builder = client.request(method, url); |
| 203 | + |
| 204 | + builder = add_auth_blocking(builder, configuration); |
| 205 | + |
| 206 | + Ok(builder.json(&request_body).send()?) |
| 207 | +} |
| 208 | + |
| 209 | +mod tests; |
0 commit comments