Skip to content
Merged
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 @@ -321,12 +321,6 @@ internal interface ChatApi {
message: DraftMessage,
): Call<Unit>

@CheckResult
fun queryDraftMessages(
offset: Int?,
limit: Int?,
): Call<List<DraftMessage>>

@CheckResult
fun queryDrafts(
filter: FilterObject,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ import io.getstream.chat.android.client.api2.model.requests.FlagUserRequest
import io.getstream.chat.android.client.api2.model.requests.MuteUserRequest
import io.getstream.chat.android.client.api2.model.requests.PinnedMessagesRequest
import io.getstream.chat.android.client.api2.model.requests.QueryBannedUsersRequest
import io.getstream.chat.android.client.api2.model.requests.QueryDraftMessagesRequest
import io.getstream.chat.android.client.api2.model.requests.SyncHistoryRequest
import io.getstream.chat.android.client.api2.model.response.ChannelResponse
import io.getstream.chat.android.client.call.RetrofitCall
Expand Down Expand Up @@ -309,19 +308,6 @@ constructor(
parentId = message.parentId,
).toUnitCall()

override fun queryDraftMessages(
offset: Int?,
limit: Int?,
): Call<List<DraftMessage>> =
messageApi.queryDraftMessages(
QueryDraftMessagesRequest(
offset = offset,
limit = limit,
),
).mapDomain { response ->
response.drafts.map { it.toDomain() }
}

override fun queryDrafts(
filter: FilterObject,
limit: Int?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.requests.QueryDraftMessagesRequest
import io.getstream.chat.android.client.api2.model.response.DraftMessageResponse
import io.getstream.chat.android.client.api2.model.response.MessageResponse
import io.getstream.chat.android.client.api2.model.response.MessagesResponse
Expand Down Expand Up @@ -71,11 +70,6 @@ internal interface MessageApi {
@Query("parent_id") parentId: String?,
): RetrofitCall<Response>

@POST("/drafts/query")
fun queryDraftMessages(
@Body request: QueryDraftMessagesRequest,
): RetrofitCall<QueryDraftMessagesResponse>

/**
* Queries draft messages for the current user.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.getstream.chat.android.network.models.ChannelConfigWithInfo
import io.getstream.chat.android.network.models.ChannelMemberResponse
import io.getstream.chat.android.network.models.ReadStateResponse
import io.getstream.chat.android.network.models.UserResponse
import java.util.Date

@JsonClass(generateAdapter = true)
Expand All @@ -42,11 +43,11 @@
val member_count: Int = 0,
val messages: List<DownstreamMessageDto> = emptyList(),
val members: List<ChannelMemberResponse> = emptyList(),
val watchers: List<DownstreamUserDto> = emptyList(),
val watchers: List<UserResponse> = emptyList(),
val read: List<ReadStateResponse> = emptyList(),
// Absent for a channel whose type is no longer in the app config.
val config: ChannelConfigWithInfo? = null,
val created_by: DownstreamUserDto?,
val created_by: UserResponse?,

Check warning on line 50 in stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ChannelDtos.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this parameter to match the regular expression ^`?[_a-z][a-zA-Z0-9]*`?$

See more on https://sonarcloud.io/project/issues?id=GetStream_stream-chat-android&issues=AaDYReQafKXikGIiudkA&open=AaDYReQafKXikGIiudkA&pullRequest=6736
val team: String = "",
val cooldown: Int = 0,
val pinned_messages: List<DownstreamMessageDto> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import io.getstream.chat.android.network.models.ChannelResponse
import io.getstream.chat.android.network.models.ReadStateResponse
import io.getstream.chat.android.network.models.ThreadParticipant
import io.getstream.chat.android.network.models.UserResponse
import java.util.Date

/**
Expand Down Expand Up @@ -52,7 +53,7 @@
val channel: ChannelResponse?,
val channel_cid: String,
val created_at: Date,
val created_by: DownstreamUserDto?,
val created_by: UserResponse?,

Check warning on line 56 in stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ThreadDtos.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this parameter to match the regular expression ^`?[_a-z][a-zA-Z0-9]*`?$

See more on https://sonarcloud.io/project/issues?id=GetStream_stream-chat-android&issues=AaDYReU1fKXikGIiudkB&open=AaDYReU1fKXikGIiudkB&pullRequest=6736
val created_by_user_id: String,
val deleted_at: Date?,
val draft: DownstreamDraftDto?,
Expand Down Expand Up @@ -97,7 +98,7 @@
val parent_message_id: String,
val parent_message: DownstreamMessageDto?,
val created_by_user_id: String,
val created_by: DownstreamUserDto?,
val created_by: UserResponse?,

Check warning on line 101 in stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/model/dto/ThreadDtos.kt

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Rename this parameter to match the regular expression ^`?[_a-z][a-zA-Z0-9]*`?$

See more on https://sonarcloud.io/project/issues?id=GetStream_stream-chat-android&issues=AaDYReU1fKXikGIiudkC&open=AaDYReU1fKXikGIiudkC&pullRequest=6736
val reply_count: Int?,
val participant_count: Int?,
val active_participant_count: Int?,
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import io.getstream.chat.android.client.api2.model.dto.DownstreamChannelDto
import io.getstream.chat.android.client.api2.model.dto.DownstreamDraftDto
import io.getstream.chat.android.client.api2.model.dto.DownstreamMessageDto
import io.getstream.chat.android.client.api2.model.dto.DownstreamPendingMessageDto
import io.getstream.chat.android.client.api2.model.dto.DownstreamUserDto
import io.getstream.chat.android.network.models.ChannelMemberResponse
import io.getstream.chat.android.network.models.ChannelPushPreferencesResponse
import io.getstream.chat.android.network.models.ReadStateResponse
import io.getstream.chat.android.network.models.UserResponse
import java.util.Date

@JsonClass(generateAdapter = true)
Expand All @@ -36,7 +36,7 @@ internal data class ChannelResponse(
val push_preferences: ChannelPushPreferencesResponse? = null,
val members: List<ChannelMemberResponse> = emptyList(),
val membership: ChannelMemberResponse?,
val watchers: List<DownstreamUserDto> = emptyList(),
val watchers: List<UserResponse> = emptyList(),
val read: List<ReadStateResponse> = emptyList(),
val watcher_count: Int = 0,
val hidden: Boolean?,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,11 @@ import io.getstream.chat.android.network.models.UserResponseCommonFields
import io.getstream.chat.android.network.models.UserResponsePrivacyFields

/**
* Keys the event user models declare that `DownstreamUserDto` did not, so they used to reach
* Keys the generated user models declare that `DownstreamUserDto` did not, so they used to reach
* `User.extraData` and would otherwise stop doing so. The domain has no property for either, so they are
* kept in the overflow map. Drop with AND-1398.
*/
internal val GENERATED_EVENT_USER_EXTRA_DATA_KEYS = setOf(
internal val GENERATED_USER_EXTRA_DATA_KEYS = setOf(
"deleted_at",
"revoke_tokens_issued_before",
)
Expand All @@ -40,7 +40,7 @@ internal object UserResponseCommonFieldsAdapter :
CustomObjectDtoAdapter<UserResponseCommonFields>(
UserResponseCommonFields::class,
extraDataPropertyName = "custom",
alsoKeepInExtraData = GENERATED_EVENT_USER_EXTRA_DATA_KEYS,
alsoKeepInExtraData = GENERATED_USER_EXTRA_DATA_KEYS,
) {

@FromJson
Expand All @@ -61,7 +61,7 @@ internal object UserResponsePrivacyFieldsAdapter :
CustomObjectDtoAdapter<UserResponsePrivacyFields>(
UserResponsePrivacyFields::class,
extraDataPropertyName = "custom",
alsoKeepInExtraData = GENERATED_EVENT_USER_EXTRA_DATA_KEYS,
alsoKeepInExtraData = GENERATED_USER_EXTRA_DATA_KEYS,
) {

@FromJson
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ import io.getstream.chat.android.network.models.UserResponse
// Downstream (read-only) adapter for the generated UserResponse: collects root-level custom fields
// into `custom`, matching the wire's flattened extra data. extraDataPropertyName is its @Json name.
internal object UserResponseAdapter :
CustomObjectDtoAdapter<UserResponse>(UserResponse::class, extraDataPropertyName = "custom") {
CustomObjectDtoAdapter<UserResponse>(
UserResponse::class,
extraDataPropertyName = "custom",
alsoKeepInExtraData = GENERATED_USER_EXTRA_DATA_KEYS,
) {

@FromJson
fun fromJson(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1009,7 +1009,7 @@ private fun createChannelJsonString() =
"last_message_at": "2020-06-29T06:14:28.000Z",
"created_at": "2020-06-29T06:14:28.000Z",
"updated_at": "2020-06-29T06:14:28.000Z",
"created_by": ${createUserJsonString()},
"created_by": ${createCommonFieldsUserJsonString()},
"frozen": false,
"members": [
${createMemberJsonString()}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,10 +451,10 @@ internal object Mother {
member_count: Int = randomInt(),
messages: List<DownstreamMessageDto> = emptyList(),
members: List<ChannelMemberResponse> = emptyList(),
watchers: List<DownstreamUserDto> = emptyList(),
watchers: List<UserResponse> = listOf(randomUserResponse()),
read: List<ReadStateResponse> = emptyList(),
config: ChannelConfigWithInfo = randomChannelConfigWithInfo(),
created_by: DownstreamUserDto? = randomDownstreamUserDto(),
created_by: UserResponse? = randomUserResponse(),
team: String = randomString(),
cooldown: Int = randomInt(),
pinned_messages: List<DownstreamMessageDto> = emptyList(),
Expand Down Expand Up @@ -898,7 +898,7 @@ internal object Mother {
parentMessageId: String = randomString(),
parentMessage: DownstreamMessageDto = randomDownstreamMessageDto(),
createdByUserId: String = randomString(),
createdBy: DownstreamUserDto = randomDownstreamUserDto(id = createdByUserId),
createdBy: UserResponse = randomUserResponse(id = createdByUserId),
participantCount: Int = randomInt(),
threadParticipants: List<ThreadParticipant> = emptyList(),
lastMessageAt: Date = randomDate(),
Expand Down Expand Up @@ -1101,7 +1101,7 @@ internal object Mother {
parentMessageId: String = randomString(),
parentMessage: DownstreamMessageDto = randomDownstreamMessageDto(id = parentMessageId),
createdByUserId: String = randomString(),
createdBy: DownstreamUserDto = randomDownstreamUserDto(id = createdByUserId),
createdBy: UserResponse = randomUserResponse(id = createdByUserId),
replyCount: Int = randomInt(),
participantCount: Int = randomInt(),
activeParticipantCount: Int = randomInt(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,25 +308,6 @@ internal class MoshiChatApiTest {
verify(api, times(1)).deleteDraftMessage(any(), any(), any())
}

@ParameterizedTest
@MethodSource("io.getstream.chat.android.client.api2.MoshiChatApiTestArguments#queryDraftMessageInput")
fun testQueryDraftMessage(call: RetrofitCall<QueryDraftMessagesResponse>, expected: KClass<*>) = runTest {
// given
val api = mock<MessageApi>()
whenever(api.queryDraftMessages(any())).doReturn(call)
val sut = Fixture()
.withMessageApi(api)
.get()
// when
val result = sut.queryDraftMessages(
offset = randomInt(),
limit = randomInt(),
).await()
// then
result `should be instance of` expected
verify(api, times(1)).queryDraftMessages(any())
}

@ParameterizedTest
@MethodSource("io.getstream.chat.android.client.api2.MoshiChatApiTestArguments#queryDraftMessageInput")
fun testQueryDrafts(call: RetrofitCall<QueryDraftMessagesResponse>, expected: KClass<*>) = runTest {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1394,8 +1394,8 @@ internal class DomainMappingTest {

@Test
fun `DownstreamThreadDto is correctly mapped to Thread`() {
val user1 = randomDownstreamUserDto(id = "user1")
val user2 = randomDownstreamUserDto(id = "user2")
val user1 = randomUserResponse(id = "user1")
val user2 = randomUserResponse(id = "user2")
val participant1Dto = randomThreadParticipantDto(
userId = user1.id,
user = randomUserResponse(id = user1.id),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ internal object EventArguments {
type = channelType,
createdAt = date,
updatedAt = date,
createdBy = user,
createdBy = commonFieldsUser,
frozen = false,
members = listOf(member),
memberCount = 1,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* 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.client.api2.mapping.DomainMapping
import io.getstream.chat.android.client.api2.model.dto.DownstreamUserDto
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.models.User
import io.getstream.chat.android.network.models.UserResponse
import org.junit.jupiter.api.Assertions.fail
import org.junit.jupiter.params.ParameterizedTest
import org.junit.jupiter.params.provider.Arguments
import org.junit.jupiter.params.provider.MethodSource
import java.lang.reflect.Modifier

/**
* Parses the same user JSON through the hand-written [DownstreamUserDto] and the generated [UserResponse], and
* requires the same [User]. The fixtures have the shape a client-side connection receives for other users
* (channel watchers, created_by): common fields only, since the Go payload drops its client-side-only fields.
*/
internal class UserResponseParityTest {

private val parser = ParserFactory.createMoshiChatParser()
private val mapping = DomainMapping(
currentUserIdProvider = { "jaewoong" },
channelTransformer = NoOpChannelTransformer,
messageTransformer = NoOpMessageTransformer,
userTransformer = NoOpUserTransformer,
)

@ParameterizedTest(name = "{0}")
@MethodSource("fixtures")
fun `The generated UserResponse maps to the same User as the hand-written DTO`(name: String, json: String) {
val legacy = with(mapping) { parser.fromJson(json, DownstreamUserDto::class.java).toDomain() }
val generated = with(mapping) { parser.fromJson(json, UserResponse::class.java).toDomain() }
val differences = User::class.java.declaredFields
.filterNot { Modifier.isStatic(it.modifiers) }
.onEach { it.isAccessible = true }
.mapNotNull { f ->
val a = f.get(legacy)
val b = f.get(generated)
if (a == b) null else " ${f.name}:\n legacy = $a\n generated = $b"
}
if (differences.isNotEmpty()) fail<Unit>("$name differs:\n" + differences.joinToString("\n"))
}

companion object {

private const val COMMON = """
"id": "oleg", "role": "user", "language": "it", "banned": false, "online": true,
"created_at": "2021-10-21T21:58:10.000Z", "updated_at": "2026-09-13T11:30:11.000Z"
"""

@JvmStatic
fun fixtures() = listOf(
Arguments.of("required fields only", "{ $COMMON }"),
Arguments.of(
"optional fields and custom data",
"""
{
$COMMON,
"name": "R2-D2", "image": "https://example.com/r2.png",
"last_active": "2026-09-24T13:30:16.000Z", "deactivated_at": "2026-09-01T00:00:00.000Z",
"teams": ["blue", "red"], "teams_role": {"blue": "admin"},
"blocked_user_ids": ["luke"], "avg_response_time": 1207290,
"deleted_at": "2026-09-02T00:00:00.000Z",
"revoke_tokens_issued_before": "2026-09-03T00:00:00.000Z",
"flair": "gold", "score": 7, "profile": {"bio": "beep"}
}
""".trimIndent(),
),
)
}
}
Loading
Loading