From 9037e981375d362d28e09218b9b73497aedd63f6 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Fri, 2 Oct 2026 15:28:35 -0700 Subject: [PATCH 1/3] Using the appropriate models for meeting routes --- .../hosting/msteams/meeting/meeting.py | 11 +++-- .../hosting/msteams/meeting/route_handlers.py | 7 +-- tests/hosting_msteams/test_meetings.py | 49 ++++++++++++++----- 3 files changed, 48 insertions(+), 19 deletions(-) diff --git a/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/meeting.py b/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/meeting.py index 3f4fb2a1a..002392ccb 100644 --- a/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/meeting.py +++ b/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/meeting.py @@ -5,7 +5,8 @@ from typing import Generic, Optional, overload -from microsoft_teams.api.models.meetings import MeetingDetails +from microsoft_teams.api.activities.event.meeting_end import MeetingEndEventValue +from microsoft_teams.api.activities.event.meeting_start import MeetingStartEventValue from microsoft_agents.activity import ActivityTypes from microsoft_agents.activity.teams import MeetingParticipantsEventDetails @@ -79,7 +80,9 @@ def __call(func: MeetingStartHandler[StateT]) -> MeetingStartHandler[StateT]: async def __handler(context: TurnContext, state: StateT) -> None: """Adapt the core turn context and dispatch to the meeting start handler.""" teams_context = TeamsTurnContext(context, self._app) - meeting = MeetingDetails.model_validate(context.activity.value or {}) + meeting = MeetingStartEventValue.model_validate( + context.activity.value or {} + ) await func(teams_context, state, meeting) self._app.add_route( @@ -130,7 +133,9 @@ def __call(func: MeetingEndHandler[StateT]) -> MeetingEndHandler[StateT]: async def __handler(context: TurnContext, state: StateT) -> None: """Adapt the core turn context and dispatch to the meeting end handler.""" teams_context = TeamsTurnContext(context, self._app) - meeting = MeetingDetails.model_validate(context.activity.value or {}) + meeting = MeetingEndEventValue.model_validate( + context.activity.value or {} + ) await func(teams_context, state, meeting) self._app.add_route( diff --git a/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/route_handlers.py b/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/route_handlers.py index 85101c0f8..f262dc8dd 100644 --- a/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/route_handlers.py +++ b/libraries/microsoft-agents-hosting-msteams/microsoft_agents/hosting/msteams/meeting/route_handlers.py @@ -5,7 +5,8 @@ from typing import Awaitable, Protocol -from microsoft_teams.api.models.meetings import MeetingDetails +from microsoft_teams.api.activities.event.meeting_end import MeetingEndEventValue +from microsoft_teams.api.activities.event.meeting_start import MeetingStartEventValue from microsoft_agents.activity.teams import MeetingParticipantsEventDetails from microsoft_agents.hosting.core.app._type_defs import _StateContra @@ -19,7 +20,7 @@ def __call__( self, context: TeamsTurnContext, state: _StateContra, - meeting: MeetingDetails, + meeting: MeetingStartEventValue, /, ) -> Awaitable[None]: """Handle a meeting start event. @@ -39,7 +40,7 @@ def __call__( self, context: TeamsTurnContext, state: _StateContra, - meeting: MeetingDetails, + meeting: MeetingEndEventValue, /, ) -> Awaitable[None]: """Handle a meeting end event. diff --git a/tests/hosting_msteams/test_meetings.py b/tests/hosting_msteams/test_meetings.py index cea09d000..8d4d47a22 100644 --- a/tests/hosting_msteams/test_meetings.py +++ b/tests/hosting_msteams/test_meetings.py @@ -16,18 +16,31 @@ ) if is_supported_version: - from microsoft_teams.api.models.meetings import MeetingDetails + from microsoft_teams.api.activities.event.meeting_end import MeetingEndEventValue + from microsoft_teams.api.activities.event.meeting_start import ( + MeetingStartEventValue, + ) from microsoft_agents.activity.teams import MeetingParticipantsEventDetails from microsoft_agents.hosting.msteams import TeamsAgentExtension -def _meeting_details_value() -> dict: +def _meeting_start_value() -> dict: return { - "id": "meeting-id", - "type": "scheduled", - "joinUrl": "https://example.com/meet", - "title": "Test Meeting", - "msGraphResourceId": "graph-id", + "Id": "meeting-id", + "MeetingType": "Scheduled", + "JoinUrl": "https://example.com/meet", + "Title": "Test Meeting", + "StartTime": "2026-10-02T20:48:16.7632115Z", + } + + +def _meeting_end_value() -> dict: + return { + "Id": "meeting-id", + "MeetingType": "Scheduled", + "JoinUrl": "https://example.com/meet", + "Title": "Test Meeting", + "EndTime": "2026-10-02T20:49:20.330067Z", } @@ -98,34 +111,44 @@ async def test_start_handler_parses_meeting_details(self): user_handler = AsyncMock() @self.ext.meetings.start() - async def handler(ctx, state, meeting: MeetingDetails): + async def handler(ctx, state, meeting: MeetingStartEventValue): await user_handler(ctx, state, meeting) route_handler = self.app._routes[0]["handler"] ctx = _make_context( ActivityTypes.event, name="application/vnd.microsoft.meetingStart", - value=_meeting_details_value(), + value=_meeting_start_value(), ) await route_handler(ctx, MagicMock()) - assert isinstance(user_handler.call_args[0][2], MeetingDetails) + meeting = user_handler.call_args[0][2] + assert isinstance(meeting, MeetingStartEventValue) + assert meeting.id == "meeting-id" + assert meeting.meeting_type == "Scheduled" + assert meeting.join_url == "https://example.com/meet" + assert meeting.title == "Test Meeting" @pytest.mark.asyncio async def test_end_handler_parses_meeting_details(self): user_handler = AsyncMock() @self.ext.meetings.end() - async def handler(ctx, state, meeting: MeetingDetails): + async def handler(ctx, state, meeting: MeetingEndEventValue): await user_handler(ctx, state, meeting) route_handler = self.app._routes[0]["handler"] ctx = _make_context( ActivityTypes.event, name="application/vnd.microsoft.meetingEnd", - value=_meeting_details_value(), + value=_meeting_end_value(), ) await route_handler(ctx, MagicMock()) - assert isinstance(user_handler.call_args[0][2], MeetingDetails) + meeting = user_handler.call_args[0][2] + assert isinstance(meeting, MeetingEndEventValue) + assert meeting.id == "meeting-id" + assert meeting.meeting_type == "Scheduled" + assert meeting.join_url == "https://example.com/meet" + assert meeting.title == "Test Meeting" class TestMeetingParticipants: From 724748838bbb01a05cea2a3882281dbcb927ebd5 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Fri, 2 Oct 2026 15:36:49 -0700 Subject: [PATCH 2/3] Updating API usage --- .../teams-api-usage-manifest.json | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-hosting-msteams/teams-api-usage-manifest.json b/libraries/microsoft-agents-hosting-msteams/teams-api-usage-manifest.json index f92dc5e0e..29594f8c1 100644 --- a/libraries/microsoft-agents-hosting-msteams/teams-api-usage-manifest.json +++ b/libraries/microsoft-agents-hosting-msteams/teams-api-usage-manifest.json @@ -109,7 +109,22 @@ "constructsOrValidates": true }, { - "upstreamSymbol": "microsoft_teams.api.models.meetings.MeetingDetails", + "upstreamSymbol": "microsoft_teams.api.activities.event.meeting_start.MeetingStartEventValue", + "usage": "parsed-model", + "exposure": "publicly-exposed", + "files": [ + "microsoft_agents/hosting/msteams/meeting/meeting.py", + "microsoft_agents/hosting/msteams/meeting/route_handlers.py" + ], + "propertiesRead": [], + "propertiesWritten": [], + "methodsCalled": [ + "model_validate" + ], + "constructsOrValidates": true + }, + { + "upstreamSymbol": "microsoft_teams.api.activities.event.meeting_end.MeetingEndEventValue", "usage": "parsed-model", "exposure": "publicly-exposed", "files": [ From d4d78498afbb5a59d8d71dcd6d719d117412d0a1 Mon Sep 17 00:00:00 2001 From: Rodrigo Brandao Date: Fri, 2 Oct 2026 16:07:07 -0700 Subject: [PATCH 3/3] Adding tests --- tests/hosting_msteams/test_meetings.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/hosting_msteams/test_meetings.py b/tests/hosting_msteams/test_meetings.py index 8d4d47a22..df2b160d2 100644 --- a/tests/hosting_msteams/test_meetings.py +++ b/tests/hosting_msteams/test_meetings.py @@ -3,6 +3,8 @@ """Tests for TeamsAgentExtension.meetings (meeting lifecycle events).""" +from datetime import datetime, timezone + import pytest from unittest.mock import AsyncMock, MagicMock @@ -127,6 +129,10 @@ async def handler(ctx, state, meeting: MeetingStartEventValue): assert meeting.meeting_type == "Scheduled" assert meeting.join_url == "https://example.com/meet" assert meeting.title == "Test Meeting" + assert isinstance(meeting.start_time, datetime) + assert meeting.start_time == datetime( + 2026, 10, 2, 20, 48, 16, 763211, tzinfo=timezone.utc + ) @pytest.mark.asyncio async def test_end_handler_parses_meeting_details(self): @@ -149,6 +155,10 @@ async def handler(ctx, state, meeting: MeetingEndEventValue): assert meeting.meeting_type == "Scheduled" assert meeting.join_url == "https://example.com/meet" assert meeting.title == "Test Meeting" + assert isinstance(meeting.end_time, datetime) + assert meeting.end_time == datetime( + 2026, 10, 2, 20, 49, 20, 330067, tzinfo=timezone.utc + ) class TestMeetingParticipants: