Example #1
0
    async def test_trace_test(self):
        activity = Activity(
            type=ActivityTypes.message,
            text="how do I clean the stove?",
            conversation=ConversationAccount(),
            recipient=ChannelAccount(),
            from_property=ChannelAccount(),
        )

        response_json = QnaApplicationTest._get_json_for_file("ReturnsAnswer.json")
        qna = QnAMaker(QnaApplicationTest.tests_endpoint)

        context = TestContext(activity)

        with patch(
            "aiohttp.ClientSession.post",
            return_value=aiounittest.futurized(response_json),
        ):
            result = await qna.get_answers(context)

            qna_trace_activities = list(
                filter(
                    lambda act: act.type == "trace" and act.name == "QnAMaker",
                    context.sent,
                )
            )
            trace_activity = qna_trace_activities[0]

            self.assertEqual("trace", trace_activity.type)
            self.assertEqual("QnAMaker", trace_activity.name)
            self.assertEqual("QnAMaker Trace", trace_activity.label)
            self.assertEqual(
                "https://www.qnamaker.ai/schemas/trace", trace_activity.value_type
            )
            self.assertEqual(True, hasattr(trace_activity, "value"))
            self.assertEqual(True, hasattr(trace_activity.value, "message"))
            self.assertEqual(True, hasattr(trace_activity.value, "query_results"))
            self.assertEqual(True, hasattr(trace_activity.value, "score_threshold"))
            self.assertEqual(True, hasattr(trace_activity.value, "top"))
            self.assertEqual(True, hasattr(trace_activity.value, "strict_filters"))
            self.assertEqual(
                self._knowledge_base_id, trace_activity.value.knowledge_base_id
            )

            return result
def create_reply(activity):
    return Activity(
        type=ActivityTypes.message,
        from_property=ChannelAccount(
            id=activity.recipient.id, name=activity.recipient.name
        ),
        recipient=ChannelAccount(
            id=activity.from_property.id, name=activity.from_property.name
        ),
        reply_to_id=activity.id,
        service_url=activity.service_url,
        channel_id=activity.channel_id,
        conversation=ConversationAccount(
            is_group=activity.conversation.is_group,
            id=activity.conversation.id,
            name=activity.conversation.name,
        ),
    )
Example #3
0
 def create_reply(self, activity, text, locale=None):
     return Activity(
         type=ActivityTypes.message,
         from_property=ChannelAccount(id=activity.recipient.id,
                                      name=activity.recipient.name),
         recipient=ChannelAccount(id=activity.from_property.id,
                                  name=activity.from_property.name),
         reply_to_id=activity.id,
         service_url=activity.service_url,
         channel_id=activity.channel_id,
         conversation=ConversationAccount(
             is_group=activity.conversation.is_group,
             id=activity.conversation.id,
             name=activity.conversation.name,
         ),
         text=text or "",
         locale=locale or activity.locale,
     )
Example #4
0
    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
        )
Example #5
0
 def _create_reply(self, activity) -> Activity:
     return Activity(
         type=ActivityTypes.message,
         timestamp=datetime.utcnow(),
         from_property=ChannelAccount(id=activity.recipient.id,
                                      name=activity.recipient.name),
         recipient=ChannelAccount(id=activity.from_property.id,
                                  name=activity.from_property.name),
         reply_to_id=activity.id,
         service_url=activity.service_url,
         channel_id=activity.channel_id,
         conversation=ConversationAccount(
             is_group=activity.conversation.is_group,
             id=activity.conversation.id,
             name=activity.conversation.name,
         ),
         text="",
         locale=activity.locale,
     )
    def setUpClass(cls):
        cls.bot_id = str(uuid4())
        cls.skill_id = str(uuid4())

        cls._test_id_factory = ConversationIdFactoryForTest()

        cls._claims_identity = ClaimsIdentity({}, False)

        cls._claims_identity.claims[
            AuthenticationConstants.AUDIENCE_CLAIM] = cls.bot_id
        cls._claims_identity.claims[
            AuthenticationConstants.APP_ID_CLAIM] = cls.skill_id
        cls._claims_identity.claims[
            AuthenticationConstants.
            SERVICE_URL_CLAIM] = "http://testbot.com/api/messages"
        cls._conversation_reference = ConversationReference(
            conversation=ConversationAccount(id=str(uuid4())),
            service_url="http://testbot.com/api/messages",
        )
