Skip to content

Commit 04a6508

Browse files
authored
Implement IStreamChannel.UpdateOverwriteAsync + add unit tests (#129)
1 parent fdb2d9b commit 04a6508

12 files changed

Lines changed: 418 additions & 18 deletions

Assets/Plugins/StreamChat/Core/InternalDTO/Requests/ChannelRequestInternalDTO.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ internal partial class ChannelRequestInternalDTO
4747
public System.Collections.Generic.List<ChannelMemberRequestInternalDTO> Members { get; set; }
4848

4949
[Newtonsoft.Json.JsonProperty("own_capabilities", Required = Newtonsoft.Json.Required.Default, NullValueHandling = Newtonsoft.Json.NullValueHandling.Ignore)]
50-
public System.Collections.Generic.List<int> OwnCapabilities { get; set; }
50+
public System.Collections.Generic.List<string> OwnCapabilities { get; set; }
5151

5252
/// <summary>
5353
/// Team the channel belongs to (if multi-tenant mode is enabled)

Assets/Plugins/StreamChat/Core/LowLevelClient/Requests/ChannelRequest.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ public partial class ChannelRequest : RequestObjectBase, ISavableTo<ChannelReque
2828

2929
public System.Collections.Generic.List<ChannelMemberRequest> Members { get; set; }
3030

31-
public System.Collections.Generic.List<int> OwnCapabilities { get; set; }
31+
public System.Collections.Generic.List<string> OwnCapabilities { get; set; }
3232

3333
/// <summary>
3434
/// Team the channel belongs to (if multi-tenant mode is enabled)
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using System;
2+
using StreamChat.Core.InternalDTO.Requests;
3+
using StreamChat.Core.LowLevelClient;
4+
5+
namespace StreamChat.Core.Requests
6+
{
7+
public sealed class StreamChannelMemberRequest : ISavableTo<ChannelMemberRequestInternalDTO>
8+
{
9+
/// <summary>
10+
/// Expiration date of the ban
11+
/// </summary>
12+
public DateTimeOffset? BanExpires { get; set; }
13+
14+
/// <summary>
15+
/// Whether member is banned this channel or not
16+
/// </summary>
17+
public bool? Banned { get; set; }
18+
19+
/// <summary>
20+
/// Role of the member in the channel
21+
/// </summary>
22+
public string ChannelRole { get; set; }
23+
24+
/// <summary>
25+
/// Whether member is channel moderator or not
26+
/// </summary>
27+
public bool? IsModerator { get; set; }
28+
29+
/// <summary>
30+
/// Whether member is shadow banned in this channel or not
31+
/// </summary>
32+
public bool? ShadowBanned { get; set; }
33+
34+
ChannelMemberRequestInternalDTO ISavableTo<ChannelMemberRequestInternalDTO>.SaveToDto()
35+
=> new ChannelMemberRequestInternalDTO
36+
{
37+
BanExpires = BanExpires,
38+
Banned = Banned,
39+
ChannelRole = ChannelRole,
40+
IsModerator = IsModerator,
41+
ShadowBanned = ShadowBanned,
42+
};
43+
}
44+
}

Assets/Plugins/StreamChat/Core/Requests/StreamChannelMemberRequest.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Plugins/StreamChat/Core/Requests/StreamCustomDataRequest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ public StreamCustomDataRequest(IDictionary<string, object> internalDictionary)
3434
InternalCustomData = internalDictionary ?? throw new ArgumentNullException(nameof(internalDictionary));
3535
}
3636

37+
public StreamCustomDataRequest(IStreamCustomData customData) : this()
38+
{
39+
foreach (var key in customData.Keys)
40+
{
41+
Add(key, customData.Get<object>(key));
42+
}
43+
}
44+
3745
public void Add(string key, object value) => InternalCustomData.Add(key, value);
3846

