Skip to content

Commit

Permalink
Implement discussed way to handle the situation
Browse files Browse the repository at this point in the history
  • Loading branch information
Haarolean committed Nov 11, 2023
1 parent 023f410 commit 89c51f6
Show file tree
Hide file tree
Showing 10 changed files with 371 additions and 19 deletions.
107 changes: 93 additions & 14 deletions src/main/java/org/kohsuke/github/GHEventPayload.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.kohsuke.github;

import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.annotation.JsonSetter;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;

Expand All @@ -9,6 +10,7 @@
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

// TODO: Auto-generated Javadoc
/**
Expand Down Expand Up @@ -265,16 +267,68 @@ void lateBind() {
* @see <a href="https://docs.github.com/en/rest/reference/apps#installations">GitHub App Installation</a>
*/
public static class Installation extends GHEventPayload {

private boolean reposLoaded = false;
private List<GHRepository> repositories;
private List<Repository> rawRepositories;

/**
* Gets repositories.
* For the "deleted" action please rather call {@link #getRawRepositories()}
*
* @return the repositories
*/
public List<GHRepository> getRepositories() {
if ("deleted".equalsIgnoreCase(getAction())) {
throw new IllegalStateException("Can't call #getRepositories() on Installation event " +
"with 'deleted' action. Please rather call #getRawRepositories()");
}

if (reposLoaded) {
return Collections.unmodifiableList(repositories);
}

if (repositories == null || repositories.isEmpty()) {
return Collections.emptyList();
}

try {
for (GHRepository singleRepo : repositories) {
// populate each repository
// the repository information provided here is so limited
// as to be unusable without populating, so we do it eagerly
singleRepo.populate();
}
} catch (IOException e) {
throw new GHException("Failed to refresh repositories", e);
} finally {
reposLoaded = true;
}

return Collections.unmodifiableList(repositories);
};
}

;

/**
* Returns a list of raw, unpopulated repositories.
* Useful when calling from within Installation event with action "deleted".
* You can't fetch the info for repositories of an already deleted installation.
*
* @return List<Repository>
*/
public List<Repository> getRawRepositories() {
return Collections.unmodifiableList(rawRepositories);
}

@JsonSetter
public void setRepositories(List<GHRepository> repositories) {
this.repositories = repositories;
this.rawRepositories = repositories
.stream()
.map(r -> new Repository(r.getId(), r.getFullName(), r.getName(), r.getNodeId(), r.isPrivate()))
.collect(Collectors.toList());
}

/**
* Late bind.
Expand All @@ -286,22 +340,47 @@ void lateBind() {
"Expected installation payload, but got something else. Maybe we've got another type of event?");
}
super.lateBind();
}

if ("deleted".equalsIgnoreCase(getAction())) {
repositories.clear(); // can't populate repo list on a deleted installation
/**
* A special minimal implementation of a {@link GHRepository} which contains
* only fields from "Properties of repositories" from
* <a href="https://docs.github.com/en/webhooks-and-events/webhooks/webhook-events-and-payloads#installation">here</a>
*/
public static class Repository {
private final long id;
private final String fullName;
private final String name;
private final String nodeId;
@JsonProperty(value = "private")
private boolean isPrivate;

public Repository(long id, String fullName, String name, String nodeId, boolean isPrivate) {
this.id = id;
this.fullName = fullName;
this.name = name;
this.nodeId = nodeId;
this.isPrivate = isPrivate;
}

if (repositories != null && !repositories.isEmpty()) {
try {
for (GHRepository singleRepo : repositories) {
// populate each repository
// the repository information provided here is so limited
// as to be unusable without populating, so we do it eagerly
singleRepo.populate();
}
} catch (IOException e) {
throw new GHException("Failed to refresh repositories", e);
}
public long getId() {
return id;
}

public String getFullName() {
return fullName;
}

public String getName() {
return name;
}

public String getNodeId() {
return nodeId;
}

public boolean isPrivate() {
return isPrivate;
}
}
}
Expand Down
30 changes: 25 additions & 5 deletions src/test/java/org/kohsuke/github/GHEventPayloadTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@
import static org.hamcrest.Matchers.nullValue;
import static org.hamcrest.Matchers.sameInstance;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.*;

