diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt index 966d97b7472..794239d2694 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt @@ -1832,7 +1832,7 @@ constructor( text = option.text, custom = option.extraData, ) - return pollsApi.createPollOption(pollId, body).mapDomain { it.poll_option.toPollOption() } + return pollsApi.createPollOption(pollId, body).mapDomain { it.pollOption.toDomain() } } override fun updatePollOption(pollId: String, option: PollOption): Call { @@ -1845,7 +1845,7 @@ constructor( text = option.text, custom = option.extraData, ) - return pollsApi.updatePollOption(pollId, body).mapDomain { it.poll_option.toPollOption() } + return pollsApi.updatePollOption(pollId, body).mapDomain { it.pollOption.toDomain() } } override fun deletePollOption(pollId: String, optionId: String): Call { diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/endpoint/PollsApi.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/endpoint/PollsApi.kt index 324158ccba4..dbcb0c3216a 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/endpoint/PollsApi.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/endpoint/PollsApi.kt @@ -17,7 +17,6 @@ package io.getstream.chat.android.client.api2.endpoint import io.getstream.chat.android.client.api.AuthenticatedApi -import io.getstream.chat.android.client.api2.model.response.PollOptionResponse import io.getstream.chat.android.client.api2.model.response.PollResponse import io.getstream.chat.android.client.api2.model.response.PollVoteResponse import io.getstream.chat.android.client.api2.model.response.QueryPollVotesResponse @@ -26,6 +25,7 @@ import io.getstream.chat.android.client.call.RetrofitCall import io.getstream.chat.android.network.models.CastPollVoteRequest import io.getstream.chat.android.network.models.CreatePollOptionRequest import io.getstream.chat.android.network.models.CreatePollRequest +import io.getstream.chat.android.network.models.PollOptionResponse import io.getstream.chat.android.network.models.QueryPollVotesRequest import io.getstream.chat.android.network.models.QueryPollsRequest import io.getstream.chat.android.network.models.Response diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt index 1b93219cb93..44a1e8ce22d 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt @@ -132,6 +132,7 @@ import io.getstream.chat.android.network.models.ChannelPushPreferencesResponse import io.getstream.chat.android.network.models.ChatPreferencesResponse import io.getstream.chat.android.network.models.DeviceResponse import io.getstream.chat.android.network.models.GetApplicationResponse +import io.getstream.chat.android.network.models.PollOptionResponseData import io.getstream.chat.android.network.models.PushPreferencesResponse import io.getstream.chat.android.network.models.UnreadCountsChannel import io.getstream.chat.android.network.models.UnreadCountsChannelType @@ -525,6 +526,15 @@ internal class DomainMapping( ) } + /** + * Transforms [PollOptionResponseData] into [PollOption] + */ + internal fun PollOptionResponseData.toDomain(): PollOption = PollOption( + id = id, + text = text, + extraData = custom.mapNotNull { (key, value) -> value?.let { key to it } }.toMap(), + ) + /** * Transforms [DownstreamPollOptionDto] to [Option] * diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/MoshiChatParser.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/MoshiChatParser.kt index e4fa2e4df30..3b0c71bd12b 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/MoshiChatParser.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/MoshiChatParser.kt @@ -49,6 +49,7 @@ import io.getstream.chat.android.client.parser2.adapters.ExactDateAdapter import io.getstream.chat.android.client.parser2.adapters.NullCollectionsAsEmptyFactory import io.getstream.chat.android.client.parser2.adapters.PollOptionInputAdapter import io.getstream.chat.android.client.parser2.adapters.PollOptionRequestAdapter +import io.getstream.chat.android.client.parser2.adapters.PollOptionResponseDataAdapter import io.getstream.chat.android.client.parser2.adapters.UpdatePollOptionRequestAdapter import io.getstream.chat.android.client.parser2.adapters.UpdatePollRequestAdapter import io.getstream.chat.android.client.parser2.adapters.UpstreamMemberDataDtoAdapter @@ -102,6 +103,7 @@ internal class MoshiChatParser( .add(PollOptionInputAdapter) .add(PollOptionRequestAdapter) .add(EventRequestAdapter) + .add(PollOptionResponseDataAdapter) .add( CreatePollRequest.VotingVisibility::class.java, CreatePollRequest.VotingVisibility.VotingVisibilityAdapter(), diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/PollOptionResponseDataAdapter.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/PollOptionResponseDataAdapter.kt new file mode 100644 index 00000000000..9d84dc37ae3 --- /dev/null +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/PollOptionResponseDataAdapter.kt @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getstream.chat.android.client.parser2.adapters + +import com.squareup.moshi.FromJson +import com.squareup.moshi.JsonAdapter +import com.squareup.moshi.JsonReader +import com.squareup.moshi.JsonWriter +import com.squareup.moshi.ToJson +import io.getstream.chat.android.network.models.PollOptionResponseData + +/** + * Collects the root-level custom fields of a poll option into `custom`, which is how the API sends + * them. + */ +internal object PollOptionResponseDataAdapter : + CustomObjectDtoAdapter( + PollOptionResponseData::class, + extraDataPropertyName = "custom", + ) { + + @FromJson + fun fromJson( + jsonReader: JsonReader, + mapAdapter: JsonAdapter>, + valueAdapter: JsonAdapter, + ): PollOptionResponseData? = parseWithExtraData(jsonReader, mapAdapter, valueAdapter) + + @ToJson + @Suppress("UNUSED_PARAMETER") + fun toJson(jsonWriter: JsonWriter, value: PollOptionResponseData): Unit = + error("Can't convert this to Json") +} diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/response/PollOptionResponse.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/PollOptionResponse.kt similarity index 60% rename from stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/response/PollOptionResponse.kt rename to stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/PollOptionResponse.kt index b4f26869f93..bc467c7bbea 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/response/PollOptionResponse.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/PollOptionResponse.kt @@ -14,19 +14,25 @@ * limitations under the License. */ -package io.getstream.chat.android.client.api2.model.response +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport", +) + +package io.getstream.chat.android.network.models -import com.squareup.moshi.JsonClass -import io.getstream.chat.android.client.api2.model.dto.DownstreamPollOptionDto +import com.squareup.moshi.Json /** - * Response for creating/updating a poll option. * - * @param duration The duration of the request. - * @param poll_option The poll option object. */ -@JsonClass(generateAdapter = true) +@com.squareup.moshi.JsonClass(generateAdapter = true) internal data class PollOptionResponse( - val duration: String, - val poll_option: DownstreamPollOptionDto, + @Json(name = "duration") + internal val duration: String, + + @Json(name = "poll_option") + internal val pollOption: io.getstream.chat.android.network.models.PollOptionResponseData, ) diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/PollOptionResponseData.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/PollOptionResponseData.kt new file mode 100644 index 00000000000..53f01258003 --- /dev/null +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/PollOptionResponseData.kt @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport", +) + +package io.getstream.chat.android.network.models + +import com.squareup.moshi.Json + +/** + * + */ +@com.squareup.moshi.JsonClass(generateAdapter = true) +internal data class PollOptionResponseData( + @Json(name = "id") + internal val id: String, + + @Json(name = "text") + internal val text: String, + + @Json(name = "custom") + internal val custom: Map = emptyMap(), +) diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt index 8bb1354de88..aa165a826c8 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/Mother.kt @@ -97,6 +97,7 @@ import io.getstream.chat.android.network.models.BlockUsersResponse import io.getstream.chat.android.network.models.DeviceResponse import io.getstream.chat.android.network.models.FileUploadConfig import io.getstream.chat.android.network.models.GetApplicationResponse +import io.getstream.chat.android.network.models.PollOptionResponseData import io.getstream.chat.android.network.models.ThreadParticipant import io.getstream.chat.android.network.models.UnblockUsersResponse import io.getstream.chat.android.network.models.UnreadCountsChannel @@ -1180,6 +1181,16 @@ internal object Mother { extraData = extraData, ) + fun randomPollOptionResponseData( + id: String = randomString(), + text: String = randomString(), + custom: Map = emptyMap(), + ): PollOptionResponseData = PollOptionResponseData( + id = id, + text = text, + custom = custom, + ) + fun randomDownstreamOptionDto( id: String = randomString(), text: String = randomString(), diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt index b499bd717d1..27a946443f1 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt @@ -61,7 +61,6 @@ import io.getstream.chat.android.client.api2.model.response.MessageResponse import io.getstream.chat.android.client.api2.model.response.MessagesResponse import io.getstream.chat.android.client.api2.model.response.MuteUserResponse import io.getstream.chat.android.client.api2.model.response.ParsedPredefinedFilterResponse -import io.getstream.chat.android.client.api2.model.response.PollOptionResponse import io.getstream.chat.android.client.api2.model.response.PollResponse import io.getstream.chat.android.client.api2.model.response.PollVoteResponse import io.getstream.chat.android.client.api2.model.response.QueryBannedUsersResponse @@ -154,6 +153,7 @@ import io.getstream.chat.android.network.models.MessageActionRequest import io.getstream.chat.android.network.models.MuteChannelRequest import io.getstream.chat.android.network.models.PollOptionInput import io.getstream.chat.android.network.models.PollOptionRequest +import io.getstream.chat.android.network.models.PollOptionResponse import io.getstream.chat.android.network.models.PushPreferencesResponse import io.getstream.chat.android.network.models.QueryDraftsRequest import io.getstream.chat.android.network.models.QueryPollVotesRequest diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt index bc45ff08607..c8433ec8256 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt @@ -39,7 +39,6 @@ import io.getstream.chat.android.client.api2.model.response.MessageResponse import io.getstream.chat.android.client.api2.model.response.MessagesResponse import io.getstream.chat.android.client.api2.model.response.MuteUserResponse import io.getstream.chat.android.client.api2.model.response.ParsedPredefinedFilterResponse -import io.getstream.chat.android.client.api2.model.response.PollOptionResponse import io.getstream.chat.android.client.api2.model.response.PollResponse import io.getstream.chat.android.client.api2.model.response.PollVoteResponse import io.getstream.chat.android.client.api2.model.response.QueryBannedUsersResponse @@ -79,6 +78,7 @@ import io.getstream.chat.android.network.models.GetApplicationResponse import io.getstream.chat.android.network.models.GetUserGroupResponse import io.getstream.chat.android.network.models.ListDevicesResponse import io.getstream.chat.android.network.models.ListUserGroupsResponse +import io.getstream.chat.android.network.models.PollOptionResponse import io.getstream.chat.android.network.models.RemoveUserGroupMembersResponse import io.getstream.chat.android.network.models.Response import io.getstream.chat.android.network.models.SearchRolesResponse @@ -901,7 +901,7 @@ internal object MoshiChatApiTestArguments { RetroSuccess( PollOptionResponse( duration = randomString(), - poll_option = Mother.randomDownstreamOptionDto(), + pollOption = Mother.randomPollOptionResponseData(), ), ).toRetrofitCall(), Result.Success::class, diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/PollOptionResponseParsingTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/PollOptionResponseParsingTest.kt new file mode 100644 index 00000000000..2a3af6a25b4 --- /dev/null +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/PollOptionResponseParsingTest.kt @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2014-2026 Stream.io Inc. All rights reserved. + * + * Licensed under the Stream License; + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://github.com/GetStream/stream-chat-android/blob/main/LICENSE + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.getstream.chat.android.client.parser2 + +import io.getstream.chat.android.network.models.PollOptionResponse +import org.amshove.kluent.shouldBeEmpty +import org.amshove.kluent.shouldBeEqualTo +import org.junit.jupiter.api.Test + +internal class PollOptionResponseParsingTest { + private val parser = ParserFactory.createMoshiChatParser() + + @Test + fun `Deserialize a poll option response`() { + val response = parser.fromJson(POLL_OPTION_JSON, PollOptionResponse::class.java) + + response.duration shouldBeEqualTo "12ms" + response.pollOption.id shouldBeEqualTo "optionId" + response.pollOption.text shouldBeEqualTo "Option text" + } + + @Test + fun `Collect the root-level custom fields of a poll option`() { + val response = parser.fromJson(POLL_OPTION_JSON, PollOptionResponse::class.java) + + response.pollOption.custom shouldBeEqualTo mapOf("sentiment" to "positive") + } + + @Test + fun `Deserialize a poll option without custom fields`() { + val response = parser.fromJson(POLL_OPTION_WITHOUT_CUSTOM_JSON, PollOptionResponse::class.java) + + response.pollOption.custom.shouldBeEmpty() + } + + companion object { + private const val POLL_OPTION_JSON = + """{ + "duration": "12ms", + "poll_option": { + "id": "optionId", + "text": "Option text", + "sentiment": "positive" + } + }""" + + private const val POLL_OPTION_WITHOUT_CUSTOM_JSON = + """{ + "duration": "12ms", + "poll_option": { + "id": "optionId", + "text": "Option text" + } + }""" + } +}