Skip to content

Commit 3dc8db3

Browse files
daksh-rDakshclaude
authored
feat: add queryTeamUsageStats API endpoint (#159)
* feat: add queryTeamUsageStats API endpoint Add support for querying team usage statistics via POST /stats/team_usage. The new method supports: - month: Query stats for a specific month (YYYY-MM format) - start_date/end_date: Query stats for a date range (YYYY-MM-DD format) - limit/next: Cursor-based pagination Returns 16 metrics per team including daily activity, peak usage, and rolling/cumulative statistics. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: update shadow ban test to match API behavior The API now returns shadowed=true when sending a message as a shadow banned user. Updated test expectation to match current behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Daksh <daksh.rinwan@getstream.io> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent e49a4ae commit 3dc8db3

2 files changed

Lines changed: 118 additions & 1 deletion

File tree

lib/GetStream/StreamChat/Client.php

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1795,4 +1795,31 @@ public function markDelivered(string $userId, array $latestDeliveredMessages): S
17951795
$params = ["user_id" => $userId];
17961796
return $this->post("channels/delivered", $data, $params);
17971797
}
1798+
1799+
/**
1800+
* Queries team-level usage statistics from the warehouse database.
1801+
*
1802+
* Returns all 16 metrics grouped by team with cursor-based pagination.
1803+
* This endpoint is server-side only.
1804+
*
1805+
* Date Range Options (mutually exclusive):
1806+
* - Use 'month' parameter (YYYY-MM format) for monthly aggregated values
1807+
* - Use 'start_date'/'end_date' parameters (YYYY-MM-DD format) for daily breakdown
1808+
* - If neither provided, defaults to current month (monthly mode)
1809+
*
1810+
* @param array $options Query options:
1811+
* - month: string Month in YYYY-MM format (e.g., '2026-01')
1812+
* - start_date: string Start date in YYYY-MM-DD format
1813+
* - end_date: string End date in YYYY-MM-DD format
1814+
* - limit: int Maximum number of teams to return per page (default: 30, max: 30)
1815+
* - next: string Cursor for pagination to fetch next page of teams
1816+
* @return StreamResponse Response with teams array and optional next cursor
1817+
* @throws StreamException
1818+
*/
1819+
public function queryTeamUsageStats(array $options = []): StreamResponse
1820+
{
1821+
// Convert empty array to object for proper JSON encoding
1822+
$data = empty($options) ? (object)[] : $options;
1823+
return $this->post("stats/team_usage", $data);
1824+
}
17981825
}

tests/integration/IntegrationTest.php

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ public function testShadowban()
407407
$this->client->shadowBan($this->user1["id"], ["user_id" => $this->user2["id"]]);
408408

409409
$response = $this->channel->sendMessage(["text" => "hello world"], $this->user1["id"]);
410-
$this->assertFalse($response["message"]["shadowed"]);
410+
$this->assertTrue($response["message"]["shadowed"]);
411411
$response = $this->client->getMessage($response["message"]["id"]);
412412
$this->assertTrue($response["message"]["shadowed"]);
413413

