diff --git a/src/main/java/com/resend/Resend.java b/src/main/java/com/resend/Resend.java index bda3613..759878f 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.topics.Topics; import com.resend.services.templates.Templates; /** @@ -91,6 +92,15 @@ public Broadcasts broadcasts() { return new Broadcasts(apiKey); } + /** + * Returns a Topics object that can be used to interact with the Topics service. + * + * @return A Topics object. + */ + public Topics topics() { + return new Topics(apiKey); + } + /** * Returns a Templates object that can be used to interact with the Templates service. * diff --git a/src/main/java/com/resend/services/topics/Topics.java b/src/main/java/com/resend/services/topics/Topics.java new file mode 100644 index 0000000..97d5a7b --- /dev/null +++ b/src/main/java/com/resend/services/topics/Topics.java @@ -0,0 +1,136 @@ +package com.resend.services.topics; + +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.topics.model.*; +import okhttp3.MediaType; + +/** + * Represents the Resend Topics module. + */ +public final class Topics extends BaseService { + + /** + * Constructs an instance of the {@code Topics} class. + * + * @param apiKey The apiKey used for authentication. + */ + public Topics(final String apiKey) { + super(apiKey); + } + + /** + * Creates a new topic. + * + * @param createTopicOptions The request containing topic details. + * @return The response indicating the status of the topic creation. + * @throws ResendException If an error occurs while creating the topic. + */ + public CreateTopicResponseSuccess create(CreateTopicOptions createTopicOptions) throws ResendException { + String payload = super.resendMapper.writeValue(createTopicOptions); + AbstractHttpResponse response = super.httpClient.perform("/topics", 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, CreateTopicResponseSuccess.class); + } + + /** + * Retrieves a topic by its unique identifier. + * + * @param topicId The unique identifier of the topic. + * @return The retrieved topic's details. + * @throws ResendException If an error occurs while retrieving the topic. + */ + public GetTopicResponseSuccess get(String topicId) throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/topics/" + topicId, 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, GetTopicResponseSuccess.class); + } + + /** + * Updates a topic by its unique identifier. + * + * @param topicId The unique identifier of the topic. + * @param updateTopicOptions The new data for the topic. + * @return The response indicating the status of the topic update. + * @throws ResendException If an error occurs while updating the topic. + */ + public UpdateTopicResponseSuccess update(String topicId, UpdateTopicOptions updateTopicOptions) throws ResendException { + String payload = super.resendMapper.writeValue(updateTopicOptions); + AbstractHttpResponse response = this.httpClient.perform("/topics/" + topicId, 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, UpdateTopicResponseSuccess.class); + } + + /** + * Removes a topic by its unique identifier. + * + * @param topicId The unique identifier of the topic. + * @return The response indicating the status of the topic removal. + * @throws ResendException If an error occurs while removing the topic. + */ + public RemoveTopicResponseSuccess remove(String topicId) throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/topics/" + topicId, super.apiKey, HttpMethod.DELETE, null, MediaType.get("application/json")); + + if (!response.isSuccessful()) { + throw new ResendException(response.getCode(), response.getBody()); + } + + String responseBody = response.getBody(); + return resendMapper.readValue(responseBody, RemoveTopicResponseSuccess.class); + } + + /** + * Retrieves a list of topics and returns a List. + * + * @return A ListTopicsResponse containing the list of topics. + * @throws ResendException If an error occurs during the topics list retrieval process. + */ + public ListTopicsResponseSuccess list() throws ResendException { + AbstractHttpResponse response = this.httpClient.perform("/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, ListTopicsResponseSuccess.class); + } + + /** + * Retrieves a paginated list of topics and returns a List. + * + * @param params The params used to customize the list. + * @return A ListTopicsResponse containing the paginated list of topics. + * @throws ResendException If an error occurs during the topics list retrieval process. + */ + public ListTopicsResponseSuccess list(ListParams params) throws ResendException { + String pathWithQuery = "/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, ListTopicsResponseSuccess.class); + } +} diff --git a/src/main/java/com/resend/services/topics/model/AbstractTopic.java b/src/main/java/com/resend/services/topics/model/AbstractTopic.java new file mode 100644 index 0000000..12c2585 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/AbstractTopic.java @@ -0,0 +1,153 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Abstract base class representing common topic attributes. + * This class contains fields shared across different topic response types. + */ +public abstract class AbstractTopic { + + /** + * The unique identifier of the topic. + */ + @JsonProperty("id") + private String id; + + /** + * The name of the topic. + */ + @JsonProperty("name") + private String name; + + /** + * The description of the topic. + */ + @JsonProperty("description") + private String description; + + /** + * The default subscription preference for new contacts. + */ + @JsonProperty("default_subscription") + private String defaultSubscription; + + /** + * The creation timestamp of the topic. + */ + @JsonProperty("created_at") + private String createdAt; + + /** + * Default constructor for creating an empty AbstractTopic object. + */ + public AbstractTopic() { + } + + /** + * Constructs an AbstractTopic with the provided attributes. + * + * @param id The unique identifier of the topic. + * @param name The name of the topic. + * @param description The description of the topic. + * @param defaultSubscription The default subscription preference. + * @param createdAt The creation timestamp. + */ + public AbstractTopic(String id, String name, String description, String defaultSubscription, String createdAt) { + this.id = id; + this.name = name; + this.description = description; + this.defaultSubscription = defaultSubscription; + this.createdAt = createdAt; + } + + /** + * Gets the unique identifier of the topic. + * + * @return The topic ID. + */ + public String getId() { + return id; + } + + /** + * Sets the unique identifier of the topic. + * + * @param id The topic ID to set. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Gets the name of the topic. + * + * @return The topic name. + */ + public String getName() { + return name; + } + + /** + * Sets the name of the topic. + * + * @param name The topic name to set. + */ + public void setName(String name) { + this.name = name; + } + + /** + * Gets the description of the topic. + * + * @return The topic description. + */ + public String getDescription() { + return description; + } + + /** + * Sets the description of the topic. + * + * @param description The topic description to set. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Gets the default subscription preference for new contacts. + * + * @return The default subscription preference. + */ + public String getDefaultSubscription() { + return defaultSubscription; + } + + /** + * Sets the default subscription preference for new contacts. + * + * @param defaultSubscription The default subscription preference to set. + */ + public void setDefaultSubscription(String defaultSubscription) { + this.defaultSubscription = defaultSubscription; + } + + /** + * Gets the creation timestamp of the topic. + * + * @return The creation timestamp. + */ + public String getCreatedAt() { + return createdAt; + } + + /** + * Sets the creation timestamp of the topic. + * + * @param createdAt The creation timestamp to set. + */ + public void setCreatedAt(String createdAt) { + this.createdAt = createdAt; + } +} diff --git a/src/main/java/com/resend/services/topics/model/CreateTopicOptions.java b/src/main/java/com/resend/services/topics/model/CreateTopicOptions.java new file mode 100644 index 0000000..5017495 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/CreateTopicOptions.java @@ -0,0 +1,125 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a request to create a topic. + */ +public class CreateTopicOptions { + + /** + * The topic name. + */ + @JsonProperty("name") + private final String name; + + /** + * The default subscription preference for new contacts. + */ + @JsonProperty("default_subscription") + private final String defaultSubscription; + + /** + * The topic description. + */ + @JsonProperty("description") + private final String description; + + /** + * Private constructor used by the Builder. + * + * @param builder The builder instance. + */ + private CreateTopicOptions(Builder builder) { + this.name = builder.name; + this.defaultSubscription = builder.defaultSubscription; + this.description = builder.description; + } + + /** + * Retrieves the name of the topic. + * + * @return The topic name. + */ + public String getName() { + return name; + } + + /** + * Retrieves the default subscription preference for new contacts. + * + * @return The default subscription preference. + */ + public String getDefaultSubscription() { + return defaultSubscription; + } + + /** + * Retrieves the description of the topic. + * + * @return The topic description. + */ + public String getDescription() { + return description; + } + + /** + * Creates a new builder instance to construct CreateTopicOptions. + * + * @return A new builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing CreateTopicOptions instances. + */ + public static class Builder { + private String name; + private String defaultSubscription; + private String description; + + /** + * Set the topic name. + * + * @param name The topic name. Max length is 50 characters. + * @return This builder instance for method chaining. + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Set the default subscription preference for new contacts. + * + * @param defaultSubscription The default subscription preference. Possible values: "opt_in" or "opt_out". + * @return This builder instance for method chaining. + */ + public Builder defaultSubscription(String defaultSubscription) { + this.defaultSubscription = defaultSubscription; + return this; + } + + /** + * Set the topic description. + * + * @param description The topic description. Max length is 200 characters. + * @return This builder instance for method chaining. + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * Builds and returns a {@code CreateTopicOptions} based on the configured properties. + * + * @return A {@code CreateTopicOptions} instance. + */ + public CreateTopicOptions build() { + return new CreateTopicOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/topics/model/CreateTopicResponseSuccess.java b/src/main/java/com/resend/services/topics/model/CreateTopicResponseSuccess.java new file mode 100644 index 0000000..cad806a --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/CreateTopicResponseSuccess.java @@ -0,0 +1,48 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a response after creating a topic. + */ +public class CreateTopicResponseSuccess { + + /** + * The unique identifier associated with the topic. + */ + @JsonProperty("id") + private String id; + + /** + * Constructs a new instance of {@code CreateTopicResponse}. + */ + public CreateTopicResponseSuccess() { + } + + /** + * Constructs a CreateTopicResponse with the provided ID. + * + * @param id The ID associated with the created topic. + */ + public CreateTopicResponseSuccess(String id) { + this.id = id; + } + + /** + * Retrieves the ID associated with the created topic. + * + * @return The ID of the created topic. + */ + public String getId() { + return id; + } + + /** + * Sets the ID for the created topic. + * + * @param id The ID to be set. + */ + public void setId(String id) { + this.id = id; + } +} diff --git a/src/main/java/com/resend/services/topics/model/GetTopicResponseSuccess.java b/src/main/java/com/resend/services/topics/model/GetTopicResponseSuccess.java new file mode 100644 index 0000000..0c8f793 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/GetTopicResponseSuccess.java @@ -0,0 +1,27 @@ +package com.resend.services.topics.model; + +/** + * Represents a successful response when getting a single topic. + */ +public class GetTopicResponseSuccess extends AbstractTopic { + + /** + * Default constructor for creating an empty GetTopicResponseSuccess object. + */ + public GetTopicResponseSuccess() { + super(); + } + + /** + * Constructs a GetTopicResponseSuccess with the provided attributes. + * + * @param id The unique identifier of the topic. + * @param name The name of the topic. + * @param description The description of the topic. + * @param defaultSubscription The default subscription preference. + * @param createdAt The creation timestamp. + */ + public GetTopicResponseSuccess(String id, String name, String description, String defaultSubscription, String createdAt) { + super(id, name, description, defaultSubscription, createdAt); + } +} diff --git a/src/main/java/com/resend/services/topics/model/ListTopicsResponseSuccess.java b/src/main/java/com/resend/services/topics/model/ListTopicsResponseSuccess.java new file mode 100644 index 0000000..845b470 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/ListTopicsResponseSuccess.java @@ -0,0 +1,101 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.List; + +/** + * Represents a successful response for listing topics. + */ +public class ListTopicsResponseSuccess { + + /** + * The object type of the response. + */ + @JsonProperty("object") + private String object; + + /** + * The list of topics. + */ + @JsonProperty("data") + private List data; + + /** + * Indicates whether there are more topics available for pagination. + */ + @JsonProperty("has_more") + private Boolean hasMore; + + /** + * Default constructor + */ + public ListTopicsResponseSuccess() { + } + + /** + * Constructs a successful response for listing topics. + * + * @param object The object type of the response. + * @param data The list of topics. + * @param hasMore Whether there are more topics available for pagination. + */ + public ListTopicsResponseSuccess(String object, List data, Boolean hasMore) { + this.object = object; + this.data = data; + this.hasMore = hasMore; + } + + /** + * Gets the object type of the response. + * + * @return The object type. + */ + public String getObject() { + return object; + } + + /** + * Sets the object type of the response. + * + * @param object The object type to set. + */ + public void setObject(String object) { + this.object = object; + } + + /** + * Gets the list of topics. + * + * @return The list of topics. + */ + public List getData() { + return data; + } + + /** + * Sets the list of topics. + * + * @param data The list of topics to set. + */ + public void setData(List data) { + this.data = data; + } + + /** + * Gets the indicator whether there are more items available for pagination. + * + * @return Whether there are more items available for pagination. + */ + public Boolean hasMore() { + return hasMore; + } + + /** + * Sets the indicator whether there are more items available for pagination. + * + * @param hasMore Whether there are more items available for pagination. + */ + public void setHasMore(Boolean hasMore) { + this.hasMore = hasMore; + } +} diff --git a/src/main/java/com/resend/services/topics/model/RemoveTopicResponseSuccess.java b/src/main/java/com/resend/services/topics/model/RemoveTopicResponseSuccess.java new file mode 100644 index 0000000..5fa5196 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/RemoveTopicResponseSuccess.java @@ -0,0 +1,100 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a response object for a topic deletion operation. + */ +public class RemoveTopicResponseSuccess { + + /** + * The object type of the response. + */ + @JsonProperty("object") + private String object; + + /** + * The unique identifier associated with the topic. + */ + @JsonProperty("id") + private String id; + + /** + * A boolean flag indicating whether the topic was successfully deleted. + */ + @JsonProperty("deleted") + private boolean deleted; + + /** + * Default constructor for creating an empty RemoveTopicResponse object. + */ + public RemoveTopicResponseSuccess() { + } + + /** + * Constructor to create a RemoveTopicResponse object with the provided attributes. + * + * @param object The object type of the response. + * @param id The unique identifier associated with the topic. + * @param deleted A boolean flag indicating whether the topic was successfully deleted. + */ + public RemoveTopicResponseSuccess(String object, String id, boolean deleted) { + this.object = object; + this.id = id; + this.deleted = deleted; + } + + /** + * Get the object type of the response. + * + * @return The object type. + */ + public String getObject() { + return object; + } + + /** + * Set the object type of the response. + * + * @param object The object type to set. + */ + public void setObject(String object) { + this.object = object; + } + + /** + * Get the unique identifier associated with the topic. + * + * @return The unique identifier. + */ + public String getId() { + return id; + } + + /** + * Set the unique identifier associated with the topic. + * + * @param id The unique identifier to set. + */ + public void setId(String id) { + this.id = id; + } + + /** + * Get the boolean flag indicating whether the topic was successfully deleted. + * + * @return True if the topic was successfully deleted; otherwise, false. + */ + public boolean isDeleted() { + return deleted; + } + + /** + * Set the boolean flag indicating whether the topic was successfully deleted. + * + * @param deleted The deletion status to set. + */ + public void setDeleted(boolean deleted) { + this.deleted = deleted; + } +} diff --git a/src/main/java/com/resend/services/topics/model/Topic.java b/src/main/java/com/resend/services/topics/model/Topic.java new file mode 100644 index 0000000..813f906 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/Topic.java @@ -0,0 +1,28 @@ +package com.resend.services.topics.model; + +/** + * Represents a topic in a list response. + * This class is used when topics are returned as part of a collection. + */ +public class Topic extends AbstractTopic { + + /** + * Default constructor for creating an empty Topic object. + */ + public Topic() { + super(); + } + + /** + * Constructs a Topic with the provided attributes. + * + * @param id The unique identifier of the topic. + * @param name The name of the topic. + * @param description The description of the topic. + * @param defaultSubscription The default subscription preference. + * @param createdAt The creation timestamp. + */ + public Topic(String id, String name, String description, String defaultSubscription, String createdAt) { + super(id, name, description, defaultSubscription, createdAt); + } +} diff --git a/src/main/java/com/resend/services/topics/model/UpdateTopicOptions.java b/src/main/java/com/resend/services/topics/model/UpdateTopicOptions.java new file mode 100644 index 0000000..320063c --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/UpdateTopicOptions.java @@ -0,0 +1,97 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a request to update a topic. + */ +public class UpdateTopicOptions { + + /** + * The topic name. + */ + @JsonProperty("name") + private final String name; + + /** + * The topic description. + */ + @JsonProperty("description") + private final String description; + + /** + * Private constructor used by the Builder. + * + * @param builder The builder instance. + */ + private UpdateTopicOptions(Builder builder) { + this.name = builder.name; + this.description = builder.description; + } + + /** + * Retrieves the name of the topic. + * + * @return The topic name. + */ + public String getName() { + return name; + } + + /** + * Retrieves the description of the topic. + * + * @return The topic description. + */ + public String getDescription() { + return description; + } + + /** + * Creates a new builder instance to construct UpdateTopicOptions. + * + * @return A new builder instance. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Builder class for constructing UpdateTopicOptions instances. + */ + public static class Builder { + private String name; + private String description; + + /** + * Set the topic name. + * + * @param name The topic name. Max length is 50 characters. + * @return This builder instance for method chaining. + */ + public Builder name(String name) { + this.name = name; + return this; + } + + /** + * Set the topic description. + * + * @param description The topic description. Max length is 200 characters. + * @return This builder instance for method chaining. + */ + public Builder description(String description) { + this.description = description; + return this; + } + + /** + * Builds and returns a {@code UpdateTopicOptions} based on the configured properties. + * + * @return A {@code UpdateTopicOptions} instance. + */ + public UpdateTopicOptions build() { + return new UpdateTopicOptions(this); + } + } +} diff --git a/src/main/java/com/resend/services/topics/model/UpdateTopicResponseSuccess.java b/src/main/java/com/resend/services/topics/model/UpdateTopicResponseSuccess.java new file mode 100644 index 0000000..d17def5 --- /dev/null +++ b/src/main/java/com/resend/services/topics/model/UpdateTopicResponseSuccess.java @@ -0,0 +1,48 @@ +package com.resend.services.topics.model; + +import com.fasterxml.jackson.annotation.JsonProperty; + +/** + * Represents a response after updating a topic. + */ +public class UpdateTopicResponseSuccess { + + /** + * The unique identifier associated with the topic. + */ + @JsonProperty("id") + private String id; + + /** + * Constructs a new instance of {@code UpdateTopicResponse}. + */ + public UpdateTopicResponseSuccess() { + } + + /** + * Constructs an UpdateTopicResponse with the provided ID. + * + * @param id The ID associated with the updated topic. + */ + public UpdateTopicResponseSuccess(String id) { + this.id = id; + } + + /** + * Retrieves the ID associated with the updated topic. + * + * @return The ID of the updated topic. + */ + public String getId() { + return id; + } + + /** + * Sets the ID for the updated topic. + * + * @param id The ID to be set. + */ + public void setId(String id) { + this.id = id; + } +} diff --git a/src/test/java/com/resend/services/topics/TopicsTest.java b/src/test/java/com/resend/services/topics/TopicsTest.java new file mode 100644 index 0000000..3cfd048 --- /dev/null +++ b/src/test/java/com/resend/services/topics/TopicsTest.java @@ -0,0 +1,119 @@ +package com.resend.services.topics; + +import com.resend.core.exception.ResendException; +import com.resend.core.net.ListParams; +import com.resend.services.topics.model.*; +import com.resend.services.util.TopicsUtil; +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.*; + +/** + * Test class for Topics service. + */ +public class TopicsTest { + + @Mock + private Topics topics; + + @BeforeEach + public void setUp() { + MockitoAnnotations.openMocks(this); + topics = mock(Topics.class); + } + + @Test + public void testCreateTopic_Success() throws ResendException { + CreateTopicOptions createOptions = TopicsUtil.createTopicOptions(); + CreateTopicResponseSuccess expectedResponse = TopicsUtil.createTopicResponse(); + + when(topics.create(createOptions)).thenReturn(expectedResponse); + + CreateTopicResponseSuccess response = topics.create(createOptions); + + assertNotNull(response); + assertEquals(expectedResponse.getId(), response.getId()); + verify(topics, times(1)).create(createOptions); + } + + @Test + public void testGetTopic_Success() throws ResendException { + GetTopicResponseSuccess expectedTopic = TopicsUtil.createTopic(); + + when(topics.get(expectedTopic.getId())).thenReturn(expectedTopic); + + GetTopicResponseSuccess retrievedTopic = topics.get(expectedTopic.getId()); + + assertNotNull(retrievedTopic); + assertEquals(expectedTopic.getId(), retrievedTopic.getId()); + assertEquals(expectedTopic.getName(), retrievedTopic.getName()); + assertEquals(expectedTopic.getDescription(), retrievedTopic.getDescription()); + assertEquals(expectedTopic.getDefaultSubscription(), retrievedTopic.getDefaultSubscription()); + verify(topics, times(1)).get(expectedTopic.getId()); + } + + @Test + public void testUpdateTopic_Success() throws ResendException { + String topicId = "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"; + UpdateTopicOptions updateOptions = TopicsUtil.updateTopicOptions(); + UpdateTopicResponseSuccess expectedResponse = TopicsUtil.updateTopicResponse(); + + when(topics.update(topicId, updateOptions)).thenReturn(expectedResponse); + + UpdateTopicResponseSuccess response = topics.update(topicId, updateOptions); + + assertNotNull(response); + assertEquals(expectedResponse.getId(), response.getId()); + verify(topics, times(1)).update(topicId, updateOptions); + } + + @Test + public void testRemoveTopic_Success() throws ResendException { + String topicId = "b6d24b8e-af0b-4c3c-be0c-359bbd97381e"; + RemoveTopicResponseSuccess expectedResponse = TopicsUtil.removeTopicResponse(); + + when(topics.remove(topicId)).thenReturn(expectedResponse); + + RemoveTopicResponseSuccess response = topics.remove(topicId); + + assertNotNull(response); + assertEquals(expectedResponse.getId(), response.getId()); + assertEquals(true, response.isDeleted()); + verify(topics, times(1)).remove(topicId); + } + + @Test + public void testListTopics_Success() throws ResendException { + ListTopicsResponseSuccess expectedResponse = TopicsUtil.createListTopicsResponse(); + + when(topics.list()).thenReturn(expectedResponse); + + ListTopicsResponseSuccess response = topics.list(); + + assertNotNull(response); + assertEquals(expectedResponse.getData().size(), response.getData().size()); + assertEquals(expectedResponse.hasMore(), response.hasMore()); + verify(topics, times(1)).list(); + } + + @Test + public void testListTopicsWithPagination_Success() throws ResendException { + ListParams params = ListParams.builder() + .limit(3) + .build(); + ListTopicsResponseSuccess expectedResponse = TopicsUtil.createListTopicsResponse(); + + when(topics.list(params)).thenReturn(expectedResponse); + + ListTopicsResponseSuccess response = topics.list(params); + + assertNotNull(response); + assertEquals(expectedResponse.getData().size(), response.getData().size()); + verify(topics, times(1)).list(params); + } +} diff --git a/src/test/java/com/resend/services/util/TopicsUtil.java b/src/test/java/com/resend/services/util/TopicsUtil.java new file mode 100644 index 0000000..9d09d50 --- /dev/null +++ b/src/test/java/com/resend/services/util/TopicsUtil.java @@ -0,0 +1,123 @@ +package com.resend.services.util; + +import com.resend.services.topics.model.*; + +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class for creating test data for Topics service tests. + */ +public class TopicsUtil { + + /** + * Creates a sample CreateTopicOptions for testing. + * + * @return A CreateTopicOptions instance. + */ + public static CreateTopicOptions createTopicOptions() { + return CreateTopicOptions.builder() + .name("Weekly Newsletter") + .defaultSubscription("opt_in") + .description("Subscribe to our weekly newsletter for updates") + .build(); + } + + /** + * Creates a sample UpdateTopicOptions for testing. + * + * @return An UpdateTopicOptions instance. + */ + public static UpdateTopicOptions updateTopicOptions() { + return UpdateTopicOptions.builder() + .name("Monthly Newsletter") + .description("Subscribe to our monthly newsletter for updates") + .build(); + } + + /** + * Creates a sample CreateTopicResponse for testing. + * + * @return A CreateTopicResponse instance. + */ + public static CreateTopicResponseSuccess createTopicResponse() { + return new CreateTopicResponseSuccess("b6d24b8e-af0b-4c3c-be0c-359bbd97381e"); + } + + /** + * Creates a sample UpdateTopicResponse for testing. + * + * @return An UpdateTopicResponse instance. + */ + public static UpdateTopicResponseSuccess updateTopicResponse() { + return new UpdateTopicResponseSuccess("b6d24b8e-af0b-4c3c-be0c-359bbd97381e"); + } + + /** + * Creates a sample RemoveTopicResponse for testing. + * + * @return A RemoveTopicResponse instance. + */ + public static RemoveTopicResponseSuccess removeTopicResponse() { + return new RemoveTopicResponseSuccess("topic", "b6d24b8e-af0b-4c3c-be0c-359bbd97381e", true); + } + + /** + * Creates a sample Topic for testing. + * + * @return A Topic instance. + */ + public static GetTopicResponseSuccess createTopic() { + return new GetTopicResponseSuccess( + "b6d24b8e-af0b-4c3c-be0c-359bbd97381e", + "Weekly Newsletter", + "Weekly newsletter for our subscribers", + "opt_in", + "2023-04-08T00:11:13.110779+00:00" + ); + } + + /** + * Creates a list of sample Topics for testing. + * + * @return A list of Topic instances. + */ + public static List createTopicList() { + List topics = new ArrayList<>(); + + topics.add(new Topic( + "b6d24b8e-af0b-4c3c-be0c-359bbd97381e", + "Weekly Newsletter", + "Weekly newsletter for our subscribers", + "opt_in", + "2023-04-08T00:11:13.110779+00:00" + )); + + topics.add(new Topic( + "c7e35c9f-bg1c-5d4d-cf1d-460cce08492f", + "Monthly Updates", + "Monthly updates and announcements", + "opt_out", + "2023-04-09T00:11:13.110779+00:00" + )); + + topics.add(new Topic( + "d8f46da0-ch2d-6e5e-dg2e-571ddf19503g", + "Product Launches", + "Get notified about new product launches", + "opt_in", + "2023-04-10T00:11:13.110779+00:00" + )); + + return topics; + } + + /** + * Creates a sample ListTopicsResponse for testing. + * + * @return A ListTopicsResponse instance. + */ + public static ListTopicsResponseSuccess createListTopicsResponse() { + return new ListTopicsResponseSuccess("list", createTopicList(), false); + } +}