Skip to content

Commit

Permalink
Merge pull request #191 from grahamc/team-management
Browse files Browse the repository at this point in the history
Teams: create/update/edit teams + members
  • Loading branch information
softprops committed Jan 4, 2019
2 parents 4bada70 + 485df83 commit e11d3a1
Show file tree
Hide file tree
Showing 2 changed files with 176 additions and 14 deletions.
72 changes: 59 additions & 13 deletions examples/teams.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
extern crate pretty_env_logger;
extern crate futures;
extern crate hubcaps;
extern crate pretty_env_logger;
extern crate tokio;

use std::env;

use futures::Stream;
use tokio::runtime::Runtime;

use hubcaps::teams::{TeamMemberOptions, TeamMemberRole, TeamOptions};
use hubcaps::{Credentials, Github, Result};

fn main() -> Result<()> {
Expand All @@ -19,22 +20,67 @@ fn main() -> Result<()> {
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
Credentials::Token(token),
);

let org = "eb6cb83a-cf75-4e88-a11a-ce117467d8ae";
let repo_name = "d18e3679-9830-40a9-8cf5-16602639b43e";

println!("org teams");
rt.block_on(github.org("meetup").teams().iter().for_each(|team| {
rt.block_on(github.org(org).teams().iter().for_each(|team| {
println!("{:#?}", team);
Ok(())
}))?;
}))
.unwrap_or_else(|e| println!("error: {:#?}", e));

println!("repo teams");
rt.block_on(
github
.repo("meetup", "k8s-nginx-dogstats")
.teams()
.iter()
.for_each(|team| {
println!("{:#?}", team);
Ok(())
}),
)?;
rt.block_on(github.repo(org, repo_name).teams().iter().for_each(|team| {
println!("{:#?}", team);
Ok(())
}))
.unwrap_or_else(|e| println!("error: {:#?}", e));

let new_team = rt.block_on(github.org(org).teams().create(&TeamOptions {
name: String::from("hi"),
description: Some(String::from("there")),
permission: None,
privacy: Some(String::from("secret")),
}))?;
println!("Created team: {:#?}", new_team);

let team = github.org(org).teams().get(new_team.id);

let updated_team = rt.block_on(team.update(&TeamOptions {
name: String::from("hello"),
description: None,
permission: None,
privacy: None,
}))?;
println!("Updated team: {:#?}", updated_team);

println!(
"Adding grahamc to the team: {:#?}",
rt.block_on(team.add_user(
"grahamc",
TeamMemberOptions {
role: TeamMemberRole::Member,
}
))
);

println!("members:");
rt.block_on(team.iter_members().for_each(|member| {
println!("{:#?}", member);
Ok(())
}))
.unwrap_or_else(|e| println!("error: {:#?}", e));

println!(
"Removing grahamc from the team: {:#?}",
rt.block_on(team.remove_user("grahamc"))
);

let deleted_team = rt.block_on(team.delete())?;
println!("Deleted team: {:#?}", deleted_team);

Ok(())
}
_ => Err("example missing GITHUB_TOKEN".into()),
Expand Down
118 changes: 117 additions & 1 deletion src/teams/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use std::fmt;
use hyper::client::connect::Connect;
use serde_json;

use users::User;
use {unfold, Future, Github, Stream};

/// Team repository permissions
Expand Down Expand Up @@ -97,6 +98,18 @@ impl<C: Clone + Connect + 'static> OrgTeams<C> {
self.github.get(&format!("/orgs/{}/teams", self.org))
}

/// Get a reference to a structure for interfacing with a specific
/// team
pub fn get(&self, number: u64) -> OrgTeamActions<C> {
OrgTeamActions::new(self.github.clone(), number)
}

/// create team
pub fn create(&self, team_options: &TeamOptions) -> Future<Team> {
self.github
.post(&format!("/orgs/{}/teams", self.org), json!(team_options))
}

/// provides an iterator over all pages of teams
pub fn iter(&self) -> Stream<Team> {
unfold(
Expand Down Expand Up @@ -124,8 +137,100 @@ impl<C: Clone + Connect + 'static> OrgTeams<C> {
}
}

/// reference to teams associated with a github org
pub struct OrgTeamActions<C>
where
C: Clone + Connect + 'static,
{
github: Github<C>,
number: u64,
}

impl<C: Clone + Connect + 'static> OrgTeamActions<C> {
#[doc(hidden)]
pub fn new(github: Github<C>, number: u64) -> Self {
OrgTeamActions { github, number }
}

fn path(&self, suffix: &str) -> String {
format!("/teams/{}{}", self.number, suffix)
}

/// list the team
pub fn get(&self) -> Future<Team> {
self.github.get(&self.path(""))
}

/// edit the team
pub fn update(&self, team_options: &TeamOptions) -> Future<Team> {
self.github.patch(&self.path(""), json!(team_options))
}

/// delete the team
pub fn delete(&self) -> Future<()> {
self.github.delete(&self.path(""))
}

/// list of teams for this org
pub fn list_members(&self) -> Future<Vec<User>> {
self.github.get(&self.path("/members"))
}

/// provides an iterator over all pages of members
pub fn iter_members(&self) -> Stream<User> {
unfold(
self.github.clone(),
self.github.get_pages(&self.path("/members")),
identity,
)
}

/// add a user to the team, if they are already on the team,
/// change the role. If the user is not yet part of the
/// organization, they are invited to join.
pub fn add_user(&self, user: &str, user_options: TeamMemberOptions) -> Future<TeamMember> {
self.github.put(
&self.path(&format!("/memberships/{}", user)),
json!(user_options),
)
}

/// Remove the user from the team
pub fn remove_user(&self, user: &str) -> Future<()> {
self.github
.delete(&self.path(&format!("/memberships/{}", user)))
}
}

// representations (todo: replace with derive_builder)

#[derive(Debug, Deserialize)]
pub struct TeamMember {
pub url: String,
pub role: TeamMemberRole,
pub state: TeamMemberState,
}

#[derive(Debug, Serialize)]
pub struct TeamMemberOptions {
pub role: TeamMemberRole,
}

#[derive(Debug, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum TeamMemberRole {
Member,
Maintainer,
}

#[derive(Debug, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum TeamMemberState {
Active,
Pending,
}

/// Representation of a specific team
#[derive(Debug, Deserialize)]
pub struct Team {
pub id: u64,
Expand All @@ -134,7 +239,18 @@ pub struct Team {
pub slug: String,
pub description: Option<String>,
pub privacy: String,
pub permission: String,
pub members_url: String,
pub repositories_url: String,
pub permission: String,
}

#[derive(Debug, Serialize)]
pub struct TeamOptions {
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub privacy: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permission: Option<String>,
}

0 comments on commit e11d3a1

Please sign in to comment.