3947
public IEnumerator GetEnumerator() => InternalCustomData.GetEnumerator();
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using StreamChat.Core.Helpers;
4+
using StreamChat.Core.InternalDTO.Requests;
5+
using StreamChat.Core.LowLevelClient;
6+
using StreamChat.Core.StatefulModels;
7+
8+
namespace StreamChat.Core.Requests
9+
{
10+
public sealed class StreamUpdateOverwriteChannelRequest: ISavableTo<UpdateChannelRequestInternalDTO>
11+
{
12+
/// <summary>
13+
/// Set to `true` to accept the invite
14+
/// </summary>
15+
public bool? AcceptInvite { get; set; }
16+
17+
/// <summary>
18+
/// List of user IDs to add to the channel
19+
/// </summary>
20+
public List<StreamChannelMemberRequest> AddMembers { get; set; }
21+
22+
/// <summary>
23+
/// List of user IDs to make channel moderators
24+
/// </summary>
25+
public List<string> AddModerators { get; set; }
26+
27+
/// <summary>
28+
/// List of channel member role assignments. If any specified user is not part of the channel, the request will fail
29+
/// </summary>
30+
public List<StreamChannelMemberRequest> AssignRoles { get; set; }
31+
32+
/// <summary>
33+
/// Sets cool down period for the channel in seconds
34+
/// </summary>
35+
public int? Cooldown { get; set; }
36+
37+
#region ChannelRequest
38+
39+
/// <summary>
40+
/// Enable or disable auto translation
41+
/// </summary>
42+
public bool? AutoTranslationEnabled { get; set; }
43+
44+
/// <summary>
45+
/// Switch auto translation language
46+
/// </summary>
47+
public string AutoTranslationLanguage { get; set; }
48+
49+
public bool? Disabled { get; set; }
50+
51+
/// <summary>
52+
/// Freeze or unfreeze the channel
53+
/// </summary>
54+
public bool? Frozen { get; set; }
55+
56+
public List<string> OwnCapabilities { get; set; }
57+
58+
/// <summary>
59+
/// Team the channel belongs to (if multi-tenant mode is enabled)
60+
/// </summary>
61+
public string Team { get; set; }
62+
63+
public string Name { get; set; }
64+
65+
#endregion
66+
67+
/// <summary>
68+
/// List of user IDs to take away moderators status from
69+
/// </summary>
70+
public List<string> DemoteModerators { get; set; }
71+
72+
/// <summary>
73+
/// Set to `true` to hide channel's history when adding new members
74+
/// </summary>
75+
public bool? HideHistory { get; set; }
76+
77+
/// <summary>
78+
/// List of user IDs to invite to the channel
79+
/// </summary>
80+
public List<StreamChannelMemberRequest> Invites { get; set; }
81+
82+
/// <summary>
83+
/// Message to send to the chat when channel is successfully updated
84+
/// </summary>
85+
public StreamMessageRequest Message { get; set; }
86+
87+
/// <summary>
88+
/// Set to `true` to reject the invite
89+
/// </summary>
90+
public bool? RejectInvite { get; set; }
91+
92+
/// <summary>
93+
/// List of user IDs to remove from the channel
94+
/// </summary>
95+
public List<string> RemoveMembers { get; set; }
96+
97+
/// <summary>
98+
/// When `message` is set disables all push notifications for it
99+
/// </summary>
100+
public bool? SkipPush { get; set; }
101+
102+
/// <summary>
103+
/// Any custom data to associate with this channel. This will be accessible through <see cref="IStreamStatefulModel.CustomData"/>
104+
/// </summary>
105+
public StreamCustomDataRequest CustomData { get; set; }
106+
107+
/// <summary>
108+
/// Create update request from an instance of <see cref="IStreamChannel"/>. This way you copy channel's current state and can only
109+
/// </summary>
110+
/// <param name="channel">Channel to copy data from into the request body</param>
111+
public StreamUpdateOverwriteChannelRequest(IStreamChannel channel)
112+
{
113+
Cooldown = channel.Cooldown;
114+
AutoTranslationEnabled = channel.AutoTranslationEnabled;
115+
AutoTranslationLanguage = channel.AutoTranslationLanguage;
116+
Disabled = channel.Disabled;
117+
Frozen = channel.Frozen;
118+
OwnCapabilities = channel.OwnCapabilities?.ToList();
119+
Team = channel.Team;
120+
Name = channel.Name;
121+
122+
if (channel.CustomData.Count > 0)
123+
{
124+
CustomData = new StreamCustomDataRequest(channel.CustomData);
125+
}
126+
}
127+
128+
/// <summary>
129+
/// Create empty update request.
130+
/// Warning! Any channel data that is present on the server and is not present in this request will be removed.
131+
/// </summary>
132+
public StreamUpdateOverwriteChannelRequest()
133+
{
134+
135+
}
136+
137+
UpdateChannelRequestInternalDTO ISavableTo<UpdateChannelRequestInternalDTO>.SaveToDto()
138+
{
139+
return new UpdateChannelRequestInternalDTO
140+
{
141+
AcceptInvite = AcceptInvite,
142+
AddMembers = AddMembers?.TrySaveToDtoCollection<StreamChannelMemberRequest, ChannelMemberRequestInternalDTO>(),
143+
AddModerators = AddModerators,
144+
AssignRoles = AssignRoles?.TrySaveToDtoCollection<StreamChannelMemberRequest, ChannelMemberRequestInternalDTO>(),
145+
Cooldown = Cooldown,
146+
Data = new ChannelRequestInternalDTO
147+
{
148+
AutoTranslationEnabled = AutoTranslationEnabled,
149+
AutoTranslationLanguage = AutoTranslationLanguage,
150+
Disabled = Disabled,
151+
Frozen = Frozen,
152+
OwnCapabilities = OwnCapabilities,
153+
Team = Team,
154+
AdditionalProperties = CustomData?.ToDictionary(),
155+
Name = Name
156+
},
157+
DemoteModerators = DemoteModerators,
158+
HideHistory = HideHistory,
159+
Invites = Invites?.TrySaveToDtoCollection<StreamChannelMemberRequest, ChannelMemberRequestInternalDTO>(),
160+
Message = Message?.TrySaveToDto(),
161+
RejectInvite = RejectInvite,
162+
RemoveMembers = RemoveMembers,
163+
SkipPush = SkipPush,
164+
};
165+
}
166+
}
167+
}

