Skip to content

Conversation

@SoulPancake
Copy link
Member

@SoulPancake SoulPancake commented Jan 14, 2026

Description

solves #249

What problem is being solved?

How is it being solved?

What changes are made to solve it?

References

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 API Executor for executing raw HTTP requests to OpenFGA endpoints not yet covered by the SDK, with built-in authentication, retry logic, and error handling.
  • Documentation

    • Expanded README with guidance on calling arbitrary endpoints.
    • Added comprehensive API Executor documentation with examples.
    • Introduced new example project demonstrating API Executor usage patterns including GET/POST requests, pagination, custom headers, and error handling.
  • Bug Fixes

    • Fixed response deserialization for String responses.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

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.

  • 🔍 Trigger a full review

Walkthrough

This PR introduces the API Executor feature, enabling direct HTTP access to OpenFGA endpoints not yet wrapped by the SDK. It adds ApiExecutor and ApiExecutorRequestBuilder classes, updates HttpRequestAttempt to handle String responses, exposes an apiExecutor() method in OpenFgaClient, and provides comprehensive examples, tests, and documentation.

Changes

Cohort / File(s) Summary
Documentation & Configuration
README.md, CHANGELOG.md, examples/README.md, docs/ApiExecutor.md
Added "Calling Other Endpoints" section to main README; introduced API Executor changelog entry with migration guide; documented API Executor feature with quick start, API reference, and usage examples in new doc file; added API Executor and Streaming examples to examples README.
Core API Implementation
src/main/java/dev/openfga/sdk/api/client/ApiExecutor.java, src/main/java/dev/openfga/sdk/api/client/ApiExecutorRequestBuilder.java
New ApiExecutor class with send() methods for typed and raw JSON responses; new ApiExecutorRequestBuilder fluent builder for constructing raw HTTP requests with path params, query params, headers, and bodies.
Client Modifications
src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java, src/main/java/dev/openfga/sdk/api/client/HttpRequestAttempt.java
Added public apiExecutor() method to OpenFgaClient; enhanced deserializeResponse() to short-circuit for String.class and return raw response body.
API Executor Example
examples/api-executor/*
New self-contained example module with Gradle build files, wrapper scripts (gradlew, gradlew.bat), build configuration, and ApiExecutorExample.java demonstrating raw HTTP calls (list/get stores, pagination, custom headers, error handling).
Dependency Update
examples/streamed-list-objects/build.gradle
Updated OpenFGA SDK JAR dependency from 0.9.3 to 0.9.4.
Test Coverage
src/test/java/dev/openfga/sdk/api/client/ApiExecutorTest.java, src/test-integration/java/dev/openfga/sdk/api/client/ApiExecutorIntegrationTest.java
Comprehensive unit tests validating ApiExecutorRequestBuilder construction, error handling, and send() behavior; integration tests against live OpenFGA container covering stores, auth models, tuples, pagination, and error scenarios.

Sequence Diagram

sequenceDiagram
    participant Client
    participant OpenFgaClient
    participant ApiExecutor
    participant ApiExecutorRequestBuilder
    participant HttpRequestAttempt
    participant Server as OpenFGA Server

    Client->>OpenFgaClient: apiExecutor()
    OpenFgaClient-->>Client: ApiExecutor instance

    Client->>ApiExecutorRequestBuilder: builder(method, path)
    ApiExecutorRequestBuilder-->>Client: builder instance

    Client->>ApiExecutorRequestBuilder: pathParam(key, value)
    Client->>ApiExecutorRequestBuilder: queryParam(key, value)
    Client->>ApiExecutorRequestBuilder: header(key, value)
    Client->>ApiExecutorRequestBuilder: body(data)
    Client->>ApiExecutorRequestBuilder: build()
    ApiExecutorRequestBuilder-->>Client: request builder

    Client->>ApiExecutor: send(requestBuilder, responseType)
    ApiExecutor->>ApiExecutor: buildCompletePath()
    ApiExecutor->>ApiExecutor: buildHttpRequest()
    ApiExecutor->>HttpRequestAttempt: execute(httpRequest)
    HttpRequestAttempt->>Server: HTTP request
    Server-->>HttpRequestAttempt: HTTP response
    HttpRequestAttempt->>HttpRequestAttempt: deserializeResponse()
    HttpRequestAttempt-->>ApiExecutor: ApiResponse<T>
    ApiExecutor-->>Client: CompletableFuture<ApiResponse<T>>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • jimmyjames
  • rhamzeh
🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 39.57% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: APIExecutor for calling raw arbitrary endpoints' accurately and concisely describes the primary feature addition in the PR.

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


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.

@codecov-commenter
Copy link

codecov-commenter commented Jan 14, 2026

Codecov Report

❌ Patch coverage is 82.35294% with 18 lines in your changes missing coverage. Please review.
✅ Project coverage is 37.68%. Comparing base (68211ed) to head (b0fe538).

Files with missing lines Patch % Lines
...n/java/dev/openfga/sdk/api/client/ApiExecutor.java 75.40% 9 Missing and 6 partials ⚠️
...nfga/sdk/api/client/ApiExecutorRequestBuilder.java 91.89% 0 Missing and 3 partials ⚠️

❌ Your project status has failed because the head coverage (37.68%) 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     #273      +/-   ##
============================================
+ Coverage     37.06%   37.68%   +0.62%     
- Complexity     1200     1238      +38     
============================================
  Files           194      196       +2     
  Lines          7504     7606     +102     
  Branches        865      881      +16     
============================================
+ Hits           2781     2866      +85     
- Misses         4593     4601       +8     
- Partials        130      139       +9     

☔ 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.

@SoulPancake SoulPancake marked this pull request as ready for review January 14, 2026 15:05
@SoulPancake SoulPancake requested a review from a team as a code owner January 14, 2026 15:05
Copilot AI review requested due to automatic review settings January 14, 2026 15:05
@SoulPancake SoulPancake requested review from a team as code owners January 14, 2026 15:05
@dosubot
Copy link

dosubot bot commented Jan 14, 2026

Related Documentation

Checked 5 published document(s) in 0 knowledge base(s). No updates required.

How did I do? Any feedback?  Join Discord

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 Raw API feature that enables developers to call arbitrary OpenFGA endpoints not yet wrapped by the SDK's typed methods. The raw API maintains all SDK configuration including authentication, retry logic, error handling, and telemetry while providing flexible access to any endpoint.

Changes:

  • Added Raw API functionality with RawRequestBuilder for constructing requests and RawApi for execution
  • Updated HttpRequestAttempt to support raw string responses
  • Added comprehensive unit and integration tests

Reviewed changes

Copilot reviewed 19 out of 19 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/main/java/dev/openfga/sdk/api/client/RawRequestBuilder.java Builder pattern for constructing HTTP requests with path/query params, headers, and body
src/main/java/dev/openfga/sdk/api/client/RawApi.java Executes raw HTTP requests using SDK's internal HTTP client and configuration
src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java Adds raw() method to expose Raw API functionality
src/main/java/dev/openfga/sdk/api/client/HttpRequestAttempt.java Modified to return raw string responses when String.class is requested
src/test/java/dev/openfga/sdk/api/client/RawApiTest.java Comprehensive unit tests using WireMock
src/test-integration/java/dev/openfga/sdk/api/client/RawApiIntegrationTest.java Integration tests using OpenFGA container
docs/RawApi.md Complete API documentation with examples
examples/raw-api/ Working example demonstrating all Raw API features
README.md Main documentation updated with Raw API usage examples
examples/streamed-list-objects/build.gradle Version bump to 0.9.4

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

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: 6

🤖 Fix all issues with AI agents
In `@docs/RawApi.md`:
- Around line 59-67: The docs show using a terminal .build() call on
RawRequestBuilder but the class uses fluent chaining and returns this; remove
the .build() invocation and pass the RawRequestBuilder instance directly to
send() (i.e., update examples using RawRequestBuilder so they call
send(rawRequestBuilder) or equivalent), ensuring consistency with the other
examples that omit .build() and referencing RawRequestBuilder and the send()
usage in the ApiResponse examples.

In `@examples/raw-api/gradle/wrapper/gradle-wrapper.properties`:
- Around line 1-7: Update the Gradle wrapper distribution URL to use Gradle
8.2.1 by changing the distributionUrl value from
"https://services.gradle.org/distributions/gradle-8.6-bin.zip" to
"https://services.gradle.org/distributions/gradle-8.2.1-bin.zip", leaving the
existing security settings (validateDistributionUrl=true and
networkTimeout=10000) and other wrapper properties (distributionBase,
distributionPath, zipStoreBase, zipStorePath) unchanged.

In `@examples/raw-api/README.md`:
- Around line 21-32: The README uses a hardcoded absolute path
"/Users/anurag/openfga/java-sdk"; change that to a relative or generic
instruction so it works for all users—replace the line `cd
/Users/anurag/openfga/java-sdk` with guidance to "cd to the SDK root directory"
or a relative path like "../.." and update the step to run the build (e.g.,
"./gradlew build") from the SDK root; ensure the README clearly states "from the
SDK root directory" or similar instead of the developer-specific absolute path.
- Around line 59-67: The snippet incorrectly calls a non-existent terminal
method .build() on RawRequestBuilder; remove the trailing .build() and use the
fluent builder instance directly (e.g., assign the result of
RawRequestBuilder.builder(...).pathParam(...).queryParam(...).body(...).header(...)
to the request variable) so no .build() invocation remains and the variable type
matches RawRequestBuilder.

In `@src/main/java/dev/openfga/sdk/api/client/RawApi.java`:
- Around line 150-155: In RawApi (the block that handles request bodies), don't
call String.getBytes() without a charset; explicitly encode string bodies as
UTF-8 (e.g., use StandardCharsets.UTF_8) when setting bodyBytes for the String
branch, and add the necessary import for StandardCharsets if missing so the
request payload encoding is deterministic and consistent with HTTP/JSON
expectations.

In
`@src/test-integration/java/dev/openfga/sdk/api/client/RawApiIntegrationTest.java`:
- Around line 442-456: In writeTupleUsingRawRequest, the requestBody currently
puts "writes" as a list of objects containing "tuple_key"; change it to put
"writes" as a map with the "tuple_keys" array instead (i.e. set
requestBody.put("writes", Map.of("tuple_keys", List.of(tupleKey)))); update the
structure before building the RawRequestBuilder so the body matches the OpenFGA
Write API, then keep using RawRequestBuilder and fga.raw().send(...) as-is.
🧹 Nitpick comments (15)
examples/streamed-list-objects/build.gradle (1)

20-20: Version bump looks good.

The SDK version update from 0.9.3 to 0.9.4 is correct and consistent with the Raw API feature introduced in this PR.

Optional: Consider extracting the SDK version to a Gradle variable.

To reduce maintenance burden when bumping versions, you could extract the version number:

♻️ Optional refactor
 ext {
+    openfgaSdkVersion = "0.9.4"
     jacksonVersion = "2.18.2"
 }
 dependencies {
     // Use local build of SDK
-    implementation files('../../build/libs/openfga-sdk-0.9.4.jar')
+    implementation files("../../build/libs/openfga-sdk-${openfgaSdkVersion}.jar")

This would allow updating the version in one place and would apply consistently across all example modules.

examples/raw-api/Makefile (2)

1-2: Add all to the .PHONY declaration.

The all target on line 2 should be included in the .PHONY declaration to prevent issues if a file named all ever exists in the directory.

Suggested fix
-.PHONY: build run run-openfga
+.PHONY: all build run run-openfga

5-5: Consider pinning to a specific OpenFGA version.

Using latest for openfga_version can cause reproducibility issues in examples when the upstream image changes. Consider pinning to a specific version that matches the SDK's tested compatibility.

src/main/java/dev/openfga/sdk/api/client/RawRequestBuilder.java (2)

7-19: Minor documentation accuracy issue.

The Javadoc states that "Path and query parameters are automatically URL-encoded" (line 10), but the encoding is actually performed in RawApi.buildCompletePath(), not in this builder class. While this is functionally correct from the user's perspective, consider clarifying that encoding happens at request execution time.


72-77: Consider documenting or reconsidering silent null handling.

The pathParam, queryParam, and header methods silently ignore calls when either key or value is null. While this provides lenient handling, it could mask bugs where the caller expects a parameter to be set but accidentally passes null.

Consider either:

  1. Documenting this behavior in the Javadoc
  2. Throwing IllegalArgumentException for null keys (values could remain optional)
src/main/java/dev/openfga/sdk/api/client/RawApi.java (1)

101-109: Consider type-safe store ID access.

The instanceof check and cast pattern could be avoided if the constructor required ClientConfiguration instead of Configuration, since RawApi is typically accessed via OpenFgaClient.raw() which has a ClientConfiguration. However, the current approach offers flexibility if RawApi needs to work with other configuration types in the future.

examples/raw-api/src/main/java/dev/openfga/sdk/example/RawApiExample.java (2)

88-97: Consider removing the hardcoded fallback store ID.

The hardcoded fallback ID "01YCP46JKYM8FJCQ37NMBYHE5X" is unlikely to exist in the user's environment. If both listing and creating stores fail, subsequent examples will also fail. Consider propagating the error or exiting gracefully instead.

Suggested approach
         } catch (Exception e) {
             System.err.println("✗ Error: " + e.getMessage());
-            // Create a store on error
-            try {
-                return createStoreForExamples(fgaClient);
-            } catch (Exception ex) {
-                return "01YCP46JKYM8FJCQ37NMBYHE5X"; // fallback
-            }
+            System.err.println("Unable to list or create stores. Please ensure OpenFGA server is running.");
+            throw e;
         }

199-205: Add null check for getCause() for defensive coding.

While ExecutionException typically has a cause, adding a null check prevents potential NullPointerException in edge cases.

Suggested fix
         } catch (Exception e) {
             // Expected error - demonstrates proper error handling
             System.out.println("✓ Error handled correctly:");
             System.out.println("  Message: " + e.getMessage());
-            System.out.println("  Type: " + e.getCause().getClass().getSimpleName());
+            Throwable cause = e.getCause();
+            System.out.println("  Type: " + (cause != null ? cause.getClass().getSimpleName() : "Unknown"));
         }
examples/raw-api/build.gradle (2)

18-20: Hardcoded SDK version may cause maintenance issues.

The SDK version 0.9.4 is hardcoded in the JAR path. When the SDK version is bumped, this file will need manual updates or the example will fail to build.

Consider using a dynamic approach or at least documenting this dependency clearly:

💡 Suggested improvement
+ext {
+    sdkVersion = "0.9.4"
+    jacksonVersion = "2.18.2"
+}
+
 dependencies {
     // Use local build of SDK
-    implementation files('../../build/libs/openfga-sdk-0.9.4.jar')
+    // NOTE: Update sdkVersion when SDK version changes
+    implementation files("../../build/libs/openfga-sdk-${sdkVersion}.jar")

40-42: Duplicate comment block.

The comment block explaining spotless usage appears twice (lines 40-42 and 61-63). The second occurrence can be removed since the fmt task is self-explanatory.

🧹 Suggested cleanup
 }
 
-// Use spotless plugin to automatically format code, remove unused import, etc
-// To apply changes directly to the file, run `gradlew spotlessApply`
-// Ref: https://github.com/diffplug/spotless/tree/main/plugin-gradle
 tasks.register('fmt') {
     dependsOn 'spotlessApply'
 }

Also applies to: 61-63

src/test-integration/java/dev/openfga/sdk/api/client/RawApiIntegrationTest.java (4)

31-31: Unused ObjectMapper instance.

The ObjectMapper is declared but never used in this test class. Either remove it or use it where JSON parsing/assertions could benefit from it.


36-37: Remove debug logging system property from test setup.

Setting HttpRequestAttempt.debug-logging in tests can cause noisy output in CI/CD pipelines and is typically not needed for integration tests. If debugging is needed, consider using a test profile or making it conditional.

🧹 Suggested cleanup
     `@BeforeEach`
     public void initializeApi() throws Exception {
-        System.setProperty("HttpRequestAttempt.debug-logging", "enable");
-
         ClientConfiguration apiConfig = new ClientConfiguration().apiUrl(openfga.getHttpEndpoint());
         fga = new OpenFgaClient(apiConfig);
     }

253-255: Empty continuation token query parameter.

Passing an empty string for continuation_token is unusual. Typically, you would either omit the parameter entirely on the first request or pass a non-empty token from a previous response. The server may interpret an empty string differently than an absent parameter.

Consider removing this parameter for the initial request:

💡 Suggested fix
         RawRequestBuilder request = RawRequestBuilder.builder("GET", "/stores/{store_id}/authorization-models")
-                .queryParam("page_size", "10")
-                .queryParam("continuation_token", "");
+                .queryParam("page_size", "10");

350-361: Prefer asserting on specific exception type rather than catching generic Exception.

The current pattern catches all exceptions and just asserts non-null. This could mask unexpected failures. Consider using assertThrows with the expected exception type for clearer failure messages and stricter validation.

💡 Suggested improvement
-        // Should throw an exception
-        try {
-            fga.raw().send(request, GetStoreResponse.class).get();
-            fail("Should have thrown an exception for non-existent store");
-        } catch (Exception e) {
-            // Expected - verify it's some kind of error (ExecutionException wrapping an FgaError)
-            assertNotNull(e, "Exception should not be null");
-            System.out.println("✓ Successfully handled error for non-existent store");
-            System.out.println("  Error type: " + e.getClass().getSimpleName());
-            if (e.getCause() != null) {
-                System.out.println("  Cause: " + e.getCause().getClass().getSimpleName());
-            }
-        }
+        // Should throw ExecutionException wrapping FgaError
+        ExecutionException exception = assertThrows(
+                ExecutionException.class,
+                () -> fga.raw().send(request, GetStoreResponse.class).get());
+        
+        assertInstanceOf(dev.openfga.sdk.errors.FgaError.class, exception.getCause(),
+                "Expected FgaError for non-existent store");
src/test/java/dev/openfga/sdk/api/client/RawApiTest.java (1)

159-164: Unused mockResponse variable.

The ExperimentalResponse mockResponse is instantiated but never used since the mock response is built directly as a JSON string. Remove the unused variable.

🧹 Suggested cleanup
     `@Test`
     public void rawApi_canSendGetRequestWithTypedResponse() throws Exception {
         // Setup mock server
-        ExperimentalResponse mockResponse = new ExperimentalResponse(true, 42, "Success");
         stubFor(get(urlEqualTo("/stores/" + DEFAULT_STORE_ID + "/experimental-feature"))
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e88b1c7 and 60b69a4.

📒 Files selected for processing (19)
  • README.md
  • docs/RawApi.md
  • examples/README.md
  • examples/raw-api/Makefile
  • examples/raw-api/README.md
  • examples/raw-api/build.gradle
  • examples/raw-api/gradle.properties
  • examples/raw-api/gradle/wrapper/gradle-wrapper.properties
  • examples/raw-api/gradlew
  • examples/raw-api/gradlew.bat
  • examples/raw-api/settings.gradle
  • examples/raw-api/src/main/java/dev/openfga/sdk/example/RawApiExample.java
  • examples/streamed-list-objects/build.gradle
  • src/main/java/dev/openfga/sdk/api/client/HttpRequestAttempt.java
  • src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java
  • src/main/java/dev/openfga/sdk/api/client/RawApi.java
  • src/main/java/dev/openfga/sdk/api/client/RawRequestBuilder.java
  • src/test-integration/java/dev/openfga/sdk/api/client/RawApiIntegrationTest.java
  • src/test/java/dev/openfga/sdk/api/client/RawApiTest.java
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-08-19T19:43:18.975Z
Learnt from: jimmyjames
Repo: openfga/java-sdk PR: 211
File: examples/opentelemetry/src/main/java/dev/openfga/sdk/example/opentelemetry/OpenTelemetryExample.java:8-10
Timestamp: 2025-08-19T19:43:18.975Z
Learning: The examples/opentelemetry/src/main/java/dev/openfga/sdk/example/opentelemetry/OpenTelemetryExample.java file will be generated from mustache templates, so the auto-generation header is correct and should be retained.

Applied to files:

  • examples/README.md
  • examples/raw-api/README.md
  • examples/raw-api/src/main/java/dev/openfga/sdk/example/RawApiExample.java
  • examples/raw-api/build.gradle
🧬 Code graph analysis (2)
examples/raw-api/src/main/java/dev/openfga/sdk/example/RawApiExample.java (2)
src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java (1)
  • OpenFgaClient (23-1495)
src/main/java/dev/openfga/sdk/api/client/RawRequestBuilder.java (1)
  • RawRequestBuilder (20-145)
src/test-integration/java/dev/openfga/sdk/api/client/RawApiIntegrationTest.java (3)
src/main/java/dev/openfga/sdk/errors/FgaError.java (1)
  • com (50-84)
src/main/java/dev/openfga/sdk/api/configuration/ClientConfiguration.java (1)
  • ClientConfiguration (8-137)
src/main/java/dev/openfga/sdk/api/client/RawRequestBuilder.java (1)
  • RawRequestBuilder (20-145)
🪛 checkmake (0.2.2)
examples/raw-api/Makefile

[warning] 1-1: Missing required phony target "all"

(minphony)


[warning] 1-1: Missing required phony target "clean"

(minphony)


[warning] 1-1: Missing required phony target "test"

(minphony)


[warning] 2-2: Target "all" should be declared PHONY.

(phonydeclared)

🔇 Additional comments (24)
examples/raw-api/settings.gradle (1)

1-1: LGTM!

Standard Gradle settings file with appropriate project naming.

src/main/java/dev/openfga/sdk/api/client/HttpRequestAttempt.java (1)

219-224: LGTM!

The String.class short-circuit for raw response handling is clean and correctly placed after the Void.class check. The @SuppressWarnings("unchecked") is justified since the type is verified by the clazz == String.class condition.

Note: If response.body() returns null, this will propagate a null value, which appears consistent with how the Raw API is intended to work for empty responses.

examples/raw-api/gradle.properties (1)

1-1: LGTM!

Simple property file providing the default language configuration for the Gradle build.

examples/raw-api/Makefile (1)

8-16: LGTM!

The build, run, and run-openfga targets are well-structured. The && chaining in run-openfga correctly ensures the container only runs if the pull succeeds.

examples/raw-api/gradlew.bat (1)

1-89: LGTM - Standard Gradle wrapper script.

This is a standard Gradle-generated Windows batch wrapper script. The implementation follows established Gradle conventions for Java discovery, classpath setup, and wrapper execution.

examples/raw-api/gradlew (1)

1-234: LGTM - Standard Gradle wrapper script.

This is a standard Gradle-generated POSIX shell wrapper script with proper handling for symlinks, various Unix-like environments (Cygwin, MSYS, Darwin, AIX), and POSIX shell compatibility requirements.

src/main/java/dev/openfga/sdk/api/client/RawRequestBuilder.java (1)

118-145: LGTM - Well-designed accessors with proper encapsulation.

The package-private accessors appropriately hide implementation details while the defensive copies for map getters prevent external mutation of internal state.

src/main/java/dev/openfga/sdk/api/client/RawApi.java (2)

162-165: Verify header override behavior is intentional.

Custom headers from RawRequestBuilder are added after SDK-managed headers (set in ApiClient.requestBuilder), which means they could override standard headers like Content-Type or Authorization. If this is intentional for advanced use cases, consider documenting this behavior. If not, you may want to validate or restrict which headers users can set.


57-95: LGTM - Well-structured async request handling.

The send methods properly validate inputs, delegate appropriately, and use CompletableFuture.failedFuture for proper async error propagation. The method name format provides useful context for debugging and tracing.

examples/README.md (1)

12-18: LGTM - Clear documentation for new examples.

The new sections appropriately describe the streaming and raw API examples, following the existing documentation pattern. The description correctly highlights the key benefit of the Raw API feature (maintaining SDK configuration for endpoints not yet wrapped by the SDK).

docs/RawApi.md (2)

1-155: Well-structured documentation for the Raw API feature.

The documentation provides a comprehensive guide covering quick start, API reference, multiple usage examples, error handling, and migration guidance. The structure is clear and will help users adopt the Raw API effectively.


136-142: No action required. The documentation claim is accurate: the {store_id} placeholder is indeed auto-replaced from the client configuration when not provided via .pathParam(). This behavior is implemented in RawApi (lines 101-109) and matches the documented behavior.

src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java (1)

49-67: LGTM - Clean implementation of the raw() accessor method.

The method provides a straightforward way to access the Raw API functionality. The Javadoc is comprehensive with a useful example. Creating a new RawApi instance per call is acceptable if the object is lightweight and stateless.

README.md (3)

49-49: LGTM - TOC updated with the new section.


750-778: Good fixes for code examples.

The fixes correct the variable reference and remove the stray parenthesis in the correlationId value.


1171-1252: Comprehensive Raw API documentation added to README.

The new section provides clear guidance on using the Raw API with practical examples for both raw JSON and typed responses. The documentation aligns well with the rest of the README structure.

examples/raw-api/src/main/java/dev/openfga/sdk/example/RawApiExample.java (1)

13-57: Well-structured example demonstrating Raw API capabilities.

The example effectively showcases different Raw API use cases: typed responses, raw JSON, query parameters, custom headers, and error handling. The class documentation clearly notes the use of blocking .get() calls for simplicity with guidance on async patterns for production use.

examples/raw-api/build.gradle (1)

1-67: Overall structure looks good.

The build file follows the pattern of other example projects in the repository, with appropriate dependencies for the Raw API example including Jackson for JSON handling and OpenTelemetry as required by the SDK.

src/test-integration/java/dev/openfga/sdk/api/client/RawApiIntegrationTest.java (1)

43-72: Comprehensive test coverage for Raw API functionality.

The integration tests thoroughly cover the key Raw API use cases:

  • List/create/get stores with typed and raw responses
  • Path parameter substitution (explicit and automatic)
  • Query parameters and custom headers
  • Authorization model operations
  • Pagination support

The tests serve as excellent documentation for the Raw API feature.

Also applies to: 74-101, 135-160, 162-185, 187-237, 314-338, 364-391

src/test/java/dev/openfga/sdk/api/client/RawApiTest.java (5)

31-48: Test DTO uses public fields.

While acceptable for test DTOs, using public fields for ExperimentalResponse is unconventional compared to the SDK's model classes that use getters. This is fine for test isolation purposes.


60-64: Thorough RawRequestBuilder validation tests.

Excellent coverage of builder edge cases:

  • Null/empty method and path validation
  • Invalid HTTP method rejection
  • All valid HTTP methods accepted (including case-insensitivity)
  • Path params, query params, headers, and body handling

Also applies to: 66-154


156-337: Comprehensive WireMock-based API tests.

The unit tests effectively validate:

  • Typed and raw JSON responses
  • Request body serialization with JSON path matching
  • Query parameter encoding
  • Custom header propagation
  • HTTP 4xx/5xx error handling mapped to FgaError

The verification assertions ensure both response handling and request construction are correct.


339-365: Path parameter URL encoding test.

Good test for special characters in path parameters. The test correctly verifies that "store with spaces" is encoded to "store%20with%20spaces" in the URL.


367-378: Null argument validation tests.

These tests ensure the Raw API properly rejects null builders and response types with IllegalArgumentException, providing clear fail-fast behavior.

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

@dyeam0 dyeam0 linked an issue Jan 15, 2026 that may be closed by this pull request
1 task
@SoulPancake SoulPancake changed the title feat: raw requestor for calling arbitrary endpoints feat: APIExecutor for calling raw arbitrary endpoints Jan 27, 2026
@SoulPancake SoulPancake requested a review from Copilot January 27, 2026 06:33
@SoulPancake
Copy link
Member Author

@CodeRabbit review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 27, 2026

✅ 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.

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

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


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

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: 5

🤖 Fix all issues with AI agents
In `@docs/ApiExecutor.md`:
- Around line 53-56: The doc snippet uses the wrong type name; replace the usage
of RawApi with the correct ApiExecutor type everywhere this example appears
(e.g., change the variable declaration that uses RawApi to ApiExecutor and keep
the call to client.apiExecutor() unchanged) so the documented type matches the
implementation/class name ApiExecutor.

In
`@examples/api-executor/src/main/java/dev/openfga/sdk/example/ApiExecutorExample.java`:
- Around line 193-209: In errorHandlingExample, the catch block assumes
e.getCause() is non-null which can cause an NPE; update the catch to safely
handle a null cause when printing the error type (e.g., if e.getCause() != null
print e.getCause().getClass().getSimpleName() otherwise print
e.getClass().getSimpleName() or "UnknownCause"), and ensure the Message and Type
lines still print the original exception information without risking a
null-pointer.

In `@src/main/java/dev/openfga/sdk/api/client/ApiExecutor.java`:
- Around line 15-27: Update the Javadoc example to use the correct API executor
accessor: replace the typed-response call that uses client.raw() with
client.apiExecutor() so the example matches the raw JSON example and the
ApiExecutor flow; locate the example block referencing
ApiExecutorRequestBuilder, ApiResponse and the send method and change the
client.raw() invocation to client.apiExecutor() to ensure consistency.

In `@src/main/java/dev/openfga/sdk/api/client/ApiExecutorRequestBuilder.java`:
- Around line 12-19: The Javadoc example uses the removed RawRequestBuilder
class; update the example to reference the current ApiExecutorRequestBuilder API
by replacing occurrences of RawRequestBuilder with ApiExecutorRequestBuilder and
adjust the example variable/type (e.g.,
ApiExecutorRequestBuilder.builder("POST",
"/stores/{store_id}/endpoint").pathParam("store_id",
storeId).queryParam("limit", "50").body(requestObject).build();) so the example
matches the actual ApiExecutorRequestBuilder builder API and method chain.

In
`@src/test-integration/java/dev/openfga/sdk/api/client/ApiExecutorIntegrationTest.java`:
- Around line 28-41: The test sets the JVM property
"HttpRequestAttempt.debug-logging" in initializeApi() but never restores it;
capture the previous value at start of initializeApi() (e.g., store in a private
String previousDebugLogging field), set the property as you do, and add an
`@AfterEach` method (e.g., restoreDebugLogging or cleanupDebugLogging) that
restores the property to the saved previous value (use System.setProperty if
previous != null, otherwise remove it with System.clearProperty) so the global
state is not leaked to other tests; reference initializeApi(), the OpenFgaClient
setup, and the "HttpRequestAttempt.debug-logging" property when making the
changes.
🧹 Nitpick comments (5)
examples/api-executor/Makefile (1)

1-2: Add all to .PHONY declaration.

The all target on line 2 should be declared as PHONY to ensure it always runs regardless of file existence.

Suggested fix
-.PHONY: build run run-openfga
+.PHONY: all build run run-openfga
 all: build
examples/api-executor/build.gradle (2)

18-20: Hardcoded SDK version may become stale.

The local SDK jar path contains a hardcoded version 0.9.4. This will require manual updates when the SDK version changes.

Consider using a variable or dynamic version resolution:

+def sdkVersion = "0.9.4"
+
 dependencies {
     // Use local build of SDK
-    implementation files('../../build/libs/openfga-sdk-0.9.4.jar')
+    implementation files("../../build/libs/openfga-sdk-${sdkVersion}.jar")

Alternatively, document that this needs to be updated with SDK releases.


61-66: Remove duplicate comment block.

Lines 61-63 duplicate the spotless documentation comment already present at lines 40-42.

Suggested fix
-// Use spotless plugin to automatically format code, remove unused import, etc
-// To apply changes directly to the file, run `gradlew spotlessApply`
-// Ref: https://github.com/diffplug/spotless/tree/main/plugin-gradle
 tasks.register('fmt') {
     dependsOn 'spotlessApply'
 }
docs/ApiExecutor.md (1)

112-119: Missing pathParam for {store_id} placeholder.

The Custom Headers example uses /stores/{store_id}/action but doesn't include a .pathParam("store_id", ...) call. While the notes mention auto-replacement, explicitly showing it would be clearer for users.

Suggested fix
 ### Custom Headers
 ```java
 ApiExecutorRequestBuilder.builder("POST", "/stores/{store_id}/action")
+    .pathParam("store_id", storeId)
     .header("X-Request-ID", UUID.randomUUID().toString())
     .header("X-Idempotency-Key", "key-123")
     .body(data)
     .build();
</details>

</blockquote></details>
<details>
<summary>src/main/java/dev/openfga/sdk/api/client/ApiExecutor.java (1)</summary><blockquote>

`82-90`: **Fail fast when path placeholders remain unresolved.**

If `{store_id}` (or any placeholder) survives replacement, the request will hit a literal path and likely 404. Consider rejecting early with a clearer error.

<details>
<summary>💡 Suggested guard</summary>

```diff
             String completePath = buildCompletePath(requestBuilder);
+            if (completePath.contains("{") || completePath.contains("}")) {
+                throw new FgaInvalidParameterException(
+                        "Unresolved path parameters in request path: " + completePath);
+            }
             HttpRequest httpRequest = buildHttpRequest(requestBuilder, completePath);

@SoulPancake SoulPancake added this pull request to the merge queue Jan 28, 2026
Merged via the queue into main with commit b9337f7 Jan 28, 2026
24 checks passed
@SoulPancake SoulPancake deleted the feat/raw-requests branch January 28, 2026 13: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 Java SDK for calling arbitrary API endpoints

4 participants