// TODO: Auto-generated Javadoc
/**
Expand Down Expand Up @@ -984,15 +983,34 @@ public void InstallationRepositoriesEvent() throws Exception {
assertThat(event.getSender().getLogin(), is("Codertocat"));
}

@Test
@Payload("installation_created")
public void InstallationCreatedEvent() throws Exception {
final GHEventPayload.Installation event = getGitHubBuilder().withEndpoint(mockGitHub.apiServer().baseUrl())
.build()
.parseEventPayload(payload.asReader(), GHEventPayload.Installation.class);

assertThat(event.getAction(), is("created"));
assertThat(event.getInstallation().getId(), is(43898337L));
assertThat(event.getInstallation().getAccount().getLogin(), is("CronFire"));

assertFalse(event.getRepositories().isEmpty());
assertEquals(event.getRepositories().get(0).getId(), 1296269);
assertFalse(event.getRawRepositories().isEmpty());
assertEquals(event.getRawRepositories().get(0).getId(), 1296269);

assertThat(event.getSender().getLogin(), is("Haarolean"));
}

/**
* Installation event.
*
* @throws Exception
* the exception
*/
@Test
@Payload("installation")
public void InstallationEvent() throws Exception {
@Payload("installation_deleted")
public void InstallationDeletedEvent() throws Exception {
final GHEventPayload.Installation event = getGitHubBuilder().withEndpoint(mockGitHub.apiServer().baseUrl())
.build()
.parseEventPayload(payload.asReader(), GHEventPayload.Installation.class);
Expand All @@ -1001,7 +1019,9 @@ public void InstallationEvent() throws Exception {
assertThat(event.getInstallation().getId(), is(2L));
assertThat(event.getInstallation().getAccount().getLogin(), is("octocat"));

assertTrue(event.getRepositories().isEmpty());
assertThrows(IllegalStateException.class, () -> event.getRepositories().isEmpty());
assertFalse(event.getRawRepositories().isEmpty());
assertEquals(event.getRawRepositories().get(0).getId(), 1296269);

assertThat(event.getSender().getLogin(), is("octocat"));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
{
"action": "created",
"installation": {
"id": 43898337,
"account": {
"login": "CronFire",
"id": 68755481,
"node_id": "MDEyOk9yZ2FuaXphdGlvbjY4NzU1NDgx",
"avatar_url": "https://avatars.githubusercontent.com/u/68755481?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/CronFire",
"html_url": "https://github.com/CronFire",
"followers_url": "https://api.github.com/users/CronFire/followers",
"following_url": "https://api.github.com/users/CronFire/following{/other_user}",
"gists_url": "https://api.github.com/users/CronFire/gists{/gist_id}",
"starred_url": "https://api.github.com/users/CronFire/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/CronFire/subscriptions",
"organizations_url": "https://api.github.com/users/CronFire/orgs",
"repos_url": "https://api.github.com/users/CronFire/repos",
"events_url": "https://api.github.com/users/CronFire/events{/privacy}",
"received_events_url": "https://api.github.com/users/CronFire/received_events",
"type": "Organization",
"site_admin": false
},
"repository_selection": "selected",
"access_tokens_url": "https://api.github.com/app/installations/43898337/access_tokens",
"repositories_url": "https://api.github.com/installation/repositories",
"html_url": "https://github.com/organizations/CronFire/settings/installations/43898337",
"app_id": 421464,
"app_slug": "kapybro-dev",
"target_id": 68755481,
"target_type": "Organization",
"permissions": {
"checks": "write",
"issues": "write",
"actions": "read",
"members": "read",
"contents": "write",
"metadata": "read",
"statuses": "write",
"single_file": "read",
"pull_requests": "write",
"administration": "read"
},
"events": [
"issues",
"issue_comment",
"organization",
"public",
"pull_request",
"pull_request_review",
"pull_request_review_comment",
"push",
"repository",
"status"
],
"created_at": "2023-11-11T10:55:06.000+08:00",
"updated_at": "2023-11-11T10:55:06.000+08:00",
"single_file_name": ".github/kapybro/config.yml",
"has_multiple_single_files": true,
"single_file_paths": [
".github/kapybro/config.yml",
".github/kapybro/rules.yml"
],
"suspended_by": null,
"suspended_at": null
},
"repositories": [
{
"id": 1296269,
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
"name": "Hello-World",
"full_name": "octocat/Hello-World",
"private": false
}
],
"requester": null,
"sender": {
"login": "Haarolean",
"id": 1494347,
"node_id": "MDQ6VXNlcjE0OTQzNDc=",
"avatar_url": "https://avatars.githubusercontent.com/u/1494347?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Haarolean",
"html_url": "https://github.com/Haarolean",
"followers_url": "https://api.github.com/users/Haarolean/followers",
"following_url": "https://api.github.com/users/Haarolean/following{/other_user}",
"gists_url": "https://api.github.com/users/Haarolean/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Haarolean/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Haarolean/subscriptions",
"organizations_url": "https://api.github.com/users/Haarolean/orgs",
"repos_url": "https://api.github.com/users/Haarolean/repos",
"events_url": "https://api.github.com/users/Haarolean/events{/privacy}",
"received_events_url": "https://api.github.com/users/Haarolean/received_events",
"type": "User",
"site_admin": false
}
}
Loading

0 comments on commit 89c51f6

Please sign in to comment.