Assets/Plugins/StreamChat/Core/Requests/StreamUpdateOverwriteChannelRequest.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Assets/Plugins/StreamChat/Core/Requests/StreamUserUpsertRequest.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public sealed class StreamUserUpsertRequest : ISavableTo<UserObjectRequestIntern
5252
public string Image;
5353

5454
/// <summary>
55-
/// Any custom data to associate with this message. This will be accessible through <see cref="IStreamUser.CustomData"/>
55+
/// Any custom data to associate with this message. This will be accessible through <see cref="IStreamMessage.CustomData"/>
5656
/// </summary>
5757
public StreamCustomDataRequest CustomData { get; set; }
5858

@@ -64,7 +64,7 @@ UserObjectRequestInternalDTO ISavableTo<UserObjectRequestInternalDTO>.SaveToDto(
6464
Id = Id,
6565
Invisible = Invisible,
6666
Language = Language,
67-
PushNotifications = PushNotifications.TrySaveToDto(),
67+
PushNotifications = PushNotifications?.TrySaveToDto(),
6868
RevokeTokensIssuedBefore = RevokeTokensIssuedBefore,
6969
Role = Role,
7070
Teams = Teams,

Assets/Plugins/StreamChat/Core/StatefulModels/IStreamChannel.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,6 @@ public interface IStreamChannel : IStreamStatefulModel
200200
/// </summary>
201201
bool Muted { get; }
202202

203-
//StreamTodo: change to enum? this would simplify usage
204203
/// <summary>
205204
/// List of channel capabilities of authenticated user
206205
/// </summary>
@@ -294,7 +293,8 @@ public interface IStreamChannel : IStreamStatefulModel
294293
/// Important! Any data that is present on the channel and not included in a full update will be deleted.
295294
/// If you want to update only some fields of the channel use the <see cref="IStreamChannel.UpdatePartialAsync"/>
296295
/// </summary>
297-
Task UpdateOverwriteAsync(); //StreamTodo: NOT IMPLEMENTED
296+
/// <param name="updateOverwriteRequest"></param>
297+
Task UpdateOverwriteAsync(StreamUpdateOverwriteChannelRequest updateOverwriteRequest);
298298

299299
/// <summary>
300300
/// Update channel in a partial mode. You can selectively set and unset fields of the channel

Assets/Plugins/StreamChat/Core/StatefulModels/StreamChannel.cs

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,16 +221,14 @@ public async Task LoadOlderMessagesAsync()
221221
Cache.TryCreateOrUpdate(response);
222222
}
223223

224-
//StreamTodo: LoadNewerMessages? This would only make sense if we would start somewhere in the history. Maybe its possible with search? You jump in to past message and scroll to load newer
225-
public Task UpdateOverwriteAsync() //StreamTodo: NOT IMPLEMENTED
224+
public async Task UpdateOverwriteAsync(StreamUpdateOverwriteChannelRequest updateOverwriteRequest)
226225
{
227-
throw new NotImplementedException();
228-
// var response = await LowLevelClient.InternalChannelApi.UpdateChannelAsync(Type, Id,
229-
// new UpdateChannelRequestInternalDTO
230-
// {
231-
// });
226+
StreamAsserts.AssertNotNull(updateOverwriteRequest, nameof(updateOverwriteRequest));
227+
228+
var response = await LowLevelClient.InternalChannelApi.UpdateChannelAsync(Type, Id,
229+
updateOverwriteRequest.TrySaveToDto());
232230

233-
//Cache.TryCreateOrUpdate(response.Channel);
231+
Cache.TryCreateOrUpdate(response.Channel);
234232
}
235233

