From 1ed7da3ebc6587e589c5acea8a156035e598cd7d Mon Sep 17 00:00:00 2001 From: Gian <47775302+gpunto@users.noreply.github.com> Date: Tue, 8 Sep 2026 14:16:40 +0200 Subject: [PATCH] Deduplicate queryGroupedChannels calls in DistinctChatApi --- .../client/api/internal/DistinctChatApi.kt | 19 +++++++++ .../api/internal/DistinctChatApiEnabler.kt | 11 +++++ .../hash/QueryGroupedChannelsHash.kt | 26 ++++++++++++ .../internal/DistinctChatApiEnablerTest.kt | 6 +++ .../api/internal/DistinctChatApiTest.kt | 40 +++++++++++++++++++ 5 files changed, 102 insertions(+) create mode 100644 stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/optimisation/hash/QueryGroupedChannelsHash.kt diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApi.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApi.kt index 88c77f56b97..bb6f94236ec 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApi.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApi.kt @@ -27,11 +27,14 @@ import io.getstream.chat.android.client.api2.optimisation.hash.GetPinnedMessages import io.getstream.chat.android.client.api2.optimisation.hash.GetReactionsHash import io.getstream.chat.android.client.api2.optimisation.hash.GetRepliesHash import io.getstream.chat.android.client.api2.optimisation.hash.QueryBanedUsersHash +import io.getstream.chat.android.client.api2.optimisation.hash.QueryGroupedChannelsHash import io.getstream.chat.android.client.api2.optimisation.hash.QueryMembersHash import io.getstream.chat.android.models.BannedUser import io.getstream.chat.android.models.BannedUsersSort import io.getstream.chat.android.models.Channel import io.getstream.chat.android.models.FilterObject +import io.getstream.chat.android.models.GroupedChannels +import io.getstream.chat.android.models.GroupedChannelsGroupQuery import io.getstream.chat.android.models.Member import io.getstream.chat.android.models.Message import io.getstream.chat.android.models.PendingMessage @@ -142,6 +145,22 @@ internal class DistinctChatApi( } } + override fun queryGroupedChannels( + limit: Int?, + groups: Map?, + watch: Boolean, + presence: Boolean, + ): Call { + val uniqueKey = QueryGroupedChannelsHash(limit, groups, watch, presence).hashCode() + StreamLog.d(TAG) { + "[queryGroupedChannels] limit: $limit, groups: $groups, watch: $watch, " + + "presence: $presence, uniqueKey: $uniqueKey" + } + return getOrCreate(uniqueKey) { + delegate.queryGroupedChannels(limit, groups, watch, presence) + } + } + override fun queryBannedUsers( filter: FilterObject, sort: QuerySorter, diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnabler.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnabler.kt index 64ec3af31e2..56a7a4eb0df 100644 --- a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnabler.kt +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnabler.kt @@ -25,6 +25,8 @@ import io.getstream.chat.android.models.BannedUser import io.getstream.chat.android.models.BannedUsersSort import io.getstream.chat.android.models.Channel import io.getstream.chat.android.models.FilterObject +import io.getstream.chat.android.models.GroupedChannels +import io.getstream.chat.android.models.GroupedChannelsGroupQuery import io.getstream.chat.android.models.Member import io.getstream.chat.android.models.Message import io.getstream.chat.android.models.PendingMessage @@ -82,6 +84,15 @@ internal class DistinctChatApiEnabler( return getApi().queryChannels(query) } + override fun queryGroupedChannels( + limit: Int?, + groups: Map?, + watch: Boolean, + presence: Boolean, + ): Call { + return getApi().queryGroupedChannels(limit, groups, watch, presence) + } + override fun queryBannedUsers( filter: FilterObject, sort: QuerySorter, diff --git a/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/optimisation/hash/QueryGroupedChannelsHash.kt b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/optimisation/hash/QueryGroupedChannelsHash.kt new file mode 100644 index 00000000000..06d993b4383 --- /dev/null +++ b/stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/optimisation/hash/QueryGroupedChannelsHash.kt @@ -0,0 +1,26 @@ +/* + * 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.api2.optimisation.hash + +import io.getstream.chat.android.models.GroupedChannelsGroupQuery + +internal data class QueryGroupedChannelsHash( + val limit: Int?, + val groups: Map?, + val watch: Boolean, + val presence: Boolean, +) diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnablerTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnablerTest.kt index 747d049cf1b..07b445538c2 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnablerTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiEnablerTest.kt @@ -32,6 +32,7 @@ import io.getstream.chat.android.randomString import kotlinx.coroutines.test.TestScope import org.junit.Test import org.mockito.kotlin.any +import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.mock import org.mockito.kotlin.spy import org.mockito.kotlin.times @@ -98,6 +99,7 @@ internal class DistinctChatApiEnablerTest { members = members, ) enabler.queryChannel(channelType, channelId, channelRequest) + enabler.queryGroupedChannels(limit, null, watch = true, presence = false) // then verify(distinctApi, times(1)).getRepliesMore(messageId, firstId, limit) verify(distinctApi, times(1)).getReplies(messageId, limit) @@ -133,6 +135,7 @@ internal class DistinctChatApiEnablerTest { members = members, ) verify(distinctApi, times(1)).queryChannel(channelType, channelId, channelRequest) + verify(distinctApi, times(1)).queryGroupedChannels(limit, null, watch = true, presence = false) verifyNoInteractions(api) } @@ -173,6 +176,7 @@ internal class DistinctChatApiEnablerTest { members = members, ) enabler.queryChannel(channelType, channelId, channelRequest) + enabler.queryGroupedChannels(limit, null, watch = true, presence = false) // then verify(api, times(1)).getRepliesMore(messageId, firstId, limit) @@ -203,6 +207,7 @@ internal class DistinctChatApiEnablerTest { members = members, ) verify(api, times(1)).queryChannel(channelType, channelId, channelRequest) + verify(api, times(1)).queryGroupedChannels(limit, null, watch = true, presence = false) verify(distinctApi, times(0)).getRepliesMore(any(), any(), any()) verify(distinctApi, times(0)).getReplies(any(), any()) verify(distinctApi, times(0)).getNewerReplies(any(), any(), any()) @@ -214,5 +219,6 @@ internal class DistinctChatApiEnablerTest { verify(distinctApi, times(0)).queryBannedUsers(any(), any(), any(), any(), any(), any(), any(), any()) verify(distinctApi, times(0)).queryMembers(any(), any(), any(), any(), any(), any(), any()) verify(distinctApi, times(0)).queryChannel(any(), any(), any()) + verify(distinctApi, times(0)).queryGroupedChannels(anyOrNull(), anyOrNull(), any(), any()) } } diff --git a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiTest.kt b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiTest.kt index 57dc7460156..d5c6a8d80fb 100644 --- a/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiTest.kt +++ b/stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api/internal/DistinctChatApiTest.kt @@ -483,6 +483,46 @@ internal class DistinctChatApiTest { Assert.assertFalse(call1 === call2) } + @Test + fun `When calling queryGroupedChannels with same arguments, Then same instance of Call is returned`() { + // given + val distinctChatApi = DistinctChatApi(TestScope(), mock()) + // when + val call1 = distinctChatApi.queryGroupedChannels(limit = 30, groups = null, watch = true, presence = false) + val call2 = distinctChatApi.queryGroupedChannels(limit = 30, groups = null, watch = true, presence = false) + // then + // verify same instance of call is reused + Assert.assertTrue(call1 === call2) + } + + @Test + fun `When calling queryGroupedChannels with same arguments and first call finishes, Then different instance of Call is returned`() = + runTest { + // given + val delegateApi = mock() + whenever(delegateApi.queryGroupedChannels(any(), anyOrNull(), any(), any())).thenReturn(mock()) + val distinctChatApi = DistinctChatApi(backgroundScope, delegateApi) + // when + val call1 = distinctChatApi.queryGroupedChannels(limit = 30, groups = null, watch = true, presence = false) + call1.await() + val call2 = distinctChatApi.queryGroupedChannels(limit = 30, groups = null, watch = true, presence = false) + // then + // verify different instance of call is returned + Assert.assertFalse(call1 === call2) + } + + @Test + fun `When calling queryGroupedChannels with different arguments, Then different instance of Call is returned`() { + // given + val distinctChatApi = DistinctChatApi(TestScope(), mock()) + // when + val call1 = distinctChatApi.queryGroupedChannels(limit = 30, groups = null, watch = true, presence = false) + val call2 = distinctChatApi.queryGroupedChannels(limit = 60, groups = null, watch = true, presence = false) + // then + // verify different instance of call is returned + Assert.assertFalse(call1 === call2) + } + @Test fun `When calling queryBannedUsers with same arguments, Then same instance of Call is returned`() { // given