Example #7
0
    async def test_trace_test(self):
        activity = Activity(
            type=ActivityTypes.message,
            text='how do I clean the stove?',
            conversation=ConversationAccount(),
            recipient=ChannelAccount(),
            from_property=ChannelAccount(),
        )

        response_json = QnaApplicationTest._get_json_for_file(
            'ReturnsAnswer.json')
        qna = QnAMaker(QnaApplicationTest.tests_endpoint)

        context = TestContext(activity)

        with patch('aiohttp.ClientSession.post',
                   return_value=aiounittest.futurized(response_json)):
            result = await qna.get_answers(context)

            qna_trace_activities = list(
                filter(
                    lambda act: act.type == 'trace' and act.name == 'QnAMaker',
                    context.sent))
            trace_activity = qna_trace_activities[0]

            self.assertEqual('trace', trace_activity.type)
            self.assertEqual('QnAMaker', trace_activity.name)
            self.assertEqual('QnAMaker Trace', trace_activity.label)
            self.assertEqual('https://www.qnamaker.ai/schemas/trace',
                             trace_activity.value_type)
            self.assertEqual(True, hasattr(trace_activity, 'value'))
            self.assertEqual(True, hasattr(trace_activity.value, 'message'))
            self.assertEqual(True,
                             hasattr(trace_activity.value, 'query_results'))
            self.assertEqual(True,
                             hasattr(trace_activity.value, 'score_threshold'))
            self.assertEqual(True, hasattr(trace_activity.value, 'top'))
            self.assertEqual(True,
                             hasattr(trace_activity.value, 'strict_filters'))
            self.assertEqual(self._knowledge_base_id,
                             trace_activity.value.knowledge_base_id[0])

            return result
 def create_conversation_reference(
         name: str,
         user: str = "User1",
         bot: str = "Bot") -> ConversationReference:
     return ConversationReference(
         channel_id="test",
         service_url="https://test.com",
         conversation=ConversationAccount(
             is_group=False,
             conversation_type=name,
             id=name,
         ),
         user=ChannelAccount(
             id=user.lower(),
             name=user.lower(),
         ),
         bot=ChannelAccount(
             id=bot.lower(),
             name=bot.lower(),
         ),
     )
Example #9
0
    async def test_delivery_mode_normal(self):
        mock_credential_provider = unittest.mock.create_autospec(CredentialProvider)

        settings = BotFrameworkAdapterSettings(
            app_id="bot_id", credential_provider=mock_credential_provider
        )
        adapter = AdapterUnderTest(settings)

        async def callback(context: TurnContext):
            await context.send_activity("activity 1")
            await context.send_activity("activity 2")
            await context.send_activity("activity 3")

        inbound_activity = Activity(
            type=ActivityTypes.message,
            channel_id="emulator",
            service_url="http://tempuri.org/whatever",
            delivery_mode=DeliveryModes.normal,
            text="hello world",
            conversation=ConversationAccount(id="conversationId"),
        )

        identity = ClaimsIdentity(
            claims={
                AuthenticationConstants.AUDIENCE_CLAIM: "bot_id",
                AuthenticationConstants.APP_ID_CLAIM: "bot_id",
                AuthenticationConstants.VERSION_CLAIM: "1.0",
            },
            is_authenticated=True,
        )

        invoke_response = await adapter.process_activity_with_identity(
            inbound_activity, identity, callback
        )
        assert not invoke_response
        assert (
            adapter.connector_client_mock.conversations.send_to_conversation.call_count
            == 3
        )
    async def send_activities(
        self, context: TurnContext, activities: List[Activity]
    ) -> List[ResourceResponse]:
        """
        Send a message from the bot to the messaging API.

        :param context: A TurnContext representing the current incoming message and environment.
        :type context: :class:`botbuilder.core.TurnContext`
        :param activities: An array of outgoing activities to be sent back to the messaging API.
        :type activities: :class:`typing.List[Activity]`
        :return: An array of ResourceResponse objects containing the IDs that Slack assigned to the sent messages.
        :rtype: :class:`typing.List[ResourceResponse]`
        """

        if not context:
            raise Exception("TurnContext is required")
        if not activities:
            raise Exception("List[Activity] is required")

        responses = []

        for activity in activities:
            if activity.type == ActivityTypes.message:
                message = SlackHelper.activity_to_slack(activity)

                slack_response = await self.slack_client.post_message_to_slack(message)

                if slack_response and slack_response.status_code / 100 == 2:
                    resource_response = ActivityResourceResponse(
                        id=slack_response.data["ts"],
                        activity_id=slack_response.data["ts"],
                        conversation=ConversationAccount(
                            id=slack_response.data["channel"]
                        ),
                    )

                    responses.append(resource_response)

        return responses
