Skip to content

Conversation

@kcbiradar
Copy link

@kcbiradar kcbiradar commented Jan 15, 2026

This adds the capability to call any FGA endpoint.

What problem is being solved?
How is it being solved?
What changes are made to solve it?

References

closes

Review Checklist

  • I have clicked on "allow edits by maintainers".
  • I have added documentation for new/changed functionality in this PR or in a PR to openfga.dev [Provide a link to any relevant PRs in the references section above]
  • The correct base branch is being used, if not main
  • I have added tests to validate that the change in functionality is working as expected

Summary by CodeRabbit

Release Notes

  • New Features

    • Added raw_request method to OpenFgaClient for making custom HTTP requests to OpenFGA endpoints
    • Introduced RawResponse type with status, headers, and parsed body data
  • Bug Fixes

    • Fixed HTTP header handling in API client responses
  • Documentation

    • Added "Calling Other Endpoints" section with usage examples and code samples
  • Tests

    • Added comprehensive test coverage for raw_request functionality

✏️ Tip: You can customize this high-level summary in your review settings.

@kcbiradar kcbiradar requested review from a team as code owners January 15, 2026 13:41
@linux-foundation-easycla
Copy link

linux-foundation-easycla bot commented Jan 15, 2026

CLA Signed

The committers listed above are authorized under a signed CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

Walkthrough

This PR introduces a new raw_request method to the OpenFgaClient (both async and sync variants) enabling arbitrary HTTP requests against OpenFGA endpoints. It includes a new RawResponse data model, updates ApiClient header handling, adds comprehensive test coverage, and documents the feature in the README.

Changes

Cohort / File(s) Summary
New RawResponse Model
openfga_sdk/client/models/raw_response.py, openfga_sdk/client/models/__init__.py
New dataclass RawResponse with status, headers, and body fields; includes json() and text() convenience methods for parsing response bodies. Exported in public API.
Core raw_request Implementation
openfga_sdk/client/client.py, openfga_sdk/sync/client/client.py
Added async and sync raw_request() methods enabling configurable HTTP requests with method, path, query/path params, headers, body, and operation_name. Includes parameter validation, path variable resolution (auto-fill store_id), URL encoding, query string building, JSON serialization, default headers, and integration with ApiClient for authentication and telemetry.
ApiClient Header Response Handling
openfga_sdk/api_client.py, openfga_sdk/sync/api_client.py
Changed header return value from response_data.headers to response_data.getheaders() when _return_http_data_only is False, altering how HTTP headers are exposed.
Telemetry Guard
openfga_sdk/telemetry/attributes.py
Added type guard isinstance(response.body, dict) before accessing body fields in TelemetryAttributes.fromResponse() to prevent errors with non-dict body types.
Test Coverage
test/client/client_test.py, test/sync/client/client_test.py
Comprehensive test suite covering POST/GET requests, path/query parameter handling, store_id auto-substitution, required field validation, default headers, URL encoding, list query params expansion, and RawResponse helper methods.
Documentation
README.md
New "Calling Other Endpoints" section documenting raw_request with usage scenarios and multiple Python examples (custom endpoints, response decoding, GET calls, path parameters).

Sequence Diagram(s)

sequenceDiagram
    participant Client as OpenFgaClient
    participant ApiClient as ApiClient
    participant HTTP as HTTP Layer
    participant Response as Response Parser

    Client->>Client: raw_request(method, path, params...)
    Client->>Client: Validate operation_name (required)
    Client->>Client: Resolve path params (auto-fill store_id)
    Client->>Client: URL-encode path params
    Client->>Client: Build query string
    Client->>Client: Serialize body (JSON if dict/list)
    Client->>Client: Set default headers (Content-Type, Accept)
    Client->>ApiClient: call_api(method, path, body, headers, query_params)
    ApiClient->>HTTP: Execute HTTP request
    HTTP->>ApiClient: Return HTTP response
    ApiClient->>Response: Parse response body
    Response->>Response: Try JSON decode, fallback to raw
    Response->>Client: Return RawResponse(status, headers, body)
    Client->>Client: Return RawResponse to caller
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related issues

