From 71e27742e9ebba2c4eff4aac0f68955dfbfe6779 Mon Sep 17 00:00:00 2001 From: Kewyn Akshlley Date: Thu, 30 Oct 2025 14:37:28 -0300 Subject: [PATCH 1/3] feat: introduce the /segment API endpoints --- src/main/java/com/resend/Resend.java | 12 ++ .../resend/services/audiences/Audiences.java | 10 ++ .../broadcasts/model/BroadcastOptions.java | 36 ++++++ .../resend/services/contacts/Contacts.java | 12 +- .../contacts/model/ContactOptions.java | 40 ++++++ .../contacts/model/CreateContactOptions.java | 30 +++++ .../contacts/model/UpdateContactOptions.java | 30 +++++ .../resend/services/segments/Segments.java | 120 ++++++++++++++++++ .../services/segments/model/BaseSegment.java | 50 ++++++++ .../segments/model/CreateSegmentOptions.java | 66 ++++++++++ .../model/CreateSegmentResponseSuccess.java | 41 ++++++ .../model/GetSegmentResponseSuccess.java | 42 ++++++ .../model/ListSegmentsResponseSuccess.java | 66 ++++++++++ .../model/RemoveSegmentResponseSuccess.java | 63 +++++++++ .../services/segments/model/Segment.java | 40 ++++++ .../services/webhooks/model/WebhookEvent.java | 20 +++ .../webhooks/model/WebhookStatus.java | 7 + .../services/segments/SegmentsTest.java | 102 +++++++++++++++ .../resend/services/util/SegmentsUtil.java | 39 ++++++ 19 files changed, 822 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/resend/services/segments/Segments.java create mode 100644 src/main/java/com/resend/services/segments/model/BaseSegment.java create mode 100644 src/main/java/com/resend/services/segments/model/CreateSegmentOptions.java create mode 100644 src/main/java/com/resend/services/segments/model/CreateSegmentResponseSuccess.java create mode 100644 src/main/java/com/resend/services/segments/model/GetSegmentResponseSuccess.java create mode 100644 src/main/java/com/resend/services/segments/model/ListSegmentsResponseSuccess.java create mode 100644 src/main/java/com/resend/services/segments/model/RemoveSegmentResponseSuccess.java create mode 100644 src/main/java/com/resend/services/segments/model/Segment.java create mode 100644 src/test/java/com/resend/services/segments/SegmentsTest.java create mode 100644 src/test/java/com/resend/services/util/SegmentsUtil.java diff --git a/src/main/java/com/resend/Resend.java b/src/main/java/com/resend/Resend.java index 0de7de9..e5c7040 100644 --- a/src/main/java/com/resend/Resend.java +++ b/src/main/java/com/resend/Resend.java @@ -7,6 +7,7 @@ import com.resend.services.contacts.Contacts; import com.resend.services.domains.Domains; import com.resend.services.emails.Emails; +import com.resend.services.segments.Segments; import com.resend.services.webhooks.Webhooks; import com.resend.services.receiving.Receiving; import com.resend.services.topics.Topics; @@ -71,11 +72,22 @@ public Contacts contacts() { * Returns an Audience object that can be used to interact with the Audiences service. * * @return an Audiences object. + * @deprecated Use {@link #segments()} instead. */ + @Deprecated public Audiences audiences() { return new Audiences(apiKey); } + /** + * Returns a Segments object that can be used to interact with the Segments service. + * + * @return a Segments object. + */ + public Segments segments() { + return new Segments(apiKey); + } + /** * Returns a Batch object that can be used to interact with the Batch service. * diff --git a/src/main/java/com/resend/services/audiences/Audiences.java b/src/main/java/com/resend/services/audiences/Audiences.java index b57b2f7..958de23 100644 --- a/src/main/java/com/resend/services/audiences/Audiences.java +++ b/src/main/java/com/resend/services/audiences/Audiences.java @@ -29,7 +29,9 @@ public Audiences(final String apiKey) { * @param createAudienceOptions The Audience details. * @return The details of the created audience. * @throws ResendException If an error occurs during the Audience creation process. + * @deprecated Use {@link com.resend.services.segments.Segments#create(com.resend.services.segments.model.CreateSegmentOptions)} instead. */ + @Deprecated public CreateAudienceResponseSuccess create(CreateAudienceOptions createAudienceOptions) throws ResendException { String payload = super.resendMapper.writeValue(createAudienceOptions); AbstractHttpResponse response = httpClient.perform("/audiences", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); @@ -47,7 +49,9 @@ public CreateAudienceResponseSuccess create(CreateAudienceOptions createAudience * * @return A ListAudiencesResponseSuccess containing the list of audiences. * @throws ResendException If an error occurs during the audiences list retrieval process. + * @deprecated Use {@link com.resend.services.segments.Segments#list()} instead. */ + @Deprecated public ListAudiencesResponseSuccess list() throws ResendException { AbstractHttpResponse response = this.httpClient.perform("/audiences", super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); @@ -66,7 +70,9 @@ public ListAudiencesResponseSuccess list() throws ResendException { * * @return A ListAudiencesResponseSuccess containing the paginated list of audiences. * @throws ResendException If an error occurs during the audiences list retrieval process. + * @deprecated Use {@link com.resend.services.segments.Segments#list(ListParams)} instead. */ + @Deprecated public ListAudiencesResponseSuccess list(ListParams params) throws ResendException { String pathWithQuery = "/audiences" + URLHelper.parse(params); AbstractHttpResponse response = this.httpClient.perform(pathWithQuery, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); @@ -86,7 +92,9 @@ public ListAudiencesResponseSuccess list(ListParams params) throws ResendExcepti * @param id The unique identifier of the audience. * @return The retrieved audience details. * @throws ResendException If an error occurs while retrieving the audience. + * @deprecated Use {@link com.resend.services.segments.Segments#get(String)} instead. */ + @Deprecated public GetAudienceResponseSuccess get(String id) throws ResendException { AbstractHttpResponse response = this.httpClient.perform("/audiences/" +id, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); @@ -105,7 +113,9 @@ public GetAudienceResponseSuccess get(String id) throws ResendException { * @param id The unique identifier of the audience to delete. * @return The RemoveAudiencesResponseSuccess with the details of the removed audience. * @throws ResendException If an error occurs during the audience deletion process. + * @deprecated Use {@link com.resend.services.segments.Segments#remove(String)} instead. */ + @Deprecated public RemoveAudienceResponseSuccess remove(String id) throws ResendException { AbstractHttpResponse response = httpClient.perform("/audiences/" +id, super.apiKey, HttpMethod.DELETE, "", null); diff --git a/src/main/java/com/resend/services/broadcasts/model/BroadcastOptions.java b/src/main/java/com/resend/services/broadcasts/model/BroadcastOptions.java index 9ea8bdd..aa218c2 100644 --- a/src/main/java/com/resend/services/broadcasts/model/BroadcastOptions.java +++ b/src/main/java/com/resend/services/broadcasts/model/BroadcastOptions.java @@ -9,8 +9,12 @@ */ public class BroadcastOptions { @JsonProperty("audience_id") + @Deprecated private final String audienceId; + @JsonProperty("segment_id") + private final String segmentId; + @JsonProperty("from") private final String from; @@ -36,6 +40,7 @@ public class BroadcastOptions { */ protected BroadcastOptions(Builder builder) { this.audienceId = builder.audienceId; + this.segmentId = builder.segmentId; this.from = builder.from; this.subject = builder.subject; this.replyTo = builder.replyTo; @@ -48,11 +53,22 @@ protected BroadcastOptions(Builder builder) { * Gets the audience ID. * * @return the unique identifier of the audience. + * @deprecated Use {@link #getSegmentId()} instead. */ + @Deprecated public String getAudienceId() { return audienceId; } + /** + * Gets the segment ID. + * + * @return the unique identifier of the segment. + */ + public String getSegmentId() { + return segmentId; + } + /** * Gets the sender's email address. * @@ -114,9 +130,16 @@ protected static abstract class Builder response = httpClient.perform("/audiences/" + createContactOptions.getAudienceId()+ "/contacts" , super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/audiences/" + segmentId + "/contacts" , super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -101,8 +102,9 @@ public GetContactResponseSuccess get(GetContactOptions params) throws ResendExce } String contactIdentifier = (id != null && !id.isEmpty()) ? id : email; + String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); - AbstractHttpResponse response = this.httpClient.perform("/audiences/" +params.getAudienceId()+ "/contacts/" +contactIdentifier, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + AbstractHttpResponse response = this.httpClient.perform("/audiences/" + segmentId + "/contacts/" + contactIdentifier, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -127,8 +129,9 @@ public RemoveContactResponseSuccess remove(RemoveContactOptions params) throws R } String pathParameter = params.getId() != null ? params.getId() : params.getEmail(); + String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); - AbstractHttpResponse response = httpClient.perform("/audiences/" +params.getAudienceId()+ "/contacts/" + pathParameter, super.apiKey, HttpMethod.DELETE, "", null); + AbstractHttpResponse response = httpClient.perform("/audiences/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.DELETE, "", null); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -153,9 +156,10 @@ public UpdateContactResponseSuccess update(UpdateContactOptions params) throws R } String pathParameter = params.getId() != null ? params.getId() : params.getEmail(); + String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); String payload = super.resendMapper.writeValue(params); - AbstractHttpResponse response = httpClient.perform("/audiences/" +params.getAudienceId()+ "/contacts/" + pathParameter, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/audiences/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); diff --git a/src/main/java/com/resend/services/contacts/model/ContactOptions.java b/src/main/java/com/resend/services/contacts/model/ContactOptions.java index 34c9b9a..f60744d 100644 --- a/src/main/java/com/resend/services/contacts/model/ContactOptions.java +++ b/src/main/java/com/resend/services/contacts/model/ContactOptions.java @@ -15,10 +15,18 @@ public abstract class ContactOptions { /** * The audience_id of the contact options + * @deprecated Use {@link #segmentId} instead. */ + @Deprecated @JsonProperty("audience_id") protected final String audienceId; + /** + * The segment_id of the contact options + */ + @JsonProperty("segment_id") + protected final String segmentId; + /** * The email of the contact options */ @@ -33,6 +41,7 @@ public abstract class ContactOptions { protected ContactOptions(Builder builder) { this.id = builder.id; this.audienceId = builder.audienceId; + this.segmentId = builder.segmentId; this.email = builder.email; } @@ -49,11 +58,22 @@ public String getId() { * Get the audienceId of the ContactOptions. * * @return The audienceId of the ContactOptions. + * @deprecated Use {@link #getSegmentId()} instead. */ + @Deprecated public String getAudienceId() { return audienceId; } + /** + * Get the segmentId of the ContactOptions. + * + * @return The segmentId of the ContactOptions. + */ + public String getSegmentId() { + return segmentId; + } + /** * Get the email of the ContactOptions. * @@ -74,9 +94,16 @@ protected static abstract class Builder response = httpClient.perform("/audiences", 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, CreateSegmentResponseSuccess.class); + } + + /** + * Retrieves a list of segments and returns a ListSegmentsResponseSuccess. + * + * @return A ListSegmentsResponseSuccess containing the list of segments. + * @throws ResendException If an error occurs during the segments list retrieval process. + */ + public ListSegmentsResponseSuccess list() throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/audiences", 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, ListSegmentsResponseSuccess.class); + } + + /** + * Retrieves a paginated list of segments and returns a ListSegmentsResponseSuccess. + * @param params The params used to customize the list. + * + * @return A ListSegmentsResponseSuccess containing the paginated list of segments. + * @throws ResendException If an error occurs during the segments list retrieval process. + */ + public ListSegmentsResponseSuccess list(ListParams params) throws ResendException { + String pathWithQuery = "/audiences" + 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, ListSegmentsResponseSuccess.class); + } + + /** + * Retrieves a segment by its unique identifier. + * + * @param id The unique identifier of the segment. + * @return The retrieved segment details. + * @throws ResendException If an error occurs while retrieving the segment. + */ + public GetSegmentResponseSuccess get(String id) throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/audiences/" +id, 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, GetSegmentResponseSuccess.class); + } + + /** + * Deletes a segment based on the provided segment ID. + * + * @param id The unique identifier of the segment to delete. + * @return The RemoveSegmentResponseSuccess with the details of the removed segment. + * @throws ResendException If an error occurs during the segment deletion process. + */ + public RemoveSegmentResponseSuccess remove(String id) throws ResendException { + AbstractHttpResponse response = httpClient.perform("/audiences/" +id, super.apiKey, HttpMethod.DELETE, "", null); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, RemoveSegmentResponseSuccess.class); + } +} diff --git a/src/main/java/com/resend/services/segments/model/BaseSegment.java b/src/main/java/com/resend/services/segments/model/BaseSegment.java new file mode 100644 index 0000000..3b6a2a5 --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/BaseSegment.java @@ -0,0 +1,50 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a segment. + */ +public abstract class BaseSegment { + @JsonProperty("id") + private String id; + + @JsonProperty("name") + private String name; + + /** + * Default constructor + */ + public BaseSegment() { + + } + + /** + * Constructs a segment. + * + * @param id The ID of the segment. + * @param name The name of the segment. + */ + public BaseSegment(final String id, final String name) { + this.id = id; + this.name = name; + } + + /** + * Gets the ID of the segment. + * + * @return The ID of the segment. + */ + public String getId() { + return id; + } + + /** + * Gets the name of the segment. + * + * @return The name of the segment. + */ + public String getName() { + return name; + } +} diff --git a/src/main/java/com/resend/services/segments/model/CreateSegmentOptions.java b/src/main/java/com/resend/services/segments/model/CreateSegmentOptions.java new file mode 100644 index 0000000..08fdea4 --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/CreateSegmentOptions.java @@ -0,0 +1,66 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a request to create a segment with options. + */ +public class CreateSegmentOptions { + + @JsonProperty("name") + private final String name; + + /** + * Constructs a Segment Options object using the provided builder. + * + * @param builder The builder to construct the Segment Options. + */ + public CreateSegmentOptions(Builder builder) { + this.name = builder.name; + } + + /** + * Get the name of the Segment Options. + * + * @return The name of the Segment Options. + */ + public String getName() { + return name; + } + + /** + * Create a new builder instance for constructing CreateSegmentOptions objects. + * + * @return A new builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing CreateSegmentOptions objects. + */ + public static class Builder { + private String name; + + /** + * Set the name of the Segment Options. + * + * @param name The name of the Segment Options. + * @return The builder instance. + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Build a new CreateSegmentOptions object. + * + * @return A new CreateSegmentOptions object. + */ + public CreateSegmentOptions build() { + return new CreateSegmentOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/segments/model/CreateSegmentResponseSuccess.java b/src/main/java/com/resend/services/segments/model/CreateSegmentResponseSuccess.java new file mode 100644 index 0000000..0510d5d --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/CreateSegmentResponseSuccess.java @@ -0,0 +1,41 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a successful response for creating a segment. + * Extends the Segments class. + */ +public class CreateSegmentResponseSuccess extends BaseSegment { + + @JsonProperty("object") + private String object; + + /** + * Default constructor + */ + public CreateSegmentResponseSuccess() { + + } + + /** + * Constructs a successful response for creating a segment. + * + * @param id The ID of the segment. + * @param name The name of the segment. + * @param object The object of the segment. + */ + public CreateSegmentResponseSuccess(String id, String name, String object) { + super(id, name); + this.object = object; + } + + /** + * Get the object. + * + * @return The type of the data. + */ + public String getObject() { + return object; + } +} diff --git a/src/main/java/com/resend/services/segments/model/GetSegmentResponseSuccess.java b/src/main/java/com/resend/services/segments/model/GetSegmentResponseSuccess.java new file mode 100644 index 0000000..ad79fbe --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/GetSegmentResponseSuccess.java @@ -0,0 +1,42 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a successful response for retrieving a segment. + * Extends the Segments class. + */ +public class GetSegmentResponseSuccess extends Segment { + + @JsonProperty("object") + private String object; + + /** + * Default constructor + */ + public GetSegmentResponseSuccess() { + + } + + /** + * Constructs a successful response for retrieving a segment. + * + * @param id The ID of the segment. + * @param name The name of the segment. + * @param created_at The creation timestamp of the segment. + * @param object Additional information about the segment. + */ + public GetSegmentResponseSuccess(final String id, final String name, final String created_at, final String object) { + super(id, name, created_at); + this.object = object; + } + + /** + * Get the additional information about the segment. + * + * @return The additional information about the segment. + */ + public String getObject() { + return object; + } +} diff --git a/src/main/java/com/resend/services/segments/model/ListSegmentsResponseSuccess.java b/src/main/java/com/resend/services/segments/model/ListSegmentsResponseSuccess.java new file mode 100644 index 0000000..156170c --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/ListSegmentsResponseSuccess.java @@ -0,0 +1,66 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * Represents a successful response for listing segments. + */ +public class ListSegmentsResponseSuccess { + + @JsonProperty("data") + private List data; + + @JsonProperty("object") + private String object; + + @JsonProperty("has_more") + private Boolean hasMore; + + /** + * Default constructor + */ + public ListSegmentsResponseSuccess() { + } + + /** + * Constructs a successful response for listing segments. + * + * @param data The list of segments. + * @param object The object of the segments. + * @param hasMore Indicate if there are more items to be returned. + */ + public ListSegmentsResponseSuccess(List data, String object, Boolean hasMore) { + this.data = data; + this.object = object; + this.hasMore = hasMore; + } + + /** + * Get the list of segments. + * + * @return The list of segments. + */ + public List getData() { + return data; + } + + /** + * Get the object. + * + * @return The type of the data. + */ + public String getObject() { + return object; + } + + /** + * 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/main/java/com/resend/services/segments/model/RemoveSegmentResponseSuccess.java b/src/main/java/com/resend/services/segments/model/RemoveSegmentResponseSuccess.java new file mode 100644 index 0000000..c714912 --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/RemoveSegmentResponseSuccess.java @@ -0,0 +1,63 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a successful response for removing a segment. + */ +public class RemoveSegmentResponseSuccess { + + @JsonProperty("id") + private String id; + @JsonProperty("object") + private String object; + + @JsonProperty("deleted") + private boolean deleted; + + /** + * Default constructor + */ + public RemoveSegmentResponseSuccess() { + } + + /** + * Constructs a successful response for removing a segment. + * + * @param id The ID of the removed segment. + * @param object The Object of the removed segment. + * @param deleted The boolean indicating if the data was deleted. + */ + public RemoveSegmentResponseSuccess(final String id, final String object, final boolean deleted) { + this.id = id; + this.object = object; + this.deleted = deleted; + } + + /** + * Get the ID of the removed segment. + * + * @return The ID of the removed segment. + */ + public String getId() { + return id; + } + + /** + * Get the Object of the removed segment. + * + * @return The Object of the removed segment. + */ + public String getObject() { + return object; + } + + /** + * Get the state of the removed segment. + * + * @return The boolean indicating the state of the segment. + */ + public boolean getDeleted() { + return deleted; + } +} diff --git a/src/main/java/com/resend/services/segments/model/Segment.java b/src/main/java/com/resend/services/segments/model/Segment.java new file mode 100644 index 0000000..36c2091 --- /dev/null +++ b/src/main/java/com/resend/services/segments/model/Segment.java @@ -0,0 +1,40 @@ +package com.resend.services.segments.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a segment. + */ +public class Segment extends BaseSegment { + + @JsonProperty("created_at") + private String createdAt; + + /** + * Default constructor + */ + public Segment() { + + } + + /** + * Constructs a segment. + * + * @param createdAt The creation timestamp of the segment. + * @param id The ID of the segment. + * @param name The name of the segment. + */ + public Segment(final String id, final String name, final String createdAt) { + super(id, name); + this.createdAt = createdAt; + } + + /** + * Gets the creation timestamp of the segment. + * + * @return The creation timestamp of the segment. + */ + public String getCreatedAt() { + return createdAt; + } +} diff --git a/src/main/java/com/resend/services/webhooks/model/WebhookEvent.java b/src/main/java/com/resend/services/webhooks/model/WebhookEvent.java index e4733f4..db79f5a 100644 --- a/src/main/java/com/resend/services/webhooks/model/WebhookEvent.java +++ b/src/main/java/com/resend/services/webhooks/model/WebhookEvent.java @@ -6,20 +6,35 @@ * Represents the types of events that can trigger a webhook. */ public enum WebhookEvent { + /** Triggered when an email is sent. */ EMAIL_SENT("email.sent"), + /** Triggered when an email is delivered. */ EMAIL_DELIVERED("email.delivered"), + /** Triggered when an email delivery is delayed. */ EMAIL_DELIVERY_DELAYED("email.delivery_delayed"), + /** Triggered when an email complaint is received. */ EMAIL_COMPLAINED("email.complained"), + /** Triggered when an email bounces. */ EMAIL_BOUNCED("email.bounced"), + /** Triggered when an email is opened. */ EMAIL_OPENED("email.opened"), + /** Triggered when a link in an email is clicked. */ EMAIL_CLICKED("email.clicked"), + /** Triggered when an inbound email is received. */ EMAIL_RECEIVED("email.received"), + /** Triggered when an email fails to send. */ EMAIL_FAILED("email.failed"), + /** Triggered when a contact is created. */ CONTACT_CREATED("contact.created"), + /** Triggered when a contact is updated. */ CONTACT_UPDATED("contact.updated"), + /** Triggered when a contact is deleted. */ CONTACT_DELETED("contact.deleted"), + /** Triggered when a domain is created. */ DOMAIN_CREATED("domain.created"), + /** Triggered when a domain is updated. */ DOMAIN_UPDATED("domain.updated"), + /** Triggered when a domain is deleted. */ DOMAIN_DELETED("domain.deleted"); private final String value; @@ -28,6 +43,11 @@ public enum WebhookEvent { this.value = value; } + /** + * Gets the string value of the webhook event. + * + * @return the string value of this event + */ @JsonValue public String getValue() { return value; diff --git a/src/main/java/com/resend/services/webhooks/model/WebhookStatus.java b/src/main/java/com/resend/services/webhooks/model/WebhookStatus.java index 0756eee..8bb978f 100644 --- a/src/main/java/com/resend/services/webhooks/model/WebhookStatus.java +++ b/src/main/java/com/resend/services/webhooks/model/WebhookStatus.java @@ -6,7 +6,9 @@ * Represents the status of a webhook. */ public enum WebhookStatus { + /** The webhook is enabled and active. */ ENABLED("enabled"), + /** The webhook is disabled and inactive. */ DISABLED("disabled"); private final String value; @@ -15,6 +17,11 @@ public enum WebhookStatus { this.value = value; } + /** + * Gets the string value of the webhook status. + * + * @return the string value of this status + */ @JsonValue public String getValue() { return value; diff --git a/src/test/java/com/resend/services/segments/SegmentsTest.java b/src/test/java/com/resend/services/segments/SegmentsTest.java new file mode 100644 index 0000000..2b18042 --- /dev/null +++ b/src/test/java/com/resend/services/segments/SegmentsTest.java @@ -0,0 +1,102 @@ +package com.resend.services.segments; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.ListParams; +import com.resend.services.segments.model.CreateSegmentOptions; +import com.resend.services.segments.model.CreateSegmentResponseSuccess; +import com.resend.services.segments.model.GetSegmentResponseSuccess; +import com.resend.services.segments.model.ListSegmentsResponseSuccess; +import com.resend.services.segments.model.RemoveSegmentResponseSuccess; + +import com.resend.services.util.SegmentsUtil; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.*; + +public class SegmentsTest { + + @Mock + private Segments segments; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + segments = mock(Segments.class); + } + + @Test + public void testCreateSegment_Success() throws ResendException { + CreateSegmentResponseSuccess expectedSegment = SegmentsUtil.createSegmentResponse(); + CreateSegmentOptions param = SegmentsUtil.createSegmentRequest(); + + when(segments.create(param)) + .thenReturn(expectedSegment); + + CreateSegmentResponseSuccess createdSeg = segments.create(param); + + assertEquals(createdSeg, expectedSegment); + verify(segments, times(1)).create(param); + } + + @Test + public void testDeleteSegment_Success() throws ResendException { + String segmentId = "123"; + RemoveSegmentResponseSuccess removed = SegmentsUtil.removeSegmentsResponseSuccess(); + when(segments.remove(segmentId)) + .thenReturn(removed); + + RemoveSegmentResponseSuccess res = segments.remove(segmentId); + + assertEquals(removed, res); + } + + @Test + public void testListSegments_Success() throws ResendException { + ListSegmentsResponseSuccess expectedResponse = SegmentsUtil.createSegmentsListResponse(); + + when(segments.list()) + .thenReturn(expectedResponse); + + ListSegmentsResponseSuccess res = segments.list(); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + } + + @Test + public void testListSegmentWithPagination_Success() throws ResendException { + ListParams params = ListParams.builder() + .limit(3).build(); + ListSegmentsResponseSuccess expectedResponse = SegmentsUtil.createSegmentsListResponse(); + + when(segments.list(params)) + .thenReturn(expectedResponse); + + ListSegmentsResponseSuccess res = segments.list(params); + + assertNotNull(res); + assertEquals(params.getLimit(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + } + + @Test + public void testGetSegment_Success() throws ResendException { + String segmentId = "123"; + GetSegmentResponseSuccess expected = SegmentsUtil.getSegmentResponseSuccess(); + + when(segments.get(segmentId)) + .thenReturn(expected); + + GetSegmentResponseSuccess res = segments.get(segmentId); + + assertNotNull(res); + assertEquals(expected.getId(), res.getId()); + assertEquals(expected.getName(), res.getName()); + } +} diff --git a/src/test/java/com/resend/services/util/SegmentsUtil.java b/src/test/java/com/resend/services/util/SegmentsUtil.java new file mode 100644 index 0000000..9561786 --- /dev/null +++ b/src/test/java/com/resend/services/util/SegmentsUtil.java @@ -0,0 +1,39 @@ +package com.resend.services.util; + +import com.resend.services.segments.model.*; + +import java.util.ArrayList; +import java.util.List; + +public class SegmentsUtil { + public static CreateSegmentOptions createSegmentRequest() { + return CreateSegmentOptions.builder() + .name("seg") + .build(); + } + public static CreateSegmentResponseSuccess createSegmentResponse() { + return new CreateSegmentResponseSuccess("123", "seg", "audience"); + } + + public static RemoveSegmentResponseSuccess removeSegmentsResponseSuccess() { + return new RemoveSegmentResponseSuccess("123", "audience", true); + } + + public static ListSegmentsResponseSuccess createSegmentsListResponse() { + List segList = new ArrayList<>(); + + Segment seg1 = new Segment("1", "test1", "2023-04-08T00:11:13.110779+00:00"); + Segment seg2 = new Segment("2", "test2", "2023-04-08T00:11:13.110779+00:00"); + Segment seg3 = new Segment("3", "test3", "2023-04-08T00:11:13.110779+00:00"); + + segList.add(seg1); + segList.add(seg2); + segList.add(seg3); + + return new ListSegmentsResponseSuccess(segList, "list", true); + } + + public static GetSegmentResponseSuccess getSegmentResponseSuccess() { + return new GetSegmentResponseSuccess("123", "seg", "2023-04-08T00:11:13.110779+00:00", "audience"); + } +} From c2fa87377e1ee7f9bce3ffc4c24f4065942e5af7 Mon Sep 17 00:00:00 2001 From: Kewyn Akshlley Date: Thu, 30 Oct 2025 15:45:09 -0300 Subject: [PATCH 2/3] fix: segments endpoints --- .../java/com/resend/services/contacts/Contacts.java | 12 ++++++------ .../java/com/resend/services/segments/Segments.java | 8 ++++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/resend/services/contacts/Contacts.java b/src/main/java/com/resend/services/contacts/Contacts.java index 145be34..55ef468 100644 --- a/src/main/java/com/resend/services/contacts/Contacts.java +++ b/src/main/java/com/resend/services/contacts/Contacts.java @@ -33,7 +33,7 @@ public Contacts(final String apiKey) { public CreateContactResponseSuccess create(CreateContactOptions createContactOptions) throws ResendException { String segmentId = createContactOptions.getSegmentId() != null ? createContactOptions.getSegmentId() : createContactOptions.getAudienceId(); String payload = super.resendMapper.writeValue(createContactOptions); - AbstractHttpResponse response = httpClient.perform("/audiences/" + segmentId + "/contacts" , super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/segments/" + segmentId + "/contacts" , super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -51,7 +51,7 @@ public CreateContactResponseSuccess create(CreateContactOptions createContactOpt * @throws ResendException If an error occurs during the contacts list retrieval process. */ public ListContactsResponseSuccess list(String audienceId) throws ResendException { - AbstractHttpResponse response = this.httpClient.perform("/audiences/" +audienceId+ "/contacts" , super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + AbstractHttpResponse response = this.httpClient.perform("/segments/" +audienceId+ "/contacts" , super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -71,7 +71,7 @@ public ListContactsResponseSuccess list(String audienceId) throws ResendExceptio * @throws ResendException If an error occurs during the contacts list retrieval process. */ public ListContactsResponseSuccess list(String audienceId, ListParams params) throws ResendException { - String pathWithQuery = "/audiences/" +audienceId+ "/contacts" + URLHelper.parse(params); + String pathWithQuery = "/segments/" +audienceId+ "/contacts" + URLHelper.parse(params); AbstractHttpResponse response = this.httpClient.perform(pathWithQuery, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { @@ -104,7 +104,7 @@ public GetContactResponseSuccess get(GetContactOptions params) throws ResendExce String contactIdentifier = (id != null && !id.isEmpty()) ? id : email; String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); - AbstractHttpResponse response = this.httpClient.perform("/audiences/" + segmentId + "/contacts/" + contactIdentifier, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + AbstractHttpResponse response = this.httpClient.perform("/segments/" + segmentId + "/contacts/" + contactIdentifier, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -131,7 +131,7 @@ public RemoveContactResponseSuccess remove(RemoveContactOptions params) throws R String pathParameter = params.getId() != null ? params.getId() : params.getEmail(); String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); - AbstractHttpResponse response = httpClient.perform("/audiences/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.DELETE, "", null); + AbstractHttpResponse response = httpClient.perform("/segments/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.DELETE, "", null); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -159,7 +159,7 @@ public UpdateContactResponseSuccess update(UpdateContactOptions params) throws R String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); String payload = super.resendMapper.writeValue(params); - AbstractHttpResponse response = httpClient.perform("/audiences/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/segments/" + segmentId + "/contacts/" + pathParameter, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); diff --git a/src/main/java/com/resend/services/segments/Segments.java b/src/main/java/com/resend/services/segments/Segments.java index 31f5683..5b0dd96 100644 --- a/src/main/java/com/resend/services/segments/Segments.java +++ b/src/main/java/com/resend/services/segments/Segments.java @@ -32,7 +32,7 @@ public Segments(final String apiKey) { */ public CreateSegmentResponseSuccess create(CreateSegmentOptions createSegmentOptions) throws ResendException { String payload = super.resendMapper.writeValue(createSegmentOptions); - AbstractHttpResponse response = httpClient.perform("/audiences", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/segments", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -49,7 +49,7 @@ public CreateSegmentResponseSuccess create(CreateSegmentOptions createSegmentOpt * @throws ResendException If an error occurs during the segments list retrieval process. */ public ListSegmentsResponseSuccess list() throws ResendException { - AbstractHttpResponse response = this.httpClient.perform("/audiences", super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + AbstractHttpResponse response = this.httpClient.perform("/segments", super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -88,7 +88,7 @@ public ListSegmentsResponseSuccess list(ListParams params) throws ResendExceptio * @throws ResendException If an error occurs while retrieving the segment. */ public GetSegmentResponseSuccess get(String id) throws ResendException { - AbstractHttpResponse response = this.httpClient.perform("/audiences/" +id, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + AbstractHttpResponse response = this.httpClient.perform("/segments/" +id, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -107,7 +107,7 @@ public GetSegmentResponseSuccess get(String id) throws ResendException { * @throws ResendException If an error occurs during the segment deletion process. */ public RemoveSegmentResponseSuccess remove(String id) throws ResendException { - AbstractHttpResponse response = httpClient.perform("/audiences/" +id, super.apiKey, HttpMethod.DELETE, "", null); + AbstractHttpResponse response = httpClient.perform("/segments/" +id, super.apiKey, HttpMethod.DELETE, "", null); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); From 21de4b546336cd60982ba17d1273b125b18dacb0 Mon Sep 17 00:00:00 2001 From: Kewyn Akshlley Date: Thu, 30 Oct 2025 15:55:25 -0300 Subject: [PATCH 3/3] fix: change variable name to segmentId --- .../java/com/resend/services/contacts/Contacts.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/resend/services/contacts/Contacts.java b/src/main/java/com/resend/services/contacts/Contacts.java index 55ef468..3f7eefa 100644 --- a/src/main/java/com/resend/services/contacts/Contacts.java +++ b/src/main/java/com/resend/services/contacts/Contacts.java @@ -46,12 +46,12 @@ public CreateContactResponseSuccess create(CreateContactOptions createContactOpt /** * Retrieves a list of contacts and returns a ListContactsResponseSuccess. * - * @param audienceId The id of the audience. + * @param segmentId The id of the segment (formerly known as audienceId). * @return A ListContactsResponseSuccess containing the list of contacts. * @throws ResendException If an error occurs during the contacts list retrieval process. */ - public ListContactsResponseSuccess list(String audienceId) throws ResendException { - AbstractHttpResponse response = this.httpClient.perform("/segments/" +audienceId+ "/contacts" , super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + public ListContactsResponseSuccess list(String segmentId) throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/segments/" + segmentId + "/contacts" , super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -65,13 +65,13 @@ public ListContactsResponseSuccess list(String audienceId) throws ResendExceptio /** * Retrieves a paginated list of contacts and returns a ListContactsResponseSuccess. * - * @param audienceId The id of the audience. + * @param segmentId The id of the segment (formerly known as audienceId). * @param params The params used to customize the list. * @return A ListContactsResponseSuccess containing the paginated list of contacts. * @throws ResendException If an error occurs during the contacts list retrieval process. */ - public ListContactsResponseSuccess list(String audienceId, ListParams params) throws ResendException { - String pathWithQuery = "/segments/" +audienceId+ "/contacts" + URLHelper.parse(params); + public ListContactsResponseSuccess list(String segmentId, ListParams params) throws ResendException { + String pathWithQuery = "/segments/" + segmentId + "/contacts" + URLHelper.parse(params); AbstractHttpResponse response = this.httpClient.perform(pathWithQuery, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) {