Skip to content

Commit

Permalink
Merge pull request #117 from KozyarValeriy/feat-add-async-python-client
Browse files Browse the repository at this point in the history
feat: add async python client
  • Loading branch information
yquansah authored Feb 23, 2024
2 parents c03b665 + f11927c commit 8182227
Show file tree
Hide file tree
Showing 26 changed files with 1,067 additions and 255 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lint-sdks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
working-directory: flipt-python
run: |
poetry install
poetry run black --check .
make lint
lint-typescript:
name: Lint TypeScript
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,8 @@ Cargo.lock

.vscode/
.envrc

/flipt-python/.coverage
/flipt-python/.mypy_cache/
/flipt-python/.pytest_cache/
/flipt-python/.ruff_cache/
19 changes: 19 additions & 0 deletions flipt-python/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
format:
poetry run ruff flipt tests --fix
poetry run black flipt tests

lint:
poetry run ruff flipt tests
poetry run black flipt tests --check
poetry run mypy flipt
poetry run pytest --dead-fixtures

test:
poetry run pytest --cov

testcov:
poetry run pytest --cov --cov-report html

check: format lint test

.PHONY: format lint test check testcov
12 changes: 9 additions & 3 deletions flipt-python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,9 @@ In your Python code you can import this client and use it as so:
from flipt import FliptClient
from flipt.evaluation import BatchEvaluationRequest, EvaluationRequest

fliptClient = FliptClient()
flipt_client = FliptClient()

