tmdb_api/changes/
list.rs

1use std::borrow::Cow;
2
3use chrono::NaiveDate;
4
5use crate::client::Executor;
6
7const TV_PATH: &str = "/tv/changes";
8const MOVIE_PATH: &str = "/movie/changes";
9const PERSON_PATH: &str = "/person/changes";
10
11/// Command to list changes
12///
13/// ```rust
14/// use tmdb_api::prelude::Command;
15/// use tmdb_api::Client;
16/// use tmdb_api::client::reqwest::ReqwestExecutor;
17/// use tmdb_api::changes::list::ChangeList;
18///
19/// #[tokio::main]
20/// async fn main() {
21///     let client = Client::<ReqwestExecutor>::new("this-is-my-secret-token".into());
22///     let cmd = ChangeList::tv();
23///     let result = cmd.execute(&client).await;
24///     match result {
25///         Ok(res) => println!("found: {:#?}", res),
26///         Err(err) => eprintln!("error: {:?}", err),
27///     };
28/// }
29/// ```
30#[derive(Clone, Debug, Default)]
31pub struct ChangeList {
32    path: &'static str,
33    /// Filter the results with a start date.
34    pub start_date: Option<NaiveDate>,
35    /// Filter the results with an end date.
36    pub end_date: Option<NaiveDate>,
37    /// Which page to query.
38    pub page: Option<u32>,
39}
40
41impl ChangeList {
42    pub fn tv() -> Self {
43        Self {
44            path: TV_PATH,
45            start_date: None,
46            end_date: None,
47            page: None,
48        }
49    }
50
51    pub fn movie() -> Self {
52        Self {
53            path: MOVIE_PATH,
54            start_date: None,
55            end_date: None,
56            page: None,
57        }
58    }
59
60    pub fn person() -> Self {
61        Self {
62            path: PERSON_PATH,
63            start_date: None,
64            end_date: None,
65            page: None,
66        }
67    }
68
69    pub fn with_start_date(mut self, value: Option<NaiveDate>) -> Self {
70        self.start_date = value;
71        self
72    }
73
74    pub fn with_end_date(mut self, value: Option<NaiveDate>) -> Self {
75        self.end_date = value;
76        self
77    }
78
79    pub fn with_page(mut self, value: Option<u32>) -> Self {
80        self.page = value;
81        self
82    }
83}
84
85impl crate::prelude::Command for ChangeList {
86    type Output = crate::common::PaginatedResult<super::Change>;
87
88    fn path(&self) -> Cow<'static, str> {
89        Cow::Borrowed(self.path)
90    }
91
92    fn params(&self) -> Vec<(&'static str, Cow<'_, str>)> {
93        let mut res = Vec::with_capacity(3);
94        if let Some(ref start_date) = self.start_date {
95            res.push(("start_date", Cow::Owned(start_date.to_string())));
96        }
97        if let Some(ref end_date) = self.end_date {
98            res.push(("end_date", Cow::Owned(end_date.to_string())));
99        }
100        if let Some(page) = self.page {
101            res.push(("page", Cow::Owned(page.to_string())));
102        }
103        res
104    }
105
106    async fn execute<E: Executor>(
107        &self,
108        client: &crate::Client<E>,
109    ) -> Result<Self::Output, crate::error::Error> {
110        client.execute(self.path().as_ref(), self.params()).await
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::ChangeList;
117    use crate::client::Client;
118    use crate::client::reqwest::ReqwestExecutor;
119    use crate::prelude::Command;
120    use chrono::NaiveDate;
121    use mockito::Matcher;
122
123    #[tokio::test]
124    async fn tv_works() {
125        let mut server = mockito::Server::new_async().await;
126        let client = Client::<ReqwestExecutor>::builder()
127            .with_api_key("secret".into())
128            .with_base_url(server.url())
129            .build()
130            .unwrap();
131        let cmd = ChangeList::tv();
132
133        let _m = server
134            .mock("GET", super::TV_PATH)
135            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
136            .with_status(200)
137            .with_header("content-type", "application/json")
138            .with_body(include_str!("../../assets/tv-all-changes.json"))
139            .create_async()
140            .await;
141        let result = cmd.execute(&client).await.unwrap();
142        assert_eq!(result.page, 1);
143    }
144
145    #[tokio::test]
146    async fn tv_works_with_args() {
147        let mut server = mockito::Server::new_async().await;
148        let client = Client::<ReqwestExecutor>::builder()
149            .with_api_key("secret".into())
150            .with_base_url(server.url())
151            .build()
152            .unwrap();
153
154        let _m = server
155            .mock("GET", super::TV_PATH)
156            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
157            .match_query(Matcher::AllOf(vec![
158                Matcher::UrlEncoded("api_key".into(), "secret".into()),
159                Matcher::UrlEncoded("start_date".into(), "2015-03-14".into()),
160                Matcher::UrlEncoded("end_date".into(), "2019-03-14".into()),
161                Matcher::UrlEncoded("page".into(), "2".into()),
162            ]))
163            .with_status(200)
164            .with_header("content-type", "application/json")
165            .with_body(include_str!("../../assets/tv-all-changes.json"))
166            .create_async()
167            .await;
168
169        let result = ChangeList::tv()
170            .with_start_date(Some(NaiveDate::from_ymd_opt(2015, 3, 14).unwrap()))
171            .with_end_date(Some(NaiveDate::from_ymd_opt(2019, 3, 14).unwrap()))
172            .with_page(Some(2))
173            .execute(&client)
174            .await
175            .unwrap();
176        assert_eq!(result.page, 1);
177    }
178
179    #[tokio::test]
180    async fn movie_works() {
181        let mut server = mockito::Server::new_async().await;
182        let client = Client::<ReqwestExecutor>::builder()
183            .with_api_key("secret".into())
184            .with_base_url(server.url())
185            .build()
186            .unwrap();
187        let cmd = ChangeList::movie();
188
189        let _m = server
190            .mock("GET", super::MOVIE_PATH)
191            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
192            .with_status(200)
193            .with_header("content-type", "application/json")
194            .with_body(include_str!("../../assets/movie-all-changes.json"))
195            .create_async()
196            .await;
197        let result = cmd.execute(&client).await.unwrap();
198        assert_eq!(result.page, 1);
199    }
200
201    #[tokio::test]
202    async fn person_works() {
203        let mut server = mockito::Server::new_async().await;
204        let client = Client::<ReqwestExecutor>::builder()
205            .with_api_key("secret".into())
206            .with_base_url(server.url())
207            .build()
208            .unwrap();
209        let cmd = ChangeList::person();
210
211        let _m = server
212            .mock("GET", super::PERSON_PATH)
213            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
214            .with_status(200)
215            .with_header("content-type", "application/json")
216            .with_body(include_str!("../../assets/movie-all-changes.json"))
217            .create_async()
218            .await;
219        let result = cmd.execute(&client).await.unwrap();
220        assert_eq!(result.page, 1);
221    }
222
223    #[tokio::test]
224    async fn invalid_api_key() {
225        let mut server = mockito::Server::new_async().await;
226        let client = Client::<ReqwestExecutor>::builder()
227            .with_api_key("secret".into())
228            .with_base_url(server.url())
229            .build()
230            .unwrap();
231        let cmd = ChangeList::tv();
232
233        let _m = server
234            .mock("GET", super::TV_PATH)
235            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
236            .with_status(401)
237            .with_header("content-type", "application/json")
238            .with_body(include_str!("../../assets/invalid-api-key.json"))
239            .create_async()
240            .await;
241        let err = cmd.execute(&client).await.unwrap_err();
242        let server_err = err.as_server_error().unwrap();
243        assert_eq!(server_err.status_code, 7);
244    }
245
246    #[tokio::test]
247    async fn resource_not_found() {
248        let mut server = mockito::Server::new_async().await;
249        let client = Client::<ReqwestExecutor>::builder()
250            .with_api_key("secret".into())
251            .with_base_url(server.url())
252            .build()
253            .unwrap();
254        let cmd = ChangeList::tv();
255
256        let _m = server
257            .mock("GET", super::TV_PATH)
258            .match_query(Matcher::UrlEncoded("api_key".into(), "secret".into()))
259            .with_status(404)
260            .with_header("content-type", "application/json")
261            .with_body(include_str!("../../assets/resource-not-found.json"))
262            .create_async()
263            .await;
264        let err = cmd.execute(&client).await.unwrap_err();
265        let server_err = err.as_server_error().unwrap();
266        assert_eq!(server_err.status_code, 34);
267    }
268}
269
270#[cfg(all(test, feature = "integration"))]
271mod integration_tests {
272    use super::ChangeList;
273    use crate::client::Client;
274    use crate::client::reqwest::ReqwestExecutor;
275    use crate::prelude::Command;
276
277    #[tokio::test]
278    async fn execute_tv() {
279        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
280        let client = Client::<ReqwestExecutor>::new(secret);
281
282        let result = ChangeList::tv().execute(&client).await.unwrap();
283        assert_eq!(result.page, 1);
284    }
285
286    #[tokio::test]
287    async fn execute_movie() {
288        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
289        let client = Client::<ReqwestExecutor>::new(secret);
290
291        let result = ChangeList::movie().execute(&client).await.unwrap();
292        assert_eq!(result.page, 1);
293    }
294
295    #[tokio::test]
296    async fn execute_person() {
297        let secret = std::env::var("TMDB_TOKEN_V3").unwrap();
298        let client = Client::<ReqwestExecutor>::new(secret);
299
300        let result = ChangeList::person().execute(&client).await.unwrap();
301        assert_eq!(result.page, 1);
302    }
303}