Skip to content

Commit f46a715

Browse files
authored
Merge pull request #467 from microsoft/josh/unitTests
Unit tests & tightening up teams activity handler object types
2 parents 5f3d11f + a61dbe9 commit f46a715

File tree

6 files changed

+840
-80
lines changed

6 files changed

+840
-80
lines changed

libraries/botbuilder-core/botbuilder/core/teams/teams_activity_handler.py

Lines changed: 62 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@
66
from botbuilder.core.turn_context import TurnContext
77
from botbuilder.core import ActivityHandler, InvokeResponse, BotFrameworkAdapter
88
from botbuilder.schema.teams import (
9+
AppBasedLinkQuery,
910
TeamInfo,
1011
ChannelInfo,
12+
FileConsentCardResponse,
1113
TeamsChannelData,
1214
TeamsChannelAccount,
15+
MessagingExtensionAction,
16+
MessagingExtensionQuery,
17+
O365ConnectorCardActionQuery,
18+
TaskModuleRequest,
1319
)
1420
from botframework.connector import Channels
1521

@@ -55,26 +61,28 @@ async def on_invoke_activity(self, turn_context: TurnContext):
5561

5662
if turn_context.activity.name == "fileConsent/invoke":
5763
return await self.on_teams_file_consent(
58-
turn_context, turn_context.activity.value
64+
turn_context, FileConsentCardResponse(**turn_context.activity.value)
5965
)
6066

6167
if turn_context.activity.name == "actionableMessage/executeAction":
6268
await self.on_teams_o365_connector_card_action(
63-
turn_context, turn_context.activity.value
69+
turn_context,
70+
O365ConnectorCardActionQuery(**turn_context.activity.value),
6471
)
6572
return self._create_invoke_response()
6673

6774
if turn_context.activity.name == "composeExtension/queryLink":
6875
return self._create_invoke_response(
6976
await self.on_teams_app_based_link_query(
70-
turn_context, turn_context.activity.value
77+
turn_context, AppBasedLinkQuery(**turn_context.activity.value)
7178
)
7279
)
7380

7481
if turn_context.activity.name == "composeExtension/query":
7582
return self._create_invoke_response(
7683
await self.on_teams_messaging_extension_query(
77-
turn_context, turn_context.activity.value
84+
turn_context,
85+
MessagingExtensionQuery(**turn_context.activity.value),
7886
)
7987
)
8088

@@ -88,21 +96,24 @@ async def on_invoke_activity(self, turn_context: TurnContext):
8896
if turn_context.activity.name == "composeExtension/submitAction":
8997
return self._create_invoke_response(
9098
await self.on_teams_messaging_extension_submit_action_dispatch(
91-
turn_context, turn_context.activity.value
99+
turn_context,
100+
MessagingExtensionAction(**turn_context.activity.value),
92101
)
93102
)
94103

95104
if turn_context.activity.name == "composeExtension/fetchTask":
96105
return self._create_invoke_response(
97106
await self.on_teams_messaging_extension_fetch_task(
98-
turn_context, turn_context.activity.value
107+
turn_context,
108+
MessagingExtensionAction(**turn_context.activity.value),
99109
)
100110
)
101111

102112
if turn_context.activity.name == "composeExtension/querySettingUrl":
103113
return self._create_invoke_response(
104114
await self.on_teams_messaging_extension_configuration_query_settings_url(
105-
turn_context, turn_context.activity.value
115+
turn_context,
116+
MessagingExtensionQuery(**turn_context.activity.value),
106117
)
107118
)
108119

@@ -121,14 +132,14 @@ async def on_invoke_activity(self, turn_context: TurnContext):
121132
if turn_context.activity.name == "task/fetch":
122133
return self._create_invoke_response(
123134
await self.on_teams_task_module_fetch(
124-
turn_context, turn_context.activity.value
135+
turn_context, TaskModuleRequest(**turn_context.activity.value)
125136
)
126137
)
127138

128139
if turn_context.activity.name == "task/submit":
129140
return self._create_invoke_response(
130141
await self.on_teams_task_module_submit(
131-
turn_context, turn_context.activity.value
142+
turn_context, TaskModuleRequest(**turn_context.activity.value)
132143
)
133144
)
134145

@@ -143,7 +154,9 @@ async def on_teams_signin_verify_state(self, turn_context: TurnContext):
143154
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
144155

