Browse Source

Move team-data API access to separate module

This also adds retries (up to three total requests) if we timeout on connection.
It's possible we should be less restrictive and always retry, but the hope is
that timeouts are sufficient. This may not cover (for example) a "connection
reset by peer" error though, even if fundamentally that's also a timeout, just
of a different kind.
Mark Rousskov 5 years ago
parent
commit
12a2b3cb58
4 changed files with 43 additions and 25 deletions
  1. 3 15
      src/github.rs
  2. 1 0
      src/lib.rs
  3. 37 0
      src/team_data.rs
  4. 2 10
      src/zulip.rs

+ 3 - 15
src/github.rs

@@ -56,11 +56,7 @@ impl User {
     }
     }
 
 
     pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result<bool> {
     pub async fn is_team_member<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result<bool> {
-        let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL);
-        let permission: rust_team_data::v1::Teams = client
-            .json(client.raw().get(&url))
-            .await
-            .context("could not get team data")?;
+        let permission = crate::team_data::teams(client).await?;
         let map = permission.teams;
         let map = permission.teams;
         let is_triager = map
         let is_triager = map
             .get("wg-triage")
             .get("wg-triage")
@@ -70,11 +66,7 @@ impl User {
 
 
     // Returns the ID of the given user, if the user is in the `all` team.
     // Returns the ID of the given user, if the user is in the `all` team.
     pub async fn get_id<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result<Option<usize>> {
     pub async fn get_id<'a>(&'a self, client: &'a GithubClient) -> anyhow::Result<Option<usize>> {
-        let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL);
-        let permission: rust_team_data::v1::Teams = client
-            .json(client.raw().get(&url))
-            .await
-            .context("could not get team data")?;
+        let permission = crate::team_data::teams(client).await?;
         let map = permission.teams;
         let map = permission.teams;
         Ok(map["all"]
         Ok(map["all"]
             .members
             .members
@@ -88,11 +80,7 @@ pub async fn get_team(
     client: &GithubClient,
     client: &GithubClient,
     team: &str,
     team: &str,
 ) -> anyhow::Result<Option<rust_team_data::v1::Team>> {
 ) -> anyhow::Result<Option<rust_team_data::v1::Team>> {
-    let url = format!("{}/teams.json", rust_team_data::v1::BASE_URL);
-    let permission: rust_team_data::v1::Teams = client
-        .json(client.raw().get(&url))
-        .await
-        .context("could not get team data")?;
+    let permission = crate::team_data::teams(client).await?;
     let mut map = permission.teams;
     let mut map = permission.teams;
     Ok(map.swap_remove(team))
     Ok(map.swap_remove(team))
 }
 }

+ 1 - 0
src/lib.rs

@@ -13,6 +13,7 @@ pub mod interactions;
 pub mod notification_listing;
 pub mod notification_listing;
 pub mod payload;
 pub mod payload;
 pub mod team;
 pub mod team;
+mod team_data;
 pub mod zulip;
 pub mod zulip;
 
 
 #[derive(Debug)]
 #[derive(Debug)]

+ 37 - 0
src/team_data.rs

@@ -0,0 +1,37 @@
+use crate::github::GithubClient;
+use anyhow::Context as _;
+use rust_team_data::v1::{Teams, ZulipMapping, BASE_URL};
+use serde::de::DeserializeOwned;
+
+async fn by_url<T: DeserializeOwned>(client: &GithubClient, path: &str) -> anyhow::Result<T> {
+    let url = format!("{}{}", BASE_URL, path);
+    for _ in 0i32..3 {
+        let map: Result<T, _> = client.json(client.raw().get(&url)).await;
+        match map {
+            Ok(v) => return Ok(v),
+            Err(e) => {
+                if e.downcast_ref::<reqwest::Error>()
+                    .map_or(false, |e| e.is_timeout())
+                {
+                    continue;
+                } else {
+                    return Err(e);
+                }
+            }
+        }
+    }
+
+    Err(anyhow::anyhow!("Failed to retrieve {} in 3 requests", url))
+}
+
+pub async fn zulip_map(client: &GithubClient) -> anyhow::Result<ZulipMapping> {
+    by_url(client, "/zulip-map.json")
+        .await
+        .context("team-api: zulip-map.json")
+}
+
+pub async fn teams(client: &GithubClient) -> anyhow::Result<Teams> {
+    by_url(client, "/teams.json")
+        .await
+        .context("team-api: teams.json")
+}

+ 2 - 10
src/zulip.rs

@@ -31,20 +31,12 @@ struct Response<'a> {
 }
 }
 
 
 pub async fn to_github_id(client: &GithubClient, zulip_id: usize) -> anyhow::Result<Option<i64>> {
 pub async fn to_github_id(client: &GithubClient, zulip_id: usize) -> anyhow::Result<Option<i64>> {
-    let url = format!("{}/zulip-map.json", rust_team_data::v1::BASE_URL);
-    let map: rust_team_data::v1::ZulipMapping = client
-        .json(client.raw().get(&url))
-        .await
-        .context("could not get team data")?;
+    let map = crate::team_data::zulip_map(client).await?;
     Ok(map.users.get(&zulip_id).map(|v| *v as i64))
     Ok(map.users.get(&zulip_id).map(|v| *v as i64))
 }
 }
 
 
 pub async fn to_zulip_id(client: &GithubClient, github_id: i64) -> anyhow::Result<Option<usize>> {
 pub async fn to_zulip_id(client: &GithubClient, github_id: i64) -> anyhow::Result<Option<usize>> {
-    let url = format!("{}/zulip-map.json", rust_team_data::v1::BASE_URL);
-    let map: rust_team_data::v1::ZulipMapping = client
-        .json(client.raw().get(&url))
-        .await
-        .context("could not get team data")?;
+    let map = crate::team_data::zulip_map(client).await?;
     Ok(map
     Ok(map
         .users
         .users
         .iter()
         .iter()