diff --git a/libraries/botbuilder-core/botbuilder/core/teams/teams_activity_handler.py b/libraries/botbuilder-core/botbuilder/core/teams/teams_activity_handler.py index 4fd6d4ee1..7832887dc 100644 --- a/libraries/botbuilder-core/botbuilder/core/teams/teams_activity_handler.py +++ b/libraries/botbuilder-core/botbuilder/core/teams/teams_activity_handler.py @@ -186,6 +186,22 @@ async def on_invoke_activity(self, turn_context: TurnContext) -> InvokeResponse: ) ) + if turn_context.activity.name == "config/fetch": + return self._create_invoke_response( + await self.on_teams_config_fetch( + turn_context, + turn_context.activity.value, + ) + ) + + if turn_context.activity.name == "config/submit": + return self._create_invoke_response( + await self.on_teams_config_submit( + turn_context, + turn_context.activity.value, + ) + ) + return await super().on_invoke_activity(turn_context) except _InvokeResponseException as invoke_exception: @@ -515,6 +531,32 @@ async def on_teams_tab_submit( # pylint: disable=unused-argument """ raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED) + async def on_teams_config_fetch( # pylint: disable=unused-argument + self, turn_context: TurnContext, config_data: any + ): + """ + Override this in a derived class to provide logic for when a config is fetched. + + :param turn_context: A context object for this turn. + :param config_data: The config fetch invoke request value payload. + + :returns: A Config Response for the request. + """ + raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED) + + async def on_teams_config_submit( # pylint: disable=unused-argument + self, turn_context: TurnContext, config_data: any + ): + """ + Override this in a derived class to provide logic for when a config is submitted. + + :param turn_context: A context object for this turn. + :param config_data: The config fetch invoke request value payload. + + :returns: A Config Response for the request. + """ + raise _InvokeResponseException(status_code=HTTPStatus.NOT_IMPLEMENTED) + async def on_conversation_update_activity(self, turn_context: TurnContext): """ Invoked when a conversation update activity is received from the channel. diff --git a/libraries/botbuilder-core/tests/teams/test_teams_activity_handler.py b/libraries/botbuilder-core/tests/teams/test_teams_activity_handler.py index af9bcd4ab..2ea62717b 100644 --- a/libraries/botbuilder-core/tests/teams/test_teams_activity_handler.py +++ b/libraries/botbuilder-core/tests/teams/test_teams_activity_handler.py @@ -315,6 +315,14 @@ async def on_teams_tab_submit( self.record.append("on_teams_tab_submit") return await super().on_teams_tab_submit(turn_context, tab_submit) + async def on_teams_config_fetch(self, turn_context: TurnContext, config_data: any): + self.record.append("on_teams_config_fetch") + return await super().on_teams_config_fetch(turn_context, config_data) + + async def on_teams_config_submit(self, turn_context: TurnContext, config_data: any): + self.record.append("on_teams_config_submit") + return await super().on_teams_config_submit(turn_context, config_data) + async def on_event_activity(self, turn_context: TurnContext): self.record.append("on_event_activity") return await super().on_event_activity(turn_context) @@ -1126,6 +1134,50 @@ async def test_on_teams_tab_submit(self): assert bot.record[0] == "on_invoke_activity" assert bot.record[1] == "on_teams_tab_submit" + async def test_on_teams_config_fetch(self): + # Arrange + activity = Activity( + type=ActivityTypes.invoke, + name="config/fetch", + value={ + "data": {"key": "value", "type": "config/fetch"}, + "context": {"theme": "default"}, + }, + ) + + turn_context = TurnContext(SimpleAdapter(), activity) + + # Act + bot = TestingTeamsActivityHandler() + await bot.on_turn(turn_context) + + # Assert + assert len(bot.record) == 2 + assert bot.record[0] == "on_invoke_activity" + assert bot.record[1] == "on_teams_config_fetch" + + async def test_on_teams_config_submit(self): + # Arrange + activity = Activity( + type=ActivityTypes.invoke, + name="config/submit", + value={ + "data": {"key": "value", "type": "config/submit"}, + "context": {"theme": "default"}, + }, + ) + + turn_context = TurnContext(SimpleAdapter(), activity) + + # Act + bot = TestingTeamsActivityHandler() + await bot.on_turn(turn_context) + + # Assert + assert len(bot.record) == 2 + assert bot.record[0] == "on_invoke_activity" + assert bot.record[1] == "on_teams_config_submit" + async def test_on_end_of_conversation_activity(self): activity = Activity(type=ActivityTypes.end_of_conversation) diff --git a/libraries/botbuilder-schema/botbuilder/schema/teams/__init__.py b/libraries/botbuilder-schema/botbuilder/schema/teams/__init__.py index 0bac60e96..8fb944b16 100644 --- a/libraries/botbuilder-schema/botbuilder/schema/teams/__init__.py +++ b/libraries/botbuilder-schema/botbuilder/schema/teams/__init__.py @@ -81,6 +81,10 @@ from ._models_py3 import TeamsMeetingMember from ._models_py3 import MeetingParticipantsEventDetails from ._models_py3 import ReadReceiptInfo +from ._models_py3 import BotConfigAuth +from ._models_py3 import ConfigAuthResponse +from ._models_py3 import ConfigResponse +from ._models_py3 import ConfigTaskResponse __all__ = [ "AppBasedLinkQuery", @@ -163,4 +167,8 @@ "TeamsMeetingMember", "MeetingParticipantsEventDetails", "ReadReceiptInfo", + "BotConfigAuth", + "ConfigAuthResponse", + "ConfigResponse", + "ConfigTaskResponse", ] diff --git a/libraries/botbuilder-schema/botbuilder/schema/teams/_models_py3.py b/libraries/botbuilder-schema/botbuilder/schema/teams/_models_py3.py index a507467cc..a1ab30d6c 100644 --- a/libraries/botbuilder-schema/botbuilder/schema/teams/_models_py3.py +++ b/libraries/botbuilder-schema/botbuilder/schema/teams/_models_py3.py @@ -2619,3 +2619,83 @@ def is_message_read_instance(self, compare_message_id): return ReadReceiptInfo.is_message_read( compare_message_id, self.last_read_message_id ) + + +class BotConfigAuth(Model): + """Specifies bot config auth, including type and suggestedActions. + + :param type: The type of bot config auth. + :type type: str + :param suggested_actions: The suggested actions of bot config auth. + :type suggested_actions: ~botframework.connector.models.SuggestedActions + """ + + _attribute_map = { + "type": {"key": "type", "type": "str"}, + "suggested_actions": {"key": "suggestedActions", "type": "SuggestedActions"}, + } + + def __init__(self, *, type: str = "auth", suggested_actions=None, **kwargs) -> None: + super(BotConfigAuth, self).__init__(**kwargs) + self.type = type + self.suggested_actions = suggested_actions + + +class ConfigResponseBase(Model): + """Specifies Invoke response base, including response type. + + :param response_type: Response type for invoke request + :type response_type: str + """ + + _attribute_map = { + "response_type": {"key": "responseType", "type": "str"}, + } + + def __init__(self, *, response_type: str = None, **kwargs) -> None: + super(ConfigResponseBase, self).__init__(**kwargs) + self.response_type = response_type + + +class ConfigResponse(ConfigResponseBase): + """Envelope for Config Response Payload. + + :param config: The response to the config message. Possible values: 'auth', 'task' + :type config: T + :param cache_info: Response cache info + :type cache_info: ~botframework.connector.teams.models.CacheInfo + """ + + _attribute_map = { + "config": {"key": "config", "type": "object"}, + "cache_info": {"key": "cacheInfo", "type": "CacheInfo"}, + } + + def __init__(self, *, config=None, cache_info=None, **kwargs) -> None: + super(ConfigResponse, self).__init__(response_type="config", **kwargs) + self.config = config + self.cache_info = cache_info + + +class ConfigTaskResponse(ConfigResponse): + """Envelope for Config Task Response. + + This class uses TaskModuleResponseBase as the type for the config parameter. + """ + + def __init__(self, *, config=None, **kwargs) -> None: + super(ConfigTaskResponse, self).__init__( + config=config or TaskModuleResponseBase(), **kwargs + ) + + +class ConfigAuthResponse(ConfigResponse): + """Envelope for Config Auth Response. + + This class uses BotConfigAuth as the type for the config parameter. + """ + + def __init__(self, *, config=None, **kwargs) -> None: + super(ConfigAuthResponse, self).__init__( + config=config or BotConfigAuth(), **kwargs + ) diff --git a/libraries/botbuilder-schema/tests/teams/test_bot_config_auth.py b/libraries/botbuilder-schema/tests/teams/test_bot_config_auth.py new file mode 100644 index 000000000..f6d771c4e --- /dev/null +++ b/libraries/botbuilder-schema/tests/teams/test_bot_config_auth.py @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import aiounittest +from botbuilder.schema.teams import BotConfigAuth + + +class TestBotConfigAuth(aiounittest.AsyncTestCase): + def test_bot_config_auth_inits_with_no_args(self): + bot_config_auth_response = BotConfigAuth() + + self.assertIsNotNone(bot_config_auth_response) + self.assertIsInstance(bot_config_auth_response, BotConfigAuth) + self.assertEqual("auth", bot_config_auth_response.type) diff --git a/libraries/botbuilder-schema/tests/teams/test_config_auth_response.py b/libraries/botbuilder-schema/tests/teams/test_config_auth_response.py new file mode 100644 index 000000000..54221399d --- /dev/null +++ b/libraries/botbuilder-schema/tests/teams/test_config_auth_response.py @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import aiounittest +from botbuilder.schema.teams import ConfigAuthResponse + + +class TestConfigAuthResponse(aiounittest.AsyncTestCase): + def test_config_auth_response_init_with_no_args(self): + config_auth_response = ConfigAuthResponse() + + self.assertIsNotNone(config_auth_response) + self.assertIsInstance(config_auth_response, ConfigAuthResponse) + self.assertEqual("config", config_auth_response.response_type) diff --git a/libraries/botbuilder-schema/tests/teams/test_config_response.py b/libraries/botbuilder-schema/tests/teams/test_config_response.py new file mode 100644 index 000000000..39d2ce0d5 --- /dev/null +++ b/libraries/botbuilder-schema/tests/teams/test_config_response.py @@ -0,0 +1,13 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import aiounittest +from botbuilder.schema.teams import ConfigResponse + + +class TestConfigResponse(aiounittest.AsyncTestCase): + def test_config_response_inits_with_no_args(self): + config_response = ConfigResponse() + + self.assertIsNotNone(config_response) + self.assertIsInstance(config_response, ConfigResponse) diff --git a/libraries/botbuilder-schema/tests/teams/test_config_task_response.py b/libraries/botbuilder-schema/tests/teams/test_config_task_response.py new file mode 100644 index 000000000..53126388d --- /dev/null +++ b/libraries/botbuilder-schema/tests/teams/test_config_task_response.py @@ -0,0 +1,14 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import aiounittest +from botbuilder.schema.teams import ConfigTaskResponse + + +class TestConfigTaskResponse(aiounittest.AsyncTestCase): + def test_config_task_response_init_with_no_args(self): + config_task_response = ConfigTaskResponse() + + self.assertIsNotNone(config_task_response) + self.assertIsInstance(config_task_response, ConfigTaskResponse) + self.assertEqual("config", config_task_response.response_type)