Skip to content

Commit

Permalink
Put GithubRead behind a trait
Browse files Browse the repository at this point in the history
  • Loading branch information
Kobzol authored and Mark-Simulacrum committed Jan 20, 2024
1 parent 6bdaaf6 commit eaa9730
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 46 deletions.
2 changes: 1 addition & 1 deletion src/github/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use serde::{de::DeserializeOwned, Deserialize};
use std::borrow::Cow;
use std::fmt;

pub(crate) use read::GitHubRead;
pub(crate) use read::{GitHubApiRead, GithubRead};
pub(crate) use write::GitHubWrite;

#[derive(Clone)]
Expand Down
86 changes: 54 additions & 32 deletions src/github/api/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,58 @@ use crate::github::api::{
use reqwest::Method;
use std::collections::{HashMap, HashSet};

pub(crate) struct GitHubRead {
pub(crate) trait GithubRead {
/// Get user names by user ids
fn usernames(&self, ids: &[usize]) -> anyhow::Result<HashMap<usize, String>>;

/// Get the owners of an org
fn org_owners(&self, org: &str) -> anyhow::Result<HashSet<usize>>;

/// Get all teams associated with a org
///
/// Returns a list of tuples of team name and slug
fn org_teams(&self, org: &str) -> anyhow::Result<Vec<(String, String)>>;

/// Get the team by name and org
fn team(&self, org: &str, team: &str) -> anyhow::Result<Option<Team>>;

fn team_memberships(&self, team: &Team) -> anyhow::Result<HashMap<usize, TeamMember>>;

/// The GitHub names of users invited to the given team
fn team_membership_invitations(&self, org: &str, team: &str)
-> anyhow::Result<HashSet<String>>;

/// Get a repo by org and name
fn repo(&self, org: &str, repo: &str) -> anyhow::Result<Option<Repo>>;

/// Get teams in a repo
fn repo_teams(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoTeam>>;

/// Get collaborators in a repo
///
/// Only fetches those who are direct collaborators (i.e., not a collaborator through a repo team)
fn repo_collaborators(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoUser>>;

/// Get branch_protections
fn branch_protections(
&self,
org: &str,
repo: &str,
) -> anyhow::Result<HashMap<String, (String, BranchProtection)>>;
}

pub(crate) struct GitHubApiRead {
client: HttpClient,
}

impl GitHubRead {
impl GitHubApiRead {
pub(crate) fn from_client(client: HttpClient) -> anyhow::Result<Self> {
Ok(Self { client })
}
}

/// Get user names by user ids
pub(crate) fn usernames(&self, ids: &[usize]) -> anyhow::Result<HashMap<usize, String>> {
impl GithubRead for GitHubApiRead {
fn usernames(&self, ids: &[usize]) -> anyhow::Result<HashMap<usize, String>> {
#[derive(serde::Deserialize)]
#[serde(rename_all = "camelCase")]
struct Usernames {
Expand Down Expand Up @@ -52,8 +93,7 @@ impl GitHubRead {
Ok(result)
}

/// Get the owners of an org
pub(crate) fn org_owners(&self, org: &str) -> anyhow::Result<HashSet<usize>> {
fn org_owners(&self, org: &str) -> anyhow::Result<HashSet<usize>> {
#[derive(serde::Deserialize, Eq, PartialEq, Hash)]
struct User {
id: usize,
Expand All @@ -70,10 +110,7 @@ impl GitHubRead {
Ok(owners)
}

/// Get all teams associated with a org
///
/// Returns a list of tuples of team name and slug
pub(crate) fn org_teams(&self, org: &str) -> anyhow::Result<Vec<(String, String)>> {
fn org_teams(&self, org: &str) -> anyhow::Result<Vec<(String, String)>> {
let mut teams = Vec::new();

self.client.rest_paginated(
Expand All @@ -88,16 +125,12 @@ impl GitHubRead {
Ok(teams)
}

/// Get the team by name and org
pub(crate) fn team(&self, org: &str, team: &str) -> anyhow::Result<Option<Team>> {
fn team(&self, org: &str, team: &str) -> anyhow::Result<Option<Team>> {
self.client
.send_option(Method::GET, &format!("orgs/{org}/teams/{team}"))
}

pub(crate) fn team_memberships(
&self,
team: &Team,
) -> anyhow::Result<HashMap<usize, TeamMember>> {
fn team_memberships(&self, team: &Team) -> anyhow::Result<HashMap<usize, TeamMember>> {
#[derive(serde::Deserialize)]
struct RespTeam {
members: RespMembers,
Expand Down Expand Up @@ -176,8 +209,7 @@ impl GitHubRead {
Ok(memberships)
}

/// The GitHub names of users invited to the given team
pub(crate) fn team_membership_invitations(
fn team_membership_invitations(
&self,
org: &str,
team: &str,
Expand All @@ -196,14 +228,12 @@ impl GitHubRead {
Ok(invites)
}

/// Get a repo by org and name
pub(crate) fn repo(&self, org: &str, repo: &str) -> anyhow::Result<Option<Repo>> {
fn repo(&self, org: &str, repo: &str) -> anyhow::Result<Option<Repo>> {
self.client
.send_option(Method::GET, &format!("repos/{org}/{repo}"))
}

/// Get teams in a repo
pub(crate) fn repo_teams(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoTeam>> {
fn repo_teams(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoTeam>> {
let mut teams = Vec::new();

self.client.rest_paginated(
Expand All @@ -218,14 +248,7 @@ impl GitHubRead {
Ok(teams)
}

/// Get collaborators in a repo
///
/// Only fetches those who are direct collaborators (i.e., not a collaborator through a repo team)
pub(crate) fn repo_collaborators(
&self,
org: &str,
repo: &str,
) -> anyhow::Result<Vec<RepoUser>> {
fn repo_collaborators(&self, org: &str, repo: &str) -> anyhow::Result<Vec<RepoUser>> {
let mut users = Vec::new();

self.client.rest_paginated(
Expand All @@ -240,8 +263,7 @@ impl GitHubRead {
Ok(users)
}

/// Get branch_protections
pub(crate) fn branch_protections(
fn branch_protections(
&self,
org: &str,
repo: &str,
Expand Down
13 changes: 9 additions & 4 deletions src/github/api/write.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use anyhow::Context;
use log::debug;
use reqwest::Method;
use std::rc::Rc;

use crate::github::api::read::GitHubRead;
use crate::github::api::read::GithubRead;
use crate::github::api::{
allow_not_found, BranchProtection, BranchProtectionOp, HttpClient, Login, PushAllowanceActor,
Repo, RepoPermission, Team, TeamPrivacy, TeamPushAllowanceActor, TeamRole,
Expand All @@ -13,15 +14,19 @@ use crate::utils::ResponseExt;
pub(crate) struct GitHubWrite {
client: HttpClient,
dry_run: bool,
read: GitHubRead,
read: Rc<dyn GithubRead>,
}

impl GitHubWrite {
pub(crate) fn new(client: HttpClient, dry_run: bool) -> anyhow::Result<Self> {
pub(crate) fn new(
client: HttpClient,
read: Rc<dyn GithubRead>,
dry_run: bool,
) -> anyhow::Result<Self> {
Ok(Self {
client: client.clone(),
dry_run,
read: GitHubRead::from_client(client)?,
read,
})
}

Expand Down
11 changes: 6 additions & 5 deletions src/github/mod.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
mod api;

use self::api::{BranchProtectionOp, TeamPrivacy, TeamRole};
use crate::github::api::RepoPermission;
use crate::github::api::{GithubRead, RepoPermission};
use log::debug;
use rust_team_data::v1::Bot;
use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use std::rc::Rc;

pub(crate) use self::api::{GitHubRead, GitHubWrite, HttpClient};
pub(crate) use self::api::{GitHubApiRead, GitHubWrite, HttpClient};

static DEFAULT_DESCRIPTION: &str = "Managed by the rust-lang/team repository.";
static DEFAULT_PRIVACY: TeamPrivacy = TeamPrivacy::Closed;

pub(crate) fn create_diff(
github: GitHubRead,
github: Rc<dyn GithubRead>,
teams: Vec<rust_team_data::v1::Team>,
repos: Vec<rust_team_data::v1::Repo>,
) -> anyhow::Result<Diff> {
Expand All @@ -22,7 +23,7 @@ pub(crate) fn create_diff(
}

struct SyncGitHub {
github: GitHubRead,
github: Rc<dyn GithubRead>,
teams: Vec<rust_team_data::v1::Team>,
repos: Vec<rust_team_data::v1::Repo>,
usernames_cache: HashMap<usize, String>,
Expand All @@ -31,7 +32,7 @@ struct SyncGitHub {

impl SyncGitHub {
pub(crate) fn new(
github: GitHubRead,
github: Rc<dyn GithubRead>,
teams: Vec<rust_team_data::v1::Team>,
repos: Vec<rust_team_data::v1::Repo>,
) -> anyhow::Result<Self> {
Expand Down
9 changes: 5 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ mod team_api;
mod utils;
mod zulip;

use crate::github::{create_diff, GitHubRead, GitHubWrite, HttpClient};
use crate::github::{create_diff, GitHubApiRead, GitHubWrite, HttpClient};
use crate::team_api::TeamApi;
use crate::zulip::SyncZulip;
use anyhow::Context;
use log::{error, info, warn};
use std::rc::Rc;

const AVAILABLE_SERVICES: &[&str] = &["github", "mailgun", "zulip"];
const USER_AGENT: &str = "rust-lang teams sync (https://github.com/rust-lang/sync-team)";
Expand Down Expand Up @@ -84,13 +85,13 @@ fn app() -> anyhow::Result<()> {
let token = get_env("GITHUB_TOKEN")?;
let client =
HttpClient::from_url_and_token("https://api.github.com/".to_string(), token)?;
let gh_read = GitHubRead::from_client(client.clone())?;
let gh_read = Rc::new(GitHubApiRead::from_client(client.clone())?);
let teams = team_api.get_teams()?;
let repos = team_api.get_repos()?;
let diff = create_diff(gh_read, teams, repos)?;
let diff = create_diff(gh_read.clone(), teams, repos)?;
info!("{}", diff);
if !only_print_plan {
let gh_write = GitHubWrite::new(client, dry_run)?;
let gh_write = GitHubWrite::new(client, gh_read, dry_run)?;
diff.apply(&gh_write)?;
}
}
Expand Down

0 comments on commit eaa9730

Please sign in to comment.