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();