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
3 changes: 3 additions & 0 deletions openfga_sdk/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ async def __call_api(
response=e.body.decode("utf-8"),
credentials=self.configuration.credentials,
attributes=_telemetry_attributes,
start=start,
)

self._telemetry.metrics.request(
Expand Down Expand Up @@ -323,6 +324,7 @@ async def __call_api(
response=e,
credentials=self.configuration.credentials,
attributes=_telemetry_attributes,
start=start,
)

self._telemetry.metrics.request(
Expand All @@ -349,6 +351,7 @@ async def __call_api(
response=response_data,
credentials=self.configuration.credentials,
attributes=_telemetry_attributes,
start=start,
)

self._telemetry.metrics.request(
Expand Down
23 changes: 23 additions & 0 deletions openfga_sdk/client/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@

from openfga_sdk.configuration import Configuration
from openfga_sdk.exceptions import FgaValidationException
from openfga_sdk.telemetry.attributes import TelemetryAttribute
from openfga_sdk.telemetry.configuration import (
TelemetryConfigurationType,
TelemetryMetricConfiguration,
TelemetryMetricsConfiguration,
)
from openfga_sdk.telemetry.counters import TelemetryCounter
from openfga_sdk.telemetry.histograms import TelemetryHistogram
from openfga_sdk.validation import is_well_formed_ulid_string


Expand All @@ -31,6 +39,20 @@ def __init__(
ssl_ca_cert=None,
api_url=None, # TODO: restructure when removing api_scheme/api_host
timeout_millisec: int | None = None,
telemetry: (
dict[
TelemetryConfigurationType | str,
TelemetryMetricsConfiguration
| dict[
TelemetryHistogram | TelemetryCounter | str,
TelemetryMetricConfiguration
| dict[TelemetryAttribute | str, bool]
| None,
]
| None,
]
| None
) = None,
):
super().__init__(
api_scheme,
Expand All @@ -41,6 +63,7 @@ def __init__(
ssl_ca_cert=ssl_ca_cert,
api_url=api_url,
timeout_millisec=timeout_millisec,
telemetry=telemetry,
)
self._authorization_model_id = authorization_model_id

Expand Down
3 changes: 3 additions & 0 deletions openfga_sdk/sync/api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ def __call_api(
response=e.body.decode("utf-8"),
credentials=self.configuration.credentials,
attributes=_telemetry_attributes,
start=start,
)

self._telemetry.metrics.request(
Expand Down Expand Up @@ -323,6 +324,7 @@ def __call_api(
response=e,
credentials=self.configuration.credentials,
attributes=_telemetry_attributes,
start=start,
)

self._telemetry.metrics.request(
Expand All @@ -349,6 +351,7 @@ def __call_api(
response=response_data,
credentials=self.configuration.credentials,
attributes=_telemetry_attributes,
start=start,
)

self._telemetry.metrics.request(
Expand Down
6 changes: 6 additions & 0 deletions openfga_sdk/telemetry/attributes.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ def fromResponse(
) = None,
credentials: Credentials | None = None,
attributes: dict[TelemetryAttribute, str | bool | int | float] | None = None,
start: float | None = None,
) -> dict[TelemetryAttribute, str | bool | int | float]:
response_model_id = None
response_query_duration = None
Expand All @@ -295,6 +296,11 @@ def fromResponse(
if attributes is not None:
_attributes = attributes

if start is not None and start > 0:
_attributes[TelemetryAttributes.http_client_request_duration] = int(
(time.time() - start) * 1000
)

if isinstance(response, ApiException):
if response.status is not None:
_attributes[TelemetryAttributes.http_response_status_code] = int(
Expand Down
7 changes: 4 additions & 3 deletions openfga_sdk/telemetry/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from openfga_sdk.telemetry.configuration import (
TelemetryConfiguration,
TelemetryMetricConfiguration,
TelemetryMetricsConfiguration,
isMetricEnabled,
)
from openfga_sdk.telemetry.counters import TelemetryCounter, TelemetryCounters
Expand Down Expand Up @@ -90,7 +91,7 @@ def request(

if (
isinstance(configuration, TelemetryConfiguration)
and isinstance(configuration.metrics, TelemetryMetricConfiguration)
and isinstance(configuration.metrics, TelemetryMetricsConfiguration)
and isinstance(
configuration.metrics.fga_client_request,
TelemetryMetricConfiguration,
Expand Down Expand Up @@ -127,7 +128,7 @@ def credentialsRequest(

if (
isinstance(configuration, TelemetryConfiguration)
and isinstance(configuration.metrics, TelemetryMetricConfiguration)
and isinstance(configuration.metrics, TelemetryMetricsConfiguration)
and isinstance(
configuration.metrics.fga_client_credentials_request,
TelemetryMetricConfiguration,
Expand Down Expand Up @@ -178,7 +179,7 @@ def requestDuration(

if (
isinstance(configuration, TelemetryConfiguration)
and isinstance(configuration.metrics, TelemetryMetricConfiguration)
and type(configuration.metrics) is TelemetryMetricsConfiguration
and isinstance(
configuration.metrics.fga_client_request_duration,
TelemetryMetricConfiguration,
Expand Down
11 changes: 8 additions & 3 deletions test/rest_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,9 @@ async def test_stream_exception_in_chunks():

class FakeContent:
async def iter_chunks(self):
raise ValueError("Boom!")
if True: # This ensures the coroutine is actually created and awaited
raise ValueError("Boom!")
yield (b"", None) # This line is never reached

mock_response = MagicMock()
mock_response.status = 200
Expand All @@ -357,8 +359,11 @@ async def iter_chunks(self):
client.close = AsyncMock()

results = []
async for item in client.stream("GET", "http://example.com"):
results.append(item)
try:
async for item in client.stream("GET", "http://example.com"):
results.append(item)
except ValueError:
pass

assert results == []
client.handle_response_exception.assert_awaited_once()
Expand Down
14 changes: 12 additions & 2 deletions test/telemetry/attributes_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ def test_from_request_without_optional_params(telemetry_attributes):


def test_from_response_with_http_response(telemetry_attributes):
start_time = time.time() - 5
response = MagicMock(spec=HTTPResponse)
response.status = 200
response.getheader.side_effect = lambda header: {
Expand All @@ -135,16 +136,21 @@ def test_from_response_with_http_response(telemetry_attributes):
configuration=CredentialConfiguration(client_id="client_123"),
)
attributes = telemetry_attributes.fromResponse(
response=response, credentials=credentials
response=response,
credentials=credentials,
start=start_time,
)

assert attributes[TelemetryAttributes.http_response_status_code] == 200
assert attributes[TelemetryAttributes.fga_client_response_model_id] == "model_123"
assert attributes[TelemetryAttributes.http_server_request_duration] == "50"
assert attributes[TelemetryAttributes.fga_client_request_client_id] == "client_123"
assert TelemetryAttributes.http_client_request_duration in attributes
assert attributes[TelemetryAttributes.http_client_request_duration] > 0


def test_from_response_with_rest_response(telemetry_attributes):
start_time = time.time() - 5
response = MagicMock(spec=RESTResponse)
response.status = 404
response.headers = {
Expand All @@ -159,13 +165,17 @@ def test_from_response_with_rest_response(telemetry_attributes):
configuration=CredentialConfiguration(client_id="client_456"),
)
attributes = telemetry_attributes.fromResponse(
response=response, credentials=credentials
response=response,
credentials=credentials,
start=start_time,
)

assert attributes[TelemetryAttributes.http_response_status_code] == 404
assert attributes[TelemetryAttributes.fga_client_response_model_id] == "model_404"
assert attributes[TelemetryAttributes.http_server_request_duration] == "100"
assert attributes[TelemetryAttributes.fga_client_request_client_id] == "client_456"
assert TelemetryAttributes.http_client_request_duration in attributes
assert attributes[TelemetryAttributes.http_client_request_duration] > 0


def test_from_body_with_batch_check(telemetry_attributes):
Expand Down
Loading