1use reqwest::StatusCode;
2use serde::{Deserialize, Serialize};
3
4#[derive(Debug)]
5pub enum Error {
6 Reqwest(reqwest::Error),
7 Body(StatusCode, Vec<String>),
8}
9
10impl From<reqwest::Error> for Error {
11 fn from(err: reqwest::Error) -> Self {
12 Self::Reqwest(err)
13 }
14}
15
16#[derive(Deserialize)]
17struct BodyError {
18 errors: Vec<String>,
19}
20
21#[derive(Clone, Debug)]
22pub struct Client {
23 host: String,
24 api_key: String,
25}
26
27impl Client {
28 pub fn new(host: String, api_key: String) -> Self {
29 Self { host, api_key }
30 }
31}
32
33impl Client {
34 pub async fn post<T: Serialize>(&self, path: &str, payload: &T) -> Result<(), Error> {
35 let client = reqwest::Client::new();
36 let url = format!("{}{}", self.host, path);
37 let response = client
38 .post(url.as_str())
39 .header("Content-Type", "application/json")
40 .header("DD-API-KEY", self.api_key.as_str())
41 .json(payload)
42 .send()
43 .await?;
44 let status = response.status();
45 if status.is_client_error() || status.is_server_error() {
46 let body = response.json::<BodyError>().await?;
47 Err(Error::Body(status, body.errors))
48 } else {
49 Ok(())
50 }
51 }
52}
53
54#[cfg(test)]
55mod tests {
56 use super::*;
57 use mockito::mock;
58
59 #[tokio::test]
60 async fn post_success() {
61 let call = mock("POST", "/somewhere").with_status(202).create();
62 let client = Client::new(mockito::server_url(), String::from("fake-api-key"));
63 let result = client
64 .post("/somewhere", &String::from("Hello World!"))
65 .await;
66 assert!(result.is_ok());
67 call.expect(1);
68 }
69
70 #[tokio::test]
71 async fn post_authentication_error() {
72 let call = mock("POST", "/somewhere")
73 .with_status(403)
74 .with_body("{\"errors\":[\"Authentication error\"]}")
75 .create();
76 let client = Client::new(mockito::server_url(), String::from("fake-api-key"));
77 let result = client
78 .post("/somewhere", &String::from("Hello World!"))
79 .await;
80 assert!(result.is_err());
81 call.expect(1);
82 }
83}