From 569df3422a71bae1fb513875da5321e117066af9 Mon Sep 17 00:00:00 2001 From: "Nikhil Chitlur Navakiran (from Dev Box)" Date: Wed, 29 Oct 2025 14:14:08 -0600 Subject: [PATCH 1/3] support S2S --- .../observability/core/config.py | 1 + .../core/exporters/agent365_exporter.py | 9 ++- tests/test_agent365_exporter.py | 74 +++++++++++++++++++ 3 files changed, 83 insertions(+), 1 deletion(-) diff --git a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py index 637497c2..3c20408c 100644 --- a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py +++ b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py @@ -117,6 +117,7 @@ def _configure_internal( exporter = Agent365Exporter( token_resolver=token_resolver, cluster_category=cluster_category, + use_s2s_endpoint=kwargs.pop("use_s2s_endpoint", False), **kwargs, ) else: diff --git a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py index 7671ac4c..7f06beb5 100644 --- a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +++ b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py @@ -44,6 +44,7 @@ def __init__( self, token_resolver: Callable[[str, str], str | None], cluster_category: str = "prod", + use_s2s_endpoint: bool = False, **kwargs: Any, ): if token_resolver is None: @@ -53,6 +54,7 @@ def __init__( self._lock = threading.Lock() self._token_resolver = token_resolver self._cluster_category = cluster_category + self.use_s2s_endpoint = use_s2s_endpoint # ------------- SpanExporter API ----------------- @@ -74,7 +76,12 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: # Resolve endpoint + token discovery = PowerPlatformApiDiscovery(self._cluster_category) endpoint = discovery.get_tenant_island_cluster_endpoint(tenant_id) - url = f"https://{endpoint}/maven/agent365/agents/{agent_id}/traces?api-version=1" + endpoint_path = ( + f"/maven/agent365/service/agents/{agent_id}/traces" + if self.use_s2s_endpoint + else f"/maven/agent365/agents/{agent_id}/traces" + ) + url = f"https://{endpoint}{endpoint_path}?api-version=1" headers = {"content-type": "application/json"} try: diff --git a/tests/test_agent365_exporter.py b/tests/test_agent365_exporter.py index a63b24ee..5e272d66 100644 --- a/tests/test_agent365_exporter.py +++ b/tests/test_agent365_exporter.py @@ -213,6 +213,80 @@ def test_partitioning_by_scope(self): expected_scopes = [("scope.a", 2), ("scope.b", 1), ("scope.c", 1)] self.assertEqual(scope_data, expected_scopes) + def test_s2s_endpoint_path_when_enabled(self): + """Test 4: Test that S2S endpoint path is used when use_s2s_endpoint is True.""" + # Arrange - Create exporter with S2S endpoint enabled + s2s_exporter = Agent365Exporter( + token_resolver=self.mock_token_resolver, cluster_category="test", use_s2s_endpoint=True + ) + + spans = [self._create_mock_span("s2s_span")] + + # Mock the PowerPlatformApiDiscovery class + with patch( + "microsoft_agents_a365.observability.core.exporters.agent365_exporter.PowerPlatformApiDiscovery" + ) as mock_discovery_class: + mock_discovery = Mock() + mock_discovery.get_tenant_island_cluster_endpoint.return_value = "test-endpoint.com" + mock_discovery_class.return_value = mock_discovery + + # Mock the _post_with_retries method + with patch.object(s2s_exporter, "_post_with_retries", return_value=True) as mock_post: + # Act + result = s2s_exporter.export(spans) + + # Assert + self.assertEqual(result, SpanExportResult.SUCCESS) + mock_post.assert_called_once() + + # Verify the call arguments - should use S2S path + args, kwargs = mock_post.call_args + url, body, headers = args + + self.assertIn("test-endpoint.com", url) + self.assertIn("/maven/agent365/service/agents/test-agent-456/traces", url) + self.assertNotIn("/maven/agent365/agents/test-agent-456/traces", url) + self.assertEqual(headers["authorization"], "Bearer test_token_123") + self.assertEqual(headers["content-type"], "application/json") + + def test_default_endpoint_path_when_s2s_disabled(self): + """Test 5: Test that default endpoint path is used when use_s2s_endpoint is False.""" + # Arrange - Create exporter with S2S endpoint disabled (default behavior) + default_exporter = Agent365Exporter( + token_resolver=self.mock_token_resolver, cluster_category="test", use_s2s_endpoint=False + ) + + spans = [self._create_mock_span("default_span")] + + # Mock the PowerPlatformApiDiscovery class + with patch( + "microsoft_agents_a365.observability.core.exporters.agent365_exporter.PowerPlatformApiDiscovery" + ) as mock_discovery_class: + mock_discovery = Mock() + mock_discovery.get_tenant_island_cluster_endpoint.return_value = "test-endpoint.com" + mock_discovery_class.return_value = mock_discovery + + # Mock the _post_with_retries method + with patch.object( + default_exporter, "_post_with_retries", return_value=True + ) as mock_post: + # Act + result = default_exporter.export(spans) + + # Assert + self.assertEqual(result, SpanExportResult.SUCCESS) + mock_post.assert_called_once() + + # Verify the call arguments - should use default path + args, kwargs = mock_post.call_args + url, body, headers = args + + self.assertIn("test-endpoint.com", url) + self.assertIn("/maven/agent365/agents/test-agent-456/traces", url) + self.assertNotIn("/maven/agent365/service/agents/test-agent-456/traces", url) + self.assertEqual(headers["authorization"], "Bearer test_token_123") + self.assertEqual(headers["content-type"], "application/json") + if __name__ == "__main__": unittest.main() From e9d9dad25330e4835f91da100ea6d727786fb0ff Mon Sep 17 00:00:00 2001 From: "Nikhil Chitlur Navakiran (from Dev Box)" Date: Wed, 29 Oct 2025 15:31:38 -0600 Subject: [PATCH 2/3] use kwargs directly --- .../microsoft_agents_a365/observability/core/config.py | 1 - .../observability/core/exporters/agent365_exporter.py | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py index 3c20408c..637497c2 100644 --- a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py +++ b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/config.py @@ -117,7 +117,6 @@ def _configure_internal( exporter = Agent365Exporter( token_resolver=token_resolver, cluster_category=cluster_category, - use_s2s_endpoint=kwargs.pop("use_s2s_endpoint", False), **kwargs, ) else: diff --git a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py index 7f06beb5..3b753c05 100644 --- a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +++ b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py @@ -44,7 +44,6 @@ def __init__( self, token_resolver: Callable[[str, str], str | None], cluster_category: str = "prod", - use_s2s_endpoint: bool = False, **kwargs: Any, ): if token_resolver is None: @@ -54,7 +53,7 @@ def __init__( self._lock = threading.Lock() self._token_resolver = token_resolver self._cluster_category = cluster_category - self.use_s2s_endpoint = use_s2s_endpoint + self.use_s2s_endpoint = kwargs.pop("use_s2s_endpoint", False) # ------------- SpanExporter API ----------------- From 65589c9abfaae2c5b066db39ed5d21e23bca15d4 Mon Sep 17 00:00:00 2001 From: "Nikhil Chitlur Navakiran (from Dev Box)" Date: Wed, 29 Oct 2025 15:40:29 -0600 Subject: [PATCH 3/3] fix use of kwargs --- .../observability/core/exporters/agent365_exporter.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py index 3b753c05..bb0761ec 100644 --- a/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py +++ b/libraries/microsoft-agents-a365-observability-core/microsoft_agents_a365/observability/core/exporters/agent365_exporter.py @@ -44,7 +44,7 @@ def __init__( self, token_resolver: Callable[[str, str], str | None], cluster_category: str = "prod", - **kwargs: Any, + use_s2s_endpoint: bool = False, ): if token_resolver is None: raise ValueError("token_resolver must be provided.") @@ -53,7 +53,7 @@ def __init__( self._lock = threading.Lock() self._token_resolver = token_resolver self._cluster_category = cluster_category - self.use_s2s_endpoint = kwargs.pop("use_s2s_endpoint", False) + self._use_s2s_endpoint = use_s2s_endpoint # ------------- SpanExporter API ----------------- @@ -77,7 +77,7 @@ def export(self, spans: Sequence[ReadableSpan]) -> SpanExportResult: endpoint = discovery.get_tenant_island_cluster_endpoint(tenant_id) endpoint_path = ( f"/maven/agent365/service/agents/{agent_id}/traces" - if self.use_s2s_endpoint + if self._use_s2s_endpoint else f"/maven/agent365/agents/{agent_id}/traces" ) url = f"https://{endpoint}{endpoint_path}?api-version=1"