コード例 #1
0
    def test_copy_to_should_copy_all_references(self):
        # pylint: disable=protected-access
        old_adapter = SimpleAdapter()
        old_activity = Activity(id="2", type="message", text="test copy")
        old_context = TurnContext(old_adapter, old_activity)
        old_context.responded = True

        async def send_activities_handler(context, activities, next_handler):
            assert context is not None
            assert activities is not None
            assert next_handler is not None
            await next_handler

        async def delete_activity_handler(context, reference, next_handler):
            assert context is not None
            assert reference is not None
            assert next_handler is not None
            await next_handler

        async def update_activity_handler(context, activity, next_handler):
            assert context is not None
            assert activity is not None
            assert next_handler is not None
            await next_handler

        old_context.on_send_activities(send_activities_handler)
        old_context.on_delete_activity(delete_activity_handler)
        old_context.on_update_activity(update_activity_handler)

        adapter = SimpleAdapter()
        new_context = TurnContext(adapter, ACTIVITY)
        assert not new_context._on_send_activities  # pylint: disable=protected-access
        assert not new_context._on_update_activity  # pylint: disable=protected-access
        assert not new_context._on_delete_activity  # pylint: disable=protected-access

        old_context.copy_to(new_context)

        assert new_context.adapter == old_adapter
        assert new_context.activity == old_activity
        assert new_context.responded is True
        assert (
            len(new_context._on_send_activities) == 1
        )  # pylint: disable=protected-access
        assert (
            len(new_context._on_update_activity) == 1
        )  # pylint: disable=protected-access
        assert (
            len(new_context._on_delete_activity) == 1
        )  # pylint: disable=protected-access
コード例 #2
0
    async def continue_conversation(
            self,
            reference: ConversationReference,
            callback: Callable,
            bot_id: str = None,  # pylint: disable=unused-argument
            claims_identity: ClaimsIdentity = None,
            audience: str = None,  # pylint: disable=unused-argument
    ):
        """
        Send a proactive message to a conversation.

        .. remarks::

            Most channels require a user to initiate a conversation with a bot before the bot can send activities to the
             user.

        :param reference: A reference to the conversation to continue.
        :type reference: :class:`botbuilder.schema.ConversationReference`
        :param callback: The method to call for the resulting bot turn.
        :type callback: :class:`typing.Callable`
        :param bot_id: Unused for this override.
        :type bot_id: str
        :param claims_identity: A ClaimsIdentity for the conversation.
        :type claims_identity: :class:`botframework.connector.auth.ClaimsIdentity`
        :param audience: Unused for this override.
        :type audience: str
        """

        if not reference:
            raise Exception("ConversationReference is required")
        if not callback:
            raise Exception("callback is required")

        if claims_identity:
            request = conversation_reference_extension.get_continuation_activity(
                reference)
            context = TurnContext(self, request)
            context.turn_state[BotAdapter.BOT_IDENTITY_KEY] = claims_identity
            context.turn_state[BotAdapter.BOT_CALLBACK_HANDLER_KEY] = callback
        else:
            request = TurnContext.apply_conversation_reference(
                conversation_reference_extension.get_continuation_activity(
                    reference),
                reference,
            )
            context = TurnContext(self, request)

        return await self.run_pipeline(context, callback)
コード例 #3
0
    async def test_healthcheck_with_connector(self):
        activity = Activity(
            type=ActivityTypes.invoke,
            name="healthcheck",
        )

        adapter = TestInvokeAdapter()
        turn_context = TurnContext(adapter, activity)

        mock_connector_client = MockConnectorClient()
        turn_context.turn_state[
            BotAdapter.BOT_CONNECTOR_CLIENT_KEY] = mock_connector_client

        bot = ActivityHandler()
        await bot.on_turn(turn_context)

        self.assertIsNotNone(adapter.activity)
        self.assertIsInstance(adapter.activity.value, InvokeResponse)
        self.assertEqual(adapter.activity.value.status, 200)

        response = HealthCheckResponse.deserialize(adapter.activity.value.body)
        self.assertTrue(response.health_results.success)
        self.assertEqual(response.health_results.authorization,
                         "Bearer awesome")
        self.assertEqual(response.health_results.user_agent, USER_AGENT)
        self.assertTrue(response.health_results.messages)
        self.assertEqual(response.health_results.messages[0],
                         "Health check succeeded.")
