Skip to main content

lib_remotebuild_rs/
request.rs

1use crate::config::RequestConfig;
2use crate::jobs;
3use crate::request_error::Error;
4
5use serde::de::DeserializeOwned;
6use serde::Serialize;
7
8use std::collections::HashMap;
9
10const HEADER_RESPONSE_STATUS: &str = "x-response-status";
11const HEADER_RESPONSE_MESSAGE: &str = "x-response-message";
12const STATUS_SUCCESS: u8 = 1;
13const STATUS_ERROR: u8 = 0;
14
15#[derive(Default)]
16pub struct Request<T>
17where
18    T: Serialize,
19{
20    config: RequestConfig,
21    endpoint: String,
22    method: reqwest::Method,
23    auth: Option<Authorization>,
24    payload: Option<T>,
25}
26
27#[derive(Debug)]
28pub enum AuthorizationType {
29    Bearer,
30    Basic,
31}
32
33impl std::fmt::Display for AuthorizationType {
34    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35        write!(f, "{:?}", self)
36    }
37}
38
39pub struct Authorization {
40    auth_type: AuthorizationType,
41    payload: String,
42}
43
44impl Authorization {
45    pub fn new(auth_type: AuthorizationType, payload: String) -> Self {
46        Authorization { auth_type, payload }
47    }
48}
49
50impl<T> Request<T>
51where
52    T: Serialize + Default,
53{
54    /// Create a new request by using a config, the desired endpoint and a Serializeable value T which will
55    /// be formatted to json
56    pub fn new(config: RequestConfig, endpoint: &str, p: T) -> Self {
57        Request {
58            // WithAuthFromConfig with authorization
59            config,
60            endpoint: endpoint.to_owned(),
61            payload: Some(p),
62            ..Request::default()
63        }
64    }
65
66    /// Use the given authorization for the request
67    pub fn with_auth(&mut self, auth: Authorization) {
68        self.auth = Some(auth);
69    }
70
71    /// Use the given HTTP Method for the request
72    pub fn with_method(&mut self, method: reqwest::Method) {
73        self.method = method;
74    }
75
76    /// Prepare and send a request. Don't do any parsing of the results body.
77    async fn prepare_response(self) -> Result<(reqwest::Response, String, u8), Error> {
78        let mut req_builder = reqwest::Client::new().request(
79            self.method,
80            reqwest::Url::parse(&self.config.url)
81                .unwrap()
82                .join(&self.endpoint)
83                .unwrap(),
84        );
85
86        // Add payload
87        if let Some(p) = &self.payload {
88            req_builder = req_builder.json(p);
89        }
90
91        // Add Auth
92        if let Some(auth) = self.auth {
93            req_builder = req_builder.header(
94                "Authorization",
95                format!("{} {}", auth.auth_type, auth.payload),
96            );
97        }
98
99        // Send the request
100        let r = req_builder.send().await.map_err(Error::Request)?;
101        let status = r.status().as_u16();
102        if status != 200 {
103            return Err(Error::HTTPNotOk(status));
104        }
105
106        let headers = r.headers();
107        if !headers.contains_key(HEADER_RESPONSE_STATUS)
108            || !headers.contains_key(HEADER_RESPONSE_MESSAGE)
109        {
110            return Err(Error::InvalidHeaders);
111        }
112
113        let msg = headers
114            .get(HEADER_RESPONSE_MESSAGE)
115            .unwrap()
116            .to_str()
117            .unwrap()
118            .to_string();
119
120        let status: u8 = headers
121            .get(HEADER_RESPONSE_STATUS)
122            .unwrap()
123            .to_str()
124            .unwrap()
125            .parse()
126            .map_err(|_| Error::InvalidHeaders)?;
127
128        Ok((r, msg, status))
129    }
130
131    /// Do a request but don't parse the body
132    pub async fn do_request_void(self) -> Result<(), Error> {
133        let (_, msg, status) = self.prepare_response().await?;
134
135        if status == STATUS_SUCCESS {
136            Ok(())
137        } else {
138            Err(Error::Error(msg))
139        }
140    }
141
142    /// Do a request but try to parse the body into a RequestResult
143    pub async fn do_request<U>(self) -> Result<RequestResult<U>, Error>
144    where
145        U: DeserializeOwned,
146    {
147        let (r, msg, status) = self.prepare_response().await?;
148
149        if status == STATUS_SUCCESS {
150            let response: U = r.json().await.map_err(Error::Decode)?;
151
152            Ok(RequestResult {
153                response: Some(response),
154                message: msg.to_string(),
155                status_code: status,
156            })
157        } else {
158            Err(Error::Error(msg))
159        }
160    }
161}
162
163/// Result for a request. T represents
164/// The destination type to parse the
165/// result in.
166#[derive(Debug)]
167pub struct RequestResult<T>
168where
169    T: DeserializeOwned,
170{
171    pub response: Option<T>,
172    pub message: String,
173    pub status_code: u8,
174}
175
176/// Lists all jobs with a given limit
177#[derive(Default, Serialize)]
178pub struct ListJobs {
179    #[serde(rename(serialize = "l"))]
180    pub limit: i32,
181}
182
183/// Payload for requests with need
184/// a reference to a job by its id
185#[derive(Default, Serialize)]
186pub struct JobRequest {
187    #[serde(rename(serialize = "id"))]
188    pub job_id: u32,
189}
190
191/// Payload for creating a new Job
192/// args - additional arguments
193#[derive(Default, Serialize)]
194pub struct AddJobRequest {
195    #[serde(rename(serialize = "buildtype"))]
196    pub job_type: jobs::Type,
197
198    pub args: HashMap<String, String>,
199
200    #[serde(rename(serialize = "uploadtype"))]
201    pub upload_type: jobs::UploadType,
202
203    #[serde(rename(serialize = "disableccache"))]
204    pub disable_ccache: bool,
205}
206
207/// Payload for requests which need
208/// user credentials
209#[derive(Serialize, Debug, Default)]
210pub struct Credential {
211    #[serde(rename(serialize = "mid"))]
212    pub machine_id: String,
213
214    pub username: String,
215
216    #[serde(rename(serialize = "pass"))]
217    pub password: String,
218}