diff --git a/src/main/java/io/getstream/chat/java/models/Poll.java b/src/main/java/io/getstream/chat/java/models/Poll.java new file mode 100644 index 00000000..8f0e62fb --- /dev/null +++ b/src/main/java/io/getstream/chat/java/models/Poll.java @@ -0,0 +1,787 @@ +package io.getstream.chat.java.models; + +import com.fasterxml.jackson.annotation.*; +import io.getstream.chat.java.exceptions.StreamException; +import io.getstream.chat.java.models.Poll.CreatePollRequestData.CreatePollRequest; +import io.getstream.chat.java.models.User.UserRequestObject; +import io.getstream.chat.java.models.framework.RequestObjectBuilder; +import io.getstream.chat.java.models.framework.StreamRequest; +import io.getstream.chat.java.models.framework.StreamResponseObject; +import io.getstream.chat.java.services.PollService; +import io.getstream.chat.java.services.framework.Client; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import lombok.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import retrofit2.Call; + +/** Represents poll functionality in Stream Chat. */ +@Data +@NoArgsConstructor +public class Poll { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("name") + private String name; + + @Nullable + @JsonProperty("description") + private String description; + + @Nullable + @JsonProperty("voting_visibility") + private VotingVisibility votingVisibility; + + @Nullable + @JsonProperty("enforce_unique_vote") + private Boolean enforceUniqueVote; + + @Nullable + @JsonProperty("max_votes_allowed") + private Integer maxVotesAllowed; + + @Nullable + @JsonProperty("allow_user_suggested_options") + private Boolean allowUserSuggestedOptions; + + @Nullable + @JsonProperty("allow_answers") + private Boolean allowAnswers; + + @Nullable + @JsonProperty("is_closed") + private Boolean isClosed; + + @Nullable + @JsonProperty("options") + private List options; + + @Nullable + @JsonProperty("vote_count") + private Integer voteCount; + + @Nullable + @JsonProperty("vote_counts_by_option") + private Map voteCountsByOption; + + @Nullable + @JsonProperty("answers_count") + private Integer answersCount; + + @Nullable + @JsonProperty("created_by_id") + private String createdById; + + @Nullable + @JsonProperty("created_by") + private User createdBy; + + @Nullable + @JsonProperty("created_at") + private Date createdAt; + + @Nullable + @JsonProperty("updated_at") + private Date updatedAt; + + @NotNull @JsonIgnore private Map additionalFields = new HashMap<>(); + + @JsonAnyGetter + public Map getAdditionalFields() { + return this.additionalFields; + } + + @JsonAnySetter + public void setAdditionalField(String name, Object value) { + this.additionalFields.put(name, value); + } + + /** Voting visibility options for a poll. */ + public enum VotingVisibility { + @JsonProperty("public") + PUBLIC, + @JsonProperty("anonymous") + ANONYMOUS, + @JsonEnumDefaultValue + UNKNOWN + } + + /** A poll option. */ + @Data + @NoArgsConstructor + public static class PollOption { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("text") + private String text; + + @Nullable + @JsonProperty("position") + private Integer position; + + @Nullable + @JsonProperty("vote_count") + private Integer voteCount; + + @NotNull @JsonIgnore private Map additionalFields = new HashMap<>(); + + @JsonAnyGetter + public Map getAdditionalFields() { + return this.additionalFields; + } + + @JsonAnySetter + public void setAdditionalField(String name, Object value) { + this.additionalFields.put(name, value); + } + } + + /** Request object for poll options. */ + @Builder + @Setter + @Getter + @EqualsAndHashCode + public static class PollOptionRequestObject { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("text") + private String text; + + @Nullable + @JsonProperty("position") + private Integer position; + + @Singular @Nullable @JsonIgnore private Map additionalFields; + + @JsonAnyGetter + public Map getAdditionalFields() { + return this.additionalFields; + } + + @JsonAnySetter + public void setAdditionalField(String name, Object value) { + this.additionalFields.put(name, value); + } + + @Nullable + public static PollOptionRequestObject buildFrom(@Nullable PollOption pollOption) { + return RequestObjectBuilder.build(PollOptionRequestObject.class, pollOption); + } + } + + /** Request object for polls. */ + @Builder + @Setter + @Getter + @EqualsAndHashCode + public static class PollRequestObject { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("name") + private String name; + + @Nullable + @JsonProperty("description") + private String description; + + @Nullable + @JsonProperty("voting_visibility") + private VotingVisibility votingVisibility; + + @Nullable + @JsonProperty("enforce_unique_vote") + private Boolean enforceUniqueVote; + + @Nullable + @JsonProperty("max_votes_allowed") + private Integer maxVotesAllowed; + + @Nullable + @JsonProperty("allow_user_suggested_options") + private Boolean allowUserSuggestedOptions; + + @Nullable + @JsonProperty("allow_answers") + private Boolean allowAnswers; + + @Singular + @Nullable + @JsonProperty("options") + private List options; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + @Singular @Nullable @JsonIgnore private Map additionalFields; + + @JsonAnyGetter + public Map getAdditionalFields() { + return this.additionalFields; + } + + @JsonAnySetter + public void setAdditionalField(String name, Object value) { + this.additionalFields.put(name, value); + } + + @Nullable + public static PollRequestObject buildFrom(@Nullable Poll poll) { + return RequestObjectBuilder.build(PollRequestObject.class, poll); + } + } + + /** Response for poll creation. */ + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class CreatePollResponse extends StreamResponseObject { + @NotNull + @JsonProperty("poll") + private Poll poll; + } + + /** Response for getting a poll. */ + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class GetPollResponse extends StreamResponseObject { + @NotNull + @JsonProperty("poll") + private Poll poll; + } + + /** Request for getting a poll. */ + @Getter + @EqualsAndHashCode + @RequiredArgsConstructor + public static class GetPollRequest extends StreamRequest { + @NotNull private final String pollId; + @Nullable private String userId; + + @NotNull + public GetPollRequest userId(@NotNull String userId) { + this.userId = userId; + return this; + } + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).get(this.pollId, this.userId); + } + } + + /** Response for updating a poll. */ + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class UpdatePollResponse extends StreamResponseObject { + @NotNull + @JsonProperty("poll") + private Poll poll; + } + + /** Request data for updating a poll. */ + @Builder( + builderClassName = "UpdatePollRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class UpdatePollRequestData { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("name") + private String name; + + @Nullable + @JsonProperty("description") + private String description; + + @Nullable + @JsonProperty("voting_visibility") + private VotingVisibility votingVisibility; + + @Nullable + @JsonProperty("enforce_unique_vote") + private Boolean enforceUniqueVote; + + @Nullable + @JsonProperty("max_votes_allowed") + private Integer maxVotesAllowed; + + @Nullable + @JsonProperty("allow_user_suggested_options") + private Boolean allowUserSuggestedOptions; + + @Nullable + @JsonProperty("allow_answers") + private Boolean allowAnswers; + + @Nullable + @JsonProperty("is_closed") + private Boolean isClosed; + + @Singular + @Nullable + @JsonProperty("options") + private List options; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + public static class UpdatePollRequest extends StreamRequest { + public UpdatePollRequest() {} + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).update(this.internalBuild()); + } + } + } + + /** Request data for partially updating a poll. */ + @Builder( + builderClassName = "PartialUpdatePollRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class PartialUpdatePollRequestData { + @Singular + @Nullable + @JsonProperty("set") + private Map setValues; + + @Singular + @Nullable + @JsonProperty("unset") + private List unsetValues; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + public static class PartialUpdatePollRequest extends StreamRequest { + @NotNull private String pollId; + + private PartialUpdatePollRequest(@NotNull String pollId) { + this.pollId = pollId; + } + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).partialUpdate(this.pollId, this.internalBuild()); + } + } + } + + /** Request for deleting a poll. */ + @Getter + @EqualsAndHashCode + @RequiredArgsConstructor + public static class DeletePollRequest extends StreamRequest { + @NotNull private final String pollId; + @Nullable private String userId; + + @NotNull + public DeletePollRequest userId(@NotNull String userId) { + this.userId = userId; + return this; + } + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).delete(this.pollId, this.userId); + } + } + + /** Response for creating a poll option. */ + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class CreatePollOptionResponse extends StreamResponseObject { + @NotNull + @JsonProperty("poll_option") + private PollOption pollOption; + } + + /** Request data for creating a poll option. */ + @Builder( + builderClassName = "CreatePollOptionRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class CreatePollOptionRequestData { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("text") + private String text; + + @Nullable + @JsonProperty("position") + private Integer position; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + public static class CreatePollOptionRequest extends StreamRequest { + @NotNull private String pollId; + + private CreatePollOptionRequest(@NotNull String pollId) { + this.pollId = pollId; + } + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).createOption(this.pollId, this.internalBuild()); + } + } + } + + /** Response for updating a poll option. */ + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class UpdatePollOptionResponse extends StreamResponseObject { + @NotNull + @JsonProperty("poll_option") + private PollOption pollOption; + } + + /** Request data for updating a poll option. */ + @Builder( + builderClassName = "UpdatePollOptionRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class UpdatePollOptionRequestData { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("text") + private String text; + + @Nullable + @JsonProperty("position") + private Integer position; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + public static class UpdatePollOptionRequest extends StreamRequest { + @NotNull private String pollId; + + private UpdatePollOptionRequest(@NotNull String pollId) { + this.pollId = pollId; + } + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).updateOption(this.pollId, this.internalBuild()); + } + } + } + + /** Request for deleting a poll option. */ + @Getter + @EqualsAndHashCode + public static class DeletePollOptionRequest extends StreamRequest { + @NotNull private final String pollId; + @NotNull private final String optionId; + @Nullable private String userId; + + public DeletePollOptionRequest(@NotNull String pollId, @NotNull String optionId) { + this.pollId = pollId; + this.optionId = optionId; + } + + @NotNull + public DeletePollOptionRequest userId(@NotNull String userId) { + this.userId = userId; + return this; + } + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).deleteOption(this.pollId, this.optionId, this.userId); + } + } + + /** Response for querying polls. */ + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class QueryPollsResponse extends StreamResponseObject { + @Nullable + @JsonProperty("polls") + private List polls; + + @Nullable + @JsonProperty("next") + private String next; + + @Nullable + @JsonProperty("prev") + private String prev; + } + + /** Request data for querying polls. */ + @Builder( + builderClassName = "QueryPollsRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class QueryPollsRequestData { + @Nullable + @JsonProperty("filter") + private Map filter; + + @Singular + @Nullable + @JsonProperty("sort") + private List sorts; + + @Nullable + @JsonProperty("limit") + private Integer limit; + + @Nullable + @JsonProperty("next") + private String next; + + @Nullable + @JsonProperty("prev") + private String prev; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + public static class QueryPollsRequest extends StreamRequest { + public QueryPollsRequest() {} + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).query(this.internalBuild()); + } + } + } + + /** Request data for creating a poll. */ + @Builder( + builderClassName = "CreatePollRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class CreatePollRequestData { + @Nullable + @JsonProperty("id") + private String id; + + @Nullable + @JsonProperty("name") + private String name; + + @Nullable + @JsonProperty("description") + private String description; + + @Nullable + @JsonProperty("voting_visibility") + private VotingVisibility votingVisibility; + + @Nullable + @JsonProperty("enforce_unique_vote") + private Boolean enforceUniqueVote; + + @Nullable + @JsonProperty("max_votes_allowed") + private Integer maxVotesAllowed; + + @Nullable + @JsonProperty("allow_user_suggested_options") + private Boolean allowUserSuggestedOptions; + + @Nullable + @JsonProperty("allow_answers") + private Boolean allowAnswers; + + @Singular + @Nullable + @JsonProperty("options") + private List options; + + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("user") + private UserRequestObject user; + + public static class CreatePollRequest extends StreamRequest { + public CreatePollRequest() {} + + @Override + protected Call generateCall(Client client) throws StreamException { + return client.create(PollService.class).create(this.internalBuild()); + } + } + } + + /** + * Creates a poll creation request. + * + * @return the created request + */ + @NotNull + public static CreatePollRequest create() { + return new CreatePollRequest(); + } + + /** + * Gets a poll by ID. + * + * @param pollId the poll ID + * @return the created request + */ + @NotNull + public static GetPollRequest get(@NotNull String pollId) { + return new GetPollRequest(pollId); + } + + /** + * Updates a poll (full update). + * + * @return the created request + */ + @NotNull + public static UpdatePollRequestData.UpdatePollRequest update() { + return new UpdatePollRequestData.UpdatePollRequest(); + } + + /** + * Partially updates a poll. + * + * @param pollId the poll ID + * @return the created request + */ + @NotNull + public static PartialUpdatePollRequestData.PartialUpdatePollRequest partialUpdate( + @NotNull String pollId) { + return new PartialUpdatePollRequestData.PartialUpdatePollRequest(pollId); + } + + /** + * Deletes a poll. + * + * @param pollId the poll ID + * @return the created request + */ + @NotNull + public static DeletePollRequest delete(@NotNull String pollId) { + return new DeletePollRequest(pollId); + } + + /** + * Creates a poll option. + * + * @param pollId the poll ID + * @return the created request + */ + @NotNull + public static CreatePollOptionRequestData.CreatePollOptionRequest createOption( + @NotNull String pollId) { + return new CreatePollOptionRequestData.CreatePollOptionRequest(pollId); + } + + /** + * Updates a poll option. + * + * @param pollId the poll ID + * @return the created request + */ + @NotNull + public static UpdatePollOptionRequestData.UpdatePollOptionRequest updateOption( + @NotNull String pollId) { + return new UpdatePollOptionRequestData.UpdatePollOptionRequest(pollId); + } + + /** + * Deletes a poll option. + * + * @param pollId the poll ID + * @param optionId the option ID + * @return the created request + */ + @NotNull + public static DeletePollOptionRequest deleteOption( + @NotNull String pollId, @NotNull String optionId) { + return new DeletePollOptionRequest(pollId, optionId); + } + + /** + * Queries polls. + * + * @return the created request + */ + @NotNull + public static QueryPollsRequestData.QueryPollsRequest query() { + return new QueryPollsRequestData.QueryPollsRequest(); + } +} diff --git a/src/main/java/io/getstream/chat/java/services/PollService.java b/src/main/java/io/getstream/chat/java/services/PollService.java new file mode 100644 index 00000000..383d109c --- /dev/null +++ b/src/main/java/io/getstream/chat/java/services/PollService.java @@ -0,0 +1,126 @@ +package io.getstream.chat.java.services; + +import io.getstream.chat.java.models.Poll.CreatePollOptionRequestData; +import io.getstream.chat.java.models.Poll.CreatePollOptionResponse; +import io.getstream.chat.java.models.Poll.CreatePollRequestData; +import io.getstream.chat.java.models.Poll.CreatePollResponse; +import io.getstream.chat.java.models.Poll.GetPollResponse; +import io.getstream.chat.java.models.Poll.PartialUpdatePollRequestData; +import io.getstream.chat.java.models.Poll.QueryPollsRequestData; +import io.getstream.chat.java.models.Poll.QueryPollsResponse; +import io.getstream.chat.java.models.Poll.UpdatePollOptionRequestData; +import io.getstream.chat.java.models.Poll.UpdatePollOptionResponse; +import io.getstream.chat.java.models.Poll.UpdatePollRequestData; +import io.getstream.chat.java.models.Poll.UpdatePollResponse; +import io.getstream.chat.java.models.framework.StreamResponseObject; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import retrofit2.Call; +import retrofit2.http.Body; +import retrofit2.http.DELETE; +import retrofit2.http.GET; +import retrofit2.http.PATCH; +import retrofit2.http.POST; +import retrofit2.http.PUT; +import retrofit2.http.Path; +import retrofit2.http.Query; + +/** Service interface for Poll API endpoints. */ +public interface PollService { + + /** + * Creates a new poll. + * + * @param request The poll creation request data + * @return A response with the created poll + */ + @POST("polls") + Call create(@NotNull @Body CreatePollRequestData request); + + /** + * Gets a poll by ID. + * + * @param pollId The poll ID + * @param userId Optional user ID + * @return A response with the poll + */ + @GET("polls/{poll_id}") + Call get( + @NotNull @Path("poll_id") String pollId, @Nullable @Query("user_id") String userId); + + /** + * Updates a poll (full update). + * + * @param request The poll update request data + * @return A response with the updated poll + */ + @PUT("polls") + Call update(@NotNull @Body UpdatePollRequestData request); + + /** + * Partially updates a poll. + * + * @param pollId The poll ID + * @param request The partial update request data + * @return A response with the updated poll + */ + @PATCH("polls/{poll_id}") + Call partialUpdate( + @NotNull @Path("poll_id") String pollId, @NotNull @Body PartialUpdatePollRequestData request); + + /** + * Deletes a poll. + * + * @param pollId The poll ID + * @param userId Optional user ID + * @return A response indicating success + */ + @DELETE("polls/{poll_id}") + Call delete( + @NotNull @Path("poll_id") String pollId, @Nullable @Query("user_id") String userId); + + /** + * Creates a poll option. + * + * @param pollId The poll ID + * @param request The poll option creation request data + * @return A response with the created poll option + */ + @POST("polls/{poll_id}/options") + Call createOption( + @NotNull @Path("poll_id") String pollId, @NotNull @Body CreatePollOptionRequestData request); + + /** + * Updates a poll option. + * + * @param pollId The poll ID + * @param request The poll option update request data + * @return A response with the updated poll option + */ + @PUT("polls/{poll_id}/options") + Call updateOption( + @NotNull @Path("poll_id") String pollId, @NotNull @Body UpdatePollOptionRequestData request); + + /** + * Deletes a poll option. + * + * @param pollId The poll ID + * @param optionId The option ID + * @param userId Optional user ID + * @return A response indicating success + */ + @DELETE("polls/{poll_id}/options/{option_id}") + Call deleteOption( + @NotNull @Path("poll_id") String pollId, + @NotNull @Path("option_id") String optionId, + @Nullable @Query("user_id") String userId); + + /** + * Queries polls. + * + * @param request The query polls request data + * @return A response with the matching polls + */ + @POST("polls/query") + Call query(@NotNull @Body QueryPollsRequestData request); +} diff --git a/src/test/java/io/getstream/chat/java/PollTest.java b/src/test/java/io/getstream/chat/java/PollTest.java new file mode 100644 index 00000000..03c6cef5 --- /dev/null +++ b/src/test/java/io/getstream/chat/java/PollTest.java @@ -0,0 +1,433 @@ +package io.getstream.chat.java; + +import io.getstream.chat.java.models.Poll; +import io.getstream.chat.java.models.Poll.CreatePollOptionResponse; +import io.getstream.chat.java.models.Poll.CreatePollResponse; +import io.getstream.chat.java.models.Poll.GetPollResponse; +import io.getstream.chat.java.models.Poll.PollOptionRequestObject; +import io.getstream.chat.java.models.Poll.QueryPollsResponse; +import io.getstream.chat.java.models.Poll.UpdatePollOptionResponse; +import io.getstream.chat.java.models.Poll.UpdatePollResponse; +import io.getstream.chat.java.models.Poll.VotingVisibility; +import io.getstream.chat.java.models.Sort; +import io.getstream.chat.java.models.framework.StreamResponseObject; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** Tests for Poll functionality. */ +public class PollTest extends BasicTest { + + @DisplayName("Can create a basic poll") + @Test + void whenCreatingPoll_thenNoException() { + String pollName = "Test Poll " + UUID.randomUUID(); + + CreatePollResponse response = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name(pollName) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option 1").build()) + .option(PollOptionRequestObject.builder().text("Option 2").build()) + .request()); + + Assertions.assertNotNull(response); + Assertions.assertNotNull(response.getPoll()); + Assertions.assertEquals(pollName, response.getPoll().getName()); + Assertions.assertNotNull(response.getPoll().getId()); + Assertions.assertEquals(2, response.getPoll().getOptions().size()); + } + + @DisplayName("Can create a poll with all options") + @Test + void whenCreatingPollWithAllOptions_thenNoException() { + String pollName = "Comprehensive Poll " + UUID.randomUUID(); + + CreatePollResponse response = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name(pollName) + .description("A test poll with all options") + .votingVisibility(VotingVisibility.PUBLIC) + .enforceUniqueVote(true) + .maxVotesAllowed(3) + .allowUserSuggestedOptions(true) + .allowAnswers(true) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option A").position(0).build()) + .option(PollOptionRequestObject.builder().text("Option B").position(1).build()) + .option(PollOptionRequestObject.builder().text("Option C").position(2).build()) + .request()); + + Assertions.assertNotNull(response); + Poll poll = response.getPoll(); + Assertions.assertEquals(pollName, poll.getName()); + Assertions.assertEquals("A test poll with all options", poll.getDescription()); + Assertions.assertEquals(VotingVisibility.PUBLIC, poll.getVotingVisibility()); + Assertions.assertEquals(true, poll.getEnforceUniqueVote()); + Assertions.assertEquals(3, poll.getMaxVotesAllowed()); + Assertions.assertEquals(true, poll.getAllowUserSuggestedOptions()); + Assertions.assertEquals(true, poll.getAllowAnswers()); + Assertions.assertEquals(3, poll.getOptions().size()); + } + + @DisplayName("Can create poll with anonymous voting") + @Test + void whenCreatingAnonymousPoll_thenNoException() { + CreatePollResponse response = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Anonymous Poll " + UUID.randomUUID()) + .votingVisibility(VotingVisibility.ANONYMOUS) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Choice A").build()) + .option(PollOptionRequestObject.builder().text("Choice B").build()) + .request()); + + Assertions.assertNotNull(response); + Assertions.assertEquals(VotingVisibility.ANONYMOUS, response.getPoll().getVotingVisibility()); + } + + @DisplayName("Can get a poll by ID") + @Test + void whenGettingPoll_thenNoException() { + // Create a poll first + String pollName = "Get Test Poll " + UUID.randomUUID(); + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name(pollName) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option 1").build()) + .option(PollOptionRequestObject.builder().text("Option 2").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Get the poll + GetPollResponse getResponse = Assertions.assertDoesNotThrow(() -> Poll.get(pollId).request()); + + Assertions.assertNotNull(getResponse); + Assertions.assertNotNull(getResponse.getPoll()); + Assertions.assertEquals(pollId, getResponse.getPoll().getId()); + Assertions.assertEquals(pollName, getResponse.getPoll().getName()); + } + + @DisplayName("Can get a poll with user_id") + @Test + void whenGettingPollWithUserId_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll with user " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("A").build()) + .request()); + + // Get the poll with user_id + GetPollResponse getResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.get(createResponse.getPoll().getId()) + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(getResponse.getPoll()); + } + + @DisplayName("Can update a poll") + @Test + void whenUpdatingPoll_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Original Name") + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option 1").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Update the poll + String newName = "Updated Name " + UUID.randomUUID(); + UpdatePollResponse updateResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.update() + .id(pollId) + .name(newName) + .description("Updated description") + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("New Option 1").build()) + .option(PollOptionRequestObject.builder().text("New Option 2").build()) + .request()); + + Assertions.assertNotNull(updateResponse); + Assertions.assertEquals(newName, updateResponse.getPoll().getName()); + Assertions.assertEquals("Updated description", updateResponse.getPoll().getDescription()); + } + + @DisplayName("Can close a poll via update") + @Test + void whenClosingPoll_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll to close " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("A").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Close the poll + UpdatePollResponse updateResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.update() + .id(pollId) + .name("Poll to close") + .isClosed(true) + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(updateResponse); + Assertions.assertTrue(updateResponse.getPoll().getIsClosed()); + } + + @DisplayName("Can partially update a poll with set") + @Test + void whenPartiallyUpdatingPollWithSet_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Original Partial " + UUID.randomUUID()) + .description("Original description") + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("A").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Partial update the poll + UpdatePollResponse updateResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.partialUpdate(pollId) + .setValue("name", "Partial Updated Name") + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(updateResponse); + Assertions.assertEquals("Partial Updated Name", updateResponse.getPoll().getName()); + // Description should remain unchanged + Assertions.assertEquals("Original description", updateResponse.getPoll().getDescription()); + } + + @DisplayName("Can partially update a poll with unset") + @Test + void whenPartiallyUpdatingPollWithUnset_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll with description " + UUID.randomUUID()) + .description("To be removed") + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("A").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Partial update to unset description + UpdatePollResponse updateResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.partialUpdate(pollId) + .unsetValue("description") + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(updateResponse); + Assertions.assertNull(updateResponse.getPoll().getDescription()); + } + + @DisplayName("Can delete a poll") + @Test + void whenDeletingPoll_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll to delete " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("A").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Delete the poll + StreamResponseObject deleteResponse = + Assertions.assertDoesNotThrow( + () -> Poll.delete(pollId).userId(testUserRequestObject.getId()).request()); + + Assertions.assertNotNull(deleteResponse); + } + + @DisplayName("Can create a poll option") + @Test + void whenCreatingPollOption_thenNoException() { + // Create a poll first (with allow_user_suggested_options enabled) + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll for options " + UUID.randomUUID()) + .allowUserSuggestedOptions(true) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Initial Option").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + + // Create a new option + CreatePollOptionResponse optionResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.createOption(pollId) + .text("New Option " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(optionResponse); + Assertions.assertNotNull(optionResponse.getPollOption()); + Assertions.assertNotNull(optionResponse.getPollOption().getId()); + Assertions.assertNotNull(optionResponse.getPollOption().getText()); + } + + @DisplayName("Can update a poll option") + @Test + void whenUpdatingPollOption_thenNoException() { + // Create a poll first + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll for option update " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Original Option").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + String optionId = createResponse.getPoll().getOptions().get(0).getId(); + + // Update the option + String newText = "Updated Option " + UUID.randomUUID(); + UpdatePollOptionResponse updateResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.updateOption(pollId) + .id(optionId) + .text(newText) + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(updateResponse); + Assertions.assertNotNull(updateResponse.getPollOption()); + Assertions.assertEquals(newText, updateResponse.getPollOption().getText()); + } + + @DisplayName("Can delete a poll option") + @Test + void whenDeletingPollOption_thenNoException() { + // Create a poll with multiple options + CreatePollResponse createResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Poll for option delete " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option 1").build()) + .option(PollOptionRequestObject.builder().text("Option 2").build()) + .request()); + + String pollId = createResponse.getPoll().getId(); + String optionId = createResponse.getPoll().getOptions().get(0).getId(); + + // Delete the option + StreamResponseObject deleteResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.deleteOption(pollId, optionId) + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(deleteResponse); + } + + @DisplayName("Can query polls") + @Test + void whenQueryingPolls_thenNoException() { + // Create a poll first + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name("Query Poll " + UUID.randomUUID()) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option 1").build()) + .request()); + + // Query polls + QueryPollsResponse queryResponse = + Assertions.assertDoesNotThrow( + () -> Poll.query().userId(testUserRequestObject.getId()).request()); + + Assertions.assertNotNull(queryResponse); + Assertions.assertNotNull(queryResponse.getPolls()); + } + + @DisplayName("Can query polls with filter and sort") + @Test + void whenQueryingPollsWithFilterAndSort_thenNoException() { + // Create a poll first + String pollName = "Filter Poll " + UUID.randomUUID(); + Assertions.assertDoesNotThrow( + () -> + Poll.create() + .name(pollName) + .userId(testUserRequestObject.getId()) + .option(PollOptionRequestObject.builder().text("Option 1").build()) + .request()); + + // Query polls with filter and sort + QueryPollsResponse queryResponse = + Assertions.assertDoesNotThrow( + () -> + Poll.query() + .filter(Map.of("name", pollName)) + .sort(Sort.builder().field("created_at").direction(Sort.Direction.DESC).build()) + .limit(10) + .userId(testUserRequestObject.getId()) + .request()); + + Assertions.assertNotNull(queryResponse); + Assertions.assertNotNull(queryResponse.getPolls()); + } +}