コード例 #4
0
    async def test_on_reply_to_activity(self):
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)

        mock_adapter = Mock()
        mock_adapter.continue_conversation = MagicMock(return_value=Future())
        mock_adapter.continue_conversation.return_value.set_result(Mock())
        mock_adapter.send_activities = MagicMock(return_value=Future())
        mock_adapter.send_activities.return_value.set_result([])

        sut = self.create_skill_handler_for_testing(mock_adapter)

        activity = Activity(type=ActivityTypes.message,
                            attachments=[],
                            entities=[])
        activity_id = str(uuid4())
        TurnContext.apply_conversation_reference(activity,
                                                 self._conversation_reference)

        await sut.test_on_reply_to_activity(self._claims_identity,
                                            self._conversation_id, activity_id,
                                            activity)

        args, kwargs = mock_adapter.continue_conversation.call_args_list[0]

        assert isinstance(args[0], ConversationReference)
        assert callable(args[1])
        assert isinstance(kwargs["claims_identity"], ClaimsIdentity)

        await args[1](TurnContext(
            mock_adapter,
            conversation_reference_extension.get_continuation_activity(
                self._conversation_reference),
        ))
        assert activity.caller_id is None
コード例 #5
0
 async def test_should_update_activity(self):
     adapter = AdapterUnderTest()
     context = TurnContext(adapter, INCOMING_MESSAGE)
     self.assertTrue(
         await adapter.update_activity(context, INCOMING_MESSAGE),
         "Activity not updated.",
     )
コード例 #6
0
        async def inspector(activity: Activity, description: str = None):  # pylint: disable=unused-argument

            self.assertTrue(len(activity.attachments) == 1)
            self.assertTrue(
                activity.attachments[0].content_type
                == CardFactory.content_types.oauth_card
            )

            # send a mock EventActivity back to the bot with the token
            adapter.add_user_token(
                connection_name, activity.channel_id, activity.recipient.id, token
            )

            event_activity = create_reply(activity)
            event_activity.type = ActivityTypes.event
            event_activity.from_property, event_activity.recipient = (
                event_activity.recipient,
                event_activity.from_property,
            )
            event_activity.name = "tokens/response"
            event_activity.value = TokenResponse(
                connection_name=connection_name, token=token
            )

            context = TurnContext(adapter, event_activity)
            await callback_handler(context)
コード例 #7
0
    async def test_get_user_token_returns_token(self):
        adapter = TestAdapter()
        connection_name = "myConnection"
        channel_id = "directline"
        user_id = "testUser"
        token = "abc123"
        activity = Activity(channel_id=channel_id,
                            from_property=ChannelAccount(id=user_id))

        turn_context = TurnContext(adapter, activity)

        adapter.add_user_token(connection_name, channel_id, user_id, token)

        token_response = await adapter.get_user_token(turn_context,
                                                      connection_name)
        assert token_response
        assert token == token_response.token
        assert connection_name == token_response.connection_name

        oauth_app_credentials = MicrosoftAppCredentials(None, None)
        token_response = await adapter.get_user_token(
            turn_context,
            connection_name,
            oauth_app_credentials=oauth_app_credentials)
        assert token_response
        assert token == token_response.token
        assert connection_name == token_response.connection_name
コード例 #8
0
    async def receive_activity(self, activity):
        """
        INTERNAL: called by a `TestFlow` instance to simulate a user sending a message to the bot.
        This will cause the adapters middleware pipe to be run and it's logic to be called.
        :param activity:
        :return:
        """
        if type(activity) == str:
            activity = Activity(type='message', text=activity)
        # Initialize request
        request = copy(self.template)

        for key, value in vars(activity).items():
            if value is not None and key != 'additional_properties':
                setattr(request, key, value)

        if not request.type:
            request.type = ActivityTypes.message
        if not request.id:
            self._next_id += 1
            request.id = str(self._next_id)

        # Create context object and run middleware
        context = TurnContext(self, request)
        return await self.run_middleware(context, self.logic)
コード例 #9
0
 async def test_should_add_and_call_load_all_on_multiple_plugins(self):
     adapter = TestAdapter()
     context = TurnContext(adapter, Activity())
     foo_state = BotStateMock({"foo": "bar"})
     bar_state = BotStateMock({"bar": "foo"})
     bot_state_set = AutoSaveStateMiddleware([foo_state, bar_state])
     await bot_state_set.bot_state_set.load_all(context)
