Skip to content

Conversation

@rhamzeh
Copy link
Member

@rhamzeh rhamzeh commented Nov 19, 2025

Description

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 #255
generated from openfga/sdk-generator#660

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

  • New Features

    • Added support for StreamedListObjects endpoint
    • Introduced generic API Executor enabling calls to any OpenFGA API method with built-in retry logic and optional response decoding
  • Documentation

    • Added comprehensive guide section on calling other endpoints with usage examples and code snippets
  • Examples

    • Updated example code demonstrating the new API Executor functionality

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

@codecov-commenter
Copy link

codecov-commenter commented Nov 19, 2025

Codecov Report

❌ Patch coverage is 84.15301% with 29 lines in your changes missing coverage. Please review.
✅ Project coverage is 33.28%. Comparing base (b3d3a23) to head (fa58c97).

Files with missing lines Patch % Lines
api_executor.go 83.23% 22 Missing and 7 partials ⚠️

❌ Your project status has failed because the head coverage (33.28%) is below the target coverage (80.00%). You can increase the head coverage or adjust the target coverage.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #258      +/-   ##
==========================================
- Coverage   33.82%   33.28%   -0.54%     
==========================================
  Files         114      115       +1     
  Lines       10990     9735    -1255     
==========================================
- Hits         3717     3240     -477     
+ Misses       6911     6235     -676     
+ Partials      362      260     -102     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

A new APIExecutor feature enables arbitrary OpenFGA API endpoint calls via a builder pattern and fluent interface. The executor integrates with existing client retry logic, telemetry, and error handling. Updates include documentation, examples, core client methods, validation utilities, and comprehensive tests.

Changes

Cohort / File(s) Summary
Documentation
CHANGELOG.md, README.md
Updated changelog with new APIExecutor feature and consolidated StreamedListObjects entries; added "Calling Other Endpoints" documentation section with usage examples and table-of-contents entries.
API Client
api_client.go
Added public method GetAPIExecutor() to construct and return a new APIExecutor instance.
APIExecutor Implementation
api_executor.go
New file: Complete HTTP executor with request/response models, fluent builder pattern, Execute and ExecuteWithDecode entry points, validation, path/header/query parameter handling, retry logic with backoff, error handling, and telemetry integration.
APIExecutor Tests
api_executor_test.go
New file: Comprehensive test suite covering request validation, path/header/query building, response construction, builder chaining, retry logic, and edge cases.
Utilities
utils.go, utils_test.go
Added internal parameter validation helpers (validatePathParameter, validateParameter) with corresponding unit tests.
Examples
example/example1/example1.go
Added new example flows demonstrating APIExecutor usage: building custom requests with path parameters, headers, and decoding responses; preserved existing example flows with minor refactoring.

Sequence Diagram

sequenceDiagram
    participant Client as APIClient
    participant Executor as APIExecutor
    participant HTTPClient as HTTP Client
    participant Retry as Retry/Backoff
    participant Telemetry as Telemetry

    Client->>Executor: GetAPIExecutor() / NewAPIExecutor()
    Note over Client,Executor: Builder Pattern
    Client->>Executor: NewAPIExecutorRequestBuilder()
    Executor->>Executor: WithPathParameter/Query/Header/Body()
    Executor->>Executor: Build() → APIExecutorRequest
    
    Note over Client,Executor: Execution Flow
    Client->>Executor: Execute(ctx, request) or ExecuteWithDecode()
    Executor->>Executor: validateRequest()
    Executor->>Executor: buildPath(with URL-escaped params)
    Executor->>Executor: prepareHeaders(defaults + overrides)
    
    rect rgba(100, 149, 237, 0.2)
        Note over Executor,Retry: Retry Loop
        loop Attempt ≤ maxRetries
            Executor->>HTTPClient: Execute HTTP Request
            HTTPClient-->>Executor: Response (status, body, headers)
            Executor->>Executor: Read & Preserve Response Body
            alt Status < 300
                Executor->>Executor: Optional: decode(body → result)
                Executor->>Telemetry: recordTelemetry(success, duration)
                Executor-->>Client: *APIExecutorResponse, nil
            else Status ≥ 300
                Executor->>Executor: handleError(response)
                Executor->>Retry: determineRetry(error, attempt)
                alt Retry Eligible & Attempts Left
                    Executor->>Retry: Calculate Backoff
                    Retry-->>Executor: Wait Duration
                else No Retry
                    Executor-->>Client: *APIExecutorResponse, error
                end
            end
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Areas requiring extra attention:

  • api_executor.go core logic: Verify retry loop flow, error handling branches, and edge cases in executeInternal and helper functions (path building with URL escaping, header preparation defaults, retry determination logic)
  • Interaction with existing client components: Confirm correct integration with APIClient retry parameters, telemetry recording, and error handling
  • Builder pattern implementation: Verify nil-map initialization, parameter replacement semantics, and builder chaining correctness
  • Example code correctness: Ensure new APIExecutor examples in example1.go accurately demonstrate the feature and are consistent with documented usage patterns