145156
async def on_teams_file_consent(
146-
self, turn_context: TurnContext, file_consent_card_response
157+
self,
158+
turn_context: TurnContext,
159+
file_consent_card_response: FileConsentCardResponse,
147160
):
148161
if file_consent_card_response.action == "accept":
149162
await self.on_teams_file_consent_accept_activity(
@@ -163,27 +176,31 @@ async def on_teams_file_consent(
163176
)
164177

165178
async def on_teams_file_consent_accept_activity( # pylint: disable=unused-argument
166-
self, turn_context: TurnContext, file_consent_card_response
179+
self,
180+
turn_context: TurnContext,
181+
file_consent_card_response: FileConsentCardResponse,
167182
):
168183
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
169184

170185
async def on_teams_file_consent_decline_activity( # pylint: disable=unused-argument
171-
self, turn_context: TurnContext, file_consent_card_response
186+
self,
187+
turn_context: TurnContext,
188+
file_consent_card_response: FileConsentCardResponse,
172189
):
173190
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
174191

175192
async def on_teams_o365_connector_card_action( # pylint: disable=unused-argument
176-
self, turn_context: TurnContext, query
193+
self, turn_context: TurnContext, query: O365ConnectorCardActionQuery
177194
):
178195
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
179196

180197
async def on_teams_app_based_link_query( # pylint: disable=unused-argument
181-
self, turn_context: TurnContext, query
198+
self, turn_context: TurnContext, query: AppBasedLinkQuery
182199
):
183200
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
184201

185202
async def on_teams_messaging_extension_query( # pylint: disable=unused-argument
186-
self, turn_context: TurnContext, query
203+
self, turn_context: TurnContext, query: MessagingExtensionQuery
187204
):
188205
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
189206

@@ -193,9 +210,9 @@ async def on_teams_messaging_extension_select_item( # pylint: disable=unused-ar
193210
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
194211

195212
async def on_teams_messaging_extension_submit_action_dispatch(
196-
self, turn_context: TurnContext, action
213+
self, turn_context: TurnContext, action: MessagingExtensionAction
197214
):
198-
if not action:
215+
if not action.bot_message_preview_action:
199216
return await self.on_teams_messaging_extension_submit_action_activity(
200217
turn_context, action
201218
)
@@ -226,17 +243,17 @@ async def on_teams_messaging_extension_bot_message_send_activity( # pylint: dis
226243
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
227244

228245
async def on_teams_messaging_extension_submit_action_activity( # pylint: disable=unused-argument
229-
self, turn_context: TurnContext, action
246+
self, turn_context: TurnContext, action: MessagingExtensionAction
230247
):
231248
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
232249

233250
async def on_teams_messaging_extension_fetch_task( # pylint: disable=unused-argument
234-
self, turn_context: TurnContext, task_module_request
251+
self, turn_context: TurnContext, action: MessagingExtensionAction
235252
):
236253
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
237254

238255
async def on_teams_messaging_extension_configuration_query_settings_url( # pylint: disable=unused-argument
239-
self, turn_context: TurnContext, query
256+
self, turn_context: TurnContext, query: MessagingExtensionQuery
240257
):
241258
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
242259

@@ -251,19 +268,19 @@ async def on_teams_messaging_extension_card_button_clicked( # pylint: disable=u
251268
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
252269

253270
async def on_teams_task_module_fetch( # pylint: disable=unused-argument
254-
self, turn_context: TurnContext, task_module_request
271+
self, turn_context: TurnContext, task_module_request: TaskModuleRequest
255272
):
256273
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
257274

258275
async def on_teams_task_module_submit( # pylint: disable=unused-argument
259-
self, turn_context: TurnContext, task_module_request
276+
self, turn_context: TurnContext, task_module_request: TaskModuleRequest
260277
):
261278
raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED)
262279

263280
async def on_conversation_update_activity(self, turn_context: TurnContext):
281+
264282
if turn_context.activity.channel_id == Channels.ms_teams:
265283
channel_data = TeamsChannelData(**turn_context.activity.channel_data)
266-
267284
if turn_context.activity.members_added:
268285
return await self.on_teams_members_added_dispatch_activity(
269286
turn_context.activity.members_added, channel_data.team, turn_context
@@ -279,7 +296,9 @@ async def on_conversation_update_activity(self, turn_context: TurnContext):
279296
if channel_data:
280297
if channel_data.event_type == "channelCreated":
281298
return await self.on_teams_channel_created_activity(
282-
channel_data.channel, channel_data.team, turn_context
299+
ChannelInfo(**channel_data.channel),
300+
channel_data.team,
301+
turn_context,
283302
)
284303
if channel_data.event_type == "channelDeleted":
285304
return await self.on_teams_channel_deleted_activity(
@@ -337,17 +356,26 @@ async def on_teams_members_added_dispatch_activity( # pylint: disable=unused-ar
337356
338357
return await self.on_teams_members_added_activity(teams_members_added, team_info, turn_context)
339358
"""
359+
team_accounts_added = []
340360
for member in members_added:
341361
new_account_json = member.serialize()
342-
del new_account_json["additional_properties"]
362+
if "additional_properties" in new_account_json:
363+
del new_account_json["additional_properties"]
343364
member = TeamsChannelAccount(**new_account_json)
344-
return await self.on_teams_members_added_activity(members_added, turn_context)
365+
team_accounts_added.append(member)
366+
return await self.on_teams_members_added_activity(
367+
team_accounts_added, turn_context
368+
)
345369

346370
async def on_teams_members_added_activity(
347371
self, teams_members_added: [TeamsChannelAccount], turn_context: TurnContext
348372
):
349-
teams_members_added = [ChannelAccount(member) for member in teams_members_added]
350-
return super().on_members_added_activity(teams_members_added, turn_context)
373+
teams_members_added = [
374+
ChannelAccount(**member.serialize()) for member in teams_members_added
375+
]
376+
return await super().on_members_added_activity(
377+
teams_members_added, turn_context
378+
)
351379

352380
async def on_teams_members_removed_dispatch_activity( # pylint: disable=unused-argument
353381
self,
@@ -358,7 +386,8 @@ async def on_teams_members_removed_dispatch_activity( # pylint: disable=unused-
358386
teams_members_removed = []
359387
for member in members_removed:
360388
new_account_json = member.serialize()
361-
del new_account_json["additional_properties"]
389+
if "additional_properties" in new_account_json:
390+
del new_account_json["additional_properties"]
362391
teams_members_removed.append(TeamsChannelAccount(**new_account_json))
363392

364393
return await self.on_teams_members_removed_activity(
@@ -368,8 +397,10 @@ async def on_teams_members_removed_dispatch_activity( # pylint: disable=unused-
368397
async def on_teams_members_removed_activity(
369398
self, teams_members_removed: [TeamsChannelAccount], turn_context: TurnContext
370399
):
371-
members_removed = [ChannelAccount(member) for member in teams_members_removed]
372-
return super().on_members_removed_activity(members_removed, turn_context)
400+
members_removed = [
401+
ChannelAccount(**member.serialize()) for member in teams_members_removed
402+
]
403+
return await super().on_members_removed_activity(members_removed, turn_context)
373404

374405
async def on_teams_channel_deleted_activity( # pylint: disable=unused-argument
375406
self, channel_info: ChannelInfo, team_info: TeamInfo, turn_context: TurnContext
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Copyright (c) Microsoft Corporation. All rights reserved.
2+
# Licensed under the MIT License.
3+
4+
import unittest
5+
from typing import List
6+
from botbuilder.core import BotAdapter, TurnContext
7+
from botbuilder.schema import Activity, ConversationReference, ResourceResponse
8+
9+
10+
class SimpleAdapter(BotAdapter):
11+
# pylint: disable=unused-argument
12+
13+
def __init__(self, call_on_send=None, call_on_update=None, call_on_delete=None):
14+
super(SimpleAdapter, self).__init__()
15+
self.test_aux = unittest.TestCase("__init__")
16+
self._call_on_send = call_on_send
17+
self._call_on_update = call_on_update
18+
self._call_on_delete = call_on_delete
19+
20+
async def delete_activity(
21+
self, context: TurnContext, reference: ConversationReference
22+
):
23+
self.test_aux.assertIsNotNone(
24+
reference, "SimpleAdapter.delete_activity: missing reference"
25+
)
26+
if self._call_on_delete is not None:
27+
self._call_on_delete(reference)
28+
29+
async def send_activities(
30+
self, context: TurnContext, activities: List[Activity]
31+
) -> List[ResourceResponse]:
32+
self.test_aux.assertIsNotNone(
33+
activities, "SimpleAdapter.delete_activity: missing reference"
34+
)
35+
self.test_aux.assertTrue(
36+
len(activities) > 0,
37+
"SimpleAdapter.send_activities: empty activities array.",
38+
)
39+
40+
if self._call_on_send is not None:
41+
self._call_on_send(activities)
42+
responses = []
43+
44+
for activity in activities:
45+
responses.append(ResourceResponse(id=activity.id))
46+
47+
return responses
48+
49+
async def update_activity(self, context: TurnContext, activity: Activity):
50+
self.test_aux.assertIsNotNone(
51+
activity, "SimpleAdapter.update_activity: missing activity"
52+
)
53+
if self._call_on_update is not None:
54+
self._call_on_update(activity)
55+
56+
return ResourceResponse(activity.id)
57+
58+
async def process_request(self, activity, handler):
59+
context = TurnContext(self, activity)
60+
return self.run_pipeline(context, handler)

0 commit comments

Comments
 (0)