Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions lib/GetStream/StreamChat/Channel.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
24 changes: 24 additions & 0 deletions lib/GetStream/StreamChat/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
194 changes: 175 additions & 19 deletions tests/integration/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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
Expand Down Expand Up @@ -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']));
Expand Down Expand Up @@ -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"]));
Expand Down Expand Up @@ -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"]);
Expand Down Expand Up @@ -1440,34 +1451,32 @@ 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 = [
"id" => $msgId,
"text" => "original message"
];
$response = $this->channel->sendMessage($msg, $this->user1["id"]);

// Then update it with restricted visibility
$updatedMsg = [
"id" => $msgId,
"text" => "updated secret message",
"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"]);
Expand All @@ -1476,15 +1485,15 @@ public function testUpdateMessageWithRestrictedVisibility()
public function testUpdateMessagePartialWithRestrictedVisibility()
{
$this->channel->addMembers([$this->user1["id"], $this->user2["id"]]);

// First send a regular message
$msgId = $this->generateGuid();
$msg = [
"id" => $msgId,
"text" => "original message"
];
$response = $this->channel->sendMessage($msg, $this->user1["id"]);

// Then do a partial update with restricted visibility
$response = $this->client->partialUpdateMessage(
$msgId,
Expand All @@ -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"]);
}
Expand All @@ -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
}
}
}