From a17f63cbdab6f234daa1ef0322fd4ded2077aaa3 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Mon, 28 Apr 2025 16:04:44 +0200 Subject: [PATCH 1/5] chore: unfollowMany --- src/BatchOperations.cs | 15 ++++++ src/IBatchOperations.cs | 4 ++ tests/BatchTests.cs | 110 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 129 insertions(+) diff --git a/src/BatchOperations.cs b/src/BatchOperations.cs index a4c84ff..b9b8e6c 100644 --- a/src/BatchOperations.cs +++ b/src/BatchOperations.cs @@ -56,6 +56,21 @@ public async Task FollowManyAsync(IEnumerable follows, int throw StreamException.FromResponse(response); } + public async Task UnfollowManyAsync(IEnumerable follows, bool keepHistory = false) + { + var request = _client.BuildAppRequest("unfollow_many/", HttpMethod.Post); + + request.AddQueryParameter("keep_history", keepHistory.ToString().ToLower()); + request.SetJsonBody(StreamJsonConverter.SerializeObject(follows)); + + var response = await _client.MakeRequestAsync(request); + + if (response.StatusCode == HttpStatusCode.Created || response.StatusCode == HttpStatusCode.OK) + return StreamJsonConverter.DeserializeObject(response.Content); + + throw StreamException.FromResponse(response); + } + public async Task> GetActivitiesByIdAsync(IEnumerable ids) => await GetActivitiesAsync(ids, null); diff --git a/src/IBatchOperations.cs b/src/IBatchOperations.cs index 8e4454d..ef4e6a1 100644 --- a/src/IBatchOperations.cs +++ b/src/IBatchOperations.cs @@ -22,6 +22,10 @@ public interface IBatchOperations /// https://getstream.io/activity-feeds/docs/dotnet-csharp/add_many_activities/?language=csharp Task FollowManyAsync(IEnumerable follows, int activityCopyLimit = 100); + /// Unfollow multiple feeds in a single request. + /// https://getstream.io/activity-feeds/docs/dotnet-csharp/add_many_activities/?language=csharp + Task UnfollowManyAsync(IEnumerable follows, bool keepHistory = false); + /// Get multiple activities by activity ids. /// https://getstream.io/activity-feeds/docs/dotnet-csharp/add_many_activities/?language=csharp Task> GetActivitiesByIdAsync(IEnumerable ids); diff --git a/tests/BatchTests.cs b/tests/BatchTests.cs index 0aff891..266466d 100644 --- a/tests/BatchTests.cs +++ b/tests/BatchTests.cs @@ -10,6 +10,28 @@ namespace StreamNetTests [TestFixture] public class BatchTests : TestBase { + [Test] + public void TestUnfollowManyArgumentValidation() + { + // Should work with empty array + Assert.DoesNotThrowAsync(async () => + { + await Client.Batch.UnfollowManyAsync(new Follow[] { }); + }); + + // Should work with valid follow + Assert.DoesNotThrowAsync(async () => + { + await Client.Batch.UnfollowManyAsync(new[] { new Follow("user:1", "user:2") }, false); + }); + + // Should work with keepHistory true + Assert.DoesNotThrowAsync(async () => + { + await Client.Batch.UnfollowManyAsync(new[] { new Follow("user:1", "user:2") }, true); + }); + } + [Test] public void TestGetEnrichedActivitiesArgumentValidation() { @@ -305,5 +327,93 @@ public async Task TestBatchActivityForeignIdTime() Assert.AreEqual(1, result.Results.Count); } + + [Test] + public async Task TestBatchUnfollowManyKeepHistoryFalse() + { + // First set up follows and add activities + await Client.Batch.FollowManyAsync(new[] + { + new Follow(UserFeed, FlatFeed), + new Follow(UserFeed2, FlatFeed), + }); + + var newActivity = new Activity("1", "test", "1"); + var response = await this.FlatFeed.AddActivityAsync(newActivity); + + // Verify follows are working + var activities1 = (await this.UserFeed.GetActivitiesAsync(0, 1)).Results; + var activities2 = (await this.UserFeed2.GetActivitiesAsync(0, 1)).Results; + + Assert.IsNotNull(activities1); + Assert.AreEqual(1, activities1.Count()); + Assert.AreEqual(response.Id, activities1.First().Id); + + Assert.IsNotNull(activities2); + Assert.AreEqual(1, activities2.Count()); + Assert.AreEqual(response.Id, activities2.First().Id); + + // Use UnfollowMany with keepHistory=false + await Client.Batch.UnfollowManyAsync(new[] + { + new Follow(UserFeed, FlatFeed), + new Follow(UserFeed2, FlatFeed), + }, keepHistory: false); + + // Verify activities are removed + activities1 = (await this.UserFeed.GetActivitiesAsync(0, 1)).Results; + activities2 = (await this.UserFeed2.GetActivitiesAsync(0, 1)).Results; + + Assert.IsNotNull(activities1); + Assert.AreEqual(0, activities1.Count()); + + Assert.IsNotNull(activities2); + Assert.AreEqual(0, activities2.Count()); + } + + [Test] + public async Task TestBatchUnfollowManyKeepHistoryTrue() + { + // First set up follows and add activities + await Client.Batch.FollowManyAsync(new[] + { + new Follow(UserFeed, FlatFeed), + new Follow(UserFeed2, FlatFeed), + }); + + var newActivity = new Activity("1", "test", "1"); + var response = await this.FlatFeed.AddActivityAsync(newActivity); + + // Verify follows are working + var activities1 = (await this.UserFeed.GetActivitiesAsync(0, 1)).Results; + var activities2 = (await this.UserFeed2.GetActivitiesAsync(0, 1)).Results; + + Assert.IsNotNull(activities1); + Assert.AreEqual(1, activities1.Count()); + Assert.AreEqual(response.Id, activities1.First().Id); + + Assert.IsNotNull(activities2); + Assert.AreEqual(1, activities2.Count()); + Assert.AreEqual(response.Id, activities2.First().Id); + + // Use UnfollowMany with keepHistory=true + await Client.Batch.UnfollowManyAsync(new[] + { + new Follow(UserFeed, FlatFeed), + new Follow(UserFeed2, FlatFeed), + }, keepHistory: true); + + // Verify activities are retained + activities1 = (await this.UserFeed.GetActivitiesAsync(0, 1)).Results; + activities2 = (await this.UserFeed2.GetActivitiesAsync(0, 1)).Results; + + Assert.IsNotNull(activities1); + Assert.AreEqual(1, activities1.Count()); + Assert.AreEqual(response.Id, activities1.First().Id); + + Assert.IsNotNull(activities2); + Assert.AreEqual(1, activities2.Count()); + Assert.AreEqual(response.Id, activities2.First().Id); + } } } From ef64fddda45b3a9cb0af8cb6a45960f105a71ba8 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Mon, 28 Apr 2025 16:21:40 +0200 Subject: [PATCH 2/5] chore: unfollowMany --- src/BatchOperations.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/BatchOperations.cs b/src/BatchOperations.cs index b9b8e6c..83b78d0 100644 --- a/src/BatchOperations.cs +++ b/src/BatchOperations.cs @@ -60,8 +60,15 @@ public async Task UnfollowManyAsync(IEnumerable follows, b { var request = _client.BuildAppRequest("unfollow_many/", HttpMethod.Post); - request.AddQueryParameter("keep_history", keepHistory.ToString().ToLower()); - request.SetJsonBody(StreamJsonConverter.SerializeObject(follows)); + // Create a new anonymous object array with the properties expected by the API + var unfollowRequests = follows.Select(f => new + { + source = f.Source, + target = f.Target, + keep_history = keepHistory + }); + + request.SetJsonBody(StreamJsonConverter.SerializeObject(unfollowRequests)); var response = await _client.MakeRequestAsync(request); From 1d648221ea1e7725b391b0259ee64acc5aa4aa75 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Mon, 28 Apr 2025 16:33:37 +0200 Subject: [PATCH 3/5] chore: unfollowMany --- src/BatchOperations.cs | 6 +++--- src/IBatchOperations.cs | 6 +++--- tests/BatchTests.cs | 22 +++++++++++----------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/BatchOperations.cs b/src/BatchOperations.cs index 83b78d0..61b096c 100644 --- a/src/BatchOperations.cs +++ b/src/BatchOperations.cs @@ -56,16 +56,16 @@ public async Task FollowManyAsync(IEnumerable follows, int throw StreamException.FromResponse(response); } - public async Task UnfollowManyAsync(IEnumerable follows, bool keepHistory = false) + public async Task UnfollowManyAsync(IEnumerable unfollows) { var request = _client.BuildAppRequest("unfollow_many/", HttpMethod.Post); // Create a new anonymous object array with the properties expected by the API - var unfollowRequests = follows.Select(f => new + var unfollowRequests = unfollows.Select(f => new { source = f.Source, target = f.Target, - keep_history = keepHistory + keep_history = f.KeepHistory }); request.SetJsonBody(StreamJsonConverter.SerializeObject(unfollowRequests)); diff --git a/src/IBatchOperations.cs b/src/IBatchOperations.cs index ef4e6a1..74b333a 100644 --- a/src/IBatchOperations.cs +++ b/src/IBatchOperations.cs @@ -21,10 +21,10 @@ public interface IBatchOperations /// Follow muiltiple feeds. /// https://getstream.io/activity-feeds/docs/dotnet-csharp/add_many_activities/?language=csharp Task FollowManyAsync(IEnumerable follows, int activityCopyLimit = 100); - - /// Unfollow multiple feeds in a single request. + + /// Unfollow multiple feeds in a single request using UnfollowRelation objects. /// https://getstream.io/activity-feeds/docs/dotnet-csharp/add_many_activities/?language=csharp - Task UnfollowManyAsync(IEnumerable follows, bool keepHistory = false); + Task UnfollowManyAsync(IEnumerable unfollows); /// Get multiple activities by activity ids. /// https://getstream.io/activity-feeds/docs/dotnet-csharp/add_many_activities/?language=csharp diff --git a/tests/BatchTests.cs b/tests/BatchTests.cs index 266466d..44895eb 100644 --- a/tests/BatchTests.cs +++ b/tests/BatchTests.cs @@ -16,19 +16,19 @@ public void TestUnfollowManyArgumentValidation() // Should work with empty array Assert.DoesNotThrowAsync(async () => { - await Client.Batch.UnfollowManyAsync(new Follow[] { }); + await Client.Batch.UnfollowManyAsync(new UnfollowRelation[] { }); }); - - // Should work with valid follow + + // Should work with valid unfollow relation objects Assert.DoesNotThrowAsync(async () => { - await Client.Batch.UnfollowManyAsync(new[] { new Follow("user:1", "user:2") }, false); + await Client.Batch.UnfollowManyAsync(new[] { new UnfollowRelation("user:1", "user:2", false) }); }); // Should work with keepHistory true Assert.DoesNotThrowAsync(async () => { - await Client.Batch.UnfollowManyAsync(new[] { new Follow("user:1", "user:2") }, true); + await Client.Batch.UnfollowManyAsync(new[] { new UnfollowRelation("user:1", "user:2", true) }); }); } @@ -356,9 +356,9 @@ await Client.Batch.FollowManyAsync(new[] // Use UnfollowMany with keepHistory=false await Client.Batch.UnfollowManyAsync(new[] { - new Follow(UserFeed, FlatFeed), - new Follow(UserFeed2, FlatFeed), - }, keepHistory: false); + new UnfollowRelation(UserFeed, FlatFeed, false), + new UnfollowRelation(UserFeed2, FlatFeed, false), + }); // Verify activities are removed activities1 = (await this.UserFeed.GetActivitiesAsync(0, 1)).Results; @@ -399,9 +399,9 @@ await Client.Batch.FollowManyAsync(new[] // Use UnfollowMany with keepHistory=true await Client.Batch.UnfollowManyAsync(new[] { - new Follow(UserFeed, FlatFeed), - new Follow(UserFeed2, FlatFeed), - }, keepHistory: true); + new UnfollowRelation(UserFeed, FlatFeed, true), + new UnfollowRelation(UserFeed2, FlatFeed, true), + }); // Verify activities are retained activities1 = (await this.UserFeed.GetActivitiesAsync(0, 1)).Results; From 872703727306744b6ffcdb36b8d8bd24043c6f8f Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Mon, 28 Apr 2025 16:34:39 +0200 Subject: [PATCH 4/5] chore: unfollowMany --- src/Models/UnfollowRelation.cs | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 src/Models/UnfollowRelation.cs diff --git a/src/Models/UnfollowRelation.cs b/src/Models/UnfollowRelation.cs new file mode 100644 index 0000000..7933d7d --- /dev/null +++ b/src/Models/UnfollowRelation.cs @@ -0,0 +1,49 @@ +namespace Stream.Models +{ + /// + /// Represents a relationship to unfollow in batch operations + /// + public class UnfollowRelation + { + /// + /// Source feed id + /// + public string Source { get; set; } + + /// + /// Target feed id + /// + public string Target { get; set; } + + /// + /// Whether to keep activities from the unfollowed feed + /// + public bool KeepHistory { get; set; } + + /// + /// Creates a new instance of the UnfollowRelation class + /// + /// Source feed id + /// Target feed id + /// Whether to keep activities from the unfollowed feed + public UnfollowRelation(string source, string target, bool keepHistory = false) + { + Source = source; + Target = target; + KeepHistory = keepHistory; + } + + /// + /// Creates a new instance of the UnfollowRelation class + /// + /// Source feed + /// Target feed + /// Whether to keep activities from the unfollowed feed + public UnfollowRelation(IStreamFeed source, IStreamFeed target, bool keepHistory = false) + { + Source = source.FeedId; + Target = target.FeedId; + KeepHistory = keepHistory; + } + } +} \ No newline at end of file From 67f1e3ae0ab4d409ebfb2f041597452e0f0babb6 Mon Sep 17 00:00:00 2001 From: Aditya Agarwal Date: Mon, 28 Apr 2025 16:46:16 +0200 Subject: [PATCH 5/5] chore: fix old unrelated FlagActivity test --- tests/ModerationTests.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/ModerationTests.cs b/tests/ModerationTests.cs index 6ac4b22..479c440 100644 --- a/tests/ModerationTests.cs +++ b/tests/ModerationTests.cs @@ -98,6 +98,13 @@ public async Task TestFlagActivity() newActivity.SetData("stringint", "42"); newActivity.SetData("stringdouble", "42.2"); newActivity.SetData("stringcomplex", "{ \"test1\": 1, \"test2\": \"testing\" }"); + + // Set moderation data with origin_feed + var moderationData = new Dictionary + { + { "origin_feed", this.UserFeed.FeedId } + }; + newActivity.SetData("moderation", moderationData); var response = await this.UserFeed.AddActivityAsync(newActivity); Assert.IsNotNull(response);