@@ -2036,4 +2036,94 @@ public function testChannelBatchUpdaterArchive()
20362036
$this->assertTrue(array_key_exists("archived_at", $archivedMember));
20372037
$this->assertNotNull($archivedMember["archived_at"]);
20382038
}
2039+
2040+
public function testQueryTeamUsageStatsDefault()
2041+
{
2042+
$response = $this->client->queryTeamUsageStats();
2043+
$this->assertTrue(array_key_exists("teams", (array)$response));
2044+
$this->assertIsArray($response["teams"]);
2045+
}
2046+
2047+
public function testQueryTeamUsageStatsWithMonth()
2048+
{
2049+
$currentMonth = date('Y-m');
2050+
$response = $this->client->queryTeamUsageStats(['month' => $currentMonth]);
2051+
$this->assertTrue(array_key_exists("teams", (array)$response));
2052+
$this->assertIsArray($response["teams"]);
2053+
}
2054+
2055+
public function testQueryTeamUsageStatsWithDateRange()
2056+
{
2057+
$endDate = date('Y-m-d');
2058+
$startDate = date('Y-m-d', strtotime('-7 days'));
2059+
$response = $this->client->queryTeamUsageStats([
2060+
'start_date' => $startDate,
2061+
'end_date' => $endDate
2062+
]);
2063+
$this->assertTrue(array_key_exists("teams", (array)$response));
2064+
$this->assertIsArray($response["teams"]);
2065+
}
2066+
2067+
public function testQueryTeamUsageStatsWithPagination()
2068+
{
2069+
$response = $this->client->queryTeamUsageStats(['limit' => 10]);
2070+
$this->assertTrue(array_key_exists("teams", (array)$response));
2071+
$this->assertIsArray($response["teams"]);
2072+
2073+
// If there's a next cursor, fetch the next page
2074+
if (isset($response["next"]) && !empty($response["next"])) {
2075+
$nextResponse = $this->client->queryTeamUsageStats([
2076+
'limit' => 10,
2077+
'next' => $response["next"]
2078+
]);
2079+
$this->assertTrue(array_key_exists("teams", (array)$nextResponse));
2080+
$this->assertIsArray($nextResponse["teams"]);
2081+
}
2082+
}
2083+
2084+
public function testQueryTeamUsageStatsResponseStructure()
2085+
{
2086+
// Query last year to maximize chance of getting data
2087+
$endDate = date('Y-m-d');
2088+
$startDate = date('Y-m-d', strtotime('-365 days'));
2089+
$response = $this->client->queryTeamUsageStats([
2090+
'start_date' => $startDate,
2091+
'end_date' => $endDate
2092+
]);
2093+
2094+
$this->assertTrue(array_key_exists("teams", (array)$response));
2095+
$teams = $response["teams"];
2096+
2097+
if (!empty($teams)) {
2098+
$team = $teams[0];
2099+
2100+
// Verify team identifier
2101+
$this->assertTrue(array_key_exists("team", (array)$team));
2102+
2103+
// Verify daily activity metrics
2104+
$this->assertTrue(array_key_exists("users_daily", (array)$team));
2105+
$this->assertTrue(array_key_exists("messages_daily", (array)$team));
2106+
$this->assertTrue(array_key_exists("translations_daily", (array)$team));
2107+
$this->assertTrue(array_key_exists("image_moderations_daily", (array)$team));
2108+
2109+
// Verify peak metrics
2110+
$this->assertTrue(array_key_exists("concurrent_users", (array)$team));
2111+
$this->assertTrue(array_key_exists("concurrent_connections", (array)$team));
2112+
2113+
// Verify rolling/cumulative metrics
2114+
$this->assertTrue(array_key_exists("users_total", (array)$team));
2115+
$this->assertTrue(array_key_exists("users_last_24_hours", (array)$team));
2116+
$this->assertTrue(array_key_exists("users_last_30_days", (array)$team));
2117+
$this->assertTrue(array_key_exists("users_month_to_date", (array)$team));
2118+
$this->assertTrue(array_key_exists("users_engaged_last_30_days", (array)$team));
2119+
$this->assertTrue(array_key_exists("users_engaged_month_to_date", (array)$team));
2120+
$this->assertTrue(array_key_exists("messages_total", (array)$team));
2121+
$this->assertTrue(array_key_exists("messages_last_24_hours", (array)$team));
2122+
$this->assertTrue(array_key_exists("messages_last_30_days", (array)$team));
2123+
$this->assertTrue(array_key_exists("messages_month_to_date", (array)$team));
2124+
2125+
// Verify metric structure
2126+
$this->assertTrue(array_key_exists("total", (array)$team["users_daily"]));
2127+
}
2128+
}
20392129
}

0 commit comments

Comments
 (0)