236234
public async Task UpdatePartialAsync(IDictionary<string, object> setFields = null,
@@ -653,7 +651,7 @@ void IUpdateableFrom<UpdateChannelResponseInternalDTO, StreamChannel>.UpdateFrom
653651

654652
#region ChannelState
655653

656-
_members.TryReplaceTrackedObjects(dto.Members, cache.ChannelMembers); //Updated from Channel
654+
_members.TryAppendUniqueTrackedObjects(dto.Members, cache.ChannelMembers);
657655

658656
#endregion
659657
}
@@ -712,7 +710,11 @@ internal void HandleMessageDeletedEvent(MessageDeletedEventInternalDTO dto)
712710

713711
internal void HandleChannelUpdatedEvent(ChannelUpdatedEventInternalDTO eventDto)
714712
{
715-
Cache.TryCreateOrUpdate(eventDto.Channel);
713+
// Skip normal update. Channel Update is an overwrite operation. If something was not present in the request it was removed
714+
// Cache.TryCreateOrUpdate(eventDto.Channel);
715+
716+
UpdateChannelFieldsFromDtoOverwrite(eventDto.Channel, Cache);
717+
716718
Updated?.Invoke(this);
717719
}
718720

@@ -876,6 +878,43 @@ private void UpdateChannelFieldsFromDto(ChannelResponseInternalDTO dto, ICache c
876878

877879
#endregion
878880
}
881+
882+
private void UpdateChannelFieldsFromDtoOverwrite(ChannelResponseInternalDTO dto, ICache cache)
883+
{
884+
#region Channel
885+
886+
AutoTranslationEnabled = GetOrDefault(dto.AutoTranslationEnabled, false);
887+
AutoTranslationLanguage = GetOrDefault(dto.AutoTranslationLanguage, string.Empty);
888+
Cid = GetOrDefault(dto.Cid, Cid);
889+
Config = Config.TryLoadFromDto(dto.Config, cache);
890+
Cooldown = GetOrDefault(dto.Cooldown, null);
891+
CreatedAt = GetOrDefault(dto.CreatedAt, CreatedAt);
892+
CreatedBy = cache.TryCreateOrUpdate(dto.CreatedBy);
893+
DeletedAt = GetOrDefault(dto.DeletedAt, DeletedAt);
894+
Disabled = GetOrDefault(dto.Disabled, false);
895+
Frozen = GetOrDefault(dto.Frozen, false);
896+
Hidden = GetOrDefault(dto.Hidden, false);
897+
HideMessagesBefore = GetOrDefault(dto.HideMessagesBefore, null);
898+
Id = GetOrDefault(dto.Id, Id);
899+
LastMessageAt = GetOrDefault(dto.LastMessageAt, null);
900+
MemberCount = GetOrDefault(dto.MemberCount, MemberCount);
901+
_members.TryAppendUniqueTrackedObjects(dto.Members, cache.ChannelMembers);
902+
MuteExpiresAt = GetOrDefault(dto.MuteExpiresAt, null);
903+
Muted = GetOrDefault(dto.Muted, false);
904+
_ownCapabilities.TryReplaceValuesFromDto(dto.OwnCapabilities);
905+
Team = GetOrDefault(dto.Team, string.Empty);
906+
TruncatedAt = GetOrDefault(dto.TruncatedAt, null);
907+
TruncatedBy = cache.TryCreateOrUpdate(dto.TruncatedBy);
908+
Type = new ChannelType(GetOrDefault(dto.Type, Type));
909+
UpdatedAt = GetOrDefault(dto.UpdatedAt, UpdatedAt);
910+
911+
LoadAdditionalProperties(dto.AdditionalProperties);
912+
913+
//Not in API spec
914+
Name = GetOrDefault(dto.Name, string.Empty);
915+
916+
#endregion
917+
}
879918

880919
internal void InternalHandleMessageReadEvent(MessageReadEventInternalDTO eventDto)
881920
{

0 commit comments

Comments
 (0)