Skip to content

Commit 15bc188

Browse files
committed
[client-python] feat(pyoaev): add multi-tenancy (#205)
1 parent 7ba051f commit 15bc188

5 files changed

Lines changed: 30 additions & 6 deletions

File tree

pyoaev/client.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import TYPE_CHECKING, Any, BinaryIO, Dict, List, Optional, Union
22
from urllib import parse
3+
from uuid import UUID
34

45
import requests
56

@@ -23,6 +24,7 @@ def __init__(
2324
pagination: Optional[str] = None,
2425
order_by: Optional[str] = None,
2526
ssl_verify: Union[bool, str] = True,
27+
tenant_id: Optional[UUID] = None,
2628
**kwargs: Any,
2729
) -> None:
2830

@@ -32,6 +34,7 @@ def __init__(
3234
raise ValueError("A TOKEN must be set")
3335

3436
self.url = url
37+
self.tenant_id = tenant_id
3538
self.timeout = timeout
3639
#: Headers that will be used in request to OpenAEV
3740
self.headers = {
@@ -109,9 +112,14 @@ def _build_url(self, path: str) -> str:
109112
Returns:
110113
The full URL
111114
"""
112-
if path.startswith("http://") or path.startswith("https://"):
115+
if parse.urlparse(path).scheme in ("http", "https"):
113116
return path
114-
return f"{self.url}/api{path}"
117+
base_url = self.url.rstrip("/")
118+
normalized_path = path.lstrip("/")
119+
if self.tenant_id:
120+
return f"{base_url}/api/tenants/{self.tenant_id}/{normalized_path}"
121+
else:
122+
return f"{base_url}/api/{normalized_path}"
115123

116124
def _get_session_opts(self) -> Dict[str, Any]:
117125
return {

pyoaev/configuration/settings_loader.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from datetime import timedelta
44
from pathlib import Path
55
from typing import Annotated, Literal
6+
from uuid import UUID
67

78
from pydantic import BaseModel, ConfigDict, Field, HttpUrl, PlainSerializer
89
from pydantic_settings import (
@@ -99,6 +100,11 @@ class ConfigLoaderOAEV(BaseConfigModel):
99100
token: str = Field(
100101
description="The token for the OpenAEV platform.",
101102
)
103+
tenant_id: UUID | None = Field(
104+
default=None,
105+
description="Identifier of the tenant within the OpenAEV platform. Used in multi-tenant environments to scope "
106+
"API requests and ensure data isolation between different tenants.",
107+
)
102108

103109

104110
class ConfigLoaderCollector(BaseConfigModel):

pyoaev/daemons/base_daemon.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from abc import ABC, abstractmethod
33
from inspect import signature
44
from types import FunctionType
5+
from uuid import UUID
56

67
from pyoaev.client import OpenAEV
78
from pyoaev.configuration import Configuration
@@ -37,6 +38,7 @@ def __init__(
3738
self.api = api_client or BaseDaemon.__get_default_api_client(
3839
url=self._configuration.get("openaev_url"),
3940
token=self._configuration.get("openaev_token"),
41+
tenant_id=self._configuration.get("openaev_tenant_id"),
4042
)
4143

4244
# logging
@@ -131,8 +133,8 @@ def get_id(self):
131133
)
132134

133135
@classmethod
134-
def __get_default_api_client(cls, url, token):
135-
return OpenAEV(url=url, token=token)
136+
def __get_default_api_client(cls, url, token, tenant_id: UUID | None):
137+
return OpenAEV(url=url, token=token, tenant_id=tenant_id)
136138

137139
@classmethod
138140
def __get_default_logger(cls, log_level, name):

pyoaev/helpers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ def __init__(self, config: OpenAEVConfigHelper, icon) -> None:
322322
self.api = OpenAEV(
323323
url=config.get_conf("openaev_url"),
324324
token=config.get_conf("openaev_token"),
325+
tenant_id=config.get_conf("openaev_tenant_id"),
325326
)
326327
# Get the mq configuration from api
327328
self.config = {

pyoaev/utils.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,6 +186,7 @@ def __init__(self, api, config, logger, ping_type) -> None:
186186
threading.Thread.__init__(self)
187187
self.ping_type = ping_type
188188
self.api = api
189+
self.tenant_id = getattr(self.api, "tenant_id", None)
189190
self.config = config
190191
self.logger = logger
191192
self.in_error = False
@@ -203,9 +204,15 @@ def ping(self) -> None:
203204
self.exit_event.wait(40)
204205

205206
def run(self) -> None:
206-
self.logger.info("Starting PingAlive thread")
207+
self.logger.info(
208+
"Starting PingAlive thread",
209+
{"tenant_id": str(self.tenant_id) if self.tenant_id else None},
210+
)
207211
self.ping()
208212

209213
def stop(self) -> None:
210-
self.logger.info("Preparing PingAlive for clean shutdown")
214+
self.logger.info(
215+
"Preparing PingAlive for clean shutdown",
216+
{"tenant_id": str(self.tenant_id) if self.tenant_id else None},
217+
)
211218
self.exit_event.set()

0 commit comments

Comments
 (0)