Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 57 additions & 4 deletions src/main/java/org/kohsuke/github/GHInvitation.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,20 @@
*
* @see GitHub#getMyInvitations() GitHub#getMyInvitations()
* @see GHRepository#listInvitations() GHRepository#listInvitations()
* @see <a href="https://docs.github.com/en/rest/collaborators/invitations">Repository invitations API</a>
*/
@SuppressFBWarnings(
value = { "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD", "UWF_UNWRITTEN_FIELD", "NP_UNWRITTEN_FIELD",
"UUF_UNUSED_FIELD" },
justification = "JSON API")
public class GHInvitation extends GHObject {

private boolean expired;
private String htmlUrl;

private int id;
private GHUser invitee, inviter;
private String permissions;
private GHRepository repository;

/**
* Create default GHInvitation instance
*/
Expand All @@ -35,19 +36,23 @@ public GHInvitation() {
*
* @throws IOException
* the io exception
* @see <a href= "https://docs.github.com/en/rest/collaborators/invitations#accept-a-repository-invitation">Accept a
* repository invitation</a>
*/
public void accept() throws IOException {
root().createRequest().method("PATCH").withUrlPath("/user/repository_invitations/" + id).send();
root().createRequest().method("PATCH").withUrlPath("/user/repository_invitations/" + getId()).send();
}

/**
* Decline a repository invitation.
*
* @throws IOException
* the io exception
* @see <a href= "https://docs.github.com/en/rest/collaborators/invitations#decline-a-repository-invitation">Decline
* a repository invitation</a>
*/
public void decline() throws IOException {
root().createRequest().method("DELETE").withUrlPath("/user/repository_invitations/" + id).send();
root().createRequest().method("DELETE").withUrlPath("/user/repository_invitations/" + getId()).send();
}

/**
Expand All @@ -58,4 +63,52 @@ public void decline() throws IOException {
public URL getHtmlUrl() {
return GitHubClient.parseURL(htmlUrl);
}

/**
* Gets the invitee (the user being invited).
*
* @return the invitee
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public GHUser getInvitee() {
return invitee;
}

/**
* Gets the inviter (the user who created the invitation).
*
* @return the inviter
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public GHUser getInviter() {
return inviter;
}

/**
* Gets the permission associated with the invitation.
*
* @return the permission (e.g. "read", "write", "admin", "triage", "maintain")
*/
public String getPermission() {
return permissions;
}

/**
* Gets the repository for this invitation.
*
* @return the repository
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public GHRepository getRepository() {
return repository;
}

/**
* Whether this invitation has expired.
*
* @return true if expired
*/
public boolean isExpired() {
return expired;
}
}
111 changes: 111 additions & 0 deletions src/test/java/org/kohsuke/github/GHInvitationTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package org.kohsuke.github;

import org.junit.Test;

import java.io.IOException;
import java.util.List;

import static org.hamcrest.Matchers.*;

/**
* Tests for {@link GHInvitation}.
*
* @author Yoann Rodiere
*/
public class GHInvitationTest extends AbstractGitHubWireMockTest {

/**
* Create default GHInvitationTest instance
*/
public GHInvitationTest() {
}

/**
* Test accepting a repository invitation.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
@Test
public void testAcceptInvitation() throws IOException {
List<GHInvitation> invitations = gitHub.getMyInvitations();
assertThat(invitations, hasSize(1));

GHInvitation invitation = invitations.get(0);
assertThat(invitation.getId(), not(equalTo(0L)));

invitation.accept();

assertThat(gitHub.getMyInvitations(), is(empty()));
}

/**
* Test declining a repository invitation.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
@Test
public void testDeclineInvitation() throws IOException {
List<GHInvitation> invitations = gitHub.getMyInvitations();
assertThat(invitations, hasSize(1));

GHInvitation invitation = invitations.get(0);
assertThat(invitation.getId(), not(equalTo(0L)));

invitation.decline();

assertThat(gitHub.getMyInvitations(), is(empty()));
}

/**
* Test that listing repository invitations returns correct data for all fields.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
@Test
public void testListInvitations() throws IOException {
GHRepository repo = getRepository();
List<GHInvitation> invitations = repo.listInvitations().toList();

assertThat(invitations, hasSize(1));

GHInvitation invitation = invitations.get(0);

assertThat(invitation.getId(), equalTo(1L));
assertThat(invitation.getNodeId(), equalTo("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"));
assertThat(invitation.getPermission(), equalTo("write"));
assertThat(invitation.isExpired(), equalTo(false));
assertThat(invitation.getHtmlUrl().toString(),
equalTo("https://github.com/hub4j-test-org/github-api/invitations"));
assertThat(invitation.getCreatedAt().toString(), equalTo("2016-06-13T14:52:50Z"));

GHUser invitee = invitation.getInvitee();
assertThat(invitee, notNullValue());
assertThat(invitee.getLogin(), equalTo("octocat-invitee"));

GHUser inviter = invitation.getInviter();
assertThat(inviter, notNullValue());
assertThat(inviter.getLogin(), equalTo("octocat"));

GHRepository invitationRepo = invitation.getRepository();
assertThat(invitationRepo, notNullValue());
assertThat(invitationRepo.getName(), equalTo("github-api"));
}

private GHRepository getRepository(GitHub gitHub) throws IOException {
return gitHub.getOrganization("hub4j-test-org").getRepository("github-api");
}

/**
* Gets the repository.
*
* @return the repository
* @throws IOException
* Signals that an I/O exception has occurred.
*/
protected GHRepository getRepository() throws IOException {
return getRepository(gitHub);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"login": "yrodiere-agent",
"id": 279624850,
"node_id": "U_kgDOEKq8kg",
"avatar_url": "https://avatars.githubusercontent.com/u/279624850?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/yrodiere-agent",
"html_url": "https://github.com/yrodiere-agent",
"followers_url": "https://api.github.com/users/yrodiere-agent/followers",
"following_url": "https://api.github.com/users/yrodiere-agent/following{/other_user}",
"gists_url": "https://api.github.com/users/yrodiere-agent/gists{/gist_id}",
"starred_url": "https://api.github.com/users/yrodiere-agent/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/yrodiere-agent/subscriptions",
"organizations_url": "https://api.github.com/users/yrodiere-agent/orgs",
"repos_url": "https://api.github.com/users/yrodiere-agent/repos",
"events_url": "https://api.github.com/users/yrodiere-agent/events{/privacy}",
"received_events_url": "https://api.github.com/users/yrodiere-agent/received_events",
"type": "User",
"user_view_type": "public",
"site_admin": false,
"name": "yrodiere-agent",
"company": null,
"blog": "",
"location": null,
"email": null,
"hireable": null,
"bio": null,
"twitter_username": null,
"notification_email": null,
"public_repos": 0,
"public_gists": 0,
"followers": 0,
"following": 0,
"created_at": "2026-04-27T06:23:17Z",
"updated_at": "2026-09-04T14:14:37Z"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
[
{
"id": 1,
"node_id": "RI_kwDODFTdCc4UVBHu",
"repository": {
"id": 206888201,
"node_id": "MDEwOlJlcG9zaXRvcnkyMDY4ODgyMDE=",
"name": "github-api",
"full_name": "hub4j-test-org/github-api",
"private": false,
"owner": {
"login": "hub4j-test-org",
"id": 7544739,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
"avatar_url": "https://avatars.githubusercontent.com/u/7544739?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/hub4j-test-org",
"html_url": "https://github.com/hub4j-test-org",
"followers_url": "https://api.github.com/users/hub4j-test-org/followers",
"following_url": "https://api.github.com/users/hub4j-test-org/following{/other_user}",
"gists_url": "https://api.github.com/users/hub4j-test-org/gists{/gist_id}",
"starred_url": "https://api.github.com/users/hub4j-test-org/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/hub4j-test-org/subscriptions",
"organizations_url": "https://api.github.com/users/hub4j-test-org/orgs",
"repos_url": "https://api.github.com/users/hub4j-test-org/repos",
"events_url": "https://api.github.com/users/hub4j-test-org/events{/privacy}",
"received_events_url": "https://api.github.com/users/hub4j-test-org/received_events",
"type": "Organization",
"user_view_type": "public",
"site_admin": false
},
"html_url": "https://github.com/hub4j-test-org/github-api",
"description": "Tricky",
"fork": true,
"url": "https://api.github.com/repos/hub4j-test-org/github-api",
"forks_url": "https://api.github.com/repos/hub4j-test-org/github-api/forks",
"keys_url": "https://api.github.com/repos/hub4j-test-org/github-api/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/hub4j-test-org/github-api/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/hub4j-test-org/github-api/teams",
"hooks_url": "https://api.github.com/repos/hub4j-test-org/github-api/hooks",
"issue_events_url": "https://api.github.com/repos/hub4j-test-org/github-api/issues/events{/number}",
"events_url": "https://api.github.com/repos/hub4j-test-org/github-api/events",
"assignees_url": "https://api.github.com/repos/hub4j-test-org/github-api/assignees{/user}",
"branches_url": "https://api.github.com/repos/hub4j-test-org/github-api/branches{/branch}",
"tags_url": "https://api.github.com/repos/hub4j-test-org/github-api/tags",
"blobs_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/hub4j-test-org/github-api/statuses/{sha}",
"languages_url": "https://api.github.com/repos/hub4j-test-org/github-api/languages",
"stargazers_url": "https://api.github.com/repos/hub4j-test-org/github-api/stargazers",
"contributors_url": "https://api.github.com/repos/hub4j-test-org/github-api/contributors",
"subscribers_url": "https://api.github.com/repos/hub4j-test-org/github-api/subscribers",
"subscription_url": "https://api.github.com/repos/hub4j-test-org/github-api/subscription",
"commits_url": "https://api.github.com/repos/hub4j-test-org/github-api/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/hub4j-test-org/github-api/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/hub4j-test-org/github-api/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/hub4j-test-org/github-api/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/hub4j-test-org/github-api/contents/{+path}",
"compare_url": "https://api.github.com/repos/hub4j-test-org/github-api/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/hub4j-test-org/github-api/merges",
"archive_url": "https://api.github.com/repos/hub4j-test-org/github-api/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/hub4j-test-org/github-api/downloads",
"issues_url": "https://api.github.com/repos/hub4j-test-org/github-api/issues{/number}",
"pulls_url": "https://api.github.com/repos/hub4j-test-org/github-api/pulls{/number}",
"milestones_url": "https://api.github.com/repos/hub4j-test-org/github-api/milestones{/number}",
"notifications_url": "https://api.github.com/repos/hub4j-test-org/github-api/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/hub4j-test-org/github-api/labels{/name}",
"releases_url": "https://api.github.com/repos/hub4j-test-org/github-api/releases{/id}",
"deployments_url": "https://api.github.com/repos/hub4j-test-org/github-api/deployments"
},
"invitee": {
"login": "yrodiere-agent",
"id": 279624850,
"node_id": "U_kgDOEKq8kg",
"avatar_url": "https://avatars.githubusercontent.com/u/279624850?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/yrodiere-agent",
"html_url": "https://github.com/yrodiere-agent",
"followers_url": "https://api.github.com/users/yrodiere-agent/followers",
"following_url": "https://api.github.com/users/yrodiere-agent/following{/other_user}",
"gists_url": "https://api.github.com/users/yrodiere-agent/gists{/gist_id}",
"starred_url": "https://api.github.com/users/yrodiere-agent/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/yrodiere-agent/subscriptions",
"organizations_url": "https://api.github.com/users/yrodiere-agent/orgs",
"repos_url": "https://api.github.com/users/yrodiere-agent/repos",
"events_url": "https://api.github.com/users/yrodiere-agent/events{/privacy}",
"received_events_url": "https://api.github.com/users/yrodiere-agent/received_events",
"type": "User",
"user_view_type": "public",
"site_admin": false
},
"inviter": {
"login": "yrodiere",
"id": 412878,
"node_id": "MDQ6VXNlcjQxMjg3OA==",
"avatar_url": "https://avatars.githubusercontent.com/u/412878?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/yrodiere",
"html_url": "https://github.com/yrodiere",
"followers_url": "https://api.github.com/users/yrodiere/followers",
"following_url": "https://api.github.com/users/yrodiere/following{/other_user}",
"gists_url": "https://api.github.com/users/yrodiere/gists{/gist_id}",
"starred_url": "https://api.github.com/users/yrodiere/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/yrodiere/subscriptions",
"organizations_url": "https://api.github.com/users/yrodiere/orgs",
"repos_url": "https://api.github.com/users/yrodiere/repos",
"events_url": "https://api.github.com/users/yrodiere/events{/privacy}",
"received_events_url": "https://api.github.com/users/yrodiere/received_events",
"type": "User",
"user_view_type": "public",
"site_admin": false
},
"permissions": "write",
"created_at": "2026-11-02T13:23:59Z",
"url": "https://api.github.com/user/repository_invitations/1",
"html_url": "https://github.com/hub4j-test-org/github-api/invitations",
"expired": false
}
]
Loading
Loading