From 5f42012c88d244af06fff91d07bd0ab0ab61c7a5 Mon Sep 17 00:00:00 2001 From: Jim Anderson Date: Fri, 23 Aug 2024 12:53:18 -0500 Subject: [PATCH] feat: add support for consistency parameter --- .openapi-generator/FILES | 2 + README.md | 4 +- docs/CheckRequest.md | 1 + docs/ConsistencyPreference.md | 17 ++++ docs/ExpandRequest.md | 1 + docs/ListObjectsRequest.md | 1 + docs/ListUsersRequest.md | 1 + docs/OpenFgaApi.md | 12 +-- docs/ReadRequest.md | 1 + .../java/dev/openfga/sdk/api/OpenFgaApi.java | 8 +- .../openfga/sdk/api/client/OpenFgaClient.java | 62 +++++++++--- .../ClientBatchCheckOptions.java | 16 ++- .../api/configuration/ClientCheckOptions.java | 11 +++ .../configuration/ClientExpandOptions.java | 11 +++ .../ClientListObjectsOptions.java | 11 +++ .../ClientListRelationsOptions.java | 14 ++- .../configuration/ClientListUsersOptions.java | 11 +++ .../api/configuration/ClientReadOptions.java | 11 +++ .../openfga/sdk/api/model/CheckRequest.java | 44 ++++++++- .../sdk/api/model/ConsistencyPreference.java | 69 +++++++++++++ .../openfga/sdk/api/model/ExpandRequest.java | 47 ++++++++- .../sdk/api/model/ListObjectsRequest.java | 44 ++++++++- .../sdk/api/model/ListUsersRequest.java | 45 ++++++++- .../openfga/sdk/api/model/ReadRequest.java | 44 ++++++++- .../dev/openfga/sdk/api/OpenFgaApiTest.java | 35 ++++--- .../sdk/api/client/OpenFgaClientTest.java | 98 ++++++++++++------- 26 files changed, 525 insertions(+), 96 deletions(-) create mode 100644 docs/ConsistencyPreference.md create mode 100644 src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index 683c204a..d7553dbb 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -29,6 +29,7 @@ docs/Computed.md docs/Condition.md docs/ConditionMetadata.md docs/ConditionParamTypeRef.md +docs/ConsistencyPreference.md docs/ContextualTupleKeys.md docs/CreateStoreRequest.md docs/CreateStoreResponse.md @@ -199,6 +200,7 @@ src/main/java/dev/openfga/sdk/api/model/Computed.java src/main/java/dev/openfga/sdk/api/model/Condition.java src/main/java/dev/openfga/sdk/api/model/ConditionMetadata.java src/main/java/dev/openfga/sdk/api/model/ConditionParamTypeRef.java +src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java src/main/java/dev/openfga/sdk/api/model/ContextualTupleKeys.java src/main/java/dev/openfga/sdk/api/model/CreateStoreRequest.java src/main/java/dev/openfga/sdk/api/model/CreateStoreResponse.java diff --git a/README.md b/README.md index 7bea2461..341fde57 100644 --- a/README.md +++ b/README.md @@ -888,7 +888,7 @@ public class Example { | [**getStore**](docs/OpenFgaApi.md#getstore) | **GET** /stores/{store_id} | Get a store | | [**listObjects**](docs/OpenFgaApi.md#listobjects) | **POST** /stores/{store_id}/list-objects | List all objects of the given type that the user has a relation with | | [**listStores**](docs/OpenFgaApi.md#liststores) | **GET** /stores | List all stores | -| [**listUsers**](docs/OpenFgaApi.md#listusers) | **POST** /stores/{store_id}/list-users | [EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. | +| [**listUsers**](docs/OpenFgaApi.md#listusers) | **POST** /stores/{store_id}/list-users | List the users matching the provided filter who have a certain relation to a particular type. | | [**read**](docs/OpenFgaApi.md#read) | **POST** /stores/{store_id}/read | Get tuples from the store that matches a query, without following userset rewrite rules | | [**readAssertions**](docs/OpenFgaApi.md#readassertions) | **GET** /stores/{store_id}/assertions/{authorization_model_id} | Read assertions for an authorization model ID | | [**readAuthorizationModel**](docs/OpenFgaApi.md#readauthorizationmodel) | **GET** /stores/{store_id}/authorization-models/{id} | Return a particular version of an authorization model | @@ -927,6 +927,8 @@ public class Example { - [ConditionParamTypeRef](https://github.com/openfga/java-sdk/blob/main/docs/ConditionParamTypeRef.md) +- [ConsistencyPreference](https://github.com/openfga/java-sdk/blob/main/docs/ConsistencyPreference.md) + - [ContextualTupleKeys](https://github.com/openfga/java-sdk/blob/main/docs/ContextualTupleKeys.md) - [CreateStoreRequest](https://github.com/openfga/java-sdk/blob/main/docs/CreateStoreRequest.md) diff --git a/docs/CheckRequest.md b/docs/CheckRequest.md index af3428ac..d1873f94 100644 --- a/docs/CheckRequest.md +++ b/docs/CheckRequest.md @@ -12,6 +12,7 @@ |**authorizationModelId** | **String** | | [optional] | |**trace** | **Boolean** | Defaults to false. Making it true has performance implications. | [optional] [readonly] | |**context** | **Object** | Additional request context that will be used to evaluate any ABAC conditions encountered in the query evaluation. | [optional] | +|**consistency** | **ConsistencyPreference** | | [optional] | diff --git a/docs/ConsistencyPreference.md b/docs/ConsistencyPreference.md new file mode 100644 index 00000000..309e3beb --- /dev/null +++ b/docs/ConsistencyPreference.md @@ -0,0 +1,17 @@ + + +# ConsistencyPreference + +## Enum + + +* `UNSPECIFIED` (value: `"UNSPECIFIED"`) + +* `MINIMIZE_LATENCY` (value: `"MINIMIZE_LATENCY"`) + +* `HIGHER_CONSISTENCY` (value: `"HIGHER_CONSISTENCY"`) + +* `UNKNOWN_DEFAULT_OPEN_API` (value: `"unknown_default_open_api"`) + + + diff --git a/docs/ExpandRequest.md b/docs/ExpandRequest.md index 347ca0c7..91ddc2e9 100644 --- a/docs/ExpandRequest.md +++ b/docs/ExpandRequest.md @@ -9,6 +9,7 @@ |------------ | ------------- | ------------- | -------------| |**tupleKey** | [**ExpandRequestTupleKey**](ExpandRequestTupleKey.md) | | | |**authorizationModelId** | **String** | | [optional] | +|**consistency** | **ConsistencyPreference** | | [optional] | diff --git a/docs/ListObjectsRequest.md b/docs/ListObjectsRequest.md index 142f84a7..be8c7fd1 100644 --- a/docs/ListObjectsRequest.md +++ b/docs/ListObjectsRequest.md @@ -13,6 +13,7 @@ |**user** | **String** | | | |**contextualTuples** | [**ContextualTupleKeys**](ContextualTupleKeys.md) | | [optional] | |**context** | **Object** | Additional request context that will be used to evaluate any ABAC conditions encountered in the query evaluation. | [optional] | +|**consistency** | **ConsistencyPreference** | | [optional] | diff --git a/docs/ListUsersRequest.md b/docs/ListUsersRequest.md index 2350bfd5..89bc67c0 100644 --- a/docs/ListUsersRequest.md +++ b/docs/ListUsersRequest.md @@ -13,6 +13,7 @@ |**userFilters** | [**List<UserTypeFilter>**](UserTypeFilter.md) | The type of results returned. Only accepts exactly one value. | | |**contextualTuples** | [**List<TupleKey>**](TupleKey.md) | | [optional] | |**context** | **Object** | Additional request context that will be used to evaluate any ABAC conditions encountered in the query evaluation. | [optional] | +|**consistency** | **ConsistencyPreference** | | [optional] | diff --git a/docs/OpenFgaApi.md b/docs/OpenFgaApi.md index 4fc19af1..efefe558 100644 --- a/docs/OpenFgaApi.md +++ b/docs/OpenFgaApi.md @@ -18,8 +18,8 @@ All URIs are relative to *http://localhost* | [**listObjectsWithHttpInfo**](OpenFgaApi.md#listObjectsWithHttpInfo) | **POST** /stores/{store_id}/list-objects | List all objects of the given type that the user has a relation with | | [**listStores**](OpenFgaApi.md#listStores) | **GET** /stores | List all stores | | [**listStoresWithHttpInfo**](OpenFgaApi.md#listStoresWithHttpInfo) | **GET** /stores | List all stores | -| [**listUsers**](OpenFgaApi.md#listUsers) | **POST** /stores/{store_id}/list-users | [EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. | -| [**listUsersWithHttpInfo**](OpenFgaApi.md#listUsersWithHttpInfo) | **POST** /stores/{store_id}/list-users | [EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. | +| [**listUsers**](OpenFgaApi.md#listUsers) | **POST** /stores/{store_id}/list-users | List the users matching the provided filter who have a certain relation to a particular type. | +| [**listUsersWithHttpInfo**](OpenFgaApi.md#listUsersWithHttpInfo) | **POST** /stores/{store_id}/list-users | List the users matching the provided filter who have a certain relation to a particular type. | | [**read**](OpenFgaApi.md#read) | **POST** /stores/{store_id}/read | Get tuples from the store that matches a query, without following userset rewrite rules | | [**readWithHttpInfo**](OpenFgaApi.md#readWithHttpInfo) | **POST** /stores/{store_id}/read | Get tuples from the store that matches a query, without following userset rewrite rules | | [**readAssertions**](OpenFgaApi.md#readAssertions) | **GET** /stores/{store_id}/assertions/{authorization_model_id} | Read assertions for an authorization model ID | @@ -1142,9 +1142,9 @@ No authorization required > CompletableFuture listUsers(storeId, body) -[EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. +List the users matching the provided filter who have a certain relation to a particular type. -The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. This API is available in an experimental capacity and can be enabled with the `--experimentals enable-list-users` flag. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. +The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. ### Example @@ -1216,9 +1216,9 @@ No authorization required > CompletableFuture> listUsers listUsersWithHttpInfo(storeId, body) -[EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. +List the users matching the provided filter who have a certain relation to a particular type. -The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. This API is available in an experimental capacity and can be enabled with the `--experimentals enable-list-users` flag. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. +The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. ### Example diff --git a/docs/ReadRequest.md b/docs/ReadRequest.md index 832f57cb..88f2a9f0 100644 --- a/docs/ReadRequest.md +++ b/docs/ReadRequest.md @@ -10,6 +10,7 @@ |**tupleKey** | [**ReadRequestTupleKey**](ReadRequestTupleKey.md) | | [optional] | |**pageSize** | **Integer** | | [optional] | |**continuationToken** | **String** | | [optional] | +|**consistency** | **ConsistencyPreference** | | [optional] | diff --git a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java index ea40ec59..b25ec677 100644 --- a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java +++ b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java @@ -457,8 +457,8 @@ private CompletableFuture> listStores( } /** - * [EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. - * The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. This API is available in an experimental capacity and can be enabled with the `--experimentals enable-list-users` flag. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. + * List the users matching the provided filter who have a certain relation to a particular type. + * The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. * @param storeId (required) * @param body (required) * @return CompletableFuture<ApiResponse<ListUsersResponse>> @@ -470,8 +470,8 @@ public CompletableFuture> listUsers(String storeI } /** - * [EXPERIMENTAL] List the users matching the provided filter who have a certain relation to a particular type. - * The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. This API is available in an experimental capacity and can be enabled with the `--experimentals enable-list-users` flag. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. + * List the users matching the provided filter who have a certain relation to a particular type. + * The ListUsers API returns a list of all the users of a specific type that have a relation to a given object. To arrive at a result, the API uses: an authorization model, explicit tuples written through the Write API, contextual tuples present in the request, and implicit tuples that exist by virtue of applying set theory (such as `document:2021-budget#viewer@document:2021-budget#viewer`; the set of users who are viewers of `document:2021-budget` are the set of users who are the viewers of `document:2021-budget`). An `authorization_model_id` may be specified in the body. If it is not specified, the latest authorization model ID will be used. It is strongly recommended to specify authorization model id for better performance. You may also specify `contextual_tuples` that will be treated as regular tuples. Each of these tuples may have an associated `condition`. You may also provide a `context` object that will be used to evaluate the conditioned tuples in the system. It is strongly recommended to provide a value for all the input parameters of all the conditions, to ensure that all tuples be evaluated correctly. The response will contain the related users in an array in the \"users\" field of the response. These results may include specific objects, usersets or type-bound public access. Each of these types of results is encoded in its own type and not represented as a string.In cases where a type-bound public acces result is returned (e.g. `user:*`), it cannot be inferred that all subjects of that type have a relation to the object; it is possible that negations exist and checks should still be queried on individual subjects to ensure access to that document.The number of users in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_USERS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_USERS_MAX_RESULTS, whichever is hit first. The returned users will not be sorted, and therefore two identical calls may yield different sets of users. * @param storeId (required) * @param body (required) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with 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 3f357f0c..77cf1d9d 100644 --- a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java +++ b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java @@ -329,6 +329,9 @@ public CompletableFuture read(ClientReadRequest request, Cli if (options != null) { body.pageSize(options.getPageSize()).continuationToken(options.getContinuationToken()); + if (options.getConsistency() != null) { + body.consistency(options.getConsistency()); + } } var overrides = new ConfigurationOverride().addHeaders(options); @@ -542,12 +545,18 @@ public CompletableFuture check(ClientCheckRequest request, String storeId = configuration.getStoreIdChecked(); CheckRequest body = request.asCheckRequest(); + if (options != null) { + if (options.getConsistency() != null) { + body.consistency(options.getConsistency()); + } - if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { - body.authorizationModelId(options.getAuthorizationModelId()); - } else { - String authorizationModelId = configuration.getAuthorizationModelId(); + // Set authorizationModelId from options if available; otherwise, use the default from configuration + String authorizationModelId = !isNullOrWhitespace(options.getAuthorizationModelId()) + ? options.getAuthorizationModelId() + : configuration.getAuthorizationModelId(); body.authorizationModelId(authorizationModelId); + } else { + body.setAuthorizationModelId(configuration.getAuthorizationModelId()); } var overrides = new ConfigurationOverride().addHeaders(options); @@ -638,11 +647,18 @@ public CompletableFuture expand(ClientExpandRequest reques new ExpandRequestTupleKey().relation(request.getRelation())._object(request.getObject())); } - if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { - body.authorizationModelId(options.getAuthorizationModelId()); - } else { - String authorizationModelId = configuration.getAuthorizationModelId(); + if (options != null) { + 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(); body.authorizationModelId(authorizationModelId); + } else { + body.setAuthorizationModelId(configuration.getAuthorizationModelId()); } var overrides = new ConfigurationOverride().addHeaders(options); @@ -684,11 +700,18 @@ public CompletableFuture listObjects( } } - if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { - body.authorizationModelId(options.getAuthorizationModelId()); - } else { - String authorizationModelId = configuration.getAuthorizationModelId(); + if (options != null) { + 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(); body.authorizationModelId(authorizationModelId); + } else { + body.setAuthorizationModelId(configuration.getAuthorizationModelId()); } var overrides = new ConfigurationOverride().addHeaders(options); @@ -768,11 +791,18 @@ public CompletableFuture listUsers( } } - if (options != null && !isNullOrWhitespace(options.getAuthorizationModelId())) { - body.authorizationModelId(options.getAuthorizationModelId()); - } else { - String authorizationModelId = configuration.getAuthorizationModelId(); + if (options != null) { + 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(); body.authorizationModelId(authorizationModelId); + } else { + body.setAuthorizationModelId(configuration.getAuthorizationModelId()); } var overrides = new ConfigurationOverride().addHeaders(options); diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java index b86e9837..1a706a4a 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientBatchCheckOptions.java @@ -12,12 +12,14 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientBatchCheckOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private Integer maxParallelRequests; private String authorizationModelId; + private ConsistencyPreference consistency; public ClientBatchCheckOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -47,7 +49,19 @@ public String getAuthorizationModelId() { return authorizationModelId; } + public ClientBatchCheckOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } + public ClientCheckOptions asClientCheckOptions() { - return new ClientCheckOptions().additionalHeaders(additionalHeaders).authorizationModelId(authorizationModelId); + return new ClientCheckOptions() + .additionalHeaders(additionalHeaders) + .authorizationModelId(authorizationModelId) + .consistency(consistency); } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java index 38202413..b50a9e8e 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientCheckOptions.java @@ -12,11 +12,13 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientCheckOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private String authorizationModelId; + private ConsistencyPreference consistency; public ClientCheckOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -36,4 +38,13 @@ public ClientCheckOptions authorizationModelId(String authorizationModelId) { public String getAuthorizationModelId() { return authorizationModelId; } + + public ClientCheckOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java index ef1448da..8b40e8ca 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientExpandOptions.java @@ -12,11 +12,13 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientExpandOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private String authorizationModelId; + private ConsistencyPreference consistency; public ClientExpandOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -36,4 +38,13 @@ public ClientExpandOptions authorizationModelId(String authorizationModelId) { public String getAuthorizationModelId() { return authorizationModelId; } + + public ClientExpandOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java index 4ac1615c..b24b80cc 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListObjectsOptions.java @@ -12,11 +12,13 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientListObjectsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private String authorizationModelId; + private ConsistencyPreference consistency; public ClientListObjectsOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -36,4 +38,13 @@ public ClientListObjectsOptions authorizationModelId(String authorizationModelId public String getAuthorizationModelId() { return authorizationModelId; } + + public ClientListObjectsOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java index 3abde9da..0ae80e5b 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListRelationsOptions.java @@ -12,12 +12,14 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientListRelationsOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private Integer maxParallelRequests; private String authorizationModelId; + private ConsistencyPreference consistency; public ClientListRelationsOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -47,9 +49,19 @@ public String getAuthorizationModelId() { return authorizationModelId; } + public ClientListRelationsOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } + public ClientBatchCheckOptions asClientBatchCheckOptions() { return new ClientBatchCheckOptions() .authorizationModelId(authorizationModelId) - .maxParallelRequests(maxParallelRequests); + .maxParallelRequests(maxParallelRequests) + .consistency(consistency); } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java index 51783d6a..9017b905 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientListUsersOptions.java @@ -12,11 +12,13 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientListUsersOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private String authorizationModelId; + private ConsistencyPreference consistency; public ClientListUsersOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -36,4 +38,13 @@ public ClientListUsersOptions authorizationModelId(String authorizationModelId) public String getAuthorizationModelId() { return authorizationModelId; } + + public ClientListUsersOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } } diff --git a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java index b13cb44b..cfa880c1 100644 --- a/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java +++ b/src/main/java/dev/openfga/sdk/api/configuration/ClientReadOptions.java @@ -12,12 +12,14 @@ package dev.openfga.sdk.api.configuration; +import dev.openfga.sdk.api.model.ConsistencyPreference; import java.util.Map; public class ClientReadOptions implements AdditionalHeadersSupplier { private Map additionalHeaders; private Integer pageSize; private String continuationToken; + private ConsistencyPreference consistency; public ClientReadOptions additionalHeaders(Map additionalHeaders) { this.additionalHeaders = additionalHeaders; @@ -46,4 +48,13 @@ public ClientReadOptions continuationToken(String continuationToken) { public String getContinuationToken() { return continuationToken; } + + public ClientReadOptions consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + public ConsistencyPreference getConsistency() { + return consistency; + } } diff --git a/src/main/java/dev/openfga/sdk/api/model/CheckRequest.java b/src/main/java/dev/openfga/sdk/api/model/CheckRequest.java index a09c1507..cccba86e 100644 --- a/src/main/java/dev/openfga/sdk/api/model/CheckRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/CheckRequest.java @@ -29,7 +29,8 @@ CheckRequest.JSON_PROPERTY_CONTEXTUAL_TUPLES, CheckRequest.JSON_PROPERTY_AUTHORIZATION_MODEL_ID, CheckRequest.JSON_PROPERTY_TRACE, - CheckRequest.JSON_PROPERTY_CONTEXT + CheckRequest.JSON_PROPERTY_CONTEXT, + CheckRequest.JSON_PROPERTY_CONSISTENCY }) public class CheckRequest { public static final String JSON_PROPERTY_TUPLE_KEY = "tuple_key"; @@ -47,6 +48,9 @@ public class CheckRequest { public static final String JSON_PROPERTY_CONTEXT = "context"; private Object context; + public static final String JSON_PROPERTY_CONSISTENCY = "consistency"; + private ConsistencyPreference consistency = ConsistencyPreference.UNSPECIFIED; + public CheckRequest() {} @JsonCreator @@ -154,6 +158,28 @@ public void setContext(Object context) { this.context = context; } + public CheckRequest consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + /** + * Get consistency + * @return consistency + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ConsistencyPreference getConsistency() { + return consistency; + } + + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConsistency(ConsistencyPreference consistency) { + this.consistency = consistency; + } + /** * Return true if this Check_request object is equal to o. */ @@ -170,12 +196,13 @@ public boolean equals(Object o) { && Objects.equals(this.contextualTuples, checkRequest.contextualTuples) && Objects.equals(this.authorizationModelId, checkRequest.authorizationModelId) && Objects.equals(this.trace, checkRequest.trace) - && Objects.equals(this.context, checkRequest.context); + && Objects.equals(this.context, checkRequest.context) + && Objects.equals(this.consistency, checkRequest.consistency); } @Override public int hashCode() { - return Objects.hash(tupleKey, contextualTuples, authorizationModelId, trace, context); + return Objects.hash(tupleKey, contextualTuples, authorizationModelId, trace, context, consistency); } @Override @@ -191,6 +218,7 @@ public String toString() { .append("\n"); sb.append(" trace: ").append(toIndentedString(trace)).append("\n"); sb.append(" context: ").append(toIndentedString(context)).append("\n"); + sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); sb.append("}"); return sb.toString(); } @@ -278,6 +306,16 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `consistency` to the URL query string + if (getConsistency() != null) { + joiner.add(String.format( + "%sconsistency%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getConsistency()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + return joiner.toString(); } } diff --git a/src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java b/src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java new file mode 100644 index 00000000..0f3fc518 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java @@ -0,0 +1,69 @@ +/* + * OpenFGA + * A high performance and flexible authorization/permission engine built for developers and inspired by Google Zanzibar. + * + * The version of the OpenAPI document: 1.x + * 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.model; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +/** + * - UNSPECIFIED: Default if not set. Behavior will be the same as MINIMIZE_LATENCY - MINIMIZE_LATENCY: Minimize latency at the potential expense of lower consistency. - HIGHER_CONSISTENCY: Prefer higher consistency, at the potential expense of increased latency. + */ +public enum ConsistencyPreference { + UNSPECIFIED("UNSPECIFIED"), + + MINIMIZE_LATENCY("MINIMIZE_LATENCY"), + + HIGHER_CONSISTENCY("HIGHER_CONSISTENCY"), + + UNKNOWN_DEFAULT_OPEN_API("unknown_default_open_api"); + + private String value; + + ConsistencyPreference(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static ConsistencyPreference fromValue(String value) { + for (ConsistencyPreference b : ConsistencyPreference.values()) { + if (b.value.equals(value)) { + return b; + } + } + return UNKNOWN_DEFAULT_OPEN_API; + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + if (prefix == null) { + prefix = ""; + } + + return String.format("%s=%s", prefix, this.toString()); + } +} diff --git a/src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java b/src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java index 98f7548c..7772687d 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java @@ -23,7 +23,11 @@ /** * ExpandRequest */ -@JsonPropertyOrder({ExpandRequest.JSON_PROPERTY_TUPLE_KEY, ExpandRequest.JSON_PROPERTY_AUTHORIZATION_MODEL_ID}) +@JsonPropertyOrder({ + ExpandRequest.JSON_PROPERTY_TUPLE_KEY, + ExpandRequest.JSON_PROPERTY_AUTHORIZATION_MODEL_ID, + ExpandRequest.JSON_PROPERTY_CONSISTENCY +}) public class ExpandRequest { public static final String JSON_PROPERTY_TUPLE_KEY = "tuple_key"; private ExpandRequestTupleKey tupleKey; @@ -31,6 +35,9 @@ public class ExpandRequest { public static final String JSON_PROPERTY_AUTHORIZATION_MODEL_ID = "authorization_model_id"; private String authorizationModelId; + public static final String JSON_PROPERTY_CONSISTENCY = "consistency"; + private ConsistencyPreference consistency = ConsistencyPreference.UNSPECIFIED; + public ExpandRequest() {} public ExpandRequest tupleKey(ExpandRequestTupleKey tupleKey) { @@ -77,6 +84,28 @@ public void setAuthorizationModelId(String authorizationModelId) { this.authorizationModelId = authorizationModelId; } + public ExpandRequest consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + /** + * Get consistency + * @return consistency + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ConsistencyPreference getConsistency() { + return consistency; + } + + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConsistency(ConsistencyPreference consistency) { + this.consistency = consistency; + } + /** * Return true if this Expand_request object is equal to o. */ @@ -90,12 +119,13 @@ public boolean equals(Object o) { } ExpandRequest expandRequest = (ExpandRequest) o; return Objects.equals(this.tupleKey, expandRequest.tupleKey) - && Objects.equals(this.authorizationModelId, expandRequest.authorizationModelId); + && Objects.equals(this.authorizationModelId, expandRequest.authorizationModelId) + && Objects.equals(this.consistency, expandRequest.consistency); } @Override public int hashCode() { - return Objects.hash(tupleKey, authorizationModelId); + return Objects.hash(tupleKey, authorizationModelId, consistency); } @Override @@ -106,6 +136,7 @@ public String toString() { sb.append(" authorizationModelId: ") .append(toIndentedString(authorizationModelId)) .append("\n"); + sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); sb.append("}"); return sb.toString(); } @@ -168,6 +199,16 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `consistency` to the URL query string + if (getConsistency() != null) { + joiner.add(String.format( + "%sconsistency%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getConsistency()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + return joiner.toString(); } } diff --git a/src/main/java/dev/openfga/sdk/api/model/ListObjectsRequest.java b/src/main/java/dev/openfga/sdk/api/model/ListObjectsRequest.java index bccb0898..1ca59a31 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ListObjectsRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/ListObjectsRequest.java @@ -29,7 +29,8 @@ ListObjectsRequest.JSON_PROPERTY_RELATION, ListObjectsRequest.JSON_PROPERTY_USER, ListObjectsRequest.JSON_PROPERTY_CONTEXTUAL_TUPLES, - ListObjectsRequest.JSON_PROPERTY_CONTEXT + ListObjectsRequest.JSON_PROPERTY_CONTEXT, + ListObjectsRequest.JSON_PROPERTY_CONSISTENCY }) public class ListObjectsRequest { public static final String JSON_PROPERTY_AUTHORIZATION_MODEL_ID = "authorization_model_id"; @@ -50,6 +51,9 @@ public class ListObjectsRequest { public static final String JSON_PROPERTY_CONTEXT = "context"; private Object context; + public static final String JSON_PROPERTY_CONSISTENCY = "consistency"; + private ConsistencyPreference consistency = ConsistencyPreference.UNSPECIFIED; + public ListObjectsRequest() {} public ListObjectsRequest authorizationModelId(String authorizationModelId) { @@ -184,6 +188,28 @@ public void setContext(Object context) { this.context = context; } + public ListObjectsRequest consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + /** + * Get consistency + * @return consistency + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ConsistencyPreference getConsistency() { + return consistency; + } + + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConsistency(ConsistencyPreference consistency) { + this.consistency = consistency; + } + /** * Return true if this ListObjects_request object is equal to o. */ @@ -201,12 +227,13 @@ public boolean equals(Object o) { && Objects.equals(this.relation, listObjectsRequest.relation) && Objects.equals(this.user, listObjectsRequest.user) && Objects.equals(this.contextualTuples, listObjectsRequest.contextualTuples) - && Objects.equals(this.context, listObjectsRequest.context); + && Objects.equals(this.context, listObjectsRequest.context) + && Objects.equals(this.consistency, listObjectsRequest.consistency); } @Override public int hashCode() { - return Objects.hash(authorizationModelId, type, relation, user, contextualTuples, context); + return Objects.hash(authorizationModelId, type, relation, user, contextualTuples, context, consistency); } @Override @@ -223,6 +250,7 @@ public String toString() { .append(toIndentedString(contextualTuples)) .append("\n"); sb.append(" context: ").append(toIndentedString(context)).append("\n"); + sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); sb.append("}"); return sb.toString(); } @@ -325,6 +353,16 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `consistency` to the URL query string + if (getConsistency() != null) { + joiner.add(String.format( + "%sconsistency%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getConsistency()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + return joiner.toString(); } } diff --git a/src/main/java/dev/openfga/sdk/api/model/ListUsersRequest.java b/src/main/java/dev/openfga/sdk/api/model/ListUsersRequest.java index ecca3417..4efddd2d 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ListUsersRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/ListUsersRequest.java @@ -31,7 +31,8 @@ ListUsersRequest.JSON_PROPERTY_RELATION, ListUsersRequest.JSON_PROPERTY_USER_FILTERS, ListUsersRequest.JSON_PROPERTY_CONTEXTUAL_TUPLES, - ListUsersRequest.JSON_PROPERTY_CONTEXT + ListUsersRequest.JSON_PROPERTY_CONTEXT, + ListUsersRequest.JSON_PROPERTY_CONSISTENCY }) public class ListUsersRequest { public static final String JSON_PROPERTY_AUTHORIZATION_MODEL_ID = "authorization_model_id"; @@ -52,6 +53,9 @@ public class ListUsersRequest { public static final String JSON_PROPERTY_CONTEXT = "context"; private Object context; + public static final String JSON_PROPERTY_CONSISTENCY = "consistency"; + private ConsistencyPreference consistency = ConsistencyPreference.UNSPECIFIED; + public ListUsersRequest() {} public ListUsersRequest authorizationModelId(String authorizationModelId) { @@ -202,6 +206,28 @@ public void setContext(Object context) { this.context = context; } + public ListUsersRequest consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + /** + * Get consistency + * @return consistency + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ConsistencyPreference getConsistency() { + return consistency; + } + + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConsistency(ConsistencyPreference consistency) { + this.consistency = consistency; + } + /** * Return true if this ListUsers_request object is equal to o. */ @@ -219,12 +245,14 @@ public boolean equals(Object o) { && Objects.equals(this.relation, listUsersRequest.relation) && Objects.equals(this.userFilters, listUsersRequest.userFilters) && Objects.equals(this.contextualTuples, listUsersRequest.contextualTuples) - && Objects.equals(this.context, listUsersRequest.context); + && Objects.equals(this.context, listUsersRequest.context) + && Objects.equals(this.consistency, listUsersRequest.consistency); } @Override public int hashCode() { - return Objects.hash(authorizationModelId, _object, relation, userFilters, contextualTuples, context); + return Objects.hash( + authorizationModelId, _object, relation, userFilters, contextualTuples, context, consistency); } @Override @@ -241,6 +269,7 @@ public String toString() { .append(toIndentedString(contextualTuples)) .append("\n"); sb.append(" context: ").append(toIndentedString(context)).append("\n"); + sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); sb.append("}"); return sb.toString(); } @@ -357,6 +386,16 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `consistency` to the URL query string + if (getConsistency() != null) { + joiner.add(String.format( + "%sconsistency%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getConsistency()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + return joiner.toString(); } } diff --git a/src/main/java/dev/openfga/sdk/api/model/ReadRequest.java b/src/main/java/dev/openfga/sdk/api/model/ReadRequest.java index 85c2287b..6d89ca0d 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ReadRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/ReadRequest.java @@ -26,7 +26,8 @@ @JsonPropertyOrder({ ReadRequest.JSON_PROPERTY_TUPLE_KEY, ReadRequest.JSON_PROPERTY_PAGE_SIZE, - ReadRequest.JSON_PROPERTY_CONTINUATION_TOKEN + ReadRequest.JSON_PROPERTY_CONTINUATION_TOKEN, + ReadRequest.JSON_PROPERTY_CONSISTENCY }) public class ReadRequest { public static final String JSON_PROPERTY_TUPLE_KEY = "tuple_key"; @@ -38,6 +39,9 @@ public class ReadRequest { public static final String JSON_PROPERTY_CONTINUATION_TOKEN = "continuation_token"; private String continuationToken; + public static final String JSON_PROPERTY_CONSISTENCY = "consistency"; + private ConsistencyPreference consistency = ConsistencyPreference.UNSPECIFIED; + public ReadRequest() {} public ReadRequest tupleKey(ReadRequestTupleKey tupleKey) { @@ -106,6 +110,28 @@ public void setContinuationToken(String continuationToken) { this.continuationToken = continuationToken; } + public ReadRequest consistency(ConsistencyPreference consistency) { + this.consistency = consistency; + return this; + } + + /** + * Get consistency + * @return consistency + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ConsistencyPreference getConsistency() { + return consistency; + } + + @JsonProperty(JSON_PROPERTY_CONSISTENCY) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setConsistency(ConsistencyPreference consistency) { + this.consistency = consistency; + } + /** * Return true if this Read_request object is equal to o. */ @@ -120,12 +146,13 @@ public boolean equals(Object o) { ReadRequest readRequest = (ReadRequest) o; return Objects.equals(this.tupleKey, readRequest.tupleKey) && Objects.equals(this.pageSize, readRequest.pageSize) - && Objects.equals(this.continuationToken, readRequest.continuationToken); + && Objects.equals(this.continuationToken, readRequest.continuationToken) + && Objects.equals(this.consistency, readRequest.consistency); } @Override public int hashCode() { - return Objects.hash(tupleKey, pageSize, continuationToken); + return Objects.hash(tupleKey, pageSize, continuationToken, consistency); } @Override @@ -137,6 +164,7 @@ public String toString() { sb.append(" continuationToken: ") .append(toIndentedString(continuationToken)) .append("\n"); + sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); sb.append("}"); return sb.toString(); } @@ -209,6 +237,16 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `consistency` to the URL query string + if (getConsistency() != null) { + joiner.add(String.format( + "%sconsistency%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getConsistency()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + return joiner.toString(); } } diff --git a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java index bceac236..1250b360 100644 --- a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java +++ b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java @@ -912,8 +912,8 @@ public void readTest() throws Exception { // Given String postUrl = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/read"; String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"page_size\":null,\"continuation_token\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"page_size\":null,\"continuation_token\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, ConsistencyPreference.HIGHER_CONSISTENCY); String responseBody = String.format( "{\"tuples\":[{\"key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"}}]}", DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); @@ -922,7 +922,8 @@ public void readTest() throws Exception { .tupleKey(new ReadRequestTupleKey() ._object(DEFAULT_OBJECT) .relation(DEFAULT_RELATION) - .user(DEFAULT_USER)); + .user(DEFAULT_USER)) + .consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When var response = fga.read(DEFAULT_STORE_ID, request).get(); @@ -944,8 +945,8 @@ public void read_complexContext() throws Exception { // Given String postUrl = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/read"; String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"page_size\":null,\"continuation_token\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"page_size\":null,\"continuation_token\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, ConsistencyPreference.HIGHER_CONSISTENCY); String responseBody = String.format( "{\"tuples\":[{\"key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"," + "\"condition\":{\"context\":{" @@ -965,7 +966,8 @@ public void read_complexContext() throws Exception { .tupleKey(new ReadRequestTupleKey() ._object(DEFAULT_OBJECT) .relation(DEFAULT_RELATION) - .user(DEFAULT_USER)); + .user(DEFAULT_USER)) + .consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When var response = fga.read(DEFAULT_STORE_ID, request).get(); @@ -1313,8 +1315,8 @@ public void check() throws Exception { // Given String postPath = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/check"; String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":{\"tuple_keys\":[]},\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":{\"tuple_keys\":[]},\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, ConsistencyPreference.MINIMIZE_LATENCY); mockHttpClient.onPost(postPath).withBody(is(expectedBody)).doReturn(200, "{\"allowed\":true}"); CheckRequest request = new CheckRequest() .tupleKey(new CheckRequestTupleKey() @@ -1322,7 +1324,8 @@ public void check() throws Exception { .relation(DEFAULT_RELATION) .user(DEFAULT_USER)) .contextualTuples(new ContextualTupleKeys()) - .authorizationModelId(DEFAULT_AUTH_MODEL_ID); + .authorizationModelId(DEFAULT_AUTH_MODEL_ID) + .consistency(ConsistencyPreference.MINIMIZE_LATENCY); // When var response = fga.check(DEFAULT_STORE_ID, request).get(); @@ -1426,15 +1429,16 @@ public void expandTest() throws Exception { // Given String postPath = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/expand"; String expectedBody = String.format( - "{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\"}", - DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID); + "{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\"}", + DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY); String responseBody = String.format( "{\"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 ExpandRequestTupleKey()._object(DEFAULT_OBJECT).relation(DEFAULT_RELATION)); + .tupleKey(new ExpandRequestTupleKey()._object(DEFAULT_OBJECT).relation(DEFAULT_RELATION)) + .consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When var response = fga.expand(DEFAULT_STORE_ID, request).get(); @@ -1551,8 +1555,8 @@ public void listObjectsTest() throws Exception { // Given String postPath = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/list-objects"; String expectedBody = String.format( - "{\"authorization_model_id\":\"%s\",\"type\":null,\"relation\":\"%s\",\"user\":\"%s\",\"contextual_tuples\":null,\"context\":null}", - DEFAULT_AUTH_MODEL_ID, DEFAULT_RELATION, DEFAULT_USER); + "{\"authorization_model_id\":\"%s\",\"type\":null,\"relation\":\"%s\",\"user\":\"%s\",\"contextual_tuples\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_AUTH_MODEL_ID, DEFAULT_RELATION, DEFAULT_USER, ConsistencyPreference.HIGHER_CONSISTENCY); mockHttpClient .onPost(postPath) .withBody(is(expectedBody)) @@ -1560,7 +1564,8 @@ public void listObjectsTest() throws Exception { ListObjectsRequest request = new ListObjectsRequest() .authorizationModelId(DEFAULT_AUTH_MODEL_ID) .relation(DEFAULT_RELATION) - .user(DEFAULT_USER); + .user(DEFAULT_USER) + .consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When var response = fga.listObjects(DEFAULT_STORE_ID, request).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 7425cf6d..189df4ed 100644 --- a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java @@ -55,6 +55,7 @@ public class OpenFgaClientTest { private static final String DEFAULT_ID = "budget"; private static final String DEFAULT_OBJECT = DEFAULT_TYPE + ":" + DEFAULT_ID; private static final String DEFAULT_SCHEMA_VERSION = "1.1"; + private static final ConsistencyPreference DEFAULT_CONSISTENCY = ConsistencyPreference.UNSPECIFIED; private static final String EMPTY_RESPONSE_BODY = "{}"; private static final ClientRelationshipCondition DEFAULT_CONDITION = new ClientRelationshipCondition().name("condition").context(Map.of("some", "context")); @@ -999,8 +1000,8 @@ public void readTest() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/read", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"page_size\":null,\"continuation_token\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"page_size\":null,\"continuation_token\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, ConsistencyPreference.MINIMIZE_LATENCY); String responseBody = String.format( "{\"tuples\":[{\"key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"}}]}", DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); @@ -1009,9 +1010,10 @@ public void readTest() throws Exception { .user(DEFAULT_USER) .relation(DEFAULT_RELATION) ._object(DEFAULT_OBJECT); + ClientReadOptions options = new ClientReadOptions().consistency(ConsistencyPreference.MINIMIZE_LATENCY); // When - ClientReadResponse response = fga.read(request).get(); + ClientReadResponse response = fga.read(request, options).get(); // Then mockHttpClient.verify().post(postUrl).withBody(is(expectedBody)).called(1); @@ -1028,7 +1030,8 @@ public void readTest() throws Exception { public void read_emptyRequestSendsNoTupleKey() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/read", DEFAULT_STORE_ID); - String expectedBody = "{\"tuple_key\":null,\"page_size\":null,\"continuation_token\":null}"; + String expectedBody = + "{\"tuple_key\":null,\"page_size\":null,\"continuation_token\":null,\"consistency\":\"UNSPECIFIED\"}"; mockHttpClient.onPost(postUrl).withBody(is(expectedBody)).doReturn(200, EMPTY_RESPONSE_BODY); ClientReadRequest request = new ClientReadRequest(); @@ -1554,8 +1557,13 @@ public void check() throws Exception { String expectedBody = String.format( "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"}," + "\"contextual_tuples\":{\"tuple_keys\":[{\"user\":\"%s\",\"relation\":\"owner\",\"object\":\"%s\",\"condition\":{\"name\":\"condition\",\"context\":{\"some\":\"context\"}}}]}," - + "\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_USER, DEFAULT_OBJECT); + + "\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, + DEFAULT_RELATION, + DEFAULT_OBJECT, + DEFAULT_USER, + DEFAULT_OBJECT, + ConsistencyPreference.HIGHER_CONSISTENCY); mockHttpClient.onPost(postUrl).withBody(is(expectedBody)).doReturn(200, "{\"allowed\":true}"); ClientCheckRequest request = new ClientCheckRequest() ._object(DEFAULT_OBJECT) @@ -1566,7 +1574,9 @@ public void check() throws Exception { .relation("owner") .user(DEFAULT_USER) .condition(DEFAULT_CONDITION))); - ClientCheckOptions options = new ClientCheckOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID); + ClientCheckOptions options = new ClientCheckOptions() + .authorizationModelId(DEFAULT_AUTH_MODEL_ID) + .consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When ClientCheckResponse response = fga.check(request, options).get(); @@ -1662,8 +1672,8 @@ public void batchCheck() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, ConsistencyPreference.MINIMIZE_LATENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -1674,7 +1684,9 @@ public void batchCheck() throws Exception { ._object(DEFAULT_OBJECT) .relation(DEFAULT_RELATION) .user(DEFAULT_USER); - ClientBatchCheckOptions options = new ClientBatchCheckOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID); + ClientBatchCheckOptions options = new ClientBatchCheckOptions() + .authorizationModelId(DEFAULT_AUTH_MODEL_ID) + .consistency(ConsistencyPreference.MINIMIZE_LATENCY); // When List response = @@ -1696,8 +1708,8 @@ public void batchCheck_twentyTimes() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_CONSISTENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -1825,15 +1837,17 @@ public void expandTest() throws Exception { // Given String postPath = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/expand"; String expectedBody = String.format( - "{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\"}", - DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID); + "{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\"}", + DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY); String responseBody = String.format( "{\"tree\":{\"root\":{\"union\":{\"nodes\":[{\"leaf\":{\"users\":{\"users\":[\"%s\"]}}}]}}}}", DEFAULT_USER); mockHttpClient.onPost(postPath).withBody(is(expectedBody)).doReturn(200, responseBody); ClientExpandRequest request = new ClientExpandRequest().relation(DEFAULT_RELATION)._object(DEFAULT_OBJECT); - ClientExpandOptions options = new ClientExpandOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID); + ClientExpandOptions options = new ClientExpandOptions() + .authorizationModelId(DEFAULT_AUTH_MODEL_ID) + .consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When ClientExpandResponse response = fga.expand(request, options).get(); @@ -1940,17 +1954,19 @@ public void listObjectsTest() throws Exception { // Given String postPath = String.format("https://api.fga.example/stores/%s/list-objects", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"authorization_model_id\":\"%s\",\"type\":null,\"relation\":\"%s\",\"user\":\"%s\",\"contextual_tuples\":null,\"context\":null}", - DEFAULT_AUTH_MODEL_ID, DEFAULT_RELATION, DEFAULT_USER); + "{\"authorization_model_id\":\"%s\",\"type\":null,\"relation\":\"%s\",\"user\":\"%s\",\"contextual_tuples\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_AUTH_MODEL_ID, DEFAULT_RELATION, DEFAULT_USER, ConsistencyPreference.HIGHER_CONSISTENCY); mockHttpClient .onPost(postPath) .withBody(is(expectedBody)) .doReturn(200, String.format("{\"objects\":[\"%s\"]}", DEFAULT_OBJECT)); ClientListObjectsRequest request = new ClientListObjectsRequest().relation(DEFAULT_RELATION).user(DEFAULT_USER); + ClientListObjectsOptions options = + new ClientListObjectsOptions().consistency(ConsistencyPreference.HIGHER_CONSISTENCY); // When - ClientListObjectsResponse response = fga.listObjects(request).get(); + ClientListObjectsResponse response = fga.listObjects(request, options).get(); // Then mockHttpClient.verify().post(postPath).withBody(is(expectedBody)).called(1); @@ -2041,8 +2057,8 @@ public void listObjectsWithContextTest() throws Exception { // Given String postPath = String.format("https://api.fga.example/stores/%s/list-objects", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"authorization_model_id\":\"%s\",\"type\":null,\"relation\":\"%s\",\"user\":\"%s\",\"contextual_tuples\":null,\"context\":{\"some\":\"context\"}}", - DEFAULT_AUTH_MODEL_ID, DEFAULT_RELATION, DEFAULT_USER); + "{\"authorization_model_id\":\"%s\",\"type\":null,\"relation\":\"%s\",\"user\":\"%s\",\"contextual_tuples\":null,\"context\":{\"some\":\"context\"},\"consistency\":\"%s\"}", + DEFAULT_AUTH_MODEL_ID, DEFAULT_RELATION, DEFAULT_USER, DEFAULT_CONSISTENCY); mockHttpClient .onPost(postPath) .withBody(is(expectedBody)) @@ -2068,8 +2084,8 @@ public void listRelations() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, ConsistencyPreference.MINIMIZE_LATENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -2080,8 +2096,9 @@ public void listRelations() throws Exception { .relations(List.of(DEFAULT_RELATION)) .user(DEFAULT_USER) ._object(DEFAULT_OBJECT); - ClientListRelationsOptions options = - new ClientListRelationsOptions().authorizationModelId(DEFAULT_AUTH_MODEL_ID); + ClientListRelationsOptions options = new ClientListRelationsOptions() + .authorizationModelId(DEFAULT_AUTH_MODEL_ID) + .consistency(ConsistencyPreference.MINIMIZE_LATENCY); // When ClientListRelationsResponse response = @@ -2106,8 +2123,8 @@ public void listRelations_deny() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"%s\",\"trace\":null,\"context\":null}", - DEFAULT_USER, "owner", DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"%s\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, "owner", DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, DEFAULT_CONSISTENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -2198,8 +2215,8 @@ public void listRelations_400() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_CONSISTENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -2228,8 +2245,8 @@ public void listRelations_404() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_CONSISTENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -2257,8 +2274,8 @@ public void listRelations_500() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null}", - DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT); + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":null,\"authorization_model_id\":\"01G5JAVJ41T49E9TT3SKVS7X1J\",\"trace\":null,\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_CONSISTENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -2286,14 +2303,15 @@ public void listRelations_contextAndContextualTuples() throws Exception { // Given String postUrl = String.format("https://api.fga.example/stores/%s/check", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":{\"tuple_keys\":[{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\",\"condition\":null}]},\"authorization_model_id\":\"%s\",\"trace\":null,\"context\":{\"some\":\"context\"}}", + "{\"tuple_key\":{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\"},\"contextual_tuples\":{\"tuple_keys\":[{\"user\":\"%s\",\"relation\":\"%s\",\"object\":\"%s\",\"condition\":null}]},\"authorization_model_id\":\"%s\",\"trace\":null,\"context\":{\"some\":\"context\"},\"consistency\":\"%s\"}", DEFAULT_USER, "owner", DEFAULT_OBJECT, DEFAULT_USER, DEFAULT_RELATION, DEFAULT_OBJECT, - DEFAULT_AUTH_MODEL_ID); + DEFAULT_AUTH_MODEL_ID, + DEFAULT_CONSISTENCY); mockHttpClient .onPost(postUrl) .withBody(is(expectedBody)) @@ -2337,8 +2355,12 @@ public void listUsersTest() throws Exception { // Given String postPath = String.format("https://api.fga.example/stores/%s/list-users", DEFAULT_STORE_ID); String expectedBody = String.format( - "{\"authorization_model_id\":\"%s\",\"object\":{\"type\":\"%s\",\"id\":\"%s\"},\"relation\":\"%s\",\"user_filters\":[{\"type\":\"user\",\"relation\":null},{\"type\":\"team\",\"relation\":\"member\"}],\"contextual_tuples\":[],\"context\":null}", - DEFAULT_AUTH_MODEL_ID, DEFAULT_TYPE, DEFAULT_ID, DEFAULT_RELATION); + "{\"authorization_model_id\":\"%s\",\"object\":{\"type\":\"%s\",\"id\":\"%s\"},\"relation\":\"%s\",\"user_filters\":[{\"type\":\"user\",\"relation\":null},{\"type\":\"team\",\"relation\":\"member\"}],\"contextual_tuples\":[],\"context\":null,\"consistency\":\"%s\"}", + DEFAULT_AUTH_MODEL_ID, + DEFAULT_TYPE, + DEFAULT_ID, + DEFAULT_RELATION, + ConsistencyPreference.MINIMIZE_LATENCY); mockHttpClient .onPost(postPath) .withBody(is(expectedBody)) @@ -2355,9 +2377,11 @@ public void listUsersTest() throws Exception { add(new UserTypeFilter().type("team").relation("member")); } }); + ClientListUsersOptions options = + new ClientListUsersOptions().consistency(ConsistencyPreference.MINIMIZE_LATENCY); // When - ClientListUsersResponse response = fga.listUsers(request).get(); + ClientListUsersResponse response = fga.listUsers(request, options).get(); // Then mockHttpClient.verify().post(postPath).withBody(is(expectedBody)).called(1);