Skip to content
Merged
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
42 changes: 42 additions & 0 deletions src/main/java/com/resend/services/emails/Emails.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> response = this.httpClient.perform("/emails/" + emailId + "/share", super.apiKey, HttpMethod.POST, "", MediaType.get("application/json"));
Comment thread
dielduarte marked this conversation as resolved.

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<String> 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.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
Original file line number Diff line number Diff line change
@@ -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;
}
}
57 changes: 57 additions & 0 deletions src/test/java/com/resend/services/emails/EmailsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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\"}," +
Expand Down Expand Up @@ -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<String> 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<String> 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<String> 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<String> 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<String> httpResponse = new AbstractHttpResponse<>(200, LIST_RESPONSE_JSON, true);
Expand Down
10 changes: 10 additions & 0 deletions src/test/java/com/resend/services/util/EmailsUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -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<CreateEmailOptions> createBatchEmailOptions() {
return Arrays.asList(createEmailOptions(), createEmailOptions());
}
Expand Down
Loading