Skip to content

Conversation

@SoulPancake
Copy link
Member

@SoulPancake SoulPancake commented Nov 26, 2025

Generated from openfga/sdk-generator#668

Description

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

Release Notes

  • New Features

    • Added streaming support for list objects operations with line-by-line response processing
    • Implemented error handling and propagation for streamed responses
  • Tests

    • Added comprehensive test coverage for streaming functionality, including error scenarios and large data volumes

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

@SoulPancake SoulPancake requested a review from a team as a code owner November 26, 2025 12:54
Copilot AI review requested due to automatic review settings November 26, 2025 12:54
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 26, 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

This PR introduces a generic streaming API infrastructure for the OpenFGA Java SDK. It adds a new BaseStreamingApi<T> base class for handling line-delimited JSON responses, a generic StreamResult<T> model wrapper, and refactors the existing StreamedListObjectsApi to extend this base class. A comprehensive test suite validates the streaming pipeline with various scenarios including error handling and performance at scale.

Changes

Cohort / File(s) Summary
Build & Generation
.openapi-generator/FILES, examples/streamed-list-objects/Makefile
Added new generated API classes to OpenAPI generator manifest; updated Makefile to invoke Gradle wrapper from parent directory for build and run targets.
Streaming Base Infrastructure
src/main/java/dev/openfga/sdk/api/BaseStreamingApi.java
New generic abstract class providing shared streaming response handling for line-delimited JSON, including HTTP request execution, line deserialization, error routing, and request building.
Streaming Model
src/main/java/dev/openfga/sdk/api/model/StreamResult.java
New generic wrapper class for streaming responses that encapsulates either a successful result or an error Status, with JSON serialization support.
Streaming API Implementation
src/main/java/dev/openfga/sdk/api/StreamedListObjectsApi.java
Refactored to extend BaseStreamingApi<StreamedListObjectsResponse>, removing duplicate streaming logic in favor of inherited base class functionality.
Streaming Tests
src/test/java/dev/openfga/sdk/api/StreamingApiTest.java
New test suite covering successful streaming, embedded errors, HTTP errors, empty streams, large stream performance (1000+ objects), and StreamResult deserialization.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Areas requiring extra attention:

  • BaseStreamingApi.java: Review async error handling, line-by-line processing, and exception wrapping logic for correctness and edge cases
  • StreamedListObjectsApi.java: Verify proper delegation to base class and confirm all prior functionality is preserved
  • StreamingApiTest.java: Validate test coverage of error conditions and stream boundary cases, particularly the 1000-object scalability test

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ 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: use common streaming utils, reverse sync to template' is partially related to the changeset. While the changes do involve streaming utilities (BaseStreamingApi, StreamedListObjectsApi, StreamResult), the phrase 'reverse sync to template' is vague and unclear, and the title doesn't clearly convey that this is a refactoring that extracts common streaming logic into a base class.

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 Nov 26, 2025

Related Documentation

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

How did I do? Any feedback?  Join Discord

@codecov-commenter
Copy link

codecov-commenter commented Nov 26, 2025

Codecov Report

❌ Patch coverage is 56.66667% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 36.81%. Comparing base (33a8520) to head (b0c6c0a).

Files with missing lines Patch % Lines
...n/java/dev/openfga/sdk/api/model/StreamResult.java 25.92% 20 Missing ⚠️
...ain/java/dev/openfga/sdk/api/BaseStreamingApi.java 68.85% 9 Missing and 10 partials ⚠️

❌ Your project status has failed because the head coverage (36.81%) 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     #261      +/-   ##
============================================
- Coverage     36.89%   36.81%   -0.09%     
- Complexity     1187     1191       +4     
============================================
  Files           192      194       +2     
  Lines          7410     7440      +30     
  Branches        854      856       +2     
============================================
+ Hits           2734     2739       +5     
- Misses         4553     4578      +25     
  Partials        123      123              

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

Copilot finished reviewing on behalf of SoulPancake November 26, 2025 12:57
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: 0

🧹 Nitpick comments (7)
src/test/java/dev/openfga/sdk/api/StreamingApiTest.java (3)

51-65: Consider closing MockitoAnnotations.openMocks() to prevent resource leak.

MockitoAnnotations.openMocks(this) returns an AutoCloseable that should be closed after tests complete to release mock resources. Consider using @ExtendWith(MockitoExtension.class) instead, which handles lifecycle automatically.

