diff --git a/lib/GetStream/StreamChat/Channel.php b/lib/GetStream/StreamChat/Channel.php index 94da8f0..ac5b0e2 100644 --- a/lib/GetStream/StreamChat/Channel.php +++ b/lib/GetStream/StreamChat/Channel.php @@ -640,4 +640,43 @@ public function updateMemberPartial(string $userId, ?array $set = null, ?array $ ]; return $this->client->patch($this->getUrl() . "/member/" . urlencode($userId), $update); } + + /** + * Creates a draft message in the channel. + * @link https://getstream.io/chat/docs/php/drafts/?language=php#creating-a-draft-message + * @throws StreamException + */ + public function createDraft(array $message, string $userId): StreamResponse + { + $payload = ["message" => self::addUser($message, $userId)]; + return $this->client->post($this->getUrl() . "/draft", $payload); + } + + /** + * Deletes a draft message in the channel. + * @link https://getstream.io/chat/docs/php/drafts/?language=php#deleting-a-draft-message + * @throws StreamException + */ + public function deleteDraft(string $userId, ?string $parentId = null): StreamResponse + { + $params = ["user_id" => $userId]; + if ($parentId !== null) { + $params["parent_id"] = $parentId; + } + return $this->client->delete($this->getUrl() . "/draft", $params); + } + + /** + * Retrieves a draft message in the channel. + * @link https://getstream.io/chat/docs/php/drafts/?language=php#loading-a-draft-message + * @throws StreamException + */ + public function getDraft(string $userId, ?string $parentId = null): StreamResponse + { + $params = ["user_id" => $userId]; + if ($parentId !== null) { + $params["parent_id"] = $parentId; + } + return $this->client->get($this->getUrl() . "/draft", $params); + } } diff --git a/lib/GetStream/StreamChat/Client.php b/lib/GetStream/StreamChat/Client.php index 6b45f85..772662c 100644 --- a/lib/GetStream/StreamChat/Client.php +++ b/lib/GetStream/StreamChat/Client.php @@ -1598,4 +1598,28 @@ public function unreadCountsBatch(array $userIds): StreamResponse { return $this->post("unread_batch", ["user_ids" => $userIds]); } + + /** + * Queries drafts for a user. + * @link https://getstream.io/chat/docs/php/drafts/?language=php#querying-draft-messages + * @throws StreamException + */ + public function queryDrafts(string $userId, ?array $filter = null, ?array $sort = null, ?array $options = null): StreamResponse + { + $data = ["user_id" => $userId]; + + if ($filter !== null) { + $data["filter"] = $filter; + } + + if ($sort !== null) { + $data["sort"] = $sort; + } + + if ($options !== null) { + $data = array_merge($data, $options); + } + + return $this->post("drafts/query", $data); + } } diff --git a/tests/integration/IntegrationTest.php b/tests/integration/IntegrationTest.php index 0c7e8be..d46a7a3 100644 --- a/tests/integration/IntegrationTest.php +++ b/tests/integration/IntegrationTest.php @@ -274,16 +274,27 @@ public function testDeleteChannels() $this->assertTrue(array_key_exists("task_id", (array)$response)); $taskId = $response["task_id"]; - for ($i = 0; $i < 30; $i++) { + $completed = false; + while (!$completed) { $response = $this->client->getTask($taskId); if ($response["status"] == "completed") { - $this->assertSame($response["result"][$c1->getCID()]["status"], "ok"); - $this->assertSame($response["result"][$c2->getCID()]["status"], "ok"); - return; + $completed = true; } - usleep(300000); + usleep(500000); } - $this->assertSame($response["status"], "completed"); + $this->assertSame("ok", $response["result"][$c1->getCID()]["status"]); + $this->assertSame("ok", $response["result"][$c2->getCID()]["status"]); + + // for ($i = 0; $i < 30; $i++) { + // $response = $this->client->getTask($taskId); + // if ($response["status"] == "completed") { + // $this->assertSame($response["result"][$c1->getCID()]["status"], "ok"); + // $this->assertSame($response["result"][$c2->getCID()]["status"], "ok"); + // return; + // } + // usleep(500000); + // } + $this->assertSame("completed", $response["status"]); } public function testDeactivateUser() @@ -305,7 +316,7 @@ public function testDeactivateReactivateUsers() if ($response["status"] == "completed") { break; } - usleep(300000); + usleep(500000); } // Since we don't want to test the backend functionality, just @@ -560,7 +571,7 @@ public function testPendingMessage() $msg = ["id" => $msgId, "text" => "hello world"]; $response1 = $this->channel->sendMessage($msg, $this->user1["id"], null, ["pending" => true]); $this->assertSame($msgId, $response1["message"]["id"]); - + $response = $this->client->queryChannels(["id" => $this->channel->id], null, ['user_id' => $this->user1["id"]]); // check if length of $response["channels"][0]['pending_messages']) is 1 $this->assertSame(1, sizeof($response["channels"][0]['pending_messages'])); @@ -1205,7 +1216,7 @@ public function testPartialUpdateUsers() public function testPartialUpdateUserWithTeam() { - $user = ["id" => $this->generateGuid(), "name" => "Test User"]; + $user = ["id" => $this->generateGuid(), "name" => "Test User", "teams" => ["blue"]]; $response = $this->client->upsertUser($user); $this->assertTrue(array_key_exists("users", (array)$response)); $this->assertTrue(array_key_exists($user["id"], $response["users"])); @@ -1312,7 +1323,7 @@ public function testImportEnd2End() public function testUnreadCounts() { $this->channel->addMembers([$this->user1["id"]]); - $msgResp= $this->channel->sendMessage(["text" => "hi"], "random_user_4321"); + $msgResp = $this->channel->sendMessage(["text" => "hi"], "random_user_4321"); $resp = $this->client->unreadCounts($this->user1["id"]); $this->assertNotEmpty($resp["total_unread_count"]); @@ -1440,18 +1451,16 @@ public function testSendMessageWithRestrictedVisibility() "text" => "secret message", "restricted_visibility" => [$this->user1["id"]] ]; - - + $response = $this->channel->sendMessage($msg, $this->user1["id"]); $this->assertNotNull($response["message"]["restricted_visibility"]); $this->assertEquals([$this->user1["id"]], $response["message"]["restricted_visibility"]); - } public function testUpdateMessageWithRestrictedVisibility() { $this->channel->addMembers([$this->user1["id"], $this->user2["id"]]); - + // First send a regular message $msgId = $this->generateGuid(); $msg = [ @@ -1459,7 +1468,7 @@ public function testUpdateMessageWithRestrictedVisibility() "text" => "original message" ]; $response = $this->channel->sendMessage($msg, $this->user1["id"]); - + // Then update it with restricted visibility $updatedMsg = [ "id" => $msgId, @@ -1467,7 +1476,7 @@ public function testUpdateMessageWithRestrictedVisibility() "restricted_visibility" => [$this->user1["id"]], "user" => ["id" => $this->user1["id"]] ]; - + $response = $this->client->updateMessage($updatedMsg); $this->assertNotNull($response["message"]["restricted_visibility"]); $this->assertEquals([$this->user1["id"]], $response["message"]["restricted_visibility"]); @@ -1476,7 +1485,7 @@ public function testUpdateMessageWithRestrictedVisibility() public function testUpdateMessagePartialWithRestrictedVisibility() { $this->channel->addMembers([$this->user1["id"], $this->user2["id"]]); - + // First send a regular message $msgId = $this->generateGuid(); $msg = [ @@ -1484,7 +1493,7 @@ public function testUpdateMessagePartialWithRestrictedVisibility() "text" => "original message" ]; $response = $this->channel->sendMessage($msg, $this->user1["id"]); - + // Then do a partial update with restricted visibility $response = $this->client->partialUpdateMessage( $msgId, @@ -1496,7 +1505,7 @@ public function testUpdateMessagePartialWithRestrictedVisibility() ], $this->user1["id"] ); - + $this->assertNotNull($response["message"]["restricted_visibility"]); $this->assertEquals([$this->user1["id"]], $response["message"]["restricted_visibility"]); } @@ -1520,4 +1529,151 @@ public function testExportUsers() } $this->assertSame($response["status"], "completed"); } + + public function testCreateDraft() + { + $message = ["text" => "This is a draft message"]; + $response = $this->channel->createDraft($message, $this->user1["id"]); + + $this->assertTrue(array_key_exists("draft", (array)$response)); + $this->assertSame($response["draft"]["message"]["text"], "This is a draft message"); + } + + public function testGetDraft() + { + // First create a draft + $draftMessage = ["text" => "This is a draft to retrieve"]; + $this->channel->createDraft($draftMessage, $this->user1["id"]); + + // Then get the draft + $response = $this->channel->getDraft($this->user1["id"]); + + $this->assertTrue(array_key_exists("draft", (array)$response)); + $this->assertSame($response["draft"]["message"]["text"], "This is a draft to retrieve"); + $this->assertSame($response["draft"]["channel_cid"], $this->channel->getCID()); + } + + public function testDeleteDraft() + { + // First create a draft + $draftMessage = ["text" => "This is a draft to delete"]; + $this->channel->createDraft($draftMessage, $this->user1["id"]); + + // Then delete the draft + $this->channel->deleteDraft($this->user1["id"]); + + // Verify it's deleted by trying to get it + try { + $this->channel->getDraft($this->user1["id"]); + $this->fail("Draft should be deleted"); + } catch (\Exception $e) { + // Expected behavior, draft should not be found + } + } + + public function testThreadDraft() + { + // First create a parent message + $msg = $this->channel->sendMessage(["text" => "Parent message"], $this->user1["id"]); + $parentId = $msg["message"]["id"]; + + // Create a draft reply + $draftReply = ["text" => "This is a draft reply", "parent_id" => $parentId]; + $response = $this->channel->createDraft($draftReply, $this->user1["id"]); + + $this->assertTrue(array_key_exists("draft", (array)$response)); + $this->assertSame($response["draft"]["message"]["text"], "This is a draft reply"); + $this->assertSame($response["draft"]["parent_id"], $parentId); + + // Get the draft reply + $response = $this->channel->getDraft($this->user1["id"], $parentId); + + $this->assertTrue(array_key_exists("draft", (array)$response)); + $this->assertSame($response["draft"]["message"]["text"], "This is a draft reply"); + $this->assertSame($response["draft"]["parent_id"], $parentId); + + // Delete the draft reply + $this->channel->deleteDraft($this->user1["id"], $parentId); + + // Verify it's deleted + try { + $this->channel->getDraft($this->user1["id"], $parentId); + $this->fail("Thread draft should be deleted"); + } catch (\Exception $e) { + // Expected behavior + } + } + + public function testQueryDrafts() + { + // Create multiple drafts in different channels + $draft1 = ["text" => "Draft in channel 1"]; + $this->channel->createDraft($draft1, $this->user1["id"]); + + // Create another channel with a draft + $channel2 = $this->client->Channel("messaging", $this->generateGuid()); + $channel2->create($this->user1["id"]); + + $draft2 = ["text" => "Draft in channel 2"]; + $channel2->createDraft($draft2, $this->user1["id"]); + + // Query all drafts for the user + $response = $this->client->queryDrafts($this->user1["id"]); + + $this->assertArrayHasKey("drafts", $response); + $this->assertCount(2, $response["drafts"]); + + // Query drafts for a specific channel + $response = $this->client->queryDrafts( + $this->user1["id"], + ["channel_cid" => $channel2->getCID()] + ); + + $this->assertArrayHasKey("drafts", $response); + $this->assertCount(1, $response["drafts"]); + $draft = $response["drafts"][0]; + $this->assertEquals($channel2->getCID(), $draft["channel_cid"]); + $this->assertEquals("Draft in channel 2", $draft["message"]["text"]); + + // Query drafts with sort + $response = $this->client->queryDrafts( + $this->user1["id"], + sort: [["field" => "created_at", "direction" => 1]] + ); + + $this->assertArrayHasKey("drafts", $response); + $this->assertCount(2, $response["drafts"]); + $this->assertEquals($this->channel->getCID(), $response["drafts"][0]["channel_cid"]); + $this->assertEquals($channel2->getCID(), $response["drafts"][1]["channel_cid"]); + + // Query drafts with pagination + $response = $this->client->queryDrafts( + $this->user1["id"], + options: ["limit" => 1] + ); + + $this->assertArrayHasKey("drafts", $response); + $this->assertCount(1, $response["drafts"]); + $this->assertEquals($channel2->getCID(), $response["drafts"][0]["channel_cid"]); + + $this->assertArrayHasKey("next", $response); + $this->assertNotNull($response["next"]); + + // Query drafts with pagination and next + $response = $this->client->queryDrafts( + $this->user1["id"], + options: ["limit" => 1, "next" => $response["next"]] + ); + + $this->assertArrayHasKey("drafts", $response); + $this->assertCount(1, $response["drafts"]); + $this->assertEquals($this->channel->getCID(), $response["drafts"][0]["channel_cid"]); + + // Cleanup + try { + $channel2->delete(); + } catch (\Exception $e) { + // ignore + } + } }