Repository: microsoft/botbuilder-python
Pull request #360 (2019-10-24 22:49:46)

Merge master into 4.5.0b5
* Apply conversation reference in TurnContext.update_activity
* Trace activity helper in turn context


Patch:
diff --git a/libraries/botbuilder-core/botbuilder/core/turn_context.py b/libraries/botbuilder-core/botbuilder/core/turn_context.py
index 99d53996..75bb278d 100644
--- a/libraries/botbuilder-core/botbuilder/core/turn_context.py
+++ b/libraries/botbuilder-core/botbuilder/core/turn_context.py
@@ -3,8 +3,15 @@
 
 import re
 from copy import copy
+from datetime import datetime
 from typing import List, Callable, Union, Dict
-from botbuilder.schema import Activity, ConversationReference, Mention, ResourceResponse
+from botbuilder.schema import (
+    Activity,
+    ActivityTypes,
+    ConversationReference,
+    Mention,
+    ResourceResponse,
+)
 
 
 class TurnContext:
@@ -173,9 +180,11 @@ class TurnContext:
         :param activity:
         :return:
         """
+        reference = TurnContext.get_conversation_reference(self.activity)
+
         return await self._emit(
             self._on_update_activity,
-            activity,
+            TurnContext.apply_conversation_reference(activity, reference),
             self.adapter.update_activity(self, activity),
         )
 
@@ -240,9 +249,23 @@ class TurnContext:
                 raise error
 
         await emit_next(0)
-        # This should be changed to `return await logic()`
+        # logic does not use parentheses because it's a coroutine
         return await logic
 
+    async def send_trace_activity(
+        self, name: str, value: object, value_type: str, label: str
+    ) -> ResourceResponse:
+        trace_activity = Activity(
+            type=ActivityTypes.trace,
+            timestamp=datetime.utcnow(),
+            name=name,
+            value=value,
+            value_type=value_type,
+            label=label,
+        )
+
+        return await self.send_activity(trace_activity)
+
     @staticmethod
     def get_conversation_reference(activity: Activity) -> ConversationReference:
         """


Test patch:
diff --git a/libraries/botbuilder-core/tests/test_turn_context.py b/libraries/botbuilder-core/tests/test_turn_context.py
index 8e7c6f40..d39da5d2 100644
--- a/libraries/botbuilder-core/tests/test_turn_context.py
+++ b/libraries/botbuilder-core/tests/test_turn_context.py
@@ -1,16 +1,18 @@
 # Copyright (c) Microsoft Corporation. All rights reserved.
 # Licensed under the MIT License.
 
+from typing import Callable, List
 import aiounittest
 
 from botbuilder.schema import (
     Activity,
+    ActivityTypes,
     ChannelAccount,
     ConversationAccount,
     Mention,
     ResourceResponse,
 )
-from botbuilder.core import BotAdapter, TurnContext
+from botbuilder.core import BotAdapter, MessageFactory, TurnContext
 
 ACTIVITY = Activity(
     id="1234",
@@ -33,18 +35,19 @@ class SimpleAdapter(BotAdapter):
         assert activities
         for (idx, activity) in enumerate(activities):  # pylint: disable=unused-variable
             assert isinstance(activity, Activity)
-            assert activity.type == "message"
+            assert activity.type == "message" or activity.type == ActivityTypes.trace
             responses.append(ResourceResponse(id="5678"))
         return responses
 
     async def update_activity(self, context, activity):
         assert context is not None
         assert activity is not None
+        return ResourceResponse(id=activity.id)
 
     async def delete_activity(self, context, reference):
         assert context is not None
         assert reference is not None
-        assert reference.activity_id == "1234"
+        assert reference.activity_id == ACTIVITY.id
 
 
 class TestBotContext(aiounittest.AsyncTestCase):
@@ -225,6 +228,26 @@ class TestBotContext(aiounittest.AsyncTestCase):
         await context.update_activity(ACTIVITY)
         assert called is True
 
+    async def test_update_activity_should_apply_conversation_reference(self):
+        activity_id = "activity ID"
+        context = TurnContext(SimpleAdapter(), ACTIVITY)
+        called = False
+
+        async def update_handler(context, activity, next_handler_coroutine):
+            nonlocal called
+            called = True
+            assert context is not None
+            assert activity.id == activity_id
+            assert activity.conversation.id == ACTIVITY.conversation.id
+            await next_handler_coroutine()
+
+        context.on_update_activity(update_handler)
+        new_activity = MessageFactory.text("test text")
+        new_activity.id = activity_id
+        update_result = await context.update_activity(new_activity)
+        assert called is True
+        assert update_result.id == activity_id
+
     def test_get_conversation_reference_should_return_valid_reference(self):
         reference = TurnContext.get_conversation_reference(ACTIVITY)
 
@@ -298,3 +321,28 @@ class TestBotContext(aiounittest.AsyncTestCase):
 
         assert text, " test activity"
         assert activity.text, " test activity"
+
+    async def test_should_send_a_trace_activity(self):
+        context = TurnContext(SimpleAdapter(), ACTIVITY)
+        called = False
+
+        #  pylint: disable=unused-argument
+        async def aux_func(
+            ctx: TurnContext, activities: List[Activity], next: Callable
+        ):
+            nonlocal called
+            called = True
+            assert isinstance(activities, list), "activities not array."
+            assert len(activities) == 1, "invalid count of activities."
+            assert activities[0].type == ActivityTypes.trace, "type wrong."
+            assert activities[0].name == "name-text", "name wrong."
+            assert activities[0].value == "value-text", "value worng."
+            assert activities[0].value_type == "valueType-text", "valeuType wrong."
+            assert activities[0].label == "label-text", "label wrong."
+            return []
+
+        context.on_send_activities(aux_func)
+        await context.send_trace_activity(
+            "name-text", "value-text", "valueType-text", "label-text"
+        )
+        assert called