diff --git a/Cargo.toml b/Cargo.toml index 88f01daf..8e8df6b5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,7 +35,7 @@ http = "0.2.5" http-body = "0.4.5" hyper = { version = "0.14.13", features = ["client", "http1", "stream", "tcp"] } hyper-rustls = { version = "0.24.0", optional = true } -hyper-timeout = {version = "0.4.1", optional = true } +hyper-timeout = { version = "0.4.1", optional = true } hyper-tls = { version = "0.5.0", optional = true } once_cell = "1.7.2" percent-encoding = "2.2.0" @@ -65,8 +65,9 @@ base64 = "0.21.2" pretty_assertions = "1.4.0" [features] -default = ["rustls", "timeout", "tracing", "retry"] +default = ["follow-redirect", "retry", "rustls", "timeout", "tracing"] +follow-redirect = ["tower-http/follow-redirect"] retry = ["tower/retry", "futures-util"] rustls = ["hyper-rustls"] rustls-webpki-tokio = ["hyper-rustls/webpki-tokio"] diff --git a/src/lib.rs b/src/lib.rs index 14b6a796..8339af66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -649,6 +649,9 @@ impl OctocrabBuilder ) .layer(client); + #[cfg(feature = "follow-redirect")] + let client = tower_http::follow_redirect::FollowRedirectLayer::new().layer(client); + let mut hmap: Vec<(HeaderName, HeaderValue)> = vec![]; // Add the user agent header required by GitHub diff --git a/tests/follow_redirect.rs b/tests/follow_redirect.rs new file mode 100644 index 00000000..95bc4e20 --- /dev/null +++ b/tests/follow_redirect.rs @@ -0,0 +1,78 @@ +// Tests for calls to the /repos/{owner}/{repo}/stargazers API. +mod mock_error; + +use mock_error::setup_error_handler; +use octocrab::{models::StarGazer, Octocrab, Page}; +use serde::{Deserialize, Serialize}; +use wiremock::{ + http::HeaderValue, + matchers::{method, path}, + Mock, MockServer, ResponseTemplate, +}; + +#[derive(Serialize, Deserialize)] +struct FakePage { + items: Vec, +} + +async fn setup_api(template: ResponseTemplate) -> MockServer { + let old_owner = "old-owner"; + let new_owner = "new-owner"; + let repo = "repo"; + let mock_server = MockServer::start().await; + Mock::given(method("GET")) + .and(path(format!("/repos/{old_owner}/{repo}/stargazers"))) + .respond_with( + ResponseTemplate::new(301).append_header( + "location", + HeaderValue::from_bytes( + format!("/repos/{new_owner}/{repo}/stargazers").into_bytes(), + ) + .unwrap(), + ), + ) + .mount(&mock_server) + .await; + Mock::given(method("GET")) + .and(path(format!("/repos/{new_owner}/{repo}/stargazers"))) + .respond_with(template) + .mount(&mock_server) + .await; + setup_error_handler( + &mock_server, + &format!("GET on /repo/{new_owner}/{repo}/stargazers was not received"), + ) + .await; + mock_server +} + +fn setup_octocrab(uri: &str) -> Octocrab { + Octocrab::builder().base_uri(uri).unwrap().build().unwrap() +} + +const OWNER: &str = "old-owner"; +const REPO: &str = "repo"; + +#[tokio::test] +#[cfg_attr(not(feature = "follow-redirect"), ignore)] +async fn should_return_page_with_users() { + let star_gazers: Vec = + serde_json::from_str(include_str!("resources/stargazers.json")).unwrap(); + let login1: String = star_gazers[0].user.as_ref().unwrap().login.clone(); + let page_response = FakePage { items: star_gazers }; + let template = ResponseTemplate::new(200).set_body_json(&page_response); + let mock_server = setup_api(template).await; + let client = setup_octocrab(&mock_server.uri()); + let repos = client.repos(OWNER.to_owned(), REPO.to_owned()); + let result = repos.list_stargazers().send().await; + assert!( + result.is_ok(), + "expected successful result, got error: {:#?}", + result + ); + let Page { items, .. } = result.unwrap(); + { + assert_eq!(items.len(), 3); + assert_eq!(items[0].user.as_ref().unwrap().login, login1); + } +}