Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import io.getstream.chat.android.client.api.QueryParams
import io.getstream.chat.android.client.api2.UrlQueryPayload
import io.getstream.chat.android.client.api2.model.requests.QueryMembersRequest
import io.getstream.chat.android.client.api2.model.requests.SyncHistoryRequest
import io.getstream.chat.android.client.api2.model.response.QueryMembersResponse
import io.getstream.chat.android.client.api2.model.response.SearchMessagesResponse
import io.getstream.chat.android.client.api2.model.response.SyncHistoryResponse
import io.getstream.chat.android.client.call.RetrofitCall
import io.getstream.chat.android.network.models.MembersResponse
import io.getstream.chat.android.network.models.SearchPayload
import io.getstream.chat.android.network.models.WrappedUnreadCountsResponse
import okhttp3.ResponseBody
Expand Down Expand Up @@ -53,7 +53,7 @@ internal interface GeneralApi {
@GET("/members")
fun queryMembers(
@UrlQueryPayload @Query("payload") payload: QueryMembersRequest,
): RetrofitCall<QueryMembersResponse>
): RetrofitCall<MembersResponse>

@GET("/unread")
fun getUnreadCounts(): RetrofitCall<WrappedUnreadCountsResponse>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +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.Json

import com.squareup.moshi.JsonClass
import io.getstream.chat.android.client.api2.model.dto.DownstreamMemberDto
/**
*
*/
@com.squareup.moshi.JsonClass(generateAdapter = true)
internal data class MembersResponse(
@Json(name = "duration")
internal val duration: String,

@JsonClass(generateAdapter = true)
internal data class QueryMembersResponse(
val members: List<DownstreamMemberDto>,
@Json(name = "members")
internal val members: List<io.getstream.chat.android.network.models.ChannelMemberResponse> = emptyList(),
)
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ import io.getstream.chat.android.models.querysort.QuerySorter
import io.getstream.chat.android.network.models.AppResponseFields
import io.getstream.chat.android.network.models.BlockUsersResponse
import io.getstream.chat.android.network.models.BlockedUserResponse
import io.getstream.chat.android.network.models.ChannelMemberResponse
import io.getstream.chat.android.network.models.ChannelResponse
import io.getstream.chat.android.network.models.CreateGuestResponse
import io.getstream.chat.android.network.models.DeviceResponse
Expand Down Expand Up @@ -728,6 +729,34 @@ internal object Mother {
last_reaction_at = lastReactionAt,
)

fun randomUserResponse(
id: String = randomString(),
role: String = randomString(),
language: String = randomString(),
): UserResponse = UserResponse(
id = id,
role = role,
language = language,
banned = randomBoolean(),
online = randomBoolean(),
createdAt = randomDate(),
updatedAt = randomDate(),
)

fun randomChannelMemberResponse(
user: UserResponse = randomUserResponse(),
channelRole: String = randomString(),
): ChannelMemberResponse = ChannelMemberResponse(
user = user,
userId = user.id,
channelRole = channelRole,
createdAt = randomDate(),
updatedAt = randomDate(),
banned = randomBoolean(),
shadowBanned = randomBoolean(),
notificationsMuted = randomBoolean(),
)

fun randomDownstreamMemberDto(
user: DownstreamUserDto = randomDownstreamUserDto(),
createdAt: Date = randomDate(),
Expand Down Expand Up @@ -1065,20 +1094,6 @@ internal object Mother {
lastThreadMessageAt = lastThreadMessageAt,
)

fun randomUserResponse(
id: String = randomString(),
role: String = randomString(),
language: String = randomString(),
): UserResponse = UserResponse(
id = id,
role = role,
language = language,
banned = randomBoolean(),
online = randomBoolean(),
createdAt = randomDate(),
updatedAt = randomDate(),
)

fun randomChannelResponse(
id: String = randomString(),
type: String = randomString(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ import io.getstream.chat.android.client.api2.model.response.QueryChannelsRespons
import io.getstream.chat.android.client.api2.model.response.QueryDraftMessagesResponse
import io.getstream.chat.android.client.api2.model.response.QueryGroupedChannelsGroup
import io.getstream.chat.android.client.api2.model.response.QueryGroupedChannelsResponse
import io.getstream.chat.android.client.api2.model.response.QueryMembersResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollVotesResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollsResponse
import io.getstream.chat.android.client.api2.model.response.QueryReactionsResponse
Expand Down Expand Up @@ -147,6 +146,7 @@ import io.getstream.chat.android.network.models.ListUserGroupsResponse
import io.getstream.chat.android.network.models.MarkDeliveredRequest
import io.getstream.chat.android.network.models.MarkReadRequest
import io.getstream.chat.android.network.models.MarkUnreadRequest
import io.getstream.chat.android.network.models.MembersResponse
import io.getstream.chat.android.network.models.MessageActionRequest
import io.getstream.chat.android.network.models.MessageRequest
import io.getstream.chat.android.network.models.MuteChannelRequest
Expand Down Expand Up @@ -2378,7 +2378,7 @@ internal class MoshiChatApiTest {

@ParameterizedTest
@MethodSource("io.getstream.chat.android.client.api2.MoshiChatApiTestArguments#queryMembersInput")
fun testQueryMembers(call: RetrofitCall<QueryMembersResponse>, expected: KClass<*>) = runTest {
fun testQueryMembers(call: RetrofitCall<MembersResponse>, expected: KClass<*>) = runTest {
// given
val api = mock<GeneralApi>()
whenever(api.queryMembers(any())).doReturn(call)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import io.getstream.chat.android.client.api2.model.response.QueryChannelsRespons
import io.getstream.chat.android.client.api2.model.response.QueryDraftMessagesResponse
import io.getstream.chat.android.client.api2.model.response.QueryGroupedChannelsGroup
import io.getstream.chat.android.client.api2.model.response.QueryGroupedChannelsResponse
import io.getstream.chat.android.client.api2.model.response.QueryMembersResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollVotesResponse
import io.getstream.chat.android.client.api2.model.response.QueryPollsResponse
import io.getstream.chat.android.client.api2.model.response.QueryReactionsResponse
Expand Down Expand Up @@ -77,6 +76,7 @@ import io.getstream.chat.android.network.models.GetBlockedUsersResponse
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.MembersResponse
import io.getstream.chat.android.network.models.PollOptionResponse
import io.getstream.chat.android.network.models.RemoveUserGroupMembersResponse
import io.getstream.chat.android.network.models.Response
Expand Down Expand Up @@ -532,10 +532,12 @@ internal object MoshiChatApiTestArguments {
@JvmStatic
fun queryMembersInput() = listOf(
Arguments.of(
RetroSuccess(QueryMembersResponse(listOf(Mother.randomDownstreamMemberDto()))).toRetrofitCall(),
RetroSuccess(
MembersResponse(duration = randomString(), members = listOf(Mother.randomChannelMemberResponse())),
).toRetrofitCall(),
Result.Success::class,
),
Arguments.of(RetroError<QueryMembersResponse>(statusCode = 500).toRetrofitCall(), Result.Failure::class),
Arguments.of(RetroError<MembersResponse>(statusCode = 500).toRetrofitCall(), Result.Failure::class),
)

@JvmStatic
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* 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.MembersResponse
import org.amshove.kluent.shouldBeEqualTo
import org.amshove.kluent.shouldHaveSize
import org.junit.jupiter.api.Test

internal class ChannelMemberResponseParsingTest {
private val parser = ParserFactory.createMoshiChatParser()

@Test
fun `Collect the root-level custom fields of a member`() {
val members = parser.fromJson(MEMBERS_JSON, MembersResponse::class.java).members

members shouldHaveSize 1
// Every key the adapter keeps is declared on the model, so each one appears here only because apps
// already read it out of `extraData`. Asserted as an exact map so a key silently dropping out fails.
members.first().custom shouldBeEqualTo mapOf(
"user_id" to "leandro",
"role" to "member",
"is_moderator" to true,
"deleted_at" to "2026-08-14T12:00:00.000Z",
"deleted_messages" to listOf("m1"),
"memberProbe" to "sentinel",
)
}

@Test
fun `Standard member fields do not leak into custom`() {
val member = parser.fromJson(MEMBERS_JSON, MembersResponse::class.java).members.first()

member.channelRole shouldBeEqualTo "channel_member"
member.userId shouldBeEqualTo "leandro"
member.user?.custom shouldBeEqualTo mapOf("birthland" to "Polis Massa")
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}

companion object {
private const val MEMBERS_JSON =
"""{
"duration": "7ms",
"members": [
{
"user_id": "leandro",
"channel_role": "channel_member",
"created_at": "2021-10-22T00:07:24.000Z",
"updated_at": "2026-05-28T07:40:11.000Z",
"banned": false,
"shadow_banned": false,
"notifications_muted": false,
"role": "member",
"is_moderator": true,
"deleted_at": "2026-08-14T12:00:00.000Z",
"deleted_messages": ["m1"],
"memberProbe": "sentinel",
"user": {
"id": "leandro",
"role": "user",
"language": "pt",
"banned": false,
"online": true,
"created_at": "2021-07-20T14:17:07.000Z",
"updated_at": "2026-07-31T11:38:42.000Z",
"birthland": "Polis Massa"
}
}
]
}"""
}
}
Loading