Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions config/clients/python/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@
"destinationFilename": "openfga_sdk/client/models/list_relations_request.py",
"templateType": "SupportingFiles"
},
"client/models/list_users_request.mustache": {
"destinationFilename": "openfga_sdk/client/models/list_users_request.py",
"templateType": "SupportingFiles"
},
"client/models/read_changes_request.mustache": {
"destinationFilename": "openfga_sdk/client/models/read_changes_request.py",
"templateType": "SupportingFiles"
Expand Down
53 changes: 50 additions & 3 deletions config/clients/python/template/README_calling_api.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -603,8 +603,7 @@ response = await fga_client.expand(body. options)
# response = ExpandResponse({"tree": UsersetTree({"root": Node({"name": "document:roadmap#viewer", "leaf": Leaf({"users": Users({"users": ["user:81684243-9356-4421-8fbf-a4f8d36aa31b", "user:f52a4f7a-054d-47ff-bb6e-3ac81269988f"]})})})})})
```


##### List Objects
#### List Objects

List the objects of a particular type a user has access to.

Expand Down Expand Up @@ -641,7 +640,7 @@ response = await fga_client.list_objects(body)
# response.objects = ["document:roadmap"]
```

##### List Relations
#### List Relations

List the relations a user has on an object.

Expand Down Expand Up @@ -677,6 +676,54 @@ response = await fga_client.list_relations(body, options)
# response.relations = ["can_view", "can_edit"]
```

#### List Users

List the users who have a certain relation to a particular type.

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

```python
from openfga_sdk import OpenFgaClient
from openfga_sdk.client.models import ClientListUsersRequest, ClientTuple

configuration = ClientConfiguration(
api_url=FGA_API_URL,
# ...
)

async with OpenFgaClient(configuration) as api_client:
options = {
"authorization_model_id": "01GXSA8YR785C4FYS3C0RTG7B1"
}

request = ClientListUsersRequest(
object="document:2021-budget",
relation="can_read",
user_filters=[
UserTypeFilter(type="user"),
UserTypeFilter(type="team", relation="member"),
],
context={},
contextual_tuples=[
ClientTuple(
user="user:81684243-9356-4421-8fbf-a4f8d36aa31b",
relation="editor",
object="folder:product",
),
ClientTuple(
user="folder:product",
relation="parent",
object="document:roadmap",
),
],
)

response = await api_client.list_users(request, options)

# response.users = [{object: {type: "user", id: "81684243-9356-4421-8fbf-a4f8d36aa31b"}}, {userset: { type: "user" }}, ...]
# response.excluded_users = [ {object: {type: "user", id: "4a455e27-d15a-4434-82e0-136f9c2aa4cf"}}, ... ]
```

#### Assertions

##### Read Assertions
Expand Down
127 changes: 126 additions & 1 deletion config/clients/python/template/api_test.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ from {{packageName}}.models.leaf import Leaf
from {{packageName}}.models.list_objects_request import ListObjectsRequest
from {{packageName}}.models.list_objects_response import ListObjectsResponse
from {{packageName}}.models.list_stores_response import ListStoresResponse
from {{packageName}}.models.list_users_request import ListUsersRequest
from {{packageName}}.models.list_users_response import ListUsersResponse
from {{packageName}}.models.node import Node
from {{packageName}}.models.not_found_error_code import NotFoundErrorCode
from {{packageName}}.models.object_relation import ObjectRelation
Expand Down Expand Up @@ -377,8 +379,131 @@ class {{#operations}}Test{{classname}}(IsolatedAsyncioTestCase):
)
{{#asyncio}}await {{/asyncio}}api_client.close()


@patch.object(rest.RESTClientObject, "request")
{{#asyncio}}async {{/asyncio}}def test_list_users(self, mock_request):
"""
Test case for list_users
"""

response_body = """{
"excluded_users": [],
"users": [
{
"object": {
"id": "81684243-9356-4421-8fbf-a4f8d36aa31b",
"type": "user"
}
},
{
"userset": {
"id": "fga",
"relation": "member",
"type": "team"
}
},
{
"wildcard": {
"type": "user"
}
}
]
}"""

mock_request.return_value = mock_response(response_body, 200)

configuration = self.configuration
configuration.store_id = store_id

{{#asyncio}}async {{/asyncio}}with openfga_sdk.ApiClient(configuration) as api_client:
api_instance = open_fga_api.OpenFgaApi(api_client)

request = ListUsersRequest(
authorization_model_id="01G5JAVJ41T49E9TT3SKVS7X1J",
object="document:2021-budget",
relation="can_read",
user_filters=[
{"type": "user"},
{"type": "team", "relation": "member"},
],
context={},
contextual_tuples=[
{
"user": "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
"relation": "editor",
"object": "folder:product",
},
{
"user": "folder:product",
"relation": "parent",
"object": "document:roadmap",
},
],
)

response = {{#asyncio}}await {{/asyncio}}api_instance.list_users(request)

self.assertIsInstance(response, ListUsersResponse)

self.assertEqual(response.users.__len__(), 3)

self.assertIsNotNone(response.users[0].object)
self.assertEqual(
response.users[0].object.id, "81684243-9356-4421-8fbf-a4f8d36aa31b"
)
self.assertEqual(response.users[0].object.type, "user")
self.assertIsNone(response.users[0].userset)
self.assertIsNone(response.users[0].wildcard)

self.assertIsNone(response.users[1].object)
self.assertIsNotNone(response.users[1].userset)
self.assertEqual(response.users[1].userset.id, "fga")
self.assertEqual(response.users[1].userset.relation, "member")
self.assertEqual(response.users[1].userset.type, "team")
self.assertIsNone(response.users[1].wildcard)

self.assertIsNone(response.users[2].object)
self.assertIsNone(response.users[2].userset)
self.assertIsNotNone(response.users[2].wildcard)
self.assertEqual(response.users[2].wildcard.type, "user")

mock_request.assert_called_once_with(
"POST",
"http://api.{{sampleApiDomain}}/stores/01H0H015178Y2V4CX10C2KGHF4/list-users",
headers=ANY,
query_params=[],
post_params=[],
body={
"authorization_model_id": "01G5JAVJ41T49E9TT3SKVS7X1J",
"object": "document:2021-budget",
"relation": "can_read",
"user_filters": [
{"type": "user"},
{"type": "team", "relation": "member"},
],
"context": {},
"contextual_tuples": [
{
"user": "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
"relation": "editor",
"object": "folder:product",
},
{
"user": "folder:product",
"relation": "parent",
"object": "document:roadmap",
},
],
},
_preload_content=ANY,
_request_timeout=None,
)

await api_client.close()


@patch.object(rest.RESTClientObject, 'request')
async def test_read(self, mock_request):
{{#asyncio}}async {{/asyncio}}def test_read(self, mock_request):
"""Test case for read

Get tuples from the store that matches a query, without following userset rewrite rules
Expand Down
130 changes: 130 additions & 0 deletions config/clients/python/template/api_test_sync.mustache
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ from {{packageName}}.models.leaf import Leaf
from {{packageName}}.models.list_objects_request import ListObjectsRequest
from {{packageName}}.models.list_objects_response import ListObjectsResponse
from {{packageName}}.models.list_stores_response import ListStoresResponse
from {{packageName}}.models.list_users_request import ListUsersRequest
from {{packageName}}.models.list_users_response import ListUsersResponse
from {{packageName}}.models.node import Node
from {{packageName}}.models.not_found_error_code import NotFoundErrorCode
from {{packageName}}.models.object_relation import ObjectRelation
Expand Down Expand Up @@ -378,6 +380,134 @@ class TestOpenFgaApiSync(IsolatedAsyncioTestCase):
)
api_client.close()




@patch.object(rest.RESTClientObject, "request")
async def test_list_users(self, mock_request):
"""
Test case for list_users
"""

response_body = """{
"excluded_users": [],
"users": [
{
"object": {
"id": "81684243-9356-4421-8fbf-a4f8d36aa31b",
"type": "user"
}
},
{
"userset": {
"id": "fga",
"relation": "member",
"type": "team"
}
},
{
"wildcard": {
"type": "user"
}
}
]
}"""

mock_request.return_value = mock_response(response_body, 200)

configuration = self.configuration
configuration.store_id = store_id

with ApiClient(configuration) as api_client:
api_instance = open_fga_api.OpenFgaApi(api_client)

request = ListUsersRequest(
object="test:123",
relation="can_test",
user_filters=[{"type": "test", "relation": "test"}],
)
request.authorization_model_id = "01G5JAVJ41T49E9TT3SKVS7X1J"
request.object = "document:2021-budget"
request.relation = "can_read"
request.user_filters = [
{"type": "user"},
{"type": "team", "relation": "member"},
]
request.context = {}
request.contextual_tuples = [
{
"user": "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
"relation": "editor",
"object": "folder:product",
},
{
"user": "folder:product",
"relation": "parent",
"object": "document:roadmap",
},
]

response = api_instance.list_users(request)

self.assertIsInstance(response, ListUsersResponse)

self.assertEqual(response.users.__len__(), 3)

self.assertIsNotNone(response.users[0].object)
self.assertEqual(
response.users[0].object.id, "81684243-9356-4421-8fbf-a4f8d36aa31b"
)
self.assertEqual(response.users[0].object.type, "user")
self.assertIsNone(response.users[0].userset)
self.assertIsNone(response.users[0].wildcard)

self.assertIsNone(response.users[1].object)
self.assertIsNotNone(response.users[1].userset)
self.assertEqual(response.users[1].userset.id, "fga")
self.assertEqual(response.users[1].userset.relation, "member")
self.assertEqual(response.users[1].userset.type, "team")
self.assertIsNone(response.users[1].wildcard)

self.assertIsNone(response.users[2].object)
self.assertIsNone(response.users[2].userset)
self.assertIsNotNone(response.users[2].wildcard)
self.assertEqual(response.users[2].wildcard.type, "user")

mock_request.assert_called_once_with(
"POST",
"http://api.{{sampleApiDomain}}/stores/01H0H015178Y2V4CX10C2KGHF4/list-users",
headers=ANY,
query_params=[],
post_params=[],
body={
"authorization_model_id": "01G5JAVJ41T49E9TT3SKVS7X1J",
"object": "document:2021-budget",
"relation": "can_read",
"user_filters": [
{"type": "user"},
{"type": "team", "relation": "member"},
],
"context": {},
"contextual_tuples": [
{
"user": "user:81684243-9356-4421-8fbf-a4f8d36aa31b",
"relation": "editor",
"object": "folder:product",
},
{
"user": "folder:product",
"relation": "parent",
"object": "document:roadmap",
},
],
},
_preload_content=ANY,
_request_timeout=None,
)

api_client.close()


@patch.object(rest.RESTClientObject, 'request')
async def test_read(self, mock_request):
"""Test case for read
Expand Down
Loading