Example #11
0
    def __init__(self, reference: ConversationReference = None):

        #calling method ConsoleAdapter and initializes attributes of ConsoleAdapter class
        super(ConsoleAdapter, self).__init__()
        #setting paramaeters for ConversationReference. Setting ConversationReferences and paarameters = to self.reference
        #ConversationReference defines a particular point in a conversations
        self.reference = ConversationReference(
            channel_id='console',
            user=ChannelAccount(id='user', name='User1'),
            bot=ChannelAccount(id='bot', name='Bot'),
            conversation=ConversationAccount(id='convo1',
                                             name='',
                                             is_group=False),
            service_url='')

        # Warn users to pass in an instance of a ConversationReference, otherwise the parameter will be ignored.
        if reference is not None and not isinstance(reference,
                                                    ConversationReference):
            warnings.warn(
                'ConsoleAdapter: `reference` argument is not an instance of ConversationReference and will '
                'be ignored.')
        else:
            #getattr returns value of the named attribute of an object
            #sets self.reference attributes to initial values
            self.reference.channel_id = getattr(reference, 'channel_id',
                                                self.reference.channel_id)
            self.reference.user = getattr(reference, 'user',
                                          self.reference.user)
            self.reference.bot = getattr(reference, 'bot', self.reference.bot)
            self.reference.conversation = getattr(reference, 'conversation',
                                                  self.reference.conversation)
            self.reference.service_url = getattr(reference, 'service_url',
                                                 self.reference.service_url)
            # The only attribute on self.reference without an initial value is activity_id, so if reference does not
            # have a value for activity_id, default self.reference.activity_id to None
            self.reference.activity_id = getattr(reference, 'activity_id',
                                                 None)
        # setting _next_id variable to zero
        self._next_id = 0
Example #12
0
    async def test_do_not_throw_on_null_from(self):
        telemetry = Mock()
        my_logger = TelemetryLoggerMiddleware(telemetry, False)

        adapter = TestAdapter(template_or_conversation=Activity(
            channel_id="test",
            recipient=ChannelAccount(id="bot", name="Bot"),
            conversation=ConversationAccount(id=str(uuid.uuid4())),
        ))
        adapter.use(my_logger)

        async def send_proactive(context: TurnContext):
            await context.send_activity("proactive")

        async def logic(context: TurnContext):
            await adapter.create_conversation(
                context.activity.channel_id,
                send_proactive,
            )

        adapter.logic = logic

        test_flow = TestFlow(None, adapter)
        await test_flow.send("foo")
        await test_flow.assert_reply("proactive")

        telemetry_calls = [
            (
                TelemetryLoggerConstants.BOT_MSG_RECEIVE_EVENT,
                {
                    "fromId": None,
                    "conversationName": None,
                    "locale": None,
                    "recipientId": "bot",
                    "recipientName": "Bot",
                },
            ),
        ]
        self.assert_telemetry_calls(telemetry, telemetry_calls)
def create_activity_reply(activity: Activity,
                          text: str = None,
                          locale: str = None):

    return Activity(type=ActivityTypes.message,
                    timestamp=datetime.utcnow(),
                    from_property=ChannelAccount(
                        id=getattr(activity.recipient, 'id', None),
                        name=getattr(activity.recipient, 'name', None)),
                    recipient=ChannelAccount(id=activity.from_property.id,
                                             name=activity.from_property.name),
                    reply_to_id=activity.id,
                    service_url=activity.service_url,
                    channel_id=activity.channel_id,
                    conversation=ConversationAccount(
                        is_group=activity.conversation.is_group,
                        id=activity.conversation.id,
                        name=activity.conversation.name),
                    text=text or '',
                    locale=locale or '',
                    attachments=[],
                    entities=[])
Example #14
0
    async def test_clear_and_save(self):
        turn_context = TestUtilities.create_empty_context()
        turn_context.activity.conversation = ConversationAccount(id="1234")

        storage = MemoryStorage({})

        # Turn 0
        bot_state1 = ConversationState(storage)
        (await bot_state1.create_property("test-name").get(
            turn_context,
            lambda: TestPocoState()  # pylint: disable=unnecessary-lambda
        )).value = "test-value"
        await bot_state1.save_changes(turn_context)

        # Turn 1
        bot_state2 = ConversationState(storage)
        value1 = (
            await bot_state2.create_property("test-name").get(
                turn_context,
                lambda: TestPocoState(value="default-value")  # pylint: disable=unnecessary-lambda
            )).value

        assert value1 == "test-value"

        # Turn 2
        bot_state3 = ConversationState(storage)
        await bot_state3.clear_state(turn_context)
        await bot_state3.save_changes(turn_context)

        # Turn 3
        bot_state4 = ConversationState(storage)
        value2 = (
            await bot_state4.create_property("test-name").get(
                turn_context,
                lambda: TestPocoState(value="default-value")  # pylint: disable=unnecessary-lambda
            )).value

        assert value2, "default-value"
