egg_mode/
raw.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5//! Raw access to request- and response-building primitives used internally by egg-mode.
6//!
7//! The functions and types exposed in this module allow you to access Twitter API functions that
8//! aren't currently wrapped by egg-mode, or to provide parameters to Twitter that egg-mode doesn't
9//! currently use. These functions also allow you to have more power in how you process the data
10//! returned by Twitter. In return, much more knowledge of the Twitter API is required to
11//! effectively use these functions.
12//!
13//! The functions in this module can be divided into two categories: assembling a request, and
14//! executing it to get a response. Some wrapper types in egg-mode operate directly on requests, or
15//! create their own, so constructors for those types are also exposed here.
16//!
17//! To start using the functions in this module, you'll need a [`Token`] from the authentication
18//! flow. The parameters to an endpoint are represented by the [`ParamList`] type. They're
19//! separated out so that they can be included as part of the OAuth signature given to Twitter as
20//! part of the API call. This also means that the URL you give to the request functions should be
21//! the base URL, with no parameters.
22//!
23//! [`Token`]: ../auth/enum.Token.html
24//! [`ParamList`]: struct.ParamList.html
25//!
26//! There are three basic request functions, based on how the endpoint expects to be called:
27//!
28//! * `request_get` assembles a GET request, with the given parameters appended to the URL as a
29//!   query string. All GET endpoints that egg-mode currently wraps use this function to encode and
30//!   sign the request.
31//! * `request_post` assembles a POST request, with the given parameters included in the POST body
32//!   formatted as `x-www-form-urlencoded` data. Most POST endpoints in the Twitter API are
33//!   formatted using this function.
34//! * `request_post_json` also assembles a POST request, but instead of taking a `ParamList`, it
35//!   takes arbitrary data and formats it in the POST body as JSON. The provided data is *not* used
36//!   as part of the OAuth signature. At time of writing (between releases 0.14 and 0.15) the only
37//!   egg-mode endpoint that uses this function is [`media::set_metadata`].
38//!
39//! [`media::set_metadata`]: ../media/fn.set_metadata.html
40//!
41//! Once you have a `Request`, you can hand it to the `response_*` functions in this module to
42//! process it. Which one you select depends on how much processing you want egg-mode to do with
43//! the response:
44//!
45//! * At the most hands-off end, there's [`response_future`], which is a small wrapper that just
46//!   starts the request and hands off the `ResponseFuture` from `hyper` to give you the most power
47//!   over handling the response data.
48//! * In the middle, there's [`response_raw_bytes`], which wraps the `ResponseFuture` to return the
49//!   headers and response body after inspecting the rate-limit headers and response code, and
50//!   after inspecting the response to see whether it returned error data from Twitter.
51//! * Finally there's [`response_json`], which picks up from `response_raw_bytes` to parse the
52//!   response as JSON and deserialize it into the target type, alongside the rate-limit
53//!   information from the response headers.
54//!
55//! [`response_future`]: fn.response_future.html
56//! [`response_raw_bytes`]: fn.response_raw_bytes.html
57//! [`response_json`]: fn.response_json.html
58//!
59//! In addition, there are `request_as_*` and `response_as_*` functions available to format a
60//! request using one of the wrappers used in egg-mode. If the endpoint you're using is one that
61//! currently uses one of these wrapper types or returns and accepts data the same way as one of
62//! these endpoints, you can use these functions to get the same experience as the existing
63//! wrappers in egg-mode. See the documentation for these functions to see their assumptions and
64//! requirements.
65//!
66//! If you need the ability to assemble a request in a way that `request_get`, `request_post`, or
67//! `request_post_json` don't allow, the `RequestBuilder` type available in the `auth` submodule
68//! provides the lowest-level control over how a request is built and signed. For more information,
69//! see the [`auth`] module.
70//!
71//! [`auth`]: auth/index.html
72
73use hyper::{Body, Request};
74
75use crate::auth::Token;
76use crate::cursor;
77use crate::stream::TwitterStream;
78
79use crate::tweet::Timeline as TweetTimeline;
80
81pub use crate::common::Headers;
82pub use crate::common::ParamList;
83
84pub use crate::auth::raw::delete as request_delete;
85pub use crate::auth::raw::get as request_get;
86pub use crate::auth::raw::post as request_post;
87pub use crate::auth::raw::post_json as request_post_json;
88
89/// Assemble a GET request and convert it to a `Timeline` of tweets.
90///
91/// An endpoint wrapped by `tweet::Timeline` returns data as an array of Tweets. In addition, they
92/// also take parameters `since_id` and `max_id` to filter the earliest and latest Tweet returned
93/// (respectively), as well as a `count` parameter to limit the number of Tweets returned at once.
94/// The `Timeline` struct sets these parameters itself; you should not need to hand them to this
95/// function. These parameters are manipulated through the `older()` and `newer()` functions, as
96/// well as the `with_page_size()` function.
97///
98/// In addition, the `Timeline` struct also adds `tweet_mode=extended` and
99/// `include_ext_alt_text=true` when sending a request, to fill in the data from extended Tweets
100/// and media alt-text when returned from Twitter.
101///
102/// If you do not need to send additional parameters other than these mentioned, you can pass
103/// `None` for the `params` to make the `Timeline` manage the parameters itself.
104pub fn request_as_tweet_timeline(
105    url: &'static str,
106    token: &Token,
107    params: Option<ParamList>,
108) -> TweetTimeline {
109    TweetTimeline::new(url, params, token)
110}
111
112/// Assemble a GET request and convert it to a `CursorIter`.
113///
114/// A `CursorIter` is a wrapper around an endpoint that returns data in the following structure:
115///
116/// ```json
117/// {
118///   "previous_cursor": int,
119///   "previous_cursor_str": "string",
120///   "next_cursor": int,
121///   "next_cursor_str": "string",
122///   "<data>": [ ... ]
123/// }
124/// ```
125///
126/// Where `<data>` is named something relevant to the endpoint, and contains an array of objects.
127/// `CursorIter` expects to be able to deserialize this response into a type that implements the
128/// [`Cursor`] trait to expose these fields. (The cursor struct is the type parameter of
129/// `CursorIter` itself.) It uses these fields to set the `cursor` parameter to the given endpoint.
130/// It also sets the `count` parameter with the given `page_size`, if present. (Some cursor
131/// endpoints do not support setting a page size; an example of such an endpoint is `GET
132/// friendships/incoming`.)
133///
134/// [`Cursor`]: ../cursor/trait.Cursor.html
135///
136/// An example of a Twitter API endpoint that exposes a cursor interface is [`GET friends/list`].
137///
138/// [`GET friends/list`]: https://developer.twitter.com/en/docs/accounts-and-users/follow-search-get-users/api-reference/get-friends-list
139///
140/// If you can supply a Cursor type (or use one of the ones in the `cursor` module), `CursorIter`
141/// will wrap the responses into a `Stream` interface that automatically fetches the next page of
142/// results on-demand.
143pub fn request_as_cursor_iter<T: cursor::Cursor + serde::de::DeserializeOwned>(
144    url: &'static str,
145    token: &Token,
146    params: Option<ParamList>,
147    page_size: Option<i32>,
148) -> cursor::CursorIter<T> {
149    cursor::CursorIter::new(url, token, params, page_size)
150}
151
152pub use crate::common::get_response as response_future;
153pub use crate::common::raw_request as response_raw_bytes;
154pub use crate::common::request_with_empty_response as response_empty;
155pub use crate::common::request_with_json_response as response_json;
156
157/// Converts the given request into a `TwitterStream`.
158///
159/// This function can be used for endpoints that open a persistent stream, like `GET
160/// statuses/sample`. If you know that the messages returned by the stream you're using will look
161/// the same as `StreamMessage`, this can be a convenient way to customize a stream if you need to
162/// use other endpoints or options not available to `StreamBuilder`.
163///
164/// Since the `TwitterStream` type doesn't need to provide additional parameters to the request, it
165/// can take a signed, completed request as its constructor.
166pub fn response_as_stream(req: Request<Body>) -> TwitterStream {
167    TwitterStream::new(req)
168}
169
170pub use crate::common::RoundTrip;
171
172/// Facilities to manually assemble signed requests.
173///
174/// In case you need to do things that aren't available in the `raw` module, the `RequestBuilder`
175/// included here allows you to go deeper into the internals of egg-mode. All of the authentication
176/// internals are implemented in terms of `RequestBuilder`, meaning you can fully recreate them
177/// using your own parsing logic for the output.
178///
179/// `RequestBuilder` is designed to allow for easily creating an OAuth signature from the
180/// parameters to an API endpoint, and so they collect `ParamList` instances just like the
181/// functions in the `raw` module. However, there is also a way to manually set the request body
182/// outside of the `ParamList` struct, for endpoints like `POST media/metadata/create` or `POST
183/// oauth2/token` which require specific body formats.
184///
185/// True to its name, all the methods on `RequestBuilder` are meant to be used in a builder
186/// pattern. To begin, you need to have the URL you wish to access and the HTTP Method you would
187/// like to use. Then you can build up the query string and request body, and accumulate the
188/// parameters used in the OAuth signature. Finally, to finish building the request, you need to
189/// provide what kind of authorization you would like to use. Since there are several ways to
190/// authorize a call to Twitter, there are several options available:
191///
192/// * For [OAuth 1.0a], you can specify the keys individually in `request_keys`, or provide a
193///   complete `Token` using `request_token`.
194/// * For [OAuth 2.0 Bearer Token][bearer], you can provide the Bearer token using `request_token`.
195/// * For [Basic authentication][basic] used with Enterprise APIs and when requesting a Bearer
196///   token, you can provide the credentials as a `KeyPair` in `request_consumer_bearer`.
197///
198/// [OAuth 1.0a]: https://developer.twitter.com/en/docs/basics/authentication/oauth-1-0a
199/// [bearer]: https://developer.twitter.com/en/docs/basics/authentication/oauth-2-0
200/// [basic]: https://developer.twitter.com/en/docs/basics/authentication/basic-auth
201///
202/// For example, if you were using this type to request a specific Tweet:
203///
204/// ```rust,no_run
205/// use egg_mode::raw::auth::{RequestBuilder, Method};
206/// use egg_mode::raw::{ParamList, response_json};
207/// use egg_mode::Response;
208///
209/// # #[tokio::main]
210/// # async fn main() {
211/// # let token: egg_mode::Token = unimplemented!();
212/// let params = ParamList::new()
213///     .extended_tweets()
214///     .add_param("id", 1261253754969640960u64.to_string());
215/// let request = RequestBuilder::new(Method::GET, "https://api.twitter.com/1.1/statuses/show.json")
216///     .with_query_params(&params)
217///     .request_token(&token);
218/// let json: Response<serde_json::Value> = response_json(request).await.unwrap();
219/// # }
220/// ```
221///
222/// For more information, see the functions available on `RequestBuilder`.
223pub mod auth {
224    pub use crate::auth::raw::RequestBuilder;
225
226    #[doc(no_inline)]
227    pub use hyper::Method;
228}
229
230/// Types that can be used for deserialization from the raw API.
231///
232/// In cases where the types in egg-mode do not directly implement `Deserialize`, types are
233/// available here that represent the data sent "across the wire", which can be converted into
234/// regular egg-mode types. See the individual module docs for details.
235pub mod types {
236    pub mod direct;
237}