+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.junit.jupiter.MockitoExtension;
...
+@ExtendWith(MockitoExtension.class)
 class StreamingApiTest {
...
+    private AutoCloseable mockitoCloseable;
+
     @BeforeEach
     void setUp() {
-        MockitoAnnotations.openMocks(this);
+        mockitoCloseable = MockitoAnnotations.openMocks(this);
...
+    @AfterEach
+    void tearDown() throws Exception {
+        mockitoCloseable.close();
+    }

Alternatively, simply use @ExtendWith(MockitoExtension.class) and remove the manual openMocks call entirely.


69-72: Variable name should follow camelCase convention.

Response should be response to follow Java naming conventions. Same issue appears at line 106.

-        String Response = "{\"result\":{\"object\":\"document:1\"}}\n"
+        String response = "{\"result\":{\"object\":\"document:1\"}}\n"
                 + "{\"result\":{\"object\":\"document:2\"}}\n"
                 + "{\"result\":{\"object\":\"document:3\"}}\n";

-        Stream<String> lineStream = Response.lines();
+        Stream<String> lineStream = response.lines();

235-253: Consider strengthening deserialization assertions.

The test only verifies assertNotNull but doesn't validate the deserialized values. Consider verifying that result and error fields are correctly populated.

         Object streamResult = mapper.readValue(jsonWithResult, resultType);
         assertNotNull(streamResult);
+        @SuppressWarnings("unchecked")
+        var typedResult = (dev.openfga.sdk.api.model.StreamResult<StreamedListObjectsResponse>) streamResult;
+        assertNotNull(typedResult.getResult());
+        assertEquals("document:1", typedResult.getResult().getObject());
+        assertNull(typedResult.getError());

         // Test with error - code should be an integer
         String jsonWithError = "{\"error\":{\"code\":400,\"message\":\"Error occurred\"}}";
         Object streamResultWithError = mapper.readValue(jsonWithError, resultType);
         assertNotNull(streamResultWithError);
+        @SuppressWarnings("unchecked")
+        var typedError = (dev.openfga.sdk.api.model.StreamResult<StreamedListObjectsResponse>) streamResultWithError;
+        assertNotNull(typedError.getError());
+        assertNull(typedError.getResult());
src/main/java/dev/openfga/sdk/api/StreamedListObjectsApi.java (1)

143-144: Redundant .toString() call on String parameter.

storeId is already a String, so .toString() is unnecessary.

         String path = "/stores/{store_id}/streamed-list-objects"
-                .replace("{store_id}", StringUtil.urlEncode(storeId.toString()));
+                .replace("{store_id}", StringUtil.urlEncode(storeId));
src/main/java/dev/openfga/sdk/api/BaseStreamingApi.java (3)

86-95: Stream processing blocks the HTTP client executor thread.

The lines.forEach(...) call is synchronous and will block until all lines are consumed. For long-running streams, this ties up a thread from the HTTP client's executor pool for the entire duration. This is an acceptable trade-off for simplicity, but consider documenting this behavior for users who may want to understand resource utilization.


140-146: Redundant null check.

The result != null check on line 143 is redundant since streamResult.getResult() != null was already verified on line 140.

             } else if (streamResult.getResult() != null) {
                 // Deliver the response object to the consumer
                 T result = streamResult.getResult();
-                if (result != null) {
-                    consumer.accept(result);
-                }
+                consumer.accept(result);
             }

147-151: Exceptions are silently swallowed if errorConsumer is null.

When errorConsumer is null, any parsing exceptions are caught and discarded without logging or propagation. This could make debugging difficult when malformed lines appear in the stream.

Consider logging the exception at debug/trace level even when no errorConsumer is provided:

         } catch (Exception e) {
             if (errorConsumer != null) {
                 errorConsumer.accept(e);
+            } else {
+                // Log at debug level to aid troubleshooting
+                // logger.debug("Failed to process stream line", e);
             }
         }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 33a8520 and 2c5b8a1.