Example #15
0
async def process_activity(channel_id: str, channel_data_tenant_id: str,
                           conversation_tenant_id: str):
    activity = None
    mock_claims = unittest.mock.create_autospec(ClaimsIdentity)
    mock_credential_provider = unittest.mock.create_autospec(
        BotFrameworkAdapterSettings)

    sut = BotFrameworkAdapter(mock_credential_provider)

    async def aux_func(context):
        nonlocal activity
        activity = context.Activity

    await sut.process_activity(
        Activity(
            channel_id=channel_id,
            service_url="https://smba.trafficmanager.net/amer/",
            channel_data={"tenant": {
                "id": channel_data_tenant_id
            }},
            conversation=ConversationAccount(tenant_id=conversation_tenant_id),
        ), mock_claims, aux_func)
    return activity
    def __init__(
        self,
        logic: Coroutine = None,
        template_or_conversation: Union[Activity, ConversationReference] = None,
        send_trace_activities: bool = False,
    ):  # pylint: disable=unused-argument
        """
        Creates a new TestAdapter instance.
        :param logic:
        :param conversation: A reference to the conversation to begin the adapter state with.
        """
        super(TestAdapter, self).__init__()
        self.logic = logic
        self._next_id: int = 0
        self._user_tokens: List[UserToken] = []
        self._magic_codes: List[TokenMagicCode] = []
        self._conversation_lock = Lock()
        self.activity_buffer: List[Activity] = []
        self.updated_activities: List[Activity] = []
        self.deleted_activities: List[ConversationReference] = []
        self.send_trace_activities = send_trace_activities

        self.template = (
            template_or_conversation
            if isinstance(template_or_conversation, Activity)
            else Activity(
                channel_id="test",
                service_url="https://test.com",
                from_property=ChannelAccount(id="User1", name="user"),
                recipient=ChannelAccount(id="bot", name="Bot"),
                conversation=ConversationAccount(id="Convo1"),
            )
        )

        if isinstance(template_or_conversation, ConversationReference):
            self.template.channel_id = template_or_conversation.channel_id
    def __init__(self, reference: ConversationReference = None):
        super(ConsoleAdapter, self).__init__()

        self.reference = ConversationReference(channel_id='console',
                                               user=ChannelAccount(id='user', name='User1'),
                                               bot=ChannelAccount(id='bot', name='Bot'),
                                               conversation=ConversationAccount(id='convo1', name='', is_group=False),
                                               service_url='')

        # Warn users to pass in an instance of a ConversationReference, otherwise the parameter will be ignored.
        if reference is not None and not isinstance(reference, ConversationReference):
            warnings.warn('ConsoleAdapter: `reference` argument is not an instance of ConversationReference and will '
                          'be ignored.')
        else:
            self.reference.channel_id = getattr(reference, 'channel_id', self.reference.channel_id)
            self.reference.user = getattr(reference, 'user', self.reference.user)
            self.reference.bot = getattr(reference, 'bot', self.reference.bot)
            self.reference.conversation = getattr(reference, 'conversation', self.reference.conversation)
            self.reference.service_url = getattr(reference, 'service_url', self.reference.service_url)
            # The only attribute on self.reference without an initial value is activity_id, so if reference does not
            # have a value for activity_id, default self.reference.activity_id to None
            self.reference.activity_id = getattr(reference, 'activity_id', None)

        self._next_id = 0
    async def process_activity(self, logic: Callable):
        """
        Begins listening to console input.
        :param logic:
        :return:
        """
        while True:
            msg = input()
            if msg is None:
                pass
            else:
                self._next_id += 1
                activity = Activity(text=msg,
                                    channel_id='console',
                                    from_property=ChannelAccount(id='user', name='User1'),
                                    recipient=ChannelAccount(id='bot', name='Bot'),
                                    conversation=ConversationAccount(id='Convo1'),
                                    type=ActivityTypes.message,
                                    timestamp=datetime.datetime.now(),
                                    id=str(self._next_id))

                activity = BotContext.apply_conversation_reference(activity, self.reference, True)
                context = BotContext(self, activity)
                await self.run_middleware(context, logic)
Example #19
0
    def __init__(self, logic: Coroutine=None, conversation: ConversationReference=None, send_trace_activity: bool = False):
        """
        Creates a new TestAdapter instance.
        :param logic:
        :param conversation: A reference to the conversation to begin the adapter state with.
        """
        super(TestAdapter, self).__init__()
        self.logic = logic
        self._next_id: int = 0
        self.activity_buffer: List[Activity] = []
        self.updated_activities: List[Activity] = []
        self.deleted_activities: List[ConversationReference] = []

        self.template: Activity = Activity(
            channel_id='test',
            service_url='https://test.com',
            from_property=ChannelAccount(id='User1', name='user'),
            recipient=ChannelAccount(id='bot', name='Bot'),
            conversation=ConversationAccount(id='Convo1')
        )
        if self.template is not None:
            self.template.service_url = self.template.service_url
            self.template.conversation = self.template.conversation
            self.template.channel_id = self.template.channel_id
Example #20
0
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import pytest

from botbuilder.schema import Activity, ChannelAccount, ResourceResponse, ConversationAccount
from botbuilder.core import BotAdapter, BotContext
ACTIVITY = Activity(id='1234',
                    type='message',
                    text='test',
                    from_property=ChannelAccount(id='user', name='User Name'),
                    recipient=ChannelAccount(id='bot', name='Bot Name'),
                    conversation=ConversationAccount(id='convo',
                                                     name='Convo Name'),
                    channel_id='UnitTest',
                    service_url='https://example.org')


