Skip to content
Draft
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
23 changes: 23 additions & 0 deletions src/main/java/org/kohsuke/github/GHApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,27 @@ public PagedIterable<GHAppInstallation> listInstallations(final Instant since) {
return requester.toIterable(GHAppInstallation[].class, null);
}

/**
* Obtains the webhook deliveries for this GitHub App.
*
* @return the webhook deliveries
*/
public PagedIterable<GHAppWebhookDelivery> listWebhookDeliveries() {
return root().createRequest()
.withUrlPath("/app/hook/deliveries")
.toIterable(GHAppWebhookDelivery[].class, item -> item.wrapUp(root()));
}

/**
* Requests a redelivery for a webhook delivery of this GitHub App.
*
* @param deliveryId
* the webhook delivery id
* @throws IOException
* if the request fails
*/
public void redeliverWebhookDelivery(long deliveryId) throws IOException {
GHAppWebhookDelivery.redeliver(root(), deliveryId);
}

}
246 changes: 246 additions & 0 deletions src/main/java/org/kohsuke/github/GHAppWebhookDelivery.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
package org.kohsuke.github;

import com.fasterxml.jackson.annotation.JsonProperty;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.kohsuke.github.internal.EnumUtils;

import java.io.IOException;
import java.time.Instant;
import java.util.Map;

/**
* Represents a webhook delivery for a GitHub App.
*
* @see <a href="https://docs.github.com/en/rest/apps/webhooks?apiVersion=2022-11-28#list-deliveries-for-an-app-webhook">
* List deliveries for an app webhook</a>
*/
public class GHAppWebhookDelivery extends GHObject {

private String action;
@JsonProperty("delivered_at")
private String deliveredAt;
private double duration;
private String event;
private String guid;
@JsonProperty("installation_id")
private Long installationId;
private GHAppWebhookRequest request;
@JsonProperty("repository_id")
private Long repositoryId;
private boolean redelivery;
private GHAppWebhookResponse response;
private String status;
@JsonProperty("status_code")
private int statusCode;

/**
* Redelivers this webhook delivery.
*
* @throws IOException
* if the request fails
*/
public void redeliver() throws IOException {
redeliver(root(), getId());
}

/**
* Gets the webhook action if available.
*
* @return the action
*/
public String getAction() {
return action;
}

/**
* Gets the time this webhook was delivered.
*
* @return the delivered time as {@link Instant}
* @throws IOException
* if parsing fails
*/
public Instant getDeliveredAt() throws IOException {
return GitHubClient.parseInstant(deliveredAt);
}

/**
* Gets the duration in seconds for this delivery.
*
* @return the duration
*/
public double getDuration() {
return duration;
}

/**
* Gets the event type.
*
* @return the event
*/
public GHEvent getEvent() {
return EnumUtils.getEnumOrDefault(GHEvent.class, event, GHEvent.UNKNOWN);
}

/**
* Gets the webhook GUID.
*
* @return the guid
*/
public String getGuid() {
return guid;
}

/**
* Gets the installation id for this delivery.
*
* @return the installation id
*/
public Long getInstallationId() {
return installationId;
}

/**
* Gets the request information for this delivery.
*
* @return the request info
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public GHAppWebhookRequest getRequest() {
return request;
}

/**
* Gets the repository id for this delivery.
*
* @return the repository id
*/
public Long getRepositoryId() {
return repositoryId;
}

/**
* Whether this is a redelivery.
*
* @return true if redelivery
*/
public boolean isRedelivery() {
return redelivery;
}

/**
* Gets the response information for this delivery.
*
* @return the response info
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public GHAppWebhookResponse getResponse() {
return response;
}

/**
* Gets the status text.
*
* @return the status
*/
public String getStatus() {
return status;
}

/**
* Gets the status code.
*
* @return the status code
*/
public int getStatusCode() {
return statusCode;
}

GHAppWebhookDelivery wrapUp(GitHub root) {
this.root = root;
return this;
}

static void redeliver(GitHub root, long deliveryId) throws IOException {
root.createRequest()
.method("POST")
.withUrlPath(String.format("/app/hook/deliveries/%d/attempts", deliveryId))
.send();
}

/**
* Request data for a webhook delivery.
*/
public static class GHAppWebhookRequest {

private Map<String, String> headers;
private Object payload;

/**
* Gets the headers used in the request.
*
* @return the headers
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public Map<String, String> getHeaders() {
return headers;
}

/**
* Gets the payload delivered.
*
* @return the payload
*/
public Object getPayload() {
return payload;
}
}

/**
* Response data for a webhook delivery.
*/
public static class GHAppWebhookResponse {

private Map<String, String> headers;
private Object payload;
@JsonProperty("status_code")
private Integer statusCode;
private String status;

/**
* Gets response headers.
*
* @return the headers
*/
@SuppressFBWarnings(value = { "EI_EXPOSE_REP" }, justification = "Expected behavior")
public Map<String, String> getHeaders() {
return headers;
}

/**
* Gets response payload.
*
* @return the payload
*/
public Object getPayload() {
return payload;
}

/**
* Gets response status text when available.
*
* @return the status
*/
public String getStatus() {
return status;
}

/**
* Gets response status code when available.
*
* @return the status code
*/
public Integer getStatusCode() {
return statusCode;
}
}
}
32 changes: 32 additions & 0 deletions src/test/java/org/kohsuke/github/GHAppTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,38 @@ public void listInstallationRequests() throws IOException {
assertThat(appInstallation.getNodeId(), is("MDMwOkludGVncmF0aW9uSW5zdGFsbGF0aW9uUmVxdWVzdDEwMzcyMDQ="));
}

