diff --git a/libraries/botbuilder-core/botbuilder/core/skills/__init__.py b/libraries/botbuilder-core/botbuilder/core/skills/__init__.py index ce949b12a..0437ff48e 100644 --- a/libraries/botbuilder-core/botbuilder/core/skills/__init__.py +++ b/libraries/botbuilder-core/botbuilder/core/skills/__init__.py @@ -8,6 +8,7 @@ from .bot_framework_skill import BotFrameworkSkill from .bot_framework_client import BotFrameworkClient from .conversation_id_factory import ConversationIdFactoryBase +from .skill_conversation_id_factory import SkillConversationIdFactory from .skill_handler import SkillHandler from .skill_conversation_id_factory_options import SkillConversationIdFactoryOptions from .skill_conversation_reference import SkillConversationReference @@ -16,6 +17,7 @@ "BotFrameworkSkill", "BotFrameworkClient", "ConversationIdFactoryBase", + "SkillConversationIdFactory", "SkillConversationIdFactoryOptions", "SkillConversationReference", "SkillHandler", diff --git a/libraries/botbuilder-core/botbuilder/core/skills/conversation_id_factory.py b/libraries/botbuilder-core/botbuilder/core/skills/conversation_id_factory.py index bb00c1ac7..5cb986bfb 100644 --- a/libraries/botbuilder-core/botbuilder/core/skills/conversation_id_factory.py +++ b/libraries/botbuilder-core/botbuilder/core/skills/conversation_id_factory.py @@ -1,7 +1,7 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -from abc import ABC, abstractmethod +from abc import ABC from typing import Union from botbuilder.schema import ConversationReference from .skill_conversation_id_factory_options import SkillConversationIdFactoryOptions @@ -17,7 +17,6 @@ class ConversationIdFactoryBase(ABC): SkillConversationReferences and deletion. """ - @abstractmethod async def create_skill_conversation_id( self, options_or_conversation_reference: Union[ @@ -41,23 +40,32 @@ async def create_skill_conversation_id( """ raise NotImplementedError() - @abstractmethod async def get_conversation_reference( self, skill_conversation_id: str - ) -> Union[SkillConversationReference, ConversationReference]: + ) -> ConversationReference: + """ + [DEPRECATED] Method is deprecated, please use get_skill_conversation_reference() instead. + + Retrieves a :class:`ConversationReference` using a conversation id passed in. + + :param skill_conversation_id: The conversation id for which to retrieve the :class:`ConversationReference`. + :type skill_conversation_id: str + :returns: `ConversationReference` for the specified ID. + """ + raise NotImplementedError() + + async def get_skill_conversation_reference( + self, skill_conversation_id: str + ) -> SkillConversationReference: """ Retrieves a :class:`SkillConversationReference` using a conversation id passed in. :param skill_conversation_id: The conversation id for which to retrieve the :class:`SkillConversationReference`. :type skill_conversation_id: str - - .. note:: - SkillConversationReference is the preferred return type, while the :class:`SkillConversationReference` - type is provided for backwards compatability. + :returns: `SkillConversationReference` for the specified ID. """ raise NotImplementedError() - @abstractmethod async def delete_conversation_reference(self, skill_conversation_id: str): """ Removes any reference to objects keyed on the conversation id passed in. diff --git a/libraries/botbuilder-core/botbuilder/core/skills/skill_conversation_id_factory.py b/libraries/botbuilder-core/botbuilder/core/skills/skill_conversation_id_factory.py new file mode 100644 index 000000000..3fd322cad --- /dev/null +++ b/libraries/botbuilder-core/botbuilder/core/skills/skill_conversation_id_factory.py @@ -0,0 +1,86 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from uuid import uuid4 as uuid +from botbuilder.core import TurnContext, Storage +from .conversation_id_factory import ConversationIdFactoryBase +from .skill_conversation_id_factory_options import SkillConversationIdFactoryOptions +from .skill_conversation_reference import SkillConversationReference +from .skill_conversation_reference import ConversationReference + + +class SkillConversationIdFactory(ConversationIdFactoryBase): + def __init__(self, storage: Storage): + if not storage: + raise TypeError("storage can't be None") + + self._storage = storage + + async def create_skill_conversation_id( # pylint: disable=arguments-differ + self, options: SkillConversationIdFactoryOptions + ) -> str: + """ + Creates a new `SkillConversationReference`. + + :param options: Creation options to use when creating the `SkillConversationReference`. + :type options: :class:`botbuilder.core.skills.SkillConversationIdFactoryOptions` + :return: ID of the created `SkillConversationReference`. + """ + + if not options: + raise TypeError("options can't be None") + + conversation_reference = TurnContext.get_conversation_reference( + options.activity + ) + + skill_conversation_id = str(uuid()) + + # Create the SkillConversationReference instance. + skill_conversation_reference = SkillConversationReference( + conversation_reference=conversation_reference, + oauth_scope=options.from_bot_oauth_scope, + ) + + # Store the SkillConversationReference using the skill_conversation_id as a key. + skill_conversation_info = {skill_conversation_id: skill_conversation_reference} + + await self._storage.write(skill_conversation_info) + + # Return the generated skill_conversation_id (that will be also used as the conversation ID to call the skill). + return skill_conversation_id + + async def get_conversation_reference( + self, skill_conversation_id: str + ) -> ConversationReference: + return await super().get_conversation_reference(skill_conversation_id) + + async def get_skill_conversation_reference( + self, skill_conversation_id: str + ) -> SkillConversationReference: + """ + Retrieve a `SkillConversationReference` with the specified ID. + + :param skill_conversation_id: The ID of the `SkillConversationReference` to retrieve. + :type skill_conversation_id: str + :return: `SkillConversationReference` for the specified ID; None if not found. + """ + + if not skill_conversation_id: + raise TypeError("skill_conversation_id can't be None") + + # Get the SkillConversationReference from storage for the given skill_conversation_id. + skill_conversation_reference = await self._storage.read([skill_conversation_id]) + + return skill_conversation_reference.get(skill_conversation_id) + + async def delete_conversation_reference(self, skill_conversation_id: str): + """ + Deletes the `SkillConversationReference` with the specified ID. + + :param skill_conversation_id: The ID of the `SkillConversationReference` to be deleted. + :type skill_conversation_id: str + """ + + # Delete the SkillConversationReference from storage. + await self._storage.delete([skill_conversation_id]) diff --git a/libraries/botbuilder-core/botbuilder/core/skills/skill_handler.py b/libraries/botbuilder-core/botbuilder/core/skills/skill_handler.py index be417b046..96c45d3d1 100644 --- a/libraries/botbuilder-core/botbuilder/core/skills/skill_handler.py +++ b/libraries/botbuilder-core/botbuilder/core/skills/skill_handler.py @@ -2,6 +2,7 @@ # Licensed under the MIT License. from uuid import uuid4 +from logging import Logger, getLogger from botbuilder.core import Bot, BotAdapter, ChannelServiceHandler, TurnContext from botbuilder.schema import ( @@ -37,7 +38,7 @@ def __init__( credential_provider: CredentialProvider, auth_configuration: AuthenticationConfiguration, channel_provider: ChannelProvider = None, - logger: object = None, + logger: Logger = None, ): super().__init__(credential_provider, auth_configuration, channel_provider) @@ -51,7 +52,7 @@ def __init__( self._adapter = adapter self._bot = bot self._conversation_id_factory = conversation_id_factory - self._logger = logger + self._logger = logger or getLogger() async def on_send_to_conversation( self, claims_identity: ClaimsIdentity, conversation_id: str, activity: Activity, @@ -181,20 +182,26 @@ async def callback(turn_context: TurnContext): async def _get_skill_conversation_reference( self, conversation_id: str ) -> SkillConversationReference: - # Get the SkillsConversationReference - conversation_reference_result = await self._conversation_id_factory.get_conversation_reference( - conversation_id - ) + try: + skill_conversation_reference = await self._conversation_id_factory.get_skill_conversation_reference( + conversation_id + ) + except NotImplementedError: + self._logger.warning( + "Got NotImplementedError when trying to call get_skill_conversation_reference() " + "on the SkillConversationIdFactory, attempting to use deprecated " + "get_conversation_reference() method instead." + ) + + # Attempt to get SkillConversationReference using deprecated method. + # this catch should be removed once we remove the deprecated method. + # We need to use the deprecated method for backward compatibility. + conversation_reference = await self._conversation_id_factory.get_conversation_reference( + conversation_id + ) - # ConversationIdFactory can return either a SkillConversationReference (the newer way), - # or a ConversationReference (the old way, but still here for compatibility). If a - # ConversationReference is returned, build a new SkillConversationReference to simplify - # the remainder of this method. - if isinstance(conversation_reference_result, SkillConversationReference): - skill_conversation_reference: SkillConversationReference = conversation_reference_result - else: skill_conversation_reference: SkillConversationReference = SkillConversationReference( - conversation_reference=conversation_reference_result, + conversation_reference=conversation_reference, oauth_scope=( GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE if self._channel_provider and self._channel_provider.is_government() diff --git a/libraries/botbuilder-core/tests/skills/test_skill_conversation_id_factory.py b/libraries/botbuilder-core/tests/skills/test_skill_conversation_id_factory.py new file mode 100644 index 000000000..62b954bbd --- /dev/null +++ b/libraries/botbuilder-core/tests/skills/test_skill_conversation_id_factory.py @@ -0,0 +1,116 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from uuid import uuid4 as uuid +from aiounittest import AsyncTestCase +from botbuilder.core import MemoryStorage +from botbuilder.schema import ( + Activity, + ConversationAccount, + ConversationReference, +) +from botbuilder.core.skills import ( + BotFrameworkSkill, + SkillConversationIdFactory, + SkillConversationIdFactoryOptions, +) + + +class SkillConversationIdFactoryForTest(AsyncTestCase): + SERVICE_URL = "http://testbot.com/api/messages" + SKILL_ID = "skill" + + @classmethod + def setUpClass(cls): + cls._skill_conversation_id_factory = SkillConversationIdFactory(MemoryStorage()) + cls._application_id = str(uuid()) + cls._bot_id = str(uuid()) + + async def test_skill_conversation_id_factory_happy_path(self): + conversation_reference = self._build_conversation_reference() + + # Create skill conversation + skill_conversation_id = await self._skill_conversation_id_factory.create_skill_conversation_id( + options=SkillConversationIdFactoryOptions( + activity=self._build_message_activity(conversation_reference), + bot_framework_skill=self._build_bot_framework_skill(), + from_bot_id=self._bot_id, + from_bot_oauth_scope=self._bot_id, + ) + ) + + assert ( + skill_conversation_id and skill_conversation_id.strip() + ), "Expected a valid skill conversation ID to be created" + + # Retrieve skill conversation + retrieved_conversation_reference = await self._skill_conversation_id_factory.get_skill_conversation_reference( + skill_conversation_id + ) + + # Delete + await self._skill_conversation_id_factory.delete_conversation_reference( + skill_conversation_id + ) + + # Retrieve again + deleted_conversation_reference = await self._skill_conversation_id_factory.get_skill_conversation_reference( + skill_conversation_id + ) + + self.assertIsNotNone(retrieved_conversation_reference) + self.assertIsNotNone(retrieved_conversation_reference.conversation_reference) + self.assertEqual( + conversation_reference, + retrieved_conversation_reference.conversation_reference, + ) + self.assertIsNone(deleted_conversation_reference) + + async def test_id_is_unique_each_time(self): + conversation_reference = self._build_conversation_reference() + + # Create skill conversation + first_id = await self._skill_conversation_id_factory.create_skill_conversation_id( + options=SkillConversationIdFactoryOptions( + activity=self._build_message_activity(conversation_reference), + bot_framework_skill=self._build_bot_framework_skill(), + from_bot_id=self._bot_id, + from_bot_oauth_scope=self._bot_id, + ) + ) + + second_id = await self._skill_conversation_id_factory.create_skill_conversation_id( + options=SkillConversationIdFactoryOptions( + activity=self._build_message_activity(conversation_reference), + bot_framework_skill=self._build_bot_framework_skill(), + from_bot_id=self._bot_id, + from_bot_oauth_scope=self._bot_id, + ) + ) + + # Ensure that we get a different conversation_id each time we call create_skill_conversation_id + self.assertNotEqual(first_id, second_id) + + def _build_conversation_reference(self) -> ConversationReference: + return ConversationReference( + conversation=ConversationAccount(id=str(uuid())), + service_url=self.SERVICE_URL, + ) + + def _build_message_activity( + self, conversation_reference: ConversationReference + ) -> Activity: + if not conversation_reference: + raise TypeError(str(conversation_reference)) + + activity = Activity.create_message_activity() + activity.apply_conversation_reference(conversation_reference) + + return activity + + def _build_bot_framework_skill(self) -> BotFrameworkSkill: + return BotFrameworkSkill( + app_id=self._application_id, + id=self.SKILL_ID, + skill_endpoint=self.SERVICE_URL, + ) diff --git a/libraries/botbuilder-core/tests/skills/test_skill_handler.py b/libraries/botbuilder-core/tests/skills/test_skill_handler.py index 73997cdff..7b657620b 100644 --- a/libraries/botbuilder-core/tests/skills/test_skill_handler.py +++ b/libraries/botbuilder-core/tests/skills/test_skill_handler.py @@ -1,3 +1,6 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + import hashlib import json from datetime import datetime @@ -18,7 +21,13 @@ BotActionNotImplementedError, conversation_reference_extension, ) -from botbuilder.core.skills import ConversationIdFactoryBase, SkillHandler +from botbuilder.core.skills import ( + ConversationIdFactoryBase, + SkillHandler, + SkillConversationReference, + SkillConversationIdFactoryOptions, + BotFrameworkSkill, +) from botbuilder.schema import ( Activity, ActivityTypes, @@ -36,7 +45,44 @@ ) -class ConversationIdFactoryForTest(ConversationIdFactoryBase): +class ConversationIdFactoryForTest( + ConversationIdFactoryBase +): # pylint: disable=abstract-method + def __init__(self): + self._conversation_refs: Dict[str, str] = {} + + async def create_skill_conversation_id( # pylint: disable=W0221 + self, options: SkillConversationIdFactoryOptions + ) -> str: + conversation_reference = TurnContext.get_conversation_reference( + options.activity + ) + + key = hashlib.md5( + f"{conversation_reference.conversation.id}{conversation_reference.service_url}".encode() + ).hexdigest() + + skill_conversation_reference = SkillConversationReference( + conversation_reference=conversation_reference, + oauth_scope=options.from_bot_oauth_scope, + ) + + self._conversation_refs[key] = skill_conversation_reference + + return key + + async def get_skill_conversation_reference( + self, skill_conversation_id: str + ) -> SkillConversationReference: + return self._conversation_refs[skill_conversation_id] + + async def delete_conversation_reference(self, skill_conversation_id: str): + pass + + +class LegacyConversationIdFactoryForTest( + ConversationIdFactoryBase +): # pylint: disable=abstract-method def __init__(self): self._conversation_refs: Dict[str, str] = {} @@ -187,8 +233,23 @@ def setUpClass(cls): conversation=ConversationAccount(id=str(uuid4())), service_url="http://testbot.com/api/messages", ) + activity = Activity.create_message_activity() + activity.apply_conversation_reference(cls._conversation_reference) + skill = BotFrameworkSkill( + app_id=cls.skill_id, + id="skill", + skill_endpoint="http://testbot.com/api/messages", + ) + cls._options = SkillConversationIdFactoryOptions( + from_bot_oauth_scope=cls.bot_id, + from_bot_id=cls.bot_id, + activity=activity, + bot_framework_skill=skill, + ) - def create_skill_handler_for_testing(self, adapter) -> SkillHandlerInstanceForTests: + def create_skill_handler_for_testing( + self, adapter, factory: ConversationIdFactoryBase = None + ) -> SkillHandlerInstanceForTests: mock_bot = Mock() mock_bot.on_turn = MagicMock(return_value=Future()) mock_bot.on_turn.return_value.set_result(Mock()) @@ -196,15 +257,61 @@ def create_skill_handler_for_testing(self, adapter) -> SkillHandlerInstanceForTe return SkillHandlerInstanceForTests( adapter, mock_bot, - self._test_id_factory, + (factory or self._test_id_factory), Mock(), AuthenticationConfiguration(), ) + async def test_legacy_conversation_id_factory(self): + mock_adapter = Mock() + + legacy_factory = LegacyConversationIdFactoryForTest() + conversation_reference = ConversationReference( + conversation=ConversationAccount(id=str(uuid4())), + service_url="http://testbot.com/api/messages", + ) + + conversation_id = await legacy_factory.create_skill_conversation_id( + conversation_reference + ) + + async def continue_conversation( + reference: ConversationReference, + callback: Callable, + bot_id: str = None, + claims_identity: ClaimsIdentity = None, + audience: str = None, + ): # pylint: disable=unused-argument + # Invoke the callback created by the handler so we can assert the rest of the execution. + turn_context = TurnContext( + mock_adapter, + conversation_reference_extension.get_continuation_activity( + conversation_reference + ), + ) + await callback(turn_context) + + async def send_activities( + context: TurnContext, activities: List[Activity] + ): # pylint: disable=unused-argument + return [ResourceResponse(id="resourceId")] + + mock_adapter.continue_conversation = continue_conversation + mock_adapter.send_activities = send_activities + + activity = Activity.create_message_activity() + activity.apply_conversation_reference(conversation_reference) + + sut = self.create_skill_handler_for_testing(mock_adapter, legacy_factory) + await sut.test_on_send_to_conversation( + self._claims_identity, conversation_id, activity + ) + async def test_on_send_to_conversation(self): - self._conversation_id = await self._test_id_factory.create_skill_conversation_id( - self._conversation_reference + conversation_id = await self._test_id_factory.create_skill_conversation_id( + self._options ) + # python 3.7 doesn't support AsyncMock, change this when min ver is 3.8 send_activities_called = False @@ -260,7 +367,7 @@ async def send_activities( sut = self.create_skill_handler_for_testing(mock_adapter) resource_response = await sut.test_on_send_to_conversation( - self._claims_identity, self._conversation_id, activity + self._claims_identity, conversation_id, activity ) if activity_type == ActivityTypes.message: @@ -268,8 +375,8 @@ async def send_activities( assert resource_response.id == "resourceId" async def test_forwarding_on_send_to_conversation(self): - self._conversation_id = await self._test_id_factory.create_skill_conversation_id( - self._conversation_reference + conversation_id = await self._test_id_factory.create_skill_conversation_id( + self._options ) resource_response_id = "rId" @@ -298,15 +405,15 @@ async def side_effect( assert not activity.caller_id response = await sut.test_on_send_to_conversation( - self._claims_identity, self._conversation_id, activity + self._claims_identity, conversation_id, activity ) assert response.id is resource_response_id async def test_on_reply_to_activity(self): resource_response_id = "resourceId" - self._conversation_id = await self._test_id_factory.create_skill_conversation_id( - self._conversation_reference + conversation_id = await self._test_id_factory.create_skill_conversation_id( + self._options ) types_to_test = [ @@ -334,7 +441,7 @@ async def test_on_reply_to_activity(self): ) resource_response = await sut.test_on_reply_to_activity( - self._claims_identity, self._conversation_id, activity_id, activity + self._claims_identity, conversation_id, activity_id, activity ) # continue_conversation validation @@ -372,8 +479,8 @@ async def test_on_reply_to_activity(self): mock_adapter.send_activities.assert_not_called() async def test_on_update_activity(self): - self._conversation_id = await self._test_id_factory.create_skill_conversation_id( - self._conversation_reference + conversation_id = await self._test_id_factory.create_skill_conversation_id( + self._options ) resource_response_id = "resourceId" called_continue = False @@ -424,7 +531,7 @@ async def update_activity( sut = self.create_skill_handler_for_testing(mock_adapter) resource_response = await sut.test_on_update_activity( - self._claims_identity, self._conversation_id, activity_id, activity + self._claims_identity, conversation_id, activity_id, activity ) assert called_continue @@ -432,8 +539,8 @@ async def update_activity( assert resource_response, resource_response_id async def test_on_delete_activity(self): - self._conversation_id = await self._test_id_factory.create_skill_conversation_id( - self._conversation_reference + conversation_id = await self._test_id_factory.create_skill_conversation_id( + self._options ) resource_response_id = "resourceId" @@ -479,14 +586,14 @@ async def delete_activity( sut = self.create_skill_handler_for_testing(mock_adapter) await sut.test_on_delete_activity( - self._claims_identity, self._conversation_id, activity_id + self._claims_identity, conversation_id, activity_id ) assert called_continue assert called_delete async def test_on_get_activity_members(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() @@ -495,7 +602,7 @@ async def test_on_get_activity_members(self): with self.assertRaises(BotActionNotImplementedError): await sut.test_on_get_activity_members( - self._claims_identity, self._conversation_id, activity_id + self._claims_identity, conversation_id, activity_id ) async def test_on_create_conversation(self): @@ -510,19 +617,17 @@ async def test_on_create_conversation(self): ) async def test_on_get_conversations(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() sut = self.create_skill_handler_for_testing(mock_adapter) with self.assertRaises(BotActionNotImplementedError): - await sut.test_on_get_conversations( - self._claims_identity, self._conversation_id - ) + await sut.test_on_get_conversations(self._claims_identity, conversation_id) async def test_on_get_conversation_members(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() @@ -530,11 +635,11 @@ async def test_on_get_conversation_members(self): with self.assertRaises(BotActionNotImplementedError): await sut.test_on_get_conversation_members( - self._claims_identity, self._conversation_id + self._claims_identity, conversation_id ) async def test_on_get_conversation_paged_members(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() @@ -542,11 +647,11 @@ async def test_on_get_conversation_paged_members(self): with self.assertRaises(BotActionNotImplementedError): await sut.test_on_get_conversation_paged_members( - self._claims_identity, self._conversation_id + self._claims_identity, conversation_id ) async def test_on_delete_conversation_member(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() @@ -555,11 +660,11 @@ async def test_on_delete_conversation_member(self): with self.assertRaises(BotActionNotImplementedError): await sut.test_on_delete_conversation_member( - self._claims_identity, self._conversation_id, member_id + self._claims_identity, conversation_id, member_id ) async def test_on_send_conversation_history(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() @@ -568,11 +673,11 @@ async def test_on_send_conversation_history(self): with self.assertRaises(BotActionNotImplementedError): await sut.test_on_send_conversation_history( - self._claims_identity, self._conversation_id, transcript + self._claims_identity, conversation_id, transcript ) async def test_on_upload_attachment(self): - self._conversation_id = "" + conversation_id = "" mock_adapter = Mock() @@ -581,7 +686,7 @@ async def test_on_upload_attachment(self): with self.assertRaises(BotActionNotImplementedError): await sut.test_on_upload_attachment( - self._claims_identity, self._conversation_id, attachment_data + self._claims_identity, conversation_id, attachment_data ) async def test_event_activity(self): @@ -601,8 +706,8 @@ async def test_eoc_activity(self): ) async def __activity_callback_test(self, activity: Activity): - self._conversation_id = await self._test_id_factory.create_skill_conversation_id( - self._conversation_reference + conversation_id = await self._test_id_factory.create_skill_conversation_id( + self._options ) mock_adapter = Mock() @@ -617,7 +722,7 @@ async def __activity_callback_test(self, activity: Activity): TurnContext.apply_conversation_reference(activity, self._conversation_reference) await sut.test_on_reply_to_activity( - self._claims_identity, self._conversation_id, activity_id, activity + self._claims_identity, conversation_id, activity_id, activity ) args, kwargs = mock_adapter.continue_conversation.call_args_list[0] diff --git a/libraries/botbuilder-dialogs/tests/test_skill_dialog.py b/libraries/botbuilder-dialogs/tests/test_skill_dialog.py index 91b6dfcba..9bb695a62 100644 --- a/libraries/botbuilder-dialogs/tests/test_skill_dialog.py +++ b/libraries/botbuilder-dialogs/tests/test_skill_dialog.py @@ -43,7 +43,9 @@ ) -class SimpleConversationIdFactory(ConversationIdFactoryBase): +class SimpleConversationIdFactory( + ConversationIdFactoryBase +): # pylint: disable=abstract-method def __init__(self): self.conversation_refs = {} self.create_count = 0 @@ -68,9 +70,9 @@ async def create_skill_conversation_id( ) return key - async def get_conversation_reference( + async def get_skill_conversation_reference( self, skill_conversation_id: str - ) -> Union[SkillConversationReference, ConversationReference]: + ) -> SkillConversationReference: return self.conversation_refs[skill_conversation_id] async def delete_conversation_reference(self, skill_conversation_id: str): diff --git a/libraries/botbuilder-integration-aiohttp/tests/skills/test_skill_http_client.py b/libraries/botbuilder-integration-aiohttp/tests/skills/test_skill_http_client.py index df889cc82..b3db490b7 100644 --- a/libraries/botbuilder-integration-aiohttp/tests/skills/test_skill_http_client.py +++ b/libraries/botbuilder-integration-aiohttp/tests/skills/test_skill_http_client.py @@ -49,7 +49,7 @@ async def create_skill_conversation_id( ) return key - async def get_conversation_reference( + async def get_skill_conversation_reference( self, skill_conversation_id: str ) -> SkillConversationReference: return self._conversation_refs[skill_conversation_id]