📒 Files selected for processing (6)
  • .openapi-generator/FILES (2 hunks)
  • examples/streamed-list-objects/Makefile (1 hunks)
  • src/main/java/dev/openfga/sdk/api/BaseStreamingApi.java (1 hunks)
  • src/main/java/dev/openfga/sdk/api/StreamedListObjectsApi.java (2 hunks)
  • src/main/java/dev/openfga/sdk/api/model/StreamResult.java (1 hunks)
  • src/test/java/dev/openfga/sdk/api/StreamingApiTest.java (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/main/java/dev/openfga/sdk/api/StreamedListObjectsApi.java (3)
src/main/java/dev/openfga/sdk/util/Validation.java (1)
  • Validation (7-15)
src/main/java/dev/openfga/sdk/errors/FgaInvalidParameterException.java (1)
  • FgaInvalidParameterException (3-19)
src/main/java/dev/openfga/sdk/util/StringUtil.java (1)
  • StringUtil (12-51)
⏰ 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). (6)
  • GitHub Check: Agent
  • GitHub Check: CodeQL analysis (java)
  • GitHub Check: Test and Build OpenFGA (21)
  • GitHub Check: Test and Build OpenFGA (17)
  • GitHub Check: Test and Build OpenFGA (11)
  • GitHub Check: Analyze (java)
🔇 Additional comments (10)
examples/streamed-list-objects/Makefile (1)

8-12: LGTM!

The Gradle wrapper path update to ../../gradlew correctly aligns with the repository structure where examples reside in a subdirectory.

.openapi-generator/FILES (2)

100-102: LGTM!

The FILES manifest correctly registers the new streaming infrastructure components (BaseStreamingApi.java, updated StreamedListObjectsApi.java).


162-162: LGTM!

StreamResult.java is properly registered in the generated files manifest.

src/main/java/dev/openfga/sdk/api/model/StreamResult.java (2)

28-40: LGTM - Clean generic wrapper design.

The StreamResult<T> model correctly handles the streaming response pattern where each line contains either a result or an error. The generic type parameter enables reuse across different streaming endpoints.


81-96: LGTM!

The equals and hashCode implementations correctly handle the generic type with wildcard casting and use Objects.equals/Objects.hash for null-safe comparisons.

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

36-40: LGTM - Good refactoring to generic base class.

The class now correctly extends BaseStreamingApi<StreamedListObjectsResponse> and properly wires the TypeReference for deserialization. This enables code reuse for other streaming endpoints.


146-154: LGTM - Error handling is appropriate.

The try-catch properly notifies the error consumer before returning a failed future, ensuring both error propagation paths are covered.

src/main/java/dev/openfga/sdk/api/BaseStreamingApi.java (3)

38-57: LGTM!

Clean base class design with proper encapsulation. The use of TypeReference for generic deserialization and final fields for immutability are appropriate patterns.


97-116: Dual error reporting: errors are delivered to both errorConsumer and the CompletableFuture.

The current design calls errorConsumer.accept() AND re-throws the exception, causing the CompletableFuture to complete exceptionally. This means callers who provide both an errorConsumer and handle .exceptionally() or .whenComplete() on the returned future will observe the same error twice.

If this is intentional, consider documenting the behavior in the Javadoc. Otherwise, you may want to choose one reporting mechanism.


165-181: LGTM!

Request building logic is clean with proper exception wrapping and interceptor support.

Copy link

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 introduces a generic streaming utility framework to the OpenFGA Java SDK. It refactors the existing StreamedListObjectsApi implementation to use a new BaseStreamingApi<T> base class, which provides reusable streaming functionality for any endpoint that returns line-delimited JSON (NDJSON) format. The changes include a new generic StreamResult<T> model class to replace type-specific stream result models, comprehensive test coverage, and a minor fix to example Makefile paths.

Key Changes

  • Introduces BaseStreamingApi<T> as a reusable base class for streaming endpoints with consumer-callback pattern
  • Adds generic StreamResult<T> model to handle streaming responses uniformly across different endpoint types
  • Refactors StreamedListObjectsApi to extend BaseStreamingApi instead of implementing streaming logic directly

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/main/java/dev/openfga/sdk/api/BaseStreamingApi.java New abstract base class providing generic streaming implementation with async HTTP client, line-by-line processing, and error handling
src/main/java/dev/openfga/sdk/api/model/StreamResult.java New generic wrapper model for streaming results that can contain either a result or error
src/main/java/dev/openfga/sdk/api/StreamedListObjectsApi.java Refactored to extend BaseStreamingApi<StreamedListObjectsResponse>, removing duplicate streaming logic
src/test/java/dev/openfga/sdk/api/StreamingApiTest.java New comprehensive test suite covering successful streams, errors in streams, HTTP errors, empty streams, and large streams
examples/streamed-list-objects/Makefile Fixed gradle wrapper paths from ./gradlew to ../../gradlew
.openapi-generator/FILES Updated to include new source files

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

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.

3 participants