Suggested reviewers

  • ewanharris
  • jimmyjames

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.71% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add APIExecutor for raw HTTP calls to OpenFGA endpoints' accurately and concisely summarizes the main change—introducing an APIExecutor to enable raw HTTP calls to OpenFGA endpoints.
Linked Issues check ✅ Passed The PR fully implements the requirements from issue #255: adds a raw request method (APIExecutor) leveraging SDK's authentication/configuration, provides escape hatch for new endpoints, and maintains typed methods as recommended approach.
Out of Scope Changes check ✅ Passed All changes are in scope: APIExecutor implementation, documentation, examples, tests, and utility functions directly support the feature objective. No unrelated modifications detected.

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.

@rhamzeh
Copy link
Member Author

rhamzeh commented Nov 19, 2025

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@rhamzeh rhamzeh force-pushed the poc/expose-api-endpoint branch 3 times, most recently from f6568ee to efd50fd Compare November 19, 2025 20:58
@rhamzeh rhamzeh changed the title feat: expose interface to call the API feat: add APIExecutor for raw HTTP calls to OpenFGA endpoints Nov 19, 2025
@rhamzeh rhamzeh force-pushed the poc/expose-api-endpoint branch from efd50fd to 17db863 Compare November 19, 2025 21:13
@rhamzeh rhamzeh force-pushed the poc/expose-api-endpoint branch from 17db863 to 8eb39ec Compare November 19, 2025 21:56
@rhamzeh rhamzeh force-pushed the poc/expose-api-endpoint branch from 8eb39ec to bce1207 Compare November 20, 2025 00:20
@rhamzeh rhamzeh marked this pull request as ready for review November 20, 2025 01:36
@rhamzeh rhamzeh requested review from a team as code owners November 20, 2025 01:36
Copilot AI review requested due to automatic review settings November 20, 2025 01:36
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a generic APIExecutor capability to the OpenFGA Go SDK, allowing users to make raw HTTP calls to any OpenFGA endpoint while still benefiting from the SDK's configuration, authentication, retry logic, telemetry, and error handling.

Key Changes

  • Introduces APIExecutor interface with Execute and ExecuteWithDecode methods for making raw API calls
  • Refactors existing endpoint methods (Check, Write, ListStores, etc.) to internally use the new APIExecutor, significantly reducing code duplication
  • Adds comprehensive test coverage for the new functionality with 19 test functions covering various scenarios

Reviewed Changes

Copilot reviewed 8 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
api_executor.go Core implementation of APIExecutor interface, request builder pattern, and execution logic with retry/telemetry
api_executor_test.go Comprehensive test suite with 1252 lines covering request building, validation, path handling, headers, and edge cases
api_client.go Adds GetAPIExecutor() method to expose the executor functionality
api_open_fga.go Refactors 17 endpoint methods to use APIExecutor, reducing ~2400 lines of duplicated code
utils.go Adds validation helper functions for path and body parameters
utils_test.go Tests for the new validation functions
example/example1/example1.go Demonstrates usage of custom executor with both decoded and raw response handling
README.md Documents the new feature with usage examples and use cases
CHANGELOG.md Records the new feature addition

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link

@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: 3

🧹 Nitpick comments (4)
README.md (1)

49-49: APIExecutor docs look solid; fix minor markdownlint and style nits

