Skip to content

Commit

Permalink
feat: add streamed-list-objects endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
evansims committed Jan 16, 2025
1 parent f503532 commit 12579f3
Show file tree
Hide file tree
Showing 38 changed files with 1,594 additions and 495 deletions.
12 changes: 12 additions & 0 deletions .openapi-generator/FILES
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ docs/RelationshipCondition.md
docs/SourceInfo.md
docs/Status.md
docs/Store.md
docs/StreamResultOfStreamedListObjectsResponse.md
docs/StreamedListObjectsResponse.md
docs/Tuple.md
docs/TupleChange.md
docs/TupleKey.md
Expand Down Expand Up @@ -114,6 +116,14 @@ example/opentelemetry/main.py
example/opentelemetry/requirements.txt
example/opentelemetry/setup.cfg
example/opentelemetry/setup.py
example/streamed-list-objects/.env.example
example/streamed-list-objects/.gitignore
example/streamed-list-objects/README.md
example/streamed-list-objects/asynchronous.py
example/streamed-list-objects/requirements.txt
example/streamed-list-objects/setup.cfg
example/streamed-list-objects/setup.py
example/streamed-list-objects/synchronous.py
openfga_sdk/__init__.py
openfga_sdk/api/__init__.py
openfga_sdk/api/open_fga_api.py
Expand Down Expand Up @@ -203,6 +213,8 @@ openfga_sdk/models/relationship_condition.py
openfga_sdk/models/source_info.py
openfga_sdk/models/status.py
openfga_sdk/models/store.py
openfga_sdk/models/stream_result_of_streamed_list_objects_response.py
openfga_sdk/models/streamed_list_objects_response.py
openfga_sdk/models/tuple.py
openfga_sdk/models/tuple_change.py
openfga_sdk/models/tuple_key.py
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -1066,6 +1066,7 @@ Class | Method | HTTP request | Description
*OpenFgaApi* | [**read_authorization_model**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#read_authorization_model) | **GET** /stores/{store_id}/authorization-models/{id} | Return a particular version of an authorization model
*OpenFgaApi* | [**read_authorization_models**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#read_authorization_models) | **GET** /stores/{store_id}/authorization-models | Return all the authorization models for a particular store
*OpenFgaApi* | [**read_changes**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#read_changes) | **GET** /stores/{store_id}/changes | Return a list of all the tuple changes
*OpenFgaApi* | [**streamed_list_objects**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#streamed_list_objects) | **POST** /stores/{store_id}/streamed-list-objects | Stream all objects of the given type that the user has a relation with
*OpenFgaApi* | [**write**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#write) | **POST** /stores/{store_id}/write | Add or delete tuples from the store
*OpenFgaApi* | [**write_assertions**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#write_assertions) | **PUT** /stores/{store_id}/assertions/{authorization_model_id} | Upsert assertions for an authorization model ID
*OpenFgaApi* | [**write_authorization_model**](https://github.com/openfga/python-sdk/blob/main/docs/OpenFgaApi.md#write_authorization_model) | **POST** /stores/{store_id}/authorization-models | Create a new authorization model
Expand Down Expand Up @@ -1134,6 +1135,8 @@ Class | Method | HTTP request | Description
- [SourceInfo](https://github.com/openfga/python-sdk/blob/main/docs/SourceInfo.md)
- [Status](https://github.com/openfga/python-sdk/blob/main/docs/Status.md)
- [Store](https://github.com/openfga/python-sdk/blob/main/docs/Store.md)
- [StreamResultOfStreamedListObjectsResponse](https://github.com/openfga/python-sdk/blob/main/docs/StreamResultOfStreamedListObjectsResponse.md)
- [StreamedListObjectsResponse](https://github.com/openfga/python-sdk/blob/main/docs/StreamedListObjectsResponse.md)
- [Tuple](https://github.com/openfga/python-sdk/blob/main/docs/Tuple.md)
- [TupleChange](https://github.com/openfga/python-sdk/blob/main/docs/TupleChange.md)
- [TupleKey](https://github.com/openfga/python-sdk/blob/main/docs/TupleKey.md)
Expand Down
1 change: 1 addition & 0 deletions docs/ExpandRequest.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Name | Type | Description | Notes
**tuple_key** | [**ExpandRequestTupleKey**](ExpandRequestTupleKey.md) | |
**authorization_model_id** | **str** | | [optional]
**consistency** | [**ConsistencyPreference**](ConsistencyPreference.md) | | [optional]
**contextual_tuples** | [**ContextualTupleKeys**](ContextualTupleKeys.md) | | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)

Expand Down
87 changes: 86 additions & 1 deletion docs/OpenFgaApi.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ Method | HTTP request | Description
[**read_authorization_model**](OpenFgaApi.md#read_authorization_model) | **GET** /stores/{store_id}/authorization-models/{id} | Return a particular version of an authorization model
[**read_authorization_models**](OpenFgaApi.md#read_authorization_models) | **GET** /stores/{store_id}/authorization-models | Return all the authorization models for a particular store
[**read_changes**](OpenFgaApi.md#read_changes) | **GET** /stores/{store_id}/changes | Return a list of all the tuple changes
[**streamed_list_objects**](OpenFgaApi.md#streamed_list_objects) | **POST** /stores/{store_id}/streamed-list-objects | Stream all objects of the given type that the user has a relation with
[**write**](OpenFgaApi.md#write) | **POST** /stores/{store_id}/write | Add or delete tuples from the store
[**write_assertions**](OpenFgaApi.md#write_assertions) | **PUT** /stores/{store_id}/assertions/{authorization_model_id} | Upsert assertions for an authorization model ID
[**write_authorization_model**](OpenFgaApi.md#write_authorization_model) | **POST** /stores/{store_id}/authorization-models | Create a new authorization model
Expand Down Expand Up @@ -359,7 +360,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

Expand Down Expand Up @@ -1199,6 +1200,90 @@ No authorization required

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **streamed_list_objects**
> StreamResultOfStreamedListObjectsResponse streamed_list_objects(body)
Stream all objects of the given type that the user has a relation with

The Streamed ListObjects API is very similar to the the ListObjects API, with two differences: 1. Instead of collecting all objects before returning a response, it streams them to the client as they are collected. 2. The number of results returned is only limited by the execution timeout specified in the flag OPENFGA_LIST_OBJECTS_DEADLINE.

### Example

```python
import time
import openfga_sdk
from openfga_sdk.rest import ApiException
from pprint import pprint
# To configure the configuration
# host is mandatory
# api_scheme is optional and default to https
# store_id is mandatory
# See configuration.py for a list of all supported configuration parameters.
configuration = openfga_sdk.Configuration(
scheme = "https",
api_host = "api.fga.example",
store_id = 'YOUR_STORE_ID',
)


# When authenticating via the API TOKEN method
credentials = Credentials(method='api_token', configuration=CredentialConfiguration(api_token='TOKEN1'))
configuration = openfga_sdk.Configuration(
scheme = "https",
api_host = "api.fga.example",
store_id = 'YOUR_STORE_ID',
credentials = credentials
)

# Enter a context with an instance of the API client
async with openfga_sdk.ApiClient(configuration) as api_client:
# Create an instance of the API class
api_instance = openfga_sdk.OpenFgaApi(api_client)
body = openfga_sdk.ListObjectsRequest() # ListObjectsRequest |

try:
# Stream all objects of the given type that the user has a relation with
api_response = await api_instance.api_instance.streamed_list_objects(body)
pprint(api_response)
except ApiException as e:
print("Exception when calling OpenFgaApi->streamed_list_objects: %s\n" % e)
await api_client.close()
```


### Parameters

Name | Type | Description | Notes
------------- | ------------- | ------------- | -------------
**body** | [**ListObjectsRequest**](ListObjectsRequest.md)| |

### Return type

[**StreamResultOfStreamedListObjectsResponse**](StreamResultOfStreamedListObjectsResponse.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.(streaming responses) | - |
**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. | - |

[[Back to top]](#) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to Model list]](../README.md#documentation-for-models) [[Back to README]](../README.md)

# **write**
> object write(body)
Expand Down
12 changes: 12 additions & 0 deletions docs/StreamResultOfStreamedListObjectsResponse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# StreamResultOfStreamedListObjectsResponse


## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**result** | [**StreamedListObjectsResponse**](StreamedListObjectsResponse.md) | | [optional]
**error** | [**Status**](Status.md) | | [optional]

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


12 changes: 12 additions & 0 deletions docs/StreamedListObjectsResponse.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# StreamedListObjectsResponse

The response for a StreamedListObjects RPC.

## Properties
Name | Type | Description | Notes
------------ | ------------- | ------------- | -------------
**object** | **str** | |

[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)


8 changes: 8 additions & 0 deletions example/example1/example1.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import asyncio
import os
import sys
import uuid

from dotenv import load_dotenv

sdk_path = os.path.realpath(os.path.join(os.path.abspath(__file__), "..", "..", ".."))
sys.path.insert(0, sdk_path)

from openfga_sdk import (
ClientConfiguration,
Condition,
Expand Down Expand Up @@ -38,6 +44,8 @@


async def main():
load_dotenv()

credentials = Credentials()
if os.getenv("FGA_CLIENT_ID") is not None:
credentials = Credentials(
Expand Down
1 change: 1 addition & 0 deletions example/example1/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ openfga-sdk >= 0.9.0
python-dateutil >= 2.8.2
urllib3 >= 2.1.0
yarl >= 1.9.4
python-dotenv >= 1, <2
1 change: 1 addition & 0 deletions example/streamed-list-objects/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
FGA_API_URL="http://localhost:8080"
1 change: 1 addition & 0 deletions example/streamed-list-objects/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.env
39 changes: 39 additions & 0 deletions example/streamed-list-objects/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Streamed List Objects example for OpenFGA's Python SDK

This example demonstrates working with the `POST` `/stores/:id/streamed-list-objects` endpoint in OpenFGA using the Python SDK.

## Prerequisites

If you do not already have an OpenFGA instance running, you can start one using the following command:

```bash
docker run -d -p 8080:8080 openfga/openfga
```

## Configure the example

You may need to configure the example for your environment:

```bash
cp .env.example .env
```

Now edit the `.env` file and set the values as appropriate.

## Running the example

Begin by installing the required dependencies:

```bash
pip install -r requirements.txt
```

Next, run the example. You can use either the synchronous or asynchronous client:

```bash
python asynchronous.py
```

```bash
python synchronous.py
```
Loading

0 comments on commit 12579f3

Please sign in to comment.