v = fliptClient.evaluation.variant(
variant_flag = flipt_client.evaluation.variant(
EvaluationRequest(
namespace_key="default",
flag_key="flagll",
Expand All @@ -33,7 +33,13 @@ v = fliptClient.evaluation.variant(
)
)

print(v)
print(variant_flag)
```

There is a more detailed example in the [examples](./examples) directory.


## For developers

After adding new code, please don't forget to add unit tests for new features.
To format the code, check it with linters and run tests, use the `make check` command.
51 changes: 51 additions & 0 deletions flipt-python/example/async_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import asyncio

from flipt import AsyncFliptClient
from flipt.evaluation import BatchEvaluationRequest, EvaluationRequest


async def main():
flipt_client = AsyncFliptClient()

variant_flag = await flipt_client.evaluation.variant(
EvaluationRequest(
namespace_key="default",
flag_key="flag1",
entity_id="entity",
context={"fizz": "buzz"},
)
)
boolean_flag = await flipt_client.evaluation.boolean(
EvaluationRequest(
namespace_key="default",
flag_key="flag_boolean",
entity_id="entity",
context={"fizz": "buzz"},
)
)
batch = await flipt_client.evaluation.batch(
BatchEvaluationRequest(
requests=[
EvaluationRequest(
namespace_key="default",
flag_key="flag1",
entity_id="entity",
context={"fizz": "buzz"},
),
EvaluationRequest(
namespace_key="default",
flag_key="flag_boolean",
entity_id="entity",
context={"fizz": "buzz"},
),
]
)
)

print(variant_flag)
print(boolean_flag)
print(batch)


loop = asyncio.new_event_loop()
loop.run_until_complete(main())
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
from flipt import FliptClient
from flipt.evaluation import BatchEvaluationRequest, EvaluationRequest

fliptClient = FliptClient()
flipt_client = FliptClient()

v = fliptClient.evaluation.variant(
variant_flag = flipt_client.evaluation.variant(
EvaluationRequest(
namespace_key="default",
flag_key="flagll",
flag_key="flag1",
entity_id="entity",
context={"fizz": "buzz"},
)
)
b = fliptClient.evaluation.boolean(
boolean_flag = flipt_client.evaluation.boolean(
EvaluationRequest(
namespace_key="default",
flag_key="flag_boolean",
entity_id="entity",
context={"fizz": "buzz"},
)
)
ba = fliptClient.evaluation.batch(
batch = flipt_client.evaluation.batch(
BatchEvaluationRequest(
requests=[
EvaluationRequest(
Expand All @@ -38,6 +38,6 @@
)
)

print(v)
print(b)
print(ba)
print(variant_flag)
print(boolean_flag)
print(batch)
18 changes: 6 additions & 12 deletions flipt-python/flipt/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
import typing
from .evaluation import Evaluation
from .authentication import AuthenticationStrategy
from .async_client import AsyncFliptClient
from .sync_client import FliptClient


class FliptClient:
def __init__(
self,
url: str = "http://localhost:8080",
timeout: int = 60,
authentication: typing.Optional[AuthenticationStrategy] = None,
):
self.evaluation = Evaluation(url, timeout, authentication)
__all__ = [
'FliptClient',
'AsyncFliptClient',
]
19 changes: 19 additions & 0 deletions flipt-python/flipt/async_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import httpx

from .authentication import AuthenticationStrategy
from .evaluation import AsyncEvaluation


class AsyncFliptClient:
def __init__(
self,
url: str = "http://localhost:8080",
timeout: int = 60,
authentication: AuthenticationStrategy | None = None,
):
self.httpx_client = httpx.AsyncClient(timeout=timeout)

self.evaluation = AsyncEvaluation(url, authentication, self.httpx_client)

async def close(self) -> None:
await self.httpx_client.aclose()
10 changes: 5 additions & 5 deletions flipt-python/flipt/authentication/__init__.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
class AuthenticationStrategy:
def authenticate(self, headers: dict):
def authenticate(self, headers: dict[str, str]) -> None:
raise NotImplementedError()


class ClientTokenAuthentication(AuthenticationStrategy):
def __init__(self, token: str):
def __init__(self, token: str) -> None:
self.token = token

def authenticate(self, headers: dict):
def authenticate(self, headers: dict[str, str]) -> None:
headers["Authorization"] = f"Bearer {self.token}"


class JWTAuthentication(AuthenticationStrategy):
def __init__(self, token: str):
def __init__(self, token: str) -> None:
self.token = token

def authenticate(self, headers: dict):
def authenticate(self, headers: dict[str, str]) -> None:
headers["Authorization"] = f"JWT {self.token}"
100 changes: 22 additions & 78 deletions flipt-python/flipt/evaluation/__init__.py
Original file line number Diff line number Diff line change
@@ -1,85 +1,29 @@
import httpx
import typing
import json
from .async_evaluation_client import AsyncEvaluation
from .models import (
BatchEvaluationRequest,
BatchEvaluationResponse,
BooleanEvaluationResponse,
ErrorEvaluationReason,
ErrorEvaluationResponse,
EvaluationReason,
EvaluationRequest,
EvaluationResponse,
EvaluationResponseType,
VariantEvaluationResponse,
)
from ..authentication import AuthenticationStrategy


class Evaluation:
def __init__(
self,
url: str,
timeout: int,
authentication: typing.Optional[AuthenticationStrategy] = None,
):
self.url = url
self.headers = {}
self.timeout = timeout
if authentication:
authentication.authenticate(self.headers)

def variant(self, request: EvaluationRequest) -> VariantEvaluationResponse:
response = httpx.post(
f"{self.url}/evaluate/v1/variant",
headers=self.headers,
json=request.model_dump(),
timeout=self.timeout,
)

if response.status_code != 200:
body = response.json()
message = "internal error"

if "message" in body:
message = body["message"]

raise Exception(message)

variant_response = json.dumps(response.json()).encode("utf-8")
return VariantEvaluationResponse.model_validate_json(variant_response)

def boolean(self, request: EvaluationRequest) -> BooleanEvaluationResponse:
response = httpx.post(
f"{self.url}/evaluate/v1/boolean",
headers=self.headers,
json=request.model_dump(),
timeout=self.timeout,
)

if response.status_code != 200:
body = response.json()
message = "internal error"

if "message" in body:
message = body["message"]

raise Exception(message)

boolean_response = json.dumps(response.json()).encode("utf-8")
return BooleanEvaluationResponse.model_validate_json(boolean_response)

def batch(self, request: BatchEvaluationRequest) -> BatchEvaluationResponse:
response = httpx.post(
f"{self.url}/evaluate/v1/batch",
headers=self.headers,
json=request.model_dump(),
timeout=self.timeout,
)

if response.status_code != 200:
body = response.json()
message = "internal error"

if "message" in body:
message = body["message"]

raise Exception(message)

batch_response = json.dumps(response.json()).encode("utf-8")
return BatchEvaluationResponse.model_validate_json(batch_response)
from .sync_evaluation_client import Evaluation

__all__ = [
'Evaluation',
'AsyncEvaluation',
'EvaluationResponseType',
'EvaluationReason',
'ErrorEvaluationReason',
'EvaluationRequest',
'BatchEvaluationRequest',
'VariantEvaluationResponse',
'BooleanEvaluationResponse',
'ErrorEvaluationResponse',
'EvaluationResponse',
'BatchEvaluationResponse',
]
Loading

0 comments on commit 8182227

Please sign in to comment.