Skip to content

Commit cfed381

Browse files
committed
feat: add streamed-list-objects endpoint
1 parent 1d93d1b commit cfed381

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+2962
-623
lines changed

.openapi-generator/FILES

+14
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ docs/RelationshipCondition.md
7272
docs/SourceInfo.md
7373
docs/Status.md
7474
docs/Store.md
75+
docs/StreamResultOfStreamedListObjectsResponse.md
76+
docs/StreamedListObjectsResponse.md
7577
docs/Tuple.md
7678
docs/TupleChange.md
7779
docs/TupleKey.md
@@ -114,6 +116,14 @@ example/opentelemetry/main.py
114116
example/opentelemetry/requirements.txt
115117
example/opentelemetry/setup.cfg
116118
example/opentelemetry/setup.py
119+
example/streamed-list-objects/.env.example
120+
example/streamed-list-objects/.gitignore
121+
example/streamed-list-objects/README.md
122+
example/streamed-list-objects/asynchronous.py
123+
example/streamed-list-objects/requirements.txt
124+
example/streamed-list-objects/setup.cfg
125+
example/streamed-list-objects/setup.py
126+
example/streamed-list-objects/synchronous.py
117127
openfga_sdk/__init__.py
118128
openfga_sdk/api/__init__.py
119129
openfga_sdk/api/open_fga_api.py
@@ -203,6 +213,8 @@ openfga_sdk/models/relationship_condition.py
203213
openfga_sdk/models/source_info.py
204214
openfga_sdk/models/status.py
205215
openfga_sdk/models/store.py
216+
openfga_sdk/models/stream_result_of_streamed_list_objects_response.py
217+
openfga_sdk/models/streamed_list_objects_response.py
206218
openfga_sdk/models/tuple.py
207219
openfga_sdk/models/tuple_change.py
208220
openfga_sdk/models/tuple_key.py
@@ -255,6 +267,7 @@ test-requirements.txt
255267
test/_/configuration_test.py
256268
test/_/credentials_test.py
257269
test/_/oauth2_test.py
270+
test/_/rest_test.py
258271
test/_/validation_test.py
259272
test/__init__.py
260273
test/api/__init__.py
@@ -265,6 +278,7 @@ test/sync/client/__init__.py
265278
test/sync/client/client_test.py
266279
test/sync/oauth2_test.py
267280
test/sync/open_fga_api_test.py
281+
test/sync/rest_test.py
268282
test/telemetry/attributes_test.py
269283
test/telemetry/configuration_test.py
270284
test/telemetry/counters_test.py

README.md

