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
27 changes: 27 additions & 0 deletions lib/GetStream/StreamChat/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -1795,4 +1795,31 @@ public function markDelivered(string $userId, array $latestDeliveredMessages): S
$params = ["user_id" => $userId];
return $this->post("channels/delivered", $data, $params);
}

/**
* Queries team-level usage statistics from the warehouse database.
*
* Returns all 16 metrics grouped by team with cursor-based pagination.
* This endpoint is server-side only.
*
* Date Range Options (mutually exclusive):
* - Use 'month' parameter (YYYY-MM format) for monthly aggregated values
* - Use 'start_date'/'end_date' parameters (YYYY-MM-DD format) for daily breakdown
* - If neither provided, defaults to current month (monthly mode)
*
* @param array $options Query options:
* - month: string Month in YYYY-MM format (e.g., '2026-01')
* - start_date: string Start date in YYYY-MM-DD format
* - end_date: string End date in YYYY-MM-DD format
* - limit: int Maximum number of teams to return per page (default: 30, max: 30)
* - next: string Cursor for pagination to fetch next page of teams
* @return StreamResponse Response with teams array and optional next cursor
* @throws StreamException
*/
public function queryTeamUsageStats(array $options = []): StreamResponse
{
// Convert empty array to object for proper JSON encoding
$data = empty($options) ? (object)[] : $options;
return $this->post("stats/team_usage", $data);
}
}
92 changes: 91 additions & 1 deletion tests/integration/IntegrationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -407,7 +407,7 @@ public function testShadowban()
$this->client->shadowBan($this->user1["id"], ["user_id" => $this->user2["id"]]);

$response = $this->channel->sendMessage(["text" => "hello world"], $this->user1["id"]);
$this->assertFalse($response["message"]["shadowed"]);
$this->assertTrue($response["message"]["shadowed"]);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$response = $this->client->getMessage($response["message"]["id"]);
$this->assertTrue($response["message"]["shadowed"]);

Expand Down Expand Up @@ -2036,4 +2036,94 @@ public function testChannelBatchUpdaterArchive()
$this->assertTrue(array_key_exists("archived_at", $archivedMember));
$this->assertNotNull($archivedMember["archived_at"]);
}

public function testQueryTeamUsageStatsDefault()
{
$response = $this->client->queryTeamUsageStats();
$this->assertTrue(array_key_exists("teams", (array)$response));
$this->assertIsArray($response["teams"]);
}

public function testQueryTeamUsageStatsWithMonth()
{
$currentMonth = date('Y-m');
$response = $this->client->queryTeamUsageStats(['month' => $currentMonth]);
$this->assertTrue(array_key_exists("teams", (array)$response));
$this->assertIsArray($response["teams"]);
}

public function testQueryTeamUsageStatsWithDateRange()
{
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime('-7 days'));
$response = $this->client->queryTeamUsageStats([
'start_date' => $startDate,
'end_date' => $endDate
]);
$this->assertTrue(array_key_exists("teams", (array)$response));
$this->assertIsArray($response["teams"]);
}

public function testQueryTeamUsageStatsWithPagination()
{
$response = $this->client->queryTeamUsageStats(['limit' => 10]);
$this->assertTrue(array_key_exists("teams", (array)$response));
$this->assertIsArray($response["teams"]);

// If there's a next cursor, fetch the next page
if (isset($response["next"]) && !empty($response["next"])) {
$nextResponse = $this->client->queryTeamUsageStats([
'limit' => 10,
'next' => $response["next"]
]);
$this->assertTrue(array_key_exists("teams", (array)$nextResponse));
$this->assertIsArray($nextResponse["teams"]);
}
}

public function testQueryTeamUsageStatsResponseStructure()
{
// Query last year to maximize chance of getting data
$endDate = date('Y-m-d');
$startDate = date('Y-m-d', strtotime('-365 days'));
$response = $this->client->queryTeamUsageStats([
'start_date' => $startDate,
'end_date' => $endDate
]);

$this->assertTrue(array_key_exists("teams", (array)$response));
$teams = $response["teams"];

if (!empty($teams)) {
$team = $teams[0];

// Verify team identifier
$this->assertTrue(array_key_exists("team", (array)$team));

// Verify daily activity metrics
$this->assertTrue(array_key_exists("users_daily", (array)$team));
$this->assertTrue(array_key_exists("messages_daily", (array)$team));
$this->assertTrue(array_key_exists("translations_daily", (array)$team));
$this->assertTrue(array_key_exists("image_moderations_daily", (array)$team));

// Verify peak metrics
$this->assertTrue(array_key_exists("concurrent_users", (array)$team));
$this->assertTrue(array_key_exists("concurrent_connections", (array)$team));

// Verify rolling/cumulative metrics
$this->assertTrue(array_key_exists("users_total", (array)$team));
$this->assertTrue(array_key_exists("users_last_24_hours", (array)$team));
$this->assertTrue(array_key_exists("users_last_30_days", (array)$team));
$this->assertTrue(array_key_exists("users_month_to_date", (array)$team));
$this->assertTrue(array_key_exists("users_engaged_last_30_days", (array)$team));
$this->assertTrue(array_key_exists("users_engaged_month_to_date", (array)$team));
$this->assertTrue(array_key_exists("messages_total", (array)$team));
$this->assertTrue(array_key_exists("messages_last_24_hours", (array)$team));
$this->assertTrue(array_key_exists("messages_last_30_days", (array)$team));
$this->assertTrue(array_key_exists("messages_month_to_date", (array)$team));

// Verify metric structure
$this->assertTrue(array_key_exists("total", (array)$team["users_daily"]));
}
}
}