Skip to content

Commit 88f5d37

Browse files
authored
release(v0.3.3): misc fixes (#55)
2 parents 3f96a31 + 1b3d9b0 commit 88f5d37

26 files changed

Lines changed: 476 additions & 20 deletions

.github/ISSUE_TEMPLATE/bug_report.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@ By submitting an issue to this repository, you agree to the terms within the [Op
1919
2020
### Version of SDK
2121

22-
2322
```
2423
$ python -m openfga_sdk.help
2524
<paste here>
2625
```
2726

28-
This command is only available on openfga_sdk v0.3.2 and greater. Otherwise, please provide some basic information about your system.
27+
This command is only available on openfga_sdk `v0.3.2` and greater. Otherwise, please provide some basic information about your system.
2928

3029
### Version of OpenFGA (if known)
3130

.openapi-generator/FILES

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ docs/WriteAssertionsRequest.md
6868
docs/WriteAuthorizationModelRequest.md
6969
docs/WriteAuthorizationModelResponse.md
7070
docs/WriteRequest.md
71+
example/Makefile
72+
example/README.md
73+
example/example1/auth-model.json
74+
example/example1/example1.py
75+
example/example1/requirements.txt
76+
example/example1/setup.cfg
77+
example/example1/setup.py
7178
openfga_sdk/__init__.py
7279
openfga_sdk/api/__init__.py
7380
openfga_sdk/api/open_fga_api.py

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# Changelog
22

3+
## v0.3.3
4+
5+
### [0.3.3](https://github.com/openfga/python-sdk/compare/v0.3.2...v0.3.3) (2024-01-02)
6+
- fix: correct type hints for list_relations
7+
- fix: handle empty TupleKey in read
8+
- chore: add example project
9+
310
## v0.3.2
411

512
### [0.3.2](https://github.com/openfga/python-sdk/compare/v0.3.1...v0.3.2) (2023-12-15)

VERSION.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.3.2
1+
0.3.3

example/Makefile

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
all: run
2+
3+
project_name=example1
4+
openfga_version=latest
5+
6+
run:
7+
python example1/example1.py
8+
9+
run-openfga:
10+
docker pull docker.io/openfga/openfga:${openfga_version} && \
11+
docker run -p 8080:8080 docker.io/openfga/openfga:${openfga_version} run

example/README.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
## Examples of using the OpenFGA Python SDK
2+
3+
A set of examples on how to call the OpenFGA Python SDK
4+
5+
### Examples
6+
Example 1:
7+
A bare-bones example. It creates a store, and runs a set of calls against it including creating a model, writing tuples and checking for access.
8+
9+
### Running the Examples
10+
11+
Prerequisites:
12+
- `docker`
13+
- `make`
14+
- `python` 3.11+
15+
16+
#### Run using a published SDK
17+
18+
Steps:
19+
1. Clone/Copy the example folder
20+
2. If you have an OpenFGA server running, you can use it, otherwise run `make run-openfga` to spin up an instance (you'll need to switch to a different terminal after - don't forget to close it when done)
21+
3. Run `make run` to run the example
22+
23+
#### Run using a local unpublished SDK build
24+
25+
Steps:
26+
1. Build the SDK
27+
2. Change to the example directory
28+
3. Install the local SDK `pip install -e $LOCAL_DIR`
29+
- For example, if your local SDK is located at `$HOME/projects/python-sdk`, run `pip install -e $HOME/projects/python-sdk`
30+
- For advanced users: The above just updates `requirements.txt` and the source of openfga-sdk. If you know how to manually edit this, feel free to do that as well.
31+
4. If you have an OpenFGA server running, you can use it, otherwise run `make run-openfga` to spin up an instance (you'll need to switch to a different terminal after - don't forget to close it when done)
32+
5. Run `make run` to run the example

example/example1/auth-model.json

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
{
2+
"schema_version": "1.1",
3+
"type_definitions": [
4+
{
5+
"type": "user"
6+
},
7+
{
8+
"type": "document",
9+
"relations": {
10+
"reader": {
11+
"this": {}
12+
},
13+
"writer": {
14+
"this": {}
15+
},
16+
"owner": {
17+
"this": {}
18+
}
19+
},
20+
"metadata": {
21+
"relations": {
22+
"reader": {
23+
"directly_related_user_types": [
24+
{
25+
"type": "user"
26+
}
27+
]
28+
},
29+
"writer": {
30+
"directly_related_user_types": [
31+
{
32+
"type": "user"
33+
}
34+
]
35+
},
36+
"owner": {
37+
"directly_related_user_types": [
38+
{
39+
"type": "user"
40+
}
41+
]
42+
},
43+
"conditional_reader": {
44+
"directly_related_user_types": [
45+
{
46+
"condition": "name_starts_with_a",
47+
"type": "user"
48+
}
49+
]
50+
}
51+
}
52+
}
53+
}
54+
],
55+
"conditions": {
56+
"ViewCountLessThan200": {
57+
"name": "ViewCountLessThan200",
58+
"expression": "ViewCount < 200",
59+
"parameters": {
60+
"ViewCount": {
61+
"type_name": "TYPE_NAME_INT"
62+
},
63+
"Type": {
64+
"type_name": "TYPE_NAME_STRING"
65+
},
66+
"Name": {
67+
"type_name": "TYPE_NAME_STRING"
68+
}
69+
}
70+
}
71+
}
72+
}
73+

example/example1/example1.py

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
import asyncio
2+
import json
3+
4+
import openfga_sdk
5+
from openfga_sdk.client.models import ClientAssertion, ClientCheckRequest, ClientReadChangesRequest, ClientTuple, ClientWriteRequest
6+
from openfga_sdk.models import CreateStoreRequest, Metadata, ObjectRelation, RelationMetadata, TupleKey, TypeDefinition, Userset, Usersets, WriteAuthorizationModelRequest
7+
from openfga_sdk import ClientConfiguration, OpenFgaClient
8+
from openfga_sdk.credentials import CredentialConfiguration, Credentials
9+
import os
10+
11+
12+
async def main():
13+
credentials = Credentials()
14+
if os.getenv("FGA_CLIENT_ID") is not None:
15+
credentials = Credentials(
16+
method='client_credentials',
17+
configuration=CredentialConfiguration(
18+
api_issuer=os.getenv('FGA_API_TOKEN_ISSUER'),
19+
api_audience=os.getenv('FGA_API_AUDIENCE'),
20+
client_id=os.getenv('FGA_CLIENT_ID'),
21+
client_secret=os.getenv('FGA_CLIENT_SECRET')
22+
)
23+
)
24+
25+
if os.getenv('FGA_API_HOST') is not None:
26+
configuration = ClientConfiguration(
27+
api_host=os.getenv('FGA_API_HOST'),
28+
credentials=credentials
29+
)
30+
else:
31+
configuration = ClientConfiguration(
32+
api_scheme='http',
33+
api_host='localhost:8080',
34+
credentials=credentials
35+
)
36+
37+
async with OpenFgaClient(configuration) as fga_client:
38+
# ListStores (before create)
39+
print('Listing Stores')
40+
response = await fga_client.list_stores()
41+
print(f"Stores Count: {len(response.stores)}")
42+
43+
store_name = 'Test Store'
44+
45+
# CreateStore (before create)
46+
print('Creating Test Store')
47+
body = CreateStoreRequest(name=store_name)
48+
response = await fga_client.create_store(body)
49+
print(f"Test Store ID: {response.id}")
50+
51+
# Set the store ID
52+
fga_client.set_store_id(response.id)
53+
54+
# ListStores (after create)
55+
print('Listing Stores')
56+
response = await fga_client.list_stores()
57+
print(f"Stores Count: {len(response.stores)}")
58+
59+
# GetStore (after create)
60+
print('Getting Current Store')
61+
response = await fga_client.get_store()
62+
print(f"Current Store Name: {response.name}")
63+
64+
# ReadAuthorizationModels (before write)
65+
print('Reading Authorization Models')
66+
response = await fga_client.read_authorization_models()
67+
print(f"Models Count: {len(response.authorization_models)}")
68+
69+
# ReadLatestAuthorizationModel (before write)
70+
try:
71+
response = await fga_client.read_latest_authorization_model()
72+
if response.authorization_model is not None:
73+
print(f"Latest Authorization Model ID: {response.authorization_model.id}")
74+
except:
75+
print('Latest Authorization Model not found')
76+
77+
# WriteAuthorizationModel
78+
print('Writing an Authorization Model')
79+
with open(os.path.join(os.path.dirname(__file__), 'auth-model.json')) as f:
80+
auth_model_request = json.load(f)
81+
response = await fga_client.write_authorization_model(auth_model_request)
82+
print(f"Authorization Model ID: {response.authorization_model_id}")
83+
84+
# ReadAuthorizationModels (after write)
85+
print('Reading Authorization Models')
86+
response = await fga_client.read_authorization_models()
87+
print(f"Models Count: {len(response.authorization_models)}")
88+
89+
# ReadLatestAuthorizationModel (after write)
90+
response = await fga_client.read_latest_authorization_model()
91+
if response.authorization_model is not None:
92+
print(f"Latest Authorization Model ID: {response.authorization_model.id}")
93+
94+
auth_model_id = response.authorization_model.id
95+
96+
# Write
97+
print('Writing Tuples')
98+
body = ClientWriteRequest(
99+
writes=[
100+
ClientTuple(
101+
user='user:anne',
102+
relation='writer',
103+
object='document:roadmap',
104+
# condition=RelationshipCondition(
105+
# name='ViewCountLessThan200',
106+
# context=dict(
107+
# Name='Roadmap',
108+
# Type='Document',
109+
# ),
110+
# ),
111+
),
112+
],
113+
)
114+
options = {
115+
# You can rely on the model id set in the configuration or override it for this specific request
116+
"authorization_model_id": auth_model_id
117+
}
118+
await fga_client.write(body, options)
119+
print('Done Writing Tuples')
120+
121+
# Set the model ID
122+
fga_client.set_authorization_model_id(auth_model_id)
123+
124+
# Read
125+
print('Reading Tuples')
126+
response = await fga_client.read(TupleKey(user='user:anne', object='document:'))
127+
print(f"Read Tuples: {response.tuples}")
128+
129+
# ReadChanges
130+
print('Reading Tuple Changes')
131+
body = ClientReadChangesRequest('document')
132+
response = await fga_client.read_changes(body)
133+
print(f"Read Changes Tuples: {response.changes}")
134+
135+
# Check
136+
print('Checking for access')
137+
response = await fga_client.check(ClientCheckRequest(
138+
user='user:anne',
139+
relation='reader',
140+
object='document:roadmap',
141+
))
142+
print(f"Allowed: {response.allowed}")
143+
144+
# Checking for access with context
145+
# TODO
146+
147+
# WriteAssertions
148+
await fga_client.write_assertions([
149+
ClientAssertion(
150+
user='user:carl',
151+
relation='writer',
152+
object='document:budget',
153+
expectation=True,
154+
),
155+
ClientAssertion(
156+
user='user:anne',
157+
relation='reader',
158+
object='document:roadmap',
159+
expectation=False,
160+
),
161+
])
162+
print('Assertions updated')
163+
164+
# ReadAssertions
165+
print('Reading Assertions')
166+
response = await fga_client.read_assertions()
167+
print(f"Assertions: {response.assertions}")
168+
169+
# DeleteStore
170+
print('Deleting Current Store')
171+
await fga_client.delete_store()
172+
print(f"Deleted Store: {store_name}")
173+
174+
175+
asyncio.run(main())

example/example1/requirements.txt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
aiohttp==3.9.1
2+
aiosignal==1.3.1
3+
attrs==23.1.0
4+
frozenlist==1.4.1
5+
idna==3.6
6+
multidict==6.0.4
7+
openfga-sdk==0.3.2
8+
python-dateutil==2.8.2
9+
six==1.16.0
10+
urllib3==2.1.0
11+
yarl==1.9.4

example/example1/setup.cfg

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[flake8]
2+
max-line-length=99

0 commit comments

Comments
 (0)