Skip to content

Commit eea9e6c

Browse files
committed
Fix unable to edit thread replies.
1 parent 77b2ba0 commit eea9e6c

5 files changed

Lines changed: 32 additions & 76 deletions

File tree

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/MoshiChatApi.kt

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,6 @@ import io.getstream.chat.android.models.Member
132132
import io.getstream.chat.android.models.MemberData
133133
import io.getstream.chat.android.models.Message
134134
import io.getstream.chat.android.models.MessageReminder
135-
import io.getstream.chat.android.models.MessageType
136135
import io.getstream.chat.android.models.Mute
137136
import io.getstream.chat.android.models.PendingMessage
138137
import io.getstream.chat.android.models.Poll
@@ -253,7 +252,7 @@ constructor(
253252
channelType = channelType,
254253
channelId = channelId,
255254
message = SendMessageRequest(
256-
message = with(dtoMapping) { message.toDto(messageType = message.uploadMessageType) },
255+
message = with(dtoMapping) { message.toDto() },
257256
skip_push = message.skipPushNotification,
258257
skip_enrich_url = message.skipEnrichUrl,
259258
),
@@ -324,7 +323,7 @@ constructor(
324323
return messageApi.updateMessage(
325324
messageId = message.id,
326325
message = UpdateMessageRequest(
327-
message = with(dtoMapping) { message.toDto(messageType = message.uploadMessageType) },
326+
message = with(dtoMapping) { message.toDto() },
328327
skip_enrich_url = message.skipEnrichUrl,
329328
skip_push = message.skipPushNotification,
330329
),
@@ -1816,19 +1815,4 @@ constructor(
18161815

18171816
private fun <T : Any, R : Any> RetrofitCall<T>.flatMapDomain(transform: DomainMapping.(T) -> Call<R>): Call<R> =
18181817
flatMap { domainMapping.transform(it) }
1819-
1820-
/**
1821-
* Value to send for `message.type` on outbound send/update requests.
1822-
*
1823-
* The backend's UpdateMessage endpoint validates `type` against `oneof='' regular system`,
1824-
* so server-assigned types like `reply`, `error`, or `ephemeral` would be rejected with a 400
1825-
* if echoed back. Coerce anything outside the accepted set to the empty string — the field is
1826-
* ignored by the backend on update anyway, and on send only `regular` and `system` are
1827-
* meaningful for clients to declare.
1828-
*/
1829-
private val Message.uploadMessageType: String
1830-
get() = when (type) {
1831-
MessageType.REGULAR, MessageType.SYSTEM -> type
1832-
else -> ""
1833-
}
18341818
}

stream-chat-android-client/src/main/java/io/getstream/chat/android/client/api2/mapping/DtoMapping.kt

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ internal class DtoMapping(
5454
private val userTransformer: UserTransformer,
5555
) {
5656

57+
private val supportedUpstreamMessageTypes = setOf(MessageType.REGULAR, MessageType.SYSTEM)
58+
5759
/**
5860
* Converts [Attachment] to [AttachmentDto].
5961
*/
@@ -118,22 +120,18 @@ internal class DtoMapping(
118120

119121
/**
120122
* Transforms [Message] to [UpstreamMessageDto].
121-
*
122-
* @param messageType Value to use for [UpstreamMessageDto.type]. When `null` (the default),
123-
* the message's own type is used. Callers in the network layer may pass an explicit value to
124-
* satisfy server-side validation — for example, the UpdateMessage endpoint only accepts the
125-
* empty string, [MessageType.REGULAR], or [MessageType.SYSTEM].
126123
*/
127-
internal fun Message.toDto(messageType: String? = null): UpstreamMessageDto =
124+
internal fun Message.toDto(): UpstreamMessageDto =
128125
messageTransformer.transform(this)
129126
.run {
127+
val upstreamType = if (type in supportedUpstreamMessageTypes) type else ""
130128
UpstreamMessageDto(
131129
attachments = attachments.map { it.toDto() },
132130
cid = cid,
133131
command = command,
134132
html = html,
135133
id = id,
136-
type = messageType ?: type,
134+
type = upstreamType,
137135
mentioned_users = mentionedUsersIds,
138136
parent_id = parentId,
139137
pin_expires = pinExpires,

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTest.kt

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -72,14 +72,12 @@ import io.getstream.chat.android.client.api2.model.requests.RejectInviteRequest
7272
import io.getstream.chat.android.client.api2.model.requests.ReminderRequest
7373
import io.getstream.chat.android.client.api2.model.requests.SendActionRequest
7474
import io.getstream.chat.android.client.api2.model.requests.SendEventRequest
75-
import io.getstream.chat.android.client.api2.model.requests.SendMessageRequest
7675
import io.getstream.chat.android.client.api2.model.requests.UnblockUserRequest
7776
import io.getstream.chat.android.client.api2.model.requests.UpdateChannelPartialRequest
7877
import io.getstream.chat.android.client.api2.model.requests.UpdateCooldownRequest
7978
import io.getstream.chat.android.client.api2.model.requests.UpdateLiveLocationRequest
8079
import io.getstream.chat.android.client.api2.model.requests.UpdateMemberPartialRequest
8180
import io.getstream.chat.android.client.api2.model.requests.UpdateMemberPartialResponse
82-
import io.getstream.chat.android.client.api2.model.requests.UpdateMessageRequest
8381
import io.getstream.chat.android.client.api2.model.requests.UpsertPushPreferencesRequest
8482
import io.getstream.chat.android.client.api2.model.requests.UpstreamOptionDto
8583
import io.getstream.chat.android.client.api2.model.requests.UpstreamVoteDto
@@ -182,7 +180,6 @@ import org.junit.jupiter.params.ParameterizedTest
182180
import org.junit.jupiter.params.provider.MethodSource
183181
import org.mockito.kotlin.any
184182
import org.mockito.kotlin.anyOrNull
185-
import org.mockito.kotlin.argumentCaptor
186183
import org.mockito.kotlin.doReturn
187184
import org.mockito.kotlin.eq
188185
import org.mockito.kotlin.mock
@@ -317,38 +314,6 @@ internal class MoshiChatApiTest {
317314
verify(api, times(1)).updateMessage(eq(message.id), any())
318315
}
319316

320-
@ParameterizedTest
321-
@MethodSource("io.getstream.chat.android.client.api2.MoshiChatApiTestArguments#uploadMessageTypeInput")
322-
fun testSendMessageCoercesType(inputType: String, expectedWireType: String) = runTest {
323-
val api = mock<MessageApi>()
324-
val call = RetroSuccess(MessageResponse(message = Mother.randomDownstreamMessageDto())).toRetrofitCall()
325-
whenever(api.sendMessage(any(), any(), any())).doReturn(call)
326-
val sut = Fixture().withMessageApi(api).get()
327-
val message = randomMessage(type = inputType)
328-
329-
sut.sendMessage(randomString(), randomString(), message).await()
330-
331-
val captor = argumentCaptor<SendMessageRequest>()
332-
verify(api).sendMessage(any(), any(), captor.capture())
333-
assertEquals(expectedWireType, captor.firstValue.message.type)
334-
}
335-
336-
@ParameterizedTest
337-
@MethodSource("io.getstream.chat.android.client.api2.MoshiChatApiTestArguments#uploadMessageTypeInput")
338-
fun testUpdateMessageCoercesType(inputType: String, expectedWireType: String) = runTest {
339-
val api = mock<MessageApi>()
340-
val call = RetroSuccess(MessageResponse(message = Mother.randomDownstreamMessageDto())).toRetrofitCall()
341-
whenever(api.updateMessage(any(), any())).doReturn(call)
342-
val sut = Fixture().withMessageApi(api).get()
343-
val message = randomMessage(type = inputType)
344-
345-
sut.updateMessage(message).await()
346-
347-
val captor = argumentCaptor<UpdateMessageRequest>()
348-
verify(api).updateMessage(eq(message.id), captor.capture())
349-
assertEquals(expectedWireType, captor.firstValue.message.type)
350-
}
351-
352317
@ParameterizedTest
353318
@MethodSource("io.getstream.chat.android.client.api2.MoshiChatApiTestArguments#partialUpdateMessageInput")
354319
fun testPartialUpdateMessage(call: RetrofitCall<MessageResponse>, expected: KClass<*>) = runTest {

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/MoshiChatApiTestArguments.kt

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ import io.getstream.chat.android.client.api2.model.response.UsersResponse
6666
import io.getstream.chat.android.client.utils.RetroError
6767
import io.getstream.chat.android.client.utils.RetroSuccess
6868
import io.getstream.chat.android.models.EventType
69-
import io.getstream.chat.android.models.MessageType
7069
import io.getstream.chat.android.models.QueryRemindersResult
7170
import io.getstream.chat.android.models.UnreadChannel
7271
import io.getstream.chat.android.models.UnreadChannelByType
@@ -112,15 +111,6 @@ internal object MoshiChatApiTestArguments {
112111
@JvmStatic
113112
fun updateMessageInput() = messageResponseArguments()
114113

115-
@JvmStatic
116-
fun uploadMessageTypeInput() = listOf(
117-
Arguments.of(MessageType.REGULAR, MessageType.REGULAR),
118-
Arguments.of(MessageType.SYSTEM, MessageType.SYSTEM),
119-
Arguments.of(MessageType.REPLY, ""),
120-
Arguments.of(MessageType.ERROR, ""),
121-
Arguments.of(MessageType.EPHEMERAL, ""),
122-
)
123-
124114
@JvmStatic
125115
fun partialUpdateMessageInput() = messageResponseArguments()
126116

stream-chat-android-client/src/test/java/io/getstream/chat/android/client/api2/mapping/DtoMappingTest.kt

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ import io.getstream.chat.android.client.api2.model.dto.UpstreamReactionDto
3535
import io.getstream.chat.android.client.api2.model.dto.UpstreamUserDto
3636
import io.getstream.chat.android.client.test.randomConnectedEvent
3737
import io.getstream.chat.android.models.MessageTransformer
38+
import io.getstream.chat.android.models.MessageType
3839
import io.getstream.chat.android.models.NoOpMessageTransformer
3940
import io.getstream.chat.android.models.NoOpUserTransformer
4041
import io.getstream.chat.android.models.UserTransformer
@@ -49,6 +50,9 @@ import io.getstream.chat.android.randomReaction
4950
import io.getstream.chat.android.randomUser
5051
import org.amshove.kluent.shouldBeEqualTo
5152
import org.junit.jupiter.api.Test
53+
import org.junit.jupiter.params.ParameterizedTest
54+
import org.junit.jupiter.params.provider.Arguments
55+
import org.junit.jupiter.params.provider.MethodSource
5256
import org.mockito.kotlin.spy
5357
import org.mockito.kotlin.times
5458
import org.mockito.kotlin.verify
@@ -167,7 +171,7 @@ internal class DtoMappingTest {
167171
@Test
168172
fun `Message is correctly mapped to Dto`() {
169173
val messageTransformer = spy(NoOpMessageTransformer)
170-
val message = randomMessage()
174+
val message = randomMessage(type = MessageType.REGULAR)
171175
val mapping = Fixture()
172176
.withMessageTransformer(messageTransformer)
173177
.get()
@@ -201,14 +205,15 @@ internal class DtoMappingTest {
201205
verify(messageTransformer, times(1)).transform(message)
202206
}
203207

204-
@Test
205-
fun `Message toDto uses messageType parameter when provided`() {
206-
val message = randomMessage(type = "reply")
208+
@ParameterizedTest
209+
@MethodSource("messageTypeCoercionInput")
210+
fun `Message toDto coerces type to allowed upstream values`(inputType: String, expectedType: String) {
211+
val message = randomMessage(type = inputType)
207212
val mapping = Fixture().get()
208213

209-
val dto = with(mapping) { message.toDto(messageType = "") }
214+
val dto = with(mapping) { message.toDto() }
210215

211-
dto.type shouldBeEqualTo ""
216+
dto.type shouldBeEqualTo expectedType
212217
}
213218

214219
@Test
@@ -321,6 +326,20 @@ internal class DtoMappingTest {
321326
dto shouldBeEqualTo expected
322327
}
323328

329+
companion object {
330+
@JvmStatic
331+
fun messageTypeCoercionInput(): List<Arguments> = listOf(
332+
Arguments.of(MessageType.REGULAR, MessageType.REGULAR),
333+
Arguments.of(MessageType.SYSTEM, MessageType.SYSTEM),
334+
Arguments.of(MessageType.REPLY, ""),
335+
Arguments.of(MessageType.EPHEMERAL, ""),
336+
Arguments.of(MessageType.ERROR, ""),
337+
Arguments.of(MessageType.FAILED, ""),
338+
Arguments.of("some-unknown-type", ""),
339+
Arguments.of("", ""),
340+
)
341+
}
342+
324343
internal class Fixture {
325344

326345
private var messageTransformer: MessageTransformer = NoOpMessageTransformer

0 commit comments

Comments
 (0)