Skip to content

Commit 2098a54

Browse files
Use GraphQl to query all users (#120)
1 parent ef59ed8 commit 2098a54

File tree

6 files changed

+133
-37
lines changed

6 files changed

+133
-37
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ color-backtrace = "0.4.0"
1919
pretty_env_logger = "0.4.0"
2020
prettytable-rs = "0.8.0"
2121

22-
reqwest = { version = "0.10.3", features = ["blocking", "json", "gzip"] }
22+
reqwest = { version = "0.10.4", features = ["blocking", "json", "gzip"] }
2323
graphql_client = "0.9.0"
2424

2525
dirs = "2.0"

src/commands/show_users.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
use super::common;
22
use crate::github;
3-
use anyhow::{anyhow, Result};
3+
use anyhow::Result;
44
use structopt::StructOpt;
55

66
#[derive(Debug, StructOpt)]
77
/// Show all users in an organisation
88
pub struct ShowUsersArgs {
99
#[structopt(long, short, default_value = "divvun")]
1010
pub organisation: String,
11-
#[structopt(long, short, default_value = "all", parse(try_from_str = parse_role))]
12-
/// Filter members returned by their role.
13-
///
14-
/// Can be one of:
15-
/// * all - All members of the organization, regardless of role.
16-
/// * admin - Organization owners.
17-
/// * member - Non-owner organization members.
18-
pub role: String,
11+
//#[structopt(long, short, default_value = "all", parse(try_from_str = parse_role))]
12+
// Filter members returned by their role.
13+
//
14+
// Can be one of:
15+
// * all - All members of the organization, regardless of role.
16+
// * admin - Organization owners.
17+
// * member - Non-owner organization members.
18+
//pub role: String,
1919
}
2020

2121
impl ShowUsersArgs {
2222
pub fn run(&self) -> Result<()> {
2323
let user_token = common::user_token()?;
2424

25-
let result = github::get_org_members(&self.organisation, &self.role, &user_token);
25+
let result = github::get_org_members(&self.organisation, &user_token);
2626

2727
match result {
2828
Ok(users) => print_results(&users),
@@ -40,12 +40,12 @@ fn print_results(users: &[github::OrgMember]) {
4040
}
4141
}
4242

43-
fn parse_role(src: &str) -> Result<String> {
44-
let roles = ["all", "admin", "member"];
45-
let src = src.to_lowercase();
46-
if roles.contains(&src.as_str()) {
47-
return Ok(src);
48-
}
43+
//fn parse_role(src: &str) -> Result<String> {
44+
//let roles = ["all", "admin", "member"];
45+
//let src = src.to_lowercase();
46+
//if roles.contains(&src.as_str()) {
47+
//return Ok(src);
48+
//}
4949

50-
Err(anyhow!("role must be one of {:?}", roles))
51-
}
50+
//Err(anyhow!("role must be one of {:?}", roles))
51+
//}

src/github/graphql.rs

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ struct RepositoryDefaultBranch;
3838
)]
3939
struct OrganizationRepositoriesWithTopics;
4040

41+
#[derive(GraphQLQuery)]
42+
#[graphql(
43+
schema_path = "github.graphql",
44+
query_path = "user_query.graphql",
45+
response_derives = "Debug"
46+
)]
47+
struct OrganizationMembers;
48+
4149
fn query<T: Serialize + ?Sized>(token: &str, body: &T) -> Result<req::Response, reqwest::Error> {
4250
let client = req::Client::new();
4351
client
@@ -74,6 +82,74 @@ pub fn is_valid_token(token: &str) -> anyhow::Result<String> {
7482
Ok(username.to_string())
7583
}
7684

85+
#[derive(Debug)]
86+
pub struct OrgMember {
87+
pub login: String,
88+
pub url: String,
89+
//pub role: String,
90+
}
91+
92+
//#[derive(Debug)]
93+
//pub enum OrgRole {
94+
//Member,
95+
//Admin
96+
//}
97+
98+
pub fn get_org_members(org: &str, token: &str) -> anyhow::Result<Vec<OrgMember>> {
99+
get_org_members_rec(org, token, None)
100+
}
101+
102+
fn get_org_members_rec(
103+
org: &str,
104+
token: &str,
105+
after: Option<String>,
106+
) -> anyhow::Result<Vec<OrgMember>> {
107+
let q = OrganizationMembers::build_query(organization_members::Variables {
108+
login: org.to_string(),
109+
after,
110+
});
111+
112+
let res = query(token, &q)?;
113+
114+
let response_status = res.status();
115+
if response_status == reqwest::StatusCode::UNAUTHORIZED {
116+
return Err(Unauthorized.into());
117+
}
118+
119+
let response_body: Response<organization_members::ResponseData> = res.json()?;
120+
121+
let org_data = response_body
122+
.data
123+
.as_ref()
124+
.ok_or(InvalidRepoResponse)?
125+
.organization
126+
.as_ref()
127+
.ok_or(InvalidRepoResponse)?;
128+
129+
let members = org_data.members_with_role.nodes.as_ref();
130+
131+
let mut list_member: Vec<OrgMember> = members
132+
.ok_or(NoMembersFound)?
133+
.iter()
134+
.filter_map(|user| user.as_ref())
135+
.map(|x| OrgMember {
136+
login: x.login.to_string(),
137+
url: x.url.to_string(),
138+
})
139+
.collect();
140+
141+
let page_info = &org_data.members_with_role.page_info;
142+
143+
if page_info.has_next_page {
144+
let after = page_info.end_cursor.as_ref().map(|x| x.to_string());
145+
match get_org_members_rec(org, token, after) {
146+
Ok(mut l) => list_member.append(&mut l),
147+
Err(e) => return Err(e),
148+
}
149+
}
150+
Ok(list_member)
151+
}
152+
77153
fn list_org_repos_rec(
78154
token: &str,
79155
org: &str,

src/github/models.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ pub struct InvalidRepoResponse;
3636
#[error("no repositories found")]
3737
pub struct NoReposFound;
3838

39+
#[derive(thiserror::Error, Debug)]
40+
#[error("no members found")]
41+
pub struct NoMembersFound;
42+
3943
#[derive(thiserror::Error, Debug)]
4044
#[error("No default branch")]
4145
pub struct NoDefaultBranch;

src/github/rest.rs

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -677,30 +677,30 @@ struct SetRepoToTeamBody {
677677
}
678678

679679
// https://developer.github.com/v3/orgs/members/#members-list
680-
pub fn get_org_members(org: &str, role: &str, token: &str) -> Result<Vec<OrgMember>> {
681-
let url = format!("https://api.github.com/orgs/{}/members?role={}", org, role);
680+
//pub fn get_org_members(org: &str, role: &str, token: &str) -> Result<Vec<OrgMember>> {
681+
//let url = format!("https://api.github.com/orgs/{}/members?role={}", org, role);
682682

683-
let response = get(&url, token, None)?;
683+
//let response = get(&url, token, None)?;
684684

685-
let status = response.status();
685+
//let status = response.status();
686686

687-
if status == StatusCode::UNAUTHORIZED {
688-
return Err(models::Unauthorized.into());
689-
}
687+
//if status == StatusCode::UNAUTHORIZED {
688+
//return Err(models::Unauthorized.into());
689+
//}
690690

691-
if !status.is_success() {
692-
return Err(models::Unsuccessful(status).into());
693-
}
691+
//if !status.is_success() {
692+
//return Err(models::Unsuccessful(status).into());
693+
//}
694694

695-
let response_body: Vec<OrgMember> = response.json()?;
696-
Ok(response_body)
697-
}
695+
//let response_body: Vec<OrgMember> = response.json()?;
696+
//Ok(response_body)
697+
//}
698698

699-
#[derive(Deserialize, Debug)]
700-
pub struct OrgMember {
701-
pub login: String,
702-
pub url: String,
703-
}
699+
//#[derive(Deserialize, Debug)]
700+
//pub struct OrgMember {
701+
//pub login: String,
702+
//pub url: String,
703+
//}
704704

705705
fn process_response(response: &req::Response) -> Result<&req::Response> {
706706
let status = response.status();

user_query.graphql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,3 +54,19 @@ query OrganizationRepositoriesWithTopics($login: String!, $after: String) {
5454
}
5555
}
5656
}
57+
58+
query OrganizationMembers($login: String!, $after: String) {
59+
organization(login: $login) {
60+
membersWithRole(first: 30, after: $after) {
61+
nodes {
62+
login,
63+
email,
64+
url,
65+
}
66+
pageInfo {
67+
endCursor
68+
hasNextPage
69+
}
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)