Skip to content

Commit

Permalink
[AND-16] Implement ChatClient.partialUpdateMember() (#5497)
Browse files Browse the repository at this point in the history
* [AND-16] Implement partial update member operation.

* [AND-16] Add partial update member to CHANGELOG.md.

* [AND-16] Update CHANGELOG.md.

* [AND-16] Fix and add missing tests.

---------

Co-authored-by: PetarVelikov <[email protected]>
  • Loading branch information
VelikovPetar and PetarVelikov authored Dec 3, 2024
1 parent 222661a commit 09b3b6e
Show file tree
Hide file tree
Showing 26 changed files with 558 additions and 12 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
### ⬆️ Improved

### ✅ Added
- Add `ChatClient::partialUpdateMember` for updating a `Member` in the scope of a `Channel`. [#5497](https://github.com/GetStream/stream-chat-android/pull/5497)
- Add `ChannelClient::partialUpdateMember` for updating a `Member` in the scope of a `Channel`. [#5497](https://github.com/GetStream/stream-chat-android/pull/5497)

### ⚠️ Changed

Expand Down
4 changes: 4 additions & 0 deletions stream-chat-android-client/api/stream-chat-android-client.api
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ public final class io/getstream/chat/android/client/ChatClient {
public final fun muteUser (Ljava/lang/String;)Lio/getstream/result/call/Call;
public final fun muteUser (Ljava/lang/String;Ljava/lang/Integer;)Lio/getstream/result/call/Call;
public static synthetic fun muteUser$default (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)Lio/getstream/result/call/Call;
public final fun partialUpdateMember (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)Lio/getstream/result/call/Call;
public static synthetic fun partialUpdateMember$default (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/util/Map;Ljava/util/List;ILjava/lang/Object;)Lio/getstream/result/call/Call;
public final fun partialUpdateMessage (Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)Lio/getstream/result/call/Call;
public static synthetic fun partialUpdateMessage$default (Lio/getstream/chat/android/client/ChatClient;Ljava/lang/String;Ljava/util/Map;Ljava/util/List;ILjava/lang/Object;)Lio/getstream/result/call/Call;
public final fun partialUpdateThread (Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)Lio/getstream/result/call/Call;
Expand Down Expand Up @@ -670,6 +672,8 @@ public final class io/getstream/chat/android/client/channel/ChannelClient {
public final fun muteUser (Ljava/lang/String;)Lio/getstream/result/call/Call;
public final fun muteUser (Ljava/lang/String;Ljava/lang/Integer;)Lio/getstream/result/call/Call;
public static synthetic fun muteUser$default (Lio/getstream/chat/android/client/channel/ChannelClient;Ljava/lang/String;Ljava/lang/Integer;ILjava/lang/Object;)Lio/getstream/result/call/Call;
public final fun partialUpdateMember (Ljava/lang/String;Ljava/util/Map;Ljava/util/List;)Lio/getstream/result/call/Call;
public static synthetic fun partialUpdateMember$default (Lio/getstream/chat/android/client/channel/ChannelClient;Ljava/lang/String;Ljava/util/Map;Ljava/util/List;ILjava/lang/Object;)Lio/getstream/result/call/Call;
public final fun pinMessage (Lio/getstream/chat/android/models/Message;I)Lio/getstream/result/call/Call;
public final fun pinMessage (Lio/getstream/chat/android/models/Message;Ljava/util/Date;)Lio/getstream/result/call/Call;
public final fun query (Lio/getstream/chat/android/client/api/models/QueryChannelRequest;)Lio/getstream/result/call/Call;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2350,6 +2350,34 @@ internal constructor(
)
}

/**
* Updates specific fields of member data, retaining the custom data fields which were set previously.
*
* @param channelType The channel type. ie messaging.
* @param channelId The channel id. ie 123.
* @param userId The ID of the member to be updated.
* @param set The key-value data to be updated in the member data.
* @param unset The list of keys to be removed from the member data.
*
* @return Executable async [Call] responsible for updating member data.
*/
@CheckResult
public fun partialUpdateMember(
channelType: String,
channelId: String,
userId: String,
set: Map<String, Any> = emptyMap(),
unset: List<String> = emptyList(),
): Call<Member> {
return api.partialUpdateMember(
channelType = channelType,
channelId = channelId,
userId = userId,
set = set,
unset = unset,
)
}

/**
* Enables slow mode for the channel. When slow mode is enabled, users can only send a message every
* [cooldownTimeInSeconds] time interval. The [cooldownTimeInSeconds] is specified in seconds, and should be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,15 @@ internal interface ChatApi {
skipPush: Boolean?,
): Call<Channel>

@CheckResult
fun partialUpdateMember(
channelType: String,
channelId: String,
userId: String,
set: Map<String, Any>,
unset: List<String>,
): Call<Member>

@CheckResult
fun queryMembers(
channelType: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import io.getstream.chat.android.client.api.ChatApi
import io.getstream.chat.android.client.api.ErrorCall
import io.getstream.chat.android.models.Channel
import io.getstream.chat.android.models.CustomObject
import io.getstream.chat.android.models.Member
import io.getstream.chat.android.models.Message
import io.getstream.chat.android.models.User
import io.getstream.result.Error
Expand Down Expand Up @@ -86,6 +87,18 @@ internal class ExtraDataValidator(
.withExtraDataValidation(set)
}

override fun partialUpdateMember(
channelType: String,
channelId: String,
userId: String,
set: Map<String, Any>,
unset: List<String>,
): Call<Member> {
return delegate
.partialUpdateMember(channelType, channelId, userId, set, unset)
.withExtraDataValidation(set)
}

private fun <T : CustomObject> Call<List<T>>.withExtraDataValidation(
objects: List<T>,
): Call<List<T>> {
Expand Down Expand Up @@ -144,6 +157,7 @@ internal class ExtraDataValidator(
is Channel -> extraData.keys.filter(reservedInChannelPredicate)
is Message -> extraData.keys.filter(reservedInChannelPredicate)
is User -> extraData.keys.filter(reservedInChannelPredicate)
is Member -> extraData.keys.filter(reservedInMemberPredicate)
else -> emptyList()
}
}
Expand All @@ -153,6 +167,7 @@ internal class ExtraDataValidator(
Channel::class -> keys.filter(reservedInChannelPredicate)
Message::class -> keys.filter(reservedInMessagePredicate)
User::class -> keys.filter(reservedInUserPredicate)
Member::class -> keys.filter(reservedInMemberPredicate)
else -> emptyList()
}
}
Expand All @@ -166,6 +181,7 @@ internal class ExtraDataValidator(
is Channel -> "channel"
is Message -> "message"
is User -> "user"
is Member -> "member"
else -> ""
}
}
Expand Down Expand Up @@ -210,8 +226,24 @@ internal class ExtraDataValidator(
"updated_at",
)

private val reservedInMember = setOf(
"user",
"created_at",
"updated_at",
"invited",
"invite_accepted_at",
"invite_rejected_at",
"shadow_banned",
"banned",
"channel_role",
"notifications_muted",
"status",
"ban_expires",
)

private val reservedInChannelPredicate: (String) -> Boolean = reservedInChannel::contains
private val reservedInMessagePredicate: (String) -> Boolean = reservedInMessage::contains
private val reservedInUserPredicate: (String) -> Boolean = reservedInUser::contains
private val reservedInMemberPredicate: (String) -> Boolean = reservedInMember::contains
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ import io.getstream.chat.android.client.api2.model.requests.UnblockUserRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateChannelPartialRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateChannelRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateCooldownRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateMemberPartialRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateMessageRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateUsersRequest
import io.getstream.chat.android.client.api2.model.requests.UpstreamOptionDto
Expand Down Expand Up @@ -785,6 +786,23 @@ constructor(
).map(this::flattenChannel)
}

override fun partialUpdateMember(
channelType: String,
channelId: String,
userId: String,
set: Map<String, Any>,
unset: List<String>,
): Call<Member> {
return channelApi.partialUpdateMember(
channelType = channelType,
channelId = channelId,
userId = userId,
body = UpdateMemberPartialRequest(set, unset),
).map { response ->
response.channel_member.toDomain(currentUserIdProvider())
}
}

private fun flattenChannel(response: ChannelResponse): Channel {
return response.channel.toDomain(currentUserIdProvider(), null).let { channel ->
channel.copy(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import io.getstream.chat.android.client.api2.model.requests.TruncateChannelReque
import io.getstream.chat.android.client.api2.model.requests.UpdateChannelPartialRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateChannelRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateCooldownRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateMemberPartialRequest
import io.getstream.chat.android.client.api2.model.requests.UpdateMemberPartialResponse
import io.getstream.chat.android.client.api2.model.response.ChannelResponse
import io.getstream.chat.android.client.api2.model.response.CompletableResponse
import io.getstream.chat.android.client.api2.model.response.EventResponse
Expand Down Expand Up @@ -135,6 +137,14 @@ internal interface ChannelApi {
@Body body: InviteMembersRequest,
): RetrofitCall<ChannelResponse>

@PATCH("/channels/{type}/{id}/member/{user_id}")
fun partialUpdateMember(
@Path("type") channelType: String,
@Path("id") channelId: String,
@Path("user_id") userId: String,
@Body body: UpdateMemberPartialRequest,
): RetrofitCall<UpdateMemberPartialResponse>

@POST("/channels/{type}/{id}/event")
fun sendEvent(
@Path("type") channelType: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ internal fun DownstreamMemberDto.toDomain(currentUserId: UserId?): Member =
notificationsMuted = notifications_muted,
status = status,
banExpires = ban_expires,
extraData = extraData,
)

internal fun Member.toDto(): UpstreamMemberDto =
Expand All @@ -51,4 +52,5 @@ internal fun Member.toDto(): UpstreamMemberDto =
notifications_muted = notificationsMuted,
status = status,
ban_expires = banExpires,
extraData = extraData,
)
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,17 @@
package io.getstream.chat.android.client.api2.model.dto

import com.squareup.moshi.JsonClass
import io.getstream.chat.android.core.internal.StreamHandsOff
import java.util.Date

/**
* See [io.getstream.chat.android.client.parser2.adapters.UpstreamMemberDtoAdapter] for
* special [extraData] handling.
*/
@StreamHandsOff(
reason = "Field names can't be changed because [CustomObjectDtoAdapter] class uses reflections to add/remove " +
"content of [extraData] map",
)
@JsonClass(generateAdapter = true)
internal data class UpstreamMemberDto(
val user: UpstreamUserDto,
Expand All @@ -33,8 +42,18 @@ internal data class UpstreamMemberDto(
val notifications_muted: Boolean?,
val status: String?,
val ban_expires: Date?,
)

val extraData: Map<String, Any>,
) : ExtraDataDto

/**
* See [io.getstream.chat.android.client.parser2.adapters.DownstreamMemberDtoAdapter] for
* special [extraData] handling.
*/
@StreamHandsOff(
reason = "Field names can't be changed because [CustomObjectDtoAdapter] class uses reflections to add/remove " +
"content of [extraData] map",
)
@JsonClass(generateAdapter = true)
internal data class DownstreamMemberDto(
val user: DownstreamUserDto,
Expand All @@ -49,4 +68,6 @@ internal data class DownstreamMemberDto(
val notifications_muted: Boolean?,
val status: String?,
val ban_expires: Date?,
)

val extraData: Map<String, Any>,
) : ExtraDataDto
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright (c) 2014-2024 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.model.requests

import com.squareup.moshi.JsonClass

/**
* Used to form a partial member update request.
*
* @param set Map of key-value pairs to set.
* @param unset List of keys to unset.
*/
@JsonClass(generateAdapter = true)
internal data class UpdateMemberPartialRequest(
val set: Map<String, Any>,
val unset: List<String>,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright (c) 2014-2024 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.model.requests

import com.squareup.moshi.JsonClass
import io.getstream.chat.android.client.api2.model.dto.DownstreamMemberDto

/**
* Response model of the partial member update.
*
* @param channel_member The updated member object.
*/
@JsonClass(generateAdapter = true)
internal data class UpdateMemberPartialResponse(
val channel_member: DownstreamMemberDto,
)
Original file line number Diff line number Diff line change
Expand Up @@ -625,6 +625,22 @@ public class ChannelClient internal constructor(
return client.updateChannelPartial(channelType, channelId, set, unset)
}

/**
* Updates specific fields of custom data for a given member.
*
* @param userId The user id of the member to update.
* @param set The key-value data to be updated in the member data.
* @param unset The list of keys to be removed from the member data.
*/
@CheckResult
public fun partialUpdateMember(
userId: String,
set: Map<String, Any> = emptyMap(),
unset: List<String> = emptyList(),
): Call<Member> {
return client.partialUpdateMember(channelType, channelId, userId, set, unset)
}

/**
* Enables slow mode for the channel. When slow mode is enabled, users can only send a message every
* [cooldownTimeInSeconds] time interval. The [cooldownTimeInSeconds] is specified in seconds, and should be
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ import io.getstream.chat.android.client.parser.ChatParser
import io.getstream.chat.android.client.parser2.adapters.AttachmentDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.DateAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamChannelDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamMemberDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamMessageDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamModerationDetailsDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamReactionDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.DownstreamUserDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.EventAdapterFactory
import io.getstream.chat.android.client.parser2.adapters.ExactDateAdapter
import io.getstream.chat.android.client.parser2.adapters.UpstreamChannelDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.UpstreamMemberDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.UpstreamMessageDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.UpstreamReactionDtoAdapter
import io.getstream.chat.android.client.parser2.adapters.UpstreamUserDtoAdapter
Expand Down Expand Up @@ -67,6 +69,8 @@ internal class MoshiChatParser(
.add(UpstreamReactionDtoAdapter)
.add(DownstreamUserDtoAdapter)
.add(UpstreamUserDtoAdapter)
.add(DownstreamMemberDtoAdapter)
.add(UpstreamMemberDtoAdapter)
.add(FlagRequestAdapterFactory)
.build()
}
Expand Down
Loading

0 comments on commit 09b3b6e

Please sign in to comment.