The new “Calling Other Endpoints” section clearly explains APIExecutor usage and matches the new API (GetAPIExecutor, NewAPIExecutorRequestBuilder, Execute/ExecuteWithDecode). A couple of small cleanups:

  • Lines 1126–1129 use hard tabs in the code block (WithQueryParameter / WithHeader lines), which markdownlint flags (MD010). Replacing them with spaces will keep linters happy.
  • The sentence on Line 1099 (“while still taking into account the configuration and handling authentication, telemetry, retry and error handling”) is slightly wordy; consider tightening to something like “while still honoring the client configuration (authentication, telemetry, retries, and error handling).”

These are purely documentation/lint polish; the technical content looks correct.

Also applies to: 1096-1178

utils.go (1)

343-356: Validation helpers are fine; consider handling typed nil pointers in future

The new validatePathParameter / validateParameter helpers centralize required-field checks and match the test expectations (only empty strings are rejected for path params; any non‑nil value passes validateParameter).

If you plan to use validateParameter with pointer-typed arguments (e.g., a *MyType that may be nil), note that value == nil won’t catch a typed nil pointer stored in an interface. If you later need that behavior, you might extend it with a small reflect check to treat (*T)(nil) as invalid too. Not required for current usages, but worth keeping in mind.

api_executor.go (1)

197-219: Consider validating unresolved path placeholders for clearer errors

validateRequest only checks that OperationName, Method, and Path are non‑empty, and buildPath blindly replaces any {key} occurrences it finds. If the caller forgets to pass a required path parameter (e.g. {store_id}), the request will go out with a literal {store_id} segment and fail server‑side, which is harder to diagnose.

As a usability improvement, you could:

  • After calling buildPath, detect remaining {...} placeholders (e.g. strings.Contains(path, "{")) and return a reportError indicating which parameter is missing; or
  • More generically, parse all {name} tokens from the template and ensure each has a non‑empty entry in PathParameters (possibly using validatePathParameter).

Not required for correctness, but would make the executor’s errors much more actionable.

example/example1/example1.go (1)

290-290: Minor: Inconsistent comment indentation.

The comment has a leading tab while other comments use consistent indentation. This is a minor formatting inconsistency.

Apply this diff:

-		// custom executor + decoded response
+	// custom executor + decoded response
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3d3a23 and bce1207.