コード例 #10
0
    async def __activity_callback_test(self, activity: Activity):
        self._conversation_id = await self._test_id_factory.create_skill_conversation_id(
            self._conversation_reference)

        mock_adapter = Mock()
        mock_adapter.continue_conversation = MagicMock(return_value=Future())
        mock_adapter.continue_conversation.return_value.set_result(Mock())
        mock_adapter.send_activities = MagicMock(return_value=Future())
        mock_adapter.send_activities.return_value.set_result([])

        sut = self.create_skill_handler_for_testing(mock_adapter)

        activity_id = str(uuid4())
        TurnContext.apply_conversation_reference(activity,
                                                 self._conversation_reference)

        await sut.test_on_reply_to_activity(self._claims_identity,
                                            self._conversation_id, activity_id,
                                            activity)

        args, kwargs = mock_adapter.continue_conversation.call_args_list[0]

        assert isinstance(args[0], ConversationReference)
        assert callable(args[1])
        assert isinstance(kwargs["claims_identity"], ClaimsIdentity)

        await args[1](TurnContext(mock_adapter, activity))
コード例 #11
0
    async def test_cancelling_waterfall_telemetry(self):
        # Arrange
        dialog_id = "waterfall"
        index = 0
        guid = "(guid)"

        async def my_waterfall_step(step) -> DialogTurnResult:
            await step.context.send_activity("step1 response")
            return Dialog.end_of_turn

        dialog = WaterfallDialog(dialog_id, [my_waterfall_step])

        telemetry_client = create_autospec(NullTelemetryClient)
        dialog.telemetry_client = telemetry_client

        dialog_instance = DialogInstance()
        dialog_instance.id = dialog_id
        dialog_instance.state = {"instanceId": guid, "stepIndex": index}

        # Act
        await dialog.end_dialog(
            TurnContext(TestAdapter(), Activity()),
            dialog_instance,
            DialogReason.CancelCalled,
        )

        # Assert
        telemetry_props = telemetry_client.track_event.call_args_list[0][0][1]

        self.assertEqual(3, len(telemetry_props))
        self.assertEqual(dialog_id, telemetry_props["DialogId"])
        self.assertEqual(my_waterfall_step.__qualname__,
                         telemetry_props["StepName"])
        self.assertEqual(guid, telemetry_props["InstanceId"])
        telemetry_client.track_event.assert_called_once()
コード例 #12
0
 async def test_should_work_as_a_middleware_plugin(self):
     adapter = TestAdapter()
     context = TurnContext(adapter, Activity())
     foo_state = BotStateMock({"foo": "bar"})
     autosave_middleware = AutoSaveStateMiddleware().add(foo_state)
     await autosave_middleware.on_turn(context, aux_func)
     assert foo_state.write_called, "save_all_changes() not called."
コード例 #13
0
 async def test_should_fail_to_update_activity_if_activity_id_missing(self):
     adapter = AdapterUnderTest()
     context = TurnContext(adapter, INCOMING_MESSAGE)
     cpy = deepcopy(INCOMING_MESSAGE)
     cpy.id = None
     with self.assertRaises(Exception) as _:
         await adapter.update_activity(context, cpy)
コード例 #14
0
    async def test_should_call_multiple_on_delete_activity_handlers_in_order(
            self):
        context = TurnContext(SimpleAdapter(), ACTIVITY)
        called_first = False
        called_second = False

        async def first_delete_handler(context, reference,
                                       next_handler_coroutine):
            nonlocal called_first, called_second
            assert called_first is False, 'called_first should not be True before first_delete_handler is called.'
            called_first = True
            assert called_second is False, 'Second on_delete_activity handler was called before first.'
            assert reference is not None
            assert context is not None
            assert reference.activity_id == '1234'
            await next_handler_coroutine()

        async def second_delete_handler(context, reference,
                                        next_handler_coroutine):
            nonlocal called_first, called_second
            assert called_first
            assert called_second is False, 'called_second was set to True before second handler was called.'
            called_second = True
            assert reference is not None
            assert context is not None
            assert reference.activity_id == '1234'
            await next_handler_coroutine()

        context.on_delete_activity(first_delete_handler)
        context.on_delete_activity(second_delete_handler)
        await context.delete_activity(ACTIVITY.id)
        assert called_first is True
        assert called_second is True
コード例 #15
0
 def test_should_not_create_context_without_request(self):
     try:
         TurnContext(SimpleAdapter(), None)
     except TypeError:
         pass
     except Exception as error:
         raise error
