diff --git a/README.md b/README.md index 7aa48961..83e2d9b3 100644 --- a/README.md +++ b/README.md @@ -174,7 +174,7 @@ public class Example { } ``` -#### Auth0 Client Credentials +#### Client Credentials ```java import com.fasterxml.jackson.databind.ObjectMapper; @@ -605,6 +605,8 @@ Similar to [check](#check), but instead of checking a single user-object relatio [API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/BatchCheck) +> **Note**: The order of `batchCheck` results is not guaranteed to match the order of the checks provided. Use `correlationId` to pair responses with requests. + > Passing `ClientBatchCheckOptions` is optional. All fields of `ClientBatchCheckOptions` are optional. ```java diff --git a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java index 1ed47bc1..b0bbfab4 100644 --- a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java +++ b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java @@ -1154,6 +1154,11 @@ private Map buildTelemetryAttributes(Map attr if (body instanceof BatchCheckRequest) { BatchCheckRequest batchCheckRequest = (BatchCheckRequest) body; + if (!isNullOrWhitespace(batchCheckRequest.getAuthorizationModelId())) { + telemetryAttributes.put( + Attributes.FGA_CLIENT_REQUEST_MODEL_ID, batchCheckRequest.getAuthorizationModelId()); + } + if (batchCheckRequest.getChecks() != null) { telemetryAttributes.put( Attributes.FGA_CLIENT_REQUEST_BATCH_CHECK_SIZE, diff --git a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java index e4b52103..bcc459ca 100644 --- a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java +++ b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java @@ -711,8 +711,23 @@ public CompletableFuture batchCheck( var override = new ConfigurationOverride().addHeaders(options); - Consumer> singleBatchCheckRequest = request -> call(() -> - api.batchCheck(configuration.getStoreId(), new BatchCheckRequest().checks(request), override)) + Consumer> singleBatchCheckRequest = request -> call(() -> { + BatchCheckRequest body = new BatchCheckRequest().checks(request); + if (options.getConsistency() != null) { + body.consistency(options.getConsistency()); + } + + // Set authorizationModelId from options if available; otherwise, use the default from configuration + String authorizationModelId = !isNullOrWhitespace(options.getAuthorizationModelId()) + ? options.getAuthorizationModelId() + : configuration.getAuthorizationModelId(); + + if (!isNullOrWhitespace(authorizationModelId)) { + body.authorizationModelId(authorizationModelId); + } + + return api.batchCheck(configuration.getStoreId(), body, override); + }) .handleAsync((batchCheckResponseApiResponse, throwable) -> { Map response = batchCheckResponseApiResponse.getData().getResult(); diff --git a/src/main/java/dev/openfga/sdk/api/model/AbstractOpenApiSchema.java b/src/main/java/dev/openfga/sdk/api/model/AbstractOpenApiSchema.java index 79be0d71..db30eccf 100644 --- a/src/main/java/dev/openfga/sdk/api/model/AbstractOpenApiSchema.java +++ b/src/main/java/dev/openfga/sdk/api/model/AbstractOpenApiSchema.java @@ -137,8 +137,7 @@ public int hashCode() { public Boolean isNullable() { if (Boolean.TRUE.equals(isNullable)) { return Boolean.TRUE; - } else { - return Boolean.FALSE; } + return Boolean.FALSE; } } diff --git a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java index ca9feca6..c97fc912 100644 --- a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java @@ -2040,6 +2040,38 @@ public void shouldSplitBatchesSuccessfully(WireMockRuntimeInfo wireMockRuntimeIn assertEquals("relation not found", response3.getError().getMessage()); } + @Test + public void batchCheck_withOptions() throws Exception { + // Given + String postUrl = String.format("https://api.fga.example/stores/%s/batch-check", DEFAULT_STORE_ID); + String expectedBody = String.format( + "{\"checks\":[{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"context\":null,\"correlation_id\":\"cor-1\"}],\"authorization_model_id\":\"%s\",\"consistency\":\"%s\"}", + DEFAULT_USER, + DEFAULT_RELATION, + DEFAULT_OBJECT, + DEFAULT_AUTH_MODEL_ID, + ConsistencyPreference.MINIMIZE_LATENCY); + mockHttpClient.onPost(postUrl).withBody(is(expectedBody)).doReturn(200, "{\"result\":{}}"); + + ClientBatchCheckItem item = new ClientBatchCheckItem() + .user(DEFAULT_USER) + .relation(DEFAULT_RELATION) + ._object(DEFAULT_OBJECT) + .correlationId("cor-1"); + ClientBatchCheckRequest request = new ClientBatchCheckRequest().checks(List.of(item)); + ClientBatchCheckOptions options = new ClientBatchCheckOptions() + .authorizationModelId(DEFAULT_AUTH_MODEL_ID) + .consistency(ConsistencyPreference.MINIMIZE_LATENCY); + + // When + ClientBatchCheckResponse response = fga.batchCheck(request, options).join(); + + // Then + mockHttpClient.verify().post(postUrl).withBody(is(expectedBody)).called(1); + assertNotNull(response); + 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.