/**
* List webhook deliveries and trigger a redelivery.
*
* @throws IOException
* Signals that an I/O exception has occurred.
*/
@Test
public void listWebhookDeliveries() throws IOException {
GHApp app = gitHub.getApp();
List<GHAppWebhookDelivery> deliveries = app.listWebhookDeliveries().toList();

assertThat(deliveries, hasSize(1));

GHAppWebhookDelivery delivery = deliveries.get(0);
assertThat(delivery.getId(), is(123456789L));
assertThat(delivery.getGuid(), is("123e4567-e89b-12d3-a456-426614174000"));
assertThat(delivery.getDeliveredAt(), is(GitHubClient.parseInstant("2024-12-07T12:00:00Z")));
assertThat(delivery.isRedelivery(), is(false));
assertThat(delivery.getDuration(), is(closeTo(0.22D, 0.0001)));
assertThat(delivery.getStatus(), is("OK"));
assertThat(delivery.getStatusCode(), is(200));
assertThat(delivery.getEvent(), is(GHEvent.ISSUES));
assertThat(delivery.getAction(), is("opened"));
assertThat(delivery.getInstallationId(), is(987654321L));
assertThat(delivery.getRepositoryId(), is(123456789L));
assertThat(delivery.getRequest().getHeaders().get("content-type"), is("application/json"));
Map<?, ?> payload = (Map<?, ?>) delivery.getRequest().getPayload();
assertThat(payload.get("action"), is("opened"));

app.redeliverWebhookDelivery(delivery.getId());
}

/**
* List installations.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[
{
"id": 123456789,
"guid": "123e4567-e89b-12d3-a456-426614174000",
"delivered_at": "2024-12-07T12:00:00Z",
"redelivery": false,
"duration": 0.22,
"status": "OK",
"status_code": 200,
"event": "issues",
"action": "opened",
"installation_id": 987654321,
"repository_id": 123456789,
"url": "https://api.github.com/app/hook/deliveries/123456789",
"request": {
"headers": {
"content-type": "application/json",
"x-github-hook-id": "1111"
},
"payload": {
"action": "opened",
"hook": {
"id": 1111,
"type": "App"
}
}
},
"response": {
"headers": {
"content-type": "application/json"
},
"payload": {
"status": "delivered"
},
"status": "OK",
"status_code": 200
}
}
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"id": "a6f1c3aa-4fb8-4a85-b4c6-0d2c915c7f2b",
"name": "app-hook-deliveries",
"request": {
"url": "/app/hook/deliveries",
"method": "GET",
"headers": {
"Accept": {
"equalTo": "application/vnd.github+json"
}
}
},
"response": {
"status": 200,
"bodyFileName": "1-app_hook_deliveries.json",
"headers": {
"Content-Type": "application/json; charset=utf-8",
"Cache-Control": "public, max-age=60, s-maxage=60",
"Vary": [
"Accept",
"Accept-Encoding, Accept, X-Requested-With"
]
}
},
"uuid": "a6f1c3aa-4fb8-4a85-b4c6-0d2c915c7f2b",
"persistent": true,
"insertionIndex": 1
}
Loading