Skip to content

Commit a01095b

Browse files
authored
feat: add get and update endpoints for org members (#150)
* feat: expose org membership endpoints * feat: test org endpoints * feat: update comments * feat: update tests
1 parent 18929c7 commit a01095b

File tree

5 files changed

+210
-0
lines changed

5 files changed

+210
-0
lines changed

src/main/java/com/spotify/github/v3/clients/OrganisationClient.java

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,19 @@
2020
//
2121
package com.spotify.github.v3.clients;
2222

23+
import com.spotify.github.v3.orgs.OrgMembership;
24+
import com.spotify.github.v3.orgs.requests.OrgMembershipCreate;
25+
import java.lang.invoke.MethodHandles;
26+
import java.util.concurrent.CompletableFuture;
27+
import org.slf4j.Logger;
28+
import org.slf4j.LoggerFactory;
29+
2330
public class OrganisationClient {
2431

32+
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
33+
34+
private static final String MEMBERSHIP_TEMPLATE = "/orgs/%s/memberships/%s";
35+
2536
private final GitHubClient github;
2637

2738
private final String org;
@@ -52,4 +63,28 @@ public TeamClient createTeamClient() {
5263
public GithubAppClient createGithubAppClient() {
5364
return new GithubAppClient(github, org);
5465
}
66+
67+
/**
68+
* Get an org membership of a user.
69+
*
70+
* @param username username of the org member
71+
* @return membership
72+
*/
73+
public CompletableFuture<OrgMembership> getOrgMembership(final String username) {
74+
final String path = String.format(MEMBERSHIP_TEMPLATE, org, username);
75+
log.debug("Fetching org membership for: " + path);
76+
return github.request(path, OrgMembership.class);
77+
}
78+
79+
/**
80+
* Add or update an org membership for a user.
81+
*
82+
* @param request update org membership request
83+
* @return membership
84+
*/
85+
public CompletableFuture<OrgMembership> updateOrgMembership(final OrgMembershipCreate request, final String username) {
86+
final String path = String.format(MEMBERSHIP_TEMPLATE, org, username);
87+
log.debug("Updating membership in org: " + path);
88+
return github.put(path, github.json().toJsonUnchecked(request), OrgMembership.class);
89+
}
5590
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2016 - 2020 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github.v3.orgs;
22+
23+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
25+
import com.spotify.github.GithubStyle;
26+
import com.spotify.github.v3.User;
27+
import com.spotify.github.v3.repos.Organization;
28+
import java.net.URI;
29+
import javax.annotation.Nullable;
30+
import org.immutables.value.Value;
31+
32+
/**
33+
* Org Membership resource represents data returned by a single Membership get operation.
34+
*/
35+
@Value.Immutable
36+
@GithubStyle
37+
@JsonSerialize(as = ImmutableOrgMembership.class)
38+
@JsonDeserialize(as = ImmutableOrgMembership.class)
39+
public interface OrgMembership {
40+
41+
/** URL */
42+
@Nullable
43+
URI url();
44+
45+
/** ROLE */
46+
@Nullable
47+
String role();
48+
49+
/** STATE */
50+
@Nullable
51+
String state();
52+
53+
@Nullable
54+
Organization organization();
55+
56+
/** USER */
57+
@Nullable
58+
User user();
59+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*-
2+
* -\-\-
3+
* github-api
4+
* --
5+
* Copyright (C) 2016 - 2020 Spotify AB
6+
* --
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* -/-/-
19+
*/
20+
21+
package com.spotify.github.v3.orgs.requests;
22+
23+
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
24+
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
25+
import com.spotify.github.GithubStyle;
26+
import javax.annotation.Nullable;
27+
import org.immutables.value.Value;
28+
29+
/** Request to create a member within a given org */
30+
@Value.Immutable
31+
@GithubStyle
32+
@JsonSerialize(as = ImmutableOrgMembershipCreate.class)
33+
@JsonDeserialize(as = ImmutableOrgMembershipCreate.class)
34+
public interface OrgMembershipCreate {
35+
36+
/**
37+
* The role that this user should have in the org.
38+
* Defaults to 'member'
39+
*/
40+
@Nullable
41+
String role();
42+
}

src/test/java/com/spotify/github/v3/clients/OrganisationClientTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@
2525
import static java.util.concurrent.CompletableFuture.completedFuture;
2626
import static org.hamcrest.MatcherAssert.assertThat;
2727
import static org.hamcrest.core.Is.is;
28+
import static org.mockito.ArgumentMatchers.any;
29+
import static org.mockito.ArgumentMatchers.eq;
2830
import static org.mockito.Mockito.mock;
2931
import static org.mockito.Mockito.when;
3032

3133
import com.google.common.io.Resources;
3234
import com.spotify.github.jackson.Json;
3335
import com.spotify.github.v3.Team;
3436
import com.spotify.github.v3.checks.Installation;
37+
import com.spotify.github.v3.orgs.OrgMembership;
38+
import com.spotify.github.v3.orgs.requests.OrgMembershipCreate;
3539
import java.io.IOException;
3640
import java.util.concurrent.CompletableFuture;
3741
import org.junit.Before;
@@ -78,4 +82,34 @@ public void testAppClient() throws Exception {
7882
assertThat(installation.id(), is(1));
7983
assertThat(installation.account().login(), is("github"));
8084
}
85+
86+
@Test
87+
public void getOrgMembership() throws Exception {
88+
final CompletableFuture<OrgMembership> fixture =
89+
completedFuture(json.fromJson(getFixture("org_membership.json"), OrgMembership.class));
90+
when(github.request("/orgs/github/memberships/octocat", OrgMembership.class))
91+
.thenReturn(fixture);
92+
final OrgMembership orgMembership = organisationClient.getOrgMembership("octocat").get();
93+
assertThat(
94+
orgMembership.url().toString(), is("https://api.github.com/orgs/github/memberships/octocat"));
95+
assertThat(orgMembership.role(), is("member"));
96+
assertThat(orgMembership.state(), is("active"));
97+
}
98+
99+
@Test
100+
public void updateMembership() throws Exception {
101+
final OrgMembershipCreate orgMembershipCreateRequest =
102+
json.fromJson(getFixture("membership_update.json"), OrgMembershipCreate.class);
103+
104+
final CompletableFuture<OrgMembership> fixtureResponse =
105+
completedFuture(
106+
json.fromJson(getFixture("org_membership.json"), OrgMembership.class));
107+
when(github.put(any(), any(), eq(OrgMembership.class))).thenReturn(fixtureResponse);
108+
final CompletableFuture<OrgMembership> actualResponse =
109+
organisationClient.updateOrgMembership(orgMembershipCreateRequest, "octocat");
110+
111+
assertThat(actualResponse.get().role(), is("member"));
112+
assertThat(actualResponse.get().organization().login(), is("github"));
113+
assertThat(actualResponse.get().user().login(), is("octocat"));
114+
}
81115
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"url": "https://api.github.com/orgs/github/memberships/octocat",
3+
"state": "active",
4+
"role": "member",
5+
"organization_url": "https://api.github.com/orgs/octocat",
6+
"organization": {
7+
"login": "github",
8+
"id": 1,
9+
"node_id": "MDEyOk9yZ2FuaXphdGlvbjE=",
10+
"url": "https://api.github.com/orgs/github",
11+
"repos_url": "https://api.github.com/orgs/github/repos",
12+
"events_url": "https://api.github.com/orgs/github/events",
13+
"hooks_url": "https://api.github.com/orgs/github/hooks",
14+
"issues_url": "https://api.github.com/orgs/github/issues",
15+
"members_url": "https://api.github.com/orgs/github/members{/member}",
16+
"public_members_url": "https://api.github.com/orgs/github/public_members{/member}",
17+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
18+
"description": "A great organization"
19+
},
20+
"user": {
21+
"login": "octocat",
22+
"id": 1,
23+
"node_id": "MDQ6VXNlcjE=",
24+
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
25+
"gravatar_id": "",
26+
"url": "https://api.github.com/users/octocat",
27+
"html_url": "https://github.com/octocat",
28+
"followers_url": "https://api.github.com/users/octocat/followers",
29+
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
30+
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
31+
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
32+
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
33+
"organizations_url": "https://api.github.com/users/octocat/orgs",
34+
"repos_url": "https://api.github.com/users/octocat/repos",
35+
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
36+
"received_events_url": "https://api.github.com/users/octocat/received_events",
37+
"type": "User",
38+
"site_admin": false
39+
}
40+
}

0 commit comments

Comments
 (0)