Repository: hub4j/github-api
Pull request #1780 (2024-01-15 18:15:26)

Add Suspended Installation Properties to GHAppInstallation
# Description

A suspended app installation has `suspended_at` and `suspended_by` properties that were missing from the `GHAppInstallation` class.

https://docs.github.com/en/rest/apps/apps?apiVersion=2022-11-28#list-installations-for-the-authenticated-app



Patch:
diff --git a/.github/workflows/maven-build.yml b/.github/workflows/maven-build.yml
index 6bdf94c33..b0bb7ab9e 100644
--- a/.github/workflows/maven-build.yml
+++ b/.github/workflows/maven-build.yml
@@ -86,7 +86,7 @@ jobs:
       run: mvn -B clean install -D enable-ci --file pom.xml "-Dsurefire.argLine=--add-opens java.base/java.net=ALL-UNNAMED"
     - name: Codecov Report 
       if: matrix.os == 'ubuntu' && matrix.java == '17'
-      uses: codecov/codecov-action@v4.0.0
+      uses: codecov/codecov-action@v4.1.0
 
   test-java-8:
     name: test Java 8 (no-build)
diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml
index 235b6cb78..1b55f3d0c 100644
--- a/.github/workflows/release-drafter.yml
+++ b/.github/workflows/release-drafter.yml
@@ -17,6 +17,6 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: Release Drafter
-        uses: release-drafter/release-drafter@v5
+        uses: release-drafter/release-drafter@v6
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}        
diff --git a/pom.xml b/pom.xml
index 535352ccf..9522a0641 100644
--- a/pom.xml
+++ b/pom.xml
@@ -296,7 +296,7 @@
           <dependency>
             <groupId>org.apache.bcel</groupId>
             <artifactId>bcel</artifactId>
-            <version>6.8.1</version>
+            <version>6.8.2</version>
           </dependency>
         </dependencies>
       </plugin>
diff --git a/src/main/java/org/kohsuke/github/GHAppInstallation.java b/src/main/java/org/kohsuke/github/GHAppInstallation.java
index cf5bda32b..7466c0abe 100644
--- a/src/main/java/org/kohsuke/github/GHAppInstallation.java
+++ b/src/main/java/org/kohsuke/github/GHAppInstallation.java
@@ -7,6 +7,7 @@ import org.kohsuke.github.internal.EnumUtils;
 import java.io.IOException;
 import java.net.URL;
 import java.util.Collections;
+import java.util.Date;
 import java.util.List;
 import java.util.Map;
 import java.util.stream.Collectors;
@@ -45,6 +46,8 @@ public class GHAppInstallation extends GHObject {
     @JsonProperty("repository_selection")
     private GHRepositorySelection repositorySelection;
     private String htmlUrl;
+    private String suspendedAt;
+    private GHUser suspendedBy;
 
     /**
      * Gets the html url.
@@ -311,6 +314,25 @@ public class GHAppInstallation extends GHObject {
         throw new RuntimeException("Do not use this method.");
     }
 
+    /**
+     * Gets suspended at.
+     *
+     * @return the suspended at
+     */
+    public Date getSuspendedAt() {
+        return GitHubClient.parseDate(suspendedAt);
+    }
+
+    /**
+     * Gets suspended by.
+     *
+     * @return the suspended by
+     */
+    @SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
+    public GHUser getSuspendedBy() {
+        return suspendedBy;
+    }
+
     /**
      * Delete a Github App installation
      * <p>


Test patch:
diff --git a/src/test/java/org/kohsuke/github/GHAppInstallationTest.java b/src/test/java/org/kohsuke/github/GHAppInstallationTest.java
index 1a380a973..d6218d112 100644
--- a/src/test/java/org/kohsuke/github/GHAppInstallationTest.java
+++ b/src/test/java/org/kohsuke/github/GHAppInstallationTest.java
@@ -3,11 +3,16 @@ package org.kohsuke.github;
 import org.junit.Test;
 
 import java.io.IOException;
+import java.time.LocalDateTime;
+import java.time.Month;
+import java.time.ZoneOffset;
+import java.util.Date;
 import java.util.List;
 
 import static org.hamcrest.Matchers.*;
 
 // TODO: Auto-generated Javadoc
+
 /**
  * The Class GHAppInstallationTest.
  */
@@ -61,4 +66,23 @@ public class GHAppInstallationTest extends AbstractGHAppInstallationTest {
         assertThat(plan.getType(), equalTo(GHMarketplaceAccountType.ORGANIZATION));
     }
 
+    /**
+     * Test list installations, and one of the installations has been suspended.
+     *
+     * @throws IOException
+     *             Signals that an I/O exception has occurred.
+     */
+    @Test
+    public void testListSuspendedInstallation() throws IOException {
+        GHAppInstallation appInstallation = getAppInstallationWithToken(jwtProvider1.getEncodedAuthorization());
+
+        final GHUser suspendedBy = appInstallation.getSuspendedBy();
+        assertThat(suspendedBy.getLogin(), equalTo("gilday"));
+
+        final Date suspendedAt = appInstallation.getSuspendedAt();
+        final Date expectedSuspendedAt = Date
+                .from(LocalDateTime.of(2024, Month.FEBRUARY, 26, 2, 43, 12).toInstant(ZoneOffset.UTC));
+        assertThat(suspendedAt, equalTo(expectedSuspendedAt));
+    }
+
 }
