Repository: microsoft/botbuilder-python

Issue:
[PORT] Fix errors when From is null in telemetry
> Port this change from botbuilder-dotnet/master branch:
https://github.com/microsoft/botbuilder-dotnet/pull/3436

Fixes: #3395 

If `TelemetryLoggerMiddleware` is used and then `CreateConversationAsync()` is called, there are cases where `Activity.From` will be null, which causes things like this to throw:

```csharp
{ TelemetryConstants.FromIdProperty, activity.From.Id },
```

**Changes**:

Adds a few null-conditional operators, where appropriate.

**Testing**:

Added one test which calls `CreateConversationAsync()` while ensuring the `Activity.From` is null.


# Changed projects
* Microsoft.Bot.Builder
* integration
* Microsoft.Bot.Builder.Tests






Discussion:

Resolving patch:
diff --git a/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py b/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py
--- a/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py
+++ b/libraries/botbuilder-core/botbuilder/core/telemetry_logger_middleware.py
@@ -160,15 +160,21 @@ async def fill_receive_event_properties(
         BotTelemetryClient.track_event method for the BotMessageReceived event.
         """
         properties = {
-            TelemetryConstants.FROM_ID_PROPERTY: activity.from_property.id,
+            TelemetryConstants.FROM_ID_PROPERTY: activity.from_property.id
+            if activity.from_property
+            else None,
             TelemetryConstants.CONVERSATION_NAME_PROPERTY: activity.conversation.name,
             TelemetryConstants.LOCALE_PROPERTY: activity.locale,
             TelemetryConstants.RECIPIENT_ID_PROPERTY: activity.recipient.id,
-            TelemetryConstants.RECIPIENT_NAME_PROPERTY: activity.from_property.name,
+            TelemetryConstants.RECIPIENT_NAME_PROPERTY: activity.recipient.name,
         }
 
         if self.log_personal_information:
-            if activity.from_property.name and activity.from_property.name.strip():
+            if (
+                activity.from_property
+                and activity.from_property.name
+                and activity.from_property.name.strip()
+            ):
                 properties[
                     TelemetryConstants.FROM_NAME_PROPERTY
                 ] = activity.from_property.name


Test patch:
diff --git a/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py b/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py
--- a/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py
+++ b/libraries/botbuilder-core/botbuilder/core/adapters/test_adapter.py
@@ -7,6 +7,7 @@
 
 import asyncio
 import inspect
+import uuid
 from datetime import datetime
 from uuid import uuid4
 from typing import Awaitable, Coroutine, Dict, List, Callable, Union
@@ -215,6 +216,19 @@ async def continue_conversation(
             reference, callback, bot_id, claims_identity, audience
         )
 
+    async def create_conversation(
+        self, channel_id: str, callback: Callable  # pylint: disable=unused-argument
+    ):
+        self.activity_buffer.clear()
+        update = Activity(
+            type=ActivityTypes.conversation_update,
+            members_added=[],
+            members_removed=[],
+            conversation=ConversationAccount(id=str(uuid.uuid4())),
+        )
+        context = TurnContext(self, update)
+        return await callback(context)
+
     async def receive_activity(self, activity):
         """
         INTERNAL: called by a `TestFlow` instance to simulate a user sending a message to the bot.
diff --git a/libraries/botbuilder-core/tests/test_telemetry_middleware.py b/libraries/botbuilder-core/tests/test_telemetry_middleware.py
--- a/libraries/botbuilder-core/tests/test_telemetry_middleware.py
+++ b/libraries/botbuilder-core/tests/test_telemetry_middleware.py
@@ -3,6 +3,7 @@
 
 # pylint: disable=line-too-long,missing-docstring,unused-variable
 import copy
+import uuid
 from typing import Dict
 from unittest.mock import Mock
 import aiounittest
@@ -28,6 +29,47 @@ async def test_create_middleware(self):
         my_logger = TelemetryLoggerMiddleware(telemetry, True)
         assert my_logger
 
+    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)
+
     async def test_should_send_receive(self):
         telemetry = Mock()
         my_logger = TelemetryLoggerMiddleware(telemetry, True)
@@ -55,7 +97,7 @@ async def logic(context: TurnContext):
                     "conversationName": None,
                     "locale": None,
                     "recipientId": "bot",
-                    "recipientName": "user",
+                    "recipientName": "Bot",
                 },
             ),
             (
@@ -76,7 +118,7 @@ async def logic(context: TurnContext):
                     "conversationName": None,
                     "locale": None,
                     "recipientId": "bot",
-                    "recipientName": "user",
+                    "recipientName": "Bot",
                     "fromName": "user",
                 },
             ),
@@ -147,7 +189,7 @@ async def process(context: TurnContext) -> None:
                     "conversationName": None,
                     "locale": None,
                     "recipientId": "bot",
-                    "recipientName": "user",
+                    "recipientName": "Bot",
                 },
             ),
             (
@@ -169,7 +211,7 @@ async def process(context: TurnContext) -> None:
                     "conversationName": None,
                     "locale": None,
                     "recipientId": "bot",
-                    "recipientName": "user",
+                    "recipientName": "Bot",
                     "fromName": "user",
                 },
             ),