📒 Files selected for processing (8)
  • CHANGELOG.md (1 hunks)
  • README.md (2 hunks)
  • api_client.go (1 hunks)
  • api_executor.go (1 hunks)
  • api_executor_test.go (1 hunks)
  • example/example1/example1.go (4 hunks)
  • utils.go (1 hunks)
  • utils_test.go (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
utils_test.go (1)
utils.go (1)
  • ToPtr (10-10)
api_executor_test.go (3)
api_client.go (2)
  • APIClient (40-47)
  • NewAPIClient (56-87)
internal/constants/constants.go (1)
  • TestApiUrl (26-26)
api_executor.go (5)
  • APIExecutorRequest (20-48)
  • APIExecutorRequestBuilder (51-53)
  • NewAPIExecutorRequestBuilder (59-70)
  • NewAPIExecutor (252-254)
  • APIExecutorResponse (136-148)
api_client.go (1)
api_executor.go (2)
  • APIExecutor (151-195)
  • NewAPIExecutor (252-254)
example/example1/example1.go (3)
client/client.go (2)
  • ClientCheckRequest (2040-2046)
  • ClientCheckOptions (2048-2054)
api_executor.go (1)
  • NewAPIExecutorRequestBuilder (59-70)
model_check_response.go (1)
  • CheckResponse (22-26)
api_executor.go (5)
api_client.go (1)
  • APIClient (40-47)
telemetry/telemetry.go (4)
  • Get (65-85)
  • GetMetrics (107-109)
  • TelemetryFactoryParameters (21-23)
  • Telemetry (16-19)
errors.go (3)
  • GenericOpenAPIError (13-17)
  • FgaApiRateLimitExceededError (632-653)
  • FgaApiInternalError (485-503)
internal/constants/constants.go (2)
  • DefaultMaxRetry (40-40)
  • DefaultMinWaitInMs (43-43)
internal/utils/retryutils/retryutils.go (1)
  • GetTimeToWait (89-107)
🪛 LanguageTool
README.md

[style] ~1099-~1099: ‘taking into account’ might be wordy. Consider a shorter alternative.
Context: ...ameters, body, and headers, while still taking into account the configuration and handling authenti...

(EN_WORDINESS_PREMIUM_TAKING_INTO_ACCOUNT)

🪛 markdownlint-cli2 (0.18.1)
README.md

1126-1126: Hard tabs
Column: 1

(MD010, no-hard-tabs)


1127-1127: Hard tabs
Column: 1

(MD010, no-hard-tabs)


1129-1129: Hard tabs
Column: 1

(MD010, no-hard-tabs)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: CodeQL analysis (go)
🔇 Additional comments (16)
CHANGELOG.md (1)

3-10: Changelog entries correctly reflect the new features

The Unreleased section accurately documents the new StreamedListObjects support and the generic fgaClient.GetAPIExecutor() feature, and the Makefile change is appropriately labeled as a chore. No issues from a release-notes perspective.

api_client.go (1)

217-221: LGTM! Clean integration point for the API executor.

The method provides a clear, well-documented entry point to the new APIExecutor functionality, enabling users to call arbitrary OpenFGA endpoints while reusing the SDK's authentication and configuration.

example/example1/example1.go (6)

5-5: LGTM! Required imports for the API executor demonstration.

The new imports support manual JSON unmarshaling and HTTP method constants used in the custom executor examples below.

Also applies to: 7-7


63-64: LGTM! Good refactoring to enable store ID reuse.

Extracting the store ID into a variable makes it available for the API executor demonstration later in the example.


246-246: LGTM! Standard error formatting.

Using %s with err.Error() is appropriate for string formatting.


253-259: LGTM! Good refactoring to eliminate duplication.

Extracting the check request into a variable enables reuse across multiple check operations (lines 259, 266, and 286).


282-296: LGTM! Clear demonstration of ExecuteWithDecode.

This example effectively demonstrates the API executor's ExecuteWithDecode method, showing how to build a custom request and decode the response directly into a typed structure.


298-309: LGTM! Demonstrates raw response handling.

This example effectively shows the alternative Execute method for cases where manual unmarshaling is preferred, complementing the ExecuteWithDecode example above.

api_executor_test.go (8)

18-48: LGTM! Clean and reusable test helpers.

The test infrastructure is well-designed with clear separation of concerns. The testRoundTripper, makeResp, and newTestClient helpers provide a solid foundation for testing the API executor functionality.


52-152: LGTM! Comprehensive validation testing.

The test cases thoroughly cover all combinations of required fields (operationName, method, path) including valid requests, missing fields, and empty string values. The table-driven approach with explicit error message verification is excellent.


154-251: LGTM! Thorough path building tests.

Excellent coverage of path template substitution including special characters, URL encoding, Unicode, nil/empty parameters, and multiple placeholder scenarios. The expected results correctly show URL encoding behavior.


253-355: LGTM! Comprehensive header preparation tests.

The tests cover default headers, custom additions, overrides, and importantly case-sensitivity behavior. This ensures the header preparation logic handles all practical scenarios correctly.


431-595: LGTM! Thorough builder pattern testing.

These tests comprehensively verify the builder's behavior including nil map initialization, multiple query values, parameter overwrites, and various body types (string, struct, map, slice, nil). The coverage ensures robust builder functionality.


597-630: LGTM! Executor creation and basic execution verified.

The tests confirm that NewAPIExecutor creates a valid executor instance and that basic request execution works correctly with the test infrastructure.


632-782: LGTM! Retry behavior thoroughly tested.

The tests verify retry parameter retrieval and retry determination logic across various error scenarios, attempt numbers, and retry configurations. The validation of wait durations ensures proper backoff behavior.


784-1252: LGTM! Comprehensive edge case coverage.

The remaining tests provide excellent coverage of edge cases for path building, header preparation, response fields, builder chaining, overrides, and validation. The test suite is thorough and well-structured with proper use of table-driven tests and parallel execution.

@rhamzeh rhamzeh force-pushed the poc/expose-api-endpoint branch from 85ae031 to cdd9bf3 Compare November 20, 2025 01:46
@rhamzeh rhamzeh force-pushed the poc/expose-api-endpoint branch from cdd9bf3 to fa58c97 Compare November 20, 2025 02:01
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 Go SDK for calling arbitrary API endpoints

3 participants