Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions config/clients/java/CHANGELOG.md.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,14 @@

## [Unreleased](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v{{packageVersion}}...HEAD)

## v0.8.3

### [0.8.3](https://github.com/openfga/java-sdk/compare/v0.8.2...v0.8.3) (2025-07-15)

Fixed:
- client: fix connectTimeout config (#182)
- client: fix batchCheck error handling (#183)

## v0.8.2

### [0.8.2](https://{{gitHost}}/{{gitUserId}}/{{gitRepoId}}/compare/v0.8.1...v0.8.2) (2025-07-02)
Expand Down
2 changes: 1 addition & 1 deletion config/clients/java/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"gitRepoId": "java-sdk",
"artifactId": "openfga-sdk",
"groupId": "dev.openfga",
"packageVersion": "0.8.2",
"packageVersion": "0.8.3",
"apiPackage": "dev.openfga.sdk.api",
"authPackage": "dev.openfga.sdk.api.auth",
"clientPackage": "dev.openfga.sdk.api.client",
Expand Down
5 changes: 5 additions & 0 deletions config/clients/java/template/libraries/native/api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,11 @@ public class OpenFgaApi {
if (defaultHeaders != null) {
apiClient.addRequestInterceptor(httpRequest -> defaultHeaders.forEach(httpRequest::setHeader));
}

Duration connectTimeout = configuration.getConnectTimeout();
if (connectTimeout != null) {
apiClient.setHttpClientBuilder(apiClient.getHttpClientBuilder().connectTimeout(connectTimeout));
}
}

{{#operation}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public class OAuth2Client {
this.authRequest.setScope(clientCredentials.getScopes());
this.config = new Configuration()
.apiUrl(buildApiTokenIssuer(clientCredentials.getApiTokenIssuer()))
.connectTimeout(configuration.getConnectTimeout())
.maxRetries(configuration.getMaxRetries())
.minimumRetryDelay(configuration.getMinimumRetryDelay())
.telemetryConfiguration(configuration.getTelemetryConfiguration());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
Expand Down Expand Up @@ -699,6 +700,7 @@ public class OpenFgaClient {
var latch = new CountDownLatch(batchedChecks.size());

var responses = new ConcurrentLinkedQueue<ClientBatchCheckSingleResponse>();
var failure = new AtomicReference<Throwable>();

var override = new ConfigurationOverride().addHeaders(options);

Expand All @@ -719,26 +721,36 @@ public class OpenFgaClient {

return api.batchCheck(configuration.getStoreId(), body, override);
})
.handleAsync((batchCheckResponseApiResponse, throwable) -> {
Map<String, BatchCheckSingleResult> response =
batchCheckResponseApiResponse.getData().getResult();

List<ClientBatchCheckSingleResponse> batchResults = new ArrayList<>();
response.forEach((key, result) -> {
boolean allowed = Boolean.TRUE.equals(result.getAllowed());
ClientBatchCheckItem checkItem = correlationIdToCheck.get(key);
var singleResponse =
new ClientBatchCheckSingleResponse(allowed, checkItem, key, result.getError());
batchResults.add(singleResponse);
});
return batchResults;
})
.thenAccept(responses::addAll)
.thenRun(latch::countDown);
.whenComplete((batchCheckResponseApiResponse, throwable) -> {
try {
if (throwable != null) {
failure.compareAndSet(null, throwable);
return;
}

Map<String, BatchCheckSingleResult> response =
batchCheckResponseApiResponse.getData().getResult();

List<ClientBatchCheckSingleResponse> batchResults = new ArrayList<>();
response.forEach((key, result) -> {
boolean allowed = Boolean.TRUE.equals(result.getAllowed());
ClientBatchCheckItem checkItem = correlationIdToCheck.get(key);
var singleResponse =
new ClientBatchCheckSingleResponse(allowed, checkItem, key, result.getError());
batchResults.add(singleResponse);
});
responses.addAll(batchResults);
} finally {
latch.countDown();
}
});

try {
batchedChecks.forEach(batch -> executor.execute(() -> singleBatchCheckRequest.accept(batch)));
latch.await();
if (failure.get() != null) {
return CompletableFuture.failedFuture(failure.get());
}
return CompletableFuture.completedFuture(new ClientBatchCheckResponse(new ArrayList<>(responses)));
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2057,9 +2057,33 @@ public class OpenFgaClientTest {
assertTrue(response.getResult().isEmpty());
}

/**
* Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason
* about and debug a certain relationship.
@Test
public void batchCheck_rateLimited() {
// Given
String postUrl = String.format("https://api.fga.example/stores/%s/batch-check", DEFAULT_STORE_ID);
mockHttpClient.onPost(postUrl).doReturn(429, "{\"code\":\"rate_limited\",\"message\":\"Too Many Requests\"}");

ClientBatchCheckItem item = new ClientBatchCheckItem()
.user(DEFAULT_USER)
.relation(DEFAULT_RELATION)
._object(DEFAULT_OBJECT)
.correlationId("cor-1");
ClientBatchCheckRequest request = new ClientBatchCheckRequest().checks(List.of(item));

// When
ExecutionException execException = assertThrows(
ExecutionException.class, () -> fga.batchCheck(request).get());

// Then
mockHttpClient.verify().post(postUrl).called(1 + DEFAULT_MAX_RETRIES);
var exception = assertInstanceOf(FgaApiRateLimitExceededError.class, execException.getCause());
assertEquals(429, exception.getStatusCode());
assertEquals("{\"code\":\"rate_limited\",\"message\":\"Too Many Requests\"}", exception.getResponseData());
}

/**
* Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason
* about and debug a certain relationship.
*/
@Test
public void expandTest() throws Exception {
Expand Down
Loading