diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index efa49bb..0045d39 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -82,13 +82,17 @@ src/main/java/dev/openfga/sdk/api/auth/CredentialsFlowResponse.java src/main/java/dev/openfga/sdk/api/auth/OAuth2Client.java src/main/java/dev/openfga/sdk/api/client/ApiClient.java src/main/java/dev/openfga/sdk/api/client/ApiResponse.java +src/main/java/dev/openfga/sdk/api/client/ClientCheckRequest.java +src/main/java/dev/openfga/sdk/api/client/ClientExpandRequest.java src/main/java/dev/openfga/sdk/api/client/ClientReadRequest.java src/main/java/dev/openfga/sdk/api/client/ClientWriteRequest.java src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java src/main/java/dev/openfga/sdk/api/configuration/ApiToken.java src/main/java/dev/openfga/sdk/api/configuration/BaseConfiguration.java +src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java src/main/java/dev/openfga/sdk/api/configuration/ClientConfiguration.java src/main/java/dev/openfga/sdk/api/configuration/ClientCredentials.java +src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java src/main/java/dev/openfga/sdk/api/configuration/ClientWriteOptions.java src/main/java/dev/openfga/sdk/api/configuration/Configuration.java diff --git a/README.md b/README.md index 22c966e..72b2b8d 100644 --- a/README.md +++ b/README.md @@ -309,14 +309,16 @@ var request = new ClientReadRequest() ._object("document:roadmap"); // Read all stored relationship tuples -var request = new ClientReadRequest() +var request = new ClientReadRequest(); + +var options = new ClientReadOptions() .pageSize(10) .continuationToken("..."); -var response = fgaClient.read(request).get(); +var response = fgaClient.read(request, options).get(); // In all the above situations, the response will be of the form: -// response = { Tuples: [{ Key: { User, Relation, Object }, Timestamp }, ...]} +// response = { tuples: [{ key: { user, relation, object }, timestamp }, ...]} ``` ##### Write (Create and Delete) Relationship Tuples @@ -374,6 +376,16 @@ Check if a user has a particular relation with an object. [API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/Check) ```java +var request = new ClientCheckRequest() + .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") + .relation("writer") + ._object("document:roadmap"); +var options = new ClientCheckOptions() + // You can rely on the model id set in the configuration or override it for this specific request + .authorizationModelId("01GXSA8YR785C4FYS3C0RTG7B1"); + +var response = fgaClient.check(request, options).get(); +// response.Allowed = true ``` ##### Batch Check @@ -382,6 +394,7 @@ Run a set of [checks](#check). Batch Check will return `allowed: false` if it en If 429s or 5xxs are encountered, the underlying check will retry up to 15 times before giving up. ```java +// Coming soon ``` ##### Expand diff --git a/src/main/java/dev/openfga/sdk/api/client/ClientCheckRequest.java b/src/main/java/dev/openfga/sdk/api/client/ClientCheckRequest.java new file mode 100644 index 0000000..a8d4005 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/client/ClientCheckRequest.java @@ -0,0 +1,58 @@ +/* + * 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; + +public class ClientCheckRequest { + private String user; + private String relation; + private String _object; + + public ClientCheckRequest _object(String _object) { + this._object = _object; + return this; + } + + /** + * Get _object + * @return _object + **/ + public String getObject() { + return _object; + } + + public ClientCheckRequest relation(String relation) { + this.relation = relation; + return this; + } + + /** + * Get relation + * @return relation + **/ + public String getRelation() { + return relation; + } + + public ClientCheckRequest user(String user) { + this.user = user; + return this; + } + + /** + * Get user + * @return user + **/ + public String getUser() { + return user; + } +} diff --git a/src/main/java/dev/openfga/sdk/api/client/ClientExpandRequest.java b/src/main/java/dev/openfga/sdk/api/client/ClientExpandRequest.java new file mode 100644 index 0000000..9394753 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/client/ClientExpandRequest.java @@ -0,0 +1,58 @@ +/* + * 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; + +public class ClientExpandRequest { + private String user; + private String relation; + private String _object; + + public ClientExpandRequest _object(String _object) { + this._object = _object; + return this; + } + + /** + * Get _object + * @return _object + **/ + public String getObject() { + return _object; + } + + public ClientExpandRequest relation(String relation) { + this.relation = relation; + return this; + } + + /** + * Get relation + * @return relation + **/ + public String getRelation() { + return relation; + } + + public ClientExpandRequest user(String user) { + this.user = user; + return this; + } + + /** + * Get user + * @return user + **/ + public String getUser() { + return user; + } +} 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 1e7cace..66270f9 100644 --- a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java +++ b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java @@ -305,10 +305,37 @@ public CompletableFuture deleteTuples(TupleKeys tupleKeys) throws FgaInv * * @throws FgaInvalidParameterException When the Store ID is null, empty, or whitespace */ - public CompletableFuture check(CheckRequest request) throws FgaInvalidParameterException { + public CompletableFuture check(ClientCheckRequest request) throws FgaInvalidParameterException { + return check(request, null); + } + + /** + * Check - Check if a user has a particular relation with an object (evaluates) + * + * @throws FgaInvalidParameterException When the Store ID is null, empty, or whitespace + */ + public CompletableFuture check(ClientCheckRequest request, ClientCheckOptions options) + throws FgaInvalidParameterException { configuration.assertValid(); String storeId = configuration.getStoreIdChecked(); - return call(() -> api.check(storeId, request)); + + CheckRequest body = new CheckRequest(); + + if (request != null) { + body.tupleKey(new TupleKey() + .user(request.getUser()) + .relation(request.getRelation()) + ._object(request.getObject())); + } + + if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { + body.authorizationModelId(options.getAuthorizationModelId()); + } else { + String authorizationModelId = configuration.getAuthorizationModelId(); + body.authorizationModelId(authorizationModelId); + } + + return call(() -> api.check(storeId, body)); } /** @@ -323,10 +350,37 @@ public CompletableFuture check(CheckRequest request) throws FgaIn * * @throws FgaInvalidParameterException When the Store ID is null, empty, or whitespace */ - public CompletableFuture expand(ExpandRequest request) throws FgaInvalidParameterException { + public CompletableFuture expand(ClientExpandRequest request) throws FgaInvalidParameterException { + return expand(request, null); + } + + /** + * Expand - Expands the relationships in userset tree format (evaluates) + * + * @throws FgaInvalidParameterException When the Store ID is null, empty, or whitespace + */ + public CompletableFuture expand(ClientExpandRequest request, ClientExpandOptions options) + throws FgaInvalidParameterException { configuration.assertValid(); String storeId = configuration.getStoreIdChecked(); - return call(() -> api.expand(storeId, request)); + + ExpandRequest body = new ExpandRequest(); + + if (request != null) { + body.tupleKey(new TupleKey() + .user(request.getUser()) + .relation(request.getRelation()) + ._object(request.getObject())); + } + + if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { + body.authorizationModelId(options.getAuthorizationModelId()); + } else { + String authorizationModelId = configuration.getAuthorizationModelId(); + body.authorizationModelId(authorizationModelId); + } + + return call(() -> api.expand(storeId, body)); } /** diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java new file mode 100644 index 0000000..b69a4cf --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java @@ -0,0 +1,26 @@ +/* + * 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.configuration; + +public class ClientCheckOptions { + private String authorizationModelId; + + public ClientCheckOptions authorizationModelId(String authorizationModelId) { + this.authorizationModelId = authorizationModelId; + return this; + } + + public String getAuthorizationModelId() { + return authorizationModelId; + } +} diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java new file mode 100644 index 0000000..66dc5b5 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java @@ -0,0 +1,26 @@ +/* + * 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.configuration; + +public class ClientExpandOptions { + private String authorizationModelId; + + public ClientExpandOptions authorizationModelId(String authorizationModelId) { + this.authorizationModelId = authorizationModelId; + return this; + } + + public String getAuthorizationModelId() { + return authorizationModelId; + } +} diff --git a/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java b/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java index 2b57404..cf33f17 100644 --- a/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java +++ b/src/test-integration/java/dev/openfga/sdk/api/client/OpenFgaClientIntegrationTest.java @@ -217,8 +217,8 @@ public void write_and_check() throws Exception { String authModelId = writeAuthModel(storeId); fga.setAuthorizationModelId(authModelId); ClientWriteRequest writeRequest = new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY)); - CheckRequest checkRequest = new CheckRequest() - .tupleKey(new TupleKey().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC)); + ClientCheckRequest checkRequest = + new ClientCheckRequest().user(DEFAULT_USER).relation("reader")._object(DEFAULT_DOC); // When fga.write(writeRequest).get(); @@ -238,8 +238,8 @@ public void write_and_expand() throws Exception { String authModelId = writeAuthModel(storeId); fga.setAuthorizationModelId(authModelId); ClientWriteRequest writeRequest = new ClientWriteRequest().writes(List.of(DEFAULT_TUPLE_KEY)); - ExpandRequest expandRequest = - new ExpandRequest().tupleKey(new TupleKey()._object(DEFAULT_DOC).relation("reader")); + ClientExpandRequest expandRequest = + new ClientExpandRequest()._object(DEFAULT_DOC).relation("reader"); // When fga.write(writeRequest).get(); 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 3355f3b..9340c0a 100644 --- a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java @@ -1184,19 +1184,17 @@ public void check() 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\":{\"tuple_keys\":[]},\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null}", + "{\"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}"); - CheckRequest request = new CheckRequest() - .tupleKey(new TupleKey() - ._object(DEFAULT_OBJECT) - .relation(DEFAULT_RELATION) - .user(DEFAULT_USER)) - .contextualTuples(new ContextualTupleKeys()) - .authorizationModelId(DEFAULT_AUTH_MODEL_ID); + ClientCheckRequest request = new ClientCheckRequest() + ._object(DEFAULT_OBJECT) + .relation(DEFAULT_RELATION) + .user(DEFAULT_USER); + ClientCheckOptions options = new ClientCheckOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID); // When - CheckResponse response = fga.check(request).get(); + CheckResponse response = fga.check(request, options).get(); // Then mockHttpClient.verify().post(postUrl).withBody(is(expectedBody)).called(1); @@ -1209,7 +1207,7 @@ public void check_storeIdRequired() { clientConfiguration.storeId(null); // When - var exception = assertThrows(FgaInvalidParameterException.class, () -> fga.check(new CheckRequest()) + var exception = assertThrows(FgaInvalidParameterException.class, () -> fga.check(new ClientCheckRequest()) .get()); // Then @@ -1217,17 +1215,6 @@ public void check_storeIdRequired() { "Required parameter storeId was invalid when calling ClientConfiguration.", exception.getMessage()); } - @Test - public void check_bodyRequired() { - // When - ExecutionException execException = - assertThrows(ExecutionException.class, () -> fga.check(null).get()); - - // Then - ApiException exception = assertInstanceOf(ApiException.class, execException.getCause()); - assertEquals("Missing the required parameter 'body' when calling check", exception.getMessage()); - } - @Test public void check_400() throws Exception { // Given @@ -1237,8 +1224,9 @@ public void check_400() throws Exception { .doReturn(400, "{\"code\":\"validation_error\",\"message\":\"Generic validation error\"}"); // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.check(new CheckRequest()).get()); + ExecutionException execException = + assertThrows(ExecutionException.class, () -> fga.check(new ClientCheckRequest()) + .get()); // Then mockHttpClient.verify().post(postUrl).called(1); @@ -1258,8 +1246,9 @@ public void check_404() throws Exception { .doReturn(404, "{\"code\":\"undefined_endpoint\",\"message\":\"Endpoint not enabled\"}"); // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.check(new CheckRequest()).get()); + ExecutionException execException = + assertThrows(ExecutionException.class, () -> fga.check(new ClientCheckRequest()) + .get()); // Then mockHttpClient.verify().post(postUrl).called(1); @@ -1278,8 +1267,9 @@ public void check_500() throws Exception { .doReturn(500, "{\"code\":\"internal_error\",\"message\":\"Internal Server Error\"}"); // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.check(new CheckRequest()).get()); + ExecutionException execException = + assertThrows(ExecutionException.class, () -> fga.check(new ClientCheckRequest()) + .get()); // Then mockHttpClient.verify().post(postUrl).called(1); @@ -1304,15 +1294,14 @@ public void expandTest() throws Exception { "{\"tree\":{\"root\":{\"union\":{\"nodes\":[{\"leaf\":{\"users\":{\"users\":[\"%s\"]}}}]}}}}", DEFAULT_USER); mockHttpClient.onPost(postPath).withBody(is(expectedBody)).doReturn(200, responseBody); - ExpandRequest request = new ExpandRequest() - .authorizationModelId(DEFAULT_AUTH_MODEL_ID) - .tupleKey(new TupleKey() - ._object(DEFAULT_OBJECT) - .relation(DEFAULT_RELATION) - .user(DEFAULT_USER)); + ClientExpandRequest request = new ClientExpandRequest() + .user(DEFAULT_USER) + .relation(DEFAULT_RELATION) + ._object(DEFAULT_OBJECT); + ClientExpandOptions options = new ClientExpandOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID); // When - ExpandResponse response = fga.expand(request).get(); + ExpandResponse response = fga.expand(request, options).get(); // Then mockHttpClient.verify().post(postPath).withBody(is(expectedBody)).called(1); @@ -1336,7 +1325,7 @@ public void expand_storeIdRequired() { clientConfiguration.storeId(null); // When - var exception = assertThrows(FgaInvalidParameterException.class, () -> fga.expand(new ExpandRequest()) + var exception = assertThrows(FgaInvalidParameterException.class, () -> fga.expand(new ClientExpandRequest()) .get()); // Then @@ -1344,17 +1333,6 @@ public void expand_storeIdRequired() { "Required parameter storeId was invalid when calling ClientConfiguration.", exception.getMessage()); } - @Test - public void expand_bodyRequired() { - // When - ExecutionException execException = - assertThrows(ExecutionException.class, () -> fga.expand(null).get()); - - // Then - ApiException exception = assertInstanceOf(ApiException.class, execException.getCause()); - assertEquals("Missing the required parameter 'body' when calling expand", exception.getMessage()); - } - @Test public void expand_400() throws Exception { // Given @@ -1364,8 +1342,9 @@ public void expand_400() throws Exception { .doReturn(400, "{\"code\":\"validation_error\",\"message\":\"Generic validation error\"}"); // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.expand(new ExpandRequest()).get()); + ExecutionException execException = + assertThrows(ExecutionException.class, () -> fga.expand(new ClientExpandRequest()) + .get()); // Then mockHttpClient.verify().post(postUrl).called(1); @@ -1385,8 +1364,9 @@ public void expand_404() throws Exception { .doReturn(404, "{\"code\":\"undefined_endpoint\",\"message\":\"Endpoint not enabled\"}"); // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.expand(new ExpandRequest()).get()); + ExecutionException execException = + assertThrows(ExecutionException.class, () -> fga.expand(new ClientExpandRequest()) + .get()); // Then mockHttpClient.verify().post(postUrl).called(1); @@ -1405,8 +1385,9 @@ public void expand_500() throws Exception { .doReturn(500, "{\"code\":\"internal_error\",\"message\":\"Internal Server Error\"}"); // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.expand(new ExpandRequest()).get()); + ExecutionException execException = + assertThrows(ExecutionException.class, () -> fga.expand(new ClientExpandRequest()) + .get()); // Then mockHttpClient.verify().post(postUrl).called(1);