class SimpleAdapter(BotAdapter):
    async def send_activities(self, context, activities):
        responses = []
        assert context is not None
        assert activities is not None
        assert type(activities) == list
        assert len(activities) > 0
        for (idx, activity) in enumerate(activities):
            assert isinstance(activity, Activity)
            assert activity.type == 'message'
            responses.append(ResourceResponse(id='5678'))
        return responses
    async def create_conversation(
        self,
        reference: ConversationReference,
        logic: Callable[[TurnContext], Awaitable] = None,
        conversation_parameters: ConversationParameters = None,
    ):
        """
        Starts a new conversation with a user. Used to direct message to a member of a group.

        :param reference: The conversation reference that contains the tenant
        :type reference: :class:`botbuilder.schema.ConversationReference`
        :param logic: The logic to use for the creation of the conversation
        :type logic: :class:`typing.Callable`
        :param conversation_parameters: The information to use to create the conversation
        :type conversation_parameters:

        :raises: It raises a generic exception error.

        :return: A task representing the work queued to execute.

        .. remarks::
            To start a conversation, your bot must know its account information and the user's
            account information on that channel.
            Most channels only support initiating a direct message (non-group) conversation.
            The adapter attempts to create a new conversation on the channel, and
            then sends a conversation update activity through its middleware pipeline
            to the the callback method.
            If the conversation is established with the specified users, the ID of the activity
            will contain the ID of the new conversation.</para>
        """
        try:
            if reference.service_url is None:
                raise TypeError(
                    "BotFrameworkAdapter.create_conversation(): reference.service_url cannot be None."
                )

            # Create conversation
            parameters = (conversation_parameters if conversation_parameters
                          else ConversationParameters(bot=reference.bot,
                                                      members=[reference.user],
                                                      is_group=False))
            client = await self.create_connector_client(reference.service_url)

            # Mix in the tenant ID if specified. This is required for MS Teams.
            if reference.conversation is not None and reference.conversation.tenant_id:
                # Putting tenant_id in channel_data is a temporary while we wait for the Teams API to be updated
                parameters.channel_data = {
                    "tenant": {
                        "id": reference.conversation.tenant_id
                    }
                }

                # Permanent solution is to put tenant_id in parameters.tenant_id
                parameters.tenant_id = reference.conversation.tenant_id

            resource_response = await client.conversations.create_conversation(
                parameters)
            request = TurnContext.apply_conversation_reference(
                Activity(type=ActivityTypes.event, name="CreateConversation"),
                reference,
                is_incoming=True,
            )
            request.conversation = ConversationAccount(
                id=resource_response.id, tenant_id=parameters.tenant_id)
            request.channel_data = parameters.channel_data
            if resource_response.service_url:
                request.service_url = resource_response.service_url

            context = self.create_context(request)
            return await self.run_pipeline(context, logic)

        except Exception as error:
            raise error
    ActivityTypes,
    ChannelAccount,
    ConversationAccount,
    Entity,
    Mention,
    ResourceResponse,
)
from botbuilder.core import BotAdapter, MessageFactory, TurnContext

ACTIVITY = Activity(
    id="1234",
    type="message",
    text="test",
    from_property=ChannelAccount(id="user", name="User Name"),
    recipient=ChannelAccount(id="bot", name="Bot Name"),
    conversation=ConversationAccount(id="convo", name="Convo Name"),
    channel_id="UnitTest",
    locale=
    "en-uS",  # Intentionally oddly-cased to check that it isn't defaulted somewhere, but tests stay in English
    service_url="https://example.org",
)


class SimpleAdapter(BotAdapter):
    async def send_activities(self, context,
                              activities) -> List[ResourceResponse]:
        responses = []
        assert context is not None
        assert activities is not None
        assert isinstance(activities, list)
        assert activities
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import pytest

from botbuilder.core import TurnContext, MemoryStorage, TestAdapter, ConversationState
from botbuilder.schema import Activity, ConversationAccount

RECEIVED_MESSAGE = Activity(type='message',
                            text='received',
                            channel_id='test',
                            conversation=ConversationAccount(id='convo'))
MISSING_CHANNEL_ID = Activity(type='message',
                              text='received',
                              conversation=ConversationAccount(id='convo'))
MISSING_CONVERSATION = Activity(type='message',
                                text='received',
                                channel_id='test')
END_OF_CONVERSATION = Activity(type='endOfConversation',
                               channel_id='test',
                               conversation=ConversationAccount(id='convo'))


class TestConversationState:
    storage = MemoryStorage()
    adapter = TestAdapter()
    context = TurnContext(adapter, RECEIVED_MESSAGE)
    middleware = ConversationState(storage)

    @pytest.mark.asyncio
    async def test_should_load_and_save_state_from_storage(self):
