From 443367af394fb4d58034aba757de4e15e64c4f1e Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:49:24 +0100 Subject: [PATCH 01/16] Release 0.25.0 --- .../selective_subscription/app.py | 14 ++++--- fishjam/_openapi_client/api/room/add_peer.py | 20 ++++----- fishjam/_openapi_client/models/__init__.py | 4 +- .../_openapi_client/models/agent_output.py | 21 +--------- fishjam/_openapi_client/models/error.py | 8 ++-- fishjam/_openapi_client/models/peer.py | 20 +++++---- .../{add_peer_body.py => peer_config.py} | 42 ++++++------------- .../models/peer_details_response.py | 8 ++-- .../models/peer_details_response_data.py | 10 +++-- .../models/peer_options_agent.py | 21 +--------- .../models/peer_options_web_rtc.py | 21 +--------- .../models/peer_refresh_token_response.py | 8 ++-- .../peer_refresh_token_response_data.py | 8 ++-- fishjam/_openapi_client/models/room.py | 12 +++--- fishjam/_openapi_client/models/room_config.py | 21 +--------- .../models/room_create_details_response.py | 8 ++-- .../room_create_details_response_data.py | 10 +++-- .../models/room_details_response.py | 8 ++-- .../models/rooms_listing_response.py | 8 ++-- fishjam/_openapi_client/models/stream.py | 16 +++---- .../_openapi_client/models/stream_config.py | 41 +++++++++--------- fishjam/_openapi_client/models/streamer.py | 12 +++--- .../_openapi_client/models/streamer_token.py | 8 ++-- .../models/streams_listing_response.py | 8 ++-- .../models/subscribe_tracks_body.py | 8 ++-- .../_openapi_client/models/subscriptions.py | 10 +++-- fishjam/_openapi_client/models/viewer.py | 12 +++--- .../_openapi_client/models/viewer_token.py | 8 ++-- pyproject.toml | 2 +- uv.lock | 2 +- 30 files changed, 174 insertions(+), 225 deletions(-) rename fishjam/_openapi_client/models/{add_peer_body.py => peer_config.py} (69%) diff --git a/examples/selective_subscription/selective_subscription/app.py b/examples/selective_subscription/selective_subscription/app.py index 1ddb6bb..24e8127 100644 --- a/examples/selective_subscription/selective_subscription/app.py +++ b/examples/selective_subscription/selective_subscription/app.py @@ -29,12 +29,14 @@ async def create_peer(request: Request) -> Response: peer, token = room_service.create_peer() - return JSONResponse({ - "peer_id": peer.id, - "token": token, - "room_name": room_name, - "peer_name": peer_name, - }) + return JSONResponse( + { + "peer_id": peer.id, + "token": token, + "room_name": room_name, + "peer_name": peer_name, + } + ) except Exception as e: return JSONResponse({"error": str(e)}, status_code=500) diff --git a/fishjam/_openapi_client/api/room/add_peer.py b/fishjam/_openapi_client/api/room/add_peer.py index 8d0cd25..96b33dc 100644 --- a/fishjam/_openapi_client/api/room/add_peer.py +++ b/fishjam/_openapi_client/api/room/add_peer.py @@ -5,8 +5,8 @@ from ... import errors from ...client import AuthenticatedClient, Client -from ...models.add_peer_body import AddPeerBody from ...models.error import Error +from ...models.peer_config import PeerConfig from ...models.peer_details_response import PeerDetailsResponse from ...types import Response @@ -14,7 +14,7 @@ def _get_kwargs( room_id: str, *, - body: AddPeerBody, + body: PeerConfig, ) -> dict[str, Any]: headers: dict[str, Any] = {} @@ -81,13 +81,13 @@ def sync_detailed( room_id: str, *, client: AuthenticatedClient, - body: AddPeerBody, + body: PeerConfig, ) -> Response[Union[Error, PeerDetailsResponse]]: """Create peer Args: room_id (str): - body (AddPeerBody): + body (PeerConfig): Peer configuration Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -113,13 +113,13 @@ def sync( room_id: str, *, client: AuthenticatedClient, - body: AddPeerBody, + body: PeerConfig, ) -> Optional[Union[Error, PeerDetailsResponse]]: """Create peer Args: room_id (str): - body (AddPeerBody): + body (PeerConfig): Peer configuration Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -140,13 +140,13 @@ async def asyncio_detailed( room_id: str, *, client: AuthenticatedClient, - body: AddPeerBody, + body: PeerConfig, ) -> Response[Union[Error, PeerDetailsResponse]]: """Create peer Args: room_id (str): - body (AddPeerBody): + body (PeerConfig): Peer configuration Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. @@ -170,13 +170,13 @@ async def asyncio( room_id: str, *, client: AuthenticatedClient, - body: AddPeerBody, + body: PeerConfig, ) -> Optional[Union[Error, PeerDetailsResponse]]: """Create peer Args: room_id (str): - body (AddPeerBody): + body (PeerConfig): Peer configuration Raises: errors.UnexpectedStatus: If the server returns an undocumented status code and Client.raise_on_unexpected_status is True. diff --git a/fishjam/_openapi_client/models/__init__.py b/fishjam/_openapi_client/models/__init__.py index 90e0b47..2ee6c44 100644 --- a/fishjam/_openapi_client/models/__init__.py +++ b/fishjam/_openapi_client/models/__init__.py @@ -1,11 +1,11 @@ """Contains all the data models used in inputs/outputs""" -from .add_peer_body import AddPeerBody from .agent_output import AgentOutput from .audio_format import AudioFormat from .audio_sample_rate import AudioSampleRate from .error import Error from .peer import Peer +from .peer_config import PeerConfig from .peer_details_response import PeerDetailsResponse from .peer_details_response_data import PeerDetailsResponseData from .peer_metadata import PeerMetadata @@ -41,12 +41,12 @@ from .web_rtc_metadata import WebRTCMetadata __all__ = ( - "AddPeerBody", "AgentOutput", "AudioFormat", "AudioSampleRate", "Error", "Peer", + "PeerConfig", "PeerDetailsResponse", "PeerDetailsResponseData", "PeerMetadata", diff --git a/fishjam/_openapi_client/models/agent_output.py b/fishjam/_openapi_client/models/agent_output.py index c32db25..6a3b85b 100644 --- a/fishjam/_openapi_client/models/agent_output.py +++ b/fishjam/_openapi_client/models/agent_output.py @@ -6,7 +6,6 @@ ) from attrs import define as _attrs_define -from attrs import field as _attrs_field from ..models.audio_format import AudioFormat from ..models.audio_sample_rate import AudioSampleRate @@ -26,7 +25,6 @@ class AgentOutput: audio_format: Union[Unset, AudioFormat] = UNSET audio_sample_rate: Union[Unset, AudioSampleRate] = UNSET - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: audio_format: Union[Unset, str] = UNSET @@ -38,7 +36,7 @@ def to_dict(self) -> dict[str, Any]: audio_sample_rate = self.audio_sample_rate.value field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) + field_dict.update({}) if audio_format is not UNSET: field_dict["audioFormat"] = audio_format @@ -69,21 +67,4 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: audio_sample_rate=audio_sample_rate, ) - agent_output.additional_properties = d return agent_output - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/error.py b/fishjam/_openapi_client/models/error.py index 0087ccf..14afb29 100644 --- a/fishjam/_openapi_client/models/error.py +++ b/fishjam/_openapi_client/models/error.py @@ -23,9 +23,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "errors": errors, - }) + field_dict.update( + { + "errors": errors, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/peer.py b/fishjam/_openapi_client/models/peer.py index 929e519..291a47e 100644 --- a/fishjam/_openapi_client/models/peer.py +++ b/fishjam/_openapi_client/models/peer.py @@ -72,15 +72,17 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "id": id, - "metadata": metadata, - "status": status, - "subscribeMode": subscribe_mode, - "subscriptions": subscriptions, - "tracks": tracks, - "type": type_, - }) + field_dict.update( + { + "id": id, + "metadata": metadata, + "status": status, + "subscribeMode": subscribe_mode, + "subscriptions": subscriptions, + "tracks": tracks, + "type": type_, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/add_peer_body.py b/fishjam/_openapi_client/models/peer_config.py similarity index 69% rename from fishjam/_openapi_client/models/add_peer_body.py rename to fishjam/_openapi_client/models/peer_config.py index 9134e63..e0ae0aa 100644 --- a/fishjam/_openapi_client/models/add_peer_body.py +++ b/fishjam/_openapi_client/models/peer_config.py @@ -7,7 +7,6 @@ ) from attrs import define as _attrs_define -from attrs import field as _attrs_field from ..models.peer_type import PeerType @@ -16,12 +15,13 @@ from ..models.peer_options_web_rtc import PeerOptionsWebRTC -T = TypeVar("T", bound="AddPeerBody") +T = TypeVar("T", bound="PeerConfig") @_attrs_define -class AddPeerBody: - """ +class PeerConfig: + """Peer configuration + Attributes: options (Union['PeerOptionsAgent', 'PeerOptionsWebRTC']): Peer-specific options type_ (PeerType): Peer type Example: webrtc. @@ -29,7 +29,6 @@ class AddPeerBody: options: Union["PeerOptionsAgent", "PeerOptionsWebRTC"] type_: PeerType - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: from ..models.peer_options_web_rtc import PeerOptionsWebRTC @@ -43,11 +42,13 @@ def to_dict(self) -> dict[str, Any]: type_ = self.type_.value field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) - field_dict.update({ - "options": options, - "type": type_, - }) + + field_dict.update( + { + "options": options, + "type": type_, + } + ) return field_dict @@ -81,26 +82,9 @@ def _parse_options( type_ = PeerType(d.pop("type")) - add_peer_body = cls( + peer_config = cls( options=options, type_=type_, ) - add_peer_body.additional_properties = d - return add_peer_body - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties + return peer_config diff --git a/fishjam/_openapi_client/models/peer_details_response.py b/fishjam/_openapi_client/models/peer_details_response.py index 718c26c..6547f61 100644 --- a/fishjam/_openapi_client/models/peer_details_response.py +++ b/fishjam/_openapi_client/models/peer_details_response.py @@ -31,9 +31,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "data": data, - }) + field_dict.update( + { + "data": data, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/peer_details_response_data.py b/fishjam/_openapi_client/models/peer_details_response_data.py index fbf3fb3..f7295bf 100644 --- a/fishjam/_openapi_client/models/peer_details_response_data.py +++ b/fishjam/_openapi_client/models/peer_details_response_data.py @@ -42,10 +42,12 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "peer": peer, - "token": token, - }) + field_dict.update( + { + "peer": peer, + "token": token, + } + ) if peer_websocket_url is not UNSET: field_dict["peer_websocket_url"] = peer_websocket_url diff --git a/fishjam/_openapi_client/models/peer_options_agent.py b/fishjam/_openapi_client/models/peer_options_agent.py index 713e40f..03df548 100644 --- a/fishjam/_openapi_client/models/peer_options_agent.py +++ b/fishjam/_openapi_client/models/peer_options_agent.py @@ -7,7 +7,6 @@ ) from attrs import define as _attrs_define -from attrs import field as _attrs_field from ..models.subscribe_mode import SubscribeMode from ..types import UNSET, Unset @@ -30,7 +29,6 @@ class PeerOptionsAgent: output: Union[Unset, "AgentOutput"] = UNSET subscribe_mode: Union[Unset, SubscribeMode] = UNSET - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: output: Union[Unset, dict[str, Any]] = UNSET @@ -42,7 +40,7 @@ def to_dict(self) -> dict[str, Any]: subscribe_mode = self.subscribe_mode.value field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) + field_dict.update({}) if output is not UNSET: field_dict["output"] = output @@ -75,21 +73,4 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: subscribe_mode=subscribe_mode, ) - peer_options_agent.additional_properties = d return peer_options_agent - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/peer_options_web_rtc.py b/fishjam/_openapi_client/models/peer_options_web_rtc.py index c3f4938..326cab1 100644 --- a/fishjam/_openapi_client/models/peer_options_web_rtc.py +++ b/fishjam/_openapi_client/models/peer_options_web_rtc.py @@ -7,7 +7,6 @@ ) from attrs import define as _attrs_define -from attrs import field as _attrs_field from ..models.subscribe_mode import SubscribeMode from ..types import UNSET, Unset @@ -30,7 +29,6 @@ class PeerOptionsWebRTC: metadata: Union[Unset, "WebRTCMetadata"] = UNSET subscribe_mode: Union[Unset, SubscribeMode] = UNSET - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: metadata: Union[Unset, dict[str, Any]] = UNSET @@ -42,7 +40,7 @@ def to_dict(self) -> dict[str, Any]: subscribe_mode = self.subscribe_mode.value field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) + field_dict.update({}) if metadata is not UNSET: field_dict["metadata"] = metadata @@ -75,21 +73,4 @@ def from_dict(cls: type[T], src_dict: Mapping[str, Any]) -> T: subscribe_mode=subscribe_mode, ) - peer_options_web_rtc.additional_properties = d return peer_options_web_rtc - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/peer_refresh_token_response.py b/fishjam/_openapi_client/models/peer_refresh_token_response.py index 6075525..1100c62 100644 --- a/fishjam/_openapi_client/models/peer_refresh_token_response.py +++ b/fishjam/_openapi_client/models/peer_refresh_token_response.py @@ -31,9 +31,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "data": data, - }) + field_dict.update( + { + "data": data, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/peer_refresh_token_response_data.py b/fishjam/_openapi_client/models/peer_refresh_token_response_data.py index ca1aebc..ccc92ac 100644 --- a/fishjam/_openapi_client/models/peer_refresh_token_response_data.py +++ b/fishjam/_openapi_client/models/peer_refresh_token_response_data.py @@ -22,9 +22,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "token": token, - }) + field_dict.update( + { + "token": token, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/room.py b/fishjam/_openapi_client/models/room.py index e2c8fbf..c6e9847 100644 --- a/fishjam/_openapi_client/models/room.py +++ b/fishjam/_openapi_client/models/room.py @@ -43,11 +43,13 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "config": config, - "id": id, - "peers": peers, - }) + field_dict.update( + { + "config": config, + "id": id, + "peers": peers, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/room_config.py b/fishjam/_openapi_client/models/room_config.py index 8ba15a7..1342a86 100644 --- a/fishjam/_openapi_client/models/room_config.py +++ b/fishjam/_openapi_client/models/room_config.py @@ -7,7 +7,6 @@ ) from attrs import define as _attrs_define -from attrs import field as _attrs_field from ..models.room_type import RoomType from ..models.video_codec import VideoCodec @@ -34,7 +33,6 @@ class RoomConfig: room_type: Union[Unset, RoomType] = UNSET video_codec: Union[Unset, VideoCodec] = UNSET webhook_url: Union[None, Unset, str] = UNSET - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) def to_dict(self) -> dict[str, Any]: max_peers: Union[None, Unset, int] @@ -60,7 +58,7 @@ def to_dict(self) -> dict[str, Any]: webhook_url = self.webhook_url field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) + field_dict.update({}) if max_peers is not UNSET: field_dict["maxPeers"] = max_peers @@ -121,21 +119,4 @@ def _parse_webhook_url(data: object) -> Union[None, Unset, str]: webhook_url=webhook_url, ) - room_config.additional_properties = d return room_config - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/room_create_details_response.py b/fishjam/_openapi_client/models/room_create_details_response.py index 89c01f5..23e37e7 100644 --- a/fishjam/_openapi_client/models/room_create_details_response.py +++ b/fishjam/_openapi_client/models/room_create_details_response.py @@ -31,9 +31,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "data": data, - }) + field_dict.update( + { + "data": data, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/room_create_details_response_data.py b/fishjam/_openapi_client/models/room_create_details_response_data.py index 8ac7dc9..519dd4b 100644 --- a/fishjam/_openapi_client/models/room_create_details_response_data.py +++ b/fishjam/_openapi_client/models/room_create_details_response_data.py @@ -35,10 +35,12 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "fishjam_address": fishjam_address, - "room": room, - }) + field_dict.update( + { + "fishjam_address": fishjam_address, + "room": room, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/room_details_response.py b/fishjam/_openapi_client/models/room_details_response.py index e1b23ef..1d184c0 100644 --- a/fishjam/_openapi_client/models/room_details_response.py +++ b/fishjam/_openapi_client/models/room_details_response.py @@ -31,9 +31,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "data": data, - }) + field_dict.update( + { + "data": data, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/rooms_listing_response.py b/fishjam/_openapi_client/models/rooms_listing_response.py index 89d647b..9663502 100644 --- a/fishjam/_openapi_client/models/rooms_listing_response.py +++ b/fishjam/_openapi_client/models/rooms_listing_response.py @@ -34,9 +34,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "data": data, - }) + field_dict.update( + { + "data": data, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/stream.py b/fishjam/_openapi_client/models/stream.py index 0704770..a87aa90 100644 --- a/fishjam/_openapi_client/models/stream.py +++ b/fishjam/_openapi_client/models/stream.py @@ -61,13 +61,15 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "connectedViewers": connected_viewers, - "id": id, - "public": public, - "streamers": streamers, - "viewers": viewers, - }) + field_dict.update( + { + "connectedViewers": connected_viewers, + "id": id, + "public": public, + "streamers": streamers, + "viewers": viewers, + } + ) if audio_only is not UNSET: field_dict["audioOnly"] = audio_only diff --git a/fishjam/_openapi_client/models/stream_config.py b/fishjam/_openapi_client/models/stream_config.py index 1697925..8351355 100644 --- a/fishjam/_openapi_client/models/stream_config.py +++ b/fishjam/_openapi_client/models/stream_config.py @@ -7,7 +7,6 @@ ) from attrs import define as _attrs_define -from attrs import field as _attrs_field from ..types import UNSET, Unset @@ -21,11 +20,12 @@ class StreamConfig: Attributes: audio_only (Union[None, Unset, bool]): Restrics stream to audio only Default: False. public (Union[Unset, bool]): True if livestream viewers can omit specifying a token. Default: False. + webhook_url (Union[None, Unset, str]): Webhook URL for receiving server notifications """ audio_only: Union[None, Unset, bool] = False public: Union[Unset, bool] = False - additional_properties: dict[str, Any] = _attrs_field(init=False, factory=dict) + webhook_url: Union[None, Unset, str] = UNSET def to_dict(self) -> dict[str, Any]: audio_only: Union[None, Unset, bool] @@ -36,13 +36,21 @@ def to_dict(self) -> dict[str, Any]: public = self.public + webhook_url: Union[None, Unset, str] + if isinstance(self.webhook_url, Unset): + webhook_url = UNSET + else: + webhook_url = self.webhook_url + field_dict: dict[str, Any] = {} - field_dict.update(self.additional_properties) + field_dict.update({}) if audio_only is not UNSET: field_dict["audioOnly"] = audio_only if public is not UNSET: field_dict["public"] = public + if webhook_url is not UNSET: + field_dict["webhookUrl"] = webhook_url return field_dict @@ -61,26 +69,19 @@ def _parse_audio_only(data: object) -> Union[None, Unset, bool]: public = d.pop("public", UNSET) + def _parse_webhook_url(data: object) -> Union[None, Unset, str]: + if data is None: + return data + if isinstance(data, Unset): + return data + return cast(Union[None, Unset, str], data) + + webhook_url = _parse_webhook_url(d.pop("webhookUrl", UNSET)) + stream_config = cls( audio_only=audio_only, public=public, + webhook_url=webhook_url, ) - stream_config.additional_properties = d return stream_config - - @property - def additional_keys(self) -> list[str]: - return list(self.additional_properties.keys()) - - def __getitem__(self, key: str) -> Any: - return self.additional_properties[key] - - def __setitem__(self, key: str, value: Any) -> None: - self.additional_properties[key] = value - - def __delitem__(self, key: str) -> None: - del self.additional_properties[key] - - def __contains__(self, key: str) -> bool: - return key in self.additional_properties diff --git a/fishjam/_openapi_client/models/streamer.py b/fishjam/_openapi_client/models/streamer.py index f234c00..bc8b8f7 100644 --- a/fishjam/_openapi_client/models/streamer.py +++ b/fishjam/_openapi_client/models/streamer.py @@ -41,11 +41,13 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "id": id, - "status": status, - "token": token, - }) + field_dict.update( + { + "id": id, + "status": status, + "token": token, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/streamer_token.py b/fishjam/_openapi_client/models/streamer_token.py index e675c0d..4db3f8a 100644 --- a/fishjam/_openapi_client/models/streamer_token.py +++ b/fishjam/_openapi_client/models/streamer_token.py @@ -23,9 +23,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "token": token, - }) + field_dict.update( + { + "token": token, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/streams_listing_response.py b/fishjam/_openapi_client/models/streams_listing_response.py index d451de5..86c9fef 100644 --- a/fishjam/_openapi_client/models/streams_listing_response.py +++ b/fishjam/_openapi_client/models/streams_listing_response.py @@ -34,9 +34,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "data": data, - }) + field_dict.update( + { + "data": data, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/subscribe_tracks_body.py b/fishjam/_openapi_client/models/subscribe_tracks_body.py index 8daacc1..1ce94a7 100644 --- a/fishjam/_openapi_client/models/subscribe_tracks_body.py +++ b/fishjam/_openapi_client/models/subscribe_tracks_body.py @@ -26,9 +26,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "track_ids": track_ids, - }) + field_dict.update( + { + "track_ids": track_ids, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/subscriptions.py b/fishjam/_openapi_client/models/subscriptions.py index 82f61d3..673e2e1 100644 --- a/fishjam/_openapi_client/models/subscriptions.py +++ b/fishjam/_openapi_client/models/subscriptions.py @@ -31,10 +31,12 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "peers": peers, - "tracks": tracks, - }) + field_dict.update( + { + "peers": peers, + "tracks": tracks, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/viewer.py b/fishjam/_openapi_client/models/viewer.py index b34aeaa..66b8c61 100644 --- a/fishjam/_openapi_client/models/viewer.py +++ b/fishjam/_openapi_client/models/viewer.py @@ -41,11 +41,13 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "id": id, - "status": status, - "token": token, - }) + field_dict.update( + { + "id": id, + "status": status, + "token": token, + } + ) return field_dict diff --git a/fishjam/_openapi_client/models/viewer_token.py b/fishjam/_openapi_client/models/viewer_token.py index dee5c4a..3a2e861 100644 --- a/fishjam/_openapi_client/models/viewer_token.py +++ b/fishjam/_openapi_client/models/viewer_token.py @@ -23,9 +23,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update({ - "token": token, - }) + field_dict.update( + { + "token": token, + } + ) return field_dict diff --git a/pyproject.toml b/pyproject.toml index 1f8d304..4cdbcaf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "fishjam-server-sdk" -version = "0.24.0" +version = "0.25.0" description = "Python server SDK for the Fishjam" authors = [{ name = "Fishjam Team", email = "contact@fishjam.io" }] requires-python = ">=3.11" diff --git a/uv.lock b/uv.lock index 92efb07..56ea2ae 100644 --- a/uv.lock +++ b/uv.lock @@ -322,7 +322,7 @@ wheels = [ [[package]] name = "fishjam-server-sdk" -version = "0.24.0" +version = "0.25.0" source = { editable = "." } dependencies = [ { name = "aenum" }, From 2db39bdf327c153178b5a19b379b29facff779d6 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:50:14 +0100 Subject: [PATCH 02/16] Format --- .../selective_subscription/app.py | 14 ++++++------- fishjam/_openapi_client/models/error.py | 8 +++----- fishjam/_openapi_client/models/peer.py | 20 +++++++++---------- fishjam/_openapi_client/models/peer_config.py | 10 ++++------ .../models/peer_details_response.py | 8 +++----- .../models/peer_details_response_data.py | 10 ++++------ .../models/peer_refresh_token_response.py | 8 +++----- .../peer_refresh_token_response_data.py | 8 +++----- fishjam/_openapi_client/models/room.py | 12 +++++------ .../models/room_create_details_response.py | 8 +++----- .../room_create_details_response_data.py | 10 ++++------ .../models/room_details_response.py | 8 +++----- .../models/rooms_listing_response.py | 8 +++----- fishjam/_openapi_client/models/stream.py | 16 +++++++-------- fishjam/_openapi_client/models/streamer.py | 12 +++++------ .../_openapi_client/models/streamer_token.py | 8 +++----- .../models/streams_listing_response.py | 8 +++----- .../models/subscribe_tracks_body.py | 8 +++----- .../_openapi_client/models/subscriptions.py | 10 ++++------ fishjam/_openapi_client/models/viewer.py | 12 +++++------ .../_openapi_client/models/viewer_token.py | 8 +++----- 21 files changed, 86 insertions(+), 128 deletions(-) diff --git a/examples/selective_subscription/selective_subscription/app.py b/examples/selective_subscription/selective_subscription/app.py index 24e8127..1ddb6bb 100644 --- a/examples/selective_subscription/selective_subscription/app.py +++ b/examples/selective_subscription/selective_subscription/app.py @@ -29,14 +29,12 @@ async def create_peer(request: Request) -> Response: peer, token = room_service.create_peer() - return JSONResponse( - { - "peer_id": peer.id, - "token": token, - "room_name": room_name, - "peer_name": peer_name, - } - ) + return JSONResponse({ + "peer_id": peer.id, + "token": token, + "room_name": room_name, + "peer_name": peer_name, + }) except Exception as e: return JSONResponse({"error": str(e)}, status_code=500) diff --git a/fishjam/_openapi_client/models/error.py b/fishjam/_openapi_client/models/error.py index 14afb29..0087ccf 100644 --- a/fishjam/_openapi_client/models/error.py +++ b/fishjam/_openapi_client/models/error.py @@ -23,11 +23,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "errors": errors, - } - ) + field_dict.update({ + "errors": errors, + }) return field_dict diff --git a/fishjam/_openapi_client/models/peer.py b/fishjam/_openapi_client/models/peer.py index 291a47e..929e519 100644 --- a/fishjam/_openapi_client/models/peer.py +++ b/fishjam/_openapi_client/models/peer.py @@ -72,17 +72,15 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "id": id, - "metadata": metadata, - "status": status, - "subscribeMode": subscribe_mode, - "subscriptions": subscriptions, - "tracks": tracks, - "type": type_, - } - ) + field_dict.update({ + "id": id, + "metadata": metadata, + "status": status, + "subscribeMode": subscribe_mode, + "subscriptions": subscriptions, + "tracks": tracks, + "type": type_, + }) return field_dict diff --git a/fishjam/_openapi_client/models/peer_config.py b/fishjam/_openapi_client/models/peer_config.py index e0ae0aa..9639d0a 100644 --- a/fishjam/_openapi_client/models/peer_config.py +++ b/fishjam/_openapi_client/models/peer_config.py @@ -43,12 +43,10 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} - field_dict.update( - { - "options": options, - "type": type_, - } - ) + field_dict.update({ + "options": options, + "type": type_, + }) return field_dict diff --git a/fishjam/_openapi_client/models/peer_details_response.py b/fishjam/_openapi_client/models/peer_details_response.py index 6547f61..718c26c 100644 --- a/fishjam/_openapi_client/models/peer_details_response.py +++ b/fishjam/_openapi_client/models/peer_details_response.py @@ -31,11 +31,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "data": data, - } - ) + field_dict.update({ + "data": data, + }) return field_dict diff --git a/fishjam/_openapi_client/models/peer_details_response_data.py b/fishjam/_openapi_client/models/peer_details_response_data.py index f7295bf..fbf3fb3 100644 --- a/fishjam/_openapi_client/models/peer_details_response_data.py +++ b/fishjam/_openapi_client/models/peer_details_response_data.py @@ -42,12 +42,10 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "peer": peer, - "token": token, - } - ) + field_dict.update({ + "peer": peer, + "token": token, + }) if peer_websocket_url is not UNSET: field_dict["peer_websocket_url"] = peer_websocket_url diff --git a/fishjam/_openapi_client/models/peer_refresh_token_response.py b/fishjam/_openapi_client/models/peer_refresh_token_response.py index 1100c62..6075525 100644 --- a/fishjam/_openapi_client/models/peer_refresh_token_response.py +++ b/fishjam/_openapi_client/models/peer_refresh_token_response.py @@ -31,11 +31,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "data": data, - } - ) + field_dict.update({ + "data": data, + }) return field_dict diff --git a/fishjam/_openapi_client/models/peer_refresh_token_response_data.py b/fishjam/_openapi_client/models/peer_refresh_token_response_data.py index ccc92ac..ca1aebc 100644 --- a/fishjam/_openapi_client/models/peer_refresh_token_response_data.py +++ b/fishjam/_openapi_client/models/peer_refresh_token_response_data.py @@ -22,11 +22,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "token": token, - } - ) + field_dict.update({ + "token": token, + }) return field_dict diff --git a/fishjam/_openapi_client/models/room.py b/fishjam/_openapi_client/models/room.py index c6e9847..e2c8fbf 100644 --- a/fishjam/_openapi_client/models/room.py +++ b/fishjam/_openapi_client/models/room.py @@ -43,13 +43,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "config": config, - "id": id, - "peers": peers, - } - ) + field_dict.update({ + "config": config, + "id": id, + "peers": peers, + }) return field_dict diff --git a/fishjam/_openapi_client/models/room_create_details_response.py b/fishjam/_openapi_client/models/room_create_details_response.py index 23e37e7..89c01f5 100644 --- a/fishjam/_openapi_client/models/room_create_details_response.py +++ b/fishjam/_openapi_client/models/room_create_details_response.py @@ -31,11 +31,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "data": data, - } - ) + field_dict.update({ + "data": data, + }) return field_dict diff --git a/fishjam/_openapi_client/models/room_create_details_response_data.py b/fishjam/_openapi_client/models/room_create_details_response_data.py index 519dd4b..8ac7dc9 100644 --- a/fishjam/_openapi_client/models/room_create_details_response_data.py +++ b/fishjam/_openapi_client/models/room_create_details_response_data.py @@ -35,12 +35,10 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "fishjam_address": fishjam_address, - "room": room, - } - ) + field_dict.update({ + "fishjam_address": fishjam_address, + "room": room, + }) return field_dict diff --git a/fishjam/_openapi_client/models/room_details_response.py b/fishjam/_openapi_client/models/room_details_response.py index 1d184c0..e1b23ef 100644 --- a/fishjam/_openapi_client/models/room_details_response.py +++ b/fishjam/_openapi_client/models/room_details_response.py @@ -31,11 +31,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "data": data, - } - ) + field_dict.update({ + "data": data, + }) return field_dict diff --git a/fishjam/_openapi_client/models/rooms_listing_response.py b/fishjam/_openapi_client/models/rooms_listing_response.py index 9663502..89d647b 100644 --- a/fishjam/_openapi_client/models/rooms_listing_response.py +++ b/fishjam/_openapi_client/models/rooms_listing_response.py @@ -34,11 +34,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "data": data, - } - ) + field_dict.update({ + "data": data, + }) return field_dict diff --git a/fishjam/_openapi_client/models/stream.py b/fishjam/_openapi_client/models/stream.py index a87aa90..0704770 100644 --- a/fishjam/_openapi_client/models/stream.py +++ b/fishjam/_openapi_client/models/stream.py @@ -61,15 +61,13 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "connectedViewers": connected_viewers, - "id": id, - "public": public, - "streamers": streamers, - "viewers": viewers, - } - ) + field_dict.update({ + "connectedViewers": connected_viewers, + "id": id, + "public": public, + "streamers": streamers, + "viewers": viewers, + }) if audio_only is not UNSET: field_dict["audioOnly"] = audio_only diff --git a/fishjam/_openapi_client/models/streamer.py b/fishjam/_openapi_client/models/streamer.py index bc8b8f7..f234c00 100644 --- a/fishjam/_openapi_client/models/streamer.py +++ b/fishjam/_openapi_client/models/streamer.py @@ -41,13 +41,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "id": id, - "status": status, - "token": token, - } - ) + field_dict.update({ + "id": id, + "status": status, + "token": token, + }) return field_dict diff --git a/fishjam/_openapi_client/models/streamer_token.py b/fishjam/_openapi_client/models/streamer_token.py index 4db3f8a..e675c0d 100644 --- a/fishjam/_openapi_client/models/streamer_token.py +++ b/fishjam/_openapi_client/models/streamer_token.py @@ -23,11 +23,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "token": token, - } - ) + field_dict.update({ + "token": token, + }) return field_dict diff --git a/fishjam/_openapi_client/models/streams_listing_response.py b/fishjam/_openapi_client/models/streams_listing_response.py index 86c9fef..d451de5 100644 --- a/fishjam/_openapi_client/models/streams_listing_response.py +++ b/fishjam/_openapi_client/models/streams_listing_response.py @@ -34,11 +34,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "data": data, - } - ) + field_dict.update({ + "data": data, + }) return field_dict diff --git a/fishjam/_openapi_client/models/subscribe_tracks_body.py b/fishjam/_openapi_client/models/subscribe_tracks_body.py index 1ce94a7..8daacc1 100644 --- a/fishjam/_openapi_client/models/subscribe_tracks_body.py +++ b/fishjam/_openapi_client/models/subscribe_tracks_body.py @@ -26,11 +26,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "track_ids": track_ids, - } - ) + field_dict.update({ + "track_ids": track_ids, + }) return field_dict diff --git a/fishjam/_openapi_client/models/subscriptions.py b/fishjam/_openapi_client/models/subscriptions.py index 673e2e1..82f61d3 100644 --- a/fishjam/_openapi_client/models/subscriptions.py +++ b/fishjam/_openapi_client/models/subscriptions.py @@ -31,12 +31,10 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "peers": peers, - "tracks": tracks, - } - ) + field_dict.update({ + "peers": peers, + "tracks": tracks, + }) return field_dict diff --git a/fishjam/_openapi_client/models/viewer.py b/fishjam/_openapi_client/models/viewer.py index 66b8c61..b34aeaa 100644 --- a/fishjam/_openapi_client/models/viewer.py +++ b/fishjam/_openapi_client/models/viewer.py @@ -41,13 +41,11 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "id": id, - "status": status, - "token": token, - } - ) + field_dict.update({ + "id": id, + "status": status, + "token": token, + }) return field_dict diff --git a/fishjam/_openapi_client/models/viewer_token.py b/fishjam/_openapi_client/models/viewer_token.py index 3a2e861..dee5c4a 100644 --- a/fishjam/_openapi_client/models/viewer_token.py +++ b/fishjam/_openapi_client/models/viewer_token.py @@ -23,11 +23,9 @@ def to_dict(self) -> dict[str, Any]: field_dict: dict[str, Any] = {} field_dict.update(self.additional_properties) - field_dict.update( - { - "token": token, - } - ) + field_dict.update({ + "token": token, + }) return field_dict From e8a537df82724dc35ee2b2ac26e5a2f1583d22c2 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 11:59:44 +0100 Subject: [PATCH 03/16] Update body class name --- fishjam/api/_fishjam_client.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fishjam/api/_fishjam_client.py b/fishjam/api/_fishjam_client.py index be0c021..3dc5d29 100644 --- a/fishjam/api/_fishjam_client.py +++ b/fishjam/api/_fishjam_client.py @@ -19,11 +19,11 @@ generate_viewer_token as viewer_generate_viewer_token, ) from fishjam._openapi_client.models import ( - AddPeerBody, AgentOutput, AudioFormat, AudioSampleRate, Peer, + PeerConfig, PeerDetailsResponse, PeerOptionsAgent, PeerOptionsWebRTC, @@ -177,7 +177,7 @@ def create_peer( metadata=peer_metadata, subscribe_mode=SubscribeMode(options.subscribe_mode), ) - body = AddPeerBody(type_=PeerType.WEBRTC, options=peer_options) + body = PeerConfig(type_=PeerType.WEBRTC, options=peer_options) resp = cast( PeerDetailsResponse, @@ -198,7 +198,7 @@ def create_agent(self, room_id: str, options: AgentOptions | None = None): and Fishjam URL. """ options = options or AgentOptions() - body = AddPeerBody( + body = PeerConfig( type_=PeerType.AGENT, options=PeerOptionsAgent( output=AgentOutput( From effbb8569c866df2974df9e57e7693b24dd6b731 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:28:18 +0100 Subject: [PATCH 04/16] Migrate tests from docker compose --- .github/workflows/ci.yml | 25 +++-------- docker-compose-test.yaml | 75 ------------------------------- protos | 2 +- tests/Dockerfile | 22 --------- tests/agent/test_agent.py | 10 ++--- tests/support/env.py | 4 ++ tests/support/webhook_notifier.py | 4 ++ tests/test_notifier.py | 24 ++++------ tests/test_room_api.py | 12 ++--- 9 files changed, 32 insertions(+), 146 deletions(-) delete mode 100644 docker-compose-test.yaml delete mode 100644 tests/Dockerfile create mode 100644 tests/support/env.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 77e82da..d32ee11 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,7 +5,7 @@ on: [push] jobs: static-checks: runs-on: ubuntu-latest - strategy: &python-matrix + strategy: matrix: python-version: - "3.11" @@ -38,21 +38,10 @@ jobs: - name: Type check run: uv run pyright + + - name: Type check + run: uv run pytest + env: + FISHJAM_ID: ${{ secrets.CI_FISHJAM_ID }} + FISHJAM_MANAGEMENT_TOKEN: ${{ secrets.CI_FISHJAM_MANAGEMENT_TOKEN }} - test: - runs-on: ubuntu-latest - strategy: *python-matrix - env: - PYTHON_VERSION: ${{ matrix.python-version }} - name: test - steps: - - uses: actions/checkout@v4 - - - name: Log in to GitHub Container Registry - run: echo "${{ secrets.PACKAGE_ACCESS_TOKEN }}" | docker login ghcr.io -u USERNAME --password-stdin - - - name: Run tests - run: docker compose -f docker-compose-test.yaml up test --exit-code-from test - - - name: Tear down test containers - run: docker compose -f docker-compose-test.yaml down diff --git a/docker-compose-test.yaml b/docker-compose-test.yaml deleted file mode 100644 index 67763ea..0000000 --- a/docker-compose-test.yaml +++ /dev/null @@ -1,75 +0,0 @@ -services: - fishjam: - image: ghcr.io/fishjam-cloud/fishjam:edge - container_name: fishjam - restart: on-failure - healthcheck: - test: > - curl --fail-with-body -H "Authorization: Bearer admin" - http://fishjam:5002/admin/health || exit 1 - interval: 3s - retries: 2 - timeout: 2s - start_period: 30s - environment: - FJ_HOST: "fishjam:5002" - FJ_ADMIN_TOKEN: "admin" - FJ_PORT: 5002 - FJ_TEST_USER_TOKEN: "development" - FJ_CHECK_ORIGIN: "false" - # Fisthank - FJ_FISHTANK_TOKEN: "foo" - FJ_FISHTANK_ADDRESS: fishtank:50051 - # Broadcaster - FJ_BROADCASTING_ENABLED: "true" - FJ_BROADCASTER_URL: "http://broadcaster:4000" - FJ_BROADCASTER_TOKEN: "broadcaster_token" - FJ_BROADCASTER_WHIP_TOKEN: "whip_token" - # Dev - LOG_LEVEL: "debug" - - ports: - - "5002:5002" - - fishtank: - image: ghcr.io/fishjam-cloud/fishtank:edge - container_name: fishtank - restart: on-failure - environment: - FT_FISHJAM_NOTIFICATIONS_ENDPOINT: http://fishjam:5002/notifications/fishtank - FT_FISHJAM_TOKEN: "foo" - FT_STRUCTURED_LOGGING: true - healthcheck: - interval: 3s - retries: 2 - timeout: 2s - start_period: 30s - ports: - - "8080:8080" - - caddy: - image: caddy - container_name: proxy - restart: unless-stopped - ports: - - 5555:5555 - volumes: - - ./Caddyfile:/etc/caddy/Caddyfile:ro - - test: - container_name: test - build: - context: . - dockerfile: tests/Dockerfile - args: - PYTHON_VERSION: ${PYTHON_VERSION:-3.11} - command: uv run pytest -s -vv - environment: - DOCKER_TEST: "TRUE" - depends_on: - fishtank: - condition: service_healthy - fishjam: - condition: service_healthy - caddy: - condition: service_started diff --git a/protos b/protos index 40f4ab8..9d807b5 160000 --- a/protos +++ b/protos @@ -1 +1 @@ -Subproject commit 40f4ab8013644de2be5d7d7ff2652725935a2e92 +Subproject commit 9d807b55279de385136f82b12f5df75d73104514 diff --git a/tests/Dockerfile b/tests/Dockerfile deleted file mode 100644 index 52c6349..0000000 --- a/tests/Dockerfile +++ /dev/null @@ -1,22 +0,0 @@ -ARG UV_VERSION=0.8 -ARG PYTHON_VERSION=3.11 -FROM ghcr.io/astral-sh/uv:$UV_VERSION AS uv-image - -ARG PYTHON_VERSION -FROM python:$PYTHON_VERSION-slim AS builder - -COPY --from=uv-image /uv /uvx /bin/ - -WORKDIR /app - -COPY pyproject.toml . - -COPY examples/transcription/pyproject.toml ./examples/transcription/ -COPY examples/poet_chat/pyproject.toml ./examples/poet_chat/ -COPY examples/selective_subscription/pyproject.toml ./examples/selective_subscription/ - -COPY uv.lock . - -RUN uv sync --locked --no-install-project --all-extras - -COPY . /app diff --git a/tests/agent/test_agent.py b/tests/agent/test_agent.py index ad09984..d29f3a2 100644 --- a/tests/agent/test_agent.py +++ b/tests/agent/test_agent.py @@ -1,5 +1,4 @@ import asyncio -import os from contextlib import suppress import pytest @@ -14,15 +13,12 @@ ServerMessagePeerMetadataUpdated, ) from fishjam.events.allowed_notifications import AllowedNotification - -HOST = "proxy" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" -FISHJAM_ID = f"http://{HOST}:5555" -SERVER_API_TOKEN = os.getenv("MANAGEMENT_TOKEN", "development") +from tests.support.env import FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN @pytest.fixture def room_api(): - return FishjamClient(FISHJAM_ID, SERVER_API_TOKEN) + return FishjamClient(FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN) @pytest.fixture @@ -43,7 +39,7 @@ def agent(room: Room, room_api: FishjamClient): async def notifier(): notifier = FishjamNotifier( fishjam_id=FISHJAM_ID, - management_token=SERVER_API_TOKEN, + management_token=FISHJAM_MANAGEMENT_TOKEN, ) @notifier.on_server_notification diff --git a/tests/support/env.py b/tests/support/env.py new file mode 100644 index 0000000..cfe658a --- /dev/null +++ b/tests/support/env.py @@ -0,0 +1,4 @@ +import os + +FISHJAM_ID = os.environ["FISHJAM_ID"] +FISHJAM_MANAGEMENT_TOKEN = os.environ["FISHJAM_MANAGEMENT_TOKEN"] diff --git a/tests/support/webhook_notifier.py b/tests/support/webhook_notifier.py index 4f0419c..8583e29 100644 --- a/tests/support/webhook_notifier.py +++ b/tests/support/webhook_notifier.py @@ -26,3 +26,7 @@ def run_server(queue): global DATA_QUEUE DATA_QUEUE = queue app.run(port=5000, host="0.0.0.0", use_reloader=False, debug=False, threaded=True) + + +WEBHOOK_SERVER_URL = "http://localhost:5000/" +WEBHOOK_URL = f"{WEBHOOK_SERVER_URL}webhook" diff --git a/tests/test_notifier.py b/tests/test_notifier.py index 7db7d5d..7ec72f6 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -1,7 +1,6 @@ # pylint: disable=locally-disabled, missing-class-docstring, missing-function-docstring, redefined-outer-name, too-few-public-methods, missing-module-docstring import asyncio -import os import socket import time from multiprocessing import Process, Queue @@ -20,15 +19,10 @@ ServerMessageRoomDeleted, ) from tests.support.asyncio_utils import assert_events, cancel +from tests.support.env import FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN from tests.support.peer_socket import PeerSocket -from tests.support.webhook_notifier import run_server - -FISHJAM_HOST = "proxy" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" -FISHJAM_URL = f"http://{FISHJAM_HOST}:5555" -FISHJAM_ID = FISHJAM_URL -SERVER_API_TOKEN = os.getenv("MANAGEMENT_TOKEN", "development") -WEBHOOK_ADDRESS = "test" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" -WEBHOOK_URL = f"http://{WEBHOOK_ADDRESS}:5000/webhook" +from tests.support.webhook_notifier import WEBHOOK_SERVER_URL, WEBHOOK_URL, run_server + queue = Queue() @@ -40,7 +34,7 @@ def start_server(): timeout = 60 # wait maximum of 60 seconds while True: try: - response = requests.get(f"http://{WEBHOOK_ADDRESS}:5000/", timeout=1_000) + response = requests.get(WEBHOOK_SERVER_URL, timeout=1_000) if response.status_code == 200: # Or another condition break except (requests.ConnectionError, socket.error): @@ -59,7 +53,7 @@ class TestConnectingToServer: async def test_valid_credentials(self): notifier = FishjamNotifier( fishjam_id=FISHJAM_ID, - management_token=SERVER_API_TOKEN, + management_token=FISHJAM_MANAGEMENT_TOKEN, ) @notifier.on_server_notification @@ -77,14 +71,14 @@ def handle_notitifcation(_notification): @pytest.fixture def room_api(): - return FishjamClient(FISHJAM_ID, SERVER_API_TOKEN) + return FishjamClient(FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN) @pytest.fixture def notifier(): notifier = FishjamNotifier( fishjam_id=FISHJAM_ID, - management_token=SERVER_API_TOKEN, + management_token=FISHJAM_MANAGEMENT_TOKEN, ) return notifier @@ -133,7 +127,7 @@ async def test_peer_connected_disconnected( room = room_api.create_room(options=options) peer, token = room_api.create_peer(room.id) - peer_socket = PeerSocket(fishjam_url=FISHJAM_URL) + peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) peer_task = asyncio.create_task(peer_socket.connect(token)) await peer_socket.wait_ready() @@ -168,7 +162,7 @@ async def test_peer_connected_room_deleted( room = room_api.create_room(options=options) _peer, token = room_api.create_peer(room.id) - peer_socket = PeerSocket(fishjam_url=FISHJAM_URL) + peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) peer_task = asyncio.create_task(peer_socket.connect(token)) await peer_socket.wait_ready() diff --git a/tests/test_room_api.py b/tests/test_room_api.py index 72f9c85..3407e1e 100644 --- a/tests/test_room_api.py +++ b/tests/test_room_api.py @@ -1,4 +1,3 @@ -import os from unittest.mock import Mock, patch import httpx @@ -29,10 +28,7 @@ VideoCodec, ) from fishjam.version import get_version - -HOST = "proxy" if os.getenv("DOCKER_TEST") == "TRUE" else "localhost" -FISHJAM_ID = f"http://{HOST}:5555" -MANAGEMENT_TOKEN = os.getenv("MANAGEMENT_TOKEN", "development") +from tests.support.env import FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN MAX_PEERS = 10 CODEC_H264 = "h264" @@ -49,7 +45,7 @@ def test_invalid_token(self): room_api.create_room() def test_valid_token(self): - room_api = FishjamClient(FISHJAM_ID, MANAGEMENT_TOKEN) + room_api = FishjamClient(FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN) room = room_api.create_room() all_rooms = room_api.get_all_rooms() @@ -71,7 +67,7 @@ def mock_send(request, **kwargs): captured_headers = dict(request.headers) return mock_response - room_api = FishjamClient(FISHJAM_ID, MANAGEMENT_TOKEN) + room_api = FishjamClient(FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN) with patch.object(httpx.HTTPTransport, "handle_request", side_effect=mock_send): try: @@ -89,7 +85,7 @@ def mock_send(request, **kwargs): @pytest.fixture def room_api(): - return FishjamClient(FISHJAM_ID, MANAGEMENT_TOKEN) + return FishjamClient(FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN) class TestCreateRoom: From d15e5853d125fc072741d2ed4c641d11dde40c6c Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:29:06 +0100 Subject: [PATCH 05/16] Rename workflow step --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d32ee11..aed7f8a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,7 +39,7 @@ jobs: - name: Type check run: uv run pyright - - name: Type check + - name: Run tests run: uv run pytest env: FISHJAM_ID: ${{ secrets.CI_FISHJAM_ID }} From c03bc7d8e653be83487fc7b8a4183e89c860c87b Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:46:32 +0100 Subject: [PATCH 06/16] Add webhook tunnel --- .github/workflows/ci.yml | 11 +++++++++-- tests/support/env.py | 2 ++ tests/support/webhook_notifier.py | 4 ---- tests/test_notifier.py | 9 +++++++-- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index aed7f8a..597742b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -30,6 +30,9 @@ jobs: - name: Install project dependencies run: uv sync --locked --all-extras --dev --all-packages + - name: Start localtunnel + run: npx localtunnel --port 5000 > tunnel.log 2>&1 & + - name: Format run: uv run format_check @@ -40,8 +43,12 @@ jobs: run: uv run pyright - name: Run tests - run: uv run pytest + run: | + WEBHOOK_SERVER_URL="$(grep -o 'https://[^ ]*' tunnel.log | head -n 1)" + if [ -z "$WEBHOOK_SERVER_URL" ]; then + echo "Failed to setup localtunnel!" + fi + uv run pytest env: FISHJAM_ID: ${{ secrets.CI_FISHJAM_ID }} FISHJAM_MANAGEMENT_TOKEN: ${{ secrets.CI_FISHJAM_MANAGEMENT_TOKEN }} - diff --git a/tests/support/env.py b/tests/support/env.py index cfe658a..12f6437 100644 --- a/tests/support/env.py +++ b/tests/support/env.py @@ -2,3 +2,5 @@ FISHJAM_ID = os.environ["FISHJAM_ID"] FISHJAM_MANAGEMENT_TOKEN = os.environ["FISHJAM_MANAGEMENT_TOKEN"] +WEBHOOK_SERVER_URL = os.getenv("WEBHOOK_SERVER_URL", "http://localhost:5000/") +WEBHOOK_URL = f"{WEBHOOK_SERVER_URL}webhook" diff --git a/tests/support/webhook_notifier.py b/tests/support/webhook_notifier.py index 8583e29..4f0419c 100644 --- a/tests/support/webhook_notifier.py +++ b/tests/support/webhook_notifier.py @@ -26,7 +26,3 @@ def run_server(queue): global DATA_QUEUE DATA_QUEUE = queue app.run(port=5000, host="0.0.0.0", use_reloader=False, debug=False, threaded=True) - - -WEBHOOK_SERVER_URL = "http://localhost:5000/" -WEBHOOK_URL = f"{WEBHOOK_SERVER_URL}webhook" diff --git a/tests/test_notifier.py b/tests/test_notifier.py index 7ec72f6..ef3c42f 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -19,9 +19,14 @@ ServerMessageRoomDeleted, ) from tests.support.asyncio_utils import assert_events, cancel -from tests.support.env import FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN +from tests.support.env import ( + FISHJAM_ID, + FISHJAM_MANAGEMENT_TOKEN, + WEBHOOK_SERVER_URL, + WEBHOOK_URL, +) from tests.support.peer_socket import PeerSocket -from tests.support.webhook_notifier import WEBHOOK_SERVER_URL, WEBHOOK_URL, run_server +from tests.support.webhook_notifier import run_server queue = Queue() From a0570c1c90bac6319ebcdd45462145e31756b77d Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 12:57:55 +0100 Subject: [PATCH 07/16] Test fixes --- tests/support/asyncio_utils.py | 8 ---- tests/support/env.py | 4 +- tests/test_notifier.py | 86 +++++++++++++++++----------------- 3 files changed, 46 insertions(+), 52 deletions(-) diff --git a/tests/support/asyncio_utils.py b/tests/support/asyncio_utils.py index e415766..92a4734 100644 --- a/tests/support/asyncio_utils.py +++ b/tests/support/asyncio_utils.py @@ -29,11 +29,3 @@ def handle_message(message): raise asyncio.exceptions.TimeoutError( f"{message_checks[0]} hasn't been received within timeout" ) from exc - - -async def cancel(task): - task.cancel() - try: - await task - except asyncio.exceptions.CancelledError: - pass diff --git a/tests/support/env.py b/tests/support/env.py index 12f6437..b96d138 100644 --- a/tests/support/env.py +++ b/tests/support/env.py @@ -2,5 +2,5 @@ FISHJAM_ID = os.environ["FISHJAM_ID"] FISHJAM_MANAGEMENT_TOKEN = os.environ["FISHJAM_MANAGEMENT_TOKEN"] -WEBHOOK_SERVER_URL = os.getenv("WEBHOOK_SERVER_URL", "http://localhost:5000/") -WEBHOOK_URL = f"{WEBHOOK_SERVER_URL}webhook" +WEBHOOK_SERVER_URL = os.getenv("WEBHOOK_SERVER_URL", "http://localhost:5000") +WEBHOOK_URL = f"{WEBHOOK_SERVER_URL}/webhook" diff --git a/tests/test_notifier.py b/tests/test_notifier.py index ef3c42f..fbb802c 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -18,7 +18,7 @@ ServerMessageRoomCreated, ServerMessageRoomDeleted, ) -from tests.support.asyncio_utils import assert_events, cancel +from tests.support.asyncio_utils import assert_events from tests.support.env import ( FISHJAM_ID, FISHJAM_MANAGEMENT_TOKEN, @@ -65,13 +65,14 @@ async def test_valid_credentials(self): def handle_notitifcation(_notification): pass - notifier_task = asyncio.create_task(notifier.connect()) - await notifier.wait_ready() - assert ( - notifier._websocket and notifier._websocket.state == websockets.State.OPEN - ) + async with asyncio.TaskGroup() as tg: + tg.create_task(notifier.connect()) + await notifier.wait_ready() - await cancel(notifier_task) + assert ( + notifier._websocket + and notifier._websocket.state == websockets.State.OPEN + ) @pytest.fixture @@ -95,18 +96,19 @@ async def test_room_created_deleted( self, room_api: FishjamClient, notifier: FishjamNotifier ): event_checks = [ServerMessageRoomCreated, ServerMessageRoomDeleted] - assert_task = asyncio.create_task(assert_events(notifier, event_checks.copy())) - notifier_task = asyncio.create_task(notifier.connect()) - await notifier.wait_ready() + async with asyncio.TaskGroup() as tg: + assert_task = tg.create_task(assert_events(notifier, event_checks.copy())) + + tg.create_task(notifier.connect()) + await notifier.wait_ready() - options = RoomOptions(webhook_url=WEBHOOK_URL) - room = room_api.create_room(options=options) + options = RoomOptions(webhook_url=WEBHOOK_URL) + room = room_api.create_room(options=options) - room_api.delete_room(room.id) + room_api.delete_room(room.id) - await assert_task - await cancel(notifier_task) + await assert_task for event in event_checks: self.assert_event(event) @@ -123,26 +125,26 @@ async def test_peer_connected_disconnected( ServerMessagePeerDeleted, ServerMessageRoomDeleted, ] - assert_task = asyncio.create_task(assert_events(notifier, event_checks.copy())) - notifier_task = asyncio.create_task(notifier.connect()) - await notifier.wait_ready() + async with asyncio.TaskGroup() as tg: + assert_task = tg.create_task(assert_events(notifier, event_checks.copy())) - options = RoomOptions(webhook_url=WEBHOOK_URL) - room = room_api.create_room(options=options) + tg.create_task(notifier.connect()) + await notifier.wait_ready() - peer, token = room_api.create_peer(room.id) - peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) - peer_task = asyncio.create_task(peer_socket.connect(token)) + options = RoomOptions(webhook_url=WEBHOOK_URL) + room = room_api.create_room(options=options) - await peer_socket.wait_ready() + peer, token = room_api.create_peer(room.id) + peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) + tg.create_task(peer_socket.connect(token)) - room_api.delete_peer(room.id, peer.id) - room_api.delete_room(room.id) + await peer_socket.wait_ready() - await assert_task - await cancel(peer_task) - await cancel(notifier_task) + room_api.delete_peer(room.id, peer.id) + room_api.delete_room(room.id) + + await assert_task for event in event_checks: self.assert_event(event) @@ -158,25 +160,25 @@ async def test_peer_connected_room_deleted( ServerMessagePeerDeleted, ServerMessageRoomDeleted, ] - assert_task = asyncio.create_task(assert_events(notifier, event_checks.copy())) - notifier_task = asyncio.create_task(notifier.connect()) - await notifier.wait_ready() + async with asyncio.TaskGroup() as tg: + assert_task = tg.create_task(assert_events(notifier, event_checks.copy())) + + tg.create_task(notifier.connect()) + await notifier.wait_ready() - options = RoomOptions(webhook_url=WEBHOOK_URL) - room = room_api.create_room(options=options) - _peer, token = room_api.create_peer(room.id) + options = RoomOptions(webhook_url=WEBHOOK_URL) + room = room_api.create_room(options=options) + _peer, token = room_api.create_peer(room.id) - peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) - peer_task = asyncio.create_task(peer_socket.connect(token)) + peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) + tg.create_task(peer_socket.connect(token)) - await peer_socket.wait_ready() + await peer_socket.wait_ready() - room_api.delete_room(room.id) + room_api.delete_room(room.id) - await assert_task - await cancel(peer_task) - await cancel(notifier_task) + await assert_task for event in event_checks: self.assert_event(event) From ae3e1149c0f9543af63a596ac4554f0a9aa0a608 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 13:14:05 +0100 Subject: [PATCH 08/16] Use tee for localtunnel logs --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 597742b..7ec4d14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,7 +31,7 @@ jobs: run: uv sync --locked --all-extras --dev --all-packages - name: Start localtunnel - run: npx localtunnel --port 5000 > tunnel.log 2>&1 & + run: npx localtunnel --port 5000 2>&1 | tee tunnel.log & - name: Format run: uv run format_check From 6a89955f6bc032badfd26891b8d5c10204699e5b Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 15:53:07 +0100 Subject: [PATCH 09/16] Fix notifier test setup hanging on error --- tests/test_notifier.py | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/tests/test_notifier.py b/tests/test_notifier.py index fbb802c..81067bc 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -36,19 +36,10 @@ def start_server(): flask_process = Process(target=run_server, args=(queue,)) flask_process.start() - timeout = 60 # wait maximum of 60 seconds - while True: - try: - response = requests.get(WEBHOOK_SERVER_URL, timeout=1_000) - if response.status_code == 200: # Or another condition - break - except (requests.ConnectionError, socket.error): - time.sleep(1) # wait for 1 second before trying again - timeout -= 1 - if timeout == 0: - pytest.fail("Server did not start in the expected time") - - yield # This is where the testing happens. + response = requests.get(WEBHOOK_SERVER_URL, timeout=3) + response.raise_for_status() + + yield flask_process.terminate() From 95e9f5ea4c704fc82bc208bf196e8ad74c5f8135 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Mon, 9 Feb 2026 17:25:46 +0100 Subject: [PATCH 10/16] Fix tests and bump protos --- fishjam/_ws_notifier.py | 2 +- tests/test_notifier.py | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/fishjam/_ws_notifier.py b/fishjam/_ws_notifier.py index b3c2696..bfbd2da 100644 --- a/fishjam/_ws_notifier.py +++ b/fishjam/_ws_notifier.py @@ -130,7 +130,7 @@ async def _receive_loop(self): raise RuntimeError("Notification handler is not defined") while True: - message = cast(bytes, await self._websocket.recv()) + message = await self._websocket.recv(decode=False) message = ServerMessage().parse(message) _which, message = betterproto.which_one_of(message, "content") diff --git a/tests/test_notifier.py b/tests/test_notifier.py index 81067bc..3e48c9a 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -57,7 +57,7 @@ def handle_notitifcation(_notification): pass async with asyncio.TaskGroup() as tg: - tg.create_task(notifier.connect()) + notifier_task = tg.create_task(notifier.connect()) await notifier.wait_ready() assert ( @@ -65,6 +65,8 @@ def handle_notitifcation(_notification): and notifier._websocket.state == websockets.State.OPEN ) + notifier_task.cancel() + @pytest.fixture def room_api(): @@ -91,7 +93,7 @@ async def test_room_created_deleted( async with asyncio.TaskGroup() as tg: assert_task = tg.create_task(assert_events(notifier, event_checks.copy())) - tg.create_task(notifier.connect()) + notifier_task = tg.create_task(notifier.connect()) await notifier.wait_ready() options = RoomOptions(webhook_url=WEBHOOK_URL) @@ -101,6 +103,8 @@ async def test_room_created_deleted( await assert_task + notifier_task.cancel() + for event in event_checks: self.assert_event(event) @@ -120,7 +124,7 @@ async def test_peer_connected_disconnected( async with asyncio.TaskGroup() as tg: assert_task = tg.create_task(assert_events(notifier, event_checks.copy())) - tg.create_task(notifier.connect()) + notifier_task = tg.create_task(notifier.connect()) await notifier.wait_ready() options = RoomOptions(webhook_url=WEBHOOK_URL) @@ -128,7 +132,7 @@ async def test_peer_connected_disconnected( peer, token = room_api.create_peer(room.id) peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) - tg.create_task(peer_socket.connect(token)) + peer_socket_task = tg.create_task(peer_socket.connect(token)) await peer_socket.wait_ready() @@ -137,6 +141,9 @@ async def test_peer_connected_disconnected( await assert_task + notifier_task.cancel() + peer_socket_task.cancel() + for event in event_checks: self.assert_event(event) @@ -155,7 +162,7 @@ async def test_peer_connected_room_deleted( async with asyncio.TaskGroup() as tg: assert_task = tg.create_task(assert_events(notifier, event_checks.copy())) - tg.create_task(notifier.connect()) + notifier_task = tg.create_task(notifier.connect()) await notifier.wait_ready() options = RoomOptions(webhook_url=WEBHOOK_URL) @@ -163,7 +170,7 @@ async def test_peer_connected_room_deleted( _peer, token = room_api.create_peer(room.id) peer_socket = PeerSocket(fishjam_url=FISHJAM_ID) - tg.create_task(peer_socket.connect(token)) + peer_socket_task = tg.create_task(peer_socket.connect(token)) await peer_socket.wait_ready() @@ -171,6 +178,9 @@ async def test_peer_connected_room_deleted( await assert_task + notifier_task.cancel() + peer_socket_task.cancel() + for event in event_checks: self.assert_event(event) From 7c37ed0d4fc0a30967f281bb2ef6dc9b8f9ea471 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:16:46 +0100 Subject: [PATCH 11/16] Remove unused imports --- tests/test_notifier.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/test_notifier.py b/tests/test_notifier.py index 3e48c9a..cf125bf 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -1,8 +1,6 @@ # pylint: disable=locally-disabled, missing-class-docstring, missing-function-docstring, redefined-outer-name, too-few-public-methods, missing-module-docstring import asyncio -import socket -import time from multiprocessing import Process, Queue import pytest From 15e2c44a1bc98dad45542c99f899c0b5e9e1f554 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:23:13 +0100 Subject: [PATCH 12/16] Increase timeouts --- tests/test_notifier.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_notifier.py b/tests/test_notifier.py index cf125bf..084253c 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -34,7 +34,7 @@ def start_server(): flask_process = Process(target=run_server, args=(queue,)) flask_process.start() - response = requests.get(WEBHOOK_SERVER_URL, timeout=3) + response = requests.get(WEBHOOK_SERVER_URL, timeout=5) response.raise_for_status() yield @@ -183,5 +183,5 @@ async def test_peer_connected_room_deleted( self.assert_event(event) def assert_event(self, event): - data = queue.get(timeout=2.5) + data = queue.get(timeout=5) assert data == event or isinstance(data, event) From c9c842653d949a92424bc0e52d8ee72a0279caef Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:26:06 +0100 Subject: [PATCH 13/16] Exit with 1 if webhook setup failed --- .github/workflows/ci.yml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7ec4d14..81ccb08 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -44,11 +44,14 @@ jobs: - name: Run tests run: | - WEBHOOK_SERVER_URL="$(grep -o 'https://[^ ]*' tunnel.log | head -n 1)" - if [ -z "$WEBHOOK_SERVER_URL" ]; then + PUBLIC_URL="$(grep -o 'https://[^ ]*' tunnel.log | head -n 1)" + if [ -z "$PUBLIC_URL" ]; then echo "Failed to setup localtunnel!" + cat tunnel.log + exit 1 fi - uv run pytest + echo "Webhook URL: $PUBLIC_URL" + WEBHOOK_SERVER_URL="$PUBLIC_URL" uv run pytest env: FISHJAM_ID: ${{ secrets.CI_FISHJAM_ID }} FISHJAM_MANAGEMENT_TOKEN: ${{ secrets.CI_FISHJAM_MANAGEMENT_TOKEN }} From 43ff831498c0d75d1bb718b444bf4a7d10cdf334 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:37:39 +0100 Subject: [PATCH 14/16] Wait for localtunnel and separate jobs --- .github/workflows/ci.yml | 95 ++++++++++++++++++++++++++-------------- 1 file changed, 61 insertions(+), 34 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81ccb08..b7029b5 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,55 +3,82 @@ name: CI on: [push] jobs: - static-checks: + lint-and-format: runs-on: ubuntu-latest - strategy: - matrix: - python-version: - - "3.11" - - "3.12" - - "3.13" - name: static-checks steps: - - name: Checkout code - uses: actions/checkout@v4 - - - name: Set up Python - uses: actions/setup-python@v5 + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v6 with: - python-version: ${{ matrix.python-version }} - - - name: Install uv - uses: astral-sh/setup-uv@v6 - with: - python-version: ${{ matrix.python-version }} enable-cache: true - name: Install project dependencies - run: uv sync --locked --all-extras --dev --all-packages - - - name: Start localtunnel - run: npx localtunnel --port 5000 2>&1 | tee tunnel.log & + run: uv sync --locked --all-extras --dev - - name: Format + - name: Format Check run: uv run format_check - name: Lint run: uv run lint + type-check: + runs-on: ubuntu-latest + strategy: &strategy + matrix: + python-version: ["3.11", "3.12", "3.13"] + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v5 + with: + python-version: ${{ matrix.python-version }} + enable-cache: true + + - name: Install project dependencies + run: uv sync --locked --all-extras --dev + - name: Type check run: uv run pyright - - - name: Run tests + + test: + runs-on: ubuntu-latest + needs: [lint-and-format] + strategy: *strategy + steps: + - uses: actions/checkout@v4 + - uses: astral-sh/setup-uv@v6 + with: + python-version: ${{ matrix.python-version }} + enable-cache: true + + - name: Install project dependencies + run: uv sync --locked --all-extras --dev + + - name: Initialize Localtunnel + id: tunnel run: | - PUBLIC_URL="$(grep -o 'https://[^ ]*' tunnel.log | head -n 1)" - if [ -z "$PUBLIC_URL" ]; then - echo "Failed to setup localtunnel!" - cat tunnel.log - exit 1 - fi - echo "Webhook URL: $PUBLIC_URL" - WEBHOOK_SERVER_URL="$PUBLIC_URL" uv run pytest + npx localtunnel --port 5000 > tunnel.log 2>&1 & + + # Poll for the URL + TIMEOUT=10 + ELAPSED=0 + echo "Waiting for localtunnel to generate URL..." + + while ! grep -q "https://" tunnel.log; do + if [ $ELAPSED -ge $TIMEOUT ]; then + echo "Error: Localtunnel timed out after ${TIMEOUT}s" + cat tunnel.log + exit 1 + fi + sleep 1 + ELAPSED=$((ELAPSED + 1)) + done + + TUNNEL_URL=$(grep -o 'https://[^ ]*' tunnel.log | head -n 1) + echo "url=$TUNNEL_URL" >> $GITHUB_OUTPUT + echo "Localtunnel is live at: $TUNNEL_URL" + + - name: Run tests + run: uv run pytest env: + WEBHOOK_SERVER_URL: ${{ steps.tunnel.outputs.url }} FISHJAM_ID: ${{ secrets.CI_FISHJAM_ID }} FISHJAM_MANAGEMENT_TOKEN: ${{ secrets.CI_FISHJAM_MANAGEMENT_TOKEN }} From 3e2b693ade034a24dbee4fc608cfb90bc30157c9 Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:40:33 +0100 Subject: [PATCH 15/16] Fix uv sync step --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b7029b5..1b6e312 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: enable-cache: true - name: Install project dependencies - run: uv sync --locked --all-extras --dev + run: uv sync --locked --all-extras --all-packages - name: Format Check run: uv run format_check @@ -33,7 +33,7 @@ jobs: enable-cache: true - name: Install project dependencies - run: uv sync --locked --all-extras --dev + run: uv sync --locked --all-extras --all-packages - name: Type check run: uv run pyright @@ -50,7 +50,7 @@ jobs: enable-cache: true - name: Install project dependencies - run: uv sync --locked --all-extras --dev + run: uv sync --locked --all-extras --all-packages - name: Initialize Localtunnel id: tunnel From 0d653f0b08eccbc29dcac63d4b4fe3e6c415bede Mon Sep 17 00:00:00 2001 From: Tomasz Mazur <47872060+AHGIJMKLKKZNPJKQR@users.noreply.github.com> Date: Tue, 10 Feb 2026 08:52:35 +0100 Subject: [PATCH 16/16] Add retries to flask ready-check --- tests/test_notifier.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_notifier.py b/tests/test_notifier.py index 084253c..854dbc7 100644 --- a/tests/test_notifier.py +++ b/tests/test_notifier.py @@ -6,6 +6,8 @@ import pytest import requests import websockets +from requests.adapters import HTTPAdapter +from urllib3 import Retry from fishjam import FishjamClient, FishjamNotifier, RoomOptions from fishjam.events import ( @@ -34,7 +36,18 @@ def start_server(): flask_process = Process(target=run_server, args=(queue,)) flask_process.start() - response = requests.get(WEBHOOK_SERVER_URL, timeout=5) + session = requests.Session() + session.mount( + "http", + HTTPAdapter( + max_retries=Retry( + total=5, + backoff_factor=0.25, + ) + ), + ) + + response = session.get(WEBHOOK_SERVER_URL, timeout=5) response.raise_for_status() yield