From f656c31b0af1ac0e3c4bc9e9fffdad120b658d3d Mon Sep 17 00:00:00 2001 From: Lennart Kuijs Date: Sat, 17 Jan 2026 14:06:42 +0100 Subject: [PATCH 1/4] feat: [CHA-1699] add Future Channel Bans support - Add banFromFutureChannels field to UserBanRequestData - Add removeFutureChannelsBan method and parameter to UserUnbanRequest - Add FutureChannelBan class for query response - Add UserQueryFutureChannelBansRequest and UserQueryFutureChannelBansResponse - Add queryFutureChannelBans factory method and service endpoint --- .../io/getstream/chat/java/models/User.java | 89 ++++++++++++++++++- .../chat/java/services/UserService.java | 7 +- 2 files changed, 94 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/getstream/chat/java/models/User.java b/src/main/java/io/getstream/chat/java/models/User.java index 622db2c48..7a27ff1d3 100644 --- a/src/main/java/io/getstream/chat/java/models/User.java +++ b/src/main/java/io/getstream/chat/java/models/User.java @@ -15,6 +15,7 @@ import io.getstream.chat.java.models.User.UserMuteRequestData.UserMuteRequest; import io.getstream.chat.java.models.User.UserPartialUpdateRequestData.UserPartialUpdateRequest; import io.getstream.chat.java.models.User.UserQueryBannedRequestData.UserQueryBannedRequest; +import io.getstream.chat.java.models.User.UserQueryFutureChannelBansRequestData.UserQueryFutureChannelBansRequest; import io.getstream.chat.java.models.User.UserReactivateRequestData.UserReactivateRequest; import io.getstream.chat.java.models.User.UserUnmuteRequestData.UserUnmuteRequest; import io.getstream.chat.java.models.User.UserUpsertRequestData.UserUpsertRequest; @@ -214,6 +215,30 @@ public static class Ban { private Date createdAt; } + @Data + @NoArgsConstructor + public static class FutureChannelBan { + @Nullable + @JsonProperty("user") + private User user; + + @Nullable + @JsonProperty("expires") + private Date expires; + + @Nullable + @JsonProperty("reason") + private String reason; + + @Nullable + @JsonProperty("shadow") + private Boolean shadow; + + @NotNull + @JsonProperty("created_at") + private Date createdAt; + } + @Data @NoArgsConstructor public static class OwnUser { @@ -799,6 +824,10 @@ public static class UserBanRequestData { @JsonProperty("user") private UserRequestObject user; + @Nullable + @JsonProperty("ban_from_future_channels") + private Boolean banFromFutureChannels; + public static class UserBanRequest extends StreamRequest { @Override protected Call generateCall(Client client) { @@ -1078,6 +1107,8 @@ public static class UserUnbanRequest extends StreamRequest @Nullable private Boolean shadow; + @Nullable private Boolean removeFutureChannelsBan; + @NotNull public UserUnbanRequest type(@NotNull String type) { this.type = type; @@ -1096,9 +1127,15 @@ public UserUnbanRequest shadow(@NotNull Boolean shadow) { return this; } + @NotNull + public UserUnbanRequest removeFutureChannelsBan(@NotNull Boolean removeFutureChannelsBan) { + this.removeFutureChannelsBan = removeFutureChannelsBan; + return this; + } + @Override protected Call generateCall(Client client) { - return client.create(UserService.class).unban(targetUserId, type, id, shadow); + return client.create(UserService.class).unban(targetUserId, type, id, shadow, removeFutureChannelsBan); } } @@ -1171,6 +1208,46 @@ public static class UserQueryBannedResponse extends StreamResponseObject { private List bans; } + @Builder( + builderClassName = "UserQueryFutureChannelBansRequest", + builderMethodName = "", + buildMethodName = "internalBuild") + @Getter + @EqualsAndHashCode + public static class UserQueryFutureChannelBansRequestData { + @Nullable + @JsonProperty("user_id") + private String userId; + + @Nullable + @JsonProperty("exclude_expired_bans") + private Boolean excludeExpiredBans; + + @Nullable + @JsonProperty("limit") + private Integer limit; + + @Nullable + @JsonProperty("offset") + private Integer offset; + + public static class UserQueryFutureChannelBansRequest extends StreamRequest { + @Override + protected Call generateCall(Client client) { + return client.create(UserService.class).queryFutureChannelBans(this.internalBuild()); + } + } + } + + @Data + @NoArgsConstructor + @EqualsAndHashCode(callSuper = true) + public static class UserQueryFutureChannelBansResponse extends StreamResponseObject { + @NotNull + @JsonProperty("bans") + private List bans; + } + @Data @NoArgsConstructor @EqualsAndHashCode(callSuper = true) @@ -1305,6 +1382,16 @@ public static UserQueryBannedRequest queryBanned() { return new UserQueryBannedRequest(); } + /** + * Creates a query future channel bans request + * + * @return the created request + */ + @NotNull + public static UserQueryFutureChannelBansRequest queryFutureChannelBans() { + return new UserQueryFutureChannelBansRequest(); + } + /** * Creates a deactivate request * diff --git a/src/main/java/io/getstream/chat/java/services/UserService.java b/src/main/java/io/getstream/chat/java/services/UserService.java index d6032e30e..e01d67258 100644 --- a/src/main/java/io/getstream/chat/java/services/UserService.java +++ b/src/main/java/io/getstream/chat/java/services/UserService.java @@ -65,5 +65,10 @@ Call unban( @NotNull @Query("target_user_id") String targetUserId, @Nullable @Query("type") String channelType, @Nullable @Query("id") String channelId, - @Nullable @Query("shadow") Boolean shadow); + @Nullable @Query("shadow") Boolean shadow, + @Nullable @Query("remove_future_channels_ban") Boolean removeFutureChannelsBan); + + @GET("query_future_channel_bans") + Call queryFutureChannelBans( + @NotNull @ToJson @Query("payload") UserQueryFutureChannelBansRequestData requestData); } From 8af27e75b1c510e9c360421db876c8c228a265da Mon Sep 17 00:00:00 2001 From: Lennart Kuijs Date: Sat, 17 Jan 2026 16:14:00 +0100 Subject: [PATCH 2/4] fix: apply spotless formatting Co-Authored-By: Claude Opus 4.5 --- src/main/java/io/getstream/chat/java/models/User.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/io/getstream/chat/java/models/User.java b/src/main/java/io/getstream/chat/java/models/User.java index 7a27ff1d3..27fa608ed 100644 --- a/src/main/java/io/getstream/chat/java/models/User.java +++ b/src/main/java/io/getstream/chat/java/models/User.java @@ -1135,7 +1135,9 @@ public UserUnbanRequest removeFutureChannelsBan(@NotNull Boolean removeFutureCha @Override protected Call generateCall(Client client) { - return client.create(UserService.class).unban(targetUserId, type, id, shadow, removeFutureChannelsBan); + return client + .create(UserService.class) + .unban(targetUserId, type, id, shadow, removeFutureChannelsBan); } } @@ -1231,7 +1233,8 @@ public static class UserQueryFutureChannelBansRequestData { @JsonProperty("offset") private Integer offset; - public static class UserQueryFutureChannelBansRequest extends StreamRequest { + public static class UserQueryFutureChannelBansRequest + extends StreamRequest { @Override protected Call generateCall(Client client) { return client.create(UserService.class).queryFutureChannelBans(this.internalBuild()); From 1fb134db951c5bb548104a6ebd8898e6f1aba901 Mon Sep 17 00:00:00 2001 From: Lennart Kuijs Date: Tue, 20 Jan 2026 11:21:24 +0100 Subject: [PATCH 3/4] feat: add targetUserId to UserQueryFutureChannelBansRequestData Add target_user_id parameter to allow filtering future channel bans by target user, especially for client-side requests. Co-Authored-By: Claude Opus 4.5 --- src/main/java/io/getstream/chat/java/models/User.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/io/getstream/chat/java/models/User.java b/src/main/java/io/getstream/chat/java/models/User.java index 27fa608ed..c4ee1eebb 100644 --- a/src/main/java/io/getstream/chat/java/models/User.java +++ b/src/main/java/io/getstream/chat/java/models/User.java @@ -1221,6 +1221,10 @@ public static class UserQueryFutureChannelBansRequestData { @JsonProperty("user_id") private String userId; + @Nullable + @JsonProperty("target_user_id") + private String targetUserId; + @Nullable @JsonProperty("exclude_expired_bans") private Boolean excludeExpiredBans; From 3803fb4f2410ef63a8bc209bfe8dc89a1381088e Mon Sep 17 00:00:00 2001 From: Lennart Kuijs Date: Tue, 20 Jan 2026 11:30:27 +0100 Subject: [PATCH 4/4] test: add QueryFutureChannelBans test with target_user_id filter Test the new targetUserId parameter for filtering future channel bans. Co-Authored-By: Claude Opus 4.5 --- .../java/io/getstream/chat/java/UserTest.java | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/test/java/io/getstream/chat/java/UserTest.java b/src/test/java/io/getstream/chat/java/UserTest.java index afc61d5c6..d4db33f20 100644 --- a/src/test/java/io/getstream/chat/java/UserTest.java +++ b/src/test/java/io/getstream/chat/java/UserTest.java @@ -506,4 +506,64 @@ void whenGeneratingUserToken_thenNoException() { Assertions.assertEquals(197, token.length()); } + + @DisplayName("Can query future channel bans with target_user_id filter") + @Test + void whenQueryingFutureChannelBansWithTargetUserId_thenFiltersCorrectly() { + String creatorId = RandomStringUtils.randomAlphabetic(10); + String targetId1 = RandomStringUtils.randomAlphabetic(10); + String targetId2 = RandomStringUtils.randomAlphabetic(10); + + // Create users + UserUpsertRequest usersUpsertRequest = User.upsert(); + usersUpsertRequest.user(UserRequestObject.builder().id(creatorId).name("Creator").build()); + usersUpsertRequest.user(UserRequestObject.builder().id(targetId1).name("Target 1").build()); + usersUpsertRequest.user(UserRequestObject.builder().id(targetId2).name("Target 2").build()); + Assertions.assertDoesNotThrow(() -> usersUpsertRequest.request()); + + // Ban both targets from future channels created by creator + Assertions.assertDoesNotThrow( + () -> + User.ban() + .userId(creatorId) + .targetUserId(targetId1) + .banFromFutureChannels(true) + .reason("test ban 1") + .request()); + + Assertions.assertDoesNotThrow( + () -> + User.ban() + .userId(creatorId) + .targetUserId(targetId2) + .banFromFutureChannels(true) + .reason("test ban 2") + .request()); + + // Query with target_user_id filter - should only return the specific target + var response = + Assertions.assertDoesNotThrow( + () -> User.queryFutureChannelBans().userId(creatorId).targetUserId(targetId1).request()); + Assertions.assertEquals(1, response.getBans().size()); + Assertions.assertEquals(targetId1, response.getBans().get(0).getUser().getId()); + + // Query for the other target + response = + Assertions.assertDoesNotThrow( + () -> User.queryFutureChannelBans().userId(creatorId).targetUserId(targetId2).request()); + Assertions.assertEquals(1, response.getBans().size()); + Assertions.assertEquals(targetId2, response.getBans().get(0).getUser().getId()); + + // Query all future channel bans by creator (without target filter) + response = + Assertions.assertDoesNotThrow( + () -> User.queryFutureChannelBans().userId(creatorId).request()); + Assertions.assertTrue(response.getBans().size() >= 2); + + // Cleanup - unban both users + Assertions.assertDoesNotThrow( + () -> User.unban(targetId1).removeFutureChannelsBan(true).request()); + Assertions.assertDoesNotThrow( + () -> User.unban(targetId2).removeFutureChannelsBan(true).request()); + } }