Skip to content

Align Threads API with API specs#6140

Merged
VelikovPetar merged 9 commits intov7from
feature/AND-973_align_threads_api
Feb 16, 2026
Merged

Align Threads API with API specs#6140
VelikovPetar merged 9 commits intov7from
feature/AND-973_align_threads_api

Conversation

@VelikovPetar
Copy link
Contributor

@VelikovPetar VelikovPetar commented Feb 9, 2026

Goal

Aligns the Threads public API with the API specs.

Implementation

  1. Rename ChatClient.queryThreadsResult to ChatClient.queryThreads and delete the current ChatClient.queryThreads (it didn't support pagination)
  2. Change the return type of ChatClient.partialUpdateThread from Thread to ThreadInfo (it returns the base Thread data (ThreadInfo), not the enriched Thread data)
  3. Add the thread.updated event and it's handling
  4. Add missing fields to ThreadInfo
  5. Remove deprecated fields from QueryThreadsRequest

🎨 UI Changes

NA

Testing

There are no visible changes that could be tested. However you can any of the following:

  1. Manually call ChatClient.getThread
  2. Manually call ChatClient.partialUpdateThread and verify it doesn't fail AND
  3. Ensure the thread.updated event is triggered and properly parsed

Summary by CodeRabbit

  • New Features

    • Thread update events introduced to track and notify of real-time changes to thread information.
    • Thread details expanded to include channel context and participant membership information for better context.
  • Chores

    • Removed deprecated query parameters from thread API for a cleaner interface.
    • Updated database schema version to support enhanced thread data structures.

@VelikovPetar VelikovPetar added the pr:breaking-change Breaking change label Feb 9, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled.

🎉 Great job! This PR is ready for review.

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

DB Entities have been updated. Do we need to upgrade DB Version?
Modified Entities :

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadEntity.kt

@github-actions
Copy link
Contributor

github-actions bot commented Feb 9, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.25 MB 5.66 MB 0.41 MB 🟡
stream-chat-android-ui-components 10.60 MB 10.82 MB 0.23 MB 🟢
stream-chat-android-compose 12.81 MB 11.87 MB -0.94 MB 🚀

@VelikovPetar VelikovPetar marked this pull request as ready for review February 10, 2026 09:44
@VelikovPetar VelikovPetar requested a review from a team as a code owner February 10, 2026 09:44
# Conflicts:
#	stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/database/converter/internal/ListConverter.kt
#	stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadParticipantEntity.kt
#	stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/Mother.kt
#	stream-chat-android-offline/src/main/java/io/getstream/chat/android/offline/repository/database/internal/ChatDatabase.kt
@coderabbitai
Copy link

coderabbitai bot commented Feb 13, 2026

Walkthrough

This PR introduces ThreadUpdatedEvent to handle thread update notifications, consolidates thread query APIs by removing queryThreadsResult() in favor of queryThreads(), changes partialUpdateThread return types from Thread to ThreadInfo, and restructures thread-related DTOs to include richer participant and channel context while bumping the database schema version.

Changes

Cohort / File(s) Summary
New ThreadUpdatedEvent Type
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/events/ChatEvent.kt, stream-chat-android-core/src/main/java/io/getstream/chat/android/models/EventType.kt
Added new public ThreadUpdatedEvent data class and THREAD_UPDATED event type constant to represent thread update notifications.
Event DTO and Mapping
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/EventDtos.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/EventMapping.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/parser2/adapters/EventAdapter.kt
Introduced ThreadUpdatedEventDto DTO class, corresponding toDomain() mapping, and event adapter support for deserializing thread.updated events.
Thread API Consolidation
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/ChatApi.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt
Removed queryThreadsResult() method; consolidated into single queryThreads() returning QueryThreadsResult. Removed queryThreadsResult from public API surface.
Return Type Updates
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/ChatApi.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/endpoint/ThreadsApi.kt
Changed partialUpdateThread return type from Call<Thread> to Call<ThreadInfo> across all API layers.
Thread DTO Restructuring
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ThreadDtos.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/response/ThreadResponse.kt
Restructured DownstreamThreadInfoDto to replace channel_cid string with channel object, added threadParticipants, created_by, timestamp fields. Introduced ThreadInfoResponse for partial update endpoint. Refactored DownstreamThreadParticipantDto to use nullable user with explicit user_id.
Domain Model Updates
stream-chat-android-core/src/main/java/io/getstream/chat/android/models/ThreadInfo.kt, stream-chat-android-core/src/main/java/io/getstream/chat/android/models/ThreadParticipant.kt
Extended ThreadInfo with new channel and threadParticipants properties. Updated ThreadParticipant KDoc to clarify user may be represented by ID only.
Mapping and Domain Conversion
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DomainMapping.kt
Updated DTO-to-domain mappings to populate new channel and threadParticipants fields on ThreadInfo, and adjusted parentMessage mapping to include channel context.
Thread Update Extension
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/extensions/internal/Thread.kt
Added new applyThreadUpdatedEventChanges(threadInfo: ThreadInfo) extension function to selectively update thread title, updatedAt, and extraData from ThreadUpdatedEvent.
Event Handling
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequential.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/querythreads/internal/QueryThreadsLogic.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/state/plugin/logic/querythreads/internal/QueryThreadsStateLogic.kt
Integrated ThreadUpdatedEvent handling into sequential event handler and query threads logic; added updateThreadFromEvent() method to apply thread updates to state.
Offline Storage
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadEntity.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadParticipantEntity.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadMapper.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/database/converter/internal/ListConverter.kt, stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/database/internal/ChatDatabase.kt
Replaced threadParticipantIds (List) with threadParticipants (List) in offline storage. Introduced ThreadParticipantEntity for database persistence. Added Moshi converter support. Bumped database schema version 99 → 100.
Channel Client Integration
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/channel/ChannelClient.kt
Added ThreadUpdatedEvent to channel event relevance checks via CID matching.
API Definition Files
stream-chat-android-client/api/stream-chat-android-client.api, stream-chat-android-core/api/stream-chat-android-core.api
Updated public API signatures: removed queryThreadsResult, updated QueryThreadsRequest constructors/accessors, added ThreadUpdatedEvent, extended ThreadInfo with channel/threadParticipants components.
Test Infrastructure
stream-chat-android-client-test/src/main/java/io/getstream/chat/android/client/test/Mother.kt, stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt
Added randomThreadUpdatedEvent() helper; updated randomDownstreamThreadInfoDto() and randomThreadInfo() to include channel and threadParticipants; introduced randomThreadParticipant() helper.
Test Cases
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/ChatClientThreadsApiTest.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/EventMappingTestArguments.kt
Updated tests to reflect API consolidation (queryThreads vs. queryThreadsResult), ThreadInfo return types, restructured DTO fields, and added ThreadUpdatedEvent mapping test cases. Changed test parameter types from ThreadResponse to ThreadInfoResponse.
Event Handler Tests
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/event/handler/internal/EventHandlerSequentialTest.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/querythreads/internal/QueryThreadsLogicTest.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/state/plugin/logic/querythreads/internal/QueryThreadsStateLogicTest.kt
Added comprehensive tests for ThreadUpdatedEvent handling in sequential handler and query threads logic, verifying thread updates with title/extraData/updatedAt fields and state persistence.
Extension and Entity Tests
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/extensions/internal/ThreadExtensionsTests.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadMapperTest.kt, stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/Mother.kt
Added tests for applyThreadUpdatedEventChanges behavior; updated thread mapper tests for new ThreadParticipantEntity structure; updated offline test factories.
Test Data
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/parser2/testdata/ThreadDtoTestData.kt
Updated JSON test data and DTO constructions to reflect restructured ThreadInfo fields (channel object, threadParticipants list, timestamps).
UI Integration
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/threads/ThreadListController.kt, stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/threads/ThreadListControllerTest.kt
Updated thread list controller to use consolidated queryThreads() API instead of queryThreadsResult() in both load and pagination operations.
QueryThreadsRequest Cleanup
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api/models/QueryThreadsRequest.kt
Removed deprecated user and userId properties from public constructor, simplifying the request object.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 A thread's tale now told with fresh events,
Where participants no longer hide their cents,
Channel context flows with richer grace,
Updates dance in ThreadInfo's embrace,
No more results, just queries clean,
The best refactor we've ever seen! ✨

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.06% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Align Threads API with API specs' directly reflects the main goal of the PR to align the Threads API with specifications, which is the primary focus.
Description check ✅ Passed The PR description covers the goal, implementation details, and testing instructions. While UI Changes and Contributor/Reviewer checklists are not fully completed, the core required sections (Goal, Implementation, Testing) are well-documented.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into v7

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/AND-973_align_threads_api

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
stream-chat-android-ui-common/src/test/kotlin/io/getstream/chat/android/ui/common/feature/threads/ThreadListControllerTest.kt (1)

80-80: ⚠️ Potential issue | 🟡 Minor

Test name references the old API method name.

The test name still says queryThreadsResult but the method under test is now queryThreads. Same applies to the test on Line 155. Consider updating both test names for consistency.

Suggested fix

Line 80:

-    fun `load calls queryThreadsResult with correct query`() = runTest {
+    fun `load calls queryThreads with correct query`() = runTest {

Line 155:

-    fun `loadNextPage calls queryThreadsResult with next page query if shouldLoadNextPage returns true`() = runTest {
+    fun `loadNextPage calls queryThreads with next page query if shouldLoadNextPage returns true`() = runTest {
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/ChatClientThreadsApiTest.kt (1)

47-85: ⚠️ Potential issue | 🟡 Minor

Use backtick test names in src/test (e.g., queryThreadsError).

Rename the updated test to the backtick style to match the guideline.

♻️ Suggested rename
-fun queryThreadsError() = runTest {
+fun `query threads error`() = runTest {

As per coding guidelines: **/src/test/**/*.kt: Use backtick test names (for example: fun message list filters muted channels()) for readability

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/ChatClient.kt (2)

4518-4535: ⚠️ Potential issue | 🟡 Minor

KDoc should reflect QueryThreadsResult + pagination.

The doc still says “matching users” and doesn’t mention the paginated result type.

✏️ Suggested KDoc update
- * `@param` query [QueryThreadsRequest] with query parameters to get matching users.
+ * `@param` query [QueryThreadsRequest] with query parameters to get matching threads.
+ *
+ * `@return` Executable async [Call] returning [QueryThreadsResult] (includes pagination data).

As per coding guidelines, "Document public APIs with KDoc, including thread expectations and state notes".


4555-4574: ⚠️ Potential issue | 🟡 Minor

KDoc still references Thread/message after the return type change.

Update the doc to reflect ThreadInfo and “thread” semantics.

✏️ Suggested KDoc update
- * Partially updates specific [Thread] fields retaining the fields which were set previously.
+ * Partially updates specific [ThreadInfo] fields retaining the fields which were set previously.
...
- * `@return` Executable async [Call] responsible for partially updating the message.
+ * `@return` Executable async [Call] responsible for partially updating the thread (returns base [ThreadInfo]).

As per coding guidelines, "Document public APIs with KDoc, including thread expectations and state notes".

stream-chat-android-core/src/testFixtures/kotlin/io/getstream/chat/android/Mother.kt (1)

1010-1048: ⚠️ Potential issue | 🟡 Minor

Add KDoc for the new/updated public thread helpers.
randomThreadInfo now exposes channel and threadParticipants, and randomThreadParticipant is new; both should be documented with thread expectations/state notes.

Suggested KDoc
+/**
+ * Creates a [ThreadInfo] for tests.
+ *
+ * `@param` channel Optional channel context for the thread.
+ * `@param` threadParticipants Participants included in the thread snapshot.
+ */
 public fun randomThreadInfo(
@@
+/**
+ * Creates a [ThreadParticipant] for the provided [user].
+ */
 public fun randomThreadParticipant(

As per coding guidelines: Document public APIs with KDoc, including thread expectations and state notes.

🤖 Fix all issues with AI agents
In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DomainMappingTest.kt`:
- Line 743: Locate the `@Suppress`("LongMethod") annotation introduced in
DomainMappingTest (the suppression at line with `@Suppress`("LongMethod")) and
either remove it by refactoring the long test into smaller helper functions
(extract logical sections into private methods) or replace the suppression with
a short KDoc comment immediately above the annotation that documents why the
method must remain long (what complex behavior it covers and why extraction
isn't practical); if the suppression was intended for an experimental API
instead, use the appropriate `@OptIn` annotation for that API (naming the
experimental marker) rather than suppressing LongMethod.

In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt`:
- Around line 2115-2116: Rename the test function testPartialUpdateThread in
MoshiChatApiTest to a backtick-style name for test readability/consistency
(e.g., fun `partial update thread`(call: RetrofitCall<ThreadInfoResponse>,
expected: KClass<*>) = runTest { ... }); locate the declaration by the symbol
testPartialUpdateThread and update its name while keeping the signature and body
unchanged so test references compile.

In
`@stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt`:
- Around line 797-808: In threadInfoResponseArguments(), the error case uses the
wrong generic type; replace RetroError<ThreadResponse> with
RetroError<ThreadInfoResponse> so the error result matches the success case and
the partialUpdateThread return type; update the Arguments.of(...) call that
constructs the error Retrofit call (the RetroError instantiation) to use
ThreadInfoResponse and keep the surrounding Result.Failure::class unchanged.
🧹 Nitpick comments (3)
stream-chat-android-client/src/main/java/io/getstream/chat/android/client/internal/offline/repository/database/internal/ChatDatabase.kt (1)

134-153: Pre-existing TOCTOU race in getDatabase — not introduced by this PR, noting for awareness.

The containsKey check on line 135 is outside synchronized, so two concurrent callers for the same userId can both enter the block and create separate DB instances. The second overwrites the first, potentially leaking connections. This is pre-existing and out of scope for this PR, but worth a follow-up.

♻️ Sketch of a safer pattern (double-checked locking)
         fun getDatabase(context: Context, userId: String): ChatDatabase {
-            if (!INSTANCES.containsKey(userId)) {
-                synchronized(this) {
+            return INSTANCES[userId] ?: synchronized(this) {
+                INSTANCES[userId] ?: run {
                     val db = Room.databaseBuilder(
                         context.applicationContext,
                         ChatDatabase::class.java,
                         "stream_chat_database_$userId",
                     ).fallbackToDestructiveMigration()
                         .addCallback(
                             object : Callback() {
                                 override fun onOpen(db: SupportSQLiteDatabase) {
                                     db.execSQL("PRAGMA synchronous = 1")
                                 }
                             },
                         )
                         .build()
                     INSTANCES[userId] = db
+                    db
                 }
             }
-            return INSTANCES[userId] ?: error("DB not created")
         }
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/extensions/internal/ThreadExtensionsTests.kt (1)

423-512: Test coverage is thorough and well-structured.

The three tests correctly validate:

  1. Updatable fields (title, updatedAt, extraData) are applied
  2. Non-updatable fields remain unchanged
  3. Guard clause rejects mismatched parentMessageId

Minor naming nit: the test method names reference applyThreadInfoUpdate (e.g., Line 424, 454, 488) while the function under test is applyThreadUpdatedEventChanges. Consider aligning the names for discoverability and grep-ability.

Suggested rename
-    fun `applyThreadInfoUpdate should update title extraData and updatedAt`() {
+    fun `applyThreadUpdatedEventChanges should update title extraData and updatedAt`() {
-    fun `applyThreadInfoUpdate should not update non-updatable fields`() {
+    fun `applyThreadUpdatedEventChanges should not update non-updatable fields`() {
-    fun `applyThreadInfoUpdate should not update when thread info parent message id does not match`() {
+    fun `applyThreadUpdatedEventChanges should not update when thread info parent message id does not match`() {
stream-chat-android-client/src/test/java/io/getstream/chat/android/client/internal/offline/repository/domain/threads/internal/ThreadMapperTest.kt (1)

41-71: Strengthen participant mapping assertions with per-user lookups.

Using a single user for all participants doesn’t verify that mapping respects participant IDs.

♻️ Suggested test tightening
-        val user = randomUser()
+        val createdBy = randomUser()
         val message = randomMessage()
         val channel = randomChannel()
         val entity = randomThreadEntity()
+        val userById = entity.threadParticipants
+            .associate { it.userId to randomUser(it.userId) }
+            .toMutableMap()
+            .apply { put(entity.createdByUserId, createdBy) }
         val expected = Thread(
             parentMessageId = entity.parentMessageId,
             parentMessage = message,
             cid = entity.cid,
             channel = channel,
             createdByUserId = entity.createdByUserId,
-            createdBy = user,
+            createdBy = createdBy,
             activeParticipantCount = entity.activeParticipantCount,
             participantCount = entity.participantCount,
-            threadParticipants = entity.threadParticipants.map {
-                ThreadParticipant(
-                    user = user,
-                )
-            },
+            threadParticipants = entity.threadParticipants.map { participant ->
+                ThreadParticipant(user = userById.getValue(participant.userId))
+            },
             lastMessageAt = entity.lastMessageAt,
             createdAt = entity.createdAt,
             updatedAt = entity.updatedAt,
             deletedAt = entity.deletedAt,
             title = entity.title,
             read = entity.read.map { it.toModel { user } },
             latestReplies = emptyList(),
             extraData = entity.extraData,
             draft = null,
         )
         // When
         val model = entity.toModel(
-            getUser = { user },
+            getUser = { userById.getValue(it) },
             getMessage = { message },
             getChannel = { channel },
             getDraftMessage = { null },
         )

@VelikovPetar VelikovPetar enabled auto-merge (squash) February 16, 2026 08:05
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
76.0% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@VelikovPetar VelikovPetar merged commit 3a31ac7 into v7 Feb 16, 2026
23 of 26 checks passed
@VelikovPetar VelikovPetar deleted the feature/AND-973_align_threads_api branch February 16, 2026 08:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:breaking-change Breaking change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants