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 de41904
Show file tree
Hide file tree
Showing 41 changed files with 2,905 additions and 626 deletions.
14 changes: 14 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 Expand Up @@ -255,6 +267,7 @@ test-requirements.txt
test/_/configuration_test.py
test/_/credentials_test.py
test/_/oauth2_test.py
test/_/rest_test.py
test/_/validation_test.py
test/__init__.py
test/api/__init__.py
Expand All @@ -265,6 +278,7 @@ test/sync/client/__init__.py
test/sync/client/client_test.py
test/sync/oauth2_test.py
test/sync/open_fga_api_test.py
test/sync/rest_test.py
test/telemetry/attributes_test.py
test/telemetry/configuration_test.py
test/telemetry/counters_test.py
Expand Down
33 changes: 32 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ This is an autogenerated python SDK for OpenFGA. It provides a wrapper around th
- [Batch Check](#batch-check)
- [Expand](#expand)
- [List Objects](#list-objects)
- [Streamed List Objects](#streamed-list-objects)
- [List Relations](#list-relations)
- [List Users](#list-users)
- [Assertions](#assertions)
Expand Down Expand Up @@ -887,6 +888,33 @@ response = await fga_client.list_objects(body)
# response.objects = ["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]
```

#### Streamed List Objects

List the objects of a particular type a user has access to, using the streaming API.

[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/StreamedListObjects)

```python
# from openfga_sdk import OpenFgaClient
# from openfga_sdk.client.models import ClientListObjectsRequest

# Initialize the fga_client
# fga_client = OpenFgaClient(configuration)

results = []

documents = ClientListObjectsRequest(
type="document",
relation="writer",
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
)

async for response in fga_client.streamed_list_objects(request):
results.append(response)

# results = ["document:...", ...]
```

#### List Relations

List the relations a user has on an object.
Expand Down Expand Up @@ -1066,6 +1094,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 +1163,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 Expand Up @@ -1177,7 +1208,7 @@ If you have found a bug or if you have a feature request, please report them on

### Pull Requests

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.
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

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 de41904

Please sign in to comment.