[CHA-1578] refactor: code generated from openapi specs#205
[CHA-1578] refactor: code generated from openapi specs#205
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR refactors request payload construction across multiple modules using dictionary expansion patterns, introduces new API endpoints for recording, appeals, and data imports, launches a comprehensive webhook event handling system with constants and parsing utilities, and updates method return types and parameter types throughout chat, video, moderation, and feeds modules. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Pull request overview
This PR contains code generated from refactored OpenAPI specifications, updating multiple REST client interfaces and model definitions across the GetStream SDK. The changes primarily involve type renames, new API endpoints, parameter additions, and model restructuring to align with updated API specifications.
Key changes:
- Added new API methods and endpoints (e.g.,
get_call_stats_map, collections CRUD operations,mark_delivered) - Renamed types for clarity (e.g.,
NotificationSettings→NotificationSettingsRequest/Response, action request types now usePayloadsuffix) - Added telemetry decorators to feeds client methods
- Expanded model definitions with new fields and response types
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
getstream/video/rest_client.py |
Added get_call_stats_map method, added pagination params to query_call_session_participant_stats, updated type names for notification settings and SIP challenge |
getstream/video/async_rest_client.py |
Mirror changes to sync client for async version |
getstream/moderation/rest_client.py |
Updated type names for moderation payloads and action requests (added Payload suffix) |
getstream/moderation/async_rest_client.py |
Mirror changes to sync client for async version |
getstream/models/__init__.py |
Extensive model changes: renamed/restructured many dataclasses, added new models for collections, events, notifications; removed deprecated models |
getstream/feeds/rest_client.py |
Added telemetry decorators, new collection endpoints, renamed add_reaction → add_activity_reaction, added new parameters to existing methods |
getstream/feeds/feeds.py |
Updated method signatures to match client changes |
getstream/chat/rest_client.py |
Added mark_delivered method, updated type names, added message_timestamp parameter |
getstream/chat/async_rest_client.py |
Mirror changes to sync client for async version |
getstream/chat/channel.py |
Updated method signatures to match client changes |
getstream/chat/async_channel.py |
Mirror changes to sync client for async version |
The changes appear to be auto-generated code with consistent patterns applied across sync and async versions of the clients. No issues were identified in the generated code structure.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (10)
getstream/chat/async_channel.py(3 hunks)getstream/chat/async_rest_client.py(4 hunks)getstream/chat/channel.py(3 hunks)getstream/chat/rest_client.py(4 hunks)getstream/feeds/feeds.py(2 hunks)getstream/feeds/rest_client.py(51 hunks)getstream/moderation/async_rest_client.py(4 hunks)getstream/moderation/rest_client.py(4 hunks)getstream/video/async_rest_client.py(5 hunks)getstream/video/rest_client.py(5 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Plugins that work with audio, video, or WebRTC functionality should depend on
getstream[webrtc]instead of justgetstreamto access WebRTC-related dependencies likeaiortc,numpy,torch,torchaudio,soundfile,scipy,deepgram-sdk, andelevenlabs
Files:
getstream/chat/async_channel.pygetstream/chat/async_rest_client.pygetstream/moderation/rest_client.pygetstream/video/async_rest_client.pygetstream/chat/channel.pygetstream/moderation/async_rest_client.pygetstream/video/rest_client.pygetstream/chat/rest_client.pygetstream/feeds/rest_client.pygetstream/feeds/feeds.py
🧬 Code graph analysis (8)
getstream/chat/async_channel.py (1)
getstream/models/__init__.py (1)
ChannelInputRequest(4823-4851)
getstream/chat/async_rest_client.py (5)
getstream/common/telemetry.py (1)
operation_name(429-441)getstream/chat/rest_client.py (1)
mark_delivered(145-157)getstream/models/__init__.py (3)
DeliveredMessagePayload(7418-7420)MarkDeliveredResponse(10927-10928)ChannelInputRequest(4823-4851)getstream/utils/__init__.py (2)
build_query_param(94-126)build_body_dict(151-175)getstream/base.py (2)
post(240-256)post(429-445)
getstream/video/async_rest_client.py (3)
getstream/common/telemetry.py (1)
operation_name(429-441)getstream/models/__init__.py (4)
QueryCallStatsMapResponse(14474-14538)QueryCallSessionParticipantStatsResponse(14424-14457)NotificationSettingsRequest(12890-12908)SIPChallengeRequest(16585-16627)getstream/utils/__init__.py (1)
build_query_param(94-126)
getstream/chat/channel.py (2)
getstream/stream_response.py (1)
data(22-24)getstream/models/__init__.py (1)
ChannelInputRequest(4823-4851)
getstream/moderation/async_rest_client.py (4)
getstream/models/__init__.py (11)
ModerationPayloadRequest(12502-12514)BanActionRequestPayload(1901-1919)BlockActionRequestPayload(2040-2043)DeleteActivityRequestPayload(7131-7137)DeleteCommentRequestPayload(7226-7232)DeleteMessageRequestPayload(7291-7297)DeleteReactionRequestPayload(7322-7328)DeleteUserRequestPayload(7369-7384)MarkReviewedRequestPayload(10956-10963)ShadowBlockActionRequestPayload(17303-17306)UnbanActionRequestPayload(18368-18371)getstream/moderation/rest_client.py (2)
ban(35-58)unban(489-508)getstream/chat/async_rest_client.py (1)
delete_message(969-987)getstream/chat/rest_client.py (1)
delete_message(959-977)
getstream/video/rest_client.py (4)
getstream/common/telemetry.py (1)
operation_name(429-441)getstream/video/async_rest_client.py (1)
get_call_stats_map(875-903)getstream/models/__init__.py (4)
QueryCallStatsMapResponse(14474-14538)QueryCallSessionParticipantStatsResponse(14424-14457)NotificationSettingsRequest(12890-12908)SIPChallengeRequest(16585-16627)getstream/utils/__init__.py (1)
build_query_param(94-126)
getstream/feeds/rest_client.py (5)
getstream/common/telemetry.py (1)
operation_name(429-441)getstream/base.py (6)
post(240-256)post(429-445)delete(276-292)delete(465-481)get(222-238)get(411-427)getstream/utils/__init__.py (2)
build_body_dict(151-175)build_query_param(94-126)getstream/feeds/feeds.py (1)
delete(17-24)getstream/stream.py (2)
feeds(211-212)feeds(316-328)
getstream/feeds/feeds.py (1)
getstream/models/__init__.py (2)
FeedInput(8341-8357)EnrichmentOptions(7693-7744)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Upload results
- GitHub Check: Tests (3.11)
- GitHub Check: Tests (3.12)
- GitHub Check: Tests (3.13)
- GitHub Check: Tests (3.10)
🔇 Additional comments (35)
getstream/video/async_rest_client.py (3)
874-903: LGTM: Newget_call_stats_mapendpoint added.The new method follows the established patterns in the codebase with proper telemetry decoration, path parameter handling, and query parameter construction for optional filters.
1028-1028: Breaking change:notification_settingstype updated.The parameter type changed from
NotificationSettingstoNotificationSettingsRequest. This is a breaking change for callers passing the old type. Ensure migration documentation is provided.
1096-1096: Breaking change:challengeparameter type updated.The parameter type changed from
SIPChallengetoSIPChallengeRequest. Callers using the old type will need to update their code.getstream/moderation/async_rest_client.py (4)
213-213: Breaking change:moderation_payloadtype updated toModerationPayloadRequest.Callers using the previous
ModerationPayloadtype will need to migrate.
249-249: Breaking change:configparameter type updated.Type changed from
FeedsModerationTemplateConfigtoFeedsModerationTemplateConfigPayload.
288-288: Type alignment:sortusesSortParamRequest.This aligns with other methods in the codebase that already use
SortParamRequestfor sort parameters.
460-470: Breaking changes: All action payload types updated to*Payloadvariants.Multiple parameter types changed:
BanActionRequest→BanActionRequestPayloadBlockActionRequest→BlockActionRequestPayloadCustomActionRequest→CustomActionRequestPayloadDeleteActivityRequest→DeleteActivityRequestPayloadDeleteCommentRequest→DeleteCommentRequestPayloadDeleteMessageRequest→DeleteMessageRequestPayloadDeleteReactionRequest→DeleteReactionRequestPayloadDeleteUserRequest→DeleteUserRequestPayloadMarkReviewedRequest→MarkReviewedRequestPayloadShadowBlockActionRequest→ShadowBlockActionRequestPayloadUnbanActionRequest→UnbanActionRequestPayloadCallers will need to update their code to use the new payload types.
getstream/chat/channel.py (2)
71-71: Breaking change:dataparameter type updated toChannelInputRequest.The parameter type changed from
ChannelInputtoChannelInputRequest. Existing callers passingChannelInputwill need to migrate.
329-348: New optionalmessage_timestampparameter added tomark_unread.This is an additive, non-breaking change that extends the API to support marking unread by timestamp in addition to message ID.
getstream/chat/async_channel.py (2)
71-71: Breaking change consistent with sync version.The
dataparameter type changed toChannelInputRequest, matching the syncChannelclass.
329-348: Async version mirrors sync changes formark_unread.The
message_timestampparameter addition is consistent with the syncChannel.mark_unreadmethod.getstream/feeds/feeds.py (1)
26-66: New optional parameters added toget_or_create.Two new optional parameters added:
id_around: Optional[str]- for around-based paginationenrichment_options: Optional[EnrichmentOptions]- for controlling enrichment behaviorThese are additive, non-breaking changes that extend the API surface.
getstream/chat/rest_client.py (3)
144-157: Newmark_deliveredendpoint added.This new method allows marking messages as delivered for a user. The implementation follows the established patterns with proper telemetry decoration and parameter handling.
260-260: Breaking change:dataparameter type updated inupdate_channel.Type changed from
ChannelInputtoChannelInputRequest, consistent with the Channel wrapper classes.
627-654: Extendedmark_unreadwithmessage_timestampparameter.The new optional
message_timestampparameter is properly included in the request body viabuild_body_dict. This is a non-breaking additive change.getstream/video/rest_client.py (4)
870-899: Newget_call_stats_mapendpoint looks correct.The implementation correctly mirrors the async counterpart, using query parameters for optional filters and path parameters for required identifiers. The return type
QueryCallStatsMapResponsealigns with the model definition.
1024-1024: Type update toNotificationSettingsRequestis consistent.The change from
NotificationSettingstoNotificationSettingsRequestaligns with the new payload variant types for request bodies, matching the async client changes.
1062-1062: Type update inupdate_call_typematchescreate_call_type.Consistent use of
NotificationSettingsRequestacross both create and update operations.
1090-1090:SIPChallengeRequesttype update is appropriate.The
challengeparameter now uses the dedicated request payload type, which is consistent with the API schema refactoring pattern applied throughout this PR.getstream/chat/async_rest_client.py (4)
146-159: Newmark_deliveredmethod implementation is correct.The async implementation properly mirrors the sync counterpart, using
user_idas a query parameter andlatest_delivered_messagesin the request body. Telemetry decorator is correctly applied.
264-264: Type update toChannelInputRequestinupdate_channelis correct.This aligns with the PR's schema updates for request payload types.
631-658:mark_unreadextended withmessage_timestampparameter.The new optional
datetimeparameter is correctly added to the method signature and included in the request body viabuild_body_dict. This enhancement allows marking unread by timestamp in addition to message ID.
175-204: Async and sync clients are consistent; type difference between methods appears intentional.Both the async and sync clients use
ChannelInputforget_or_create_distinct_channelandChannelInputRequestforupdate_channel. The type difference is not an inconsistency between sync/async implementations but rather reflects intentional schema differences:ChannelInputincludescreated_by_id,truncated_by_id, andfilter_tagsfields thatChannelInputRequestomits, and theirconfig_overridesfields reference different types (ChannelConfigvsConfigOverridesRequest), suggesting the endpoints accept different request structures.getstream/moderation/rest_client.py (5)
203-225:custom_checkcorrectly updated to useModerationPayloadRequest.The type update for
moderation_payloadparameter aligns with the new payload variant types.
243-252:v2_upsert_templateupdated withFeedsModerationTemplateConfigPayload.The
configparameter type change is consistent with the schema refactoring.
278-292:query_moderation_flagsupdated to useSortParamRequest.Consistent with other methods using the new request type for sort parameters.
448-486:submit_actioncorrectly updated with new payload variant types.All action parameters (ban, block, custom, delete_*, mark_reviewed, shadow_block, unban) now use their respective
...RequestPayloadtypes, aligning with the schema updates.
254-276: Consider clarifying the intent of type inconsistency in moderation methods.The
flagmethod (line 263) usesModerationPayloadwhilecustom_check(line 211) usesModerationPayloadRequest. This pattern is consistent across the async client as well, wherecheckandflaguseModerationPayload, while onlycustom_checkusesModerationPayloadRequest. Since both types have identical structure, clarify whether this distinction is intentional (e.g., to explicitly markcustom_checkas request-only) or if the methods should be unified to use the same type for consistency.getstream/feeds/rest_client.py (7)
3-3: Telemetry import added for instrumentation.The addition of telemetry enables observability across all Feeds API methods.
34-80:add_activityextended with new parameters and telemetry.New optional parameters
restrict_replies,skip_enrich_url, andcollection_refsexpand the API capabilities. Telemetry decorator correctly applied.
505-560: New collection CRUD endpoints added.The
delete_collections,read_collections,update_collections,create_collections, andupsert_collectionsmethods provide comprehensive collection management. Each uses appropriate HTTP methods and parameter handling.
799-808:list_feed_groupsextended withinclude_soft_deletedfilter.Correctly passed as a query parameter for the GET request.
1299-1329: New feed visibility endpoints added.
list_feed_visibilities,get_feed_visibility, andupdate_feed_visibilityprovide visibility management capabilities with consistent patterns.
1375-1395: Newget_feeds_rate_limitsendpoint.Provides rate limit introspection with platform-specific filters (android, ios, web, server_side).
1604-1617:delete_feed_user_datachanged from DELETE to POST.The method now uses POST with a request body containing
hard_delete. This is a semantic change from the typical DELETE pattern but is appropriate when the operation requires request body parameters.
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (6)
getstream/moderation/rest_client.py (1)
133-133: Use consistent payload type acrosscheckandcustom_checkmethods.The
checkmethod usesModerationPayload(line 133), whilecustom_checkusesModerationPayloadRequest(line 258). Both classes are structurally identical, suggesting this naming inconsistency is likely unintentional. Standardize on a single type name for both methods.getstream/video/async_call.py (1)
149-176: Avoid silent breaking changes from mid-signature parameter insertion (positional args).
go_live(...)andstop_live(...)add new optional parameters between existing ones, which can mis-bind existing positional calls without an exception. If positional args are supported for this public API, prefer appending new params at the end or make options keyword-only via*afterself.Also applies to: 427-453
getstream/video/rest_client.py (1)
951-980: Samesort: List[SortParamRequest]query-param encoding risk as async client.getstream/video/call.py (1)
145-173: Public API: mid-signature param insertion can silently break positional callers.
Consider appending new options at the end or making them keyword-only (def go_live(self, *, ...)) to avoid wrong-parameter binding.Also applies to: 407-433
getstream/feeds/feeds.py (1)
26-67: High-risk breaking change:id_aroundinserted before existing params may silently mis-bind positional calls.
If users callget_or_create(10)expectinglimit=10, they’ll now passid_around="10". Prefer keyword-only params or append new params at the end to preserve positional behavior.getstream/video/async_rest_client.py (1)
955-984: Bug confirmed:sort: List[SortParamRequest]passed tobuild_query_paramwill be incorrectly encoded.
build_query_paramconverts list items withstr(v)instead of callingto_json()on objects that have it.SortParamRequesthasDataClassJsonMixinwhich providesto_json(), but the list branch at line 29 ofgetstream/utils/__init__.pybypasses it. This produces invalid wire format (e.g.,"SortParamRequest(direction=1, ...)"instead of proper JSON).The correct pattern is used elsewhere: pass
sorttobuild_body_dict(as ingetstream/common/rest_client.py:574-577). Other affected endpoints includegetstream/feeds/rest_client.pyandgetstream/chat/rest_client.py.
🤖 Fix all issues with AI agents
In @getstream/feeds/rest_client.py:
- Around line 1592-1599: The current query_feeds_usage_stats method constructs
the request body with build_body_dict which yields {"_from": ...} but the API
expects the field mapped as "from"; fix by creating a
QueryFeedsUsageStatsRequest instance with _from and to, serialize that model for
the POST payload (replace the build_body_dict/json assignment with model
instantiation and model.dict()/serialize call per existing patterns), ensuring
the correct field name is sent; also apply the same change to the
query_aggregate_call_stats method in the video client.
In @getstream/video/async_rest_client.py:
- Around line 890-920: get_call_stats_map currently forwards start_time and
end_time datetime objects to build_query_param which json.dumps can't serialize;
convert start_time and end_time to ISO-8601 strings (e.g., via
start_time.isoformat()) before calling build_query_param, or add a small helper
to normalize datetimes to strings and use it in get_call_stats_map so
build_query_param only receives serializable values; keep the rest of the method
and path_params unchanged.
In @getstream/video/rest_client.py:
- Around line 749-766: The stop_recording method always sends an empty JSON body
because build_body_dict() returns {}; update stop_recording to avoid sending
'{}' when there are no fields by calling body = build_body_dict() and passing
json=body if body is truthy, otherwise pass json=None (or omit the json param)
to self.post; reference the stop_recording function and build_body_dict to
implement this conditional so the backend does not receive an unnecessary empty
object.
- Around line 886-916: The query builder is serializing datetime via json.dumps
and fails; update build_query_param to detect datetime (and datetime-like)
values and serialize them using the existing encode_datetime utility (or call
encode_datetime for the start_time/end_time before passing) so ISO strings are
produced; ensure build_query_param handles both naive and aware datetimes and
lists/dicts containing datetimes so calls from get_call_stats_map (and its async
counterpart) will no longer raise TypeError.
In @getstream/webhook.py:
- Around line 378-382: The code currently instantiates webhook event classes by
calling event_class(**data) which bypasses DataClassJsonMixin custom
deserialization (e.g., datetime_from_unix_ns); change the instantiation in the
function that calls _get_event_class to use the dataclass deserializer (call
event_class.from_dict(data) or the classmethod provided by DataClassJsonMixin)
so field mappings and datetime decoders are applied; ensure you import/handle
the from_dict method on the event classes and fall back to the current behavior
only if from_dict is not available.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled by default for public repositories
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (14)
getstream/chat/async_rest_client.pygetstream/chat/rest_client.pygetstream/common/async_rest_client.pygetstream/common/rest_client.pygetstream/feeds/feeds.pygetstream/feeds/rest_client.pygetstream/models/__init__.pygetstream/moderation/async_rest_client.pygetstream/moderation/rest_client.pygetstream/video/async_call.pygetstream/video/async_rest_client.pygetstream/video/call.pygetstream/video/rest_client.pygetstream/webhook.py
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
Plugins that work with audio, video, or WebRTC functionality should depend on
getstream[webrtc]instead of justgetstreamto access WebRTC-related dependencies likeaiortc,numpy,torch,torchaudio,soundfile,scipy,deepgram-sdk, andelevenlabs
Files:
getstream/feeds/feeds.pygetstream/common/async_rest_client.pygetstream/common/rest_client.pygetstream/video/call.pygetstream/moderation/rest_client.pygetstream/chat/async_rest_client.pygetstream/video/async_call.pygetstream/chat/rest_client.pygetstream/moderation/async_rest_client.pygetstream/feeds/rest_client.pygetstream/video/rest_client.pygetstream/video/async_rest_client.pygetstream/webhook.py
🧬 Code graph analysis (12)
getstream/feeds/feeds.py (3)
getstream/stream_response.py (2)
data(22-24)StreamResponse(11-36)getstream/models/__init__.py (3)
FeedInput(8576-8592)EnrichmentOptions(7921-7975)UpdateFeedResponse(20025-20027)getstream/feeds/rest_client.py (1)
update_feed(914-940)
getstream/common/async_rest_client.py (4)
getstream/common/telemetry.py (1)
operation_name(429-441)getstream/models/__init__.py (6)
ListImportV2TasksResponse(11189-11195)ImportV2TaskSettings(10750-10756)UserRequest(21095-21119)CreateImportV2TaskResponse(6949-6973)DeleteImportV2TaskResponse(7505-7506)GetImportV2TaskResponse(10066-10090)getstream/utils/__init__.py (2)
build_query_param(94-126)build_body_dict(151-175)getstream/base.py (6)
get(222-238)get(411-427)post(240-256)post(429-445)delete(276-292)delete(465-481)
getstream/common/rest_client.py (4)
getstream/common/async_rest_client.py (4)
list_import_v2_tasks(432-438)create_import_v2_task(441-453)delete_import_v2_task(456-466)get_import_v2_task(469-477)getstream/models/__init__.py (6)
ListImportV2TasksResponse(11189-11195)ImportV2TaskSettings(10750-10756)UserRequest(21095-21119)CreateImportV2TaskResponse(6949-6973)DeleteImportV2TaskResponse(7505-7506)GetImportV2TaskResponse(10066-10090)getstream/utils/__init__.py (2)
build_query_param(94-126)build_body_dict(151-175)getstream/base.py (6)
get(222-238)get(411-427)post(240-256)post(429-445)delete(276-292)delete(465-481)
getstream/video/call.py (4)
getstream/video/async_call.py (1)
stop_recording(455-463)getstream/video/async_rest_client.py (1)
stop_recording(754-769)getstream/video/rest_client.py (1)
stop_recording(750-765)getstream/models/__init__.py (1)
StopRecordingResponse(18228-18229)
getstream/moderation/rest_client.py (1)
getstream/models/__init__.py (19)
UserRequest(21095-21119)AppealResponse(1562-1564)GetAppealResponse(9727-9731)ModerationPayloadRequest(12953-12965)BanActionRequestPayload(2044-2062)BlockActionRequestPayload(2183-2186)CustomActionRequestPayload(7100-7104)DeleteActivityRequestPayload(7345-7351)DeleteCommentRequestPayload(7440-7446)DeleteMessageRequestPayload(7510-7516)DeleteReactionRequestPayload(7541-7547)DeleteUserRequestPayload(7588-7603)MarkReviewedRequestPayload(11382-11392)RejectAppealRequestPayload(16404-16405)RestoreActionRequestPayload(16681-16684)ShadowBlockActionRequestPayload(17837-17840)UnbanActionRequestPayload(18934-18940)UnblockActionRequestPayload(18959-18962)SubmitActionResponse(18352-18359)
getstream/chat/async_rest_client.py (4)
getstream/chat/rest_client.py (1)
mark_delivered(145-157)getstream/models/__init__.py (4)
DeliveredMessagePayload(7637-7639)MarkDeliveredResponse(11315-11316)ChannelInputRequest(4990-5018)MessageActionResponse(11676-11680)getstream/utils/__init__.py (2)
build_query_param(94-126)build_body_dict(151-175)getstream/base.py (2)
post(240-256)post(429-445)
getstream/video/async_call.py (5)
getstream/video/async_rest_client.py (1)
stop_recording(754-769)getstream/video/call.py (1)
stop_recording(435-443)getstream/video/rest_client.py (1)
stop_recording(750-765)getstream/stream_response.py (1)
StreamResponse(11-36)getstream/models/__init__.py (1)
StopRecordingResponse(18228-18229)
getstream/chat/rest_client.py (5)
getstream/chat/async_rest_client.py (1)
mark_delivered(147-159)getstream/models/__init__.py (4)
DeliveredMessagePayload(7637-7639)MarkDeliveredResponse(11315-11316)ChannelInputRequest(4990-5018)MessageActionResponse(11676-11680)getstream/stream_response.py (2)
StreamResponse(11-36)data(22-24)getstream/utils/__init__.py (2)
build_query_param(94-126)build_body_dict(151-175)getstream/base.py (2)
post(240-256)post(429-445)
getstream/moderation/async_rest_client.py (3)
getstream/moderation/rest_client.py (5)
appeal(35-52)get_appeal(55-61)query_appeals(64-83)ban(86-109)unban(548-567)getstream/models/__init__.py (19)
UserRequest(21095-21119)AppealResponse(1562-1564)GetAppealResponse(9727-9731)QueryAppealsResponse(14781-14785)BanActionRequestPayload(2044-2062)BlockActionRequestPayload(2183-2186)CustomActionRequestPayload(7100-7104)DeleteActivityRequestPayload(7345-7351)DeleteCommentRequestPayload(7440-7446)DeleteMessageRequestPayload(7510-7516)DeleteReactionRequestPayload(7541-7547)DeleteUserRequestPayload(7588-7603)MarkReviewedRequestPayload(11382-11392)RejectAppealRequestPayload(16404-16405)RestoreActionRequestPayload(16681-16684)ShadowBlockActionRequestPayload(17837-17840)UnbanActionRequestPayload(18934-18940)UnblockActionRequestPayload(18959-18962)SubmitActionResponse(18352-18359)getstream/utils/__init__.py (1)
build_body_dict(151-175)
getstream/feeds/rest_client.py (5)
getstream/common/telemetry.py (1)
operation_name(429-441)getstream/base.py (6)
post(240-256)post(429-445)delete(276-292)delete(465-481)get(222-238)get(411-427)getstream/stream_response.py (2)
StreamResponse(11-36)data(22-24)getstream/utils/__init__.py (2)
build_body_dict(151-175)build_query_param(94-126)getstream/feeds/feeds.py (1)
delete(17-24)
getstream/video/rest_client.py (2)
getstream/models/__init__.py (4)
QueryCallStatsMapResponse(14968-15032)SortParamRequest(17992-17999)QueryCallSessionParticipantStatsResponse(14918-14951)NotificationSettingsRequest(13341-13359)getstream/utils/__init__.py (2)
build_body_dict(151-175)build_query_param(94-126)
getstream/webhook.py (1)
getstream/models/__init__.py (9)
CustomEvent(7151-7170)AppealAcceptedEvent(1425-1449)CallAcceptedEvent(2608-2620)BlockedUserEvent(2287-2303)ClosedCaptionEvent(5975-5990)CallCreatedEvent(2701-2713)ChannelCreatedEvent(4728-4770)ChannelUpdatedEvent(5695-5743)ChannelVisibleEvent(5747-5789)
🔇 Additional comments (40)
getstream/moderation/rest_client.py (5)
34-83: LGTM - New appeal endpoints implemented correctly.The three new appeal-related methods (
appeal,get_appeal,query_appeals) follow the established patterns in this file with proper telemetry decorators, body construction, and return types matching the corresponding response models.
314-314: Inconsistent payload type:flagusesModerationPayloadwhilecustom_checkusesModerationPayloadRequest.Similar to
check, theflagmethod'smoderation_payloadparameter still usesModerationPayloadrather thanModerationPayloadRequest. The AI summary indicates this should have been changed toModerationPayloadRequest.
262-262: Type updated toModerationPayloadRequest.This aligns with the refactored OpenAPI specs using
*Requestpayload variants.
296-296: Type updated toFeedsModerationTemplateConfigPayload.This matches the payload type refactoring pattern.
499-545:submit_actionexpanded with new parameters and updated payload types.The method signature changes look correct:
- Added
appeal_idparameter to support appeal actionsitem_idchanged toOptional[str]to accommodate appeal-based actions- Payload types renamed to
*RequestPayloadvariants- New action payloads added:
reject_appeal,restore,unblockThe body construction correctly includes all new fields.
getstream/moderation/async_rest_client.py (6)
34-85: LGTM - Async appeal endpoints implemented correctly.The async versions of
appeal,get_appeal, andquery_appealscorrectly mirror the synchronous implementations with properasync/awaitusage.
135-135: Same inconsistency:checkusesModerationPayloadinstead ofModerationPayloadRequest.This mirrors the same issue in the synchronous client. If
custom_checkwas updated to useModerationPayloadRequest,checkshould likely be updated as well for consistency.
320-320: Same inconsistency:flagusesModerationPayloadinstead ofModerationPayloadRequest.Mirrors the sync client issue. The AI summary indicates this should have been changed.
266-266: Type updated toModerationPayloadRequest.Consistent with the sync client update.
302-302: Type updated toFeedsModerationTemplateConfigPayload.Consistent with the sync client update.
507-553:submit_actionasync version correctly mirrors sync implementation.All parameter and payload type changes align with the synchronous version:
appeal_idaddeditem_idnowOptional[str]- Payload types use
*RequestPayloadvariants- New actions:
reject_appeal,restore,unblockgetstream/webhook.py (5)
1-7: LGTM on imports structure.Standard library imports and typing are appropriate for webhook handling functionality.
170-326: LGTM on event type constants.Comprehensive coverage of webhook event types with clear naming conventions.
329-348: LGTM onget_event_typeimplementation.The function handles all input types correctly with appropriate exception handling and documented failure behavior.
385-543: Event mapping is comprehensive and correct.The distinction between
"*"(CustomEvent) and"custom"(CustomVideoEvent) is intentional. Theevent_mapdictionary is recreated on each call, which is a minor inefficiency but acceptable for generated code.
546-565: LGTM on signature verification.The use of
hmac.compare_digest()for timing-safe comparison is correct security practice, preventing timing attacks on signature validation.getstream/video/async_call.py (1)
454-463:stop_recording()wrapper wiring looks correct.
Pure formatting/refactor; still forwardstype/idand syncs response.getstream/video/async_rest_client.py (4)
308-344:go_live/stop_liverequest-body propagation looks consistent.
New recording flags are included in the request body viabuild_body_dict(...)and forwarded correctly.Also applies to: 718-752
1038-1057: Payload type shift toNotificationSettingsRequestlooks correct.
UsingNotificationSettingsRequestaligns with request-body semantics (vs response-ish models).Also applies to: 1078-1102
1107-1123:resolve_sip_inbound(... challenge: SIPChallengeRequest ...)looks consistent with request typing.
The request body usesbuild_body_dict(challenge=challenge)and should serialize via the model’sto_dict.
753-770: Empty JSON body is consistent with established codebase patterns, but backend contract verification still required.Both sync and async
stop_recordingimplementations use identical patterns—callingbuild_body_dict()with no arguments. This matches the standardized approach elsewhere in the codebase for endpoints without request parameters. However, explicit confirmation of the backend API contract expectations for this endpoint should be validated against the API specification or integration tests.getstream/video/rest_client.py (3)
304-340:go_live/stop_livepropagation looks consistent with the async variant.Also applies to: 714-748
1034-1051:NotificationSettingsRequestusage looks correct for create/update call type.Also applies to: 1072-1096
1101-1117:SIPChallengeRequesttyping looks consistent for request payload.getstream/video/call.py (1)
434-444:stop_recording()wrapper looks correct (formatting-only change).getstream/feeds/feeds.py (1)
68-87:update(...)new fields are forwarded correctly.
description,name, andfilter_tagsare passed through toclient.update_feed(...)as expected.getstream/common/async_rest_client.py (1)
431-478: Import V2 task endpoints look consistent and correctly shaped.
Verb/path usage and request construction follow the existing client patterns.getstream/common/rest_client.py (1)
423-466: Sync Import V2 task endpoints look consistent with async variant.getstream/chat/rest_client.py (3)
144-157: Newmark_deliveredendpoint looks good.The implementation correctly passes
user_idas a query parameter andlatest_delivered_messagesin the request body. This aligns with the async version inasync_rest_client.py.
627-654: Addition ofmessage_timestampparameter looks correct.The new optional
message_timestampparameter is properly added to both the method signature and the request body, consistent with the async version.
1042-1059: Return type change toMessageActionResponseis a breaking change.The return type changed from
MessageResponsetoMessageActionResponseforrun_message_action. This is a breaking change for existing consumers. The same applies tocommit_messageandtranslate_messagebelow.Since this is generated code, please ensure that downstream consumers are aware of these breaking changes to:
run_message_action(line 1049)commit_message(line 1065)translate_message(line 1190)getstream/chat/async_rest_client.py (4)
146-159: Asyncmark_deliveredimplementation is consistent with sync version.The implementation correctly mirrors the sync version with proper
awaitusage.
264-264: SameChannelInputvsChannelInputRequestinconsistency as sync version.As noted in
rest_client.py,update_channelusesChannelInputRequestwhileget_or_create_distinct_channel(line 182) andget_or_create_channel(line 529) still useChannelInput.Please confirm this is intentional per the OpenAPI spec.
631-658:mark_unreadchanges are consistent with sync version.The
message_timestampparameter addition is correctly implemented.
1052-1085: Return type changes toMessageActionResponsemirror sync version.The async versions of
run_message_action,commit_message, andtranslate_messagecorrectly use the updatedMessageActionResponsereturn type.getstream/feeds/rest_client.py (5)
3-3: Telemetry integration looks good.The telemetry import and decorator usage across all methods follows the established pattern in the codebase.
509-564: New collection methods follow established patterns.The
delete_collections,read_collections,update_collections,create_collections, andupsert_collectionsmethods are correctly implemented with appropriate HTTP methods and telemetry decorators.
1314-1344: New feed visibility methods look correct.The
list_feed_visibilities,get_feed_visibility, andupdate_feed_visibilitymethods follow the established REST patterns.
1346-1373: New batch operations follow established patterns.The
create_feeds_batch,delete_feeds_batch, andown_batchmethods are correctly implemented.
1619-1632:delete_feed_user_datachanged from DELETE to POST.The method now uses
self.postinstead ofself.deleteand includeshard_deletein the request body rather than as a query parameter. This appears intentional based on the endpoint path/users/{user_id}/delete.
| remove_filter_tags: Optional[List[str]] = None, | ||
| remove_members: Optional[List[str]] = None, | ||
| data: Optional[ChannelInput] = None, | ||
| data: Optional[ChannelInputRequest] = None, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Search for all usages of ChannelInput and ChannelInputRequest in chat client files
echo "=== ChannelInput usages ==="
rg -n "ChannelInput[^R]" getstream/chat/rest_client.py getstream/chat/async_rest_client.py
echo ""
echo "=== ChannelInputRequest usages ==="
rg -n "ChannelInputRequest" getstream/chat/rest_client.py getstream/chat/async_rest_client.pyRepository: GetStream/stream-py
Length of output: 632
🏁 Script executed:
#!/bin/bash
# Get context around the lines where ChannelInput and ChannelInputRequest are used
echo "=== Context around line 178 (ChannelInput) ==="
sed -n '170,185p' getstream/chat/rest_client.py
echo ""
echo "=== Context around line 260 (ChannelInputRequest) ==="
sed -n '252,270p' getstream/chat/rest_client.py
echo ""
echo "=== Context around line 525 (ChannelInput) ==="
sed -n '517,535p' getstream/chat/rest_client.py
# Also check the imports and type definitions
echo ""
echo "=== Type definitions and imports ==="
rg -n "class ChannelInput|class ChannelInputRequest" getstream/Repository: GetStream/stream-py
Length of output: 2575
🏁 Script executed:
#!/bin/bash
# Examine the type definitions to understand the difference
echo "=== ChannelInput definition ==="
sed -n '/^class ChannelInput/,/^class [A-Z]/p' getstream/models/__init__.py | head -50
echo ""
echo "=== ChannelInputRequest definition ==="
sed -n '/^class ChannelInputRequest/,/^class [A-Z]/p' getstream/models/__init__.py | head -50
# Identify method at line 260
echo ""
echo "=== Full method context at line 260 ==="
sed -n '240,280p' getstream/chat/rest_client.py | head -40Repository: GetStream/stream-py
Length of output: 4897
Confirm type inconsistency across channel methods requires alignment.
The inconsistency is real: get_or_create_distinct_channel (line 178) and get_or_create_channel (line 525) use ChannelInput, while update_channel (line 260) uses ChannelInputRequest. The two types differ structurally—ChannelInput includes created_by_id, truncated_by_id, and filter_tags fields, while ChannelInputRequest uses ConfigOverridesRequest instead of ChannelConfig. Verify per the OpenAPI spec whether this distinction is intentional (e.g., create vs. update operations allow different fields) or if all three methods should use the same type.
| @telemetry.operation_name("getstream.api.feeds.query_feeds_usage_stats") | ||
| def query_feeds_usage_stats( | ||
| self, _from: Optional[str] = None, to: Optional[str] = None | ||
| ) -> StreamResponse[QueryFeedsUsageStatsResponse]: | ||
| json = build_body_dict(_from=_from, to=to) | ||
| return self.post( | ||
| "/api/v2/feeds/stats/usage", QueryFeedsUsageStatsResponse, json=json | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the build_body_dict function definition
rg -n "def build_body_dict" --type py -A 10Repository: GetStream/stream-py
Length of output: 1748
🏁 Script executed:
#!/bin/bash
# Search for other uses of _from in the codebase to understand the pattern
rg -n "_from" --type py getstream/ -B 2 -A 2 | head -60Repository: GetStream/stream-py
Length of output: 4773
🏁 Script executed:
#!/bin/bash
# Check if there are API specs or documentation about the expected JSON keys
find . -type f \( -name "*.md" -o -name "*.rst" -o -name "*.txt" \) -exec grep -l "feeds.*usage.*stats\|query.*feeds.*usage\|_from\|from.*parameter" {} \;Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Read the build_body_dict implementation
sed -n '151,177p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 1033
🏁 Script executed:
#!/bin/bash
# Look for tests related to build_body_dict or query_feeds_usage_stats
find . -type f -name "*.py" -path "*/test*" -exec grep -l "build_body_dict\|query_feeds_usage_stats\|query_aggregate_call_stats" {} \;Repository: GetStream/stream-py
Length of output: 87
🏁 Script executed:
#!/antml:bash
# Search for any place where the JSON key is explicitly set to "from" without underscore
rg -n '"from"' --type py getstream/ | head -20Repository: GetStream/stream-py
Length of output: 310
🏁 Script executed:
#!/bin/bash
# Find the QueryFeedsUsageStatsResponse model and check its structure
rg -n "class QueryFeedsUsageStatsResponse" --type py -A 10Repository: GetStream/stream-py
Length of output: 912
🏁 Script executed:
#!/bin/bash
# Search for any test files that test the query_feeds_usage_stats method
rg -n "query_feeds_usage_stats" --type pyRepository: GetStream/stream-py
Length of output: 244
🏁 Script executed:
#!/bin/bash
# Look at the test_decoding.py file to understand the patterns
cat tests/test_decoding.py | head -100Repository: GetStream/stream-py
Length of output: 3192
🏁 Script executed:
#!/bin/bash
# Search for tests that actually send requests with _from parameter
rg -n "from.*to" --type py tests/ -B 5 -A 5 | head -40Repository: GetStream/stream-py
Length of output: 2608
🏁 Script executed:
#!/bin/bash
# Check if there are any comments or documentation about the API endpoint
rg -n "/api/v2/feeds/stats" --type py -B 5 -A 5Repository: GetStream/stream-py
Length of output: 915
🏁 Script executed:
#!/antml:bash
# Look for other similar patterns where build_body_dict is used with keyword arguments
rg -n "build_body_dict\(" --type py -A 1 | head -40Repository: GetStream/stream-py
Length of output: 2059
🏁 Script executed:
#!/an/bash
# Check if there's any request body transformation elsewhere
rg -n "field_name" --type py getstream/models/__init__.py | grep -i "from\|to" | head -20Repository: GetStream/stream-py
Length of output: 1640
🏁 Script executed:
#!/bin/bash
# Look for any comments or patterns around reserved keywords in requests vs responses
rg -n "reserved\|keyword" --type py getstream/ | head -20Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/antml:bash
# Check how other Python clients handle this pattern - look for similar underscore-prefixed parameters
rg -n "def.*_from.*to.*Optional" --type py -A 5 | head -50Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Search for any explicit JSON mapping in request building
rg -n "\"from\"" --type py -B 3 -A 3 | head -40Repository: GetStream/stream-py
Length of output: 1158
🏁 Script executed:
#!/an/bash
# Look at request/response examples if they exist in comments or docs
find . -type f \( -name "*.md" -o -name "*.rst" -o -name "*.json" \) -exec grep -l "feeds.*stats\|query.*usage" {} \;Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Check how the video client handles query_aggregate_call_stats - does it use build_body_dict or QueryAggregateCallStatsRequest?
sed -n '1241,1250p' getstream/video/rest_client.pyRepository: GetStream/stream-py
Length of output: 483
🏁 Script executed:
#!/an/bash
# Check if QueryAggregateCallStatsRequest is used anywhere or if build_body_dict is consistently used
rg -n "QueryAggregateCallStatsRequest\|QueryFeedsUsageStatsRequest" --type pyRepository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Check the async version in feeds to see if it has the same pattern
rg -n "query_feeds_usage_stats" --type py -A 10 | head -30Repository: GetStream/stream-py
Length of output: 1028
🏁 Script executed:
#!/an/bash
# Check if request models are instantiated and used in any request building
rg -n "QueryFeedsUsageStatsRequest\|QueryAggregateCallStatsRequest" --type py -B 2 -A 2Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Check if there are other methods that properly use request models for building request bodies
rg -n "to_dict\(\)" --type py getstream/feeds/rest_client.py -B 5 | head -40Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Check if DataClassJsonMixin has proper serialization that respects field_name
rg -n "DataClassJsonMixin" --type py getstream/models/__init__.py | head -5Repository: GetStream/stream-py
Length of output: 282
🏁 Script executed:
#!/an/bash
# Look for examples of request model instantiation and to_dict usage
rg -n "\.to_dict\(\)" --type py getstream/ | head -20Repository: GetStream/stream-py
Length of output: 129
🏁 Script executed:
#!/an/bash
# Check if DataClassJsonMixin.to_dict() respects field_name metadata
# Let's look at a test to see how DataClassJsonMixin serializes with field_name
rg -n "to_dict" --type py tests/ -B 5 -A 5 | head -50Repository: GetStream/stream-py
Length of output: 1143
🏁 Script executed:
#!/an/bash
# Create a simple test to verify DataClassJsonMixin behavior with field_name
python3 << 'EOF'
from dataclasses import dataclass, field as dc_field
from dataclasses_json import DataClassJsonMixin, config as dc_config
from typing import Optional
# Simulate the QueryFeedsUsageStatsRequest structure
@dataclass
class TestRequest(DataClassJsonMixin):
_from: Optional[str] = dc_field(default=None, metadata=dc_config(field_name="from"))
to: Optional[str] = dc_field(default=None, metadata=dc_config(field_name="to"))
# Test with values
req = TestRequest(_from="2024-01-01", to="2024-12-31")
print("to_dict():", req.to_dict())
# Test with None values
req_none = TestRequest(_from=None, to=None)
print("to_dict() with None:", req_none.to_dict())
# Test to_json
print("to_json():", req.to_json())
EOFRepository: GetStream/stream-py
Length of output: 149
🏁 Script executed:
#!/an/bash
# Simpler test without network - just check if dataclasses_json is available and test locally
python3 -c "
from dataclasses import dataclass, field as dc_field
from dataclasses_json import DataClassJsonMixin, config as dc_config
from typing import Optional
@dataclass
class TestRequest(DataClassJsonMixin):
_from: Optional[str] = dc_field(default=None, metadata=dc_config(field_name='from'))
to: Optional[str] = dc_field(default=None, metadata=dc_config(field_name='to'))
req = TestRequest(_from='2024-01-01', to='2024-12-31')
print('Result:', req.to_dict())
" 2>&1 || echo "Test failed or packages not available"Repository: GetStream/stream-py
Length of output: 149
🏁 Script executed:
#!/an/bash
# Check if there's any pattern where request models are used for serialization
rg -n "QueryFeedsUsageStatsRequest\|QueryAggregateCallStatsRequest\|QueryCallStatsRequest" --type py -B 3 -A 3Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Check how the post method works - does it handle serialization?
sed -n '1,100p' getstream/feeds/rest_client.py | grep -A 20 "def post"Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Search for the post method in rest_client.py
rg -n "def post" --type py getstream/feeds/rest_client.py -A 10 | head -30Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Look at the parent class or base to understand post method
rg -n "class.*RestClient\|def post" --type py getstream/feeds/rest_client.py | head -10Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# Find where RestClient is defined
fd -e py -x grep -l "class RestClient" {} \;Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/an/bash
# List the files in getstream/feeds to understand structure
ls -la getstream/feeds/Repository: GetStream/stream-py
Length of output: 422
🏁 Script executed:
#!/an/bash
# Check if rest_client.py has class definition
head -50 getstream/feeds/rest_client.pyRepository: GetStream/stream-py
Length of output: 1853
🏁 Script executed:
#!/an/bash
# Search for where the json parameter is used in post calls
rg -n "self.post" --type py getstream/feeds/rest_client.py -B 2 | head -40Repository: GetStream/stream-py
Length of output: 1136
Use the QueryFeedsUsageStatsRequest model to properly serialize the _from parameter.
The build_body_dict() function produces {"_from": ...} but the API expects {"from": ...} based on the request model definition. The QueryFeedsUsageStatsRequest model already has the correct field mapping configured. Instantiate and serialize it instead:
Recommended fix
@telemetry.operation_name("getstream.api.feeds.query_feeds_usage_stats")
def query_feeds_usage_stats(
self, _from: Optional[str] = None, to: Optional[str] = None
) -> StreamResponse[QueryFeedsUsageStatsResponse]:
- json = build_body_dict(_from=_from, to=to)
+ request = QueryFeedsUsageStatsRequest(_from=_from, to=to)
+ json = request.to_dict()
return self.post(
"/api/v2/feeds/stats/usage", QueryFeedsUsageStatsResponse, json=json
)Note: The same issue exists in query_aggregate_call_stats in the video client.
🤖 Prompt for AI Agents
In @getstream/feeds/rest_client.py around lines 1592 - 1599, The current
query_feeds_usage_stats method constructs the request body with build_body_dict
which yields {"_from": ...} but the API expects the field mapped as "from"; fix
by creating a QueryFeedsUsageStatsRequest instance with _from and to, serialize
that model for the POST payload (replace the build_body_dict/json assignment
with model instantiation and model.dict()/serialize call per existing patterns),
ensuring the correct field name is sent; also apply the same change to the
query_aggregate_call_stats method in the video client.
| @telemetry.operation_name("getstream.api.video.get_call_stats_map") | ||
| async def get_call_stats_map( | ||
| self, | ||
| call_type: str, | ||
| call_id: str, | ||
| session: str, | ||
| start_time: Optional[datetime] = None, | ||
| end_time: Optional[datetime] = None, | ||
| exclude_publishers: Optional[bool] = None, | ||
| exclude_subscribers: Optional[bool] = None, | ||
| exclude_sfus: Optional[bool] = None, | ||
| ) -> StreamResponse[QueryCallStatsMapResponse]: | ||
| query_params = build_query_param( | ||
| start_time=start_time, | ||
| end_time=end_time, | ||
| exclude_publishers=exclude_publishers, | ||
| exclude_subscribers=exclude_subscribers, | ||
| exclude_sfus=exclude_sfus, | ||
| ) | ||
| path_params = { | ||
| "call_type": call_type, | ||
| "call_id": call_id, | ||
| "session": session, | ||
| } | ||
| return await self.get( | ||
| "/api/v2/video/call_stats/{call_type}/{call_id}/{session}/map", | ||
| QueryCallStatsMapResponse, | ||
| query_params=query_params, | ||
| path_params=path_params, | ||
| ) | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find build_query_param implementation
rg -n "def build_query_param" --type pyRepository: GetStream/stream-py
Length of output: 203
🏁 Script executed:
#!/bin/bash
# Check the implementation of build_query_param for datetime handling
rg -A 30 "def build_query_param" getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 2651
🏁 Script executed:
#!/bin/bash
# Verify get_call_stats_map method exists and matches the snippet
rg -A 25 "async def get_call_stats_map" getstream/video/async_rest_client.pyRepository: GetStream/stream-py
Length of output: 1011
🏁 Script executed:
#!/bin/bash
# Get more lines from build_query_param to see the complete else clause
sed -n '94,120p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 1147
🏁 Script executed:
#!/bin/bash
# Also check if there's any import of datetime or datetime handling in utils
rg -n "datetime" getstream/utils/__init__.py | head -20Repository: GetStream/stream-py
Length of output: 867
🏁 Script executed:
#!/bin/bash
# Get the complete build_query_param function including the else clause
sed -n '94,128p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 1432
Bug confirmed: datetime query params will cause TypeError in build_query_param.
get_call_stats_map passes start_time and end_time as datetime objects to build_query_param(), but the function doesn't handle datetime types. It falls through to json.dumps(value), which cannot serialize datetime objects by default, causing a TypeError at runtime.
Proposed fix (convert datetime to ISO-8601 before build_query_param)
@@
async def get_call_stats_map(
@@
exclude_sfus: Optional[bool] = None,
) -> StreamResponse[QueryCallStatsMapResponse]:
+ if isinstance(start_time, datetime):
+ start_time = start_time.isoformat()
+ if isinstance(end_time, datetime):
+ end_time = end_time.isoformat()
query_params = build_query_param(
start_time=start_time,
end_time=end_time,
exclude_publishers=exclude_publishers,
exclude_subscribers=exclude_subscribers,
exclude_sfus=exclude_sfus,
)🤖 Prompt for AI Agents
In @getstream/video/async_rest_client.py around lines 890 - 920,
get_call_stats_map currently forwards start_time and end_time datetime objects
to build_query_param which json.dumps can't serialize; convert start_time and
end_time to ISO-8601 strings (e.g., via start_time.isoformat()) before calling
build_query_param, or add a small helper to normalize datetimes to strings and
use it in get_call_stats_map so build_query_param only receives serializable
values; keep the rest of the method and path_params unchanged.
| @telemetry.operation_name("getstream.api.video.get_call_stats_map") | ||
| def get_call_stats_map( | ||
| self, | ||
| call_type: str, | ||
| call_id: str, | ||
| session: str, | ||
| start_time: Optional[datetime] = None, | ||
| end_time: Optional[datetime] = None, | ||
| exclude_publishers: Optional[bool] = None, | ||
| exclude_subscribers: Optional[bool] = None, | ||
| exclude_sfus: Optional[bool] = None, | ||
| ) -> StreamResponse[QueryCallStatsMapResponse]: | ||
| query_params = build_query_param( | ||
| start_time=start_time, | ||
| end_time=end_time, | ||
| exclude_publishers=exclude_publishers, | ||
| exclude_subscribers=exclude_subscribers, | ||
| exclude_sfus=exclude_sfus, | ||
| ) | ||
| path_params = { | ||
| "call_type": call_type, | ||
| "call_id": call_id, | ||
| "session": session, | ||
| } | ||
| return self.get( | ||
| "/api/v2/video/call_stats/{call_type}/{call_id}/{session}/map", | ||
| QueryCallStatsMapResponse, | ||
| query_params=query_params, | ||
| path_params=path_params, | ||
| ) | ||
|
|
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find build_query_param function definition and check its implementation
rg -n "def build_query_param" -A 20 getstream/Repository: GetStream/stream-py
Length of output: 3140
🏁 Script executed:
#!/bin/bash
# Look for both sync and async client implementations of get_call_stats_map
rg -n "def get_call_stats_map" getstream/ -B 2 -A 15Repository: GetStream/stream-py
Length of output: 2813
🏁 Script executed:
#!/bin/bash
# Check imports in rest_client.py to understand what modules are available
head -50 getstream/video/rest_client.pyRepository: GetStream/stream-py
Length of output: 1944
🏁 Script executed:
#!/bin/bash
# Get the complete build_query_param function implementation
sed -n '94,128p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 1432
🏁 Script executed:
#!/bin/bash
# Check imports and context in utils/__init__.py around build_query_param
head -100 getstream/utils/__init__.py | tail -50Repository: GetStream/stream-py
Length of output: 1473
🏁 Script executed:
#!/bin/bash
# Search for custom JSON encoder or datetime handling in the codebase
rg -n "JSONEncoder" getstream/Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check if datetime objects are actually used with build_query_param elsewhere
rg -n "datetime" getstream/video/rest_client.py | head -20Repository: GetStream/stream-py
Length of output: 312
🏁 Script executed:
#!/bin/bash
# Find datetime_to_iso_string function and check its usage
rg -n "datetime_to_iso_string\|def.*iso" getstream/utils/__init__.py -B 2 -A 5Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check the full imports and beginning of utils/__init__.py
head -30 getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 741
🏁 Script executed:
#!/bin/bash
# Search for any datetime conversion or isoformat usage in rest_client.py
rg -n "isoformat\|strftime" getstream/video/rest_client.pyRepository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check if there's a test for get_call_stats_map to see if datetime is actually tested
rg -n "get_call_stats_map" getstream/ --type py -B 2 -A 10 | grep -A 10 "test\|Test"Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check what happens in json.dumps when datetime is passed - verify the actual error
python3 << 'EOF'
import json
from datetime import datetime
# Try to serialize a datetime object
dt = datetime.now()
try:
result = json.dumps(dt)
print(f"Success: {result}")
except TypeError as e:
print(f"Error: {e}")
print(f"Type: {type(dt)}")
EOFRepository: GetStream/stream-py
Length of output: 152
🏁 Script executed:
#!/bin/bash
# Check if datetime is being converted to ISO format before being passed to build_query_param
rg -n "start_time\|end_time" getstream/video/rest_client.py -B 3 -A 3 | grep -A 5 "get_call_stats_map"Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check if there's a wrapper or conversion happening in the base client or get method
rg -n "def get\(" getstream/base.py -A 15 | head -50Repository: GetStream/stream-py
Length of output: 1168
🏁 Script executed:
#!/bin/bash
# Check _request_sync and _request_async to see how query_params are handled
rg -n "_request_sync\|_request_async" getstream/base.py -A 30 | head -100Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Look for where query_params are actually used in the request
rg -n "query_params" getstream/base.py -B 2 -A 2 | head -60Repository: GetStream/stream-py
Length of output: 2121
🏁 Script executed:
#!/bin/bash
# Check _prepare_request to see full implementation
sed -n '98,160p' getstream/base.pyRepository: GetStream/stream-py
Length of output: 2143
🏁 Script executed:
#!/bin/bash
# Look for other methods that handle datetime parameters to see if there's a pattern
rg -n "datetime\|isoformat" getstream/video/rest_client.py -B 5 -A 2 | head -80Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check other similar methods in the async client to understand the pattern
rg -n "start_time\|end_time" getstream/video/async_rest_client.py -B 3 -A 8 | head -100Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Search for other query param methods to see if any convert datetime
rg -n "build_query_param" getstream/video/rest_client.py -B 10 | grep -A 10 "datetime\|start\|end" | head -80Repository: GetStream/stream-py
Length of output: 793
🏁 Script executed:
#!/bin/bash
# Look at the complete get_call_stats method to see if there's datetime handling
rg -n "get_call_stats" getstream/video/rest_client.py -A 25 | head -100Repository: GetStream/stream-py
Length of output: 1179
🏁 Script executed:
#!/bin/bash
# Check if datetime has isoformat method being called anywhere in the codebase
rg -n "\.isoformat\(\)" getstream/ --type py | head -20Repository: GetStream/stream-py
Length of output: 121
🏁 Script executed:
#!/bin/bash
# Check the isoformat function and surrounding code
sed -n '45,60p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 412
🏁 Script executed:
#!/bin/bash
# Search for where datetime objects should be converted to ISO format
rg -n "datetime_to_iso_string\|to_iso\|\.isoformat" getstream/utils/__init__.py -B 3 -A 3Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Look for any conversion of datetime in the entire getstream directory
rg -n "strftime\|isoformat" getstream/ --type pyRepository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Find the complete function name for datetime conversion
sed -n '40,55p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 443
🏁 Script executed:
#!/bin/bash
# Check if datetime_to_iso_string is exported and used
rg -n "datetime_to_iso\|datetime_to" getstream/utils/__init__.py -B 2 -A 5Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check what's exported from utils/__init__.py
rg -n "__all__\|^def " getstream/utils/__init__.py | head -30Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Find the function definition for datetime encoding
sed -n '35,55p' getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 593
🏁 Script executed:
#!/bin/bash
# Check imports in rest_client.py to see if datetime_to_iso_string or similar is imported
head -20 getstream/video/rest_client.pyRepository: GetStream/stream-py
Length of output: 695
🏁 Script executed:
#!/bin/bash
# Test what happens when we pass a datetime to build_query_param
python3 << 'EOF'
import sys
sys.path.insert(0, '/tmp/repo')
from datetime import datetime
import json
# Simulate build_query_param behavior
def build_query_param(**kwargs):
params = {}
for key, value in kwargs.items():
if value is None:
continue
if hasattr(value, "to_json") and callable(value.to_json):
params[key] = value.to_json()
elif isinstance(value, bool):
params[key] = str(value).lower()
elif isinstance(value, (str, int)):
params[key] = str(value)
elif isinstance(value, list):
from urllib.parse import quote
params[key] = ",".join(quote(str(v)) for v in value)
else:
# For dictionaries or any other types of objects
params[key] = json.dumps(value)
return params
# Test with datetime
dt = datetime.now()
try:
result = build_query_param(start_time=dt)
print(f"Success: {result}")
except TypeError as e:
print(f"Error: {e}")
print("This confirms datetime objects will cause json.dumps() to fail")
EOFRepository: GetStream/stream-py
Length of output: 181
🏁 Script executed:
#!/bin/bash
# Check if encode_datetime is used anywhere in the codebase
rg -n "encode_datetime" getstream/ --type pyRepository: GetStream/stream-py
Length of output: 45004
🏁 Script executed:
#!/bin/bash
# Check other methods in rest_client.py that have datetime parameters to see if they handle it
rg -n "def.*datetime" getstream/video/rest_client.py -A 15 | head -100Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Search for any method that properly handles datetime in query params
rg -n "start_time\|end_time" getstream/ --type py -B 5 -A 10 | grep -B 5 -A 5 "encode_datetime\|isoformat" | head -80Repository: GetStream/stream-py
Length of output: 45
🏁 Script executed:
#!/bin/bash
# Check if build_query_param needs to import and use encode_datetime
grep -n "encode_datetime" getstream/utils/__init__.pyRepository: GetStream/stream-py
Length of output: 156
🏁 Script executed:
#!/bin/bash
# Verify the issue by checking the actual build_query_param implementation one more time
# to confirm datetime is NOT handled
sed -n '94,128p' getstream/utils/__init__.py | grep -n "datetime"Repository: GetStream/stream-py
Length of output: 45
build_query_param does not handle datetime objects—passes to json.dumps() and will raise TypeError.
Both get_call_stats_map in sync (rest_client.py lines 887–916) and async (async_rest_client.py lines 891–906) clients accept datetime parameters (start_time, end_time) and pass them directly to build_query_param. The function falls through to json.dumps(value) for unhandled types, which cannot serialize datetime objects and will raise TypeError: Object of type datetime is not JSON serializable. The codebase has an encode_datetime utility function for ISO serialization, but it is not integrated into build_query_param.
🤖 Prompt for AI Agents
In @getstream/video/rest_client.py around lines 886 - 916, The query builder is
serializing datetime via json.dumps and fails; update build_query_param to
detect datetime (and datetime-like) values and serialize them using the existing
encode_datetime utility (or call encode_datetime for the start_time/end_time
before passing) so ISO strings are produced; ensure build_query_param handles
both naive and aware datetimes and lists/dicts containing datetimes so calls
from get_call_stats_map (and its async counterpart) will no longer raise
TypeError.
d1cc8b8 to
5f647cd
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@getstream/tests/test_webhook.py`:
- Around line 77-716: Tests call parse_webhook_event with payloads that lack
required dataclass fields (e.g., CustomEvent requires created_at and custom;
MessageNewEvent requires message_id, watcher_count, message), causing TypeError
when event_class(**data) in parse_webhook_event is invoked; fix by updating the
test payloads passed to parse_webhook_event to include all mandatory fields for
each event class (populate created_at with an ISO datetime string or datetime
object, include a non-empty custom dict for CustomEvent, and add message_id,
watcher_count, message for MessageNewEvent, etc.) so that instantiation of the
dataclasses (via parse_webhook_event) succeeds.
🧹 Nitpick comments (1)
getstream/webhook.py (1)
387-550:event_mapdict is rebuilt on every call.
_get_event_classallocates a ~130-entry dict each invocation. Since this is generated code the impact is low, but moving the map to module scope would be a trivial improvement.♻️ Suggested refactor
+_EVENT_MAP = { + "*": CustomEvent, + "appeal.accepted": AppealAcceptedEvent, + # ... rest of entries ... +} + def _get_event_class(event_type: str): """Map event type to event class.""" - event_map = { - "*": CustomEvent, - ... - } - return event_map.get(event_type) + return _EVENT_MAP.get(event_type)
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
getstream/chat/async_rest_client.py (1)
1629-1640:⚠️ Potential issue | 🔴 Critical
query_segment_targetsuses"Sort"/"Filter"keys (likely wrong).At Line 1637-1638 the request body uses
"Sort"and"Filter", while the method parameters aresortandfilterand other endpoints use lowercase keys. Unless the API explicitly expects capitalized keys, this will silently drop the parameters server-side.Proposed fix
json = build_body_dict( **{ "limit": limit, "next": next, "prev": prev, - "Sort": sort, - "Filter": filter, + "sort": sort, + "filter": filter, } )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/chat/async_rest_client.py` around lines 1629 - 1640, The request body in query_segment_targets is using capitalized keys "Sort" and "Filter" instead of the lowercase parameter names, which will drop the parameters; update the build_body_dict call in query_segment_targets to pass "sort": sort and "filter": filter (matching the method parameters and other endpoints) so the payload uses the correct keys and values.tests/test_video_examples.py (1)
391-395:⚠️ Potential issue | 🔴 CriticalCI is failing due to rate limiting in
test_fps(429 at Line 392).Given the unit pipeline failure, this test should be guarded (e.g.,
@pytest.mark.skip_in_ci) or skip when a 429 is encountered.Minimal CI-stabilizing change
+@pytest.mark.skip_in_ci def test_fps(client: Stream): response = client.video.get_active_calls_status() resolution = response.data.metrics.publishers.all.video.resolution print(resolution.p10)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/test_video_examples.py` around lines 391 - 395, The test_fps test is flaky in CI due to rate limiting (HTTP 429) from client.video.get_active_calls_status(); modify test_fps to either decorate it with `@pytest.mark.skip_in_ci` or wrap the call in a try/except that catches the 429 error (e.g., the HTTP error or client-specific RateLimitError) and calls pytest.skip when encountered; ensure you reference the test function name test_fps and the call client.video.get_active_calls_status() so the guard/exception handling is applied only to this test.getstream/moderation/async_rest_client.py (1)
437-449:⚠️ Potential issue | 🔴 CriticalFix missing
{id}path parameter for moderation rule get/delete endpoints.
"/api/v2/moderation/moderation_rule/{id}"is used in both methods, but neither method accepts anidnor passespath_params. This will fail at runtime (Line 442 / Line 448).Proposed fix
`@telemetry.operation_name`("getstream.api.moderation.delete_moderation_rule") async def delete_moderation_rule( - self, + self, + id: str, ) -> StreamResponse[DeleteModerationRuleResponse]: - return await self.delete( - "/api/v2/moderation/moderation_rule/{id}", DeleteModerationRuleResponse - ) + path_params = {"id": id} + return await self.delete( + "/api/v2/moderation/moderation_rule/{id}", + DeleteModerationRuleResponse, + path_params=path_params, + ) `@telemetry.operation_name`("getstream.api.moderation.get_moderation_rule") -async def get_moderation_rule(self) -> StreamResponse[GetModerationRuleResponse]: - return await self.get( - "/api/v2/moderation/moderation_rule/{id}", GetModerationRuleResponse - ) +async def get_moderation_rule(self, id: str) -> StreamResponse[GetModerationRuleResponse]: + path_params = {"id": id} + return await self.get( + "/api/v2/moderation/moderation_rule/{id}", + GetModerationRuleResponse, + path_params=path_params, + )If these files are regenerated in CI, this likely needs to be fixed in the generator templates/spec too.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/moderation/async_rest_client.py` around lines 437 - 449, Both delete_moderation_rule and get_moderation_rule are calling the path "/api/v2/moderation/moderation_rule/{id}" but neither accepts an id nor supplies path_params; update the two methods (delete_moderation_rule and get_moderation_rule) to accept an id parameter (e.g., id: str or int) and pass it as path_params={'id': id} in the call to self.delete/self.get so the {id} placeholder is resolved at runtime; ensure the parameter name matches the placeholder and keep the return types DeleteModerationRuleResponse and GetModerationRuleResponse unchanged.
🧹 Nitpick comments (5)
getstream/utils/__init__.py (1)
142-161: Async docstring doesn't mentiondatetimesupport.The sync version's docstring was updated (line 111) to note datetime handling, but this async counterpart still omits it. Since
build_query_param_asyncdelegates tobuild_query_param, the same capabilities apply.📝 Suggested docstring update
This function handles various data types: - JSON-serializable objects with a `to_json` method will be serialized using that method. + - datetime objects are converted to ISO 8601 strings. - Booleans are converted to lowercase strings.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/utils/__init__.py` around lines 142 - 161, The async function build_query_param_async delegates to build_query_param and therefore also supports datetime objects; update its docstring to mention datetime handling (e.g., that datetime objects are serialized/handled the same way as described in build_query_param), mirroring the sync version's note about datetime support so the async docstring accurately reflects supported types and behavior when offloading to asyncio.to_thread.getstream/common/rest_client.py (1)
1018-1042: CI rate-limit ondelete_userssuggests retry/backoff or test isolation.
The pipeline shows HTTP 429 for this call. Consider adding client-side retry/backoff for 429s or ensuring CI uses stubbed/mocked responses to avoid flakiness.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/common/rest_client.py` around lines 1018 - 1042, Tests are flaking due to HTTP 429s from delete_users; add client-side retry with exponential backoff and jitter for 429 responses in the delete_users call path (or use the client's central HTTP call wrapper if one exists) so CI doesn't fail intermittently. Modify the delete_users method to catch 429s, retry up to a small max (e.g., 3-5 attempts) with increasing delays and random jitter, and surface the final error if all attempts fail; alternatively, wire delete_users to an existing retry-enabled request helper in this module so the retry logic is centralized and reusable.getstream/tests/test_webhook.py (1)
79-1043: Parametrize the event-type mapping tests to reduce repetition.
TestParseWebhookEventis essentially the same test repeated for many event types. A@pytest.mark.parametrizetable (event_type → expected class) would keep coverage while shrinking the file and making additions/removals trivial.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/tests/test_webhook.py` around lines 79 - 1043, Replace the many near-duplicate test_* methods in TestParseWebhookEvent with a single parametrized pytest test that calls parse_webhook_event for each event type and asserts the returned object's class name; create a mapping/list of tuples (event_type, expected_class_name) and decorate one function (e.g., test_parse_event_mapping) with `@pytest.mark.parametrize` to iterate them, keep the warnings.catch_warnings block and assertion logic identical to existing tests so parse_webhook_event and the expected class names (e.g., "CallAcceptedEvent", "ChannelCreatedEvent", etc.) are used unchanged.getstream/webhook.py (1)
355-390: Exposeinfer_missingas a configurable kw-only parameter to allow stricter payload validation.The current implementation unconditionally uses
infer_missing=True(line 387), which sets missing required fields toNoneinstead of raising an error. This can mask incomplete webhook payloads. Expose this as a kw-only parameter with the current behavior as default to maintain backward compatibility while allowing callers to opt into strict parsing.Proposed change
-def parse_webhook_event(raw_event: Union[bytes, str, Dict[str, Any]]) -> Any: +def parse_webhook_event( + raw_event: Union[bytes, str, Dict[str, Any]], *, infer_missing: bool = True +) -> Any: @@ - try: - return event_class.from_dict(data, infer_missing=True) + try: + return event_class.from_dict(data, infer_missing=infer_missing) except Exception as e: raise ValueError(f"Failed to deserialize webhook event: {e}")🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/webhook.py` around lines 355 - 390, Update parse_webhook_event to accept a keyword-only parameter infer_missing (default True) so callers can opt into strict validation; change the signature to include *, infer_missing: bool = True, document the new parameter in the docstring, and pass infer_missing through to event_class.from_dict(data, infer_missing=infer_missing) instead of hardcoding True (keep current defaults/behavior for backward compatibility).getstream/chat/rest_client.py (1)
95-109:schedule_campaignsends an empty{}JSON body.
build_body_dict(**{})produces{}. Same pattern asstop_recordingandcommit_message. If the backend requires no body for this endpoint, consider omittingjson=entirely.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@getstream/chat/rest_client.py` around lines 95 - 109, The schedule_campaign method currently constructs an empty JSON body via build_body_dict(**{}) and passes it to self.post; if the /api/v2/chat/campaigns/{id}/stop endpoint requires no request body, remove the empty body by deleting the call to build_body_dict and the json= parameter from the self.post invocation (i.e., call self.post(...) without json) so schedule_campaign no longer sends {} — locate the schedule_campaign function and its use of build_body_dict and self.post to make this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@getstream/chat/rest_client.py`:
- Around line 1622-1630: The body dict passed to build_body_dict uses PascalCase
keys "Sort" and "Filter" which should be lowercase; update the dictionary
entries (the build_body_dict call that currently maps "Sort": sort and "Filter":
filter) to use "sort": sort and "filter": filter so the server receives them,
and mirror the same change in the async client counterpart
(async_rest_client.py) where the same PascalCase keys appear.
---
Outside diff comments:
In `@getstream/chat/async_rest_client.py`:
- Around line 1629-1640: The request body in query_segment_targets is using
capitalized keys "Sort" and "Filter" instead of the lowercase parameter names,
which will drop the parameters; update the build_body_dict call in
query_segment_targets to pass "sort": sort and "filter": filter (matching the
method parameters and other endpoints) so the payload uses the correct keys and
values.
In `@getstream/moderation/async_rest_client.py`:
- Around line 437-449: Both delete_moderation_rule and get_moderation_rule are
calling the path "/api/v2/moderation/moderation_rule/{id}" but neither accepts
an id nor supplies path_params; update the two methods (delete_moderation_rule
and get_moderation_rule) to accept an id parameter (e.g., id: str or int) and
pass it as path_params={'id': id} in the call to self.delete/self.get so the
{id} placeholder is resolved at runtime; ensure the parameter name matches the
placeholder and keep the return types DeleteModerationRuleResponse and
GetModerationRuleResponse unchanged.
In `@tests/test_video_examples.py`:
- Around line 391-395: The test_fps test is flaky in CI due to rate limiting
(HTTP 429) from client.video.get_active_calls_status(); modify test_fps to
either decorate it with `@pytest.mark.skip_in_ci` or wrap the call in a try/except
that catches the 429 error (e.g., the HTTP error or client-specific
RateLimitError) and calls pytest.skip when encountered; ensure you reference the
test function name test_fps and the call client.video.get_active_calls_status()
so the guard/exception handling is applied only to this test.
---
Duplicate comments:
In `@getstream/chat/rest_client.py`:
- Line 276: The update_channel signature uses ChannelInputRequest while
get_or_create_distinct_channel and get_or_create_channel use ChannelInput,
causing a type inconsistency; standardize on one type (prefer ChannelInput for
consistency) by changing the update_channel parameter type from
ChannelInputRequest to ChannelInput (or convert ChannelInputRequest usages to
ChannelInput where appropriate), update any internal references inside
update_channel to match ChannelInput fields, and ensure all imports and type
hints (in update_channel, get_or_create_distinct_channel, get_or_create_channel)
are aligned to the chosen type to avoid mismatches.
In `@getstream/video/rest_client.py`:
- Around line 1066-1080: query_call_session_participant_stats is passing sort:
List[SortParamRequest] into build_query_param which currently stringifies list
items via str(v) instead of serializing them with each
SortParamRequest.to_json(); update the serialization so build_query_param (or
the call site) maps the sort list to [s.to_json() for s in sort] before passing
it (or adjust build_query_param to detect SortParamRequest instances and call
to_json()), ensuring SortParamRequest objects are correctly converted to JSON
for query string construction.
- Around line 994-1025: get_call_stats_map (and
get_call_participant_session_metrics) currently passes datetime objects
(start_time/end_time and since/until) into build_query_param which will end up
calling json.dumps and raise TypeError; convert any datetime values to an
ISO8601 string (e.g., using .isoformat()) before calling build_query_param or
enhance build_query_param to detect datetime and serialize to ISO strings;
update the calls in get_call_stats_map and get_call_participant_session_metrics
to pass the serialized string(s) (or adjust build_query_param to handle datetime
types) so no raw datetime reaches json.dumps.
- Around line 516-557: The stop_recording implementation currently calls
build_body_dict(**{}) which sends an empty JSON object; change stop_recording to
not send an empty body—either omit the json argument in the self.post call or
pass json=None so no {} is sent. Locate stop_recording (and build_body_dict
usage) and update the self.post invocation for the
"/api/v2/video/call/{type}/{id}/recordings/{recording_type}/stop" endpoint to
call self.post(..., path_params=path_params) without json (or with json=None)
instead of json=build_body_dict(**{}).
---
Nitpick comments:
In `@getstream/chat/rest_client.py`:
- Around line 95-109: The schedule_campaign method currently constructs an empty
JSON body via build_body_dict(**{}) and passes it to self.post; if the
/api/v2/chat/campaigns/{id}/stop endpoint requires no request body, remove the
empty body by deleting the call to build_body_dict and the json= parameter from
the self.post invocation (i.e., call self.post(...) without json) so
schedule_campaign no longer sends {} — locate the schedule_campaign function and
its use of build_body_dict and self.post to make this change.
In `@getstream/common/rest_client.py`:
- Around line 1018-1042: Tests are flaking due to HTTP 429s from delete_users;
add client-side retry with exponential backoff and jitter for 429 responses in
the delete_users call path (or use the client's central HTTP call wrapper if one
exists) so CI doesn't fail intermittently. Modify the delete_users method to
catch 429s, retry up to a small max (e.g., 3-5 attempts) with increasing delays
and random jitter, and surface the final error if all attempts fail;
alternatively, wire delete_users to an existing retry-enabled request helper in
this module so the retry logic is centralized and reusable.
In `@getstream/tests/test_webhook.py`:
- Around line 79-1043: Replace the many near-duplicate test_* methods in
TestParseWebhookEvent with a single parametrized pytest test that calls
parse_webhook_event for each event type and asserts the returned object's class
name; create a mapping/list of tuples (event_type, expected_class_name) and
decorate one function (e.g., test_parse_event_mapping) with
`@pytest.mark.parametrize` to iterate them, keep the warnings.catch_warnings block
and assertion logic identical to existing tests so parse_webhook_event and the
expected class names (e.g., "CallAcceptedEvent", "ChannelCreatedEvent", etc.)
are used unchanged.
In `@getstream/utils/__init__.py`:
- Around line 142-161: The async function build_query_param_async delegates to
build_query_param and therefore also supports datetime objects; update its
docstring to mention datetime handling (e.g., that datetime objects are
serialized/handled the same way as described in build_query_param), mirroring
the sync version's note about datetime support so the async docstring accurately
reflects supported types and behavior when offloading to asyncio.to_thread.
In `@getstream/webhook.py`:
- Around line 355-390: Update parse_webhook_event to accept a keyword-only
parameter infer_missing (default True) so callers can opt into strict
validation; change the signature to include *, infer_missing: bool = True,
document the new parameter in the docstring, and pass infer_missing through to
event_class.from_dict(data, infer_missing=infer_missing) instead of hardcoding
True (keep current defaults/behavior for backward compatibility).
| json = build_body_dict( | ||
| limit=limit, next=next, prev=prev, sort=sort, filter=filter | ||
| **{ | ||
| "limit": limit, | ||
| "next": next, | ||
| "prev": prev, | ||
| "Sort": sort, | ||
| "Filter": filter, | ||
| } | ||
| ) |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Step 1: Verify PascalCase "Sort" and "Filter" usage
echo "=== Checking for PascalCase Sort/Filter ==="
rg -n '"Sort"|"Filter"' getstream/chat/rest_client.py getstream/chat/async_rest_client.py
echo ""
echo "=== Checking for lowercase sort/filter in rest_client.py ==="
rg -n '"sort"|"filter"' getstream/chat/rest_client.py | head -30Repository: GetStream/stream-py
Length of output: 1029
🏁 Script executed:
#!/bin/bash
# Verify the method name and context for the PascalCase issue
echo "=== rest_client.py lines 1620-1635 ==="
sed -n '1620,1635p' getstream/chat/rest_client.py
echo ""
echo "=== async_rest_client.py lines 1630-1645 ==="
sed -n '1630,1645p' getstream/chat/async_rest_client.pyRepository: GetStream/stream-py
Length of output: 1039
"Sort" and "Filter" keys are PascalCase — a code-generation bug affecting both sync and async clients.
Every other method in this file uses lowercase "sort" and "filter" for these body keys. The PascalCase variants at lines 1627–1628 (rest_client.py) and 1637–1638 (async_rest_client.py) will cause the server to silently ignore the sort and filter parameters, breaking query_segment_targets.
Fix
json = build_body_dict(
**{
"limit": limit,
"next": next,
"prev": prev,
- "Sort": sort,
- "Filter": filter,
+ "sort": sort,
+ "filter": filter,
}
)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@getstream/chat/rest_client.py` around lines 1622 - 1630, The body dict passed
to build_body_dict uses PascalCase keys "Sort" and "Filter" which should be
lowercase; update the dictionary entries (the build_body_dict call that
currently maps "Sort": sort and "Filter": filter) to use "sort": sort and
"filter": filter so the server receives them, and mirror the same change in the
async client counterpart (async_rest_client.py) where the same PascalCase keys
appear.
Summary by CodeRabbit
New Features
Bug Fixes