Repository: hub4j/github-api
Pull request #1371 (2022-02-02 13:39:02)

Fix installation payload handling
Hey there,

Long time no see :).

The problem was initially reported in my Quarkus GitHub App project: https://github.com/quarkiverse/quarkus-github-app/issues/253. 

Parsing `Installation` event payloads with a connected `GitHub` instance (i.e. not an offline one) leads to:
```
Exception: java.lang.NullPointerException: Missing instance URL!
	at java.base/java.util.Objects.requireNonNull(Objects.java:247)
	at org.kohsuke.github.GHRepository.populate(GHRepository.java:3260)
	at org.kohsuke.github.GHEventPayload$Installation.lateBind(GHEventPayload.java:276)
	at org.kohsuke.github.GitHub.parseEventPayload(GitHub.java:944)
```

Installation payloads do not contain the URL of the repository so we cannot populate the data from there. You can see an example in the tests here:
https://github.com/hub4j/github-api/blob/main/src/test/resources/org/kohsuke/github/GHEventPayloadTest/installation.json#L45-L53

I think what I did is safe but better check the URL thing as I decided to always use a forged URL rather than the one provided in the JSON. I can do it in another way if you prefer and keep the JSON URL when it's provided but, given we already had a try/catch for a corner case, I thought we might as well always forge the URL.

The tests are not failing because we are testing all the payloads with an offline `GitHub` instance thus not populating the repositories. Maybe we should change that? But I'm not totally sure of the consequences of this change. So I thought I might as well ask.

Very motivated to get this change in as one of my users is blocked by this so eager to hear your feedback :).


