diff --git a/src/IStreamFeed.cs b/src/IStreamFeed.cs
index 1fc0cd9..3dcf972 100644
--- a/src/IStreamFeed.cs
+++ b/src/IStreamFeed.cs
@@ -28,6 +28,8 @@ public interface IStreamFeed
/// https://getstream.io/activity-feeds/docs/dotnet-csharp/feeds_101/?language=csharp
Task AddActivitiesAsync(IEnumerable activities);
+ Task BatchUpdateActivityToTargetsAsync(List reqs);
+
/// Add a new activity to the feed.
/// https://getstream.io/activity-feeds/docs/dotnet-csharp/feeds_101/?language=csharp
Task AddActivityAsync(Activity activity);
diff --git a/src/Models/UpdateToTargets.cs b/src/Models/UpdateToTargets.cs
new file mode 100644
index 0000000..850a283
--- /dev/null
+++ b/src/Models/UpdateToTargets.cs
@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
+
+namespace Stream.Models
+{
+ public class UpdateToTargetsRequest
+ {
+ [JsonProperty("foreign_id")]
+ public string ForeignID { get; set; }
+
+ [JsonProperty("time")]
+ public string Time { get; set; }
+
+ [JsonProperty("id")]
+ public string Id { get; set; }
+
+ [JsonProperty("new_targets")]
+ public List NewTargets { get; set; }
+
+ [JsonProperty("added_targets")]
+ public List Adds { get; set; }
+
+ [JsonProperty("removed_targets")]
+ public List RemovedTargets { get; set; }
+ }
+}
diff --git a/src/StreamFeed.cs b/src/StreamFeed.cs
index e2693f2..2c804b5 100644
--- a/src/StreamFeed.cs
+++ b/src/StreamFeed.cs
@@ -127,6 +127,24 @@ public async Task> GetActivitiesAsync(int offset =
throw StreamException.FromResponse(response);
}
+ public async Task BatchUpdateActivityToTargetsAsync(List reqs)
+ {
+ var endpoint = $"feed_targets/{_feedSlug}/{_userId}/activity_to_targets/";
+
+ var request = _client.BuildAppRequest(endpoint, HttpMethod.Post);
+ request.SetJsonBody(StreamJsonConverter.SerializeObject(reqs));
+ var response = await _client.MakeRequestAsync(request);
+ if (response.StatusCode != HttpStatusCode.Created)
+ {
+ throw new HttpRequestException($"Request failed with status code {response.StatusCode}");
+ }
+
+ if (response.StatusCode == HttpStatusCode.Created)
+ return StreamJsonConverter.DeserializeObject(response.Content);
+
+ throw StreamException.FromResponse(response);
+ }
+
public async Task UpdateActivityToTargetsAsync(string id,
IEnumerable adds = null,
IEnumerable newTargets = null,
@@ -136,6 +154,8 @@ public async Task UpdateActivityToTargetsAsync(string i
newTargets?.ForEach(FeedIdValidator.ThrowIfFeedIdIsInvalid);
removed?.ForEach(FeedIdValidator.ThrowIfFeedIdIsInvalid);
+
+
var payload = new
{
id = id,
diff --git a/tests/ActivityTests/UpdateActivityTests.cs b/tests/ActivityTests/UpdateActivityTests.cs
index 1671e6c..ff3239c 100644
--- a/tests/ActivityTests/UpdateActivityTests.cs
+++ b/tests/ActivityTests/UpdateActivityTests.cs
@@ -164,6 +164,96 @@ public async Task TestUpdateToTargets()
Assert.AreEqual(2, updatedAct.To.ToList().FindAll(t => newOnes.Contains(t)).Count);
}
+ [Test]
+ public async Task TestBatchUpdateToTargets()
+ {
+ var fidTime = new ForeignIdTime(Guid.NewGuid().ToString(), DateTime.UtcNow);
+
+ var targets = new List()
+ {
+ "flat:" + Guid.NewGuid().ToString(),
+ "user:" + Guid.NewGuid().ToString(),
+ };
+
+ var act = new Activity("upd", "test", "1")
+ {
+ ForeignId = fidTime.ForeignId,
+ Time = fidTime.Time,
+ To = targets,
+ };
+
+ var insertedAct = await this.UserFeed.AddActivityAsync(act);
+ Assert.AreEqual(2, insertedAct.To.Count);
+
+ // add 1
+ var add = "user:" + Guid.NewGuid().ToString();
+ var updateReqs = new List
+ {
+ new UpdateToTargetsRequest
+ {
+ Id = insertedAct.Id,
+ Adds = new List { add },
+ }
+ };
+ var updateResp = await this.UserFeed.BatchUpdateActivityToTargetsAsync(updateReqs);
+ Assert.AreEqual(insertedAct.Id, updateResp.Activity.Id);
+ Assert.AreEqual(1, updateResp.Added.Count);
+ Assert.AreEqual(add, updateResp.Added[0]);
+ Assert.AreEqual(3, updateResp.Activity.To.Count);
+ Assert.IsNotNull(updateResp.Activity.To.ToList().Find(t => t == add));
+
+ var updatedAct = (await this.UserFeed.GetActivitiesAsync(0, 1, FeedFilter.Where().IdLessThanEqual(insertedAct.Id))).Results.FirstOrDefault();
+ Assert.NotNull(updatedAct);
+ Assert.AreEqual(3, updatedAct.To.Count);
+ Assert.IsNotNull(updatedAct.To.ToList().Find(t => t == add));
+
+ // remove 1
+ var remove = targets[0];
+ updateReqs = new List
+ {
+ new UpdateToTargetsRequest
+ {
+ Id = insertedAct.Id,
+ RemovedTargets = new List { remove },
+ }
+ };
+ updateResp = await this.UserFeed.BatchUpdateActivityToTargetsAsync(updateReqs);
+ Assert.AreEqual(insertedAct.Id, updateResp.Activity.Id);
+ Assert.AreEqual(1, updateResp.Removed.Count);
+ Assert.AreEqual(remove, updateResp.Removed[0]);
+ Assert.AreEqual(2, updateResp.Activity.To.Count);
+ Assert.IsNull(updateResp.Activity.To.ToList().Find(t => t == remove));
+
+ updatedAct = (await this.UserFeed.GetActivitiesAsync(0, 1, FeedFilter.Where().IdLessThanEqual(insertedAct.Id))).Results.FirstOrDefault();
+ Assert.NotNull(updatedAct);
+ Assert.AreEqual(2, updatedAct.To.Count);
+ Assert.IsNull(updatedAct.To.ToList().Find(t => t == remove));
+
+ // new ones
+ var newOnes = new List()
+ {
+ "flat:" + Guid.NewGuid().ToString(),
+ "user:" + Guid.NewGuid().ToString(),
+ };
+ updateReqs = new List
+ {
+ new UpdateToTargetsRequest
+ {
+ Id = insertedAct.Id,
+ NewTargets = newOnes,
+ }
+ };
+ updateResp = await this.UserFeed.BatchUpdateActivityToTargetsAsync(updateReqs);
+ Assert.AreEqual(insertedAct.Id, updateResp.Activity.Id);
+ Assert.AreEqual(2, updateResp.Activity.To.Count);
+ Assert.AreEqual(2, updateResp.Added.Count);
+ Assert.AreEqual(2, updateResp.Added.ToList().FindAll(t => newOnes.Contains(t)).Count);
+ updatedAct = (await this.UserFeed.GetActivitiesAsync(0, 1, FeedFilter.Where().IdLessThanEqual(insertedAct.Id))).Results.FirstOrDefault();
+ Assert.NotNull(updatedAct);
+ Assert.AreEqual(2, updatedAct.To.Count);
+ Assert.AreEqual(2, updatedAct.To.ToList().FindAll(t => newOnes.Contains(t)).Count);
+ }
+
[Test]
public async Task TestActivityPartialUpdateByForeignIDTime()
{
diff --git a/tests/ClientTests.cs b/tests/ClientTests.cs
index 855fde2..90ed6f1 100644
--- a/tests/ClientTests.cs
+++ b/tests/ClientTests.cs
@@ -115,7 +115,24 @@ public void TestToken()
Assert.AreEqual(true, (bool)result["testing"]);
Assert.False(result.ContainsKey("missing"));
}
+
+ [Test]
+ public void TestModerationToken()
+ {
+ var result = DecodeJwt(Client.CreateUserToken("user"));
+ Assert.AreEqual("user", (string)result["user_id"]);
+
+ var extra = new Dictionary()
+ {
+ { "client", "dotnet" },
+ { "required_moderation_template", "mod_template_1" },
+ };
+ result = DecodeJwt(Client.CreateUserToken("user2", extra));
+ Assert.AreEqual("mod_template_1", (string)result["required_moderation_template"]);
+ Assert.False(result.ContainsKey("missing"));
+ }
+
private Dictionary DecodeJwt(string token)
{
var segment = token.Split('.')[1];