コード例 #16
0
 def test_should_not_create_context_without_adapter(self):
     try:
         TurnContext(None, ACTIVITY)
     except TypeError:
         pass
     except Exception as error:
         raise error
    async def test_on_teams_messaging_extension_configuration_query_settings_url(
            self):
        # Arrange
        activity = Activity(
            type=ActivityTypes.invoke,
            name="composeExtension/querySettingUrl",
            value={
                "commandId": "test_command",
                "parameters": [],
                "messagingExtensionQueryOptions": {
                    "skip": 1,
                    "count": 1
                },
                "state": "state_string",
            },
        )

        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_messaging_extension_configuration_query_settings_url")
    async def test_on_teams_messaging_extension_bot_message_send_activity_with_empty_string(
        self, ):
        # Arrange
        activity = Activity(
            type=ActivityTypes.invoke,
            name="composeExtension/submitAction",
            value={
                "data": {
                    "key": "value"
                },
                "context": {
                    "theme": "dark"
                },
                "commandId": "test_command",
                "commandContext": "command_context_test",
                "botMessagePreviewAction": "",
                "botActivityPreview": [Activity().serialize()],
                "messagePayload": MessageActionsPayload().serialize(),
            },
        )

        turn_context = TurnContext(SimpleAdapter(), activity)

        # Act
        bot = TestingTeamsActivityHandler()
        await bot.on_turn(turn_context)

        # Assert
        assert len(bot.record) == 3
        assert bot.record[0] == "on_invoke_activity"
        assert bot.record[
            1] == "on_teams_messaging_extension_submit_action_dispatch"
        assert bot.record[2] == "on_teams_messaging_extension_submit_action"
    async def test_on_teams_messaging_extension_fetch_task(self):
        # Arrange
        activity = Activity(
            type=ActivityTypes.invoke,
            name="composeExtension/fetchTask",
            value={
                "data": {
                    "key": "value"
                },
                "context": {
                    "theme": "dark"
                },
                "commandId": "test_command",
                "commandContext": "command_context_test",
                "botMessagePreviewAction": "message_action",
                "botActivityPreview": [{
                    "id": "123"
                }],
                "messagePayload": {
                    "id": "abc123"
                },
            },
        )
        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_messaging_extension_fetch_task"
コード例 #20
0
    async def continue_conversation(
        self,
        reference: ConversationReference,
        callback: Callable,
        bot_id: str = None,
        claims_identity: ClaimsIdentity = None,
        audience: str = None,
    ):
        """
        Sends a proactive message to a conversation. Call this method to proactively send a message to a conversation.
        Most _channels require a user to initiate a conversation with a bot before the bot can send activities
        to the user.
        :param bot_id: The application ID of the bot. This parameter is ignored in
        single tenant the Adpters (Console, Test, etc) but is critical to the BotFrameworkAdapter
        which is multi-tenant aware. </param>
        :param reference: A reference to the conversation to continue.</param>
        :param callback: The method to call for the resulting bot turn.</param>
        :param claims_identity:
        """

        if not reference:
            raise Exception("ConversationReference is required")
        if not callback:
            raise Exception("callback is required")

        request = TurnContext.apply_conversation_reference(
            conversation_reference_extension.get_continuation_activity(
                reference),
            reference,
        )
        context = TurnContext(self, request)

        return await self.run_pipeline(context, callback)
    async def test_on_teams_members_removed_activity(self):
        # arrange
        activity = Activity(
            type=ActivityTypes.conversation_update,
            channel_data={
                "eventType": "teamMemberRemoved",
                "team": {
                    "id": "team_id_1",
                    "name": "new_team_name"
                },
            },
            members_removed=[
                ChannelAccount(
                    id="123",
                    name="test_user",
                    aad_object_id="asdfqwerty",
                    role="tester",
                )
            ],
            channel_id=Channels.ms_teams,
        )

        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_conversation_update_activity"
        assert bot.record[1] == "on_teams_members_removed"
コード例 #22
0
    async def test_get_team_details_works_without_team_id(self):
        adapter = SimpleAdapterWithCreateConversation()
        ACTIVITY.channel_data = {}
        turn_context = TurnContext(adapter, ACTIVITY)
        result = TeamsInfo.get_team_id(turn_context)

        assert result == ""