+32-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ This is an autogenerated python SDK for OpenFGA. It provides a wrapper around th
3737
- [Batch Check](#batch-check)
3838
- [Expand](#expand)
3939
- [List Objects](#list-objects)
40+
- [Streamed List Objects](#streamed-list-objects)
4041
- [List Relations](#list-relations)
4142
- [List Users](#list-users)
4243
- [Assertions](#assertions)
@@ -985,6 +986,33 @@ response = await fga_client.list_objects(body)
985986
# response.objects = ["document:0192ab2a-d83f-756d-9397-c5ed9f3cb69a"]
986987
```
987988

989+
#### Streamed List Objects
990+
991+
List the objects of a particular type a user has access to, using the streaming API.
992+
993+
[API Documentation](https://openfga.dev/api/service#/Relationship%20Queries/StreamedListObjects)
994+
995+
```python
996+
# from openfga_sdk import OpenFgaClient
997+
# from openfga_sdk.client.models import ClientListObjectsRequest
998+
999+
# Initialize the fga_client
1000+
# fga_client = OpenFgaClient(configuration)
1001+
1002+
results = []
1003+
1004+
documents = ClientListObjectsRequest(
1005+
type="document",
1006+
relation="writer",
1007+
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
1008+
)
1009+
1010+
async for response in fga_client.streamed_list_objects(request):
1011+
results.append(response)
1012+
1013+
# results = ["document:...", ...]
1014+
```
1015+
9881016
#### List Relations
9891017

9901018
List the relations a user has on an object.
@@ -1164,6 +1192,7 @@ Class | Method | HTTP request | Description
11641192
*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
11651193
*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
11661194
*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
1195+
*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
11671196
*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
11681197
*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
11691198
*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
@@ -1232,6 +1261,8 @@ Class | Method | HTTP request | Description
12321261
- [SourceInfo](https://github.com/openfga/python-sdk/blob/main/docs/SourceInfo.md)
12331262
- [Status](https://github.com/openfga/python-sdk/blob/main/docs/Status.md)
12341263
- [Store](https://github.com/openfga/python-sdk/blob/main/docs/Store.md)
1264+
- [StreamResultOfStreamedListObjectsResponse](https://github.com/openfga/python-sdk/blob/main/docs/StreamResultOfStreamedListObjectsResponse.md)
1265+
- [StreamedListObjectsResponse](https://github.com/openfga/python-sdk/blob/main/docs/StreamedListObjectsResponse.md)
12351266
- [Tuple](https://github.com/openfga/python-sdk/blob/main/docs/Tuple.md)
12361267
- [TupleChange](https://github.com/openfga/python-sdk/blob/main/docs/TupleChange.md)
12371268
- [TupleKey](https://github.com/openfga/python-sdk/blob/main/docs/TupleKey.md)
@@ -1275,7 +1306,7 @@ If you have found a bug or if you have a feature request, please report them on
12751306

12761307
### Pull Requests
12771308

1278-
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.
1309+
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.
12791310

12801311
## Author
12811312

docs/ExpandRequest.md

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Name | Type | Description | Notes
77
**tuple_key** | [**ExpandRequestTupleKey**](ExpandRequestTupleKey.md) | |
88
**authorization_model_id** | **str** | | [optional]
99
**consistency** | [**ConsistencyPreference**](ConsistencyPreference.md) | | [optional]
10+
**contextual_tuples** | [**ContextualTupleKeys**](ContextualTupleKeys.md) | | [optional]
1011

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

docs/OpenFgaApi.md

+86-1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Method | HTTP request | Description
1818
[**read_authorization_model**](OpenFgaApi.md#read_authorization_model) | **GET** /stores/{store_id}/authorization-models/{id} | Return a particular version of an authorization model
1919
[**read_authorization_models**](OpenFgaApi.md#read_authorization_models) | **GET** /stores/{store_id}/authorization-models | Return all the authorization models for a particular store
2020
[**read_changes**](OpenFgaApi.md#read_changes) | **GET** /stores/{store_id}/changes | Return a list of all the tuple changes
21+
[**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
2122
[**write**](OpenFgaApi.md#write) | **POST** /stores/{store_id}/write | Add or delete tuples from the store
2223
[**write_assertions**](OpenFgaApi.md#write_assertions) | **PUT** /stores/{store_id}/assertions/{authorization_model_id} | Upsert assertions for an authorization model ID
2324
[**write_authorization_model**](OpenFgaApi.md#write_authorization_model) | **POST** /stores/{store_id}/authorization-models | Create a new authorization model
@@ -359,7 +360,7 @@ No authorization required
359360
360361
Expand all relationships in userset tree format, and following userset rewrite rules. Useful to reason about and debug a certain relationship
361362

362-
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`.
363+
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\" ] } } } } } ```
363364

364365
### Example
365366

@@ -1199,6 +1200,90 @@ No authorization required
11991200

12001201
[[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)
12011202

1203+
# **streamed_list_objects**
1204+
> StreamResultOfStreamedListObjectsResponse streamed_list_objects(body)
1205+
1206+
Stream all objects of the given type that the user has a relation with
1207+
1208+
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.
1209+
1210+
### Example
1211+
1212+
```python
1213+
import time
1214+
import openfga_sdk
1215+
from openfga_sdk.rest import ApiException
1216+
from pprint import pprint
1217+
# To configure the configuration
1218+
# host is mandatory
1219+
# api_scheme is optional and default to https
1220+
# store_id is mandatory
1221+
# See configuration.py for a list of all supported configuration parameters.
1222+
configuration = openfga_sdk.Configuration(
1223+
scheme = "https",
1224+
api_host = "api.fga.example",
1225+
store_id = 'YOUR_STORE_ID',
1226+
)
1227+
1228+
1229+
# When authenticating via the API TOKEN method
1230+
credentials = Credentials(method='api_token', configuration=CredentialConfiguration(api_token='TOKEN1'))
1231+
configuration = openfga_sdk.Configuration(
1232+
scheme = "https",
1233+
api_host = "api.fga.example",
1234+
store_id = 'YOUR_STORE_ID',
1235+
credentials = credentials
1236+
)
1237+
1238+
# Enter a context with an instance of the API client
1239+
async with openfga_sdk.ApiClient(configuration) as api_client:
1240+
# Create an instance of the API class
1241+
api_instance = openfga_sdk.OpenFgaApi(api_client)
1242+
body = openfga_sdk.ListObjectsRequest() # ListObjectsRequest |
1243+
1244+
try:
1245+
# Stream all objects of the given type that the user has a relation with
1246+
api_response = await api_instance.api_instance.streamed_list_objects(body)
1247+
pprint(api_response)
1248+
except ApiException as e:
1249+
print("Exception when calling OpenFgaApi->streamed_list_objects: %s\n" % e)
1250+
await api_client.close()
1251+
```
1252+
1253+
1254+
### Parameters
1255+
1256+
Name | Type | Description | Notes
1257+
------------- | ------------- | ------------- | -------------
1258+
**body** | [**ListObjectsRequest**](ListObjectsRequest.md)| |
1259+
1260+
### Return type
1261+
1262+
[**StreamResultOfStreamedListObjectsResponse**](StreamResultOfStreamedListObjectsResponse.md)
1263+
1264+
### Authorization
1265+
1266+
No authorization required
1267+
1268+
### HTTP request headers
1269+
1270+
- **Content-Type**: application/json
1271+
- **Accept**: application/json
1272+
1273+
### HTTP response details
1274+
| Status code | Description | Response headers |
1275+
|-------------|-------------|------------------|
1276+
**200** | A successful response.(streaming responses) | - |
1277+
**400** | Request failed due to invalid input. | - |
1278+
**401** | Not authenticated. | - |
1279+
**403** | Forbidden. | - |
1280+
**404** | Request failed due to incorrect path. | - |
1281+
**409** | Request was aborted due a transaction conflict. | - |
1282+
**422** | Request timed out due to excessive request throttling. | - |
1283+
**500** | Request failed due to internal server error. | - |
1284+
1285+
[[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)
1286+
12021287
# **write**
12031288
> object write(body)
12041289
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# StreamResultOfStreamedListObjectsResponse
2+
3+
4+
## Properties
5+
Name | Type | Description | Notes
6+
------------ | ------------- | ------------- | -------------
7+
**result** | [**StreamedListObjectsResponse**](StreamedListObjectsResponse.md) | | [optional]
8+
**error** | [**Status**](Status.md) | | [optional]
9+
10+
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
11+
12+

docs/StreamedListObjectsResponse.md

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# StreamedListObjectsResponse
2+
3+
The response for a StreamedListObjects RPC.
4+
5+
## Properties
6+
Name | Type | Description | Notes
7+
------------ | ------------- | ------------- | -------------
8+
**object** | **str** | |
9+
10+
[[Back to Model list]](../README.md#documentation-for-models) [[Back to API list]](../README.md#documentation-for-api-endpoints) [[Back to README]](../README.md)
11+
12+

example/example1/example1.py

+8
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
import asyncio
22
import os
3+
import sys
34
import uuid
45

6+
from dotenv import load_dotenv
7+
8+
sdk_path = os.path.realpath(os.path.join(os.path.abspath(__file__), "..", "..", ".."))
9+
sys.path.insert(0, sdk_path)
10+
511
from openfga_sdk import (
612
ClientConfiguration,
713
Condition,
@@ -38,6 +44,8 @@
3844

3945

4046
async def main():
47+
load_dotenv()
48+
4149
credentials = Credentials()
4250
if os.getenv("FGA_CLIENT_ID") is not None:
4351
credentials = Credentials(

example/example1/requirements.txt

+1
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ openfga-sdk >= 0.9.0
88
python-dateutil >= 2.8.2
99
urllib3 >= 2.1.0
1010
yarl >= 1.9.4
11+
python-dotenv >= 1, <2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
FGA_API_URL="http://localhost:8080"
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.env
+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Streamed List Objects example for OpenFGA's Python SDK
2+
3+
This example demonstrates working with the `POST` `/stores/:id/streamed-list-objects` endpoint in OpenFGA using the Python SDK.
4+
5+
## Prerequisites
6+
7+
If you do not already have an OpenFGA instance running, you can start one using the following command:
8+
9+
```bash
10+
docker run -d -p 8080:8080 openfga/openfga
11+
```
12+
13+
## Configure the example
14+
15+
You may need to configure the example for your environment:
16+
17+
```bash
18+
cp .env.example .env
19+
```
20+
21+
Now edit the `.env` file and set the values as appropriate.
22+
23+
## Running the example
24+
25+
Begin by installing the required dependencies:
26+
27+
```bash
28+
pip install -r requirements.txt
29+
```
30+
31+
Next, run the example. You can use either the synchronous or asynchronous client:
32+
33+
```bash
34+
python asynchronous.py
35+
```
36+
37+
```bash
38+
python synchronous.py
39+
```

0 commit comments

Comments
 (0)