Skip to content

Commit

Permalink
chore(python-sdk): add example project
Browse files Browse the repository at this point in the history
  • Loading branch information
booniepepper committed Dec 30, 2023
1 parent 65aeb0e commit 010bb1e
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 0 deletions.
28 changes: 28 additions & 0 deletions config/clients/python/config.overrides.json
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,34 @@
},
"help.py": {
"destinationFilename": "openfga_sdk/help.py"
},
"example/Makefile": {
"destinationFilename": "example/Makefile",
"templateType": "SupportingFiles"
},
"example/README.md": {
"destinationFilename": "example/README.md",
"templateType": "SupportingFiles"
},
"example/example1/auth-model.json": {
"destinationFilename": "example/example1/auth-model.json",
"templateType": "SupportingFiles"
},
"example/example1/example1.py": {
"destinationFilename": "example/example1/example1.py",
"templateType": "SupportingFiles"
},
"example/example1/requirements.txt": {
"destinationFilename": "example/example1/requirements.txt",
"templateType": "SupportingFiles"
},
"example/example1/setup.py": {
"destinationFilename": "example/example1/setup.py",
"templateType": "SupportingFiles"
},
"example/example1/setup.cfg": {
"destinationFilename": "example/example1/setup.cfg",
"templateType": "SupportingFiles"
}
}
}
11 changes: 11 additions & 0 deletions config/clients/python/template/example/Makefile
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
32 changes: 32 additions & 0 deletions config/clients/python/template/example/README.md
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 config/clients/python/template/example/example1/auth-model.json
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 config/clients/python/template/example/example1/example1.py
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 config/clients/python/template/example/example1/requirements.txt
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
2 changes: 2 additions & 0 deletions config/clients/python/template/example/example1/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[flake8]
max-line-length=99
33 changes: 33 additions & 0 deletions config/clients/python/template/example/example1/setup.py
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"
)

0 comments on commit 010bb1e

Please sign in to comment.