コード例 #23
0
 async def test_should_pass_force_flag_through_in_load_all_call(self):
     adapter = TestAdapter()
     context = TurnContext(adapter, Activity())
     foo_state = BotStateMock({"foo": "bar"})
     foo_state.assert_force = True
     autosave_middleware = AutoSaveStateMiddleware().add(foo_state)
     await autosave_middleware.bot_state_set.load_all(context, True)
コード例 #24
0
 async def test_should_fail_to_update_activity_if_serviceUrl_missing(self):
     adapter = AdapterUnderTest()
     context = TurnContext(adapter, incoming_message)
     cpy = deepcopy(incoming_message)
     cpy.service_url = None
     with self.assertRaises(Exception) as _:
         await adapter.update_activity(context, cpy)
コード例 #25
0
    async def _intercept_oauth_cards(self, claims_identity: ClaimsIdentity,
                                     activity: Activity) -> bool:
        if activity.attachments:
            oauth_card_attachment = next(
                (attachment for attachment in activity.attachments
                 if attachment.content_type == ContentTypes.oauth_card),
                None,
            )

            if oauth_card_attachment:
                target_skill = self._get_calling_skill(claims_identity)
                if target_skill:
                    oauth_card = oauth_card_attachment.content
                    token_exchange_resource = oauth_card.get(
                        "TokenExchangeResource") or oauth_card.get(
                            "tokenExchangeResource")
                    if token_exchange_resource:
                        context = TurnContext(self._adapter, activity)
                        context.turn_state["BotIdentity"] = claims_identity

                        # We need to know what connection name to use for the token exchange so we figure that out here
                        connection_name = (
                            self._configuration.SSO_CONNECTION_NAME
                            if target_skill.group == "Waterfall" else
                            self._configuration.SSO_CONNECTION_NAME_TEAMS)

                        if not connection_name:
                            raise ValueError(
                                "The SSO connection name cannot be null.")

                        # AAD token exchange
                        try:
                            uri = token_exchange_resource.get("uri")
                            result = await self._token_exchange_provider.exchange_token(
                                context,
                                connection_name,
                                activity.recipient.id,
                                TokenExchangeRequest(uri=uri),
                            )

                            if result.token:
                                # If token above is null, then SSO has failed and hence we return false.
                                # If not, send an invoke to the skill with the token.
                                return await self._send_token_exchange_invoke_to_skill(
                                    incoming_activity=activity,
                                    connection_name=oauth_card.get(
                                        "connectionName"),
                                    resource_id=token_exchange_resource.get(
                                        "id"),
                                    token=result.token,
                                    target_skill=target_skill,
                                )

                        except Exception as exception:
                            print(f"Unable to exchange token: {exception}")
                            traceback.print_exc()
                            return False

        return False
コード例 #26
0
 async def test_should_add_and_call_save_all_changes_on_a_single_plugin(
         self):
     adapter = TestAdapter()
     context = TurnContext(adapter, Activity())
     foo_state = BotStateMock({"foo": "bar"})
     bot_state_set = AutoSaveStateMiddleware().add(foo_state)
     await bot_state_set.bot_state_set.save_all_changes(context)
     assert foo_state.write_called, "write not called for plugin."
コード例 #27
0
 def test_should_not_be_able_to_set_responded_to_false(self):
     context = TurnContext(SimpleAdapter(), ACTIVITY)
     try:
         context.responded = False
     except ValueError:
         pass
     except Exception as error:
         raise error
コード例 #28
0
    async def test_send_message_to_teams_channel_works(self):
        adapter = SimpleAdapterWithCreateConversation()

        turn_context = TurnContext(adapter, ACTIVITY)
        result = await TeamsInfo.send_message_to_teams_channel(
            turn_context, ACTIVITY, "teamId123")
        assert result[0].activity_id == "new_conversation_id"
        assert result[1] == "reference123"
コード例 #29
0
    async def test_get_team_details_works_with_team_id(self):
        adapter = SimpleAdapterWithCreateConversation()
        team_id = "teamId123"
        ACTIVITY.channel_data = {"team": {"id": team_id}}
        turn_context = TurnContext(adapter, ACTIVITY)
        result = TeamsInfo.get_team_id(turn_context)

        assert result == team_id
コード例 #30
0
 def _get_context(utterance: str, bot_adapter: BotAdapter) -> TurnContext:
     activity = Activity(
         type=ActivityTypes.message,
         text=utterance,
         conversation=ConversationAccount(),
         recipient=ChannelAccount(),
         from_property=ChannelAccount(),
     )
     return TurnContext(bot_adapter, activity)