Example #24
0
    async def test_post_activity_using_invoke_response(self):
        for is_gov in [True, False]:
            with self.subTest(is_government=is_gov):
                # pylint: disable=undefined-variable
                # pylint: disable=cell-var-from-loop
                conversation_id = str(uuid4())
                conversation_id_factory = SimpleConversationIdFactory(
                    conversation_id)
                test_activity = MessageFactory.text("some message")
                test_activity.conversation = ConversationAccount()
                expected_oauth_scope = (
                    AuthenticationConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE)
                mock_channel_provider: ChannelProvider = Mock(
                    spec=ChannelProvider)

                def is_government_mock():
                    nonlocal expected_oauth_scope
                    if is_government:
                        expected_oauth_scope = (
                            GovernmentConstants.TO_CHANNEL_FROM_BOT_OAUTH_SCOPE
                        )

                    return is_government

                mock_channel_provider.is_government = Mock(
                    side_effect=is_government_mock)

                skill = BotFrameworkSkill(
                    id="SomeSkill",
                    app_id="",
                    skill_endpoint="https://someskill.com/api/messages",
                )

                async def _mock_post_content(
                    to_url: str,
                    token: str,  # pylint: disable=unused-argument
                    activity: Activity,
                ) -> (int, object):
                    nonlocal self

                    self.assertEqual(skill.skill_endpoint, to_url)
                    # Assert that the activity being sent has what we expect.
                    self.assertEqual(conversation_id, activity.conversation.id)
                    self.assertEqual("https://parentbot.com/api/messages",
                                     activity.service_url)

                    # Create mock response.
                    return 200, None

                sut = await self._create_http_client_with_mock_handler(
                    _mock_post_content, conversation_id_factory)
                result = await sut.post_activity_to_skill(
                    "", skill, "https://parentbot.com/api/messages",
                    test_activity)

                # Assert factory options
                self.assertEqual(
                    "", conversation_id_factory.creation_options.from_bot_id)
                self.assertEqual(
                    expected_oauth_scope,
                    conversation_id_factory.creation_options.
                    from_bot_oauth_scope,
                )
                self.assertEqual(
                    test_activity,
                    conversation_id_factory.creation_options.activity)
                self.assertEqual(
                    skill, conversation_id_factory.creation_options.
                    bot_framework_skill)

                # Assert result
                self.assertIsInstance(result, InvokeResponse)
                self.assertEqual(200, result.status)
    async def post_activity(
        self,
        from_bot_id: str,
        to_bot_id: str,
        to_url: str,
        service_url: str,
        conversation_id: str,
        activity: Activity,
    ) -> InvokeResponse:
        if not from_bot_id:
            raise TypeError("from_bot_id")
        if not to_bot_id:
            raise TypeError("to_bot_id")
        if not to_url:
            raise TypeError("to_url")
        if not service_url:
            raise TypeError("service_url")
        if not conversation_id:
            raise TypeError("conversation_id")
        if not activity:
            raise TypeError("activity")

        if self._logger:
            self._logger.log(20, f"post to skill '{to_bot_id}' at '{to_url}'")

        credentials = await self._credentials_factory.create_credentials(
            from_bot_id, to_bot_id, self._login_endpoint, True
        )

        # Get token for the skill call
        token = credentials.get_access_token() if credentials.microsoft_app_id else None

        # Clone the activity so we can modify it before sending without impacting the original object.
        activity_copy = deepcopy(activity)

        # Apply the appropriate addressing to the newly created Activity.
        activity_copy.relates_to = ConversationReference(
            service_url=activity_copy.service_url,
            activity_id=activity_copy.id,
            channel_id=activity_copy.channel_id,
            conversation=ConversationAccount(
                id=activity_copy.conversation.id,
                name=activity_copy.conversation.name,
                conversation_type=activity_copy.conversation.conversation_type,
                aad_object_id=activity_copy.conversation.aad_object_id,
                is_group=activity_copy.conversation.is_group,
                role=activity_copy.conversation.role,
                tenant_id=activity_copy.conversation.tenant_id,
                properties=activity_copy.conversation.properties,
            ),
            bot=None,
        )
        activity_copy.conversation.id = conversation_id
        activity_copy.service_url = service_url
        if not activity_copy.recipient:
            activity_copy.recipient = ChannelAccount(role=RoleTypes.skill)
        else:
            activity_copy.recipient.role = RoleTypes.skill

        headers_dict = {
            "Content-type": "application/json; charset=utf-8",
        }
        if token:
            headers_dict.update(
                {"Authorization": f"Bearer {token}",}
            )
        json_content = dumps(activity_copy.serialize()).encode("utf-8")

        request = HttpRequest(
            request_uri=to_url, content=json_content, headers=headers_dict
        )
        response = await self._http_client.post(request=request)

        data = await response.read_content_str()

        if not await response.is_succesful() and self._logger:
            # Otherwise we can assume we don't have to deserialize - so just log the content so it's not lost.
            self._logger.log(
                40,
                f"Bot Framework call failed to '{to_url}' returning '{int(response.status_code)}' and '{data}'",
            )

        return InvokeResponse(
            status=response.status_code, body=loads(data) if data else None
        )
