From 1625b60ad004c7cd8b48b0e358273ae612587dd0 Mon Sep 17 00:00:00 2001 From: Evan Sims Date: Wed, 26 Mar 2025 10:32:29 -0500 Subject: [PATCH] feat: support List Stores name filter --- CHANGELOG.md | 49 +++++--- docs/OpenFgaApi.md | 6 +- openfga_sdk/api/open_fga_api.py | 12 +- openfga_sdk/client/client.py | 3 + openfga_sdk/sync/client/client.py | 3 + openfga_sdk/sync/open_fga_api.py | 12 +- test/api/open_fga_api_test.py | 191 ++++++++++++++++++++++++++++++ test/client/client_test.py | 63 ++++++++++ test/sync/client/client_test.py | 63 ++++++++++ 9 files changed, 376 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ab88585..cf7bb578 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### [0.9.3](https://github.com/openfga/python-sdk/compare/v0.9.2...v0.9.3) (2025-03-26) +- feat: feat: support List Stores name filter (#181) - fix: urllib3 compatibility < v2 (#179) ### [0.9.2](https://github.com/openfga/python-sdk/compare/v0.9.1...v0.9.2) (2025-03-25) @@ -87,16 +88,19 @@ Please note that if you use third-party OpenTelemetry tooling to visualize the a - feat: enhancements to OpenTelemetry support (#120) Note this introduces some breaking changes to our metrics: + 1. `fga-client.request.method` is now in TitleCase to match the naming conventions in the Protos, e.g. `Check`, `ListObjects`, etc.. 2. Due to possible high costs for attributes with high cardinality, we are no longer including the following attributes by default: - * `fga-client.user` - * `http.client.request.duration` - * `http.server.request.duration` + +- `fga-client.user` +- `http.client.request.duration` +- `http.server.request.duration` We added configuration options to allow you to set which specific metrics and attributes you care about in case the defaults don't work for your use-case ## v0.6.1 ### [0.6.1](https://github.com/openfga/python-sdk/compare/v0.6.0...v0.6.1) (2024-07-31) + - feat: add support for specifying consistency when evaluating or reading (#129) Note: To use this feature, you need to be running OpenFGA v1.5.7+ with the experimental flag `enable-consistency-params` enabled. See the [v1.5.7 release notes](https://github.com/openfga/openfga/releases/tag/v1.5.7) for details. @@ -164,21 +168,22 @@ You will have to modify some parts of your code, but we hope this will be to the and so the Pointer-to-String conversion is no longer needed. Some of the changes to expect: + - The following request interfaces changed: - - `CheckRequest`: the `TupleKey` field is now of interface `CheckRequestTupleKey`, you can also now pass in `Context` - - `ExpandRequest`: the `TupleKey` field is now of interface `ExpandRequestTupleKey` - - `ReadRequest`: the `TupleKey` field is now of interface `ReadRequestTupleKey` - - `WriteRequest`: now takes `WriteRequestWrites` and `WriteRequestDeletes`, the latter of which accepts `TupleKeyWithoutCondition` - - And more + - `CheckRequest`: the `TupleKey` field is now of interface `CheckRequestTupleKey`, you can also now pass in `Context` + - `ExpandRequest`: the `TupleKey` field is now of interface `ExpandRequestTupleKey` + - `ReadRequest`: the `TupleKey` field is now of interface `ReadRequestTupleKey` + - `WriteRequest`: now takes `WriteRequestWrites` and `WriteRequestDeletes`, the latter of which accepts `TupleKeyWithoutCondition` + - And more - The following interfaces had fields that were optional are are now required: - - `CreateStoreResponse` - - `GetStoreResponse` - - `ListStoresResponse` - - `ListObjectsResponse` - - `ReadChangesResponse` - - `ReadResponse` - - `AuthorizationModel` - - And more + - `CreateStoreResponse` + - `GetStoreResponse` + - `ListStoresResponse` + - `ListObjectsResponse` + - `ReadChangesResponse` + - `ReadResponse` + - `AuthorizationModel` + - And more Take a look at the changes in models in https://github.com/openfga/python-sdk/commit/9ed1f70d64db71451de2eb26e330bbd511625c5c and https://github.com/openfga/python-sdk/pull/59/files for more. @@ -191,6 +196,7 @@ Note: `v0.3.4` has been re-released as `v0.4.0` due to breaking changes ## v0.3.3 ### [0.3.3](https://github.com/openfga/python-sdk/compare/v0.3.2...v0.3.3) (2024-01-02) + - fix: correct type hints for list_relations - fix: handle empty TupleKey in read - chore: add example project @@ -198,17 +204,20 @@ Note: `v0.3.4` has been re-released as `v0.4.0` due to breaking changes ## v0.3.2 ### [0.3.2](https://github.com/openfga/python-sdk/compare/v0.3.1...v0.3.2) (2023-12-15) + - feat: allow passing ssl certs to client configuration - feat: setup openfga_sdk.help for bug info ## v0.3.1 ### [0.3.1](https://github.com/openfga/python-sdk/compare/v0.3.0...v0.3.1) (2023-12-01) + - chore(deps): reduce min urllib3 to 1.25.11, add dependabot & bump deps ## v0.3.0 ### [0.3.0](https://github.com/openfga/python-sdk/compare/v0.2.1...v0.3.0) (2023-11-02) + - feat(client): introduce synchronous OpenFgaClient (https://github.com/openfga/python-sdk/commit/c92b436543e263f2c1af6af15f1c4fda1c9dad21) - refactor(config): extract oauth2 from credentials, removing logic from credentials configuration (https://github.com/openfga/python-sdk/commit/f91d14b25f86dd3f2e4d48229bb53cc7d9b20f1b) - feat(client): performance improvements to batch_check (https://github.com/openfga/python-sdk/commit/d8f2d429d2c279c0e56d5ef2a6172df8bfadd82b) @@ -216,6 +225,7 @@ Note: `v0.3.4` has been re-released as `v0.4.0` due to breaking changes ## v0.2.1 ### [0.2.1](https://github.com/openfga/python-sdk/compare/v0.2.0...v0.2.1) (2023-09-05) + - fix(client): fix a crash when calling check with contextual tuples (https://github.com/openfga/python-sdk/commit/dded83f9a75dc1f01c1cfbd8385a25654129f78f) - chore(docs): update README and fix a few typos (https://github.com/openfga/python-sdk/pull/21, https://github.com/openfga/python-sdk/pull/31, https://github.com/openfga/python-sdk/pull/32, https://github.com/openfga/python-sdk/pull/33, https://github.com/openfga/python-sdk/pull/34, https://github.com/openfga/python-sdk/pull/37) @@ -224,6 +234,7 @@ Note: `v0.3.4` has been re-released as `v0.4.0` due to breaking changes ### [0.2.0](https://github.com/openfga/python-sdk/compare/v0.1.1...v0.2.0) (2023-05-25) Changes: + - [BREAKING] feat!: `schema_version` is now required when calling `write_authorization_model` - [BREAKING] chore!: drop support for python < 3.10 - feat(client): add OpenFgaClient wrapper see [docs](https://github.com/openfga/python-sdk/tree/main#readme), see the `v0.1.1` docs for [the OpenFgaApi docs](https://github.com/openfga/python-sdk/tree/v0.1.1#readme) @@ -247,18 +258,20 @@ Changes: Updated to include support for [OpenFGA 0.3.0](https://github.com/openfga/openfga/releases/tag/v0.3.0) Changes: + - [BREAKING] feat(list-objects)!: response has been changed to include the object type - e.g. response that was `{"object_ids":["roadmap"]}`, will now be `{"objects":["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]}` + e.g. response that was `{"object_ids":["roadmap"]}`, will now be `{"objects":["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]}` Fixes: -- fix(models): update interfaces that had incorrectly optional fields to make them required +- fix(models): update interfaces that had incorrectly optional fields to make them required ## v0.0.1 ### [0.0.1](https://github.com/openfga/python-sdk/releases/tag/v0.0.1) (2022-08-31) Initial OpenFGA Python SDK release + - Support for [OpenFGA](https://github.com/openfga/openfga) API - CRUD stores - Create, read & list authorization models diff --git a/docs/OpenFgaApi.md b/docs/OpenFgaApi.md index 379be95d..bce21985 100644 --- a/docs/OpenFgaApi.md +++ b/docs/OpenFgaApi.md @@ -643,10 +643,11 @@ async with openfga_sdk.ApiClient(configuration) as api_client: api_instance = openfga_sdk.OpenFgaApi(api_client) page_size = 56 # int | (optional) continuation_token = 'continuation_token_example' # str | (optional) + name = 'name_example' # str | The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated (optional) try: # List all stores - api_response = await api_instance.api_instance.list_stores(page_size=page_size, continuation_token=continuation_token) + api_response = await api_instance.api_instance.list_stores(page_size=page_size, continuation_token=continuation_token, name=name) pprint(api_response) except ApiException as e: print("Exception when calling OpenFgaApi->list_stores: %s\n" % e) @@ -660,6 +661,7 @@ Name | Type | Description | Notes ------------- | ------------- | ------------- | ------------- **page_size** | **int**| | [optional] **continuation_token** | **str**| | [optional] + **name** | **str**| The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated | [optional] ### Return type @@ -777,7 +779,7 @@ No authorization required Get tuples from the store that matches a query, without following userset rewrite rules -The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). +The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. If tuple_key.user is specified, it needs to be a full object (e.g., `type:user_id`). ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). ### Example diff --git a/openfga_sdk/api/open_fga_api.py b/openfga_sdk/api/open_fga_api.py index 7a9e6460..2be2fbfb 100644 --- a/openfga_sdk/api/open_fga_api.py +++ b/openfga_sdk/api/open_fga_api.py @@ -1255,6 +1255,8 @@ async def list_stores(self, **kwargs): :type page_size: int, optional :param continuation_token:(optional) :type continuation_token: str, optional + :param name: The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated(optional) + :type name: str, optional :param async_req: Whether to execute the request asynchronously. :type async_req: bool, optional :param _preload_content: if False, the urllib3.HTTPResponse object will @@ -1284,6 +1286,8 @@ async def list_stores_with_http_info(self, **kwargs): :type page_size: int, optional :param continuation_token:(optional) :type continuation_token: str, optional + :param name: The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated(optional) + :type name: str, optional :param async_req: Whether to execute the request asynchronously. :type async_req: bool, optional :param _return_http_data_only: response data without head status code @@ -1311,7 +1315,7 @@ async def list_stores_with_http_info(self, **kwargs): local_var_params = locals() - all_params = ["page_size", "continuation_token"] + all_params = ["page_size", "continuation_token", "name"] all_params.extend( [ "async_req", @@ -1347,6 +1351,8 @@ async def list_stores_with_http_info(self, **kwargs): query_params.append( ("continuation_token", local_var_params["continuation_token"]) ) + if local_var_params.get("name") is not None: + query_params.append(("name", local_var_params["name"])) header_params = dict(local_var_params.get("_headers", {})) @@ -1593,7 +1599,7 @@ async def list_users_with_http_info(self, body, **kwargs): async def read(self, body, **kwargs): """Get tuples from the store that matches a query, without following userset rewrite rules - The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). + The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. If tuple_key.user is specified, it needs to be a full object (e.g., `type:user_id`). ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). >>> thread = await api.read(body) @@ -1620,7 +1626,7 @@ async def read(self, body, **kwargs): async def read_with_http_info(self, body, **kwargs): """Get tuples from the store that matches a query, without following userset rewrite rules - The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). + The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. If tuple_key.user is specified, it needs to be a full object (e.g., `type:user_id`). ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). >>> thread = api.read_with_http_info(body) diff --git a/openfga_sdk/client/client.py b/openfga_sdk/client/client.py index eae1cd8d..7dbe1729 100644 --- a/openfga_sdk/client/client.py +++ b/openfga_sdk/client/client.py @@ -120,6 +120,8 @@ def options_to_kwargs( """ kwargs = {} if options is not None: + if options.get("name"): + kwargs["name"] = options["name"] if options.get("page_size"): kwargs["page_size"] = options["page_size"] if options.get("continuation_token"): @@ -237,6 +239,7 @@ async def list_stores( ): """ List the stores in the system + :param name(options) - The name parameter instructs the API to only include results that match that name. Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated. :param page_size(options) - Number of items returned per request :param continuation_token(options) - No continuation_token by default :param header(options) - Custom headers to send alongside the request diff --git a/openfga_sdk/sync/client/client.py b/openfga_sdk/sync/client/client.py index 27c55a06..d3238185 100644 --- a/openfga_sdk/sync/client/client.py +++ b/openfga_sdk/sync/client/client.py @@ -121,6 +121,8 @@ def options_to_kwargs( """ kwargs = {} if options is not None: + if options.get("name"): + kwargs["name"] = options["name"] if options.get("page_size"): kwargs["page_size"] = options["page_size"] if options.get("continuation_token"): @@ -238,6 +240,7 @@ def list_stores( ): """ List the stores in the system + :param name(options) - The name parameter instructs the API to only include results that match that name. Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated. :param page_size(options) - Number of items returned per request :param continuation_token(options) - No continuation_token by default :param header(options) - Custom headers to send alongside the request diff --git a/openfga_sdk/sync/open_fga_api.py b/openfga_sdk/sync/open_fga_api.py index 401c1dd9..f9f28eb3 100644 --- a/openfga_sdk/sync/open_fga_api.py +++ b/openfga_sdk/sync/open_fga_api.py @@ -1253,6 +1253,8 @@ def list_stores(self, **kwargs): :type page_size: int, optional :param continuation_token:(optional) :type continuation_token: str, optional + :param name: The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated(optional) + :type name: str, optional :param async_req: Whether to execute the request asynchronously. :type async_req: bool, optional :param _preload_content: if False, the urllib3.HTTPResponse object will @@ -1282,6 +1284,8 @@ def list_stores_with_http_info(self, **kwargs): :type page_size: int, optional :param continuation_token:(optional) :type continuation_token: str, optional + :param name: The name parameter instructs the API to only include results that match that name.Multiple results may be returned. Only exact matches will be returned; substring matches and regexes will not be evaluated(optional) + :type name: str, optional :param async_req: Whether to execute the request asynchronously. :type async_req: bool, optional :param _return_http_data_only: response data without head status code @@ -1309,7 +1313,7 @@ def list_stores_with_http_info(self, **kwargs): local_var_params = locals() - all_params = ["page_size", "continuation_token"] + all_params = ["page_size", "continuation_token", "name"] all_params.extend( [ "async_req", @@ -1345,6 +1349,8 @@ def list_stores_with_http_info(self, **kwargs): query_params.append( ("continuation_token", local_var_params["continuation_token"]) ) + if local_var_params.get("name") is not None: + query_params.append(("name", local_var_params["name"])) header_params = dict(local_var_params.get("_headers", {})) @@ -1591,7 +1597,7 @@ def list_users_with_http_info(self, body, **kwargs): def read(self, body, **kwargs): """Get tuples from the store that matches a query, without following userset rewrite rules - The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). + The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. If tuple_key.user is specified, it needs to be a full object (e.g., `type:user_id`). ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). >>> thread = api.read(body) @@ -1618,7 +1624,7 @@ def read(self, body, **kwargs): def read_with_http_info(self, body, **kwargs): """Get tuples from the store that matches a query, without following userset rewrite rules - The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). + The Read API will return the tuples for a certain store that match a query filter specified in the body of the request. The API doesn't guarantee order by any field. It is different from the `/stores/{store_id}/expand` API in that it only returns relationship tuples that are stored in the system and satisfy the query. In the body: 1. `tuple_key` is optional. If not specified, it will return all tuples in the store. 2. `tuple_key.object` is mandatory if `tuple_key` is specified. It can be a full object (e.g., `type:object_id`) or type only (e.g., `type:`). 3. `tuple_key.user` is mandatory if tuple_key is specified in the case the `tuple_key.object` is a type only. If tuple_key.user is specified, it needs to be a full object (e.g., `type:user_id`). ## Examples ### Query for all objects in a type definition To query for all objects that `user:bob` has `reader` relationship in the `document` type definition, call read API with body of ```json { \"tuple_key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:\" } } ``` The API will return tuples and a continuation token, something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `user:bob` has a `reader` relationship with 1 document `document:2021-budget`. Note that this API, unlike the List Objects API, does not evaluate the tuples in the store. The continuation token will be empty if there are no more tuples to query. ### Query for all stored relationship tuples that have a particular relation and object To query for all users that have `reader` relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\", \"relation\": \"reader\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`). Note that, even if the model said that all `writers` are also `readers`, the API will not return writers such as `user:anne` because it only returns tuples and does not evaluate them. ### Query for all users with all relationships for a particular document To query for all users that have any relationship with `document:2021-budget`, call read API with body of ```json { \"tuple_key\": { \"object\": \"document:2021-budget\" } } ``` The API will return something like ```json { \"tuples\": [ { \"key\": { \"user\": \"user:anne\", \"relation\": \"writer\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-05T13:42:12.356Z\" }, { \"key\": { \"user\": \"user:bob\", \"relation\": \"reader\", \"object\": \"document:2021-budget\" }, \"timestamp\": \"2021-10-06T15:32:11.128Z\" } ], \"continuation_token\": \"eyJwayI6IkxBVEVTVF9OU0NPTkZJR19hdXRoMHN0b3JlIiwic2siOiIxem1qbXF3MWZLZExTcUoyN01MdTdqTjh0cWgifQ==\" } ``` This means that `document:2021-budget` has 1 `reader` (`user:bob`) and 1 `writer` (`user:anne`). >>> thread = api.read_with_http_info(body) diff --git a/test/api/open_fga_api_test.py b/test/api/open_fga_api_test.py index 2114deee..b7151963 100644 --- a/test/api/open_fga_api_test.py +++ b/test/api/open_fga_api_test.py @@ -1622,6 +1622,197 @@ async def test_check_custom_header(self, mock_request): _request_timeout=None, ) + @patch.object(rest.RESTClientObject, "request") + async def test_list_stores_with_name(self, mock_request): + """Test case for list_stores with name parameter + + Get stores filtered by name + """ + response_body = """ +{ + "stores": [ + { + "id": "01YCP46JKYM8FJCQ37NMBYHE5X", + "name": "test-store", + "created_at": "2022-07-25T21:15:37.524Z", + "updated_at": "2022-07-25T21:15:37.524Z", + "deleted_at": "2022-07-25T21:15:37.524Z" + }, + { + "id": "01YCP46JKYM8FJCQ37NMBYHE6X", + "name": "other-store", + "created_at": "2022-07-25T21:15:37.524Z", + "updated_at": "2022-07-25T21:15:37.524Z", + "deleted_at": "2022-07-25T21:15:37.524Z" + } + ], + "continuation_token": "token123" +} + """ + mock_request.return_value = mock_response(response_body, 200) + configuration = self.configuration + async with openfga_sdk.ApiClient(configuration) as api_client: + api_instance = open_fga_api.OpenFgaApi(api_client) + # Get stores filtered by name + api_response = await api_instance.list_stores(name="test-store") + self.assertIsInstance(api_response, ListStoresResponse) + self.assertEqual(api_response.continuation_token, "token123") + store1 = Store( + id="01YCP46JKYM8FJCQ37NMBYHE5X", + name="test-store", + created_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + updated_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + deleted_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + ) + store2 = Store( + id="01YCP46JKYM8FJCQ37NMBYHE6X", + name="other-store", + created_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + updated_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + deleted_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + ) + stores = [store1, store2] + self.assertEqual(api_response.stores, stores) + mock_request.assert_called_once_with( + "GET", + "http://api.fga.example/stores", + headers=ANY, + body=None, + query_params=[("name", "test-store")], + post_params=[], + _preload_content=ANY, + _request_timeout=None, + ) + await api_client.close() + + @patch.object(rest.RESTClientObject, "request") + async def test_read_with_tuple_key_user(self, mock_request): + """Test case for read with tuple key containing user + + Get tuples from the store that matches a query with user + """ + response_body = """ + { + "tuples": [ + { + "key": { + "user": "user:bob", + "relation": "reader", + "object": "document:2021-budget" + }, + "timestamp": "2021-10-06T15:32:11.128Z" + } + ], + "continuation_token": "token123" +} + """ + mock_request.return_value = mock_response(response_body, 200) + configuration = self.configuration + configuration.store_id = store_id + async with openfga_sdk.ApiClient(configuration) as api_client: + api_instance = open_fga_api.OpenFgaApi(api_client) + body = ReadRequest( + tuple_key=ReadRequestTupleKey( + object="document:2021-budget", + relation="reader", + user="user:bob", + ), + ) + api_response = await api_instance.read(body=body) + self.assertIsInstance(api_response, ReadResponse) + key = TupleKey( + user="user:bob", + relation="reader", + object="document:2021-budget", + ) + timestamp = datetime.fromisoformat("2021-10-06T15:32:11.128+00:00") + expected_data = ReadResponse( + tuples=[Tuple(key=key, timestamp=timestamp)], + continuation_token="token123", + ) + self.assertEqual(api_response, expected_data) + mock_request.assert_called_once_with( + "POST", + "http://api.fga.example/stores/01H0H015178Y2V4CX10C2KGHF4/read", + headers=ANY, + query_params=[], + post_params=[], + body={ + "tuple_key": { + "object": "document:2021-budget", + "relation": "reader", + "user": "user:bob", + } + }, + _preload_content=ANY, + _request_timeout=None, + ) + await api_client.close() + + @patch.object(rest.RESTClientObject, "request") + async def test_read_with_type_only_object(self, mock_request): + """Test case for read with type-only object + + Get tuples from the store that matches a query with type-only object + """ + response_body = """ + { + "tuples": [ + { + "key": { + "user": "user:bob", + "relation": "reader", + "object": "document:2021-budget" + }, + "timestamp": "2021-10-06T15:32:11.128Z" + } + ], + "continuation_token": "token123" +} + """ + mock_request.return_value = mock_response(response_body, 200) + configuration = self.configuration + configuration.store_id = store_id + async with openfga_sdk.ApiClient(configuration) as api_client: + api_instance = open_fga_api.OpenFgaApi(api_client) + body = ReadRequest( + tuple_key=ReadRequestTupleKey( + object="document:", + relation="reader", + user="user:bob", + ), + ) + api_response = await api_instance.read(body=body) + self.assertIsInstance(api_response, ReadResponse) + key = TupleKey( + user="user:bob", + relation="reader", + object="document:2021-budget", + ) + timestamp = datetime.fromisoformat("2021-10-06T15:32:11.128+00:00") + expected_data = ReadResponse( + tuples=[Tuple(key=key, timestamp=timestamp)], + continuation_token="token123", + ) + self.assertEqual(api_response, expected_data) + mock_request.assert_called_once_with( + "POST", + "http://api.fga.example/stores/01H0H015178Y2V4CX10C2KGHF4/read", + headers=ANY, + query_params=[], + post_params=[], + body={ + "tuple_key": { + "object": "document:", + "relation": "reader", + "user": "user:bob", + } + }, + _preload_content=ANY, + _request_timeout=None, + ) + await api_client.close() + if __name__ == "__main__": unittest.main() diff --git a/test/client/client_test.py b/test/client/client_test.py index 959accbf..fdbd27df 100644 --- a/test/client/client_test.py +++ b/test/client/client_test.py @@ -10,6 +10,7 @@ NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. """ +import json import uuid from datetime import datetime @@ -191,6 +192,68 @@ async def test_list_stores(self, mock_request): ) await api_client.close() + @patch.object(rest.RESTClientObject, "request") + async def test_list_stores_with_name(self, mock_request): + """Test list_stores with name option""" + continuation_token = uuid.uuid4().hex + + response_body = json.dumps( + { + "stores": [ + { + "id": "01H0K5JSRNKM3P8T1SVZ9RGX90", + "name": "test-store", + "created_at": "2022-07-25T21:15:37.524Z", + "updated_at": "2022-07-25T21:15:37.524Z", + "deleted_at": "2022-07-25T21:15:37.524Z", + } + ], + "continuation_token": continuation_token, + } + ) + + store = Store( + id="01H0K5JSRNKM3P8T1SVZ9RGX90", + name="test-store", + created_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + updated_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + deleted_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + ) + + mock_request.return_value = mock_response(response_body, 200) + configuration = self.configuration + + async with OpenFgaClient(configuration) as api_client: + api_response = await api_client.list_stores( + options={ + "name": "test-store", + "page_size": 1, + "continuation_token": continuation_token, + } + ) + + self.assertIsInstance(api_response, ListStoresResponse) + self.assertEqual(api_response.continuation_token, continuation_token) + self.assertEqual(len(api_response.stores), 1) + self.assertEqual(api_response.stores[0], store) + self.assertEqual(api_response.stores[0].name, "test-store") + self.assertEqual(api_response.stores[0].id, "01H0K5JSRNKM3P8T1SVZ9RGX90") + + mock_request.assert_called_once_with( + "GET", + "http://api.fga.example/stores", + headers=ANY, + body=ANY, + query_params=[ + ("page_size", 1), + ("continuation_token", continuation_token), + ("name", "test-store"), + ], + post_params=ANY, + _preload_content=ANY, + _request_timeout=ANY, + ) + @patch.object(rest.RESTClientObject, "request") async def test_create_store(self, mock_request): """Test case for create_store diff --git a/test/sync/client/client_test.py b/test/sync/client/client_test.py index 124fbdbc..a4985f0d 100644 --- a/test/sync/client/client_test.py +++ b/test/sync/client/client_test.py @@ -10,6 +10,7 @@ NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. """ +import json import uuid from datetime import datetime @@ -190,6 +191,68 @@ def test_list_stores(self, mock_request): ) api_client.close() + @patch.object(rest.RESTClientObject, "request") + def test_list_stores_with_name(self, mock_request): + """Test list_stores with name option""" + continuation_token = uuid.uuid4().hex + + response_body = json.dumps( + { + "stores": [ + { + "id": "01H0K5JSRNKM3P8T1SVZ9RGX90", + "name": "test-store", + "created_at": "2022-07-25T21:15:37.524Z", + "updated_at": "2022-07-25T21:15:37.524Z", + "deleted_at": "2022-07-25T21:15:37.524Z", + } + ], + "continuation_token": continuation_token, + } + ) + + store = Store( + id="01H0K5JSRNKM3P8T1SVZ9RGX90", + name="test-store", + created_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + updated_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + deleted_at=datetime.fromisoformat("2022-07-25T21:15:37.524+00:00"), + ) + + mock_request.return_value = mock_response(response_body, 200) + configuration = self.configuration + + with OpenFgaClient(configuration) as api_client: + api_response = api_client.list_stores( + options={ + "name": "test-store", + "page_size": 1, + "continuation_token": continuation_token, + } + ) + + self.assertIsInstance(api_response, ListStoresResponse) + self.assertEqual(api_response.continuation_token, continuation_token) + self.assertEqual(len(api_response.stores), 1) + self.assertEqual(api_response.stores[0], store) + self.assertEqual(api_response.stores[0].name, "test-store") + self.assertEqual(api_response.stores[0].id, "01H0K5JSRNKM3P8T1SVZ9RGX90") + + mock_request.assert_called_once_with( + "GET", + "http://api.fga.example/stores", + headers=ANY, + body=ANY, + query_params=[ + ("page_size", 1), + ("continuation_token", continuation_token), + ("name", "test-store"), + ], + post_params=ANY, + _preload_content=ANY, + _request_timeout=ANY, + ) + @patch.object(rest.RESTClientObject, "request") def test_create_store(self, mock_request): """Test case for create_store