Possibly related PRs

  • feat: improve error messaging #245: Modifies ApiClient.__call_api() header handling and exception propagation, overlapping with this PR's changes to the same ApiClient method.

Suggested reviewers

  • ttrzeng
  • daniel-jonathan
  • rhamzeh
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 85.71% which is sufficient. The required threshold is 80.00%.
Title check ✅ Passed The title 'feat: add raw requestor for calling arbitrary endpoints' directly and clearly summarizes the main change—introducing a raw_request method to call any FGA endpoint. It accurately reflects the primary objective of the pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@dosubot
Copy link

dosubot bot commented Jan 15, 2026

Related Documentation

Checked 6 published document(s) in 1 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

@SoulPancake SoulPancake changed the title feat/add-raw-request-method feat: add raw requestor for calling arbitrary endpoints Jan 15, 2026
@kcbiradar kcbiradar force-pushed the feat/add-raw-request-method branch from 840bc79 to f4dec14 Compare January 15, 2026 13:53
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
README.md (1)

4-4: Malformed badge URL - missing closing parenthesis.

The Socket badge URL appears to be malformed. It's missing a closing parenthesis before the opening bracket for the link URL.

🔧 Suggested fix
-[![Socket Badge](https://badge.socket.dev/pypi/package/openfga-sdk/0.9.9](https://socket.dev/pypi/package/openfga-sdk)
+[![Socket Badge](https://badge.socket.dev/pypi/package/openfga-sdk/0.9.9)](https://socket.dev/pypi/package/openfga-sdk)
🤖 Fix all issues with AI agents
In `@openfga_sdk/sync/client/client.py`:
- Around line 1233-1234: The except block "except ApiException as e: raise" is
redundant and should be removed; locate the handler in
openfga_sdk/sync/client/client.py where ApiException is caught and simply delete
that except clause so the original ApiException will propagate naturally (leave
surrounding try/except structure and other exception handlers unchanged and
ensure indentation remains valid).
🧹 Nitpick comments (6)
openfga_sdk/client/models/raw_response.py (1)

49-63: Consider moving json import to module level.

The json module is imported inside the method body (lines 50 and 58). Since json is part of Python's standard library and always available, importing it at the module level (line 8-9 area) would be more idiomatic and slightly more efficient for repeated calls.

♻️ Suggested refactor

Add import at the top of the file:

 from dataclasses import dataclass
 from typing import Any
+import json

Then remove the inline imports in json() and text() methods.

openfga_sdk/sync/client/client.py (2)

1139-1141: Consider validating options["headers"] type before merging.

The code assumes options["headers"] is a dict when calling update(). For consistency with the set_heading_if_not_set helper (lines 102-103) which handles non-dict headers values, consider adding a type check.

♻️ Suggested defensive check
         request_headers = dict(headers) if headers else {}
         if options and options.get("headers"):
-            request_headers.update(options["headers"])
+            opt_headers = options.get("headers")
+            if isinstance(opt_headers, dict):
+                request_headers.update(opt_headers)

1196-1199: Header checks are case-sensitive but HTTP headers are case-insensitive.

The checks "Content-Type" not in request_headers won't match if a user provides "content-type" (lowercase). This could result in duplicate headers with different casing.

♻️ Suggested case-insensitive check
+        # Case-insensitive header check helper
+        request_headers_lower = {k.lower(): k for k in request_headers}
+        
-        if "Content-Type" not in request_headers:
+        if "content-type" not in request_headers_lower:
             request_headers["Content-Type"] = "application/json"
-        if "Accept" not in request_headers:
+        if "accept" not in request_headers_lower:
             request_headers["Accept"] = "application/json"
test/sync/client/client_test.py (1)

4151-4454: Good raw_request coverage; consider 2 small edge-case tests.

  1. Assert that custom headers passed to raw_request(..., headers=...) are actually forwarded (e.g., verify X-Experimental-Feature is present in the mocked call headers).
  2. Add a non-JSON response body case (e.g., content-type: text/plain) to validate RawResponse.text()/json() behavior when JSON parsing should fail.
openfga_sdk/client/client.py (2)

1140-1143: Define header precedence + coerce header values to strings.

Right now options["headers"] overrides the explicit headers argument (Line 1140-1143), and non-string values can slip in (your tests cover integer header values elsewhere). It’d be good to (a) document precedence, and (b) normalize values to str before sending.


1209-1234: Tighten telemetry + response typing.

  • TelemetryAttribute import inside the method is unused (Line 1211).
  • TelemetryAttributes.fga_client_request_method is set to operation_name.lower() (Line 1213-1214). Please confirm this attribute is meant to hold the operation name vs the HTTP verb (since you already have method).
  • Response parsing should allow top-level JSON arrays too (currently annotated as dict[str, Any], but json.loads(...) can return list[Any]). Consider widening the type and aligning RawResponse.body accordingly.

Also applies to: 1242-1265

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a67559 and 7d1ec7a.

📒 Files selected for processing (10)
  • README.md
  • openfga_sdk/api_client.py
  • openfga_sdk/client/client.py
  • openfga_sdk/client/models/__init__.py
  • openfga_sdk/client/models/raw_response.py
  • openfga_sdk/sync/api_client.py
  • openfga_sdk/sync/client/client.py
  • openfga_sdk/telemetry/attributes.py
  • test/client/client_test.py
  • test/sync/client/client_test.py
🧰 Additional context used
🧬 Code graph analysis (6)
openfga_sdk/client/models/__init__.py (1)
openfga_sdk/client/models/raw_response.py (1)
  • RawResponse (13-90)
openfga_sdk/sync/api_client.py (3)
openfga_sdk/rest.py (1)
  • getheaders (111-115)
openfga_sdk/sync/rest.py (1)
  • getheaders (111-115)
test/error_handling_test.py (1)
  • getheaders (31-32)
openfga_sdk/api_client.py (3)
openfga_sdk/rest.py (1)
  • getheaders (111-115)
openfga_sdk/sync/rest.py (1)
  • getheaders (111-115)
test/error_handling_test.py (1)
  • getheaders (31-32)
test/client/client_test.py (1)
openfga_sdk/client/models/raw_response.py (3)
  • RawResponse (13-90)
  • json (33-65)
  • text (67-90)
test/sync/client/client_test.py (2)
openfga_sdk/client/models/raw_response.py (3)
  • RawResponse (13-90)
  • json (33-65)
  • text (67-90)
openfga_sdk/rest.py (6)
  • RESTClientObject (128-461)
  • response (56-60)
  • response (63-67)
  • status (84-88)
  • status (91-95)
  • close (162-166)
openfga_sdk/client/client.py (6)
openfga_sdk/client/models/raw_response.py (2)
  • json (33-65)
  • RawResponse (13-90)
openfga_sdk/exceptions.py (2)
  • ApiException (118-298)
  • FgaValidationException (36-61)
openfga_sdk/sync/rest.py (5)
  • RESTResponse (27-125)
  • data (70-74)
  • data (77-81)
  • status (84-88)
  • status (91-95)
openfga_sdk/telemetry/attributes.py (3)
  • get (97-104)
  • TelemetryAttribute (14-16)
  • TelemetryAttributes (19-369)
openfga_sdk/api_client.py (2)
  • get_store_id (937-941)
  • update_params_for_auth (771-810)
openfga_sdk/sync/api_client.py (2)
  • get_store_id (937-941)
  • update_params_for_auth (771-810)
🔇 Additional comments (17)
openfga_sdk/client/models/__init__.py (1)

26-26: LGTM!

The RawResponse import and export follow the established pattern in this module. This correctly exposes the new model for downstream usage.

Also applies to: 49-49

openfga_sdk/api_client.py (1)

420-423: LGTM!

The change from response_data.headers to response_data.getheaders() ensures consistent header access and returns a proper dict[str, str] that aligns with the new RawResponse model's expected header format.

openfga_sdk/sync/api_client.py (1)

420-423: LGTM!

Consistent with the async client change, ensuring both sync and async variants use the same getheaders() method for header access.

openfga_sdk/telemetry/attributes.py (1)

298-302: Good defensive fix for handling non-dict response bodies.

The added isinstance(response.body, dict) check properly guards against AttributeError when body is bytes, str, or None - which is possible with the new RawResponse model where body type is bytes | str | dict[str, Any] | None.

README.md (2)

51-51: LGTM!

Good addition to the table of contents for the new feature section.


1264-1400: Comprehensive documentation for the new raw_request feature.

The documentation is well-structured with clear use cases and multiple examples covering:

  • Basic POST requests
  • Response handling (JSON parsing, accessing headers/status)
  • GET requests with query parameters
  • Path parameter substitution (explicit and automatic store_id)

The examples follow the existing documentation patterns in this README.

openfga_sdk/client/models/raw_response.py (1)

33-90: LGTM! The helper methods are well-implemented.

The json() and text() methods handle all expected body types (dict, str, bytes, None) with appropriate error handling and fallbacks. The UnicodeDecodeError handling with errors="replace" in text() is a good defensive pattern.

openfga_sdk/sync/client/client.py (3)

1-4: LGTM! Imports are appropriate for the new functionality.

The new imports (json, urllib.parse, RawResponse, ApiException, RESTResponse) are all necessary for the raw_request method implementation.

Also applies to: 38-38, 46-46, 77-77


1146-1167: LGTM! Path parameter handling is well-implemented.

The logic correctly:

  • Auto-fills store_id from configuration when not provided in path_params
  • URL-encodes path parameter values for safety
  • Validates that all placeholders are resolved before making the request

1243-1265: LGTM! Response body parsing handles all expected formats.

The parsing logic correctly handles JSON responses (both string and bytes) with appropriate fallbacks for non-JSON content. The final RawResponse construction properly captures status, headers, and body.

test/client/client_test.py (5)

28-28: LGTM! Import is correctly placed.

The RawResponse import is appropriately added alongside other model imports.


4170-4218: LGTM! Comprehensive test for POST request with body.

This test verifies:

  • RawResponse creation and properties (status, headers, body)
  • json() and text() helper methods work correctly
  • The API is called with correct method, path, body, query params, and headers

4330-4379: LGTM! Validation error tests cover required scenarios.

The tests properly verify that FgaValidationException is raised for:

  • Missing operation_name (required parameter)
  • Missing store_id when path contains {store_id}
  • Missing path parameters when placeholders exist

4381-4413: LGTM! List query parameter expansion test.

This test verifies that list values in query_params are correctly expanded into multiple key-value pairs (e.g., {"ids": ["id1", "id2", "id3"]} becomes [("ids", "id1"), ("ids", "id2"), ("ids", "id3")]).


4444-4475: LGTM! URL encoding test verifies special character handling.

The test confirms that path parameters containing spaces and special characters (value with spaces & special chars) are properly URL-encoded to value%20with%20spaces%20%26%20special%20chars.

test/sync/client/client_test.py (1)

26-26: RawResponse import looks fine.

openfga_sdk/client/client.py (1)

3-6: No issues with the new imports.

Also applies to: 39-40, 46-51, 76-76

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@kcbiradar
Copy link
Author

@rhamzeh @SoulPancake, please review and suggest if any changes are required.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature] Add raw request method to Python SDK for calling arbitrary API endpoints

1 participant