Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions examples/snippets/conversation/messages/list/snippet.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Sinch Python Snippet

TODO: Update links when v2 is released.
This snippet is available at https://github.com/sinch/sinch-sdk-python/blob/v2.0/docs/snippets/
"""

import os
from dotenv import load_dotenv
from sinch import SinchClient

load_dotenv()

sinch_client = SinchClient(
project_id=os.environ.get("SINCH_PROJECT_ID") or "MY_PROJECT_ID",
key_id=os.environ.get("SINCH_KEY_ID") or "MY_KEY_ID",
key_secret=os.environ.get("SINCH_KEY_SECRET") or "MY_KEY_SECRET",
conversation_region=os.environ.get("SINCH_CONVERSATION_REGION") or "MY_CONVERSATION_REGION"
)

# The ID of the Conversation App to list messages from
app_id = "CONVERSATION_APP_ID"

messages = sinch_client.conversation.messages.list(
app_id=app_id,
page_size=10
)

page_counter = 1
while True:
print(f"Page {page_counter} List of Messages: {messages}")

if not messages.has_next_page:
break

messages = messages.next_page()
page_counter += 1
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import pytest
from sinch.core.models.http_response import HTTPResponse
from sinch.domains.conversation.api.v1.internal import ListMessagesEndpoint
from sinch.domains.conversation.models.v1.messages.internal import (
ListMessagesResponse,
)
from sinch.domains.conversation.models.v1.messages.internal.request import (
ListMessagesRequest,
)
from tests.unit.domains.conversation.v1.models.response.test_conversation_message_response_model import (
contact_message_response_data,
)


@pytest.fixture
def request_data():
return ListMessagesRequest(page_size=10)


@pytest.fixture
def endpoint(request_data):
return ListMessagesEndpoint("test_project_id", request_data)


@pytest.fixture
def mock_list_messages_response(contact_message_response_data):
return HTTPResponse(
status_code=200,
body={
"messages": [contact_message_response_data],
"next_page_token": "token_next_page_abc",
},
headers={"Content-Type": "application/json"},
)


def test_build_url_expects_correct_url(endpoint, mock_sinch_client_conversation):
"""Test that the URL is built correctly (no path params beyond project_id)."""
assert (
endpoint.build_url(mock_sinch_client_conversation)
== "https://us.conversation.api.sinch.com/v1/projects/test_project_id/messages"
)


def test_build_query_params_expects_excludes_unset_fields():
"""Test that query params only include non-None fields."""
request_data = ListMessagesRequest(page_size=10)
endpoint = ListMessagesEndpoint("test_project_id", request_data)

query_params = endpoint.build_query_params()

assert query_params["page_size"] == 10
assert "conversation_id" not in query_params


def test_build_query_params_expects_parsed_params():
"""Test that all query param fields are serialized when set."""
request_data = ListMessagesRequest(
conversation_id="CONV123",
contact_id="CONTACT456",
app_id="APP789",
channel_identity="+46701234567",
page_size=20,
page_token="token_xyz",
view="WITH_METADATA",
messages_source="DISPATCH_SOURCE",
only_recipient_originated=True,
channel="WHATSAPP",
direction="TO_APP",
)
endpoint = ListMessagesEndpoint("test_project_id", request_data)

query_params = endpoint.build_query_params()

assert query_params["conversation_id"] == "CONV123"
assert query_params["contact_id"] == "CONTACT456"
assert query_params["app_id"] == "APP789"
assert query_params["channel_identity"] == "+46701234567"
assert query_params["page_size"] == 20
assert query_params["page_token"] == "token_xyz"
assert query_params["view"] == "WITH_METADATA"
assert query_params["messages_source"] == "DISPATCH_SOURCE"
assert query_params["only_recipient_originated"] is True
assert query_params["channel"] == "WHATSAPP"
assert query_params["direction"] == "TO_APP"


def test_handle_response_expects_list_messages_response(
endpoint, mock_list_messages_response
):
"""Test that a successful response is parsed to ListMessagesResponse."""
result = endpoint.handle_response(mock_list_messages_response)

assert isinstance(result, ListMessagesResponse)
assert result.next_page_token == "token_next_page_abc"
assert result.messages is not None
assert len(result.messages) == 1
assert result.messages[0].id == "CAPY123456789ABCDEFGHIJKLMNOP"


def test_handle_response_expects_empty_messages_list():
"""Test that response with empty messages list is handled correctly."""
request_data = ListMessagesRequest(page_size=10)
endpoint = ListMessagesEndpoint("test_project_id", request_data)
mock_response = HTTPResponse(
status_code=200,
body={"messages": [], "next_page_token": None},
headers={"Content-Type": "application/json"},
)

result = endpoint.handle_response(mock_response)

assert isinstance(result, ListMessagesResponse)
assert result.messages == []
assert result.next_page_token is None
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from datetime import datetime, timezone

from sinch.domains.conversation.models.v1.messages.internal.request import (
ListMessagesRequest,
)


def test_list_messages_request_expects_parsed_input():
"""Test that the model correctly parses input with all parameters."""
start = datetime(2025, 1, 1, 12, 0, 0, tzinfo=timezone.utc)
end = datetime(2025, 1, 8, 12, 0, 0, tzinfo=timezone.utc)

request = ListMessagesRequest(
conversation_id="CONV123456789ABCDEFGHIJKLM",
contact_id="CONTACT456789ABCDEFGHIJKLMNOP",
app_id="APP123456789ABCDEFGHIJK",
channel_identity="+46701234567",
start_time=start,
end_time=end,
page_size=50,
page_token="next_page_token_abc",
view="WITH_METADATA",
messages_source="CONVERSATION_SOURCE",
only_recipient_originated=True,
channel="WHATSAPP",
direction="TO_CONTACT",
)

assert request.conversation_id == "CONV123456789ABCDEFGHIJKLM"
assert request.contact_id == "CONTACT456789ABCDEFGHIJKLMNOP"
assert request.app_id == "APP123456789ABCDEFGHIJK"
assert request.channel_identity == "+46701234567"
assert request.start_time == start
assert request.end_time == end
assert request.page_size == 50
assert request.page_token == "next_page_token_abc"
assert request.view == "WITH_METADATA"
assert request.messages_source == "CONVERSATION_SOURCE"
assert request.only_recipient_originated is True
assert request.channel == "WHATSAPP"
assert request.direction == "TO_CONTACT"
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from sinch.domains.conversation.models.v1.messages.internal import (
ListMessagesResponse,
)
from tests.unit.domains.conversation.v1.models.response.test_conversation_message_response_model import (
contact_message_response_data,
app_message_response_data,
)


def test_list_messages_response_expects_correct_mapping(
contact_message_response_data,
app_message_response_data,
):
"""
Test that response is correctly parsed from dict and
content property returns messages.
"""
data = {
"messages": [contact_message_response_data, app_message_response_data],
"next_page_token": "token_abc",
}
response = ListMessagesResponse.model_validate(data)

assert response.next_page_token == "token_abc"
assert response.messages is not None
assert len(response.messages) == 2
assert response.messages[0].id == contact_message_response_data["id"]
assert response.messages[1].id == app_message_response_data["id"]
assert response.content == response.messages
assert len(response.content) == 2


def test_list_messages_response_expects_empty_messages_list():
"""Test that response with empty messages list has content as empty list."""
response = ListMessagesResponse(messages=[], next_page_token=None)

assert response.messages == []
assert response.content == []
assert response.next_page_token is None