diff --git a/src/main/java/com/resend/services/emails/Emails.java b/src/main/java/com/resend/services/emails/Emails.java index 05b861d..1770d03 100644 --- a/src/main/java/com/resend/services/emails/Emails.java +++ b/src/main/java/com/resend/services/emails/Emails.java @@ -160,6 +160,48 @@ public CancelEmailResponse cancel(String emailId) throws ResendException { return resendMapper.readValue(responseBody, CancelEmailResponse.class); } + /** + * Creates a shareable link for an email, using the default expiration. + * + * @param emailId The unique identifier of the email. + * @return The share link details. + * @throws ResendException If an error occurs while creating the shareable link. + */ + public ShareEmailResponse share(String emailId) throws ResendException { + + AbstractHttpResponse response = this.httpClient.perform("/emails/" + emailId + "/share", super.apiKey, HttpMethod.POST, "", MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, ShareEmailResponse.class); + } + + /** + * Creates a shareable link for an email. + * + * @param emailId The unique identifier of the email. + * @param shareEmailOptions The options for the shareable link, such as its expiration. + * @return The share link details. + * @throws ResendException If an error occurs while creating the shareable link. + */ + public ShareEmailResponse share(String emailId, ShareEmailOptions shareEmailOptions) throws ResendException { + + String payload = super.resendMapper.writeValue(shareEmailOptions); + AbstractHttpResponse response = this.httpClient.perform("/emails/" + emailId + "/share", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, ShareEmailResponse.class); + } + /** * Retrieves a list of emails and returns a List. * diff --git a/src/main/java/com/resend/services/emails/model/ShareEmailOptions.java b/src/main/java/com/resend/services/emails/model/ShareEmailOptions.java new file mode 100644 index 0000000..746644c --- /dev/null +++ b/src/main/java/com/resend/services/emails/model/ShareEmailOptions.java @@ -0,0 +1,69 @@ +package com.resend.services.emails.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a request to create a shareable link for an email. + */ +public class ShareEmailOptions { + + @JsonProperty("expires_in") + private final String expiresIn; + + private ShareEmailOptions(Builder builder) { + + this.expiresIn = builder.expiresIn; + + } + + /** + * Retrieves how long the shareable link stays valid for. + * + * @return The expiration duration of the shareable link. + */ + public String getExpiresIn() { + return expiresIn; + } + + /** + * Creates a new builder instance to construct ShareEmailOptions. + * + * @return A new builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing ShareEmailOptions instances. + */ + public static class Builder { + /** + * Creates a new Builder instance. + */ + public Builder() { + } + + private String expiresIn; + + /** + * Set how long the shareable link stays valid for. + * + * @param expiresIn A human-readable duration (e.g., "10m", "2 hours", "1 day", "1h 30m"). Defaults to "48h" and is capped at 48 hours. + * @return This builder instance for method chaining. + */ + public Builder expiresIn(String expiresIn) { + this.expiresIn = expiresIn; + return this; + } + + /** + * Builds and returns a {@code ShareEmailOptions} based on the configured properties. + * + * @return A {@code ShareEmailOptions} instance. + */ + public ShareEmailOptions build() { + return new ShareEmailOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/emails/model/ShareEmailResponse.java b/src/main/java/com/resend/services/emails/model/ShareEmailResponse.java new file mode 100644 index 0000000..c329f3e --- /dev/null +++ b/src/main/java/com/resend/services/emails/model/ShareEmailResponse.java @@ -0,0 +1,51 @@ +package com.resend.services.emails.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the share email response. + */ +public class ShareEmailResponse extends EmailResponse { + + /** + * The shareable link URL for the email. + */ + @JsonProperty("url") + private String url; + + /** + * Constructs a new instance of {@code ShareEmailResponse}. + */ + public ShareEmailResponse() { + } + + /** + * Constructs a ShareEmailResponse with the provided ID, object and URL. + * + * @param id The ID associated with the email. + * @param object The resource object. + * @param url The shareable link URL for the email. + */ + public ShareEmailResponse(String id, String object, String url) { + super(id, object); + this.url = url; + } + + /** + * Retrieves the shareable link URL for the email. + * + * @return The shareable link URL. + */ + public String getUrl() { + return url; + } + + /** + * Sets the shareable link URL for the email. + * + * @param url The shareable link URL to be set. + */ + public void setUrl(String url) { + this.url = url; + } +} diff --git a/src/test/java/com/resend/services/emails/EmailsTest.java b/src/test/java/com/resend/services/emails/EmailsTest.java index 6f2fe46..5adad08 100644 --- a/src/test/java/com/resend/services/emails/EmailsTest.java +++ b/src/test/java/com/resend/services/emails/EmailsTest.java @@ -43,6 +43,9 @@ public class EmailsTest { private static final String CANCEL_RESPONSE_JSON = "{\"id\":\"" + UPDATE_EMAIL_ID + "\",\"object\":\"emails\"}"; + private static final String SHARE_RESPONSE_JSON = + "{\"object\":\"email\",\"id\":\"" + UPDATE_EMAIL_ID + "\",\"url\":\"https://resend.com/share/abc123\"}"; + private static final String LIST_RESPONSE_JSON = "{\"object\":\"emails\",\"has_more\":true,\"data\":[" + "{\"id\":\"email_1\",\"from\":\"sender1@example.com\"}," + @@ -156,6 +159,60 @@ public void testCancelEmail_Success() throws ResendException { assertEquals(UPDATE_EMAIL_ID, response.getId()); } + @Test + public void testShareEmail_DefaultExpiresIn_Success() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SHARE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/emails/" + UPDATE_EMAIL_ID + "/share"), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); + + ShareEmailResponse response = emails.share(UPDATE_EMAIL_ID); + + assertNotNull(response); + assertEquals(UPDATE_EMAIL_ID, response.getId()); + assertEquals("https://resend.com/share/abc123", response.getUrl()); + } + + @Test + public void testShareEmail_CustomExpiresIn_Success() throws ResendException { + ShareEmailOptions shareEmailOptions = EmailsUtil.shareEmailOptions(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, SHARE_RESPONSE_JSON, true); + + when(httpClient.perform(eq("/emails/" + UPDATE_EMAIL_ID + "/share"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ShareEmailResponse response = emails.share(UPDATE_EMAIL_ID, shareEmailOptions); + + assertNotNull(response); + assertEquals(UPDATE_EMAIL_ID, response.getId()); + assertEquals("https://resend.com/share/abc123", response.getUrl()); + } + + @Test + public void testShareEmail_InvalidExpiresIn_ThrowsResendException() throws ResendException { + ShareEmailOptions shareEmailOptions = ShareEmailOptions.builder().expiresIn("72h").build(); + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(422, + "{\"name\":\"validation_error\",\"message\":\"expires_in exceeds the 48 hour maximum\"}", false); + + when(httpClient.perform(eq("/emails/" + UPDATE_EMAIL_ID + "/share"), anyString(), eq(HttpMethod.POST), anyString(), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> emails.share(UPDATE_EMAIL_ID, shareEmailOptions)); + assertEquals(422, (int) ex.getStatusCode()); + } + + @Test + public void testShareEmail_NotFound_ThrowsResendException() throws ResendException { + AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(404, + "{\"name\":\"not_found\",\"message\":\"Email not found\"}", false); + + when(httpClient.perform(eq("/emails/" + EMAIL_ID + "/share"), anyString(), eq(HttpMethod.POST), eq(""), any(MediaType.class))) + .thenReturn(httpResponse); + + ResendException ex = assertThrows(ResendException.class, () -> emails.share(EMAIL_ID)); + assertEquals(404, (int) ex.getStatusCode()); + } + @Test public void testListEmails_Success() throws ResendException { AbstractHttpResponse httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true); diff --git a/src/test/java/com/resend/services/util/EmailsUtil.java b/src/test/java/com/resend/services/util/EmailsUtil.java index d594d42..64e3ac4 100644 --- a/src/test/java/com/resend/services/util/EmailsUtil.java +++ b/src/test/java/com/resend/services/util/EmailsUtil.java @@ -99,6 +99,16 @@ public static CancelEmailResponse cancelEmailResponse() { return new CancelEmailResponse("123", "emails"); } + public static ShareEmailOptions shareEmailOptions() { + return ShareEmailOptions.builder() + .expiresIn("1 day") + .build(); + } + + public static ShareEmailResponse shareEmailResponse() { + return new ShareEmailResponse("123", "email", "https://resend.com/share/abc123"); + } + public static List createBatchEmailOptions() { return Arrays.asList(createEmailOptions(), createEmailOptions()); }