diff --git a/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/__files/1-app.json b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/__files/1-app.json
new file mode 100644
index 000000000..89a67cab2
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/__files/1-app.json
@@ -0,0 +1,42 @@
+{
+  "id": 83009,
+  "slug": "cleanthat",
+  "node_id": "MDM6QXBwNjU1NTA=",
+  "owner": {
+    "login": "solven-eu",
+    "id": 34552197,
+    "node_id": "MDEyOk9yZ2FuaXphdGlvbjM0NTUyMTk3",
+    "avatar_url": "https://avatars.githubusercontent.com/u/34552197?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/solven-eu",
+    "html_url": "https://github.com/solven-eu",
+    "followers_url": "https://api.github.com/users/solven-eu/followers",
+    "following_url": "https://api.github.com/users/solven-eu/following{/other_user}",
+    "gists_url": "https://api.github.com/users/solven-eu/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/solven-eu/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/solven-eu/subscriptions",
+    "organizations_url": "https://api.github.com/users/solven-eu/orgs",
+    "repos_url": "https://api.github.com/users/solven-eu/repos",
+    "events_url": "https://api.github.com/users/solven-eu/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/solven-eu/received_events",
+    "type": "Organization",
+    "site_admin": false
+  },
+  "name": "CleanThat",
+  "description": "Cleanthat cleans branches automatically to fix/improve your code.\r\n\r\nFeatures :\r\n- Fix branches a pull_requests head\r\n- Open pull_request to fix protected branches\r\n- Format `.md`, `.java`, `.scala`, `.json`, `.yaml` with the help of [Spotless](https://github.com/diffplug/spotless)\r\n- Refactor `.java` files to improve code-style, security and stability",
+  "external_url": "https://github.com/solven-eu/cleanthat",
+  "html_url": "https://github.com/apps/cleanthat",
+  "created_at": "2020-05-19T13:45:43Z",
+  "updated_at": "2023-01-27T06:10:21Z",
+  "permissions": {
+    "checks": "write",
+    "contents": "write",
+    "metadata": "read",
+    "pull_requests": "write"
+  },
+  "events": [
+    "pull_request",
+    "push"
+  ],
+  "installations_count": 280
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/__files/2-app_installations.json b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/__files/2-app_installations.json
new file mode 100644
index 000000000..8514a6733
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/__files/2-app_installations.json
@@ -0,0 +1,61 @@
+[
+  {
+    "id": 12131496,
+    "account": {
+      "login": "hub4j-test-org",
+      "id": 7544739,
+      "node_id": "MDEyOk9yZ2FuaXphdGlvbjc1NDQ3Mzk=",
+      "avatar_url": "https://avatars3.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",
+      "site_admin": false
+    },
+    "repository_selection": "selected",
+    "access_tokens_url": "https://api.github.com/app/installations/12131496/access_tokens",
+    "repositories_url": "https://api.github.com/installation/repositories",
+    "html_url": "https://github.com/organizations/hub4j-test-org/settings/installations/12131496",
+    "app_id": 83009,
+    "app_slug": "ghapi-test-app-2",
+    "target_id": 7544739,
+    "target_type": "Organization",
+    "permissions": {},
+    "events": [],
+    "created_at": "2020-09-30T15:05:32.000Z",
+    "updated_at": "2020-09-30T15:05:32.000Z",
+    "single_file_name": null,
+    "has_multiple_single_files": false,
+    "single_file_paths": [],
+    "suspended_by": {
+      "login": "gilday",
+      "id": 1431609,
+      "node_id": "MDQ6VXNlcjE0MzE2MDk=",
+      "avatar_url": "https://avatars.githubusercontent.com/u/1431609?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/gilday",
+      "html_url": "https://github.com/gilday",
+      "followers_url": "https://api.github.com/users/gilday/followers",
+      "following_url": "https://api.github.com/users/gilday/following{/other_user}",
+      "gists_url": "https://api.github.com/users/gilday/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/gilday/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/gilday/subscriptions",
+      "organizations_url": "https://api.github.com/users/gilday/orgs",
+      "repos_url": "https://api.github.com/users/gilday/repos",
+      "events_url": "https://api.github.com/users/gilday/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/gilday/received_events",
+      "type": "User",
+      "site_admin": false
+    },
+    "suspended_at": "2024-02-26T02:43:12Z"
+  }
+]
diff --git a/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/mappings/1-app.json b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/mappings/1-app.json
new file mode 100644
index 000000000..51181fa14
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/mappings/1-app.json
@@ -0,0 +1,42 @@
+{
+  "id": "144fdb7f-667e-4cf4-bd37-67ed11bdc421",
+  "name": "app",
+  "request": {
+    "url": "/app",
+    "method": "GET",
+    "headers": {
+      "Accept": {
+        "equalTo": "application/vnd.github.machine-man-preview+json"
+      }
+    }
+  },
+  "response": {
+    "status": 200,
+    "bodyFileName": "1-app.json",
+    "headers": {
+      "Server": "GitHub.com",
+      "Date": "Sun, 19 Mar 2023 13:02:50 GMT",
+      "Content-Type": "application/json; charset=utf-8",
+      "Cache-Control": "public, max-age=60, s-maxage=60",
+      "Vary": [
+        "Accept",
+        "Accept-Encoding, Accept, X-Requested-With"
+      ],
+      "ETag": "W/\"00fa67d861eb73a934cd9229b76c2dc7c2c235babf8d281e2dd4a1e31ca3b930\"",
+      "X-GitHub-Media-Type": "github.v3; param=machine-man-preview; format=json",
+      "x-github-api-version-selected": "2022-11-28",
+      "Access-Control-Expose-Headers": "ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset",
+      "Access-Control-Allow-Origin": "*",
+      "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": "C09A:5A83:172C70C:179D1FD:641707FA"
+    }
+  },
+  "uuid": "144fdb7f-667e-4cf4-bd37-67ed11bdc421",
+  "persistent": true,
+  "insertionIndex": 1
+}
\ No newline at end of file
diff --git a/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/mappings/2-app_installations.json b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/mappings/2-app_installations.json
new file mode 100644
index 000000000..abae32ba4
--- /dev/null
+++ b/src/test/resources/org/kohsuke/github/GHAppInstallationTest/wiremock/testListSuspendedInstallation/mappings/2-app_installations.json
@@ -0,0 +1,41 @@
+{
+  "id": "45ac2593-8123-49ae-ad1a-ded446491b14",
+  "name": "app_installations",
+  "request": {
+    "url": "/app/installations",
+    "method": "GET",
+    "headers": {
+      "Accept": {
+        "equalTo": "application/vnd.github.machine-man-preview+json"
+      }
+    }
+  },
+  "response": {
+    "status": 200,
+    "bodyFileName": "2-app_installations.json",
+    "headers": {
+      "Date": "Thu, 05 Nov 2020 20:42:31 GMT",
+      "Content-Type": "application/json; charset=utf-8",
+      "Server": "GitHub.com",
+      "Status": "200 OK",
+      "Cache-Control": "public, max-age=60, s-maxage=60",
+      "Vary": [
+        "Accept",
+        "Accept-Encoding, Accept, X-Requested-With",
+        "Accept-Encoding"
+      ],
+      "ETag": "W/\"60d3ec5c9014799f5e12b88e16e771a386b905ad8d41cd18aed34e58b11c58d4\"",
+      "X-GitHub-Media-Type": "github.v3; param=machine-man-preview; format=json",
+      "Strict-Transport-Security": "max-age=31536000; includeSubdomains; preload",
+      "X-Frame-Options": "deny",
+      "X-Content-Type-Options": "nosniff",
+      "X-XSS-Protection": "1; mode=block",
+      "Referrer-Policy": "origin-when-cross-origin, strict-origin-when-cross-origin",
+      "Content-Security-Policy": "default-src 'none'",
+      "X-GitHub-Request-Id": "9294:AE05:BDAC831:DB35870:5FA463B7"
+    }
+  },
+  "uuid": "45ac2593-8123-49ae-ad1a-ded446491b14",
+  "persistent": true,
+  "insertionIndex": 2
+}
\ No newline at end of file