Skip to content

Commit 0b7b482

Browse files
feat(modules): add OpenFGA module (#762)
Add OpenFGA testcontainer module --------- Signed-off-by: Petr Fedchenkov <[email protected]> Co-authored-by: David Ankin <[email protected]>
1 parent ef65bd1 commit 0b7b482

File tree

7 files changed

+840
-48
lines changed

7 files changed

+840
-48
lines changed

core/README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Testcontainers Core
66
.. automodule:: testcontainers.core.container
77
:members:
88
:undoc-members:
9-
9+
1010
.. autoclass:: testcontainers.core.network.Network
1111
:members:
1212

modules/openfga/README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
.. autoclass:: testcontainers.openfga.OpenFGAContainer
2+
.. title:: testcontainers.openfga.OpenFGAContainer
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
#
2+
# Licensed under the Apache License, Version 2.0 (the "License"); you may
3+
# not use this file except in compliance with the License. You may obtain
4+
# a copy of the License at
5+
#
6+
# http://www.apache.org/licenses/LICENSE-2.0
7+
#
8+
# Unless required by applicable law or agreed to in writing, software
9+
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10+
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11+
# License for the specific language governing permissions and limitations
12+
# under the License.
13+
14+
from typing import Optional
15+
16+
import requests
17+
18+
from testcontainers.core.container import DockerContainer
19+
from testcontainers.core.waiting_utils import wait_container_is_ready
20+
21+
no_client = False
22+
try:
23+
from openfga_sdk import ClientConfiguration
24+
from openfga_sdk.credentials import CredentialConfiguration, Credentials
25+
from openfga_sdk.sync import OpenFgaClient
26+
except ImportError:
27+
no_client = True
28+
29+
class OpenFgaClient:
30+
pass
31+
32+
33+
_DEFAULT_RUN_COMMAND = "run"
34+
35+
36+
class OpenFGAContainer(DockerContainer):
37+
"""
38+
OpenFGAContainer container.
39+
40+
Example:
41+
42+
.. doctest::
43+
44+
>>> from testcontainers.openfga import OpenFGAContainer
45+
>>> from sys import version_info
46+
47+
>>> with OpenFGAContainer("openfga/openfga:v1.8.4") as openfga:
48+
... {"continuation_token": "", 'stores': []} if version_info < (3, 10) else openfga.get_client().list_stores()
49+
{'continuation_token': '', 'stores': []}
50+
"""
51+
52+
# pylint: disable=too-many-arguments
53+
def __init__(
54+
self,
55+
image: str = "openfga/openfga:latest",
56+
preshared_keys: Optional[list[str]] = None,
57+
playground_port: int = 3000,
58+
http_port: int = 8080,
59+
grpc_port: int = 8081,
60+
cmd: str = _DEFAULT_RUN_COMMAND,
61+
) -> None:
62+
super().__init__(image=image)
63+
self.preshared_keys = preshared_keys
64+
self.playground_port = playground_port
65+
self.http_port = http_port
66+
self.grpc_port = grpc_port
67+
self.with_exposed_ports(self.playground_port, self.http_port, self.grpc_port)
68+
self.cmd = cmd
69+
70+
def _configure(self) -> None:
71+
if self.preshared_keys:
72+
self.cmd += " --authn-method=preshared"
73+
self.cmd += f' --authn-preshared-keys="{",".join(self.preshared_keys)}"'
74+
self.with_command(self.cmd)
75+
76+
def get_api_url(self) -> str:
77+
host = self.get_container_host_ip()
78+
port = self.get_exposed_port(self.http_port)
79+
return f"http://{host}:{port}"
80+
81+
@wait_container_is_ready(requests.exceptions.ConnectionError, requests.exceptions.ReadTimeout)
82+
def _readiness_probe(self) -> None:
83+
self.exec(["grpc_health_probe", "-addr=0.0.0.0:8081"]) # from chart
84+
85+
def start(self) -> "OpenFGAContainer":
86+
super().start()
87+
self._readiness_probe()
88+
return self
89+
90+
def get_preshared_keys(self) -> Optional[list[str]]:
91+
return self.preshared_keys
92+
93+
def get_client(self) -> "OpenFgaClient":
94+
if no_client:
95+
raise NotImplementedError("failed to import openfga_sdk: is python < 3.10?")
96+
97+
credentials = None
98+
if preshared_keys := self.get_preshared_keys():
99+
credentials = Credentials(
100+
method="api_token",
101+
configuration=CredentialConfiguration(
102+
api_token=preshared_keys[0],
103+
),
104+
)
105+
client_configuration = ClientConfiguration(api_url=self.get_api_url(), credentials=credentials)
106+
return OpenFgaClient(client_configuration)

modules/openfga/tests/test_openfga.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import pytest
2+
from testcontainers.openfga import OpenFGAContainer
3+
from sys import version_info
4+
5+
6+
def test_openfga():
7+
if version_info < (3, 10):
8+
with pytest.raises(NotImplementedError):
9+
_test_openfga()
10+
else:
11+
_test_openfga()
12+
13+
14+
def _test_openfga():
15+
with OpenFGAContainer("openfga/openfga:v1.8.4") as openfga:
16+
client = openfga.get_client()
17+
assert client
18+
assert client.list_stores()

0 commit comments

Comments
 (0)