diff --git a/lib/GetStream/StreamChat/ChannelBatchUpdater.php b/lib/GetStream/StreamChat/ChannelBatchUpdater.php new file mode 100644 index 0000000..bf87719 --- /dev/null +++ b/lib/GetStream/StreamChat/ChannelBatchUpdater.php @@ -0,0 +1,242 @@ +client = $client; + } + + /** + * Adds members to channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function addMembers(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "addMembers", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Removes members from channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function removeMembers(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "removeMembers", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Invites members to channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function inviteMembers(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "inviteMembers", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Assigns roles to members in channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function assignRoles(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "assignRoles", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Adds moderators to channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function addModerators(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "addModerators", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Removes moderator role from members in channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function demoteModerators(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "demoteModerators", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Hides channels matching the filter for the specified members. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function hide(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "hide", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Shows channels matching the filter for the specified members. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function show(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "show", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Archives channels matching the filter for the specified members. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function archive(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "archive", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Unarchives channels matching the filter for the specified members. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $members Array of member arrays, each with user_id (required) and optional channel_role + * @return StreamResponse + * @throws StreamException + */ + public function unarchive(array $filter, array $members): StreamResponse + { + $options = [ + "operation" => "unarchive", + "filter" => $filter, + "members" => $members, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Updates data on channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $data Data to update (keys: frozen, disabled, custom, team, config_overrides, auto_translation_enabled, auto_translation_language) + * @return StreamResponse + * @throws StreamException + */ + public function updateData(array $filter, array $data): StreamResponse + { + $options = [ + "operation" => "updateData", + "filter" => $filter, + "data" => $data, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Adds filter tags to channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $tags Array of filter tag strings + * @return StreamResponse + * @throws StreamException + */ + public function addFilterTags(array $filter, array $tags): StreamResponse + { + $options = [ + "operation" => "addFilterTags", + "filter" => $filter, + "filter_tags_update" => $tags, + ]; + return $this->client->updateChannelsBatch($options); + } + + /** + * Removes filter tags from channels matching the filter. + * @param array $filter Filter to match channels (keys: cids, types, filter_tags) + * @param array $tags Array of filter tag strings + * @return StreamResponse + * @throws StreamException + */ + public function removeFilterTags(array $filter, array $tags): StreamResponse + { + $options = [ + "operation" => "removeFilterTags", + "filter" => $filter, + "filter_tags_update" => $tags, + ]; + return $this->client->updateChannelsBatch($options); + } +} diff --git a/lib/GetStream/StreamChat/Client.php b/lib/GetStream/StreamChat/Client.php index 07ac6ef..b532a02 100644 --- a/lib/GetStream/StreamChat/Client.php +++ b/lib/GetStream/StreamChat/Client.php @@ -432,6 +432,25 @@ public function deleteChannels(array $cids, ?array $options = null): StreamRespo return $this->post("channels/delete", $options); } + /** Updates channels in batch based on the provided options. + * This is an asynchronous operation and the returned value is a task Id. + * You can use `getTask` to check the status of the task. + * @link https://getstream.io/chat/docs/php/channel_update/?language=php + * @throws StreamException + */ + public function updateChannelsBatch(array $options): StreamResponse + { + return $this->put("channels/batch", $options); + } + + /** Returns a ChannelBatchUpdater instance for batch channel operations. + * @return ChannelBatchUpdater + */ + public function channelBatchUpdater(): ChannelBatchUpdater + { + return new ChannelBatchUpdater($this); + } + /** Creates a guest user. * @link https://getstream.io/chat/docs/php/authless_users/?language=php * @throws StreamException diff --git a/tests/integration/IntegrationTest.php b/tests/integration/IntegrationTest.php index ad43dcf..9114841 100644 --- a/tests/integration/IntegrationTest.php +++ b/tests/integration/IntegrationTest.php @@ -1853,4 +1853,187 @@ public function testMarkDelivered() $this->assertInstanceOf(\GetStream\StreamChat\StreamResponse::class, $response); } + + public function testUpdateChannelsBatch() + { + $user = ["id" => $this->generateGuid()]; + $this->client->upsertUser($user); + + $c1 = $this->getChannel(); + $c2 = $this->getChannel(); + + $filter = [ + "cids" => [ + '$in' => [$c1->getCID(), $c2->getCID()] + ] + ]; + + $options = [ + "operation" => "addMembers", + "filter" => $filter, + "members" => [ + ["user_id" => $user["id"]] + ] + ]; + + $response = $this->client->updateChannelsBatch($options); + $this->assertTrue(array_key_exists("task_id", (array)$response)); + $this->assertNotEmpty($response["task_id"]); + } + + public function testChannelBatchUpdaterAddMembers() + { + $user1 = ["id" => $this->generateGuid()]; + $user2 = ["id" => $this->generateGuid()]; + $this->client->upsertUser($user1); + $this->client->upsertUser($user2); + + $c1 = $this->getChannel(); + $c2 = $this->getChannel(); + + $filter = [ + "cids" => [ + '$in' => [$c1->getCID(), $c2->getCID()] + ] + ]; + + $members = [ + ["user_id" => $user1["id"]], + ["user_id" => $user2["id"]] + ]; + + $updater = $this->client->channelBatchUpdater(); + $response = $updater->addMembers($filter, $members); + + $this->assertTrue(array_key_exists("task_id", (array)$response)); + $taskId = $response["task_id"]; + + $completed = false; + while (!$completed) { + $response = $this->client->getTask($taskId); + if ($response["status"] == "completed") { + $completed = true; + } + usleep(500000); + } + + // Wait a bit for changes to be visible + usleep(2000000); + + // Verify members were added to both channels + $c1Members = $c1->queryMembers(); + $c2Members = $c2->queryMembers(); + + $c1MemberIds = array_map(function ($member) { + return $member["user"]["id"]; + }, $c1Members["members"]); + + $c2MemberIds = array_map(function ($member) { + return $member["user"]["id"]; + }, $c2Members["members"]); + + $this->assertContains($user1["id"], $c1MemberIds); + $this->assertContains($user2["id"], $c1MemberIds); + $this->assertContains($user1["id"], $c2MemberIds); + $this->assertContains($user2["id"], $c2MemberIds); + } + + public function testChannelBatchUpdaterRemoveMembers() + { + $user1 = ["id" => $this->generateGuid()]; + $user2 = ["id" => $this->generateGuid()]; + $this->client->upsertUser($user1); + $this->client->upsertUser($user2); + + $c1 = $this->getChannel(); + $c1->addMembers([$user1["id"], $user2["id"]]); + + $filter = [ + "cids" => [ + '$in' => [$c1->getCID()] + ] + ]; + + $members = [ + ["user_id" => $user1["id"]] + ]; + + $updater = $this->client->channelBatchUpdater(); + $response = $updater->removeMembers($filter, $members); + + $this->assertTrue(array_key_exists("task_id", (array)$response)); + $taskId = $response["task_id"]; + + $completed = false; + while (!$completed) { + $response = $this->client->getTask($taskId); + if ($response["status"] == "completed") { + $completed = true; + } + usleep(500000); + } + + // Wait a bit for changes to be visible + usleep(2000000); + + // Verify member was removed + $c1Members = $c1->queryMembers(); + $c1MemberIds = array_map(function ($member) { + return $member["user"]["id"]; + }, $c1Members["members"]); + + $this->assertNotContains($user1["id"], $c1MemberIds); + $this->assertContains($user2["id"], $c1MemberIds); + } + + public function testChannelBatchUpdaterArchive() + { + $user1 = ["id" => $this->generateGuid()]; + $this->client->upsertUser($user1); + + $c1 = $this->getChannel(); + $c1->addMembers([$user1["id"]]); + + $filter = [ + "cids" => [ + '$in' => [$c1->getCID()] + ] + ]; + + $members = [ + ["user_id" => $user1["id"]] + ]; + + $updater = $this->client->channelBatchUpdater(); + $response = $updater->archive($filter, $members); + + $this->assertTrue(array_key_exists("task_id", (array)$response)); + $taskId = $response["task_id"]; + + $completed = false; + while (!$completed) { + $response = $this->client->getTask($taskId); + if ($response["status"] == "completed") { + $completed = true; + } + usleep(500000); + } + + // Wait a bit for changes to be visible + usleep(2000000); + + // Verify channel was archived + $c1Members = $c1->queryMembers(); + $archivedMember = null; + foreach ($c1Members["members"] as $member) { + if ($member["user"]["id"] == $user1["id"]) { + $archivedMember = $member; + break; + } + } + + $this->assertNotNull($archivedMember); + $this->assertTrue(array_key_exists("archived_at", $archivedMember)); + $this->assertNotNull($archivedMember["archived_at"]); + } }