Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 6 additions & 8 deletions packages/http/httpx/kiota_http/httpx_request_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,16 +487,14 @@ async def _get_error_from_response(
)
attribute_span.set_attribute(ERROR_BODY_FOUND_KEY, bool(root_node))

if not root_node or not error_class:
return None
_get_obj_ctx = trace.set_span_in_context(_throw_failed_resp_span)
_get_obj_span = tracer.start_span("get_object_value", context=_get_obj_ctx)

if not root_node:
return None
error = None
if error_class:
error = root_node.get_object_value(error_class)
try:
return root_node.get_object_value(error_class)
finally:
_get_obj_span.end()
return error

async def throw_failed_responses(
self,
Expand Down Expand Up @@ -575,7 +573,7 @@ async def throw_failed_responses(
exc = APIError(
(
"The server returned an unexpected status code and the error registered"
f" for this code failed to deserialize: {type(error)}"
f" for this code failed to deserialize: {response_status_code}"
),
response_status_code,
response_headers, # type: ignore
Expand Down
54 changes: 50 additions & 4 deletions packages/http/httpx/tests/test_httpx_request_adapter.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,13 @@
SerializationWriterFactoryRegistry,
)
from opentelemetry import trace
from opentelemetry.sdk.trace import SpanProcessor, TracerProvider

from kiota_http.httpx_request_adapter import HttpxRequestAdapter
from kiota_http.middleware import REQUEST_OPTIONS_KEY
from kiota_http.middleware.options import ResponseHandlerOption

from .helpers import MockResponseObject
from .helpers import MockErrorObject, MockResponseObject

APPLICATION_JSON = "application/json"
BASE_URL = "https://graph.microsoft.com"
Expand Down Expand Up @@ -237,9 +238,54 @@ async def test_throw_failed_responses_not_apierror(
span = mock_otel_span
await request_adapter.throw_failed_responses(resp, mock_error_500_map, span, span)
assert (
"The server returned an unexpected status code and the error registered"
" for this code failed to deserialize"
) in str(e.value.message)
str(e.value.message) == "The server returned an unexpected status code and the error"
" registered for this code failed to deserialize: 500"
)


@pytest.mark.asyncio
async def test_throw_failed_responses_empty_error_body(
request_adapter, mock_apierror_XXX_map, mock_otel_span
):
resp = httpx.Response(status_code=503)
assert request_adapter.get_response_content_type(resp) is None

with pytest.raises(APIError) as e:
span = mock_otel_span
await request_adapter.throw_failed_responses(resp, mock_apierror_XXX_map, span, span)
assert (
str(e.value.message) == "The server returned an unexpected status code and the error"
" registered for this code failed to deserialize: 503"
)
assert e.value.response_status_code == 503


@pytest.mark.asyncio
@pytest.mark.parametrize(
"root_node", [
None,
MockErrorObject,
Mock(get_object_value=Mock(side_effect=ValueError("unexpected value"))),
],
ids=["no error body", "error body", "error body that fails to deserialize"]
)
async def test_throw_failed_responses_ends_every_span(
root_node, request_adapter, mock_apierror_XXX_map, mock_otel_span, monkeypatch
):
processor = Mock(spec=SpanProcessor)
provider = TracerProvider()
provider.add_span_processor(processor)
monkeypatch.setattr("kiota_http.httpx_request_adapter.tracer", provider.get_tracer(__name__))
request_adapter.get_root_parse_node = AsyncMock(return_value=root_node)
resp = httpx.Response(status_code=503, headers={"Content-Type": "application/json"})

with pytest.raises(APIError):
await request_adapter.throw_failed_responses(
resp, mock_apierror_XXX_map, mock_otel_span, mock_otel_span
)
started = [start.args[0] for start in processor.on_start.call_args_list]
assert "throw_failed_responses" in [s.name for s in started]
assert [s.name for s in started if s.end_time is None] == []


@pytest.mark.asyncio
Expand Down
Loading