From 2393de5f537c6cb34253a1d5c20c2acaf1349378 Mon Sep 17 00:00:00 2001 From: Kewyn Akshlley Date: Fri, 31 Oct 2025 11:07:40 -0300 Subject: [PATCH] feat: Add segments and topics to sub services --- .../services/contacts/ContactSegments.java | 137 ++++++++++++++++ .../services/contacts/ContactTopics.java | 108 +++++++++++++ .../resend/services/contacts/Contacts.java | 147 +++++++++++++----- .../model/AddContactToSegmentOptions.java | 125 +++++++++++++++ .../AddContactToSegmentResponseSuccess.java | 33 ++++ .../contacts/model/ContactOptions.java | 17 +- .../contacts/model/ContactSegment.java | 100 ++++++++++++ .../contacts/model/CreateContactOptions.java | 19 ++- .../model/ListContactSegmentsOptions.java | 97 ++++++++++++ .../ListContactSegmentsResponseSuccess.java | 102 ++++++++++++ .../RemoveContactFromSegmentOptions.java | 125 +++++++++++++++ ...moveContactFromSegmentResponseSuccess.java | 57 +++++++ .../contacts/model/UpdateContactOptions.java | 12 +- .../contacts/ContactSegmentsTest.java | 145 +++++++++++++++++ .../services/contacts/ContactTopicsTest.java | 94 +++++++++++ .../services/contacts/ContactsTest.java | 66 -------- .../resend/services/util/ContactsUtil.java | 47 ++++++ 17 files changed, 1321 insertions(+), 110 deletions(-) create mode 100644 src/main/java/com/resend/services/contacts/ContactSegments.java create mode 100644 src/main/java/com/resend/services/contacts/ContactTopics.java create mode 100644 src/main/java/com/resend/services/contacts/model/AddContactToSegmentOptions.java create mode 100644 src/main/java/com/resend/services/contacts/model/AddContactToSegmentResponseSuccess.java create mode 100644 src/main/java/com/resend/services/contacts/model/ContactSegment.java create mode 100644 src/main/java/com/resend/services/contacts/model/ListContactSegmentsOptions.java create mode 100644 src/main/java/com/resend/services/contacts/model/ListContactSegmentsResponseSuccess.java create mode 100644 src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentOptions.java create mode 100644 src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentResponseSuccess.java create mode 100644 src/test/java/com/resend/services/contacts/ContactSegmentsTest.java create mode 100644 src/test/java/com/resend/services/contacts/ContactTopicsTest.java diff --git a/src/main/java/com/resend/services/contacts/ContactSegments.java b/src/main/java/com/resend/services/contacts/ContactSegments.java new file mode 100644 index 0000000..ff73d9c --- /dev/null +++ b/src/main/java/com/resend/services/contacts/ContactSegments.java @@ -0,0 +1,137 @@ +package com.resend.services.contacts; + +import com.resend.core.exception.ResendException; +import com.resend.core.helper.URLHelper; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.ListParams; +import com.resend.core.service.BaseService; +import com.resend.services.contacts.model.*; +import okhttp3.MediaType; + +/** + * Represents the Contact Segments sub-service. + * Handles operations for managing contact membership in segments. + */ +public class ContactSegments extends BaseService { + + /** + * Constructs an instance of the {@code ContactSegments} class. + * + * @param apiKey The apiKey used for authentication. + */ + public ContactSegments(final String apiKey) { + super(apiKey); + } + + /** + * Adds an existing contact to a segment. + * + * @param options The options containing the contact identifier (id or email) and segment ID. + * @return The AddContactToSegmentResponseSuccess with the segment ID. + * @throws ResendException If an error occurs during the segment addition process. + */ + public AddContactToSegmentResponseSuccess add(AddContactToSegmentOptions options) throws ResendException { + if ((options.getId() == null && options.getEmail() == null) || + (options.getId() != null && options.getEmail() != null)) { + throw new IllegalArgumentException("Either 'id' or 'email' must be provided, but not both."); + } + + if (options.getSegmentId() == null || options.getSegmentId().isEmpty()) { + throw new IllegalArgumentException("Segment ID must be provided"); + } + + String contactIdOrEmail = options.getId() != null ? options.getId() : options.getEmail(); + String endpoint = "/contacts/" + contactIdOrEmail + "/segments/" + options.getSegmentId(); + + AbstractHttpResponse response = httpClient.perform(endpoint, 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, AddContactToSegmentResponseSuccess.class); + } + + /** + * Removes an existing contact from a segment. + * + * @param options The options containing the contact identifier (id or email) and segment ID. + * @return The RemoveContactFromSegmentResponseSuccess with the segment ID and deletion status. + * @throws ResendException If an error occurs during the segment removal process. + */ + public RemoveContactFromSegmentResponseSuccess remove(RemoveContactFromSegmentOptions options) throws ResendException { + if ((options.getId() == null && options.getEmail() == null) || + (options.getId() != null && options.getEmail() != null)) { + throw new IllegalArgumentException("Either 'id' or 'email' must be provided, but not both."); + } + + if (options.getSegmentId() == null || options.getSegmentId().isEmpty()) { + throw new IllegalArgumentException("Segment ID must be provided"); + } + + String contactIdOrEmail = options.getId() != null ? options.getId() : options.getEmail(); + String endpoint = "/contacts/" + contactIdOrEmail + "/segments/" + options.getSegmentId(); + + AbstractHttpResponse response = httpClient.perform(endpoint, super.apiKey, HttpMethod.DELETE, "", null); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, RemoveContactFromSegmentResponseSuccess.class); + } + + /** + * Retrieves a list of segments that a contact belongs to. + * + * @param contactIdOrEmail The contact ID or email address. + * @return The ListContactSegmentsResponseSuccess with the list of segments. + * @throws ResendException If an error occurs during the segment list retrieval process. + */ + public ListContactSegmentsResponseSuccess list(String contactIdOrEmail) throws ResendException { + if (contactIdOrEmail == null || contactIdOrEmail.isEmpty()) { + throw new IllegalArgumentException("Contact ID or email must be provided"); + } + + String endpoint = "/contacts/" + contactIdOrEmail + "/segments"; + AbstractHttpResponse response = httpClient.perform(endpoint, 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, ListContactSegmentsResponseSuccess.class); + } + + /** + * Retrieves a paginated list of segments that a contact belongs to. + * + * @param contactId The contact ID. + * @param params The params used to customize the list. + * @return The ListContactSegmentsResponseSuccess with the paginated list of segments. + * @throws ResendException If an error occurs during the segment list retrieval process. + */ + public ListContactSegmentsResponseSuccess list(String contactId, ListParams params) throws ResendException { + if (contactId == null || contactId.isEmpty()) { + throw new IllegalArgumentException("Contact ID must be provided"); + } + + String pathWithQuery = "/contacts/" + contactId + "/segments" + URLHelper.parse(params); + AbstractHttpResponse response = 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, ListContactSegmentsResponseSuccess.class); + } +} diff --git a/src/main/java/com/resend/services/contacts/ContactTopics.java b/src/main/java/com/resend/services/contacts/ContactTopics.java new file mode 100644 index 0000000..6e54c19 --- /dev/null +++ b/src/main/java/com/resend/services/contacts/ContactTopics.java @@ -0,0 +1,108 @@ +package com.resend.services.contacts; + +import com.resend.core.exception.ResendException; +import com.resend.core.helper.URLHelper; +import com.resend.core.net.AbstractHttpResponse; +import com.resend.core.net.HttpMethod; +import com.resend.core.net.ListParams; +import com.resend.core.service.BaseService; +import com.resend.services.contacts.model.ListContactTopicsResponse; +import com.resend.services.contacts.model.UpdateContactTopicsOptions; +import com.resend.services.contacts.model.UpdateContactTopicsResponse; +import okhttp3.MediaType; + +/** + * Represents the Contact Topics sub-service. + * Handles operations for managing contact topic subscriptions. + */ +public class ContactTopics extends BaseService { + + /** + * Constructs an instance of the {@code ContactTopics} class. + * + * @param apiKey The apiKey used for authentication. + */ + public ContactTopics(final String apiKey) { + super(apiKey); + } + + /** + * Retrieves a list of topic subscriptions for a contact. + * + * @param contactIdOrEmail The contact ID or email address. + * @return A ListContactTopicsResponse containing the list of topic subscriptions. + * @throws ResendException If an error occurs during the topic list retrieval process. + */ + public ListContactTopicsResponse list(String contactIdOrEmail) throws ResendException { + if (contactIdOrEmail == null || contactIdOrEmail.isEmpty()) { + throw new IllegalArgumentException("Contact ID or email must be provided"); + } + + AbstractHttpResponse response = this.httpClient.perform("/contacts/" + contactIdOrEmail + "/topics", 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, ListContactTopicsResponse.class); + } + + /** + * Retrieves a paginated list of topic subscriptions for a contact. + * + * @param contactIdOrEmail The contact ID or email address. + * @param params The params used to customize the list. + * @return A ListContactTopicsResponse containing the paginated list of topic subscriptions. + * @throws ResendException If an error occurs during the topic list retrieval process. + */ + public ListContactTopicsResponse list(String contactIdOrEmail, ListParams params) throws ResendException { + if (contactIdOrEmail == null || contactIdOrEmail.isEmpty()) { + throw new IllegalArgumentException("Contact ID or email must be provided"); + } + + String pathWithQuery = "/contacts/" + contactIdOrEmail + "/topics" + 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, ListContactTopicsResponse.class); + } + + /** + * Updates topic subscriptions for a contact. + * + * @param options The options containing the contact identifier and topic updates. + * @return The UpdateContactTopicsResponse with the contact ID. + * @throws ResendException If an error occurs during the topic update process. + */ + public UpdateContactTopicsResponse update(UpdateContactTopicsOptions options) throws ResendException { + if ((options.getId() == null && options.getEmail() == null) || + (options.getId() != null && options.getEmail() != null)) { + throw new IllegalArgumentException("Either 'id' or 'email' must be provided, but not both."); + } + + if (options.getTopics() == null || options.getTopics().isEmpty()) { + throw new IllegalArgumentException("Topics list must not be empty"); + } + + String contactIdOrEmail = options.getId() != null ? options.getId() : options.getEmail(); + + // Serialize just the topics array (not the whole options object) + String payload = super.resendMapper.writeValue(options.getTopics()); + AbstractHttpResponse response = httpClient.perform("/contacts/" + contactIdOrEmail + "/topics", super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + + return resendMapper.readValue(responseBody, UpdateContactTopicsResponse.class); + } +} diff --git a/src/main/java/com/resend/services/contacts/Contacts.java b/src/main/java/com/resend/services/contacts/Contacts.java index 22f6071..a67a068 100644 --- a/src/main/java/com/resend/services/contacts/Contacts.java +++ b/src/main/java/com/resend/services/contacts/Contacts.java @@ -10,10 +10,34 @@ import okhttp3.MediaType; /** - * Represents the Resend Contacts module. + * Represents the Resend Contacts module. + * + *

This service handles global contact operations (CRUD for contacts not associated with specific segments). + * For specialized operations, use the following sub-services:

+ * + *
    + *
  • {@link #segments()} - Manage contact membership in segments (add/remove contacts from segments)
  • + *
  • {@link #topics()} - Manage contact topic subscriptions (list and update topic preferences)
  • + *
+ * + *

Global Contact Operations:

+ *
    + *
  • {@link #create(CreateContactOptions)} - Create a global contact
  • + *
  • {@link #list()} - List all global contacts
  • + *
  • {@link #list(ListParams)} - List global contacts with pagination
  • + *
  • {@link #get(String)} - Get a global contact by ID or email
  • + *
  • {@link #update(UpdateContactOptions)} - Update a global contact
  • + *
  • {@link #remove(String)} - Remove a global contact
  • + *
+ * + *

Migration Note: Methods that accept {@code segmentId} or {@code audienceId} parameters + * are deprecated. Use the Segments service for segment-specific operations instead.

*/ public class Contacts extends BaseService { + private ContactSegments contactSegments; + private ContactTopics contactTopics; + /** * Constructs an instance of the {@code Contacts} class. * @@ -24,22 +48,46 @@ public Contacts(final String apiKey) { } /** - * Creates a Contact. + * Returns the ContactSegments sub-service for managing contact segment memberships. + * + * @return The ContactSegments service instance. + */ + public ContactSegments segments() { + if (this.contactSegments == null) { + this.contactSegments = new ContactSegments(this.apiKey); + } + return this.contactSegments; + } + + /** + * Returns the ContactTopics sub-service for managing contact topic subscriptions. + * + * @return The ContactTopics service instance. + */ + public ContactTopics topics() { + if (this.contactTopics == null) { + this.contactTopics = new ContactTopics(this.apiKey); + } + return this.contactTopics; + } + + /** + * Creates a global contact (not associated with any segment). + * + *

To add a contact to a segment, first create the global contact using this method, + * then use {@code contacts().segments().add(options)} to add it to specific segments.

+ * + *

Note: The {@code segmentId} and {@code audienceId} fields in {@code CreateContactOptions} + * are ignored. Use the workflow above for segment membership.

* * @param createContactOptions The Contact details. * @return The details of the created contact. * @throws ResendException If an error occurs during the Contact creation process. */ public CreateContactResponseSuccess create(CreateContactOptions createContactOptions) throws ResendException { - String segmentId = createContactOptions.getSegmentId() != null ? createContactOptions.getSegmentId() : createContactOptions.getAudienceId(); String payload = super.resendMapper.writeValue(createContactOptions); - // Use /contacts for global contacts (when no segment ID is provided) - String endpoint = (segmentId == null || segmentId.isEmpty()) - ? "/contacts" - : "/segments/" + segmentId + "/contacts"; - - AbstractHttpResponse response = httpClient.perform(endpoint, super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/contacts", super.apiKey, HttpMethod.POST, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -50,12 +98,18 @@ public CreateContactResponseSuccess create(CreateContactOptions createContactOpt } /** - * Retrieves a list of contacts and returns a ListContactsResponseSuccess. + * Retrieves a list of contacts in a segment. + * + * @deprecated This method is deprecated. Segment-related operations have been moved to dedicated services. + * For segment-specific contact operations, use the Segments service instead: + * {@code resend.segments().get(segmentId)} or contact operations through the Segments API. + * This method will be removed in a future version. * * @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. */ + @Deprecated public ListContactsResponseSuccess list(String segmentId) throws ResendException { AbstractHttpResponse response = this.httpClient.perform("/segments/" + segmentId + "/contacts" , super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); @@ -69,13 +123,19 @@ public ListContactsResponseSuccess list(String segmentId) throws ResendException } /** - * Retrieves a paginated list of contacts and returns a ListContactsResponseSuccess. + * Retrieves a paginated list of contacts in a segment. + * + * @deprecated This method is deprecated. Segment-related operations have been moved to dedicated services. + * For segment-specific contact operations, use the Segments service instead: + * {@code resend.segments().get(segmentId)} or contact operations through the Segments API. + * This method will be removed in a future version. * * @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. */ + @Deprecated 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")); @@ -128,15 +188,18 @@ public ListContactsResponseSuccess list(ListParams params) throws ResendExceptio } /** - * Retrieves a contact by its unique identifier. + * Retrieves a global contact by its unique identifier. + * + *

Note: The {@code segmentId} and {@code audienceId} fields in {@code GetContactOptions} + * are ignored. This method only retrieves global contacts. Use {@link #get(String)} for a simpler API.

* - * @param params The object containing: - * – {@code audienceId}: the audience to which the contact belongs - * – Either {@code id}: the contact's id - * or {@code email}: the contact's email address + * @deprecated Use {@link #get(String)} instead for global contacts. + * + * @param params The object containing either {@code id} or {@code email} of the contact. * @return The retrieved contact details. * @throws ResendException If an error occurs while retrieving the contact. */ + @Deprecated public GetContactResponseSuccess get(GetContactOptions params) throws ResendException { String id = params.getId(); String email = params.getEmail(); @@ -146,14 +209,8 @@ public GetContactResponseSuccess get(GetContactOptions params) throws ResendExce } String contactIdentifier = (id != null && !id.isEmpty()) ? id : email; - String segmentId = params.getSegmentId() != null ? params.getSegmentId() : params.getAudienceId(); - - // Use /contacts for global contacts (when no segment ID is provided) - String endpoint = (segmentId == null || segmentId.isEmpty()) - ? "/contacts/" + contactIdentifier - : "/segments/" + segmentId + "/contacts/" + contactIdentifier; - AbstractHttpResponse response = this.httpClient.perform(endpoint, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); + AbstractHttpResponse response = this.httpClient.perform("/contacts/" + contactIdentifier, super.apiKey, HttpMethod.GET, null, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -188,12 +245,18 @@ public GetContactResponseSuccess get(String contactIdOrEmail) throws ResendExcep } /** - * Deletes a contact based on the provided contact ID or email. + * Deletes a global contact based on the provided contact ID or email. + * + *

Note: The {@code segmentId} and {@code audienceId} fields in {@code RemoveContactOptions} + * are ignored. This method only removes global contacts. Use {@link #remove(String)} for a simpler API.

+ * + * @deprecated Use {@link #remove(String)} instead for global contacts. * * @param params The object with identifier of the contact to delete. * @return The RemoveContactsResponseSuccess with the details of the removed contact. * @throws ResendException If an error occurs during the contact deletion process. */ + @Deprecated public RemoveContactResponseSuccess remove(RemoveContactOptions params) throws ResendException { if ((params.getId() == null && params.getEmail() == null) || (params.getId() != null && params.getEmail() != null)) { @@ -201,14 +264,8 @@ 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(); - - // Use /contacts for global contacts (when no segment ID is provided) - String endpoint = (segmentId == null || segmentId.isEmpty()) - ? "/contacts/" + pathParameter - : "/segments/" + segmentId + "/contacts/" + pathParameter; - AbstractHttpResponse response = httpClient.perform(endpoint, super.apiKey, HttpMethod.DELETE, "", null); + AbstractHttpResponse response = httpClient.perform("/contacts/" + pathParameter, super.apiKey, HttpMethod.DELETE, "", null); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -244,7 +301,10 @@ public RemoveContactResponseSuccess remove(String contactId) throws ResendExcept } /** - * Updates a contact based on the provided contact ID or email. + * Updates a global contact based on the provided contact ID or email. + * + *

Note: The {@code segmentId} and {@code audienceId} fields in {@code UpdateContactOptions} + * are ignored. This method only updates global contacts.

* * @param params The object with identifier of the contact to patch. * @return The UpdateContactResponseSuccess with the details of the patched contact. @@ -257,15 +317,9 @@ 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(); - - // Use /contacts for global contacts (when no segment ID is provided) - String endpoint = (segmentId == null || segmentId.isEmpty()) - ? "/contacts/" + pathParameter - : "/segments/" + segmentId + "/contacts/" + pathParameter; String payload = super.resendMapper.writeValue(params); - AbstractHttpResponse response = httpClient.perform(endpoint, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); + AbstractHttpResponse response = httpClient.perform("/contacts/" + pathParameter, super.apiKey, HttpMethod.PATCH, payload, MediaType.get("application/json")); if (!response.isSuccessful()) { throw new ResendException(response.getCode(), response.getBody()); @@ -279,6 +333,11 @@ public UpdateContactResponseSuccess update(UpdateContactOptions params) throws R /** * Retrieves a list of topic subscriptions for a contact. * + * @deprecated This method is deprecated. Topics related operations have been moved to dedicated services. + * For contact-topics operations, use the Contacts service instead: + * {@code resend.contacts().topics.list(contactIdOrEmail)}. + * This method will be removed in a future version. + * * @param contactIdOrEmail The contact ID or email address. * @return A ListContactTopicsResponse containing the list of topic subscriptions. * @throws ResendException If an error occurs during the topic list retrieval process. @@ -302,6 +361,11 @@ public ListContactTopicsResponse getTopics(String contactIdOrEmail) throws Resen /** * Retrieves a paginated list of topic subscriptions for a contact. * + * @deprecated This method is deprecated. Topics related operations have been moved to dedicated services. + * For contact-topics operations, use the Contacts service instead: + * {@code resend.contacts().topics.list(contactIdOrEmail, params)}. + * This method will be removed in a future version. + * * @param contactIdOrEmail The contact ID or email address. * @param params The params used to customize the list. * @return A ListContactTopicsResponse containing the paginated list of topic subscriptions. @@ -327,6 +391,11 @@ public ListContactTopicsResponse getTopics(String contactIdOrEmail, ListParams p /** * Updates topic subscriptions for a contact. * + * @deprecated This method is deprecated. Topics related operations have been moved to dedicated services. + * For contact-topics operations, use the Contacts service instead: + * {@code resend.contacts().topics.list(options)}. + * This method will be removed in a future version. + * * @param options The options containing the contact identifier and topic updates. * @return The UpdateContactTopicsResponse with the contact ID. * @throws ResendException If an error occurs during the topic update process. diff --git a/src/main/java/com/resend/services/contacts/model/AddContactToSegmentOptions.java b/src/main/java/com/resend/services/contacts/model/AddContactToSegmentOptions.java new file mode 100644 index 0000000..c999186 --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/AddContactToSegmentOptions.java @@ -0,0 +1,125 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Represents the options for adding a contact to a segment. + */ +public class AddContactToSegmentOptions { + + /** + * The contact ID (either id or email must be provided, but not both). + */ + @JsonIgnore + private String id; + + /** + * The contact email address (either id or email must be provided, but not both). + */ + @JsonIgnore + private String email; + + /** + * The segment ID to add the contact to (required). + */ + @JsonIgnore + private String segmentId; + + /** + * Constructs a new AddContactToSegmentOptions using the builder. + * + * @param builder The builder to construct from. + */ + private AddContactToSegmentOptions(Builder builder) { + this.id = builder.id; + this.email = builder.email; + this.segmentId = builder.segmentId; + } + + /** + * Gets the contact ID. + * + * @return The contact ID. + */ + public String getId() { + return id; + } + + /** + * Gets the contact email. + * + * @return The contact email. + */ + public String getEmail() { + return email; + } + + /** + * Gets the segment ID. + * + * @return The segment ID. + */ + public String getSegmentId() { + return segmentId; + } + + /** + * Creates a new Builder for constructing AddContactToSegmentOptions. + * + * @return A new Builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing AddContactToSegmentOptions instances. + */ + public static class Builder { + private String id; + private String email; + private String segmentId; + + /** + * Sets the contact ID. + * + * @param id The contact ID. + * @return This builder instance. + */ + public Builder id(String id) { + this.id = id; + return this; + } + + /** + * Sets the contact email. + * + * @param email The contact email. + * @return This builder instance. + */ + public Builder email(String email) { + this.email = email; + return this; + } + + /** + * Sets the segment ID. + * + * @param segmentId The segment ID. + * @return This builder instance. + */ + public Builder segmentId(String segmentId) { + this.segmentId = segmentId; + return this; + } + + /** + * Builds the AddContactToSegmentOptions instance. + * + * @return A new AddContactToSegmentOptions instance. + */ + public AddContactToSegmentOptions build() { + return new AddContactToSegmentOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/contacts/model/AddContactToSegmentResponseSuccess.java b/src/main/java/com/resend/services/contacts/model/AddContactToSegmentResponseSuccess.java new file mode 100644 index 0000000..b3e7b1f --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/AddContactToSegmentResponseSuccess.java @@ -0,0 +1,33 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the successful response from adding a contact to a segment. + */ +public class AddContactToSegmentResponseSuccess { + + /** + * The segment ID. + */ + @JsonProperty("id") + private String id; + + /** + * Gets the segment ID. + * + * @return The segment ID. + */ + public String getId() { + return id; + } + + /** + * Sets the segment ID. + * + * @param id The segment ID. + */ + public void setId(String id) { + this.id = id; + } +} 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 f60744d..ec88e5b 100644 --- a/src/main/java/com/resend/services/contacts/model/ContactOptions.java +++ b/src/main/java/com/resend/services/contacts/model/ContactOptions.java @@ -1,10 +1,15 @@ package com.resend.services.contacts.model; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** * Common superclass for contact options. + * + *

Note: These option classes are for global contact operations only. + * Segment-related fields are ignored.

*/ +@JsonInclude(JsonInclude.Include.NON_NULL) public abstract class ContactOptions { /** @@ -15,7 +20,7 @@ public abstract class ContactOptions { /** * The audience_id of the contact options - * @deprecated Use {@link #segmentId} instead. + * @deprecated This field is ignored. Use ContactSegments service for segment operations. */ @Deprecated @JsonProperty("audience_id") @@ -23,7 +28,9 @@ public abstract class ContactOptions { /** * The segment_id of the contact options + * @deprecated This field is ignored. Use ContactSegments service for segment operations. */ + @Deprecated @JsonProperty("segment_id") protected final String segmentId; @@ -58,7 +65,7 @@ public String getId() { * Get the audienceId of the ContactOptions. * * @return The audienceId of the ContactOptions. - * @deprecated Use {@link #getSegmentId()} instead. + * @deprecated This field is ignored. Use ContactSegments service for segment operations. */ @Deprecated public String getAudienceId() { @@ -69,7 +76,9 @@ public String getAudienceId() { * Get the segmentId of the ContactOptions. * * @return The segmentId of the ContactOptions. + * @deprecated This field is ignored. Use ContactSegments service for segment operations. */ + @Deprecated public String getSegmentId() { return segmentId; } @@ -125,7 +134,7 @@ public B id(String id) { * * @param audienceId The audienceId of the ContactOptions. * @return The builder instance. - * @deprecated Use {@link #segmentId(String)} instead. + * @deprecated This field is ignored. Use ContactSegments service for segment operations. */ @Deprecated public B audienceId(String audienceId) { @@ -138,7 +147,9 @@ public B audienceId(String audienceId) { * * @param segmentId The segmentId of the ContactOptions. * @return The builder instance. + * @deprecated This field is ignored. Use ContactSegments service for segment operations. */ + @Deprecated public B segmentId(String segmentId) { this.segmentId = segmentId; return self(); diff --git a/src/main/java/com/resend/services/contacts/model/ContactSegment.java b/src/main/java/com/resend/services/contacts/model/ContactSegment.java new file mode 100644 index 0000000..d47fea6 --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/ContactSegment.java @@ -0,0 +1,100 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a segment that a contact belongs to. + */ +public class ContactSegment { + + /** + * The segment ID. + */ + @JsonProperty("id") + private String id; + + /** + * The segment name. + */ + @JsonProperty("name") + private String name; + + /** + * The segment creation timestamp. + */ + @JsonProperty("created_at") + private String createdAt; + + /** + * Default constructor. + */ + public ContactSegment() { + } + + /** + * Constructor with all fields. + * + * @param id The segment ID. + * @param name The segment name. + * @param createdAt The segment creation timestamp. + */ + public ContactSegment(String id, String name, String createdAt) { + this.id = id; + this.name = name; + this.createdAt = createdAt; + } + + /** + * Gets the segment ID. + * + * @return The segment ID. + */ + public String getId() { + return id; + } + + /** + * Sets the segment ID. + * + * @param id The segment ID. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Gets the segment name. + * + * @return The segment name. + */ + public String getName() { + return name; + } + + /** + * Sets the segment name. + * + * @param name The segment name. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the segment creation timestamp. + * + * @return The segment creation timestamp. + */ + public String getCreatedAt() { + return createdAt; + } + + /** + * Sets the segment creation timestamp. + * + * @param createdAt The segment creation timestamp. + */ + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } +} diff --git a/src/main/java/com/resend/services/contacts/model/CreateContactOptions.java b/src/main/java/com/resend/services/contacts/model/CreateContactOptions.java index 88e5022..5be69bf 100644 --- a/src/main/java/com/resend/services/contacts/model/CreateContactOptions.java +++ b/src/main/java/com/resend/services/contacts/model/CreateContactOptions.java @@ -1,10 +1,20 @@ package com.resend.services.contacts.model; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** - * Represents a request to create a contact. + * Represents a request to create a global contact. + * + *

Note: This class is for creating global contacts only. + * To add a contact to a segment, use the workflow: + *

    + *
  1. Create global contact: {@code resend.contacts().create(options)}
  2. + *
  3. Add to segment: {@code resend.contacts().segments().add(options)}
  4. + *
+ *

*/ +@JsonInclude(JsonInclude.Include.NON_NULL) public class CreateContactOptions { @JsonProperty("audience_id") @@ -12,6 +22,7 @@ public class CreateContactOptions { private final String audienceId; @JsonProperty("segment_id") + @Deprecated private final String segmentId; @JsonProperty("email") @@ -55,7 +66,10 @@ public String getAudienceId() { * Get the segment ID of the contact. * * @return The segment ID of the contact. + * @deprecated This field is ignored when creating global contacts. + * Use {@code resend.contacts().segments().add(options)} to add contacts to segments. */ + @Deprecated public String getSegmentId() { return segmentId; } @@ -134,7 +148,10 @@ public Builder audienceId(String audienceId) { * * @param segmentId The segment ID of the contact. * @return The builder instance. + * @deprecated This field is ignored when creating global contacts. + * Use {@code resend.contacts().segments().add(options)} to add contacts to segments. */ + @Deprecated public Builder segmentId(String segmentId) { this.segmentId = segmentId; return this; diff --git a/src/main/java/com/resend/services/contacts/model/ListContactSegmentsOptions.java b/src/main/java/com/resend/services/contacts/model/ListContactSegmentsOptions.java new file mode 100644 index 0000000..d0acc2a --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/ListContactSegmentsOptions.java @@ -0,0 +1,97 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Represents the options for listing segments a contact belongs to. + */ +public class ListContactSegmentsOptions { + + /** + * The contact ID (either id or email must be provided, but not both). + */ + @JsonIgnore + private String id; + + /** + * The contact email address (either id or email must be provided, but not both). + */ + @JsonIgnore + private String email; + + /** + * Constructs a new ListContactSegmentsOptions using the builder. + * + * @param builder The builder to construct from. + */ + private ListContactSegmentsOptions(Builder builder) { + this.id = builder.id; + this.email = builder.email; + } + + /** + * Gets the contact ID. + * + * @return The contact ID. + */ + public String getId() { + return id; + } + + /** + * Gets the contact email. + * + * @return The contact email. + */ + public String getEmail() { + return email; + } + + /** + * Creates a new Builder for constructing ListContactSegmentsOptions. + * + * @return A new Builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing ListContactSegmentsOptions instances. + */ + public static class Builder { + private String id; + private String email; + + /** + * Sets the contact ID. + * + * @param id The contact ID. + * @return This builder instance. + */ + public Builder id(String id) { + this.id = id; + return this; + } + + /** + * Sets the contact email. + * + * @param email The contact email. + * @return This builder instance. + */ + public Builder email(String email) { + this.email = email; + return this; + } + + /** + * Builds the ListContactSegmentsOptions instance. + * + * @return A new ListContactSegmentsOptions instance. + */ + public ListContactSegmentsOptions build() { + return new ListContactSegmentsOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/contacts/model/ListContactSegmentsResponseSuccess.java b/src/main/java/com/resend/services/contacts/model/ListContactSegmentsResponseSuccess.java new file mode 100644 index 0000000..c00058c --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/ListContactSegmentsResponseSuccess.java @@ -0,0 +1,102 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +/** + * Represents the successful response from listing segments a contact belongs to. + */ +public class ListContactSegmentsResponseSuccess { + + /** + * The object type. + */ + @JsonProperty("object") + private String object; + + /** + * The list of segments. + */ + @JsonProperty("data") + private List data; + + /** + * Indicates whether there are more segments to retrieve. + */ + @JsonProperty("has_more") + private Boolean hasMore; + + /** + * Default constructor. + */ + public ListContactSegmentsResponseSuccess() { + } + + /** + * Constructor with all fields. + * + * @param object The object type. + * @param data The list of segments. + * @param hasMore Whether there are more segments to retrieve. + */ + public ListContactSegmentsResponseSuccess(String object, List data, Boolean hasMore) { + this.object = object; + this.data = data; + this.hasMore = hasMore; + } + + /** + * Gets the object type. + * + * @return The object type. + */ + public String getObject() { + return object; + } + + /** + * Sets the object type. + * + * @param object The object type. + */ + public void setObject(String object) { + this.object = object; + } + + /** + * Gets the list of segments. + * + * @return The list of segments. + */ + public List getData() { + return data; + } + + /** + * Sets the list of segments. + * + * @param data The list of segments. + */ + public void setData(List data) { + this.data = data; + } + + /** + * Gets whether there are more segments to retrieve. + * + * @return True if there are more segments, false otherwise. + */ + public Boolean getHasMore() { + return hasMore; + } + + /** + * Sets whether there are more segments to retrieve. + * + * @param hasMore True if there are more segments, false otherwise. + */ + public void setHasMore(Boolean hasMore) { + this.hasMore = hasMore; + } +} diff --git a/src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentOptions.java b/src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentOptions.java new file mode 100644 index 0000000..a44f923 --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentOptions.java @@ -0,0 +1,125 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * Represents the options for removing a contact from a segment. + */ +public class RemoveContactFromSegmentOptions { + + /** + * The contact ID (either id or email must be provided, but not both). + */ + @JsonIgnore + private String id; + + /** + * The contact email address (either id or email must be provided, but not both). + */ + @JsonIgnore + private String email; + + /** + * The segment ID to remove the contact from (required). + */ + @JsonIgnore + private String segmentId; + + /** + * Constructs a new RemoveContactFromSegmentOptions using the builder. + * + * @param builder The builder to construct from. + */ + private RemoveContactFromSegmentOptions(Builder builder) { + this.id = builder.id; + this.email = builder.email; + this.segmentId = builder.segmentId; + } + + /** + * Gets the contact ID. + * + * @return The contact ID. + */ + public String getId() { + return id; + } + + /** + * Gets the contact email. + * + * @return The contact email. + */ + public String getEmail() { + return email; + } + + /** + * Gets the segment ID. + * + * @return The segment ID. + */ + public String getSegmentId() { + return segmentId; + } + + /** + * Creates a new Builder for constructing RemoveContactFromSegmentOptions. + * + * @return A new Builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing RemoveContactFromSegmentOptions instances. + */ + public static class Builder { + private String id; + private String email; + private String segmentId; + + /** + * Sets the contact ID. + * + * @param id The contact ID. + * @return This builder instance. + */ + public Builder id(String id) { + this.id = id; + return this; + } + + /** + * Sets the contact email. + * + * @param email The contact email. + * @return This builder instance. + */ + public Builder email(String email) { + this.email = email; + return this; + } + + /** + * Sets the segment ID. + * + * @param segmentId The segment ID. + * @return This builder instance. + */ + public Builder segmentId(String segmentId) { + this.segmentId = segmentId; + return this; + } + + /** + * Builds the RemoveContactFromSegmentOptions instance. + * + * @return A new RemoveContactFromSegmentOptions instance. + */ + public RemoveContactFromSegmentOptions build() { + return new RemoveContactFromSegmentOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentResponseSuccess.java b/src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentResponseSuccess.java new file mode 100644 index 0000000..41a48cb --- /dev/null +++ b/src/main/java/com/resend/services/contacts/model/RemoveContactFromSegmentResponseSuccess.java @@ -0,0 +1,57 @@ +package com.resend.services.contacts.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents the successful response from removing a contact from a segment. + */ +public class RemoveContactFromSegmentResponseSuccess { + + /** + * The segment ID. + */ + @JsonProperty("id") + private String id; + + /** + * Indicates whether the contact was successfully removed from the segment. + */ + @JsonProperty("deleted") + private Boolean deleted; + + /** + * Gets the segment ID. + * + * @return The segment ID. + */ + public String getId() { + return id; + } + + /** + * Sets the segment ID. + * + * @param id The segment ID. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Gets the deleted status. + * + * @return True if the contact was successfully removed from the segment. + */ + public Boolean getDeleted() { + return deleted; + } + + /** + * Sets the deleted status. + * + * @param deleted True if the contact was successfully removed from the segment. + */ + public void setDeleted(Boolean deleted) { + this.deleted = deleted; + } +} diff --git a/src/main/java/com/resend/services/contacts/model/UpdateContactOptions.java b/src/main/java/com/resend/services/contacts/model/UpdateContactOptions.java index 8d36761..8985a16 100644 --- a/src/main/java/com/resend/services/contacts/model/UpdateContactOptions.java +++ b/src/main/java/com/resend/services/contacts/model/UpdateContactOptions.java @@ -1,10 +1,15 @@ package com.resend.services.contacts.model; +import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.annotation.JsonProperty; /** - * Represents a request to update a contact. + * Represents a request to update a global contact. + * + *

Note: This class is for updating global contacts only. + * Segment-related fields are ignored.

*/ +@JsonInclude(JsonInclude.Include.NON_NULL) public class UpdateContactOptions { @JsonProperty("id") @@ -18,6 +23,7 @@ public class UpdateContactOptions { private final String audienceId; @JsonProperty("segment_id") + @Deprecated private final String segmentId; @JsonProperty("unsubscribed") @@ -59,7 +65,9 @@ public String getAudienceId() { * Get the segment ID of the contact. * * @return The segment ID of the contact. + * @deprecated This field is ignored when updating global contacts. */ + @Deprecated public String getSegmentId() { return segmentId; } @@ -148,7 +156,9 @@ public Builder audienceId(String audienceId) { * * @param segmentId The segment ID of the contact. * @return The builder instance. + * @deprecated This field is ignored when updating global contacts. */ + @Deprecated public Builder segmentId(String segmentId) { this.segmentId = segmentId; return this; diff --git a/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java b/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java new file mode 100644 index 0000000..69a56ee --- /dev/null +++ b/src/test/java/com/resend/services/contacts/ContactSegmentsTest.java @@ -0,0 +1,145 @@ +package com.resend.services.contacts; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.ListParams; +import com.resend.services.contacts.model.*; +import com.resend.services.util.ContactsUtil; +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 ContactSegmentsTest { + + @Mock + private ContactSegments contactSegments; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + contactSegments = mock(ContactSegments.class); + } + + @Test + public void testAddContactToSegmentById_Success() throws ResendException { + AddContactToSegmentOptions options = ContactsUtil.createAddContactToSegmentOptions(); + AddContactToSegmentResponseSuccess expectedResponse = ContactsUtil.addContactToSegmentResponseSuccess(); + + when(contactSegments.add(options)) + .thenReturn(expectedResponse); + + AddContactToSegmentResponseSuccess res = contactSegments.add(options); + + assertNotNull(res); + assertEquals(expectedResponse.getId(), res.getId()); + verify(contactSegments, times(1)).add(options); + } + + @Test + public void testAddContactToSegmentByEmail_Success() throws ResendException { + AddContactToSegmentOptions options = AddContactToSegmentOptions.builder() + .email("steve.wozniak@gmail.com") + .segmentId("78261eea-8f8b-4381-83c6-79fa7120f1cf") + .build(); + AddContactToSegmentResponseSuccess expectedResponse = ContactsUtil.addContactToSegmentResponseSuccess(); + + when(contactSegments.add(options)) + .thenReturn(expectedResponse); + + AddContactToSegmentResponseSuccess res = contactSegments.add(options); + + assertNotNull(res); + assertEquals(expectedResponse.getId(), res.getId()); + verify(contactSegments, times(1)).add(options); + } + + @Test + public void testRemoveContactFromSegmentById_Success() throws ResendException { + RemoveContactFromSegmentOptions options = ContactsUtil.createRemoveContactFromSegmentOptions(); + RemoveContactFromSegmentResponseSuccess expectedResponse = ContactsUtil.removeContactFromSegmentResponseSuccess(); + + when(contactSegments.remove(options)) + .thenReturn(expectedResponse); + + RemoveContactFromSegmentResponseSuccess res = contactSegments.remove(options); + + assertNotNull(res); + assertEquals(expectedResponse.getId(), res.getId()); + assertEquals(expectedResponse.getDeleted(), res.getDeleted()); + verify(contactSegments, times(1)).remove(options); + } + + @Test + public void testRemoveContactFromSegmentByEmail_Success() throws ResendException { + RemoveContactFromSegmentOptions options = RemoveContactFromSegmentOptions.builder() + .email("steve.wozniak@gmail.com") + .segmentId("78261eea-8f8b-4381-83c6-79fa7120f1cf") + .build(); + RemoveContactFromSegmentResponseSuccess expectedResponse = ContactsUtil.removeContactFromSegmentResponseSuccess(); + + when(contactSegments.remove(options)) + .thenReturn(expectedResponse); + + RemoveContactFromSegmentResponseSuccess res = contactSegments.remove(options); + + assertNotNull(res); + assertEquals(expectedResponse.getId(), res.getId()); + assertEquals(expectedResponse.getDeleted(), res.getDeleted()); + verify(contactSegments, times(1)).remove(options); + } + + @Test + public void testListContactSegmentsById_Success() throws ResendException { + String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; + ListContactSegmentsResponseSuccess expectedResponse = ContactsUtil.listContactSegmentsResponseSuccess(); + + when(contactSegments.list(contactId)) + .thenReturn(expectedResponse); + + ListContactSegmentsResponseSuccess res = contactSegments.list(contactId); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + verify(contactSegments, times(1)).list(contactId); + } + + @Test + public void testListContactSegmentsByEmail_Success() throws ResendException { + String contactEmail = "steve.wozniak@gmail.com"; + ListContactSegmentsResponseSuccess expectedResponse = ContactsUtil.listContactSegmentsResponseSuccess(); + + when(contactSegments.list(contactEmail)) + .thenReturn(expectedResponse); + + ListContactSegmentsResponseSuccess res = contactSegments.list(contactEmail); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + verify(contactSegments, times(1)).list(contactEmail); + } + + @Test + public void testListContactSegmentsWithPagination_Success() throws ResendException { + String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; + ListParams params = ListParams.builder() + .limit(10) + .build(); + ListContactSegmentsResponseSuccess expectedResponse = ContactsUtil.listContactSegmentsResponseSuccess(); + + when(contactSegments.list(contactId, params)) + .thenReturn(expectedResponse); + + ListContactSegmentsResponseSuccess res = contactSegments.list(contactId, params); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + verify(contactSegments, times(1)).list(contactId, params); + } +} diff --git a/src/test/java/com/resend/services/contacts/ContactTopicsTest.java b/src/test/java/com/resend/services/contacts/ContactTopicsTest.java new file mode 100644 index 0000000..8dda8e1 --- /dev/null +++ b/src/test/java/com/resend/services/contacts/ContactTopicsTest.java @@ -0,0 +1,94 @@ +package com.resend.services.contacts; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.ListParams; +import com.resend.services.contacts.model.ListContactTopicsResponse; +import com.resend.services.contacts.model.UpdateContactTopicsOptions; +import com.resend.services.contacts.model.UpdateContactTopicsResponse; +import com.resend.services.util.ContactsUtil; +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 ContactTopicsTest { + + @Mock + private ContactTopics contactTopics; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + contactTopics = mock(ContactTopics.class); + } + + @Test + public void testListTopicsById_Success() throws ResendException { + String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; + ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); + + when(contactTopics.list(contactId)) + .thenReturn(expectedResponse); + + ListContactTopicsResponse res = contactTopics.list(contactId); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + verify(contactTopics, times(1)).list(contactId); + } + + @Test + public void testListTopicsByEmail_Success() throws ResendException { + String contactEmail = "steve.wozniak@gmail.com"; + ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); + + when(contactTopics.list(contactEmail)) + .thenReturn(expectedResponse); + + ListContactTopicsResponse res = contactTopics.list(contactEmail); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + verify(contactTopics, times(1)).list(contactEmail); + } + + @Test + public void testListTopicsWithPagination_Success() throws ResendException { + String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; + ListParams params = ListParams.builder() + .limit(10) + .build(); + ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); + + when(contactTopics.list(contactId, params)) + .thenReturn(expectedResponse); + + ListContactTopicsResponse res = contactTopics.list(contactId, params); + + assertNotNull(res); + assertEquals(expectedResponse.getData().size(), res.getData().size()); + assertEquals(expectedResponse.getObject(), res.getObject()); + verify(contactTopics, times(1)).list(contactId, params); + } + + @Test + public void testUpdateTopics_Success() throws ResendException { + UpdateContactTopicsOptions options = ContactsUtil.createUpdateTopicsOptions(); + UpdateContactTopicsResponse expectedResponse = ContactsUtil.updateContactTopicsResponse(); + + when(contactTopics.update(options)) + .thenReturn(expectedResponse); + + UpdateContactTopicsResponse res = contactTopics.update(options); + + assertNotNull(res); + assertEquals(expectedResponse.getId(), res.getId()); + verify(contactTopics, times(1)).update(options); + } +} diff --git a/src/test/java/com/resend/services/contacts/ContactsTest.java b/src/test/java/com/resend/services/contacts/ContactsTest.java index f4bbb97..c518d56 100644 --- a/src/test/java/com/resend/services/contacts/ContactsTest.java +++ b/src/test/java/com/resend/services/contacts/ContactsTest.java @@ -138,72 +138,6 @@ public void testGetContactByEmail_Success() throws ResendException { verify(contacts, times(1)).get(params); } - @Test - public void testGetTopicsById_Success() throws ResendException { - String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); - - when(contacts.getTopics(contactId)) - .thenReturn(expectedResponse); - - ListContactTopicsResponse res = contacts.getTopics(contactId); - - assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contacts, times(1)).getTopics(contactId); - } - - @Test - public void testGetTopicsByEmail_Success() throws ResendException { - String contactEmail = "steve.wozniak@gmail.com"; - ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); - - when(contacts.getTopics(contactEmail)) - .thenReturn(expectedResponse); - - ListContactTopicsResponse res = contacts.getTopics(contactEmail); - - assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contacts, times(1)).getTopics(contactEmail); - } - - @Test - public void testGetTopicsWithPagination_Success() throws ResendException { - String contactId = "e169aa45-1ecf-4183-9955-b1499d5701d3"; - ListParams params = ListParams.builder() - .limit(10) - .build(); - ListContactTopicsResponse expectedResponse = ContactsUtil.createContactTopicsListResponse(); - - when(contacts.getTopics(contactId, params)) - .thenReturn(expectedResponse); - - ListContactTopicsResponse res = contacts.getTopics(contactId, params); - - assertNotNull(res); - assertEquals(expectedResponse.getData().size(), res.getData().size()); - assertEquals(expectedResponse.getObject(), res.getObject()); - verify(contacts, times(1)).getTopics(contactId, params); - } - - @Test - public void testUpdateTopics_Success() throws ResendException { - UpdateContactTopicsOptions options = ContactsUtil.createUpdateTopicsOptions(); - UpdateContactTopicsResponse expectedResponse = ContactsUtil.updateContactTopicsResponse(); - - when(contacts.updateTopics(options)) - .thenReturn(expectedResponse); - - UpdateContactTopicsResponse res = contacts.updateTopics(options); - - assertNotNull(res); - assertEquals(expectedResponse.getId(), res.getId()); - verify(contacts, times(1)).updateTopics(options); - } - // Global contacts tests (without segment ID) @Test diff --git a/src/test/java/com/resend/services/util/ContactsUtil.java b/src/test/java/com/resend/services/util/ContactsUtil.java index 3fbe7b4..c8ea043 100644 --- a/src/test/java/com/resend/services/util/ContactsUtil.java +++ b/src/test/java/com/resend/services/util/ContactsUtil.java @@ -103,4 +103,51 @@ public static UpdateContactTopicsOptions createUpdateTopicsOptions() { public static UpdateContactTopicsResponse updateContactTopicsResponse() { return new UpdateContactTopicsResponse("e169aa45-1ecf-4183-9955-b1499d5701d3"); } + + public static AddContactToSegmentOptions createAddContactToSegmentOptions() { + return AddContactToSegmentOptions.builder() + .id("e169aa45-1ecf-4183-9955-b1499d5701d3") + .segmentId("78261eea-8f8b-4381-83c6-79fa7120f1cf") + .build(); + } + + public static AddContactToSegmentResponseSuccess addContactToSegmentResponseSuccess() { + AddContactToSegmentResponseSuccess response = new AddContactToSegmentResponseSuccess(); + response.setId("78261eea-8f8b-4381-83c6-79fa7120f1cf"); + return response; + } + + public static RemoveContactFromSegmentOptions createRemoveContactFromSegmentOptions() { + return RemoveContactFromSegmentOptions.builder() + .id("e169aa45-1ecf-4183-9955-b1499d5701d3") + .segmentId("78261eea-8f8b-4381-83c6-79fa7120f1cf") + .build(); + } + + public static RemoveContactFromSegmentResponseSuccess removeContactFromSegmentResponseSuccess() { + RemoveContactFromSegmentResponseSuccess response = new RemoveContactFromSegmentResponseSuccess(); + response.setId("78261eea-8f8b-4381-83c6-79fa7120f1cf"); + response.setDeleted(true); + return response; + } + + public static ListContactSegmentsResponseSuccess listContactSegmentsResponseSuccess() { + List segments = new ArrayList<>(); + + ContactSegment seg1 = new ContactSegment( + "78261eea-8f8b-4381-83c6-79fa7120f1cf", + "Registered Users", + "2023-10-06T22:59:55.977Z" + ); + ContactSegment seg2 = new ContactSegment( + "b9d24c8e-bf1c-5d4d-cf1d-470cbd97482f", + "Premium Users", + "2023-11-12T14:23:10.123Z" + ); + + segments.add(seg1); + segments.add(seg2); + + return new ListContactSegmentsResponseSuccess("list", segments, false); + } }