Skip to content

Commit

Permalink
test: add tests on batchCheck()
Browse files Browse the repository at this point in the history
  • Loading branch information
booniepepper committed Nov 8, 2023
1 parent 31870f6 commit a951dd2
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* OpenFGA
* A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar.
*
* The version of the OpenAPI document: 0.1
* Contact: community@openfga.dev
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/

package dev.openfga.sdk.api.client;

import dev.openfga.sdk.api.model.CheckResponse;
import java.util.List;
import java.util.Map;

public class ClientBatchCheckResponse extends CheckResponse {
private final ClientCheckRequest request;
private final Throwable throwable;
private final int statusCode;
private final Map<String, List<String>> headers;
private final String rawResponse;

public ClientBatchCheckResponse(
ClientCheckRequest request, ClientCheckResponse clientCheckResponse, Throwable throwable) {
this.request = request;
this.throwable = throwable;

this.statusCode = clientCheckResponse.getStatusCode();
this.headers = clientCheckResponse.getHeaders();
this.rawResponse = clientCheckResponse.getRawResponse();
this.setAllowed(clientCheckResponse.getAllowed());
this.setResolution(clientCheckResponse.getResolution());
}

public ClientCheckRequest getRequest() {
return request;
}

/**
* Returns the result of the check.
* <p>
* If the HTTP request was unsuccessful, this result will be null. If this is the case, you can examine the
* original request with {@link ClientBatchCheckResponse#getRequest()} and the exception with
* {@link ClientBatchCheckResponse#getThrowable()}.
*
* @return the check result. Is null if the HTTP request was unsuccessful.
*/
@Override
public Boolean getAllowed() {
return super.getAllowed();
}

/**
* Returns the caught exception if the HTTP request was unsuccessful.
* <p>
* If the HTTP request was unsuccessful, this result will be null. If this is the case, you can examine the
* original request with {@link ClientBatchCheckResponse#getRequest()} and the exception with
* {@link ClientBatchCheckResponse#getThrowable()}.
*
* @return the caught exception. Is null if the HTTP request was successful.
*/
public Throwable getThrowable() {
return throwable;
}

public int getStatusCode() {
return statusCode;
}

public Map<String, List<String>> getHeaders() {
return headers;
}

public String getRawResponse() {
return rawResponse;
}
}
29 changes: 19 additions & 10 deletions src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,7 @@
import dev.openfga.sdk.errors.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.*;
import java.util.function.Consumer;

public class OpenFgaClient {
Expand Down Expand Up @@ -387,26 +385,37 @@ public CompletableFuture<ClientCheckResponse> check(ClientCheckRequest request,
*
* @throws FgaInvalidParameterException When the Store ID is null, empty, or whitespace
*/
public CompletableFuture<List<ClientCheckResponse>> batchCheck(
List<ClientCheckRequest> requests, ClientBatchCheckOptions options) {
public CompletableFuture<List<ClientBatchCheckResponse>> batchCheck(
List<ClientCheckRequest> requests, ClientBatchCheckOptions options) throws FgaInvalidParameterException {
configuration.assertValid();
configuration.assertValidStoreId();

int maxParallelRequests = options.getMaxParallelRequests() != null
? options.getMaxParallelRequests()
: DEFAULT_MAX_METHOD_PARALLEL_REQS;
var executor = Executors.newWorkStealingPool(maxParallelRequests);
var latch = new CountDownLatch(requests.size());

var responses = new ConcurrentLinkedQueue<ClientCheckResponse>();
var responses = new ConcurrentLinkedQueue<ClientBatchCheckResponse>();

final var clientCheckOptions = options.asClientCheckOptions();

Consumer<ClientCheckRequest> singleClientCheckRequest =
request -> call(() -> this.check(request, clientCheckOptions)).thenApply(responses::add);
request -> call(() -> this.check(request, clientCheckOptions)).handle((response, exception) -> {
try {
responses.add(new ClientBatchCheckResponse(request, response, exception));
} finally {
latch.countDown();
}
return true;
});

requests.forEach(request -> executor.execute(() -> singleClientCheckRequest.accept(request)));

try {
executor.wait();
latch.await();
return CompletableFuture.completedFuture(new ArrayList<>(responses));
} catch (InterruptedException e) {
} catch (Exception e) {
return CompletableFuture.failedFuture(e);
}
}
Expand Down Expand Up @@ -573,7 +582,7 @@ private interface CheckedInvocation<R> {
private <T> CompletableFuture<T> call(CheckedInvocation<T> action) {
try {
return action.call();
} catch (FgaInvalidParameterException | ApiException exception) {
} catch (Exception exception) {
return CompletableFuture.failedFuture(exception);
}
}
Expand Down
53 changes: 53 additions & 0 deletions src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import java.time.Duration;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -1309,6 +1311,57 @@ public void check_500() throws Exception {
"{\"code\":\"internal_error\",\"message\":\"Internal Server Error\"}", exception.getResponseData());
}

/**
* Check whether a user is authorized to access an object.
*/
@Test
public void batchCheck() throws Exception {
// Given
String postUrl = String.format("https://localhost/stores/%s/check", DEFAULT_STORE_ID);
String expectedBody = String.format(
"{\"tuple_key\":{\"object\":\"%s\",\"relation\":\"%s\",\"user\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null}",
DEFAULT_OBJECT, DEFAULT_RELATION, DEFAULT_USER);
mockHttpClient.onPost(postUrl).withBody(is(expectedBody)).doReturn(200, "{\"allowed\":true}");
ClientCheckRequest request = new ClientCheckRequest()
._object(DEFAULT_OBJECT)
.relation(DEFAULT_RELATION)
.user(DEFAULT_USER);
ClientBatchCheckOptions options = new ClientBatchCheckOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID);

// When
List<ClientBatchCheckResponse> response =
fga.batchCheck(List.of(request), options).get();

// Then
mockHttpClient.verify().post(postUrl).withBody(is(expectedBody)).called(1);
assertEquals(Boolean.TRUE, response.get(0).getAllowed());
}

@Test
public void batchCheck_twentyTimes() throws Exception {
// Given
String postUrl = String.format("https://localhost/stores/%s/check", DEFAULT_STORE_ID);
String expectedBody = String.format(
"{\"tuple_key\":{\"object\":\"%s\",\"relation\":\"%s\",\"user\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null}",
DEFAULT_OBJECT, DEFAULT_RELATION, DEFAULT_USER);
mockHttpClient.onPost(postUrl).withBody(is(expectedBody)).doReturn(200, "{\"allowed\":true}");
List<ClientCheckRequest> requests = IntStream.range(0, 20)
.mapToObj(ignored -> new ClientCheckRequest()
._object(DEFAULT_OBJECT)
.relation(DEFAULT_RELATION)
.user(DEFAULT_USER))
.collect(Collectors.toList());
ClientBatchCheckOptions options = new ClientBatchCheckOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID);

// When
List<ClientBatchCheckResponse> responses =
fga.batchCheck(requests, options).get();

// Then
mockHttpClient.verify().post(postUrl).withBody(is(expectedBody)).called(20);
responses.forEach(response -> assertEquals(Boolean.TRUE, response.getAllowed()));
}

/**
* Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason
* about and debug a certain relationship.
Expand Down

0 comments on commit a951dd2

Please sign in to comment.