From 573c49429a40e37de2815171c0054dc73e22f0b4 Mon Sep 17 00:00:00 2001 From: dielduarte Date: Fri, 21 Aug 2026 19:31:24 -0300 Subject: [PATCH 1/2] feat: add broadcasts.clickedLinks() method --- .../services/broadcasts/Broadcasts.java | 41 ++++++++++ .../model/BroadcastClickedLink.java | 79 +++++++++++++++++++ ...tBroadcastClickedLinksResponseSuccess.java | 67 ++++++++++++++++ .../services/broadcasts/BroadcastsTest.java | 37 +++++++++ .../resend/services/util/BroadcastsUtil.java | 9 +++ 5 files changed, 233 insertions(+) create mode 100644 src/main/java/com/resend/services/broadcasts/model/BroadcastClickedLink.java create mode 100644 src/main/java/com/resend/services/broadcasts/model/ListBroadcastClickedLinksResponseSuccess.java diff --git a/src/main/java/com/resend/services/broadcasts/Broadcasts.java b/src/main/java/com/resend/services/broadcasts/Broadcasts.java index f8ca819..7bc64b0 100644 --- a/src/main/java/com/resend/services/broadcasts/Broadcasts.java +++ b/src/main/java/com/resend/services/broadcasts/Broadcasts.java @@ -183,6 +183,47 @@ public ListBroadcastRecipientsResponseSuccess recipients(String id, ListBroadcas return resendMapper.readValue(responseBody, ListBroadcastRecipientsResponseSuccess.class); } + /** + * Retrieves a broadcast's clicked links. + * + * @param id The unique identifier of the broadcast. + * @return A ListBroadcastClickedLinksResponseSuccess containing the broadcast's clicked links. + * @throws ResendException If an error occurs during the clicked links retrieval process. + */ + public ListBroadcastClickedLinksResponseSuccess clickedLinks(String id) throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/broadcasts/" + id + "/clicked-links", super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, ListBroadcastClickedLinksResponseSuccess.class); + } + + /** + * Retrieves a paginated list of a broadcast's clicked links. + * + * @param id The unique identifier of the broadcast. + * @param params The params used to customize the list. + * + * @return A ListBroadcastClickedLinksResponseSuccess containing the paginated list of clicked links. + * @throws ResendException If an error occurs during the clicked links retrieval process. + */ + public ListBroadcastClickedLinksResponseSuccess clickedLinks(String id, ListParams params) throws ResendException { + String pathWithQuery = "/broadcasts/" + id + "/clicked-links" + URLHelper.parse(params); + AbstractHttpResponse response = this.httpClient.perform(pathWithQuery, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, ListBroadcastClickedLinksResponseSuccess.class); + } + /** * Updates a Broadcast. * diff --git a/src/main/java/com/resend/services/broadcasts/model/BroadcastClickedLink.java b/src/main/java/com/resend/services/broadcasts/model/BroadcastClickedLink.java new file mode 100644 index 0000000..327f5b3 --- /dev/null +++ b/src/main/java/com/resend/services/broadcasts/model/BroadcastClickedLink.java @@ -0,0 +1,79 @@ +package com.resend.services.broadcasts.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a single clicked link row for a broadcast. + */ +public class BroadcastClickedLink { + + @JsonProperty("id") + private String id; + + @JsonProperty("url") + private String url; + + @JsonProperty("clicks") + private Integer clicks; + + @JsonProperty("unique_clicks") + private Integer uniqueClicks; + + /** + * Default constructor + */ + public BroadcastClickedLink() { + + } + + /** + * Constructs a new BroadcastClickedLink instance. + * + * @param id An opaque cursor for this row, used only for pagination. It does not identify any entity in Resend. + * @param url The URL that was clicked. + * @param clicks Total number of clicks on this URL. + * @param uniqueClicks Number of unique clicks on this URL. + */ + public BroadcastClickedLink(String id, String url, Integer clicks, Integer uniqueClicks) { + this.id = id; + this.url = url; + this.clicks = clicks; + this.uniqueClicks = uniqueClicks; + } + + /** + * Gets the opaque pagination cursor for this row. + * + * @return the pagination cursor + */ + public String getId() { + return id; + } + + /** + * Gets the URL that was clicked. + * + * @return the clicked URL + */ + public String getUrl() { + return url; + } + + /** + * Gets the total number of clicks on this URL. + * + * @return the total click count + */ + public Integer getClicks() { + return clicks; + } + + /** + * Gets the number of unique clicks on this URL. + * + * @return the unique click count + */ + public Integer getUniqueClicks() { + return uniqueClicks; + } +} diff --git a/src/main/java/com/resend/services/broadcasts/model/ListBroadcastClickedLinksResponseSuccess.java b/src/main/java/com/resend/services/broadcasts/model/ListBroadcastClickedLinksResponseSuccess.java new file mode 100644 index 0000000..3b3d21c --- /dev/null +++ b/src/main/java/com/resend/services/broadcasts/model/ListBroadcastClickedLinksResponseSuccess.java @@ -0,0 +1,67 @@ +package com.resend.services.broadcasts.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * Represents a paginated list of a broadcast's clicked links. + */ +public class ListBroadcastClickedLinksResponseSuccess { + + @JsonProperty("object") + private String object; + + @JsonProperty("data") + private List data; + + @JsonProperty("has_more") + private Boolean hasMore; + + /** + * Default constructor + */ + public ListBroadcastClickedLinksResponseSuccess() { + + } + + /** + * Constructs a new ListBroadcastClickedLinksResponseSuccess instance. + * + * @param object Type of the object (e.g., "list"). + * @param data List of BroadcastClickedLink objects. + * @param hasMore Indicate if there are more items to be returned. + */ + public ListBroadcastClickedLinksResponseSuccess(String object, List data, Boolean hasMore) { + this.object = object; + this.data = data; + this.hasMore = hasMore; + } + + /** + * Gets the type of the object. + * + * @return the object type (e.g., "list") + */ + public String getObject() { + return object; + } + + /** + * Gets the list of BroadcastClickedLink objects. + * + * @return the list of clicked links + */ + public List getData() { + return data; + } + + /** + * Gets the indicator whether there are more items available for pagination. + * + * @return Whether there are more items available for pagination. + */ + public Boolean hasMore() { + return hasMore; + } +} diff --git a/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java b/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java index 10465e4..95b874e 100644 --- a/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java +++ b/src/test/java/com/resend/services/broadcasts/BroadcastsTest.java @@ -52,6 +52,12 @@ public class BroadcastsTest { "{\"id\":\"3\",\"audience_id\":\"" + AUDIENCE_ID + "\",\"status\":\"queued\",\"created_at\":\"2024-12-03 08:45:00+00\"}" + "]}"; + private static final String CLICKED_LINKS_RESPONSE_JSON = + "{\"object\":\"list\",\"has_more\":false,\"data\":[" + + "{\"id\":\"b2Zmc2V0OjA\",\"url\":\"https://resend.com/pricing\",\"clicks\":42,\"unique_clicks\":30}," + + "{\"id\":\"b2Zmc2V0OjE\",\"url\":\"https://resend.com/docs\",\"clicks\":17,\"unique_clicks\":15}" + + "]}"; + private static final String SEND_RESPONSE_JSON = "{\"id\":\"" + BROADCAST_ID + "\"}"; @@ -203,6 +209,37 @@ public void testListBroadcastsWithPagination_Success() throws ResendException { assertEquals("list", response.getObject()); } + @Test + public void testClickedLinks_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CLICKED_LINKS_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/broadcasts/" + GET_BROADCAST_ID + "/clicked-links"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ListBroadcastClickedLinksResponseSuccess response = broadcasts.clickedLinks(GET_BROADCAST_ID); + + assertNotNull(response); + assertEquals(2, response.getData().size()); + assertEquals("list", response.getObject()); + assertEquals("https://resend.com/pricing", response.getData().get(0).getUrl()); + assertEquals(42, response.getData().get(0).getClicks()); + assertEquals(30, response.getData().get(0).getUniqueClicks()); + } + + @Test + public void testClickedLinksWithPagination_Success() throws ResendException { + ListParams params = ListParams.builder().limit(2).build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, CLICKED_LINKS_RESPONSE_JSON, true); + + when(httpClient.perform(startsWith("/broadcasts/" + GET_BROADCAST_ID + "/clicked-links?"), anyString(), eq(HttpMethod.GET), isNull(), any(MediaType.class))) + .thenReturn(httpResponse); + + ListBroadcastClickedLinksResponseSuccess response = broadcasts.clickedLinks(GET_BROADCAST_ID, params); + + assertNotNull(response); + assertEquals("list", response.getObject()); + } + @Test public void testUpdateBroadcast_Success() throws ResendException { UpdateBroadcastOptions updateOptions = BroadcastsUtil.updateBroadcastRequest(); diff --git a/src/test/java/com/resend/services/util/BroadcastsUtil.java b/src/test/java/com/resend/services/util/BroadcastsUtil.java index a7bb6b9..88f99fd 100644 --- a/src/test/java/com/resend/services/util/BroadcastsUtil.java +++ b/src/test/java/com/resend/services/util/BroadcastsUtil.java @@ -80,6 +80,15 @@ public static ListBroadcastsResponseSuccess createBroadcastsListResponse() { return new ListBroadcastsResponseSuccess("list", broadcastList, true); } + public static ListBroadcastClickedLinksResponseSuccess createBroadcastClickedLinksResponse() { + List clickedLinks = new ArrayList<>(); + + clickedLinks.add(new BroadcastClickedLink("b2Zmc2V0OjA", "https://resend.com/pricing", 42, 30)); + clickedLinks.add(new BroadcastClickedLink("b2Zmc2V0OjE", "https://resend.com/docs", 17, 15)); + + return new ListBroadcastClickedLinksResponseSuccess("list", clickedLinks, false); + } + public static SendBroadcastOptions sendBroadcastRequest() { return SendBroadcastOptions.builder() .scheduledAt("2024-12-18T15:00:00.000Z") From 22af4bb261ac473cf9c2d5d48aa4edf6a6cadac6 Mon Sep 17 00:00:00 2001 From: dielduarte Date: Fri, 21 Aug 2026 21:51:00 -0300 Subject: [PATCH 2/2] fix: remove unused test fixture helper --- .../java/com/resend/services/util/BroadcastsUtil.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/src/test/java/com/resend/services/util/BroadcastsUtil.java b/src/test/java/com/resend/services/util/BroadcastsUtil.java index 88f99fd..a7bb6b9 100644 --- a/src/test/java/com/resend/services/util/BroadcastsUtil.java +++ b/src/test/java/com/resend/services/util/BroadcastsUtil.java @@ -80,15 +80,6 @@ public static ListBroadcastsResponseSuccess createBroadcastsListResponse() { return new ListBroadcastsResponseSuccess("list", broadcastList, true); } - public static ListBroadcastClickedLinksResponseSuccess createBroadcastClickedLinksResponse() { - List clickedLinks = new ArrayList<>(); - - clickedLinks.add(new BroadcastClickedLink("b2Zmc2V0OjA", "https://resend.com/pricing", 42, 30)); - clickedLinks.add(new BroadcastClickedLink("b2Zmc2V0OjE", "https://resend.com/docs", 17, 15)); - - return new ListBroadcastClickedLinksResponseSuccess("list", clickedLinks, false); - } - public static SendBroadcastOptions sendBroadcastRequest() { return SendBroadcastOptions.builder() .scheduledAt("2024-12-18T15:00:00.000Z")