From f4a08c94fcdef2a7f08f54e100ed70b18e814338 Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Fri, 28 Aug 2026 16:09:32 +0200 Subject: [PATCH] Swap the message moderation and reaction groups to the generated models --- .../client/api2/mapping/DomainMapping.kt | 32 +++++------ ...actionDtos.kt => DownstreamReactionDto.kt} | 8 --- .../client/api2/model/dto/MessageDtos.kt | 6 +- .../network/models/ModerationV2Response.kt | 56 +++++++++++++++++++ .../network/models/ReactionGroupResponse.kt | 47 ++++++++++++++++ .../models/ReactionGroupUserResponse.kt} | 32 +++++++---- .../getstream/chat/android/client/Mother.kt | 34 +++++------ .../client/api2/mapping/DomainMappingTest.kt | 40 ++++++------- .../client/parser2/ModerationParsingTest.kt | 24 ++++---- .../parser2/ReactionGroupParsingTest.kt | 41 ++++++++++---- .../parser2/testdata/MessageDtoTestData.kt | 10 ++-- 11 files changed, 226 insertions(+), 104 deletions(-) rename stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/{ReactionDtos.kt => DownstreamReactionDto.kt} (89%) create mode 100644 stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ModerationV2Response.kt create mode 100644 stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupResponse.kt rename stream-chat-android-client/src/main/java/io/getstream/chat/android/{client/api2/model/dto/DownstreamModerationDto.kt => network/models/ReactionGroupUserResponse.kt} (50%) 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 db124811efc..3f85d569844 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 @@ -34,14 +34,12 @@ import io.getstream.chat.android.client.api2.model.dto.DownstreamMemberDto import io.getstream.chat.android.client.api2.model.dto.DownstreamMemberInfoDto import io.getstream.chat.android.client.api2.model.dto.DownstreamMessageDto import io.getstream.chat.android.client.api2.model.dto.DownstreamModerationDetailsDto -import io.getstream.chat.android.client.api2.model.dto.DownstreamModerationDto import io.getstream.chat.android.client.api2.model.dto.DownstreamMuteDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPendingMessageDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPollDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPollOptionDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPushPreferenceDto import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionDto -import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionGroupDto import io.getstream.chat.android.client.api2.model.dto.DownstreamReminderDto import io.getstream.chat.android.client.api2.model.dto.DownstreamReminderInfoDto import io.getstream.chat.android.client.api2.model.dto.DownstreamThreadDto @@ -136,6 +134,7 @@ import io.getstream.chat.android.network.models.DeviceResponse import io.getstream.chat.android.network.models.FullUserResponse import io.getstream.chat.android.network.models.GetApplicationResponse import io.getstream.chat.android.network.models.GetOGResponse +import io.getstream.chat.android.network.models.ModerationV2Response import io.getstream.chat.android.network.models.PollOptionResponseData import io.getstream.chat.android.network.models.PollResponseData import io.getstream.chat.android.network.models.PollVoteResponseData @@ -143,6 +142,7 @@ import io.getstream.chat.android.network.models.PollVotesResponse import io.getstream.chat.android.network.models.PrivacySettingsResponse import io.getstream.chat.android.network.models.PushPreferencesResponse import io.getstream.chat.android.network.models.QueryPollsResponse +import io.getstream.chat.android.network.models.ReactionGroupResponse import io.getstream.chat.android.network.models.ReactionResponse import io.getstream.chat.android.network.models.UnreadCountsChannel import io.getstream.chat.android.network.models.UnreadCountsChannelType @@ -530,15 +530,15 @@ internal class DomainMapping( ) /** - * Transforms [DownstreamReactionGroupDto] to [ReactionGroup]. + * Transforms [ReactionGroupResponse] to [ReactionGroup]. */ - internal fun DownstreamReactionGroupDto.toDomain(type: String): ReactionGroup = + internal fun ReactionGroupResponse.toDomain(type: String): ReactionGroup = ReactionGroup( type = type, count = count, - sumScore = sum_scores, - firstReactionAt = first_reaction_at, - lastReactionAt = last_reaction_at, + sumScore = sumScores, + firstReactionAt = firstReactionAt, + lastReactionAt = lastReactionAt, ) /** @@ -1124,16 +1124,16 @@ internal class DomainMapping( ) /** - * Maps the network [DownstreamModerationDto] to the domain model [Moderation]. + * Maps the network [ModerationV2Response] to the domain model [Moderation]. */ - internal fun DownstreamModerationDto.toDomain() = Moderation( - action = ModerationAction.fromValue(this.action), - originalText = this.original_text, - textHarms = this.text_harms.orEmpty(), - imageHarms = this.image_harms.orEmpty(), - blocklistMatched = this.blocklist_matched, - semanticFilterMatched = this.semantic_filter_matched, - platformCircumvented = this.platform_circumvented ?: false, + internal fun ModerationV2Response.toDomain() = Moderation( + action = ModerationAction.fromValue(action), + originalText = originalText, + textHarms = textHarms.orEmpty(), + imageHarms = imageHarms.orEmpty(), + blocklistMatched = blocklistMatched, + semanticFilterMatched = semanticFilterMatched, + platformCircumvented = platformCircumvented ?: false, ) /** diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ReactionDtos.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/DownstreamReactionDto.kt similarity index 89% rename from stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ReactionDtos.kt rename to stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/DownstreamReactionDto.kt index a33d3f7cd50..47b0ff113ce 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ReactionDtos.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/DownstreamReactionDto.kt @@ -43,11 +43,3 @@ internal data class DownstreamReactionDto( val emoji_code: String?, val extraData: Map, ) : ExtraDataDto - -@JsonClass(generateAdapter = true) -internal data class DownstreamReactionGroupDto( - val count: Int, - val sum_scores: Int, - val first_reaction_at: Date, - val last_reaction_at: Date, -) diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt index a367c70bc80..ff48cd2e122 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/MessageDtos.kt @@ -19,7 +19,9 @@ package io.getstream.chat.android.client.api2.model.dto import com.squareup.moshi.JsonClass import io.getstream.chat.android.core.internal.StreamHandsOff import io.getstream.chat.android.network.models.Attachment +import io.getstream.chat.android.network.models.ModerationV2Response import io.getstream.chat.android.network.models.PollResponseData +import io.getstream.chat.android.network.models.ReactionGroupResponse import java.util.Date /** @@ -58,7 +60,7 @@ internal data class DownstreamMessageDto( val quoted_message_id: String?, val reaction_counts: Map?, val reaction_scores: Map?, - val reaction_groups: Map?, + val reaction_groups: Map?, val reply_count: Int, val deleted_reply_count: Int, val shadowed: Boolean = false, @@ -70,7 +72,7 @@ internal data class DownstreamMessageDto( val updated_at: Date, val user: DownstreamUserDto, val moderation_details: DownstreamModerationDetailsDto? = null, // Used for Moderation V1 - val moderation: DownstreamModerationDto? = null, // Used for Moderation V2 + val moderation: ModerationV2Response? = null, // Used for Moderation V2 val poll: PollResponseData? = null, val reminder: DownstreamReminderInfoDto? = null, val shared_location: DownstreamLocationDto? = null, diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ModerationV2Response.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ModerationV2Response.kt new file mode 100644 index 00000000000..21327a6dda6 --- /dev/null +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ModerationV2Response.kt @@ -0,0 +1,56 @@ +/* + * 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 ModerationV2Response( + @Json(name = "action") + internal val action: String, + + @Json(name = "original_text") + internal val originalText: String, + + @Json(name = "blocklist_matched") + internal val blocklistMatched: String? = null, + + @Json(name = "platform_circumvented") + internal val platformCircumvented: Boolean? = null, + + @Json(name = "semantic_filter_matched") + internal val semanticFilterMatched: String? = null, + + @Json(name = "blocklists_matched") + internal val blocklistsMatched: List? = emptyList(), + + @Json(name = "image_harms") + internal val imageHarms: List? = emptyList(), + + @Json(name = "text_harms") + internal val textHarms: List? = emptyList(), +) diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupResponse.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupResponse.kt new file mode 100644 index 00000000000..83ae3e7e349 --- /dev/null +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupResponse.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. + */ + +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport", +) + +package io.getstream.chat.android.network.models + +import com.squareup.moshi.Json + +/** + * ReactionGroupResponse contains all information about a reaction of the same type. + */ +@com.squareup.moshi.JsonClass(generateAdapter = true) +internal data class ReactionGroupResponse( + @Json(name = "count") + internal val count: Int, + + @Json(name = "first_reaction_at") + internal val firstReactionAt: java.util.Date, + + @Json(name = "last_reaction_at") + internal val lastReactionAt: java.util.Date, + + @Json(name = "sum_scores") + internal val sumScores: Int, + + @Json(name = "latest_reactions_by") + internal val latestReactionsBy: List = emptyList(), +) diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/DownstreamModerationDto.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupUserResponse.kt similarity index 50% rename from stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/DownstreamModerationDto.kt rename to stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupUserResponse.kt index 15da19bb0a4..37ec1f7f39e 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/DownstreamModerationDto.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/network/models/ReactionGroupUserResponse.kt @@ -14,20 +14,28 @@ * limitations under the License. */ -package io.getstream.chat.android.client.api2.model.dto +@file:Suppress( + "ArrayInDataClass", + "EnumEntryName", + "RemoveRedundantQualifierName", + "UnusedImport", +) + +package io.getstream.chat.android.network.models -import com.squareup.moshi.JsonClass +import com.squareup.moshi.Json /** - * Downstream JSON holding the data related to Moderation V2. + * Contains information about a user who reacted with this reaction type. */ -@JsonClass(generateAdapter = true) -internal data class DownstreamModerationDto( - val action: String, - val original_text: String, - val text_harms: List?, - val image_harms: List?, - val blocklist_matched: String?, - val semantic_filter_matched: String?, - val platform_circumvented: Boolean?, +@com.squareup.moshi.JsonClass(generateAdapter = true) +internal data class ReactionGroupUserResponse( + @Json(name = "created_at") + internal val createdAt: java.util.Date, + + @Json(name = "user_id") + internal val userId: String, + + @Json(name = "user") + internal val user: io.getstream.chat.android.network.models.UserResponse? = null, ) 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 df123c37ca8..b6ea364d977 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 @@ -36,14 +36,12 @@ import io.getstream.chat.android.client.api2.model.dto.DownstreamMemberDto import io.getstream.chat.android.client.api2.model.dto.DownstreamMemberInfoDto import io.getstream.chat.android.client.api2.model.dto.DownstreamMessageDto import io.getstream.chat.android.client.api2.model.dto.DownstreamModerationDetailsDto -import io.getstream.chat.android.client.api2.model.dto.DownstreamModerationDto import io.getstream.chat.android.client.api2.model.dto.DownstreamMuteDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPendingMessageDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPollDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPollOptionDto import io.getstream.chat.android.client.api2.model.dto.DownstreamPushPreferenceDto import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionDto -import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionGroupDto import io.getstream.chat.android.client.api2.model.dto.DownstreamReminderDto import io.getstream.chat.android.client.api2.model.dto.DownstreamThreadDto import io.getstream.chat.android.client.api2.model.dto.DownstreamThreadInfoDto @@ -100,11 +98,13 @@ import io.getstream.chat.android.network.models.FileUploadResponse import io.getstream.chat.android.network.models.FullUserResponse import io.getstream.chat.android.network.models.GetApplicationResponse import io.getstream.chat.android.network.models.GetOGResponse +import io.getstream.chat.android.network.models.ModerationV2Response import io.getstream.chat.android.network.models.PollOptionResponseData import io.getstream.chat.android.network.models.PollResponseData import io.getstream.chat.android.network.models.PollVoteResponseData import io.getstream.chat.android.network.models.PollVotesResponse import io.getstream.chat.android.network.models.QueryPollsResponse +import io.getstream.chat.android.network.models.ReactionGroupResponse import io.getstream.chat.android.network.models.ReactionResponse import io.getstream.chat.android.network.models.ThreadParticipant import io.getstream.chat.android.network.models.UnblockUsersResponse @@ -320,7 +320,7 @@ internal object Mother { quoted_message_id: String? = randomString(), reaction_counts: Map? = emptyMap(), reaction_scores: Map? = emptyMap(), - reaction_groups: Map? = emptyMap(), + reaction_groups: Map? = emptyMap(), reply_count: Int = randomInt(), deleted_reply_count: Int = randomInt(), shadowed: Boolean = randomBoolean(), @@ -332,7 +332,7 @@ internal object Mother { updated_at: Date = randomDate(), user: DownstreamUserDto = randomDownstreamUserDto(), moderation_details: DownstreamModerationDetailsDto? = null, - moderation: DownstreamModerationDto? = null, + moderation: ModerationV2Response? = null, poll: PollResponseData? = null, member: DownstreamMemberInfoDto? = randomDownstreamMemberInfoDto(), deleted_for_me: Boolean? = null, @@ -698,16 +698,16 @@ internal object Mother { expires = expires, ) - fun randomDownstreamReactionGroupDto( + fun randomReactionGroupResponse( count: Int = randomInt(), sumScores: Int = randomInt(), firstReactionAt: Date = randomDate(), lastReactionAt: Date = randomDate(), - ): DownstreamReactionGroupDto = DownstreamReactionGroupDto( + ): ReactionGroupResponse = ReactionGroupResponse( count = count, - sum_scores = sumScores, - first_reaction_at = firstReactionAt, - last_reaction_at = lastReactionAt, + sumScores = sumScores, + firstReactionAt = firstReactionAt, + lastReactionAt = lastReactionAt, ) fun randomChannelMemberResponse( @@ -942,7 +942,7 @@ internal object Mother { extraData = extraData, ) - fun randomDownstreamModerationDto( + fun randomModerationV2Response( action: String = randomString(), originalText: String = randomString(), textHarms: List = listOf(randomString()), @@ -950,14 +950,14 @@ internal object Mother { blocklistMatched: String = randomString(), semanticFilterMatched: String = randomString(), platformCircumvented: Boolean = randomBoolean(), - ): DownstreamModerationDto = DownstreamModerationDto( + ): ModerationV2Response = ModerationV2Response( action = action, - original_text = originalText, - text_harms = textHarms, - image_harms = imageHarms, - blocklist_matched = blocklistMatched, - semantic_filter_matched = semanticFilterMatched, - platform_circumvented = platformCircumvented, + originalText = originalText, + textHarms = textHarms, + imageHarms = imageHarms, + blocklistMatched = blocklistMatched, + semanticFilterMatched = semanticFilterMatched, + platformCircumvented = platformCircumvented, ) fun randomPrivacySettingsDto( diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt index 91517fab57f..07e264cc18a 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt @@ -41,13 +41,11 @@ import io.getstream.chat.android.client.Mother.randomDownstreamFlagDto import io.getstream.chat.android.client.Mother.randomDownstreamMemberDto import io.getstream.chat.android.client.Mother.randomDownstreamMessageDto import io.getstream.chat.android.client.Mother.randomDownstreamModerationDetailsDto -import io.getstream.chat.android.client.Mother.randomDownstreamModerationDto import io.getstream.chat.android.client.Mother.randomDownstreamMuteDto import io.getstream.chat.android.client.Mother.randomDownstreamOptionDto import io.getstream.chat.android.client.Mother.randomDownstreamPendingMessageDto import io.getstream.chat.android.client.Mother.randomDownstreamPollDto import io.getstream.chat.android.client.Mother.randomDownstreamReactionDto -import io.getstream.chat.android.client.Mother.randomDownstreamReactionGroupDto import io.getstream.chat.android.client.Mother.randomDownstreamReminderDto import io.getstream.chat.android.client.Mother.randomDownstreamThreadDto import io.getstream.chat.android.client.Mother.randomDownstreamThreadInfoDto @@ -56,11 +54,13 @@ import io.getstream.chat.android.client.Mother.randomDownstreamUserGroupDto import io.getstream.chat.android.client.Mother.randomDownstreamVoteDto import io.getstream.chat.android.client.Mother.randomFileUploadConfig import io.getstream.chat.android.client.Mother.randomFullUserResponse +import io.getstream.chat.android.client.Mother.randomModerationV2Response import io.getstream.chat.android.client.Mother.randomPollResponseData import io.getstream.chat.android.client.Mother.randomPollVotesResponse import io.getstream.chat.android.client.Mother.randomPrivacySettingsDto import io.getstream.chat.android.client.Mother.randomQueryPollsResponse import io.getstream.chat.android.client.Mother.randomQueryRemindersResponse +import io.getstream.chat.android.client.Mother.randomReactionGroupResponse import io.getstream.chat.android.client.Mother.randomReactionResponse import io.getstream.chat.android.client.Mother.randomRoleDto import io.getstream.chat.android.client.Mother.randomSearchWarningDto @@ -217,7 +217,7 @@ internal class DomainMappingTest { pinned_by = randomDownstreamUserDto(), quoted_message = randomDownstreamMessageDto(), moderation_details = randomDownstreamModerationDetailsDto(), - moderation = randomDownstreamModerationDto(), + moderation = randomModerationV2Response(), poll = randomPollResponseData(), deleted_for_me = randomBoolean(), ).toDomain() @@ -656,19 +656,19 @@ internal class DomainMappingTest { } @Test - fun `DownstreamReactionGroupDto is correctly mapped to ReactionGroup`() { - val downstreamReactionGroupDto = randomDownstreamReactionGroupDto() + fun `ReactionGroupResponse is correctly mapped to ReactionGroup`() { + val reactionGroupResponse = randomReactionGroupResponse() val sut = Fixture().get() val type = randomString() val reactionGroup = with(sut) { - downstreamReactionGroupDto.toDomain(type) + reactionGroupResponse.toDomain(type) } val expected = ReactionGroup( type = type, - count = downstreamReactionGroupDto.count, - sumScore = downstreamReactionGroupDto.sum_scores, - firstReactionAt = downstreamReactionGroupDto.first_reaction_at, - lastReactionAt = downstreamReactionGroupDto.last_reaction_at, + count = reactionGroupResponse.count, + sumScore = reactionGroupResponse.sumScores, + firstReactionAt = reactionGroupResponse.firstReactionAt, + lastReactionAt = reactionGroupResponse.lastReactionAt, ) assertEquals(expected, reactionGroup) } @@ -1310,18 +1310,18 @@ internal class DomainMappingTest { } @Test - fun `DownstreamModerationDto is correctly mapped to Moderation`() { - val downstreamModerationDto = randomDownstreamModerationDto() + fun `ModerationV2Response is correctly mapped to Moderation`() { + val moderationResponse = randomModerationV2Response() val sut = Fixture().get() - val moderation = with(sut) { downstreamModerationDto.toDomain() } + val moderation = with(sut) { moderationResponse.toDomain() } val expected = Moderation( - action = ModerationAction(downstreamModerationDto.action), - originalText = downstreamModerationDto.original_text, - textHarms = downstreamModerationDto.text_harms ?: emptyList(), - imageHarms = downstreamModerationDto.image_harms ?: emptyList(), - blocklistMatched = downstreamModerationDto.blocklist_matched, - semanticFilterMatched = downstreamModerationDto.semantic_filter_matched, - platformCircumvented = downstreamModerationDto.platform_circumvented ?: false, + action = ModerationAction(moderationResponse.action), + originalText = moderationResponse.originalText, + textHarms = moderationResponse.textHarms ?: emptyList(), + imageHarms = moderationResponse.imageHarms ?: emptyList(), + blocklistMatched = moderationResponse.blocklistMatched, + semanticFilterMatched = moderationResponse.semanticFilterMatched, + platformCircumvented = moderationResponse.platformCircumvented ?: false, ) assertEquals(expected, moderation) } diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ModerationParsingTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ModerationParsingTest.kt index 0e07b8db178..cbefb7e3231 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ModerationParsingTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ModerationParsingTest.kt @@ -18,12 +18,12 @@ package io.getstream.chat.android.client.parser2 import com.squareup.moshi.JsonDataException import io.getstream.chat.android.client.api2.mapping.DomainMapping -import io.getstream.chat.android.client.api2.model.dto.DownstreamModerationDto import io.getstream.chat.android.client.parser2.direct.ModerationAdapter import io.getstream.chat.android.client.parser2.testdata.ModerationTestData import io.getstream.chat.android.models.NoOpChannelTransformer import io.getstream.chat.android.models.NoOpMessageTransformer import io.getstream.chat.android.models.NoOpUserTransformer +import io.getstream.chat.android.network.models.ModerationV2Response import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows @@ -41,18 +41,18 @@ internal class ModerationParsingTest { private val adapter = ModerationAdapter() - // region DTO path (JSON → DownstreamModerationDto → Moderation) + // region Generated path (JSON → ModerationV2Response → Moderation) @Test - fun `DTO path - deserializes all fields`() { - val dto = parser.fromJson(ModerationTestData.jsonAllFields, DownstreamModerationDto::class.java) + fun `Generated path - deserializes all fields`() { + val dto = parser.fromJson(ModerationTestData.jsonAllFields, ModerationV2Response::class.java) val domain = with(domainMapping) { dto.toDomain() } assertEquals(ModerationTestData.expectedAllFields, domain) } @Test - fun `DTO path - deserializes with optional fields missing`() { - val dto = parser.fromJson(ModerationTestData.jsonOptionalFieldsMissing, DownstreamModerationDto::class.java) + fun `Generated path - deserializes with optional fields missing`() { + val dto = parser.fromJson(ModerationTestData.jsonOptionalFieldsMissing, ModerationV2Response::class.java) val domain = with(domainMapping) { dto.toDomain() } assertEquals(ModerationTestData.expectedOptionalFieldsMissing, domain) } @@ -78,8 +78,8 @@ internal class ModerationParsingTest { // region Explicit null values @Test - fun `DTO path - deserializes with explicit null values`() { - val dto = parser.fromJson(ModerationTestData.jsonWithExplicitNulls, DownstreamModerationDto::class.java) + fun `Generated path - deserializes with explicit null values`() { + val dto = parser.fromJson(ModerationTestData.jsonWithExplicitNulls, ModerationV2Response::class.java) val domain = with(domainMapping) { dto.toDomain() } assertEquals(ModerationTestData.expectedWithExplicitNulls, domain) } @@ -95,9 +95,9 @@ internal class ModerationParsingTest { // region Error message parity @Test - fun `DTO path - throws on missing action`() { + fun `Generated path - throws on missing action`() { assertThrows { - parser.fromJson(ModerationTestData.jsonMissingAction, DownstreamModerationDto::class.java) + parser.fromJson(ModerationTestData.jsonMissingAction, ModerationV2Response::class.java) } } @@ -109,9 +109,9 @@ internal class ModerationParsingTest { } @Test - fun `DTO path - throws on missing original_text`() { + fun `Generated path - throws on missing original_text`() { assertThrows { - parser.fromJson(ModerationTestData.jsonMissingOriginalText, DownstreamModerationDto::class.java) + parser.fromJson(ModerationTestData.jsonMissingOriginalText, ModerationV2Response::class.java) } } diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ReactionGroupParsingTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ReactionGroupParsingTest.kt index 91612ed7ed1..bc35c04b3f5 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ReactionGroupParsingTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/ReactionGroupParsingTest.kt @@ -19,14 +19,15 @@ package io.getstream.chat.android.client.parser2 import com.squareup.moshi.JsonDataException import com.squareup.moshi.Moshi import io.getstream.chat.android.client.api2.mapping.DomainMapping -import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionGroupDto import io.getstream.chat.android.client.parser2.direct.ReactionGroupAdapter import io.getstream.chat.android.client.parser2.testdata.ReactionGroupTestData import io.getstream.chat.android.models.NoOpChannelTransformer import io.getstream.chat.android.models.NoOpMessageTransformer import io.getstream.chat.android.models.NoOpUserTransformer import io.getstream.chat.android.network.infrastructure.IsoDateAdapter +import io.getstream.chat.android.network.models.ReactionGroupResponse import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertTrue import org.junit.jupiter.api.Test import org.junit.jupiter.api.assertThrows import java.util.Date @@ -48,11 +49,11 @@ internal class ReactionGroupParsingTest { private val testType = "like" - // region DTO path (JSON → DownstreamReactionGroupDto → ReactionGroup) + // region Generated path (JSON → ReactionGroupResponse → ReactionGroup) @Test - fun `DTO path - deserializes all fields`() { - val dto = parser.fromJson(ReactionGroupTestData.jsonAllFields, DownstreamReactionGroupDto::class.java) + fun `Generated path - deserializes all fields`() { + val dto = parser.fromJson(ReactionGroupTestData.jsonAllFields, ReactionGroupResponse::class.java) val reactionGroup = with(domainMapping) { dto.toDomain(testType) } assertEquals(ReactionGroupTestData.expectedReactionGroupAllFields, reactionGroup) } @@ -90,8 +91,9 @@ internal class ReactionGroupParsingTest { @Test fun `Both paths - same error on missing count`() { + val field = "count" val dtoException = assertThrows { - parser.fromJson(ReactionGroupTestData.jsonMissingCount, DownstreamReactionGroupDto::class.java) + parser.fromJson(ReactionGroupTestData.jsonMissingCount, ReactionGroupResponse::class.java) } val directException = assertThrows { reactionGroupAdapter.parseWithType( @@ -101,13 +103,17 @@ internal class ReactionGroupParsingTest { testType, ) } - assertEquals(dtoException.message, directException.message) + // Both paths must fail on the same wire field. The wording differs by design: the generated model + // names the Kotlin property and its @Json name, the direct adapter names the wire key. + assertTrue(dtoException.message.orEmpty().contains(field)) + assertTrue(directException.message.orEmpty().contains(field)) } @Test fun `Both paths - same error on missing sum_scores`() { + val field = "sum_scores" val dtoException = assertThrows { - parser.fromJson(ReactionGroupTestData.jsonMissingSumScores, DownstreamReactionGroupDto::class.java) + parser.fromJson(ReactionGroupTestData.jsonMissingSumScores, ReactionGroupResponse::class.java) } val directException = assertThrows { reactionGroupAdapter.parseWithType( @@ -117,13 +123,17 @@ internal class ReactionGroupParsingTest { testType, ) } - assertEquals(dtoException.message, directException.message) + // Both paths must fail on the same wire field. The wording differs by design: the generated model + // names the Kotlin property and its @Json name, the direct adapter names the wire key. + assertTrue(dtoException.message.orEmpty().contains(field)) + assertTrue(directException.message.orEmpty().contains(field)) } @Test fun `Both paths - same error on missing first_reaction_at`() { + val field = "first_reaction_at" val dtoException = assertThrows { - parser.fromJson(ReactionGroupTestData.jsonMissingFirstReactionAt, DownstreamReactionGroupDto::class.java) + parser.fromJson(ReactionGroupTestData.jsonMissingFirstReactionAt, ReactionGroupResponse::class.java) } val directException = assertThrows { reactionGroupAdapter.parseWithType( @@ -133,13 +143,17 @@ internal class ReactionGroupParsingTest { testType, ) } - assertEquals(dtoException.message, directException.message) + // Both paths must fail on the same wire field. The wording differs by design: the generated model + // names the Kotlin property and its @Json name, the direct adapter names the wire key. + assertTrue(dtoException.message.orEmpty().contains(field)) + assertTrue(directException.message.orEmpty().contains(field)) } @Test fun `Both paths - same error on missing last_reaction_at`() { + val field = "last_reaction_at" val dtoException = assertThrows { - parser.fromJson(ReactionGroupTestData.jsonMissingLastReactionAt, DownstreamReactionGroupDto::class.java) + parser.fromJson(ReactionGroupTestData.jsonMissingLastReactionAt, ReactionGroupResponse::class.java) } val directException = assertThrows { reactionGroupAdapter.parseWithType( @@ -149,7 +163,10 @@ internal class ReactionGroupParsingTest { testType, ) } - assertEquals(dtoException.message, directException.message) + // Both paths must fail on the same wire field. The wording differs by design: the generated model + // names the Kotlin property and its @Json name, the direct adapter names the wire key. + assertTrue(dtoException.message.orEmpty().contains(field)) + assertTrue(directException.message.orEmpty().contains(field)) } // endregion diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt index 3e8ab311505..ed631c48fd7 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/MessageDtoTestData.kt @@ -18,9 +18,9 @@ package io.getstream.chat.android.client.parser2.testdata import io.getstream.chat.android.client.api2.model.dto.DownstreamMessageDto import io.getstream.chat.android.client.api2.model.dto.DownstreamModerationDetailsDto -import io.getstream.chat.android.client.api2.model.dto.DownstreamReactionGroupDto import io.getstream.chat.android.client.api2.model.dto.DownstreamUserGroupDto import io.getstream.chat.android.network.models.MessageRequest +import io.getstream.chat.android.network.models.ReactionGroupResponse import org.intellij.lang.annotations.Language import java.util.Date @@ -151,11 +151,11 @@ internal object MessageDtoTestData { reaction_scores = mapOf("like" to 10), reaction_groups = mapOf( "like" to - DownstreamReactionGroupDto( + ReactionGroupResponse( count = 2, - sum_scores = 10, - first_reaction_at = Date(1591787071588), - last_reaction_at = Date(1591787071588), + sumScores = 10, + firstReactionAt = Date(1591787071588), + lastReactionAt = Date(1591787071588), ), ), latest_reactions = listOf(ReactionDtoTestData.downstreamReactionWithoutExtraData),