Example #26
0
    async def post_activity(
        self,
        from_bot_id: str,
        to_bot_id: str,
        to_url: str,
        service_url: str,
        conversation_id: str,
        activity: Activity,
    ) -> InvokeResponse:
        app_credentials = await self._get_app_credentials(
            from_bot_id, to_bot_id)

        if not app_credentials:
            raise KeyError(
                "Unable to get appCredentials to connect to the skill")

        # Get token for the skill call
        token = (app_credentials.get_access_token()
                 if app_credentials.microsoft_app_id else None)

        # Capture current activity settings before changing them.
        # TODO: DO we need to set the activity ID? (events that are created manually don't have it).
        original_conversation_id = activity.conversation.id
        original_service_url = activity.service_url
        original_caller_id = activity.caller_id
        original_relates_to = activity.relates_to

        try:
            # TODO: The relato has to be ported to the adapter in the new integration library when
            #  resolving conflicts in merge
            activity.relates_to = ConversationReference(
                service_url=activity.service_url,
                activity_id=activity.id,
                channel_id=activity.channel_id,
                conversation=ConversationAccount(
                    id=activity.conversation.id,
                    name=activity.conversation.name,
                    conversation_type=activity.conversation.conversation_type,
                    aad_object_id=activity.conversation.aad_object_id,
                    is_group=activity.conversation.is_group,
                    role=activity.conversation.role,
                    tenant_id=activity.conversation.tenant_id,
                    properties=activity.conversation.properties,
                ),
                bot=None,
            )
            activity.conversation.id = conversation_id
            activity.service_url = service_url
            activity.caller_id = f"urn:botframework:aadappid:{from_bot_id}"

            headers_dict = {
                "Content-type": "application/json; charset=utf-8",
            }
            if token:
                headers_dict.update({
                    "Authorization": f"Bearer {token}",
                })

            json_content = json.dumps(activity.serialize())
            resp = await self._session.post(
                to_url,
                data=json_content.encode("utf-8"),
                headers=headers_dict,
            )
            resp.raise_for_status()
            data = (await resp.read()).decode()
            content = json.loads(data) if data else None

            return InvokeResponse(status=resp.status, body=content)

        finally:
            # Restore activity properties.
            activity.conversation.id = original_conversation_id
            activity.service_url = original_service_url
            activity.caller_id = original_caller_id
            activity.relates_to = original_relates_to
    async def test_exchange_token_from_credentials(self):
        mock_credential_provider = unittest.mock.create_autospec(CredentialProvider)

        settings = BotFrameworkAdapterSettings(
            app_id="bot_id", credential_provider=mock_credential_provider,
        )
        adapter = AdapterUnderTest(settings)

        identity = ClaimsIdentity(
            claims={
                AuthenticationConstants.AUDIENCE_CLAIM: "bot_id",
                AuthenticationConstants.APP_ID_CLAIM: "bot_id",
                AuthenticationConstants.VERSION_CLAIM: "1.0",
            },
            is_authenticated=True,
        )

        inbound_activity = Activity(
            type=ActivityTypes.invoke,
            name=SignInConstants.token_exchange_operation_name,
            service_url="http://tempuri.org/whatever",
            conversation=ConversationAccount(id="conversationId"),
            value=TokenExchangeInvokeRequest(
                id="token_exchange_id",
                token="token",
                connection_name="connection_name",
            ),
        )

        async def callback(context):
            result = await adapter.exchange_token_from_credentials(
                turn_context=context,
                oauth_app_credentials=None,
                connection_name=context.activity.value.connection_name,
                exchange_request=TokenExchangeRequest(
                    token=context.activity.value.token, uri=context.activity.service_url
                ),
                user_id="user_id",
            )

            activity = Activity(
                type=ActivityTypes.invoke_response,
                value=InvokeResponse(
                    status=200,
                    body=TokenExchangeInvokeResponse(
                        id=context.activity.value.id,
                        connection_name=result.connection_name,
                    ),
                ),
            )

            await context.send_activity(activity)

        invoke_response = await adapter.process_activity_with_identity(
            inbound_activity, identity, callback,
        )

        assert invoke_response
        assert invoke_response.status == 200
        assert invoke_response.body.id == inbound_activity.value.id
        assert (
            invoke_response.body.connection_name
            == inbound_activity.value.connection_name
        )
