messagebird_async/sms/async/
send.rs

1/// Query a list of messages
2///
3/// Still needs some work and the names are garbage
4use super::super::*;
5
6use futures::*;
7use hyper;
8
9use hyper_rustls;
10
11use std::env;
12use std::fmt;
13use std::marker::PhantomData;
14use std::ops::Deref;
15
16/// API Token Access
17///
18/// They can be managed under https://dashboard.messagebird.com/en/developers/access
19#[derive(Debug, Clone)]
20pub struct AccessKey(String);
21
22impl Deref for AccessKey {
23    type Target = String;
24    fn deref(&self) -> &Self::Target {
25        &self.0
26    }
27}
28
29impl FromStr for AccessKey {
30    type Err = MessageBirdError;
31    fn from_str(s: &str) -> Result<Self, Self::Err> {
32        // TODO eval length
33        Ok(AccessKey(s.to_string()))
34    }
35}
36
37impl From<String> for AccessKey {
38    fn from(s: String) -> Self {
39        AccessKey(s)
40    }
41}
42
43impl fmt::Display for AccessKey {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        write!(f, "{}", self.0)
46    }
47}
48
49impl AccessKey {
50    pub fn from_env() -> Result<AccessKey, MessageBirdError> {
51        let raw =
52            env::var("MESSAGEBIRD_ACCESSKEY").map_err(|_e| MessageBirdError::AccessKeyError {
53                msg: "env".to_string(),
54            })?;
55        AccessKey::from_str(raw.as_str())
56    }
57}
58
59/// Request object for a list of sent or in processing messages
60pub type RequestMessageList = Request<parameter::list::ListParameters, MessageList>;
61
62/// Request returning one individual message
63pub type RequestView = Request<parameter::view::ViewParameters, Message>;
64
65/// Request to send a message
66pub type RequestSend = Request<parameter::send::SendParameters, Message>;
67
68/// Generic API request object to messagebird REST API
69/// Handles authorization and parses returned json into structures
70///
71/// Should not be used!
72/// Use `RequestMessageList`,`RequestView`,`RequestSend` instead!
73pub struct Request<T, R> {
74    future: Box<dyn Future<Item = R, Error = MessageBirdError>>,
75    phantom: PhantomData<T>,
76}
77
78impl<T, R> Future for Request<T, R> {
79    type Item = R;
80    type Error = MessageBirdError;
81    fn poll(&mut self) -> Result<Async<Self::Item>, Self::Error> {
82        self.future.poll()
83    }
84}
85
86fn request_future_with_json_response<R>(
87    client: &mut hyper::Client<
88        hyper_rustls::HttpsConnector<hyper::client::HttpConnector>,
89        hyper::Body,
90    >,
91    request: hyper::Request<hyper::Body>,
92) -> impl Future<Item = R, Error = MessageBirdError>
93where
94    R: 'static + Sized + Send + Sync + for<'de> serde::de::Deserialize<'de> + std::fmt::Debug,
95{
96    debug!("request {:?}", request);
97    let fut = client
98        .request(request)
99        .map_err(|e: hyper::Error| {
100            debug!("request {:?}", e);
101            MessageBirdError::RequestError
102        })
103        .and_then(|response: hyper::Response<hyper::Body>| {
104            let status = response.status();
105            debug!("rest status code: {}", status);
106
107            futures::future::ok(response)
108            // } else {
109            //     futures::future::err(MessageBirdError::ServiceError {
110            //         code: status.as_u16(),
111            //         description : "TODO".to_string(),
112            //         parameter : None,
113            //     })
114            // }
115        })
116        .and_then(|response: hyper::Response<hyper::Body>| {
117            let status = response.status();
118            let body: hyper::Body = response.into_body();
119            body.concat2()
120                .map_err(|e| {
121                    debug!("body concat {:?}", e);
122                    MessageBirdError::RequestError
123                })
124                .map(move |x| (status, x))
125            // returns a hyper::Chunk!
126        })
127        // use the body after concatenation
128        .and_then(|(status, body): (_, hyper::Chunk)| {
129            debug!("response: {:?}", String::from_utf8(body.to_vec()).unwrap());
130            match status {
131                hyper::StatusCode::OK | hyper::StatusCode::CREATED => {
132                    // try to parse as json with serde_json
133                    match serde_json::from_slice::<R>(&body).map_err(|e| {
134                        debug!("Failed to parse response body: {:?}", e);
135                        MessageBirdError::ParseError
136                    }) {
137                        Err(e) => futures::future::err(e),
138                        Ok(x) => {
139                            debug!("Parsed response {:?}", x);
140                            futures::future::ok(x)
141                        }
142                    }
143                }
144                _ => match serde_json::from_slice::<ServiceErrors>(&body).map_err(|e| {
145                    debug!("Failed to parse response body: {:?}", e);
146                    MessageBirdError::ParseError
147                }) {
148                    Err(e) => futures::future::err(e),
149                    Ok(service_errors) => {
150                        let service_errors = service_errors.into();
151                        debug!("Parsed error response {:?}", service_errors);
152                        futures::future::err(MessageBirdError::ServiceError(service_errors))
153                    }
154                },
155            }
156        });
157    fut
158}
159
160impl<P, R> Request<P, R>
161where
162    P: Send + Query,
163    R: 'static + Send + Sync + for<'de> serde::de::Deserialize<'de> + std::fmt::Debug,
164{
165    pub fn new(parameters: &P, accesskey: &AccessKey) -> Self {
166        let https = hyper_rustls::HttpsConnector::new(4);
167        let mut client: hyper::Client<_, hyper::Body> = hyper::Client::builder().build(https);
168
169        let mut request = hyper::Request::builder();
170        request.uri(parameters.uri());
171        request.method(parameters.method());
172        request.header(
173            hyper::header::AUTHORIZATION,
174            format!("AccessKey {}", accesskey),
175        );
176        debug!("{:?}", request);
177
178        // XXX refactor needed - badly needed
179        let request: hyper::Request<_> = if parameters.method() == hyper::Method::POST {
180            request.header(
181                hyper::header::CONTENT_TYPE,
182                format!("application/x-www-form-urlencoded"),
183            );
184            parameters
185                .uri()
186                .query()
187                .map(|body: &str| {
188                    let body = body.to_string();
189                    request.header(hyper::header::CONTENT_LENGTH, format!("{}", body.len()));
190                    request.body(body.into()).unwrap()
191                })
192                .unwrap_or_else(|| {
193                    request.header(hyper::header::CONTENT_LENGTH, format!("{}", 0));
194                    request.body(hyper::Body::empty()).unwrap()
195                })
196        } else {
197            request.header(hyper::header::CONTENT_LENGTH, format!("{}", 0));
198            request.body(hyper::Body::empty()).unwrap()
199        };
200
201        let future = request_future_with_json_response::<R>(&mut client, request);
202
203        // TODO avoid this boxing if possible
204        let future = Box::new(future);
205        Self {
206            future,
207            phantom: PhantomData,
208        }
209    }
210}