Skip to content
Open
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
21 changes: 21 additions & 0 deletions lib/stream-chat/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1195,6 +1195,27 @@ def channel_batch_updater
ChannelBatchUpdater.new(self)
end

# 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 month [String, nil] Month in YYYY-MM format (e.g., '2026-01')
# @param start_date [String, nil] Start date in YYYY-MM-DD format
# @param end_date [String, nil] End date in YYYY-MM-DD format
# @param limit [Integer, nil] Maximum number of teams to return per page (default: 30, max: 30)
# @param next_cursor [String, nil] Cursor for pagination to fetch next page of teams
# @return [StreamChat::StreamResponse] Response with teams array and optional next cursor
sig { params(options: T.untyped).returns(StreamChat::StreamResponse) }
def query_team_usage_stats(**options)
post('stats/team_usage', data: options)
end

private

sig { returns(T::Hash[String, String]) }
Expand Down
86 changes: 85 additions & 1 deletion spec/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ def loop_times(times)
id: msg_id,
text: 'Hello world'
}, @random_user[:id])
expect(response['message']['shadowed']).to eq(false)
expect(response['message']['shadowed']).to eq(true)
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 = @client.get_message(msg_id)
expect(response['message']['shadowed']).to eq(true)

Expand Down Expand Up @@ -1475,4 +1475,88 @@ def loop_times(times)
expect(response.status_code).to eq(201)
end
end

describe '#query_team_usage_stats' do
it 'queries team usage stats with default options' do
response = @client.query_team_usage_stats
expect(response).to include 'teams'
expect(response['teams']).to be_an(Array)
end

it 'queries team usage stats with month parameter' do
current_month = Time.now.strftime('%Y-%m')
response = @client.query_team_usage_stats(month: current_month)
expect(response).to include 'teams'
expect(response['teams']).to be_an(Array)
end

it 'queries team usage stats with date range' do
end_date = Date.today
start_date = end_date - 7
response = @client.query_team_usage_stats(
start_date: start_date.strftime('%Y-%m-%d'),
end_date: end_date.strftime('%Y-%m-%d')
)
expect(response).to include 'teams'
expect(response['teams']).to be_an(Array)
end

it 'queries team usage stats with pagination' do
response = @client.query_team_usage_stats(limit: 10)
expect(response).to include 'teams'
expect(response['teams']).to be_an(Array)

# If there's a next cursor, fetch the next page
if response['next'] && !response['next'].empty?
next_response = @client.query_team_usage_stats(limit: 10, next: response['next'])
expect(next_response).to include 'teams'
expect(next_response['teams']).to be_an(Array)
end
end

it 'returns proper response structure when data exists' do
# Query last year to maximize chance of getting data
end_date = Date.today
start_date = end_date - 365
response = @client.query_team_usage_stats(
start_date: start_date.strftime('%Y-%m-%d'),
end_date: end_date.strftime('%Y-%m-%d')
)

expect(response).to include 'teams'
teams = response['teams']

next unless teams && !teams.empty?

team = teams[0]

# Verify team identifier
expect(team).to include 'team'

# Verify daily activity metrics
expect(team).to include 'users_daily'
expect(team).to include 'messages_daily'
expect(team).to include 'translations_daily'
expect(team).to include 'image_moderations_daily'

# Verify peak metrics
expect(team).to include 'concurrent_users'
expect(team).to include 'concurrent_connections'

# Verify rolling/cumulative metrics
expect(team).to include 'users_total'
expect(team).to include 'users_last_24_hours'
expect(team).to include 'users_last_30_days'
expect(team).to include 'users_month_to_date'
expect(team).to include 'users_engaged_last_30_days'
expect(team).to include 'users_engaged_month_to_date'
expect(team).to include 'messages_total'
expect(team).to include 'messages_last_24_hours'
expect(team).to include 'messages_last_30_days'
expect(team).to include 'messages_month_to_date'

# Verify metric structure
expect(team['users_daily']).to include 'total'
end
end
end