Example #28
0
    async def create_test_flow(
        dialog: Dialog,
        test_case: SkillFlowTestCase = SkillFlowTestCase.root_bot_only,
        enabled_trace=False,
    ) -> TestAdapter:
        conversation_id = "testFlowConversationId"
        storage = MemoryStorage()
        conversation_state = ConversationState(storage)
        user_state = UserState(storage)

        activity = Activity(
            channel_id="test",
            service_url="https://test.com",
            from_property=ChannelAccount(id="user1", name="User1"),
            recipient=ChannelAccount(id="bot", name="Bot"),
            conversation=ConversationAccount(is_group=False,
                                             conversation_type=conversation_id,
                                             id=conversation_id),
        )

        dialog_manager = DialogManager(dialog)
        dialog_manager.user_state = user_state
        dialog_manager.conversation_state = conversation_state

        async def logic(context: TurnContext):
            if test_case != SkillFlowTestCase.root_bot_only:
                # Create a skill ClaimsIdentity and put it in turn_state so isSkillClaim() returns True.
                claims_identity = ClaimsIdentity({}, False)
                claims_identity.claims[
                    "ver"] = "2.0"  # AuthenticationConstants.VersionClaim
                claims_identity.claims["aud"] = (
                    SimpleComponentDialog.skill_bot_id
                )  # AuthenticationConstants.AudienceClaim
                claims_identity.claims["azp"] = (
                    SimpleComponentDialog.parent_bot_id
                )  # AuthenticationConstants.AuthorizedParty
                context.turn_state[
                    BotAdapter.BOT_IDENTITY_KEY] = claims_identity

                if test_case == SkillFlowTestCase.root_bot_consuming_skill:
                    # Simulate the SkillConversationReference with a channel OAuthScope stored in turn_state.
                    # This emulates a response coming to a root bot through SkillHandler.
                    context.turn_state[
                        SkillHandler.
                        SKILL_CONVERSATION_REFERENCE_KEY] = SkillConversationReference(
                            None, AuthenticationConstants.
                            TO_CHANNEL_FROM_BOT_OAUTH_SCOPE)

                if test_case == SkillFlowTestCase.middle_skill:
                    # Simulate the SkillConversationReference with a parent Bot ID stored in turn_state.
                    # This emulates a response coming to a skill from another skill through SkillHandler.
                    context.turn_state[
                        SkillHandler.
                        SKILL_CONVERSATION_REFERENCE_KEY] = SkillConversationReference(
                            None, SimpleComponentDialog.parent_bot_id)

            async def aux(
                turn_context: TurnContext,  # pylint: disable=unused-argument
                activities: List[Activity],
                next: Callable,
            ):
                for activity in activities:
                    if activity.type == ActivityTypes.end_of_conversation:
                        SimpleComponentDialog.eoc_sent = activity
                        break

                return await next()

            # Interceptor to capture the EoC activity if it was sent so we can assert it in the tests.
            context.on_send_activities(aux)

            SimpleComponentDialog.dm_turn_result = await dialog_manager.on_turn(
                context)

        adapter = TestAdapter(logic, activity, enabled_trace)
        adapter.use(AutoSaveStateMiddleware([user_state, conversation_state]))

        return adapter
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

import aiounittest

from botbuilder.core import TurnContext, MemoryStorage, ConversationState
from botbuilder.core.adapters import TestAdapter
from botbuilder.schema import Activity, ConversationAccount

RECEIVED_MESSAGE = Activity(
    type="message",
    text="received",
    channel_id="test",
    conversation=ConversationAccount(id="convo"),
)
MISSING_CHANNEL_ID = Activity(type="message",
                              text="received",
                              conversation=ConversationAccount(id="convo"))
MISSING_CONVERSATION = Activity(type="message",
                                text="received",
                                channel_id="test")
END_OF_CONVERSATION = Activity(
    type="endOfConversation",
    channel_id="test",
    conversation=ConversationAccount(id="convo"),
)


class TestConversationState(aiounittest.AsyncTestCase):
    storage = MemoryStorage()
    adapter = TestAdapter()
Example #30
0
    AuthenticationConstants,
    AppCredentials,
    CredentialProvider,
    SimpleChannelProvider,
    GovernmentConstants,
    SimpleCredentialProvider,
)

REFERENCE = ConversationReference(
    activity_id="1234",
    channel_id="test",
    locale="en-uS",  # Intentionally oddly-cased to check that it isn't defaulted somewhere, but tests stay in English
    service_url="https://example.org/channel",
    user=ChannelAccount(id="user", name="User Name"),
    bot=ChannelAccount(id="bot", name="Bot Name"),
    conversation=ConversationAccount(id="convo1"),
)

TEST_ACTIVITY = Activity(text="test", type=ActivityTypes.message)

INCOMING_MESSAGE = TurnContext.apply_conversation_reference(
    copy(TEST_ACTIVITY), REFERENCE, True
)
OUTGOING_MESSAGE = TurnContext.apply_conversation_reference(
    copy(TEST_ACTIVITY), REFERENCE
)
INCOMING_INVOKE = TurnContext.apply_conversation_reference(
    Activity(type=ActivityTypes.invoke), REFERENCE, True
)