oauth1_twitter/endpoints/
access_token.rs1use http_api_client_endpoint::{http::StatusCode, Body, Endpoint, Request, Response};
4use serde::{Deserialize, Serialize};
5
6use super::common::{EndpointError, EndpointRet};
7use crate::objects::{
8 authentication_access_token::AuthenticationAccessToken,
9 authentication_request_token::AuthenticationRequestToken, consumer_key::ConsumerKey,
10};
11
12pub const URL: &str = "https://api.twitter.com/oauth/access_token";
13
14#[derive(Debug, Clone)]
16pub struct AccessTokenEndpoint {
17 pub consumer_key: ConsumerKey,
18 pub authentication_request_token: AuthenticationRequestToken,
19 pub oauth_verifier: String,
20}
21impl AccessTokenEndpoint {
22 pub fn new(
23 consumer_key: ConsumerKey,
24 authentication_request_token: AuthenticationRequestToken,
25 oauth_verifier: impl AsRef<str>,
26 ) -> Self {
27 Self {
28 consumer_key,
29 authentication_request_token,
30 oauth_verifier: oauth_verifier.as_ref().into(),
31 }
32 }
33}
34
35impl Endpoint for AccessTokenEndpoint {
36 type RenderRequestError = EndpointError;
37
38 type ParseResponseOutput = EndpointRet<AccessTokenResponseBody>;
39 type ParseResponseError = EndpointError;
40
41 fn render_request(&self) -> Result<Request<Body>, Self::RenderRequestError> {
42 let query = AccessTokenRequestQuery {
43 oauth_verifier: self.oauth_verifier.to_owned(),
44 };
45
46 let request_tmp = reqwest_oauth1::Client::new()
47 .post(URL)
48 .sign(
49 self.consumer_key
50 .secrets_with_request_token(&self.authentication_request_token),
51 )
52 .query(&query)
53 .generate_signature()
54 .map_err(EndpointError::MakeReqwestRequestBuilderFailed)?
55 .build()
56 .map_err(EndpointError::MakeReqwestRequestFailed)?;
57
58 let mut request = Request::builder()
59 .method(request_tmp.method())
60 .uri(request_tmp.url().as_str())
61 .body(vec![])
62 .map_err(EndpointError::MakeRequestFailed)?;
63
64 let headers = request.headers_mut();
65 *headers = request_tmp.headers().to_owned();
66
67 Ok(request)
68 }
69
70 fn parse_response(
71 &self,
72 response: Response<Body>,
73 ) -> Result<Self::ParseResponseOutput, Self::ParseResponseError> {
74 let status = response.status();
75
76 match status {
77 StatusCode::OK => Ok(EndpointRet::Ok(
78 serde_urlencoded::from_bytes::<AccessTokenResponseBody>(response.body())
79 .map_err(EndpointError::DeResponseBodyOkFailed)?,
80 )),
81 status => match serde_json::from_slice(response.body()) {
82 Ok(fail_json) => Ok(EndpointRet::Other((status, Ok(fail_json)))),
83 Err(_) => Ok(EndpointRet::Other((
84 status,
85 Err(response.body().to_owned()),
86 ))),
87 },
88 }
89 }
90}
91
92#[derive(Deserialize, Serialize, Debug, Clone)]
94pub struct AccessTokenRequestQuery {
95 pub oauth_verifier: String,
96}
97
98#[derive(Deserialize, Serialize, Debug, Clone)]
100pub struct AccessTokenResponseBody {
101 pub oauth_token: String,
102 pub oauth_token_secret: String,
103 pub user_id: u64,
104 pub screen_name: String,
105}
106
107impl AccessTokenResponseBody {
108 pub fn authentication_access_token(&self) -> AuthenticationAccessToken {
109 AuthenticationAccessToken::new(&self.oauth_token, &self.oauth_token_secret)
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 use http_api_client_endpoint::http::Method;
118
119 #[test]
120 fn test_render_request() {
121 let req = AccessTokenEndpoint::new(
123 ConsumerKey::new("foo", "bar"),
124 AuthenticationRequestToken::new("aaa", "xxx"),
125 "bbb",
126 )
127 .render_request()
128 .unwrap();
129 assert_eq!(req.method(), Method::POST);
130 assert_eq!(req.uri(), "https://api.twitter.com/oauth/access_token");
131 let req_header_authorization =
132 String::from_utf8_lossy(req.headers().get("Authorization").unwrap().as_bytes());
133 assert!(req_header_authorization.starts_with(r#"OAuth oauth_consumer_key="foo""#));
134 assert!(req_header_authorization.contains(r#"oauth_verifier="bbb""#));
135 }
136
137 #[test]
138 fn test_parse_response() {
139 let body = include_str!("../../tests/response_body_files/access_token.txt");
141 let res = Response::builder()
142 .status(StatusCode::OK)
143 .body(body.as_bytes().to_owned())
144 .unwrap();
145 let ret = AccessTokenEndpoint::new(
146 ConsumerKey::new("foo", "bar"),
147 AuthenticationRequestToken::new("aaa", "xxx"),
148 "bbb",
149 )
150 .parse_response(res)
151 .unwrap();
152 match &ret {
153 EndpointRet::Ok(body) => {
154 assert_eq!(
155 body.oauth_token,
156 "62532xx-eWudHldSbIaelX7swmsiHImEL4KinwaGloxxxxxx"
157 );
158 assert_eq!(
159 body.oauth_token_secret,
160 "2EEfA6BG5ly3sR3XjE0IBSnlQu4ZrUzPiYxxxxxx"
161 );
162 assert_eq!(body.user_id, 6253282);
163 assert_eq!(body.screen_name, "twitterapi");
164 }
165 EndpointRet::Other(_) => panic!("{ret:?}"),
166 }
167 }
168}