From 9bc69f01aabacc6659960fa7976f8b916b39dcb2 Mon Sep 17 00:00:00 2001 From: Piotr Olaszewski Date: Tue, 21 Jan 2025 20:45:36 +0100 Subject: [PATCH] feat: Align code generated from sdk-generator --- .openapi-generator/FILES | 17 +- CHANGELOG.md | 4 + CONTRIBUTING.md | 4 +- README.md | 96 ++++--- docs/Assertion.md | 2 + docs/AuthErrorCode.md | 29 ++ docs/BatchCheckItem.md | 16 ++ docs/BatchCheckRequest.md | 15 ++ docs/BatchCheckResponse.md | 13 + docs/BatchCheckSingleResult.md | 14 + docs/CheckError.md | 15 ++ docs/ErrorCode.md | 4 + docs/ExpandRequest.md | 1 + docs/ForbiddenResponse.md | 14 + docs/InternalErrorCode.md | 2 - docs/OpenFgaApi.md | 231 ++++++++++++++-- docs/OpenTelemetry.md | 2 +- .../dev/openfga/sdk/example/Example1.java | 8 +- .../dev/openfga/sdk/example/KotlinExample1.kt | 8 +- .../java/dev/openfga/sdk/api/OpenFgaApi.java | 115 ++++++-- .../openfga/sdk/api/client/OpenFgaClient.java | 7 +- .../model/ClientReadChangesRequest.java | 12 + .../dev/openfga/sdk/api/model/Assertion.java | 104 +++++++- .../openfga/sdk/api/model/AuthErrorCode.java | 81 ++++++ .../openfga/sdk/api/model/BatchCheckItem.java | 247 ++++++++++++++++++ .../sdk/api/model/BatchCheckRequest.java | 236 +++++++++++++++++ .../sdk/api/model/BatchCheckResponse.java | 154 +++++++++++ .../sdk/api/model/BatchCheckSingleResult.java | 171 ++++++++++++ .../dev/openfga/sdk/api/model/CheckError.java | 217 +++++++++++++++ .../sdk/api/model/ConsistencyPreference.java | 2 +- .../dev/openfga/sdk/api/model/ErrorCode.java | 4 + .../openfga/sdk/api/model/ExpandRequest.java | 41 ++- .../sdk/api/model/ForbiddenResponse.java | 176 +++++++++++++ .../sdk/api/model/InternalErrorCode.java | 2 - .../openfga/sdk/api/model/ReadRequest.java | 2 + .../sdk/api/OpenFgaApiIntegrationTest.java | 4 +- .../client/OpenFgaClientIntegrationTest.java | 2 +- .../dev/openfga/sdk/example/Example1.java | 8 +- .../dev/openfga/sdk/api/OpenFgaApiTest.java | 34 ++- .../sdk/api/client/OpenFgaClientTest.java | 11 +- 40 files changed, 2001 insertions(+), 124 deletions(-) create mode 100644 docs/AuthErrorCode.md create mode 100644 docs/BatchCheckItem.md create mode 100644 docs/BatchCheckRequest.md create mode 100644 docs/BatchCheckResponse.md create mode 100644 docs/BatchCheckSingleResult.md create mode 100644 docs/CheckError.md create mode 100644 docs/ForbiddenResponse.md create mode 100644 src/main/java/dev/openfga/sdk/api/model/AuthErrorCode.java create mode 100644 src/main/java/dev/openfga/sdk/api/model/BatchCheckItem.java create mode 100644 src/main/java/dev/openfga/sdk/api/model/BatchCheckRequest.java create mode 100644 src/main/java/dev/openfga/sdk/api/model/BatchCheckResponse.java create mode 100644 src/main/java/dev/openfga/sdk/api/model/BatchCheckSingleResult.java create mode 100644 src/main/java/dev/openfga/sdk/api/model/CheckError.java create mode 100644 src/main/java/dev/openfga/sdk/api/model/ForbiddenResponse.java diff --git a/.openapi-generator/FILES b/.openapi-generator/FILES index d7553dbb..0237b40c 100644 --- a/.openapi-generator/FILES +++ b/.openapi-generator/FILES @@ -5,9 +5,6 @@ .github/ISSUE_TEMPLATE/config.yaml .github/ISSUE_TEMPLATE/feature_request.yaml .github/dependabot.yaml -.github/workflows/fossa.yaml -.github/workflows/main.yaml -.github/workflows/semgrep.yaml .gitignore CHANGELOG.md CONTRIBUTING.md @@ -21,7 +18,13 @@ docs/AbortedMessageResponse.md docs/Any.md docs/Assertion.md docs/AssertionTupleKey.md +docs/AuthErrorCode.md docs/AuthorizationModel.md +docs/BatchCheckItem.md +docs/BatchCheckRequest.md +docs/BatchCheckResponse.md +docs/BatchCheckSingleResult.md +docs/CheckError.md docs/CheckRequest.md docs/CheckRequestTupleKey.md docs/CheckResponse.md @@ -39,6 +42,7 @@ docs/ExpandRequest.md docs/ExpandRequestTupleKey.md docs/ExpandResponse.md docs/FgaObject.md +docs/ForbiddenResponse.md docs/GetStoreResponse.md docs/InternalErrorCode.md docs/InternalErrorMessageResponse.md @@ -192,7 +196,13 @@ src/main/java/dev/openfga/sdk/api/model/AbstractOpenApiSchema.java src/main/java/dev/openfga/sdk/api/model/Any.java src/main/java/dev/openfga/sdk/api/model/Assertion.java src/main/java/dev/openfga/sdk/api/model/AssertionTupleKey.java +src/main/java/dev/openfga/sdk/api/model/AuthErrorCode.java src/main/java/dev/openfga/sdk/api/model/AuthorizationModel.java +src/main/java/dev/openfga/sdk/api/model/BatchCheckItem.java +src/main/java/dev/openfga/sdk/api/model/BatchCheckRequest.java +src/main/java/dev/openfga/sdk/api/model/BatchCheckResponse.java +src/main/java/dev/openfga/sdk/api/model/BatchCheckSingleResult.java +src/main/java/dev/openfga/sdk/api/model/CheckError.java src/main/java/dev/openfga/sdk/api/model/CheckRequest.java src/main/java/dev/openfga/sdk/api/model/CheckRequestTupleKey.java src/main/java/dev/openfga/sdk/api/model/CheckResponse.java @@ -210,6 +220,7 @@ src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java src/main/java/dev/openfga/sdk/api/model/ExpandRequestTupleKey.java src/main/java/dev/openfga/sdk/api/model/ExpandResponse.java src/main/java/dev/openfga/sdk/api/model/FgaObject.java +src/main/java/dev/openfga/sdk/api/model/ForbiddenResponse.java src/main/java/dev/openfga/sdk/api/model/GetStoreResponse.java src/main/java/dev/openfga/sdk/api/model/InternalErrorCode.java src/main/java/dev/openfga/sdk/api/model/InternalErrorMessageResponse.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8cabdc11..7360ee80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## [Unreleased](https://github.com/openfga/java-sdk/compare/v0.7.2...HEAD) + +- feat: add support for `start_time` parameter in `ReadChanges` endpoint + ## v0.7.2 ### [0.7.2](https://github.com/openfga/java-sdk/compare/v0.7.1...v0.7.2) (2024-12-18) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 86e45188..47cff816 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -33,7 +33,7 @@ That repo includes an issue template that will walk through all the places to ch ### Submitting Pull Requests -Considering that the SDKs are autogenerated, please make sure to submit your Pull Requests to the [sdk-generator](https://github.com/openfga/sdk-generator). We will not accept PRs to this repository because they will be overwritten on the next sdk generation. +While we accept Pull Requests on this repository, the SDKs are autogenerated so please consider additionally submitting your Pull Requests to the [sdk-generator](https://github.com/openfga/sdk-generator) and linking the two PRs together and to the corresponding issue. This will greatly assist the OpenFGA team in being able to give timely reviews as well as deploying fixes and updates to our other SDKs as well. ## Getting in touch @@ -43,4 +43,4 @@ Please do not open issues for general support or usage questions. Instead, join ### Vulnerability Reporting -Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://github.com/openfga/java-sdk/blob/main/.github/SECURITY.md) details the procedure for disclosing security issues. +Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://github.com/openfga/.github/blob/main/SECURITY.md) details the procedure for disclosing security issues. diff --git a/README.md b/README.md index b99f88a9..86fd5baa 100644 --- a/README.md +++ b/README.md @@ -72,7 +72,7 @@ OpenFGA is designed to make it easy for application builders to model their perm The OpenFGA Java SDK is available on [Maven Central](https://central.sonatype.com/). -The OpenFGA Java SDK currently supports **Java11** as the minimum JDK version. +The OpenFGA Java SDK currently supports **Java 11** as the minimum JDK version. It can be used with the following: @@ -125,7 +125,7 @@ libraryDependencies += "dev.openfga" % "openfga-sdk" % "0.7.2" We strongly recommend you initialize the `OpenFgaClient` only once and then re-use it throughout your app, otherwise you will incur the cost of having to re-initialize multiple times or at every request, the cost of reduced connection pooling and re-use, and would be particularly costly in the client credentials flow, as that flow will be preformed on every request. -> The `Client` will by default retry API requests up to 15 times on 429 and 5xx errors. +> The `Client` will by default retry API requests up to 3 times on 429 and 5xx errors. #### No Credentials @@ -249,7 +249,7 @@ If your server is configured with [authentication enabled](https://openfga.dev/d Get a paginated list of stores. -[API Documentation](https://openfga.dev/api/service/docs/api#/Stores/ListStores) +[API Documentation](https://openfga.dev/api/service#/Stores/ListStores) > Passing `ClientListStoresOptions` is optional. All fields of `ClientListStoresOptions` are optional. @@ -267,7 +267,7 @@ var stores = fgaClient.listStores(options); Initialize a store. -[API Documentation](https://openfga.dev/api/service/docs/api#/Stores/CreateStore) +[API Documentation](https://openfga.dev/api/service#/Stores/CreateStore) > Passing `ClientCreateStoreOptions` is optional. All fields of `ClientCreateStoreOptions` are optional. @@ -290,7 +290,7 @@ fgaClient.setStoreId(store.getId()); Get information about the current store. -[API Documentation](https://openfga.dev/api/service/docs/api#/Stores/GetStore) +[API Documentation](https://openfga.dev/api/service#/Stores/GetStore) > Requires a client initialized with a storeId @@ -307,7 +307,7 @@ var store = fgaClient.getStore(options).get(); Delete a store. -[API Documentation](https://openfga.dev/api/service/docs/api#/Stores/DeleteStore) +[API Documentation](https://openfga.dev/api/service#/Stores/DeleteStore) > Requires a client initialized with a storeId @@ -438,7 +438,8 @@ Reads the list of historical relationship tuple writes and deletes. > Passing `ClientReadChangesOptions` is optional. All fields of `ClientReadChangesOptions` are optional. ```java -var request = new ClientReadChangesRequest().type("document"); +var startTime = OffsetDateTime.parse("2022-01-01T00:00:00+00:00"); +var request = new ClientReadChangesRequest().type("document").startTime(startTime); var options = new ClientReadChangesOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) .pageSize(10) @@ -466,12 +467,12 @@ Reads the relationship tuples stored in the database. It does not evaluate nor e var request = new ClientReadRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:roadmap"); + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"); // Find all relationship tuples where a certain user has a relationship as any relation to a certain document var request = new ClientReadRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") - ._object("document:roadmap"); + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"); // Find all relationship tuples where a certain user is a viewer of any document var request = new ClientReadRequest() @@ -481,7 +482,7 @@ var request = new ClientReadRequest() // Find all relationship tuples where any user has a relationship as any relation with a particular document var request = new ClientReadRequest() - ._object("document:roadmap"); + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"); // Read all stored relationship tuples var request = new ClientReadRequest(); @@ -515,17 +516,17 @@ var request = new ClientWriteRequest() new TupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:roadmap"), + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"), new TupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:budget") + ._object("document:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5") )) .deletes(List.of( new TupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("writer") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") )); var options = new ClientWriteOptions() @@ -552,17 +553,17 @@ var request = new ClientWriteRequest() new ClientTupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:roadmap"), + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"), new ClientTupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:budget") + ._object("document:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5") )) .deletes(List.of( new ClientTupleKeyWithoutCondition() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("writer") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") )); var options = new ClientWriteOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) @@ -588,7 +589,7 @@ Check if a user has a particular relation with an object. var request = new ClientCheckRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("writer") - ._object("document:roadmap"); + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"); var options = new ClientCheckOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) // You can rely on the model id set in the configuration or override it for this specific request @@ -601,7 +602,7 @@ var response = fgaClient.check(request, options).get(); ##### Batch Check Run a set of [checks](#check). Batch Check will return `allowed: false` if it encounters an error, and will return the error in the body. -If 429s or 5xxs are encountered, the underlying check will retry up to 15 times before giving up. +If 429s or 5xxs are encountered, the underlying check will retry up to 3 times before giving up. > Passing `ClientBatchCheckOptions` is optional. All fields of `ClientBatchCheckOptions` are optional. @@ -610,31 +611,31 @@ var request = List.of( new ClientCheckRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") .contextualTuples(List.of( new ClientTupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("editor") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") )), new ClientCheckRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("admin") - ._object("document:roadmap"), + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"), .contextualTuples(List.of( new ClientTupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("editor") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") )), new ClientCheckRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("creator") - ._object("document:roadmap"), + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"), new ClientCheckRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("deleter") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") ); var options = new ClientBatchCheckOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) @@ -650,11 +651,11 @@ response.getResponses() = [{ request: { user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b", relation: "viewer", - _object: "document:roadmap", + _object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a", contextualTuples: [{ user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b", relation: "editor", - _object: "document:roadmap" + _object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a" }] } }, { @@ -662,11 +663,11 @@ response.getResponses() = [{ request: { user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b", relation: "admin", - _object: "document:roadmap", + _object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a", contextualTuples: [{ user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b", relation: "editor", - _object: "document:roadmap" + _object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a" }] } }, { @@ -674,7 +675,7 @@ response.getResponses() = [{ request: { user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b", relation: "creator", - _object: "document:roadmap", + _object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a", }, error: }, { @@ -682,7 +683,7 @@ response.getResponses() = [{ request: { user: "user:81684243-9356-4421-8fbf-a4f8d36aa31b", relation: "deleter", - _object: "document:roadmap", + _object: "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a", }}, ] */ @@ -699,7 +700,7 @@ Expands the relationships in userset tree format. ```java var request = new ClientExpandRequest() .relation("viewer") - ._object("document:roadmap"); + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"); var options = new ClientExpandOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) // You can rely on the model id set in the configuration or override it for this specific request @@ -707,7 +708,7 @@ var options = new ClientExpandOptions() var response = fgaClient.expand(request, options).get(); -// response.getTree().getRoot() = {"name":"document:roadmap#viewer","leaf":{"users":{"users":["user:81684243-9356-4421-8fbf-a4f8d36aa31b","user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"]}}} +// response.getTree().getRoot() = {"name":"document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a#viewer","leaf":{"users":{"users":["user:81684243-9356-4421-8fbf-a4f8d36aa31b","user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"]}}} ``` ##### List Objects @@ -727,7 +728,7 @@ var request = new ClientListObjectsRequest() new ClientTupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("writer") - ._object("document:budget") + ._object("document:0192ab2d-d36e-7cb3-a4a8-5d1d67a300c5") )); var options = new ClientListObjectsOptions() .additionalHeaders(Map.of("Some-Http-Header", "Some value")) @@ -736,7 +737,7 @@ var options = new ClientListObjectsOptions() var response = fgaClient.listObjects(request, options).get(); -// response.getObjects() = ["document:roadmap"] +// response.getObjects() = ["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"] ``` ##### List Relations @@ -748,13 +749,13 @@ List the relations a user has on an object. ```java var request = new ClientListRelationsRequest() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") .relations(List.of("can_view", "can_edit", "can_delete", "can_rename")) .contextualTuples(List.of( new ClientTupleKey() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("editor") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") ) ); var options = new ClientListRelationsOptions() @@ -798,7 +799,7 @@ var request = new ClientListUsersRequest() new ClientTupleKey() .user("folder:product") .relation("parent") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") )); var options = new ClientListUsersOptions() @@ -845,7 +846,7 @@ var assertions = List.of( new ClientAssertion() .user("user:81684243-9356-4421-8fbf-a4f8d36aa31b") .relation("viewer") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") .expectation(true) ); fgaClient.writeAssertions(assertions, options).get(); @@ -854,7 +855,7 @@ fgaClient.writeAssertions(assertions, options).get(); ### Retries -If a network request fails with a 429 or 5xx error from the server, the SDK will automatically retry the request up to 15 times with a minimum wait time of 100 milliseconds between each attempt. +If a network request fails with a 429 or 5xx error from the server, the SDK will automatically retry the request up to 3 times with a minimum wait time of 100 milliseconds between each attempt. To customize this behavior, call `maxRetries` and `minimumRetryDelay` on the `ClientConfiguration` builder. `maxRetries` determines the maximum number of retries (up to 15), while `minimumRetryDelay` sets the minimum wait time between retries in milliseconds. @@ -883,6 +884,7 @@ public class Example { | Method | HTTP request | Description | | ------------- | ------------- | ------------- | +| [**batchCheck**](docs/OpenFgaApi.md#batchcheck) | **POST** /stores/{store_id}/batch-check | Send a list of `check` operations in a single request | | [**check**](docs/OpenFgaApi.md#check) | **POST** /stores/{store_id}/check | Check whether a user is authorized to access an object | | [**createStore**](docs/OpenFgaApi.md#createstore) | **POST** /stores | Create a store | | [**deleteStore**](docs/OpenFgaApi.md#deletestore) | **DELETE** /stores/{store_id} | Delete a store | @@ -913,8 +915,20 @@ public class Example { - [AssertionTupleKey](https://github.com/openfga/java-sdk/blob/main/docs/AssertionTupleKey.md) +- [AuthErrorCode](https://github.com/openfga/java-sdk/blob/main/docs/AuthErrorCode.md) + - [AuthorizationModel](https://github.com/openfga/java-sdk/blob/main/docs/AuthorizationModel.md) +- [BatchCheckItem](https://github.com/openfga/java-sdk/blob/main/docs/BatchCheckItem.md) + +- [BatchCheckRequest](https://github.com/openfga/java-sdk/blob/main/docs/BatchCheckRequest.md) + +- [BatchCheckResponse](https://github.com/openfga/java-sdk/blob/main/docs/BatchCheckResponse.md) + +- [BatchCheckSingleResult](https://github.com/openfga/java-sdk/blob/main/docs/BatchCheckSingleResult.md) + +- [CheckError](https://github.com/openfga/java-sdk/blob/main/docs/CheckError.md) + - [CheckRequest](https://github.com/openfga/java-sdk/blob/main/docs/CheckRequest.md) - [CheckRequestTupleKey](https://github.com/openfga/java-sdk/blob/main/docs/CheckRequestTupleKey.md) @@ -949,6 +963,8 @@ public class Example { - [FgaObject](https://github.com/openfga/java-sdk/blob/main/docs/FgaObject.md) +- [ForbiddenResponse](https://github.com/openfga/java-sdk/blob/main/docs/ForbiddenResponse.md) + - [GetStoreResponse](https://github.com/openfga/java-sdk/blob/main/docs/GetStoreResponse.md) - [InternalErrorCode](https://github.com/openfga/java-sdk/blob/main/docs/InternalErrorCode.md) @@ -1077,7 +1093,7 @@ If you have found a bug or if you have a feature request, please report them on ### Pull Requests -All changes made to this repo will be overwritten on the next generation, so we kindly ask that you send all pull requests related to the SDKs to the [sdk-generator repo](https://github.com/openfga/sdk-generator) instead. +While we accept Pull Requests on this repository, the SDKs are autogenerated so please consider additionally submitting your Pull Requests to the [sdk-generator](https://github.com/openfga/sdk-generator) and linking the two PRs together and to the corresponding issue. This will greatly assist the OpenFGA team in being able to give timely reviews as well as deploying fixes and updates to our other SDKs as well. ## Author diff --git a/docs/Assertion.md b/docs/Assertion.md index 704cc8ef..1600a087 100644 --- a/docs/Assertion.md +++ b/docs/Assertion.md @@ -9,6 +9,8 @@ |------------ | ------------- | ------------- | -------------| |**tupleKey** | [**AssertionTupleKey**](AssertionTupleKey.md) | | | |**expectation** | **Boolean** | | | +|**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] | diff --git a/docs/AuthErrorCode.md b/docs/AuthErrorCode.md new file mode 100644 index 00000000..13fd92a4 --- /dev/null +++ b/docs/AuthErrorCode.md @@ -0,0 +1,29 @@ + + +# AuthErrorCode + +## Enum + + +* `NO_AUTH_ERROR` (value: `"no_auth_error"`) + +* `AUTH_FAILED_INVALID_SUBJECT` (value: `"auth_failed_invalid_subject"`) + +* `AUTH_FAILED_INVALID_AUDIENCE` (value: `"auth_failed_invalid_audience"`) + +* `AUTH_FAILED_INVALID_ISSUER` (value: `"auth_failed_invalid_issuer"`) + +* `INVALID_CLAIMS` (value: `"invalid_claims"`) + +* `AUTH_FAILED_INVALID_BEARER_TOKEN` (value: `"auth_failed_invalid_bearer_token"`) + +* `BEARER_TOKEN_MISSING` (value: `"bearer_token_missing"`) + +* `UNAUTHENTICATED` (value: `"unauthenticated"`) + +* `FORBIDDEN` (value: `"forbidden"`) + +* `UNKNOWN_DEFAULT_OPEN_API` (value: `"unknown_default_open_api"`) + + + diff --git a/docs/BatchCheckItem.md b/docs/BatchCheckItem.md new file mode 100644 index 00000000..829ad070 --- /dev/null +++ b/docs/BatchCheckItem.md @@ -0,0 +1,16 @@ + + +# BatchCheckItem + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**tupleKey** | [**CheckRequestTupleKey**](CheckRequestTupleKey.md) | | | +|**contextualTuples** | [**ContextualTupleKeys**](ContextualTupleKeys.md) | | [optional] | +|**context** | **Object** | | [optional] | +|**correlationId** | **String** | correlation_id must be a string containing only letters, numbers, or hyphens, with length ≤ 36 characters. | | + + + diff --git a/docs/BatchCheckRequest.md b/docs/BatchCheckRequest.md new file mode 100644 index 00000000..bbdacc55 --- /dev/null +++ b/docs/BatchCheckRequest.md @@ -0,0 +1,15 @@ + + +# BatchCheckRequest + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**checks** | [**List<BatchCheckItem>**](BatchCheckItem.md) | | | +|**authorizationModelId** | **String** | | [optional] | +|**consistency** | **ConsistencyPreference** | | [optional] | + + + diff --git a/docs/BatchCheckResponse.md b/docs/BatchCheckResponse.md new file mode 100644 index 00000000..31da9440 --- /dev/null +++ b/docs/BatchCheckResponse.md @@ -0,0 +1,13 @@ + + +# BatchCheckResponse + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**result** | [**Map<String, BatchCheckSingleResult>**](BatchCheckSingleResult.md) | map keys are the correlation_id values from the BatchCheckItems in the request | [optional] | + + + diff --git a/docs/BatchCheckSingleResult.md b/docs/BatchCheckSingleResult.md new file mode 100644 index 00000000..47944c39 --- /dev/null +++ b/docs/BatchCheckSingleResult.md @@ -0,0 +1,14 @@ + + +# BatchCheckSingleResult + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**allowed** | **Boolean** | | [optional] | +|**error** | [**CheckError**](CheckError.md) | | [optional] | + + + diff --git a/docs/CheckError.md b/docs/CheckError.md new file mode 100644 index 00000000..6f43fe30 --- /dev/null +++ b/docs/CheckError.md @@ -0,0 +1,15 @@ + + +# CheckError + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**inputError** | **ErrorCode** | | [optional] | +|**internalError** | **InternalErrorCode** | | [optional] | +|**message** | **String** | | [optional] | + + + diff --git a/docs/ErrorCode.md b/docs/ErrorCode.md index 995351bc..0817723b 100644 --- a/docs/ErrorCode.md +++ b/docs/ErrorCode.md @@ -101,6 +101,10 @@ * `UNSUPPORTED_SCHEMA_VERSION` (value: `"unsupported_schema_version"`) +* `CANCELLED` (value: `"cancelled"`) + +* `INVALID_START_TIME` (value: `"invalid_start_time"`) + * `UNKNOWN_DEFAULT_OPEN_API` (value: `"unknown_default_open_api"`) diff --git a/docs/ExpandRequest.md b/docs/ExpandRequest.md index 91ddc2e9..9eee15a7 100644 --- a/docs/ExpandRequest.md +++ b/docs/ExpandRequest.md @@ -10,6 +10,7 @@ |**tupleKey** | [**ExpandRequestTupleKey**](ExpandRequestTupleKey.md) | | | |**authorizationModelId** | **String** | | [optional] | |**consistency** | **ConsistencyPreference** | | [optional] | +|**contextualTuples** | [**ContextualTupleKeys**](ContextualTupleKeys.md) | | [optional] | diff --git a/docs/ForbiddenResponse.md b/docs/ForbiddenResponse.md new file mode 100644 index 00000000..47e05893 --- /dev/null +++ b/docs/ForbiddenResponse.md @@ -0,0 +1,14 @@ + + +# ForbiddenResponse + + +## Properties + +| Name | Type | Description | Notes | +|------------ | ------------- | ------------- | -------------| +|**code** | **AuthErrorCode** | | [optional] | +|**message** | **String** | | [optional] | + + + diff --git a/docs/InternalErrorCode.md b/docs/InternalErrorCode.md index 0b14c215..b293fda2 100644 --- a/docs/InternalErrorCode.md +++ b/docs/InternalErrorCode.md @@ -9,8 +9,6 @@ * `INTERNAL_ERROR` (value: `"internal_error"`) -* `CANCELLED` (value: `"cancelled"`) - * `DEADLINE_EXCEEDED` (value: `"deadline_exceeded"`) * `ALREADY_EXISTS` (value: `"already_exists"`) diff --git a/docs/OpenFgaApi.md b/docs/OpenFgaApi.md index efefe558..afa96aa0 100644 --- a/docs/OpenFgaApi.md +++ b/docs/OpenFgaApi.md @@ -4,6 +4,8 @@ All URIs are relative to *http://localhost* | Method | HTTP request | Description | |------------- | ------------- | -------------| +| [**batchCheck**](OpenFgaApi.md#batchCheck) | **POST** /stores/{store_id}/batch-check | Send a list of `check` operations in a single request | +| [**batchCheckWithHttpInfo**](OpenFgaApi.md#batchCheckWithHttpInfo) | **POST** /stores/{store_id}/batch-check | Send a list of `check` operations in a single request | | [**check**](OpenFgaApi.md#check) | **POST** /stores/{store_id}/check | Check whether a user is authorized to access an object | | [**checkWithHttpInfo**](OpenFgaApi.md#checkWithHttpInfo) | **POST** /stores/{store_id}/check | Check whether a user is authorized to access an object | | [**createStore**](OpenFgaApi.md#createStore) | **POST** /stores | Create a store | @@ -39,13 +41,174 @@ All URIs are relative to *http://localhost* +## batchCheck + +> CompletableFuture batchCheck(storeId, body) + +Send a list of `check` operations in a single request + +The `BatchCheck` API functions nearly identically to `Check`, but instead of checking a single user-object relationship BatchCheck accepts a list of relationships to check and returns a map containing `BatchCheckItem` response for each check it received. An associated `correlation_id` is required for each check in the batch. This ID is used to correlate a check to the appropriate response. It is a string consisting of only alphanumeric characters or hyphens with a maximum length of 36 characters. This `correlation_id` is used to map the result of each check to the item which was checked, so it must be unique for each item in the batch. We recommend using a UUID or ULID as the `correlation_id`, but you can use whatever unique identifier you need as long as it matches this regex pattern: `^[\\w\\d-]{1,36}$` For more details on how `Check` functions, see the docs for `/check`. ### Examples #### A BatchCheckRequest ```json { \"checks\": [ { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:anne\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PM3QM7VBPGB8KMPK8SBD5\" }, { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:bob\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PMM6A90NV5ET0F28CYSZQ\" } ] } ``` Below is a possible response to the above request. Note that the result map's keys are the `correlation_id` values from the checked items in the request: ```json { \"result\": { \"01JA8PMM6A90NV5ET0F28CYSZQ\": { \"allowed\": false, \"error\": {\"message\": \"\"} }, \"01JA8PM3QM7VBPGB8KMPK8SBD5\": { \"allowed\": true, \"error\": {\"message\": \"\"} } } ``` + +### Example + +```java +// Import classes: +import dev.openfga.sdk.api.client.ApiClient; +import dev.openfga.sdk.api.client.ApiException; +import dev.openfga.sdk.api.configuration.Configuration; +import dev.openfga.sdk.api.client.models.*; +import dev.openfga.sdk.api.OpenFgaApi; +import java.util.concurrent.CompletableFuture; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("http://localhost"); + + OpenFgaApi apiInstance = new OpenFgaApi(defaultClient); + String storeId = "storeId_example"; // String | + BatchCheckRequest body = new BatchCheckRequest(); // BatchCheckRequest | + try { + CompletableFuture result = apiInstance.batchCheck(storeId, body); + System.out.println(result.get()); + } catch (ApiException e) { + System.err.println("Exception when calling OpenFgaApi#batchCheck"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Reason: " + e.getResponseBody()); + System.err.println("Response headers: " + e.getResponseHeaders()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **storeId** | **String**| | | +| **body** | [**BatchCheckRequest**](BatchCheckRequest.md)| | | + +### Return type + +CompletableFuture<[**BatchCheckResponse**](BatchCheckResponse.md)> + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | A successful response. | - | +| **400** | Request failed due to invalid input. | - | +| **401** | Not authenticated. | - | +| **403** | Forbidden. | - | +| **404** | Request failed due to incorrect path. | - | +| **409** | Request was aborted due a transaction conflict. | - | +| **422** | Request timed out due to excessive request throttling. | - | +| **500** | Request failed due to internal server error. | - | + +## batchCheckWithHttpInfo + +> CompletableFuture> batchCheck batchCheckWithHttpInfo(storeId, body) + +Send a list of `check` operations in a single request + +The `BatchCheck` API functions nearly identically to `Check`, but instead of checking a single user-object relationship BatchCheck accepts a list of relationships to check and returns a map containing `BatchCheckItem` response for each check it received. An associated `correlation_id` is required for each check in the batch. This ID is used to correlate a check to the appropriate response. It is a string consisting of only alphanumeric characters or hyphens with a maximum length of 36 characters. This `correlation_id` is used to map the result of each check to the item which was checked, so it must be unique for each item in the batch. We recommend using a UUID or ULID as the `correlation_id`, but you can use whatever unique identifier you need as long as it matches this regex pattern: `^[\\w\\d-]{1,36}$` For more details on how `Check` functions, see the docs for `/check`. ### Examples #### A BatchCheckRequest ```json { \"checks\": [ { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:anne\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PM3QM7VBPGB8KMPK8SBD5\" }, { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:bob\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PMM6A90NV5ET0F28CYSZQ\" } ] } ``` Below is a possible response to the above request. Note that the result map's keys are the `correlation_id` values from the checked items in the request: ```json { \"result\": { \"01JA8PMM6A90NV5ET0F28CYSZQ\": { \"allowed\": false, \"error\": {\"message\": \"\"} }, \"01JA8PM3QM7VBPGB8KMPK8SBD5\": { \"allowed\": true, \"error\": {\"message\": \"\"} } } ``` + +### Example + +```java +// Import classes: +import dev.openfga.sdk.api.client.ApiClient; +import dev.openfga.sdk.api.client.ApiException; +import dev.openfga.sdk.api.client.ApiResponse; +import dev.openfga.sdk.api.configuration.Configuration; +import dev.openfga.sdk.api.client.models.*; +import dev.openfga.sdk.api.OpenFgaApi; +import java.util.concurrent.CompletableFuture; + +public class Example { + public static void main(String[] args) { + ApiClient defaultClient = Configuration.getDefaultApiClient(); + defaultClient.setBasePath("http://localhost"); + + OpenFgaApi apiInstance = new OpenFgaApi(defaultClient); + String storeId = "storeId_example"; // String | + BatchCheckRequest body = new BatchCheckRequest(); // BatchCheckRequest | + try { + CompletableFuture> response = apiInstance.batchCheckWithHttpInfo(storeId, body); + System.out.println("Status code: " + response.get().getStatusCode()); + System.out.println("Response headers: " + response.get().getHeaders()); + System.out.println("Response body: " + response.get().getData()); + } catch (InterruptedException | ExecutionException e) { + ApiException apiException = (ApiException)e.getCause(); + System.err.println("Exception when calling OpenFgaApi#batchCheck"); + System.err.println("Status code: " + apiException.getCode()); + System.err.println("Response headers: " + apiException.getResponseHeaders()); + System.err.println("Reason: " + apiException.getResponseBody()); + e.printStackTrace(); + } catch (ApiException e) { + System.err.println("Exception when calling OpenFgaApi#batchCheck"); + System.err.println("Status code: " + e.getCode()); + System.err.println("Response headers: " + e.getResponseHeaders()); + System.err.println("Reason: " + e.getResponseBody()); + e.printStackTrace(); + } + } +} +``` + +### Parameters + + +| Name | Type | Description | Notes | +|------------- | ------------- | ------------- | -------------| +| **storeId** | **String**| | | +| **body** | [**BatchCheckRequest**](BatchCheckRequest.md)| | | + +### Return type + +CompletableFuture> + + +### Authorization + +No authorization required + +### HTTP request headers + +- **Content-Type**: application/json +- **Accept**: application/json + +### HTTP response details +| Status code | Description | Response headers | +|-------------|-------------|------------------| +| **200** | A successful response. | - | +| **400** | Request failed due to invalid input. | - | +| **401** | Not authenticated. | - | +| **403** | Forbidden. | - | +| **404** | Request failed due to incorrect path. | - | +| **409** | Request was aborted due a transaction conflict. | - | +| **422** | Request timed out due to excessive request throttling. | - | +| **500** | Request failed due to internal server error. | - | + + ## check > CompletableFuture check(storeId, body) Check whether a user is authorized to access an object -The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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 return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. +The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. ### Requesting higher consistency By default, the Check API caches results for a short time to optimize performance. You may request higher consistency to inform the server that higher consistency should be preferred at the expense of increased latency. Care should be taken when requesting higher consistency due to the increased latency. ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"consistency\": \"HIGHER_CONSISTENCY\" } ``` ### Example @@ -108,6 +271,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -119,7 +283,7 @@ No authorization required Check whether a user is authorized to access an object -The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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 return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. +The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. ### Requesting higher consistency By default, the Check API caches results for a short time to optimize performance. You may request higher consistency to inform the server that higher consistency should be preferred at the expense of increased latency. Care should be taken when requesting higher consistency due to the increased latency. ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"consistency\": \"HIGHER_CONSISTENCY\" } ``` ### Example @@ -192,6 +356,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -265,6 +430,7 @@ No authorization required | **201** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -347,6 +513,7 @@ No authorization required | **201** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -419,6 +586,7 @@ No authorization required | **204** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -500,6 +668,7 @@ No authorization required | **204** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -512,7 +681,7 @@ No authorization required Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship -The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. +The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. ### Expand Request with Contextual Tuples Given the model ```python model schema 1.1 type user type folder relations define owner: [user] type document relations define parent: [folder] define viewer: [user] or writer define writer: [user] or owner from parent ``` and the initial tuples ```json [{ \"user\": \"user:bob\", \"relation\": \"owner\", \"object\": \"folder:1\" }] ``` To expand all `writers` of `document:1` when `document:1` is put in `folder:1`, the first call could be ```json { \"tuple_key\": { \"object\": \"document:1\", \"relation\": \"writer\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"folder:1\", \"relation\": \"parent\", \"object\": \"document:1\" } ] } } ``` this returns: ```json { \"tree\": { \"root\": { \"name\": \"document:1#writer\", \"union\": { \"nodes\": [ { \"name\": \"document:1#writer\", \"leaf\": { \"users\": { \"users\": [] } } }, { \"name\": \"document:1#writer\", \"leaf\": { \"tupleToUserset\": { \"tupleset\": \"document:1#parent\", \"computed\": [ { \"userset\": \"folder:1#owner\" } ] } } } ] } } } } ``` This tells us that the `owner` of `folder:1` may also be a writer. So our next call could be to find the `owners` of `folder:1` ```json { \"tuple_key\": { \"object\": \"folder:1\", \"relation\": \"owner\" } } ``` which gives ```json { \"tree\": { \"root\": { \"name\": \"folder:1#owner\", \"leaf\": { \"users\": { \"users\": [ \"user:bob\" ] } } } } } ``` ### Example @@ -575,6 +744,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -586,7 +756,7 @@ No authorization required Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship -The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. +The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. ### Expand Request with Contextual Tuples Given the model ```python model schema 1.1 type user type folder relations define owner: [user] type document relations define parent: [folder] define viewer: [user] or writer define writer: [user] or owner from parent ``` and the initial tuples ```json [{ \"user\": \"user:bob\", \"relation\": \"owner\", \"object\": \"folder:1\" }] ``` To expand all `writers` of `document:1` when `document:1` is put in `folder:1`, the first call could be ```json { \"tuple_key\": { \"object\": \"document:1\", \"relation\": \"writer\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"folder:1\", \"relation\": \"parent\", \"object\": \"document:1\" } ] } } ``` this returns: ```json { \"tree\": { \"root\": { \"name\": \"document:1#writer\", \"union\": { \"nodes\": [ { \"name\": \"document:1#writer\", \"leaf\": { \"users\": { \"users\": [] } } }, { \"name\": \"document:1#writer\", \"leaf\": { \"tupleToUserset\": { \"tupleset\": \"document:1#parent\", \"computed\": [ { \"userset\": \"folder:1#owner\" } ] } } } ] } } } } ``` This tells us that the `owner` of `folder:1` may also be a writer. So our next call could be to find the `owners` of `folder:1` ```json { \"tuple_key\": { \"object\": \"folder:1\", \"relation\": \"owner\" } } ``` which gives ```json { \"tree\": { \"root\": { \"name\": \"folder:1#owner\", \"leaf\": { \"users\": { \"users\": [ \"user:bob\" ] } } } } } ``` ### Example @@ -659,6 +829,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -732,6 +903,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -814,6 +986,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -826,7 +999,7 @@ No authorization required List all objects of the given type that the user has a relation with -The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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 objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. +The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will contain the related objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. ### Example @@ -889,6 +1062,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -900,7 +1074,7 @@ No authorization required List all objects of the given type that the user has a relation with -The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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 objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. +The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will contain the related objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. ### Example @@ -973,6 +1147,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1048,6 +1223,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1132,6 +1308,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1144,7 +1321,7 @@ No authorization required 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. +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 access 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 @@ -1207,6 +1384,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1218,7 +1396,7 @@ No authorization required 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. +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 access 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 @@ -1291,6 +1469,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1366,6 +1545,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1450,6 +1630,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1462,7 +1643,7 @@ No authorization required Read assertions for an authorization model ID -The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. +The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. ### Example @@ -1525,6 +1706,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1536,7 +1718,7 @@ No authorization required Read assertions for an authorization model ID -The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. +The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. ### Example @@ -1609,6 +1791,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1684,6 +1867,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1768,6 +1952,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1845,6 +2030,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1931,6 +2117,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -1939,7 +2126,7 @@ No authorization required ## readChanges -> CompletableFuture readChanges(storeId, type, pageSize, continuationToken) +> CompletableFuture readChanges(storeId, type, pageSize, continuationToken, startTime) Return a list of all the tuple changes @@ -1966,8 +2153,9 @@ public class Example { String type = "type_example"; // String | Integer pageSize = 56; // Integer | String continuationToken = "continuationToken_example"; // String | + OffsetDateTime startTime = OffsetDateTime.now(); // OffsetDateTime | Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time. try { - CompletableFuture result = apiInstance.readChanges(storeId, type, pageSize, continuationToken); + CompletableFuture result = apiInstance.readChanges(storeId, type, pageSize, continuationToken, startTime); System.out.println(result.get()); } catch (ApiException e) { System.err.println("Exception when calling OpenFgaApi#readChanges"); @@ -1989,6 +2177,7 @@ public class Example { | **type** | **String**| | [optional] | | **pageSize** | **Integer**| | [optional] | | **continuationToken** | **String**| | [optional] | +| **startTime** | **OffsetDateTime**| Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time. | [optional] | ### Return type @@ -2010,6 +2199,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2017,7 +2207,7 @@ No authorization required ## readChangesWithHttpInfo -> CompletableFuture> readChanges readChangesWithHttpInfo(storeId, type, pageSize, continuationToken) +> CompletableFuture> readChanges readChangesWithHttpInfo(storeId, type, pageSize, continuationToken, startTime) Return a list of all the tuple changes @@ -2045,8 +2235,9 @@ public class Example { String type = "type_example"; // String | Integer pageSize = 56; // Integer | String continuationToken = "continuationToken_example"; // String | + OffsetDateTime startTime = OffsetDateTime.now(); // OffsetDateTime | Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time. try { - CompletableFuture> response = apiInstance.readChangesWithHttpInfo(storeId, type, pageSize, continuationToken); + CompletableFuture> response = apiInstance.readChangesWithHttpInfo(storeId, type, pageSize, continuationToken, startTime); System.out.println("Status code: " + response.get().getStatusCode()); System.out.println("Response headers: " + response.get().getHeaders()); System.out.println("Response body: " + response.get().getData()); @@ -2077,6 +2268,7 @@ public class Example { | **type** | **String**| | [optional] | | **pageSize** | **Integer**| | [optional] | | **continuationToken** | **String**| | [optional] | +| **startTime** | **OffsetDateTime**| Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time. | [optional] | ### Return type @@ -2098,6 +2290,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2173,6 +2366,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2257,6 +2451,7 @@ No authorization required | **200** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2269,7 +2464,7 @@ No authorization required Upsert assertions for an authorization model ID -The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. +The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, the expectation of whether a call to the Check API of that tuple key will return true or false, and optionally a list of contextual tuples. ### Example @@ -2333,6 +2528,7 @@ No authorization required | **204** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2344,7 +2540,7 @@ No authorization required Upsert assertions for an authorization model ID -The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. +The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, the expectation of whether a call to the Check API of that tuple key will return true or false, and optionally a list of contextual tuples. ### Example @@ -2418,6 +2614,7 @@ No authorization required | **204** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2493,6 +2690,7 @@ No authorization required | **201** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | @@ -2577,6 +2775,7 @@ No authorization required | **201** | A successful response. | - | | **400** | Request failed due to invalid input. | - | | **401** | Not authenticated. | - | +| **403** | Forbidden. | - | | **404** | Request failed due to incorrect path. | - | | **409** | Request was aborted due a transaction conflict. | - | | **422** | Request timed out due to excessive request throttling. | - | diff --git a/docs/OpenTelemetry.md b/docs/OpenTelemetry.md index 7d918ab7..94aed42f 100644 --- a/docs/OpenTelemetry.md +++ b/docs/OpenTelemetry.md @@ -27,7 +27,7 @@ In cases when metrics events are sent, they will not be viewable outside of infr | `fga-client.response.model_id` | string | Yes | Authorization model ID that the FGA server used | | `fga-client.user` | string | No | User associated with the action of the request for check and list users | | `http.client.request.duration` | int | No | Duration for the SDK to complete the request, in milliseconds | -| `http.host` | string | Yes | Host identifier of the origin the request was sent to | +| `http.host` | string | Yes | Host identifier of the origin the request was sent to | | `http.request.method` | string | Yes | HTTP method for the request | | `http.request.resend_count` | int | Yes | Number of retries attempted, if any | | `http.response.status_code` | int | Yes | Status code of the response (e.g., `200` for success) | diff --git a/example/example1/src/main/java/dev/openfga/sdk/example/Example1.java b/example/example1/src/main/java/dev/openfga/sdk/example/Example1.java index efbb7ba9..931b97e9 100644 --- a/example/example1/src/main/java/dev/openfga/sdk/example/Example1.java +++ b/example/example1/src/main/java/dev/openfga/sdk/example/Example1.java @@ -105,7 +105,7 @@ public void run(String apiUrl) throws Exception { .writes(List.of(new ClientTupleKey() .user("user:anne") .relation("writer") - ._object("document:roadmap"))), + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"))), new ClientWriteOptions() .disableTransactions(true) .authorizationModelId(authorizationModel.getAuthorizationModelId())) @@ -130,7 +130,7 @@ public void run(String apiUrl) throws Exception { .check(new ClientCheckRequest() .user("user:anne") .relation("reader") - ._object("document:roadmap")) + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a")) .get(); System.out.println("Allowed: " + failingCheckResponse.getAllowed()); } catch (Exception e) { @@ -144,7 +144,7 @@ public void run(String apiUrl) throws Exception { // .check(new ClientCheckRequest() // .user("user:anne") // .relation("reader") - // ._object("document:roadmap") + // ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") // .context(Map.of("ViewCount", 100))) // .get(); // System.out.println("Allowed: " + checkResponse.getAllowed()); @@ -160,7 +160,7 @@ public void run(String apiUrl) throws Exception { new ClientAssertion() .user("user:anne") .relation("reader") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") .expectation(false))) .get(); System.out.println("Assertions updated"); diff --git a/example/example1/src/main/kotlin/dev/openfga/sdk/example/KotlinExample1.kt b/example/example1/src/main/kotlin/dev/openfga/sdk/example/KotlinExample1.kt index ee287262..3e11bb3d 100644 --- a/example/example1/src/main/kotlin/dev/openfga/sdk/example/KotlinExample1.kt +++ b/example/example1/src/main/kotlin/dev/openfga/sdk/example/KotlinExample1.kt @@ -113,7 +113,7 @@ internal class KotlinExample1 { ClientTupleKey() .user("user:anne") .relation("writer") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") ) ), ClientWriteOptions() @@ -141,7 +141,7 @@ internal class KotlinExample1 { ClientCheckRequest() .user("user:anne") .relation("reader") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") ) .get() println("Allowed: " + failingCheckResponse.allowed) @@ -156,7 +156,7 @@ internal class KotlinExample1 { // .check(new ClientCheckRequest() // .user("user:anne") // .relation("reader") - // ._object("document:roadmap") + // ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") // .context(Map.of("ViewCount", 100))) // .get(); // System.out.println("Allowed: " + checkResponse.getAllowed()); @@ -173,7 +173,7 @@ internal class KotlinExample1 { ClientAssertion() .user("user:anne") .relation("reader") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") .expectation(false) ) ) diff --git a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java index b25ec677..6d6c95ad 100644 --- a/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java +++ b/src/main/java/dev/openfga/sdk/api/OpenFgaApi.java @@ -18,6 +18,8 @@ import dev.openfga.sdk.api.auth.*; import dev.openfga.sdk.api.client.*; import dev.openfga.sdk.api.configuration.*; +import dev.openfga.sdk.api.model.BatchCheckRequest; +import dev.openfga.sdk.api.model.BatchCheckResponse; import dev.openfga.sdk.api.model.CheckRequest; import dev.openfga.sdk.api.model.CheckResponse; import dev.openfga.sdk.api.model.CreateStoreRequest; @@ -49,6 +51,7 @@ import java.net.URI; import java.net.http.HttpRequest; import java.time.Duration; +import java.time.OffsetDateTime; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -88,9 +91,65 @@ public OpenFgaApi(Configuration configuration, ApiClient apiClient) throws FgaIn } } + /** + * Send a list of `check` operations in a single request + * The `BatchCheck` API functions nearly identically to `Check`, but instead of checking a single user-object relationship BatchCheck accepts a list of relationships to check and returns a map containing `BatchCheckItem` response for each check it received. An associated `correlation_id` is required for each check in the batch. This ID is used to correlate a check to the appropriate response. It is a string consisting of only alphanumeric characters or hyphens with a maximum length of 36 characters. This `correlation_id` is used to map the result of each check to the item which was checked, so it must be unique for each item in the batch. We recommend using a UUID or ULID as the `correlation_id`, but you can use whatever unique identifier you need as long as it matches this regex pattern: `^[\\w\\d-]{1,36}$` For more details on how `Check` functions, see the docs for `/check`. ### Examples #### A BatchCheckRequest ```json { \"checks\": [ { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:anne\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PM3QM7VBPGB8KMPK8SBD5\" }, { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:bob\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PMM6A90NV5ET0F28CYSZQ\" } ] } ``` Below is a possible response to the above request. Note that the result map's keys are the `correlation_id` values from the checked items in the request: ```json { \"result\": { \"01JA8PMM6A90NV5ET0F28CYSZQ\": { \"allowed\": false, \"error\": {\"message\": \"\"} }, \"01JA8PM3QM7VBPGB8KMPK8SBD5\": { \"allowed\": true, \"error\": {\"message\": \"\"} } } ``` + * @param storeId (required) + * @param body (required) + * @return CompletableFuture<ApiResponse<BatchCheckResponse>> + * @throws ApiException if fails to make API call + */ + public CompletableFuture> batchCheck(String storeId, BatchCheckRequest body) + throws ApiException, FgaInvalidParameterException { + return batchCheck(storeId, body, this.configuration); + } + + /** + * Send a list of `check` operations in a single request + * The `BatchCheck` API functions nearly identically to `Check`, but instead of checking a single user-object relationship BatchCheck accepts a list of relationships to check and returns a map containing `BatchCheckItem` response for each check it received. An associated `correlation_id` is required for each check in the batch. This ID is used to correlate a check to the appropriate response. It is a string consisting of only alphanumeric characters or hyphens with a maximum length of 36 characters. This `correlation_id` is used to map the result of each check to the item which was checked, so it must be unique for each item in the batch. We recommend using a UUID or ULID as the `correlation_id`, but you can use whatever unique identifier you need as long as it matches this regex pattern: `^[\\w\\d-]{1,36}$` For more details on how `Check` functions, see the docs for `/check`. ### Examples #### A BatchCheckRequest ```json { \"checks\": [ { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:anne\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PM3QM7VBPGB8KMPK8SBD5\" }, { \"tuple_key\": { \"object\": \"document:2021-budget\" \"relation\": \"reader\", \"user\": \"user:bob\", }, \"contextual_tuples\": {...} \"context\": {} \"correlation_id\": \"01JA8PMM6A90NV5ET0F28CYSZQ\" } ] } ``` Below is a possible response to the above request. Note that the result map's keys are the `correlation_id` values from the checked items in the request: ```json { \"result\": { \"01JA8PMM6A90NV5ET0F28CYSZQ\": { \"allowed\": false, \"error\": {\"message\": \"\"} }, \"01JA8PM3QM7VBPGB8KMPK8SBD5\": { \"allowed\": true, \"error\": {\"message\": \"\"} } } ``` + * @param storeId (required) + * @param body (required) + * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with + * @return CompletableFuture<ApiResponse<BatchCheckResponse>> + * @throws ApiException if fails to make API call + */ + public CompletableFuture> batchCheck( + String storeId, BatchCheckRequest body, ConfigurationOverride configurationOverride) + throws ApiException, FgaInvalidParameterException { + return batchCheck(storeId, body, this.configuration.override(configurationOverride)); + } + + private CompletableFuture> batchCheck( + String storeId, BatchCheckRequest body, Configuration configuration) + throws ApiException, FgaInvalidParameterException { + + assertParamExists(storeId, "storeId", "batchCheck"); + + assertParamExists(body, "body", "batchCheck"); + + String path = "/stores/{store_id}/batch-check".replace("{store_id}", ApiClient.urlEncode(storeId.toString())); + + Map methodParameters = new HashMap<>(); + methodParameters.put("storeId", storeId); + methodParameters.put("body", body); + + Map telemetryAttributes = buildTelemetryAttributes(methodParameters); + + telemetryAttributes.put(Attributes.FGA_CLIENT_REQUEST_METHOD, "BatchCheck"); + + try { + HttpRequest request = buildHttpRequest("POST", path, body, configuration); + return new HttpRequestAttempt<>(request, "batchCheck", BatchCheckResponse.class, apiClient, configuration) + .addTelemetryAttributes(telemetryAttributes) + .attemptHttpRequest(); + } catch (ApiException e) { + return CompletableFuture.failedFuture(e); + } + } + /** * Check whether a user is authorized to access an object - * The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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 return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. + * The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. ### Requesting higher consistency By default, the Check API caches results for a short time to optimize performance. You may request higher consistency to inform the server that higher consistency should be preferred at the expense of increased latency. Care should be taken when requesting higher consistency due to the increased latency. ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"consistency\": \"HIGHER_CONSISTENCY\" } ``` * @param storeId (required) * @param body (required) * @return CompletableFuture<ApiResponse<CheckResponse>> @@ -103,7 +162,7 @@ public CompletableFuture> check(String storeId, Check /** * Check whether a user is authorized to access an object - * The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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 return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. + * The Check API returns whether a given user has a relationship with a given object in a given store. The `user` field of the request can be a specific target, such as `user:anne`, or a userset (set of users) such as `group:marketing#member` or a type-bound public access `user:*`. 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`). A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. You may also provide an `authorization_model_id` in the body. This will be used to assert that the input `tuple_key` is valid for the model specified. If not specified, the assertion will be made against the latest authorization model ID. It is strongly recommended to specify authorization model id for better performance. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will return whether the relationship exists in the field `allowed`. Some exceptions apply, but in general, if a Check API responds with `{allowed: true}`, then you can expect the equivalent ListObjects query to return the object, and viceversa. For example, if `Check(user:anne, reader, document:2021-budget)` responds with `{allowed: true}`, then `ListObjects(user:anne, reader, document)` may include `document:2021-budget` in the response. ## Examples ### Querying with contextual tuples In order to check if user `user:anne` of type `user` has a `reader` relationship with object `document:2021-budget` given the following contextual tuple ```json { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ``` the Check API can be used with the following request body: ```json { \"tuple_key\": { \"user\": \"user:anne\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"time_slot:office_hours\" } ] }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` ### Querying usersets Some Checks will always return `true`, even without any tuples. For example, for the following authorization model ```python model schema 1.1 type user type document relations define reader: [user] ``` the following query ```json { \"tuple_key\": { \"user\": \"document:2021-budget#reader\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" } } ``` will always return `{ \"allowed\": true }`. This is because usersets are self-defining: the userset `document:2021-budget#reader` will always have the `reader` relation with `document:2021-budget`. ### Querying usersets with difference in the model A Check for a userset can yield results that must be treated carefully if the model involves difference. For example, for the following authorization model ```python model schema 1.1 type user type group relations define member: [user] type document relations define blocked: [user] define reader: [group#member] but not blocked ``` the following query ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"user:anne\", \"relation\": \"member\", \"object\": \"group:finance\" }, { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, { \"user\": \"user:anne\", \"relation\": \"blocked\", \"object\": \"document:2021-budget\" } ] }, } ``` will return `{ \"allowed\": true }`, even though a specific user of the userset `group:finance#member` does not have the `reader` relationship with the given object. ### Requesting higher consistency By default, the Check API caches results for a short time to optimize performance. You may request higher consistency to inform the server that higher consistency should be preferred at the expense of increased latency. Care should be taken when requesting higher consistency due to the increased latency. ```json { \"tuple_key\": { \"user\": \"group:finance#member\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"consistency\": \"HIGHER_CONSISTENCY\" } ``` * @param storeId (required) * @param body (required) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with @@ -245,7 +304,7 @@ private CompletableFuture> deleteStore(String storeId, Configu /** * Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship - * The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. + * The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. ### Expand Request with Contextual Tuples Given the model ```python model schema 1.1 type user type folder relations define owner: [user] type document relations define parent: [folder] define viewer: [user] or writer define writer: [user] or owner from parent ``` and the initial tuples ```json [{ \"user\": \"user:bob\", \"relation\": \"owner\", \"object\": \"folder:1\" }] ``` To expand all `writers` of `document:1` when `document:1` is put in `folder:1`, the first call could be ```json { \"tuple_key\": { \"object\": \"document:1\", \"relation\": \"writer\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"folder:1\", \"relation\": \"parent\", \"object\": \"document:1\" } ] } } ``` this returns: ```json { \"tree\": { \"root\": { \"name\": \"document:1#writer\", \"union\": { \"nodes\": [ { \"name\": \"document:1#writer\", \"leaf\": { \"users\": { \"users\": [] } } }, { \"name\": \"document:1#writer\", \"leaf\": { \"tupleToUserset\": { \"tupleset\": \"document:1#parent\", \"computed\": [ { \"userset\": \"folder:1#owner\" } ] } } } ] } } } } ``` This tells us that the `owner` of `folder:1` may also be a writer. So our next call could be to find the `owners` of `folder:1` ```json { \"tuple_key\": { \"object\": \"folder:1\", \"relation\": \"owner\" } } ``` which gives ```json { \"tree\": { \"root\": { \"name\": \"folder:1#owner\", \"leaf\": { \"users\": { \"users\": [ \"user:bob\" ] } } } } } ``` * @param storeId (required) * @param body (required) * @return CompletableFuture<ApiResponse<ExpandResponse>> @@ -258,7 +317,7 @@ public CompletableFuture> expand(String storeId, Exp /** * Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship - * The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. + * The Expand API will return all users and usersets that have certain relationship with an object in a certain store. This is different from the `/stores/{store_id}/read` API in that both users and computed usersets are returned. Body parameters `tuple_key.object` and `tuple_key.relation` are all required. A `contextual_tuples` object may also be included in the body of the request. This object contains one field `tuple_keys`, which is an array of tuple keys. Each of these tuples may have an associated `condition`. The response will return a tree whose leaves are the specific users and usersets. Union, intersection and difference operator are located in the intermediate nodes. ## Example To expand all users that have the `reader` relationship with object `document:2021-budget`, use the Expand API with the following request body ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" }, \"authorization_model_id\": \"01G50QVV17PECNVAHX1GG4Y5NC\" } ``` OpenFGA's response will be a userset tree of the users and usersets that have read access to the document. ```json { \"tree\":{ \"root\":{ \"type\":\"document:2021-budget#reader\", \"union\":{ \"nodes\":[ { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"users\":{ \"users\":[ \"user:bob\" ] } } }, { \"type\":\"document:2021-budget#reader\", \"leaf\":{ \"computed\":{ \"userset\":\"document:2021-budget#writer\" } } } ] } } } } ``` The caller can then call expand API for the `writer` relationship for the `document:2021-budget`. ### Expand Request with Contextual Tuples Given the model ```python model schema 1.1 type user type folder relations define owner: [user] type document relations define parent: [folder] define viewer: [user] or writer define writer: [user] or owner from parent ``` and the initial tuples ```json [{ \"user\": \"user:bob\", \"relation\": \"owner\", \"object\": \"folder:1\" }] ``` To expand all `writers` of `document:1` when `document:1` is put in `folder:1`, the first call could be ```json { \"tuple_key\": { \"object\": \"document:1\", \"relation\": \"writer\" }, \"contextual_tuples\": { \"tuple_keys\": [ { \"user\": \"folder:1\", \"relation\": \"parent\", \"object\": \"document:1\" } ] } } ``` this returns: ```json { \"tree\": { \"root\": { \"name\": \"document:1#writer\", \"union\": { \"nodes\": [ { \"name\": \"document:1#writer\", \"leaf\": { \"users\": { \"users\": [] } } }, { \"name\": \"document:1#writer\", \"leaf\": { \"tupleToUserset\": { \"tupleset\": \"document:1#parent\", \"computed\": [ { \"userset\": \"folder:1#owner\" } ] } } } ] } } } } ``` This tells us that the `owner` of `folder:1` may also be a writer. So our next call could be to find the `owners` of `folder:1` ```json { \"tuple_key\": { \"object\": \"folder:1\", \"relation\": \"owner\" } } ``` which gives ```json { \"tree\": { \"root\": { \"name\": \"folder:1#owner\", \"leaf\": { \"users\": { \"users\": [ \"user:bob\" ] } } } } } ``` * @param storeId (required) * @param body (required) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with @@ -351,7 +410,7 @@ private CompletableFuture> getStore(String storeId /** * List all objects of the given type that the user has a relation with - * The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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 objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. + * The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will contain the related objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. * @param storeId (required) * @param body (required) * @return CompletableFuture<ApiResponse<ListObjectsResponse>> @@ -364,7 +423,7 @@ public CompletableFuture> listObjects(String st /** * List all objects of the given type that the user has a relation with - * The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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 objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. + * The ListObjects API returns a list of all the objects of the given type that the user has a relation with. 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. By default, the Check API caches results for a short time to optimize performance. You may specify a value of `HIGHER_CONSISTENCY` for the optional `consistency` parameter in the body to inform the server that higher conisistency is preferred at the expense of increased latency. Consideration should be given to the increased latency if requesting higher consistency. The response will contain the related objects in an array in the \"objects\" field of the response and they will be strings in the object format `<type>:<id>` (e.g. \"document:roadmap\"). The number of objects in the response array will be limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE and by the upper bound specified in the flag OPENFGA_LIST_OBJECTS_MAX_RESULTS, whichever is hit first. The objects given will not be sorted, and therefore two identical calls can give a given different set of objects. * @param storeId (required) * @param body (required) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with @@ -458,7 +517,7 @@ private CompletableFuture> listStores( /** * 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. + * 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 access 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>> @@ -471,7 +530,7 @@ public CompletableFuture> listUsers(String storeI /** * 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. + * 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 access 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 @@ -570,7 +629,7 @@ private CompletableFuture> read( /** * Read assertions for an authorization model ID - * The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. + * The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. * @param storeId (required) * @param authorizationModelId (required) * @return CompletableFuture<ApiResponse<ReadAssertionsResponse>> @@ -583,7 +642,7 @@ public CompletableFuture> readAssertions( /** * Read assertions for an authorization model ID - * The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. + * The ReadAssertions API will return, for a given authorization model id, all the assertions stored for it. * @param storeId (required) * @param authorizationModelId (required) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with @@ -760,13 +819,14 @@ private CompletableFuture> readAuth * @param type (optional) * @param pageSize (optional) * @param continuationToken (optional) + * @param startTime Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time. (optional) * @return CompletableFuture<ApiResponse<ReadChangesResponse>> * @throws ApiException if fails to make API call */ public CompletableFuture> readChanges( - String storeId, String type, Integer pageSize, String continuationToken) + String storeId, String type, Integer pageSize, String continuationToken, OffsetDateTime startTime) throws ApiException, FgaInvalidParameterException { - return readChanges(storeId, type, pageSize, continuationToken, this.configuration); + return readChanges(storeId, type, pageSize, continuationToken, startTime, this.configuration); } /** @@ -776,6 +836,7 @@ public CompletableFuture> readChanges( * @param type (optional) * @param pageSize (optional) * @param continuationToken (optional) + * @param startTime Start date and time of changes to read. Format: ISO 8601 timestamp (e.g., 2022-01-01T00:00:00Z) If a continuation_token is provided along side start_time, the continuation_token will take precedence over start_time. (optional) * @param configurationOverride Override the {@link Configuration} this OpenFgaApi was constructed with * @return CompletableFuture<ApiResponse<ReadChangesResponse>> * @throws ApiException if fails to make API call @@ -785,20 +846,40 @@ public CompletableFuture> readChanges( String type, Integer pageSize, String continuationToken, + OffsetDateTime startTime, ConfigurationOverride configurationOverride) throws ApiException, FgaInvalidParameterException { return readChanges( - storeId, type, pageSize, continuationToken, this.configuration.override(configurationOverride)); + storeId, + type, + pageSize, + continuationToken, + startTime, + this.configuration.override(configurationOverride)); } private CompletableFuture> readChanges( - String storeId, String type, Integer pageSize, String continuationToken, Configuration configuration) + String storeId, + String type, + Integer pageSize, + String continuationToken, + OffsetDateTime startTime, + Configuration configuration) throws ApiException, FgaInvalidParameterException { assertParamExists(storeId, "storeId", "readChanges"); String path = "/stores/{store_id}/changes".replace("{store_id}", ApiClient.urlEncode(storeId.toString())); - path = pathWithParams(path, "type", type, "page_size", pageSize, "continuation_token", continuationToken); + path = pathWithParams( + path, + "type", + type, + "page_size", + pageSize, + "continuation_token", + continuationToken, + "start_time", + startTime); Map methodParameters = new HashMap<>(); methodParameters.put("storeId", storeId); @@ -874,7 +955,7 @@ private CompletableFuture> write(String storeId, WriteReques /** * Upsert assertions for an authorization model ID - * The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. + * The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, the expectation of whether a call to the Check API of that tuple key will return true or false, and optionally a list of contextual tuples. * @param storeId (required) * @param authorizationModelId (required) * @param body (required) @@ -889,7 +970,7 @@ public CompletableFuture> writeAssertions( /** * Upsert assertions for an authorization model ID - * The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, and the expectation of whether a call to the Check API of that tuple key will return true or false. + * The WriteAssertions API will upsert new assertions for an authorization model id, or overwrite the existing ones. An assertion is an object that contains a tuple key, the expectation of whether a call to the Check API of that tuple key will return true or false, and optionally a list of contextual tuples. * @param storeId (required) * @param authorizationModelId (required) * @param body (required) 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 2f0453ee..51c3f512 100644 --- a/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java +++ b/src/main/java/dev/openfga/sdk/api/client/OpenFgaClient.java @@ -294,7 +294,12 @@ public CompletableFuture readChanges( var options = readChangesOptions != null ? readChangesOptions : new ClientReadChangesOptions(); var overrides = new ConfigurationOverride().addHeaders(options); return call(() -> api.readChanges( - storeId, request.getType(), options.getPageSize(), options.getContinuationToken(), overrides)) + storeId, + request.getType(), + options.getPageSize(), + options.getContinuationToken(), + request.getStartTime(), + overrides)) .thenApply(ClientReadChangesResponse::new); } diff --git a/src/main/java/dev/openfga/sdk/api/client/model/ClientReadChangesRequest.java b/src/main/java/dev/openfga/sdk/api/client/model/ClientReadChangesRequest.java index 3bb14d74..507cd0e6 100644 --- a/src/main/java/dev/openfga/sdk/api/client/model/ClientReadChangesRequest.java +++ b/src/main/java/dev/openfga/sdk/api/client/model/ClientReadChangesRequest.java @@ -12,15 +12,27 @@ package dev.openfga.sdk.api.client.model; +import java.time.OffsetDateTime; + public class ClientReadChangesRequest { private String type; + private OffsetDateTime startTime; public ClientReadChangesRequest type(String type) { this.type = type; return this; } + public ClientReadChangesRequest startTime(OffsetDateTime startTime) { + this.startTime = startTime; + return this; + } + public String getType() { return type; } + + public OffsetDateTime getStartTime() { + return startTime; + } } diff --git a/src/main/java/dev/openfga/sdk/api/model/Assertion.java b/src/main/java/dev/openfga/sdk/api/model/Assertion.java index 621abccb..97261afc 100644 --- a/src/main/java/dev/openfga/sdk/api/model/Assertion.java +++ b/src/main/java/dev/openfga/sdk/api/model/Assertion.java @@ -17,13 +17,20 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; import java.util.Objects; import java.util.StringJoiner; /** * Assertion */ -@JsonPropertyOrder({Assertion.JSON_PROPERTY_TUPLE_KEY, Assertion.JSON_PROPERTY_EXPECTATION}) +@JsonPropertyOrder({ + Assertion.JSON_PROPERTY_TUPLE_KEY, + Assertion.JSON_PROPERTY_EXPECTATION, + Assertion.JSON_PROPERTY_CONTEXTUAL_TUPLES, + Assertion.JSON_PROPERTY_CONTEXT +}) public class Assertion { public static final String JSON_PROPERTY_TUPLE_KEY = "tuple_key"; private AssertionTupleKey tupleKey; @@ -31,6 +38,12 @@ public class Assertion { public static final String JSON_PROPERTY_EXPECTATION = "expectation"; private Boolean expectation; + public static final String JSON_PROPERTY_CONTEXTUAL_TUPLES = "contextual_tuples"; + private List contextualTuples = new ArrayList<>(); + + public static final String JSON_PROPERTY_CONTEXT = "context"; + private Object context; + public Assertion() {} public Assertion tupleKey(AssertionTupleKey tupleKey) { @@ -77,6 +90,58 @@ public void setExpectation(Boolean expectation) { this.expectation = expectation; } + public Assertion contextualTuples(List contextualTuples) { + this.contextualTuples = contextualTuples; + return this; + } + + public Assertion addContextualTuplesItem(TupleKey contextualTuplesItem) { + if (this.contextualTuples == null) { + this.contextualTuples = new ArrayList<>(); + } + this.contextualTuples.add(contextualTuplesItem); + return this; + } + + /** + * Get contextualTuples + * @return contextualTuples + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONTEXTUAL_TUPLES) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public List getContextualTuples() { + return contextualTuples; + } + + @JsonProperty(JSON_PROPERTY_CONTEXTUAL_TUPLES) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setContextualTuples(List contextualTuples) { + this.contextualTuples = contextualTuples; + } + + public Assertion context(Object context) { + this.context = context; + return this; + } + + /** + * Additional request context that will be used to evaluate any ABAC conditions encountered in the query evaluation. + * @return context + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONTEXT) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public Object getContext() { + return context; + } + + @JsonProperty(JSON_PROPERTY_CONTEXT) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setContext(Object context) { + this.context = context; + } + /** * Return true if this Assertion object is equal to o. */ @@ -90,12 +155,14 @@ public boolean equals(Object o) { } Assertion assertion = (Assertion) o; return Objects.equals(this.tupleKey, assertion.tupleKey) - && Objects.equals(this.expectation, assertion.expectation); + && Objects.equals(this.expectation, assertion.expectation) + && Objects.equals(this.contextualTuples, assertion.contextualTuples) + && Objects.equals(this.context, assertion.context); } @Override public int hashCode() { - return Objects.hash(tupleKey, expectation); + return Objects.hash(tupleKey, expectation, contextualTuples, context); } @Override @@ -104,6 +171,10 @@ public String toString() { sb.append("class Assertion {\n"); sb.append(" tupleKey: ").append(toIndentedString(tupleKey)).append("\n"); sb.append(" expectation: ").append(toIndentedString(expectation)).append("\n"); + sb.append(" contextualTuples: ") + .append(toIndentedString(contextualTuples)) + .append("\n"); + sb.append(" context: ").append(toIndentedString(context)).append("\n"); sb.append("}"); return sb.toString(); } @@ -166,6 +237,33 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `contextual_tuples` to the URL query string + if (getContextualTuples() != null) { + for (int i = 0; i < getContextualTuples().size(); i++) { + if (getContextualTuples().get(i) != null) { + joiner.add(getContextualTuples() + .get(i) + .toUrlQueryString(String.format( + "%scontextual_tuples%s%s", + prefix, + suffix, + "".equals(suffix) + ? "" + : String.format("%s%d%s", containerPrefix, i, containerSuffix)))); + } + } + } + + // add `context` to the URL query string + if (getContext() != null) { + joiner.add(String.format( + "%scontext%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getContext()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + return joiner.toString(); } } diff --git a/src/main/java/dev/openfga/sdk/api/model/AuthErrorCode.java b/src/main/java/dev/openfga/sdk/api/model/AuthErrorCode.java new file mode 100644 index 00000000..f931aac1 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/AuthErrorCode.java @@ -0,0 +1,81 @@ +/* + * 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; + +/** + * Gets or Sets AuthErrorCode + */ +public enum AuthErrorCode { + NO_AUTH_ERROR("no_auth_error"), + + AUTH_FAILED_INVALID_SUBJECT("auth_failed_invalid_subject"), + + AUTH_FAILED_INVALID_AUDIENCE("auth_failed_invalid_audience"), + + AUTH_FAILED_INVALID_ISSUER("auth_failed_invalid_issuer"), + + INVALID_CLAIMS("invalid_claims"), + + AUTH_FAILED_INVALID_BEARER_TOKEN("auth_failed_invalid_bearer_token"), + + BEARER_TOKEN_MISSING("bearer_token_missing"), + + UNAUTHENTICATED("unauthenticated"), + + FORBIDDEN("forbidden"), + + UNKNOWN_DEFAULT_OPEN_API("unknown_default_open_api"); + + private String value; + + AuthErrorCode(String value) { + this.value = value; + } + + @JsonValue + public String getValue() { + return value; + } + + @Override + public String toString() { + return String.valueOf(value); + } + + @JsonCreator + public static AuthErrorCode fromValue(String value) { + for (AuthErrorCode b : AuthErrorCode.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/BatchCheckItem.java b/src/main/java/dev/openfga/sdk/api/model/BatchCheckItem.java new file mode 100644 index 00000000..84b47306 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/BatchCheckItem.java @@ -0,0 +1,247 @@ +/* + * 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.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * BatchCheckItem + */ +@JsonPropertyOrder({ + BatchCheckItem.JSON_PROPERTY_TUPLE_KEY, + BatchCheckItem.JSON_PROPERTY_CONTEXTUAL_TUPLES, + BatchCheckItem.JSON_PROPERTY_CONTEXT, + BatchCheckItem.JSON_PROPERTY_CORRELATION_ID +}) +public class BatchCheckItem { + public static final String JSON_PROPERTY_TUPLE_KEY = "tuple_key"; + private CheckRequestTupleKey tupleKey; + + public static final String JSON_PROPERTY_CONTEXTUAL_TUPLES = "contextual_tuples"; + private ContextualTupleKeys contextualTuples; + + public static final String JSON_PROPERTY_CONTEXT = "context"; + private Object context; + + public static final String JSON_PROPERTY_CORRELATION_ID = "correlation_id"; + private String correlationId; + + public BatchCheckItem() {} + + public BatchCheckItem tupleKey(CheckRequestTupleKey tupleKey) { + this.tupleKey = tupleKey; + return this; + } + + /** + * Get tupleKey + * @return tupleKey + **/ + @javax.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_TUPLE_KEY) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public CheckRequestTupleKey getTupleKey() { + return tupleKey; + } + + @JsonProperty(JSON_PROPERTY_TUPLE_KEY) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setTupleKey(CheckRequestTupleKey tupleKey) { + this.tupleKey = tupleKey; + } + + public BatchCheckItem contextualTuples(ContextualTupleKeys contextualTuples) { + this.contextualTuples = contextualTuples; + return this; + } + + /** + * Get contextualTuples + * @return contextualTuples + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONTEXTUAL_TUPLES) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ContextualTupleKeys getContextualTuples() { + return contextualTuples; + } + + @JsonProperty(JSON_PROPERTY_CONTEXTUAL_TUPLES) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setContextualTuples(ContextualTupleKeys contextualTuples) { + this.contextualTuples = contextualTuples; + } + + public BatchCheckItem context(Object context) { + this.context = context; + return this; + } + + /** + * Get context + * @return context + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONTEXT) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public Object getContext() { + return context; + } + + @JsonProperty(JSON_PROPERTY_CONTEXT) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setContext(Object context) { + this.context = context; + } + + public BatchCheckItem correlationId(String correlationId) { + this.correlationId = correlationId; + return this; + } + + /** + * correlation_id must be a string containing only letters, numbers, or hyphens, with length ≤ 36 characters. + * @return correlationId + **/ + @javax.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_CORRELATION_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public String getCorrelationId() { + return correlationId; + } + + @JsonProperty(JSON_PROPERTY_CORRELATION_ID) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setCorrelationId(String correlationId) { + this.correlationId = correlationId; + } + + /** + * Return true if this BatchCheckItem object is equal to o. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BatchCheckItem batchCheckItem = (BatchCheckItem) o; + return Objects.equals(this.tupleKey, batchCheckItem.tupleKey) + && Objects.equals(this.contextualTuples, batchCheckItem.contextualTuples) + && Objects.equals(this.context, batchCheckItem.context) + && Objects.equals(this.correlationId, batchCheckItem.correlationId); + } + + @Override + public int hashCode() { + return Objects.hash(tupleKey, contextualTuples, context, correlationId); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class BatchCheckItem {\n"); + sb.append(" tupleKey: ").append(toIndentedString(tupleKey)).append("\n"); + sb.append(" contextualTuples: ") + .append(toIndentedString(contextualTuples)) + .append("\n"); + sb.append(" context: ").append(toIndentedString(context)).append("\n"); + sb.append(" correlationId: ").append(toIndentedString(correlationId)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + // add `tuple_key` to the URL query string + if (getTupleKey() != null) { + joiner.add(getTupleKey().toUrlQueryString(prefix + "tuple_key" + suffix)); + } + + // add `contextual_tuples` to the URL query string + if (getContextualTuples() != null) { + joiner.add(getContextualTuples().toUrlQueryString(prefix + "contextual_tuples" + suffix)); + } + + // add `context` to the URL query string + if (getContext() != null) { + joiner.add(String.format( + "%scontext%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getContext()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + // add `correlation_id` to the URL query string + if (getCorrelationId() != null) { + joiner.add(String.format( + "%scorrelation_id%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getCorrelationId()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + return joiner.toString(); + } +} diff --git a/src/main/java/dev/openfga/sdk/api/model/BatchCheckRequest.java b/src/main/java/dev/openfga/sdk/api/model/BatchCheckRequest.java new file mode 100644 index 00000000..e36d38d5 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/BatchCheckRequest.java @@ -0,0 +1,236 @@ +/* + * 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.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * BatchCheckRequest + */ +@JsonPropertyOrder({ + BatchCheckRequest.JSON_PROPERTY_CHECKS, + BatchCheckRequest.JSON_PROPERTY_AUTHORIZATION_MODEL_ID, + BatchCheckRequest.JSON_PROPERTY_CONSISTENCY +}) +public class BatchCheckRequest { + public static final String JSON_PROPERTY_CHECKS = "checks"; + private List checks = new ArrayList<>(); + + 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 BatchCheckRequest() {} + + public BatchCheckRequest checks(List checks) { + this.checks = checks; + return this; + } + + public BatchCheckRequest addChecksItem(BatchCheckItem checksItem) { + if (this.checks == null) { + this.checks = new ArrayList<>(); + } + this.checks.add(checksItem); + return this; + } + + /** + * Get checks + * @return checks + **/ + @javax.annotation.Nonnull + @JsonProperty(JSON_PROPERTY_CHECKS) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public List getChecks() { + return checks; + } + + @JsonProperty(JSON_PROPERTY_CHECKS) + @JsonInclude(value = JsonInclude.Include.ALWAYS) + public void setChecks(List checks) { + this.checks = checks; + } + + public BatchCheckRequest authorizationModelId(String authorizationModelId) { + this.authorizationModelId = authorizationModelId; + return this; + } + + /** + * Get authorizationModelId + * @return authorizationModelId + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_AUTHORIZATION_MODEL_ID) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public String getAuthorizationModelId() { + return authorizationModelId; + } + + @JsonProperty(JSON_PROPERTY_AUTHORIZATION_MODEL_ID) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setAuthorizationModelId(String authorizationModelId) { + this.authorizationModelId = authorizationModelId; + } + + public BatchCheckRequest 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 BatchCheck_request object is equal to o. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BatchCheckRequest batchCheckRequest = (BatchCheckRequest) o; + return Objects.equals(this.checks, batchCheckRequest.checks) + && Objects.equals(this.authorizationModelId, batchCheckRequest.authorizationModelId) + && Objects.equals(this.consistency, batchCheckRequest.consistency); + } + + @Override + public int hashCode() { + return Objects.hash(checks, authorizationModelId, consistency); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class BatchCheckRequest {\n"); + sb.append(" checks: ").append(toIndentedString(checks)).append("\n"); + sb.append(" authorizationModelId: ") + .append(toIndentedString(authorizationModelId)) + .append("\n"); + sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + // add `checks` to the URL query string + if (getChecks() != null) { + for (int i = 0; i < getChecks().size(); i++) { + if (getChecks().get(i) != null) { + joiner.add(getChecks() + .get(i) + .toUrlQueryString(String.format( + "%schecks%s%s", + prefix, + suffix, + "".equals(suffix) + ? "" + : String.format("%s%d%s", containerPrefix, i, containerSuffix)))); + } + } + } + + // add `authorization_model_id` to the URL query string + if (getAuthorizationModelId() != null) { + joiner.add(String.format( + "%sauthorization_model_id%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getAuthorizationModelId()), StandardCharsets.UTF_8) + .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/BatchCheckResponse.java b/src/main/java/dev/openfga/sdk/api/model/BatchCheckResponse.java new file mode 100644 index 00000000..8eaa9d02 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/BatchCheckResponse.java @@ -0,0 +1,154 @@ +/* + * 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.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * BatchCheckResponse + */ +@JsonPropertyOrder({BatchCheckResponse.JSON_PROPERTY_RESULT}) +public class BatchCheckResponse { + public static final String JSON_PROPERTY_RESULT = "result"; + private Map result = new HashMap<>(); + + public BatchCheckResponse() {} + + public BatchCheckResponse result(Map result) { + this.result = result; + return this; + } + + public BatchCheckResponse putResultItem(String key, BatchCheckSingleResult resultItem) { + if (this.result == null) { + this.result = new HashMap<>(); + } + this.result.put(key, resultItem); + return this; + } + + /** + * map keys are the correlation_id values from the BatchCheckItems in the request + * @return result + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_RESULT) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public Map getResult() { + return result; + } + + @JsonProperty(JSON_PROPERTY_RESULT) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setResult(Map result) { + this.result = result; + } + + /** + * Return true if this BatchCheckResponse object is equal to o. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BatchCheckResponse batchCheckResponse = (BatchCheckResponse) o; + return Objects.equals(this.result, batchCheckResponse.result); + } + + @Override + public int hashCode() { + return Objects.hash(result); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class BatchCheckResponse {\n"); + sb.append(" result: ").append(toIndentedString(result)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + // add `result` to the URL query string + if (getResult() != null) { + for (String _key : getResult().keySet()) { + if (getResult().get(_key) != null) { + joiner.add(getResult() + .get(_key) + .toUrlQueryString(String.format( + "%sresult%s%s", + prefix, + suffix, + "".equals(suffix) + ? "" + : String.format("%s%d%s", containerPrefix, _key, containerSuffix)))); + } + } + } + + return joiner.toString(); + } +} diff --git a/src/main/java/dev/openfga/sdk/api/model/BatchCheckSingleResult.java b/src/main/java/dev/openfga/sdk/api/model/BatchCheckSingleResult.java new file mode 100644 index 00000000..821c2506 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/BatchCheckSingleResult.java @@ -0,0 +1,171 @@ +/* + * 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.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * BatchCheckSingleResult + */ +@JsonPropertyOrder({BatchCheckSingleResult.JSON_PROPERTY_ALLOWED, BatchCheckSingleResult.JSON_PROPERTY_ERROR}) +public class BatchCheckSingleResult { + public static final String JSON_PROPERTY_ALLOWED = "allowed"; + private Boolean allowed; + + public static final String JSON_PROPERTY_ERROR = "error"; + private CheckError error; + + public BatchCheckSingleResult() {} + + public BatchCheckSingleResult allowed(Boolean allowed) { + this.allowed = allowed; + return this; + } + + /** + * Get allowed + * @return allowed + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_ALLOWED) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public Boolean getAllowed() { + return allowed; + } + + @JsonProperty(JSON_PROPERTY_ALLOWED) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setAllowed(Boolean allowed) { + this.allowed = allowed; + } + + public BatchCheckSingleResult error(CheckError error) { + this.error = error; + return this; + } + + /** + * Get error + * @return error + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_ERROR) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public CheckError getError() { + return error; + } + + @JsonProperty(JSON_PROPERTY_ERROR) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setError(CheckError error) { + this.error = error; + } + + /** + * Return true if this BatchCheckSingleResult object is equal to o. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + BatchCheckSingleResult batchCheckSingleResult = (BatchCheckSingleResult) o; + return Objects.equals(this.allowed, batchCheckSingleResult.allowed) + && Objects.equals(this.error, batchCheckSingleResult.error); + } + + @Override + public int hashCode() { + return Objects.hash(allowed, error); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class BatchCheckSingleResult {\n"); + sb.append(" allowed: ").append(toIndentedString(allowed)).append("\n"); + sb.append(" error: ").append(toIndentedString(error)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + // add `allowed` to the URL query string + if (getAllowed() != null) { + joiner.add(String.format( + "%sallowed%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getAllowed()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + // add `error` to the URL query string + if (getError() != null) { + joiner.add(getError().toUrlQueryString(prefix + "error" + suffix)); + } + + return joiner.toString(); + } +} diff --git a/src/main/java/dev/openfga/sdk/api/model/CheckError.java b/src/main/java/dev/openfga/sdk/api/model/CheckError.java new file mode 100644 index 00000000..b11177f2 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/CheckError.java @@ -0,0 +1,217 @@ +/* + * 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.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * CheckError + */ +@JsonPropertyOrder({ + CheckError.JSON_PROPERTY_INPUT_ERROR, + CheckError.JSON_PROPERTY_INTERNAL_ERROR, + CheckError.JSON_PROPERTY_MESSAGE +}) +public class CheckError { + public static final String JSON_PROPERTY_INPUT_ERROR = "input_error"; + private ErrorCode inputError = ErrorCode.NO_ERROR; + + public static final String JSON_PROPERTY_INTERNAL_ERROR = "internal_error"; + private InternalErrorCode internalError = InternalErrorCode.NO_INTERNAL_ERROR; + + public static final String JSON_PROPERTY_MESSAGE = "message"; + private String message; + + public CheckError() {} + + public CheckError inputError(ErrorCode inputError) { + this.inputError = inputError; + return this; + } + + /** + * Get inputError + * @return inputError + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_INPUT_ERROR) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ErrorCode getInputError() { + return inputError; + } + + @JsonProperty(JSON_PROPERTY_INPUT_ERROR) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setInputError(ErrorCode inputError) { + this.inputError = inputError; + } + + public CheckError internalError(InternalErrorCode internalError) { + this.internalError = internalError; + return this; + } + + /** + * Get internalError + * @return internalError + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_INTERNAL_ERROR) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public InternalErrorCode getInternalError() { + return internalError; + } + + @JsonProperty(JSON_PROPERTY_INTERNAL_ERROR) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setInternalError(InternalErrorCode internalError) { + this.internalError = internalError; + } + + public CheckError message(String message) { + this.message = message; + return this; + } + + /** + * Get message + * @return message + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_MESSAGE) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public String getMessage() { + return message; + } + + @JsonProperty(JSON_PROPERTY_MESSAGE) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setMessage(String message) { + this.message = message; + } + + /** + * Return true if this CheckError object is equal to o. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CheckError checkError = (CheckError) o; + return Objects.equals(this.inputError, checkError.inputError) + && Objects.equals(this.internalError, checkError.internalError) + && Objects.equals(this.message, checkError.message); + } + + @Override + public int hashCode() { + return Objects.hash(inputError, internalError, message); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class CheckError {\n"); + sb.append(" inputError: ").append(toIndentedString(inputError)).append("\n"); + sb.append(" internalError: ").append(toIndentedString(internalError)).append("\n"); + sb.append(" message: ").append(toIndentedString(message)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + // add `input_error` to the URL query string + if (getInputError() != null) { + joiner.add(String.format( + "%sinput_error%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getInputError()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + // add `internal_error` to the URL query string + if (getInternalError() != null) { + joiner.add(String.format( + "%sinternal_error%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getInternalError()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + // add `message` to the URL query string + if (getMessage() != null) { + joiner.add(String.format( + "%smessage%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getMessage()), 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 index 0f3fc518..fcef615c 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java +++ b/src/main/java/dev/openfga/sdk/api/model/ConsistencyPreference.java @@ -16,7 +16,7 @@ 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. + * Controls the consistency preferences when calling the query APIs. - 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"), diff --git a/src/main/java/dev/openfga/sdk/api/model/ErrorCode.java b/src/main/java/dev/openfga/sdk/api/model/ErrorCode.java index dd021a2e..dafbef28 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ErrorCode.java +++ b/src/main/java/dev/openfga/sdk/api/model/ErrorCode.java @@ -115,6 +115,10 @@ public enum ErrorCode { UNSUPPORTED_SCHEMA_VERSION("unsupported_schema_version"), + CANCELLED("cancelled"), + + INVALID_START_TIME("invalid_start_time"), + UNKNOWN_DEFAULT_OPEN_API("unknown_default_open_api"); private String value; 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 7772687d..d85f95a0 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/ExpandRequest.java @@ -26,7 +26,8 @@ @JsonPropertyOrder({ ExpandRequest.JSON_PROPERTY_TUPLE_KEY, ExpandRequest.JSON_PROPERTY_AUTHORIZATION_MODEL_ID, - ExpandRequest.JSON_PROPERTY_CONSISTENCY + ExpandRequest.JSON_PROPERTY_CONSISTENCY, + ExpandRequest.JSON_PROPERTY_CONTEXTUAL_TUPLES }) public class ExpandRequest { public static final String JSON_PROPERTY_TUPLE_KEY = "tuple_key"; @@ -38,6 +39,9 @@ public class ExpandRequest { public static final String JSON_PROPERTY_CONSISTENCY = "consistency"; private ConsistencyPreference consistency = ConsistencyPreference.UNSPECIFIED; + public static final String JSON_PROPERTY_CONTEXTUAL_TUPLES = "contextual_tuples"; + private ContextualTupleKeys contextualTuples; + public ExpandRequest() {} public ExpandRequest tupleKey(ExpandRequestTupleKey tupleKey) { @@ -106,6 +110,28 @@ public void setConsistency(ConsistencyPreference consistency) { this.consistency = consistency; } + public ExpandRequest contextualTuples(ContextualTupleKeys contextualTuples) { + this.contextualTuples = contextualTuples; + return this; + } + + /** + * Get contextualTuples + * @return contextualTuples + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CONTEXTUAL_TUPLES) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public ContextualTupleKeys getContextualTuples() { + return contextualTuples; + } + + @JsonProperty(JSON_PROPERTY_CONTEXTUAL_TUPLES) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setContextualTuples(ContextualTupleKeys contextualTuples) { + this.contextualTuples = contextualTuples; + } + /** * Return true if this Expand_request object is equal to o. */ @@ -120,12 +146,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.consistency, expandRequest.consistency); + && Objects.equals(this.consistency, expandRequest.consistency) + && Objects.equals(this.contextualTuples, expandRequest.contextualTuples); } @Override public int hashCode() { - return Objects.hash(tupleKey, authorizationModelId, consistency); + return Objects.hash(tupleKey, authorizationModelId, consistency, contextualTuples); } @Override @@ -137,6 +164,9 @@ public String toString() { .append(toIndentedString(authorizationModelId)) .append("\n"); sb.append(" consistency: ").append(toIndentedString(consistency)).append("\n"); + sb.append(" contextualTuples: ") + .append(toIndentedString(contextualTuples)) + .append("\n"); sb.append("}"); return sb.toString(); } @@ -209,6 +239,11 @@ public String toUrlQueryString(String prefix) { .replaceAll("\\+", "%20"))); } + // add `contextual_tuples` to the URL query string + if (getContextualTuples() != null) { + joiner.add(getContextualTuples().toUrlQueryString(prefix + "contextual_tuples" + suffix)); + } + return joiner.toString(); } } diff --git a/src/main/java/dev/openfga/sdk/api/model/ForbiddenResponse.java b/src/main/java/dev/openfga/sdk/api/model/ForbiddenResponse.java new file mode 100644 index 00000000..d7cc4b87 --- /dev/null +++ b/src/main/java/dev/openfga/sdk/api/model/ForbiddenResponse.java @@ -0,0 +1,176 @@ +/* + * 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.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonPropertyOrder; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.Objects; +import java.util.StringJoiner; + +/** + * ForbiddenResponse + */ +@JsonPropertyOrder({ForbiddenResponse.JSON_PROPERTY_CODE, ForbiddenResponse.JSON_PROPERTY_MESSAGE}) +public class ForbiddenResponse { + public static final String JSON_PROPERTY_CODE = "code"; + private AuthErrorCode code = AuthErrorCode.NO_AUTH_ERROR; + + public static final String JSON_PROPERTY_MESSAGE = "message"; + private String message; + + public ForbiddenResponse() {} + + public ForbiddenResponse code(AuthErrorCode code) { + this.code = code; + return this; + } + + /** + * Get code + * @return code + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_CODE) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public AuthErrorCode getCode() { + return code; + } + + @JsonProperty(JSON_PROPERTY_CODE) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setCode(AuthErrorCode code) { + this.code = code; + } + + public ForbiddenResponse message(String message) { + this.message = message; + return this; + } + + /** + * Get message + * @return message + **/ + @javax.annotation.Nullable + @JsonProperty(JSON_PROPERTY_MESSAGE) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public String getMessage() { + return message; + } + + @JsonProperty(JSON_PROPERTY_MESSAGE) + @JsonInclude(value = JsonInclude.Include.USE_DEFAULTS) + public void setMessage(String message) { + this.message = message; + } + + /** + * Return true if this ForbiddenResponse object is equal to o. + */ + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + ForbiddenResponse forbiddenResponse = (ForbiddenResponse) o; + return Objects.equals(this.code, forbiddenResponse.code) + && Objects.equals(this.message, forbiddenResponse.message); + } + + @Override + public int hashCode() { + return Objects.hash(code, message); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("class ForbiddenResponse {\n"); + sb.append(" code: ").append(toIndentedString(code)).append("\n"); + sb.append(" message: ").append(toIndentedString(message)).append("\n"); + sb.append("}"); + return sb.toString(); + } + + /** + * Convert the given object to string with each line indented by 4 spaces + * (except the first line). + */ + private String toIndentedString(Object o) { + if (o == null) { + return "null"; + } + return o.toString().replace("\n", "\n "); + } + + /** + * Convert the instance into URL query string. + * + * @return URL query string + */ + public String toUrlQueryString() { + return toUrlQueryString(null); + } + + /** + * Convert the instance into URL query string. + * + * @param prefix prefix of the query string + * @return URL query string + */ + public String toUrlQueryString(String prefix) { + String suffix = ""; + String containerSuffix = ""; + String containerPrefix = ""; + if (prefix == null) { + // style=form, explode=true, e.g. /pet?name=cat&type=manx + prefix = ""; + } else { + // deepObject style e.g. /pet?id[name]=cat&id[type]=manx + prefix = prefix + "["; + suffix = "]"; + containerSuffix = "]"; + containerPrefix = "["; + } + + StringJoiner joiner = new StringJoiner("&"); + + // add `code` to the URL query string + if (getCode() != null) { + joiner.add(String.format( + "%scode%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getCode()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + // add `message` to the URL query string + if (getMessage() != null) { + joiner.add(String.format( + "%smessage%s=%s", + prefix, + suffix, + URLEncoder.encode(String.valueOf(getMessage()), StandardCharsets.UTF_8) + .replaceAll("\\+", "%20"))); + } + + return joiner.toString(); + } +} diff --git a/src/main/java/dev/openfga/sdk/api/model/InternalErrorCode.java b/src/main/java/dev/openfga/sdk/api/model/InternalErrorCode.java index b807b166..e89ed2cd 100644 --- a/src/main/java/dev/openfga/sdk/api/model/InternalErrorCode.java +++ b/src/main/java/dev/openfga/sdk/api/model/InternalErrorCode.java @@ -23,8 +23,6 @@ public enum InternalErrorCode { INTERNAL_ERROR("internal_error"), - CANCELLED("cancelled"), - DEADLINE_EXCEEDED("deadline_exceeded"), ALREADY_EXISTS("already_exists"), 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 6d89ca0d..344e3578 100644 --- a/src/main/java/dev/openfga/sdk/api/model/ReadRequest.java +++ b/src/main/java/dev/openfga/sdk/api/model/ReadRequest.java @@ -73,6 +73,8 @@ public ReadRequest pageSize(Integer pageSize) { /** * Get pageSize + * minimum: 1 + * maximum: 100 * @return pageSize **/ @javax.annotation.Nullable diff --git a/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java b/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java index a51568b0..6182a5fd 100644 --- a/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java +++ b/src/test-integration/java/dev/openfga/sdk/api/OpenFgaApiIntegrationTest.java @@ -285,7 +285,7 @@ public void write_and_readChanges() throws Exception { // When api.write(storeId, writeRequest).get(); ReadChangesResponse response = - api.readChanges(storeId, null, null, null).get().getData(); + api.readChanges(storeId, null, null, null, null).get().getData(); // Then assertEquals(1, response.getChanges().size()); @@ -318,7 +318,7 @@ public void write_readAssertions() throws Exception { // Then String responseJson = mapper.writeValueAsString(response.getAssertions()); assertEquals( - "[{\"tuple_key\":{\"object\":\"document:2021-budget\",\"relation\":\"reader\",\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\"},\"expectation\":true}]", + "[{\"tuple_key\":{\"object\":\"document:2021-budget\",\"relation\":\"reader\",\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\"},\"expectation\":true,\"contextual_tuples\":[],\"context\":null}]", responseJson); } 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 5e5f322c..76e93b13 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 @@ -331,7 +331,7 @@ public void write_readAssertions() throws Exception { // Then String responseJson = mapper.writeValueAsString(response.getAssertions()); assertEquals( - "[{\"tuple_key\":{\"object\":\"document:2021-budget\",\"relation\":\"reader\",\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\"},\"expectation\":true}]", + "[{\"tuple_key\":{\"object\":\"document:2021-budget\",\"relation\":\"reader\",\"user\":\"user:81684243-9356-4421-8fbf-a4f8d36aa31b\"},\"expectation\":true,\"contextual_tuples\":[],\"context\":null}]", responseJson); } diff --git a/src/test-integration/java/dev/openfga/sdk/example/Example1.java b/src/test-integration/java/dev/openfga/sdk/example/Example1.java index 3d3d7af9..1af068bf 100644 --- a/src/test-integration/java/dev/openfga/sdk/example/Example1.java +++ b/src/test-integration/java/dev/openfga/sdk/example/Example1.java @@ -104,7 +104,7 @@ public void run(String apiUrl) throws Exception { .writes(List.of(new ClientTupleKey() .user("user:anne") .relation("writer") - ._object("document:roadmap"))), + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"))), new ClientWriteOptions() .disableTransactions(true) .authorizationModelId(authorizationModel.getAuthorizationModelId())) @@ -129,7 +129,7 @@ public void run(String apiUrl) throws Exception { .check(new ClientCheckRequest() .user("user:anne") .relation("reader") - ._object("document:roadmap")) + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a")) .get(); System.out.println("Allowed: " + failingCheckResponse.getAllowed()); } catch (Exception e) { @@ -143,7 +143,7 @@ public void run(String apiUrl) throws Exception { // .check(new ClientCheckRequest() // .user("user:anne") // .relation("reader") - // ._object("document:roadmap") + // ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") // .context(Map.of("ViewCount", 100))) // .get(); // System.out.println("Allowed: " + checkResponse.getAllowed()); @@ -159,7 +159,7 @@ public void run(String apiUrl) throws Exception { new ClientAssertion() .user("user:anne") .relation("reader") - ._object("document:roadmap") + ._object("document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a") .expectation(false))) .get(); System.out.println("Assertions updated"); diff --git a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java index 1250b360..e9525469 100644 --- a/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java +++ b/src/test/java/dev/openfga/sdk/api/OpenFgaApiTest.java @@ -24,6 +24,7 @@ import dev.openfga.sdk.errors.*; import java.net.http.HttpClient; import java.time.Duration; +import java.time.OffsetDateTime; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -804,9 +805,10 @@ public void readChangesTest() throws Exception { String type = null; // Input is optional Integer pageSize = null; // Input is optional String continuationToken = null; // Input is optional + OffsetDateTime startTime = null; // Input is optional // When - var response = fga.readChanges(DEFAULT_STORE_ID, type, pageSize, continuationToken) + var response = fga.readChanges(DEFAULT_STORE_ID, type, pageSize, continuationToken, startTime) .get(); // Then @@ -824,8 +826,9 @@ public void readChangesTest() throws Exception { @Test public void readChanges_storeIdRequired() throws Exception { // When - var exception = assertThrows(FgaInvalidParameterException.class, () -> fga.readChanges(null, null, null, null) - .get()); + var exception = + assertThrows(FgaInvalidParameterException.class, () -> fga.readChanges(null, null, null, null, null) + .get()); // Then assertEquals("Required parameter storeId was invalid when calling readChanges.", exception.getMessage()); @@ -841,11 +844,12 @@ public void readChanges_400() throws Exception { String type = null; // Input is optional Integer pageSize = null; // Input is optional String continuationToken = null; // Input is optional + OffsetDateTime startTime = null; // Input is optional // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.readChanges(DEFAULT_STORE_ID, type, pageSize, continuationToken) - .get()); + ExecutionException execException = assertThrows(ExecutionException.class, () -> fga.readChanges( + DEFAULT_STORE_ID, type, pageSize, continuationToken, startTime) + .get()); // Then mockHttpClient.verify().get(getUrl).called(1); @@ -866,11 +870,12 @@ public void readChanges_404() throws Exception { String type = null; // Input is optional Integer pageSize = null; // Input is optional String continuationToken = null; // Input is optional + OffsetDateTime startTime = null; // Input is optional // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.readChanges(DEFAULT_STORE_ID, type, pageSize, continuationToken) - .get()); + ExecutionException execException = assertThrows(ExecutionException.class, () -> fga.readChanges( + DEFAULT_STORE_ID, type, pageSize, continuationToken, startTime) + .get()); // Then mockHttpClient.verify().get(getUrl).called(1); @@ -890,11 +895,12 @@ public void readChanges_500() throws Exception { String type = null; // Input is optional Integer pageSize = null; // Input is optional String continuationToken = null; // Input is optional + OffsetDateTime startTime = null; // Input is optional // When - ExecutionException execException = assertThrows( - ExecutionException.class, () -> fga.readChanges(DEFAULT_STORE_ID, type, pageSize, continuationToken) - .get()); + ExecutionException execException = assertThrows(ExecutionException.class, () -> fga.readChanges( + DEFAULT_STORE_ID, type, pageSize, continuationToken, startTime) + .get()); // Then mockHttpClient.verify().get(getUrl).called(1 + DEFAULT_MAX_RETRIES); @@ -1429,7 +1435,7 @@ 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\",\"consistency\":\"%s\"}", + "{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\",\"contextual_tuples\":null}", DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY); String responseBody = String.format( "{\"tree\":{\"root\":{\"union\":{\"nodes\":[{\"leaf\":{\"users\":{\"users\":[\"%s\"]}}}]}}}}", @@ -1792,7 +1798,7 @@ public void writeAssertionsTest() throws Exception { String putUrl = "https://api.fga.example/stores/01YCP46JKYM8FJCQ37NMBYHE5X/assertions/01G5JAVJ41T49E9TT3SKVS7X1J"; String expectedBody = String.format( - "{\"assertions\":[{\"tuple_key\":{\"object\":\"%s\",\"relation\":\"%s\",\"user\":\"%s\"},\"expectation\":true}]}", + "{\"assertions\":[{\"tuple_key\":{\"object\":\"%s\",\"relation\":\"%s\",\"user\":\"%s\"},\"expectation\":true,\"contextual_tuples\":[],\"context\":null}]}", DEFAULT_OBJECT, DEFAULT_RELATION, DEFAULT_USER); mockHttpClient.onPut(putUrl).withBody(is(expectedBody)).doReturn(200, EMPTY_RESPONSE_BODY); WriteAssertionsRequest request = new WriteAssertionsRequest() 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 e02e7ab2..280080f8 100644 --- a/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java +++ b/src/test/java/dev/openfga/sdk/api/client/OpenFgaClientTest.java @@ -26,6 +26,7 @@ import dev.openfga.sdk.errors.*; import java.net.http.HttpClient; import java.time.Duration; +import java.time.OffsetDateTime; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -707,13 +708,15 @@ public void readLatestAuthorizationModelTest() throws Exception { public void readChanges() throws Exception { // Given String changeType = "repo"; + OffsetDateTime startTime = null; String user = "user:81684243-9356-4421-8fbf-a4f8d36aa31b"; String relation = "viewer"; - String object = "document:roadmap"; + String object = "document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"; String continuationToken = "eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ"; - ClientReadChangesRequest request = new ClientReadChangesRequest().type(changeType); + ClientReadChangesRequest request = + new ClientReadChangesRequest().type(changeType).startTime(startTime); String getUrl = String.format("https://api.fga.example/stores/%s/changes?type=%s", DEFAULT_STORE_ID, changeType); String responseBody = String.format( @@ -1875,7 +1878,7 @@ 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\",\"consistency\":\"%s\"}", + "{\"tuple_key\":{\"relation\":\"%s\",\"object\":\"%s\"},\"authorization_model_id\":\"%s\",\"consistency\":\"%s\",\"contextual_tuples\":null}", DEFAULT_RELATION, DEFAULT_OBJECT, DEFAULT_AUTH_MODEL_ID, ConsistencyPreference.HIGHER_CONSISTENCY); String responseBody = String.format( "{\"tree\":{\"root\":{\"union\":{\"nodes\":[{\"leaf\":{\"users\":{\"users\":[\"%s\"]}}}]}}}}", @@ -2564,7 +2567,7 @@ public void writeAssertionsTest() throws Exception { String putUrl = String.format( "https://api.fga.example/stores/%s/assertions/%s", DEFAULT_STORE_ID, DEFAULT_AUTH_MODEL_ID); String expectedBody = String.format( - "{\"assertions\":[{\"tuple_key\":{\"object\":\"%s\",\"relation\":\"%s\",\"user\":\"%s\"},\"expectation\":true}]}", + "{\"assertions\":[{\"tuple_key\":{\"object\":\"%s\",\"relation\":\"%s\",\"user\":\"%s\"},\"expectation\":true,\"contextual_tuples\":[],\"context\":null}]}", DEFAULT_OBJECT, DEFAULT_RELATION, DEFAULT_USER); mockHttpClient.onPut(putUrl).withBody(is(expectedBody)).doReturn(200, EMPTY_RESPONSE_BODY); List assertions = List.of(new ClientAssertion()