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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ class _ConversationUpdateBase(CustomBaseModel):


class ConversationUpdateActivity(_ConversationUpdateBase, ActivityBase):
"""Output model for received conversation update activities with required fields and read-only properties."""

channel_data: ConversationChannelData # pyright: ignore [reportGeneralTypeIssues, reportIncompatibleVariableOverride]
"""Channel data with event type information."""
"""Output model for received conversation update activities with read-only properties.
Note: channel_data may be absent for some conversationUpdate payloads
(e.g. Direct Line activities) and should be treated as optional.
"""
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .sensitive_usage_entity import SensitiveUsage, SensitiveUsageEntity, SensitiveUsagePattern
from .stream_info_entity import StreamInfoEntity
from .targeted_message_info_entity import TargetedMessageInfoEntity
from .unknown_entity import UnknownEntity

__all__ = [
"AIMessageEntity",
Expand All @@ -43,5 +44,6 @@
"SensitiveUsagePattern",
"StreamInfoEntity",
"TargetedMessageInfoEntity",
"UnknownEntity",
"Entity",
]
2 changes: 2 additions & 0 deletions packages/api/src/microsoft_teams/api/models/entity/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from .sensitive_usage_entity import SensitiveUsageEntity
from .stream_info_entity import StreamInfoEntity
from .targeted_message_info_entity import TargetedMessageInfoEntity
from .unknown_entity import UnknownEntity

Entity = Union[
ClientInfoEntity,
Expand All @@ -27,4 +28,5 @@
ProductInfoEntity,
QuotedReplyEntity,
TargetedMessageInfoEntity,
UnknownEntity,
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from pydantic import ConfigDict

from ..custom_base_model import CustomBaseModel


class UnknownEntity(CustomBaseModel):
"""Entity for unknown or forward-compatible types."""

Comment thread
dcaayushd marked this conversation as resolved.
model_config = ConfigDict(extra="allow")

# Type identifier for the unknown entity.
type: str
"Type identifier for the unknown entity."
Comment thread
dcaayushd marked this conversation as resolved.
56 changes: 56 additions & 0 deletions packages/api/tests/unit/test_activity.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@
from datetime import datetime

import pytest
from microsoft_teams.api.activities import ActivityTypeAdapter
from microsoft_teams.api.activities.conversation import ConversationUpdateActivity
from microsoft_teams.api.activities.message import MessageActivity
from microsoft_teams.api.models import (
Account,
ActivityInputBase,
ChannelData,
ChannelInfo,
ConversationAccount,
MeetingInfo,
MentionEntity,
NotificationInfo,
TeamInfo,
TenantInfo,
UnknownEntity,
)


Expand Down Expand Up @@ -106,3 +111,54 @@ def test_should_have_channel_data_accessors(
assert activity.team and activity.team.id == "team-id"
assert activity.meeting and activity.meeting.id == "meeting-id"
assert activity.notification and activity.notification.alert is True


@pytest.mark.unit
class TestActivityTypeAdapter:
"""Unit tests for ActivityTypeAdapter behavior."""

def test_accepts_unknown_entity_types(self) -> None:
payload = {
"type": "message",
"id": "msg-unknown-1",
"from": {"id": "user-123", "name": "Test User"},
"conversation": {"id": "conv-456", "conversationType": "personal"},
"recipient": {"id": "bot-789", "name": "Test Bot"},
"entities": [
{
"type": "ClientCapabilities",
"supportsListening": True,
"supportsSplashScreen": True,
"supportsTts": True,
},
{
"type": "mention",
"mentioned": {"id": "user-123", "name": "Test User"},
"text": "<at>Test User</at>",
},
],
}

activity = ActivityTypeAdapter.validate_python(payload)

assert isinstance(activity, MessageActivity)
assert activity.entities is not None
assert len(activity.entities) == 2
assert isinstance(activity.entities[0], UnknownEntity)
assert activity.entities[0].type == "ClientCapabilities"
assert activity.entities[0].model_dump().get("supportsListening") is True
assert isinstance(activity.entities[1], MentionEntity)

def test_conversation_update_without_channel_data(self) -> None:
payload = {
"type": "conversationUpdate",
"id": "conv-update-1",
"from": {"id": "user-123", "name": "Test User"},
"conversation": {"id": "conv-456", "conversationType": "personal"},
"recipient": {"id": "bot-789", "name": "Test Bot"},
}

activity = ActivityTypeAdapter.validate_python(payload)

assert isinstance(activity, ConversationUpdateActivity)
assert activity.channel_data is None