Patch:
diff --git a/src/main/java/org/kohsuke/github/GHEventPayload.java b/src/main/java/org/kohsuke/github/GHEventPayload.java
index 78cb9868a..899717017 100644
--- a/src/main/java/org/kohsuke/github/GHEventPayload.java
+++ b/src/main/java/org/kohsuke/github/GHEventPayload.java
@@ -267,12 +267,15 @@ public abstract class GHEventPayload extends GitHubInteractiveObject {
         void lateBind() {
             if (getInstallation() == null) {
                 throw new IllegalStateException(
-                        "Expected check_suite payload, but got something else. Maybe we've got another type of event?");
+                        "Expected installation payload, but got something else. Maybe we've got another type of event?");
             }
             super.lateBind();
             if (repositories != null && !repositories.isEmpty()) {
                 try {
-                    for (GHRepository singleRepo : repositories) { // warp each of the repository
+                    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) {
@@ -326,7 +329,7 @@ public abstract class GHEventPayload extends GitHubInteractiveObject {
         void lateBind() {
             if (getInstallation() == null) {
                 throw new IllegalStateException(
-                        "Expected check_suite payload, but got something else. Maybe we've got another type of event?");
+                        "Expected installation_repositories payload, but got something else. Maybe we've got another type of event?");
             }
             super.lateBind();
             List<GHRepository> repositories;
diff --git a/src/main/java/org/kohsuke/github/GHRepository.java b/src/main/java/org/kohsuke/github/GHRepository.java
index ca06de69e..b97143dfb 100644
--- a/src/main/java/org/kohsuke/github/GHRepository.java
+++ b/src/main/java/org/kohsuke/github/GHRepository.java
@@ -24,7 +24,6 @@
 package org.kohsuke.github;
 
 import com.fasterxml.jackson.annotation.JsonProperty;
-import com.fasterxml.jackson.core.JsonParseException;
 import com.infradna.tool.bridge_method_injector.WithBridgeMethods;
 import edu.umd.cs.findbugs.annotations.CheckForNull;
 import edu.umd.cs.findbugs.annotations.NonNull;
@@ -2978,7 +2977,7 @@ public class GHRepository extends GHObject {
         if (tail.length() > 0 && !tail.startsWith("/")) {
             tail = '/' + tail;
         }
-        return "/repos/" + getOwnerName() + "/" + name + tail;
+        return "/repos/" + full_name + tail;
     }
 
     /**
@@ -3257,29 +3256,13 @@ public class GHRepository extends GHObject {
             return; // can't populate if the root is offline
         }
 
-        final URL url = requireNonNull(getUrl(), "Missing instance URL!");
+        // We don't use the URL provided in the JSON because it is not reliable:
+        // 1. There is bug in Push event payloads that returns the wrong url.
+        // For Push event repository records, they take the form "https://github.com/{fullName}".
+        // All other occurrences of "url" take the form "https://api.github.com/...".
+        // 2. For Installation event payloads, the URL is not provided at all.
 
-        try {
-            // IMPORTANT: the url for repository records does not reliably point to the API url.
-            // There is bug in Push event payloads that returns the wrong url.
-            // All other occurrences of "url" take the form "https://api.github.com/...".
-            // For Push event repository records, they take the form "https://github.com/{fullName}".
-            root().createRequest()
-                    .withPreview(BAPTISTE)
-                    .withPreview(NEBULA)
-                    .setRawUrlPath(url.toString())
-                    .fetchInto(this);
-        } catch (HttpException e) {
-            if (e.getCause() instanceof JsonParseException) {
-                root().createRequest()
-                        .withPreview(BAPTISTE)
-                        .withPreview(NEBULA)
-                        .withUrlPath("/repos/" + full_name)
-                        .fetchInto(this);
-            } else {
-                throw e;
-            }
-        }
+        root().createRequest().withPreview(BAPTISTE).withPreview(NEBULA).withUrlPath(getApiTailUrl("")).fetchInto(this);
     }
 
     /**


Test patch:
diff --git a/src/test/java/org/kohsuke/github/GHEventPayloadTest.java b/src/test/java/org/kohsuke/github/GHEventPayloadTest.java
index f245250a9..f39573191 100644
--- a/src/test/java/org/kohsuke/github/GHEventPayloadTest.java
+++ b/src/test/java/org/kohsuke/github/GHEventPayloadTest.java
@@ -630,7 +630,7 @@ public class GHEventPayloadTest extends AbstractGitHubWireMockTest {
                 GHEventPayload.CheckRun.class);
         final GHCheckRun checkRun2 = verifyBasicCheckRunEvent(event2);
 
-        int expectedRequestCount = mockGitHub.isUseProxy() ? 3 : 2;
+        int expectedRequestCount = 2;
         assertThat("pull body should be populated",
                 checkRun2.getPullRequests().get(0).getBody(),
                 equalTo("This is a pretty simple change that we need to pull into main."));
@@ -767,7 +767,8 @@ public class GHEventPayloadTest extends AbstractGitHubWireMockTest {
     @Test
     @Payload("installation")
     public void InstallationEvent() throws Exception {
-        final GHEventPayload.Installation event = GitHub.offline()
+        final GHEventPayload.Installation event = getGitHubBuilder().withEndpoint(mockGitHub.apiServer().baseUrl())
+                .build()
                 .parseEventPayload(payload.asReader(), GHEventPayload.Installation.class);
 
         assertThat(event.getAction(), is("deleted"));
@@ -775,10 +776,11 @@ public class GHEventPayloadTest extends AbstractGitHubWireMockTest {
         assertThat(event.getInstallation().getAccount().getLogin(), is("octocat"));
 
         assertThat(event.getRepositories().get(0).getId(), is(1296269L));
-        assertThat(event.getRepositories().get(0).getNodeId(), is("MDEwOlJlcG9zaXRvcnkxODY4NTMwMDc="));
+        assertThat(event.getRepositories().get(0).getNodeId(), is("MDEwOlJlcG9zaXRvcnkxMjk2MjY5"));
         assertThat(event.getRepositories().get(0).getName(), is("Hello-World"));
         assertThat(event.getRepositories().get(0).getFullName(), is("octocat/Hello-World"));
         assertThat(event.getRepositories().get(0).isPrivate(), is(false));
+        assertThat(event.getRepositories().get(0).getOwner().getLogin(), is("octocat"));
 
         assertThat(event.getSender().getLogin(), is("octocat"));
     }
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/installation.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/installation.json
index 1697660a5..38f10f627 100644
--- a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/installation.json
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/installation.json
@@ -45,7 +45,7 @@
   "repositories": [
     {
       "id": 1296269,
-      "node_id": "MDEwOlJlcG9zaXRvcnkxODY4NTMwMDc=",
+      "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
       "name": "Hello-World",
       "full_name": "octocat/Hello-World",
       "private": false
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/__files/repos_octocat_hello-world-1.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/__files/repos_octocat_hello-world-1.json
new file mode 100644
index 000000000..fd3c1ce7c
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/__files/repos_octocat_hello-world-1.json
@@ -0,0 +1,108 @@
+{
+  "id": 1296269,
+  "node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
+  "name": "Hello-World",
+  "full_name": "octocat/Hello-World",
+  "private": false,
+  "owner": {
+    "login": "octocat",
+    "id": 583231,
+    "node_id": "MDQ6VXNlcjU4MzIzMQ==",
+    "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/octocat",
+    "html_url": "https://github.com/octocat",
+    "followers_url": "https://api.github.com/users/octocat/followers",
+    "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+    "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+    "organizations_url": "https://api.github.com/users/octocat/orgs",
+    "repos_url": "https://api.github.com/users/octocat/repos",
+    "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/octocat/received_events",
+    "type": "User",
+    "site_admin": false
+  },
+  "html_url": "https://github.com/octocat/Hello-World",
+  "description": "My first repository on GitHub!",
+  "fork": false,
+  "url": "https://api.github.com/repos/octocat/Hello-World",
+  "forks_url": "https://api.github.com/repos/octocat/Hello-World/forks",
+  "keys_url": "https://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
+  "collaborators_url": "https://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
+  "teams_url": "https://api.github.com/repos/octocat/Hello-World/teams",
+  "hooks_url": "https://api.github.com/repos/octocat/Hello-World/hooks",
+  "issue_events_url": "https://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
+  "events_url": "https://api.github.com/repos/octocat/Hello-World/events",
+  "assignees_url": "https://api.github.com/repos/octocat/Hello-World/assignees{/user}",
+  "branches_url": "https://api.github.com/repos/octocat/Hello-World/branches{/branch}",
+  "tags_url": "https://api.github.com/repos/octocat/Hello-World/tags",
+  "blobs_url": "https://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
+  "git_tags_url": "https://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
+  "git_refs_url": "https://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
+  "trees_url": "https://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
+  "statuses_url": "https://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
+  "languages_url": "https://api.github.com/repos/octocat/Hello-World/languages",
+  "stargazers_url": "https://api.github.com/repos/octocat/Hello-World/stargazers",
+  "contributors_url": "https://api.github.com/repos/octocat/Hello-World/contributors",
+  "subscribers_url": "https://api.github.com/repos/octocat/Hello-World/subscribers",
+  "subscription_url": "https://api.github.com/repos/octocat/Hello-World/subscription",
+  "commits_url": "https://api.github.com/repos/octocat/Hello-World/commits{/sha}",
+  "git_commits_url": "https://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
+  "comments_url": "https://api.github.com/repos/octocat/Hello-World/comments{/number}",
+  "issue_comment_url": "https://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
+  "contents_url": "https://api.github.com/repos/octocat/Hello-World/contents/{+path}",
+  "compare_url": "https://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
+  "merges_url": "https://api.github.com/repos/octocat/Hello-World/merges",
+  "archive_url": "https://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
+  "downloads_url": "https://api.github.com/repos/octocat/Hello-World/downloads",
+  "issues_url": "https://api.github.com/repos/octocat/Hello-World/issues{/number}",
+  "pulls_url": "https://api.github.com/repos/octocat/Hello-World/pulls{/number}",
+  "milestones_url": "https://api.github.com/repos/octocat/Hello-World/milestones{/number}",
+  "notifications_url": "https://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
+  "labels_url": "https://api.github.com/repos/octocat/Hello-World/labels{/name}",
+  "releases_url": "https://api.github.com/repos/octocat/Hello-World/releases{/id}",
+  "deployments_url": "https://api.github.com/repos/octocat/Hello-World/deployments",
+  "created_at": "2011-01-26T19:01:12Z",
+  "updated_at": "2022-02-03T00:06:57Z",
+  "pushed_at": "2022-01-30T18:13:40Z",
+  "git_url": "git://github.com/octocat/Hello-World.git",
+  "ssh_url": "git@github.com:octocat/Hello-World.git",
+  "clone_url": "https://github.com/octocat/Hello-World.git",
+  "svn_url": "https://github.com/octocat/Hello-World",
+  "homepage": "",
+  "size": 1,
+  "stargazers_count": 1764,
+  "watchers_count": 1764,
+  "language": null,
+  "has_issues": true,
+  "has_projects": true,
+  "has_downloads": true,
+  "has_wiki": true,
+  "has_pages": false,
+  "forks_count": 1682,
+  "mirror_url": null,
+  "archived": false,
+  "disabled": false,
+  "open_issues_count": 802,
+  "license": null,
+  "allow_forking": true,
+  "is_template": false,
+  "topics": [],
+  "visibility": "public",
+  "forks": 1682,
+  "open_issues": 802,
+  "watchers": 1764,
+  "default_branch": "master",
+  "permissions": {
+    "admin": false,
+    "maintain": false,
+    "push": false,
+    "triage": false,
+    "pull": true
+  },
+  "temp_clone_token": "",
+  "network_count": 1682,
+  "subscribers_count": 1731
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/__files/users_octocat-2.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/__files/users_octocat-2.json
new file mode 100644
index 000000000..2652766fa
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/__files/users_octocat-2.json
@@ -0,0 +1,34 @@
+{
+  "login": "octocat",
+  "id": 583231,
+  "node_id": "MDQ6VXNlcjU4MzIzMQ==",
+  "avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
+  "gravatar_id": "",
+  "url": "https://api.github.com/users/octocat",
+  "html_url": "https://github.com/octocat",
+  "followers_url": "https://api.github.com/users/octocat/followers",
+  "following_url": "https://api.github.com/users/octocat/following{/other_user}",
+  "gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
+  "starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
+  "subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
+  "organizations_url": "https://api.github.com/users/octocat/orgs",
+  "repos_url": "https://api.github.com/users/octocat/repos",
+  "events_url": "https://api.github.com/users/octocat/events{/privacy}",
+  "received_events_url": "https://api.github.com/users/octocat/received_events",
+  "type": "User",
+  "site_admin": false,
+  "name": "The Octocat",
+  "company": "@github",
+  "blog": "https://github.blog",
+  "location": "San Francisco",
+  "email": "octocat@github.com",
+  "hireable": null,
+  "bio": null,
+  "twitter_username": null,
+  "public_repos": 8,
+  "public_gists": 8,
+  "followers": 4752,
+  "following": 9,
+  "created_at": "2011-01-25T18:44:36Z",
+  "updated_at": "2022-01-24T15:08:43Z"
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/mappings/repos_octocat_hello-world-1.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/mappings/repos_octocat_hello-world-1.json
new file mode 100644
index 000000000..942435fa5
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/mappings/repos_octocat_hello-world-1.json
@@ -0,0 +1,47 @@
+{
+  "id": "825b1b2a-1bcf-4273-9204-54f989479669",
+  "name": "repos_octocat_hello-world",
+  "request": {
+    "url": "/repos/octocat/Hello-World",
+    "method": "GET",
+    "headers": {
+      "Accept": {
+        "equalTo": "application/vnd.github.baptiste-preview+json, application/vnd.github.nebula-preview+json"
+      }
+    }
+  },
+  "response": {
+    "status": 200,
+    "bodyFileName": "repos_octocat_hello-world-1.json",
+    "headers": {
+      "Server": "GitHub.com",
+      "Date": "Thu, 03 Feb 2022 14:07:49 GMT",
+      "Content-Type": "application/json; charset=utf-8",
+      "Cache-Control": "private, max-age=60, s-maxage=60",
+      "Vary": [
+        "Accept, Authorization, Cookie, X-GitHub-OTP",
+        "Accept-Encoding, Accept, X-Requested-With"
+      ],
+      "ETag": "W/\"54ebfbf708e274f11202ea42a54ccb98955c89b119059c79c8f1bf7e76126198\"",
+      "Last-Modified": "Thu, 03 Feb 2022 00:06:57 GMT",
+      "X-OAuth-Scopes": "admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, gist, notifications, repo, user, workflow, write:discussion",
+      "X-Accepted-OAuth-Scopes": "repo",
+      "X-GitHub-Media-Type": "github.v3; param=baptiste-preview.nebula-preview; format=json",
+      "X-RateLimit-Limit": "5000",
+      "X-RateLimit-Remaining": "4999",
+      "X-RateLimit-Reset": "1643900869",
+      "X-RateLimit-Used": "1",
+      "X-RateLimit-Resource": "core",
+      "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+      "X-Frame-Options": "deny",
+      "X-Content-Type-Options": "nosniff",
+      "X-XSS-Protection": "0",
+      "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
+      "Content-Security-Policy": "default-src 'none'",
+      "X-GitHub-Request-Id": "AD00:CEBF:18E9BF0:19A3623:61FBE1B5"
+    }
+  },
+  "uuid": "825b1b2a-1bcf-4273-9204-54f989479669",
+  "persistent": true,
+  "insertionIndex": 1
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/mappings/users_octocat-2.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/mappings/users_octocat-2.json
new file mode 100644
index 000000000..3a64fa211
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/InstallationEvent/mappings/users_octocat-2.json
@@ -0,0 +1,47 @@
+{
+  "id": "a801936f-7ec1-4f5a-8e1a-999cff08aec8",
+  "name": "users_octocat",
+  "request": {
+    "url": "/users/octocat",
+    "method": "GET",
+    "headers": {
+      "Accept": {
+        "equalTo": "application/vnd.github.v3+json"
+      }
+    }
+  },
+  "response": {
+    "status": 200,
+    "bodyFileName": "users_octocat-2.json",
+    "headers": {
+      "Server": "GitHub.com",
+      "Date": "Thu, 03 Feb 2022 14:09:13 GMT",
+      "Content-Type": "application/json; charset=utf-8",
+      "Cache-Control": "private, max-age=60, s-maxage=60",
+      "Vary": [
+        "Accept, Authorization, Cookie, X-GitHub-OTP",
+        "Accept-Encoding, Accept, X-Requested-With"
+      ],
+      "ETag": "W/\"6b9192ff77357b29af6623ef400f86c862e7b184905220e1f1d09cfd0a545d37\"",
+      "Last-Modified": "Mon, 24 Jan 2022 15:08:43 GMT",
+      "X-OAuth-Scopes": "admin:gpg_key, admin:org, admin:org_hook, admin:public_key, admin:repo_hook, gist, notifications, repo, user, workflow, write:discussion",
+      "X-Accepted-OAuth-Scopes": "",
+      "X-GitHub-Media-Type": "github.v3; format=json",
+      "X-RateLimit-Limit": "5000",
+      "X-RateLimit-Remaining": "4998",
+      "X-RateLimit-Reset": "1643900869",
+      "X-RateLimit-Used": "2",
+      "X-RateLimit-Resource": "core",
+      "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+      "X-Frame-Options": "deny",
+      "X-Content-Type-Options": "nosniff",
+      "X-XSS-Protection": "0",
+      "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
+      "Content-Security-Policy": "default-src 'none'",
+      "X-GitHub-Request-Id": "AD02:CC9B:2A1ACBB:2AF0199:61FBE209"
+    }
+  },
+  "uuid": "a801936f-7ec1-4f5a-8e1a-999cff08aec8",
+  "persistent": true,
+  "insertionIndex": 2
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/__files/repos_hub4j-test-org_github-api-2.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/__files/repos_hub4j-test-org_github-api-2.json
index b3ae4b16b..e703d6c90 100644
--- a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/__files/repos_hub4j-test-org_github-api-2.json
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/__files/repos_hub4j-test-org_github-api-2.json
@@ -331,4 +331,4 @@
   },
   "network_count": 478,
   "subscribers_count": 0
-}
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/mappings/repos_hub4j-test-org_github-api-2.json b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/mappings/repos_hub4j-test-org_github-api-2.json
index fda1bcb64..5335fe6f4 100644
--- a/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/mappings/repos_hub4j-test-org_github-api-2.json
+++ b/src/test/resources/org/kohsuke/github/GHEventPayloadTest/wiremock/pushToFork/mappings/repos_hub4j-test-org_github-api-2.json
@@ -44,4 +44,4 @@
   "uuid": "90aa0017-3f50-4829-bda4-6531fbcfba60",
   "persistent": true,
   "insertionIndex": 2
-}
+}
\ No newline at end of file