-
Notifications
You must be signed in to change notification settings - Fork 45
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
chore(python-sdk): add example project
- Loading branch information
1 parent
65aeb0e
commit 010bb1e
Showing
8 changed files
with
365 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
all: run | ||
|
||
project_name=example1 | ||
openfga_version=latest | ||
|
||
run: | ||
python example1/example1.py | ||
|
||
run-openfga: | ||
docker pull docker.io/openfga/openfga:${openfga_version} && \ | ||
docker run -p 8080:8080 docker.io/openfga/openfga:${openfga_version} run |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
## Examples of using the OpenFGA Python SDK | ||
|
||
A set of examples on how to call the OpenFGA Python SDK | ||
|
||
### Examples | ||
Example 1: | ||
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. | ||
|
||
### Running the Examples | ||
|
||
Prerequisites: | ||
- `docker` | ||
- `make` | ||
- `python` 3.11+ | ||
|
||
#### Run using a published SDK | ||
|
||
Steps: | ||
1. Clone/Copy the example folder | ||
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) | ||
3. Run `make run` to run the example | ||
|
||
#### Run using a local unpublished SDK build | ||
|
||
Steps: | ||
1. Build the SDK | ||
2. Change to the example directory | ||
3. Install the local SDK `pip install -e $LOCAL_DIR` | ||
- For example, if your local SDK is located at `$HOME/projects/python-sdk`, run `pip install -e $HOME/projects/python-sdk` | ||
- 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. | ||
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) | ||
5. Run `make run` to run the example |
73 changes: 73 additions & 0 deletions
73
config/clients/python/template/example/example1/auth-model.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
{ | ||
"schema_version": "1.1", | ||
"type_definitions": [ | ||
{ | ||
"type": "user" | ||
}, | ||
{ | ||
"type": "document", | ||
"relations": { | ||
"reader": { | ||
"this": {} | ||
}, | ||
"writer": { | ||
"this": {} | ||
}, | ||
"owner": { | ||
"this": {} | ||
} | ||
}, | ||
"metadata": { | ||
"relations": { | ||
"reader": { | ||
"directly_related_user_types": [ | ||
{ | ||
"type": "user" | ||
} | ||
] | ||
}, | ||
"writer": { | ||
"directly_related_user_types": [ | ||
{ | ||
"type": "user" | ||
} | ||
] | ||
}, | ||
"owner": { | ||
"directly_related_user_types": [ | ||
{ | ||
"type": "user" | ||
} | ||
] | ||
}, | ||
"conditional_reader": { | ||
"directly_related_user_types": [ | ||
{ | ||
"condition": "name_starts_with_a", | ||
"type": "user" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} | ||
], | ||
"conditions": { | ||
"ViewCountLessThan200": { | ||
"name": "ViewCountLessThan200", | ||
"expression": "ViewCount < 200", | ||
"parameters": { | ||
"ViewCount": { | ||
"type_name": "TYPE_NAME_INT" | ||
}, | ||
"Type": { | ||
"type_name": "TYPE_NAME_STRING" | ||
}, | ||
"Name": { | ||
"type_name": "TYPE_NAME_STRING" | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
175 changes: 175 additions & 0 deletions
175
config/clients/python/template/example/example1/example1.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,175 @@ | ||
import asyncio | ||
import json | ||
|
||
import openfga_sdk | ||
from openfga_sdk.client.models import ClientAssertion, ClientCheckRequest, ClientReadChangesRequest, ClientTuple, ClientWriteRequest | ||
from openfga_sdk.models import CreateStoreRequest, Metadata, ObjectRelation, RelationMetadata, TupleKey, TypeDefinition, Userset, Usersets, WriteAuthorizationModelRequest | ||
from openfga_sdk import ClientConfiguration, OpenFgaClient | ||
from openfga_sdk.credentials import CredentialConfiguration, Credentials | ||
import os | ||
|
||
|
||
async def main(): | ||
credentials = Credentials() | ||
if os.getenv("FGA_CLIENT_ID") is not None: | ||
credentials = Credentials( | ||
method='client_credentials', | ||
configuration=CredentialConfiguration( | ||
api_issuer=os.getenv('FGA_API_TOKEN_ISSUER'), | ||
api_audience=os.getenv('FGA_API_AUDIENCE'), | ||
client_id=os.getenv('FGA_CLIENT_ID'), | ||
client_secret=os.getenv('FGA_CLIENT_SECRET') | ||
) | ||
) | ||
|
||
if os.getenv('FGA_API_HOST') is not None: | ||
configuration = ClientConfiguration( | ||
api_host=os.getenv('FGA_API_HOST'), | ||
credentials=credentials | ||
) | ||
else: | ||
configuration = ClientConfiguration( | ||
api_scheme='http', | ||
api_host='localhost:8080', | ||
credentials=credentials | ||
) | ||
|
||
async with OpenFgaClient(configuration) as fga_client: | ||
# ListStores (before create) | ||
print('Listing Stores') | ||
response = await fga_client.list_stores() | ||
print(f"Stores Count: {len(response.stores)}") | ||
|
||
store_name = 'Test Store' | ||
|
||
# CreateStore (before create) | ||
print('Creating Test Store') | ||
body = CreateStoreRequest(name=store_name) | ||
response = await fga_client.create_store(body) | ||
print(f"Test Store ID: {response.id}") | ||
|
||
# Set the store ID | ||
fga_client.set_store_id(response.id) | ||
|
||
# ListStores (after create) | ||
print('Listing Stores') | ||
response = await fga_client.list_stores() | ||
print(f"Stores Count: {len(response.stores)}") | ||
|
||
# GetStore (after create) | ||
print('Getting Current Store') | ||
response = await fga_client.get_store() | ||
print(f"Current Store Name: {response.name}") | ||
|
||
# ReadAuthorizationModels (before write) | ||
print('Reading Authorization Models') | ||
response = await fga_client.read_authorization_models() | ||
print(f"Models Count: {len(response.authorization_models)}") | ||
|
||
# ReadLatestAuthorizationModel (before write) | ||
try: | ||
response = await fga_client.read_latest_authorization_model() | ||
if response.authorization_model is not None: | ||
print(f"Latest Authorization Model ID: {response.authorization_model.id}") | ||
except: | ||
print('Latest Authorization Model not found') | ||
|
||
# WriteAuthorizationModel | ||
print('Writing an Authorization Model') | ||
with open(os.path.join(os.path.dirname(__file__), 'auth-model.json')) as f: | ||
auth_model_request = json.load(f) | ||
response = await fga_client.write_authorization_model(auth_model_request) | ||
print(f"Authorization Model ID: {response.authorization_model_id}") | ||
|
||
# ReadAuthorizationModels (after write) | ||
print('Reading Authorization Models') | ||
response = await fga_client.read_authorization_models() | ||
print(f"Models Count: {len(response.authorization_models)}") | ||
|
||
# ReadLatestAuthorizationModel (after write) | ||
response = await fga_client.read_latest_authorization_model() | ||
if response.authorization_model is not None: | ||
print(f"Latest Authorization Model ID: {response.authorization_model.id}") | ||
|
||
auth_model_id = response.authorization_model.id | ||
|
||
# Write | ||
print('Writing Tuples') | ||
body = ClientWriteRequest( | ||
writes=[ | ||
ClientTuple( | ||
user='user:anne', | ||
relation='writer', | ||
object='document:roadmap', | ||
# condition=RelationshipCondition( | ||
# name='ViewCountLessThan200', | ||
# context=dict( | ||
# Name='Roadmap', | ||
# Type='Document', | ||
# ), | ||
# ), | ||
), | ||
], | ||
) | ||
options = { | ||
# You can rely on the model id set in the configuration or override it for this specific request | ||
"authorization_model_id": auth_model_id | ||
} | ||
await fga_client.write(body, options) | ||
print('Done Writing Tuples') | ||
|
||
# Set the model ID | ||
fga_client.set_authorization_model_id(auth_model_id) | ||
|
||
# Read | ||
print('Reading Tuples') | ||
response = await fga_client.read(TupleKey(user='user:anne', object='document:')) | ||
print(f"Read Tuples: {response.tuples}") | ||
|
||
# ReadChanges | ||
print('Reading Tuple Changes') | ||
body = ClientReadChangesRequest('document') | ||
response = await fga_client.read_changes(body) | ||
print(f"Read Changes Tuples: {response.changes}") | ||
|
||
# Check | ||
print('Checking for access') | ||
response = await fga_client.check(ClientCheckRequest( | ||
user='user:anne', | ||
relation='reader', | ||
object='document:roadmap', | ||
)) | ||
print(f"Allowed: {response.allowed}") | ||
|
||
# Checking for access with context | ||
# TODO | ||
|
||
# WriteAssertions | ||
await fga_client.write_assertions([ | ||
ClientAssertion( | ||
user='user:carl', | ||
relation='writer', | ||
object='document:budget', | ||
expectation=True, | ||
), | ||
ClientAssertion( | ||
user='user:anne', | ||
relation='reader', | ||
object='document:roadmap', | ||
expectation=False, | ||
), | ||
]) | ||
print('Assertions updated') | ||
|
||
# ReadAssertions | ||
print('Reading Assertions') | ||
response = await fga_client.read_assertions() | ||
print(f"Assertions: {response.assertions}") | ||
|
||
# DeleteStore | ||
print('Deleting Current Store') | ||
await fga_client.delete_store() | ||
print(f"Deleted Store: {store_name}") | ||
|
||
|
||
asyncio.run(main()) |
11 changes: 11 additions & 0 deletions
11
config/clients/python/template/example/example1/requirements.txt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
aiohttp==3.9.1 | ||
aiosignal==1.3.1 | ||
attrs==23.1.0 | ||
frozenlist==1.4.1 | ||
idna==3.6 | ||
multidict==6.0.4 | ||
openfga-sdk==0.3.2 | ||
python-dateutil==2.8.2 | ||
six==1.16.0 | ||
urllib3==2.1.0 | ||
yarl==1.9.4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
[flake8] | ||
max-line-length=99 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# coding: utf-8 | ||
|
||
""" | ||
Python SDK for OpenFGA | ||
API version: 0.1 | ||
Website: https://openfga.dev | ||
Documentation: https://openfga.dev/docs | ||
Support: https://discord.gg/8naAwJfWN6 | ||
License: [Apache-2.0](https://github.com/openfga/python-sdk/blob/main/LICENSE) | ||
NOTE: This file was auto generated by OpenAPI Generator (https://openapi-generator.tech). DO NOT EDIT. | ||
""" | ||
|
||
from setuptools import setup, find_packages | ||
|
||
NAME = "example1" | ||
VERSION = "0.0.1" | ||
REQUIRES = ["openfga-sdk >= 0.3"] | ||
|
||
setup( | ||
name=NAME, | ||
version=VERSION, | ||
description="An example of using the OpenFGA Python SDK", | ||
author="OpenFGA (https://openfga.dev)", | ||
author_email="[email protected]", | ||
url="https://github.com/openfga/python-sdk", | ||
install_requires=REQUIRES, | ||
python_requires='>=3.10', | ||
packages=find_packages(exclude=["test", "tests"]), | ||
include_package_data=True, | ||
license="Apache-2.0" | ||
) |