miso-client 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of miso-client might be problematic. Click here for more details.
- miso_client/__init__.py +83 -81
- miso_client/errors.py +9 -4
- miso_client/models/config.py +56 -35
- miso_client/services/__init__.py +5 -5
- miso_client/services/auth.py +65 -48
- miso_client/services/cache.py +42 -41
- miso_client/services/encryption.py +18 -17
- miso_client/services/logger.py +109 -95
- miso_client/services/permission.py +27 -36
- miso_client/services/redis.py +17 -15
- miso_client/services/role.py +25 -36
- miso_client/utils/__init__.py +3 -3
- miso_client/utils/config_loader.py +24 -16
- miso_client/utils/data_masker.py +27 -28
- miso_client/utils/http_client.py +91 -81
- miso_client/utils/jwt_tools.py +14 -17
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/METADATA +37 -1
- miso_client-0.2.0.dist-info/RECORD +23 -0
- miso_client-0.1.0.dist-info/RECORD +0 -23
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/WHEEL +0 -0
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/licenses/LICENSE +0 -0
- {miso_client-0.1.0.dist-info → miso_client-0.2.0.dist-info}/top_level.txt +0 -0
|
@@ -8,6 +8,7 @@ Optimized to extract userId from JWT token before API calls for cache optimizati
|
|
|
8
8
|
|
|
9
9
|
import time
|
|
10
10
|
from typing import List, cast
|
|
11
|
+
|
|
11
12
|
from ..models.config import PermissionResult
|
|
12
13
|
from ..services.cache import CacheService
|
|
13
14
|
from ..utils.http_client import HttpClient
|
|
@@ -16,11 +17,11 @@ from ..utils.jwt_tools import extract_user_id
|
|
|
16
17
|
|
|
17
18
|
class PermissionService:
|
|
18
19
|
"""Permission service for user authorization with caching."""
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
def __init__(self, http_client: HttpClient, cache: CacheService):
|
|
21
22
|
"""
|
|
22
23
|
Initialize permission service.
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
Args:
|
|
25
26
|
http_client: HTTP client instance
|
|
26
27
|
cache: Cache service instance (handles Redis + in-memory fallback)
|
|
@@ -33,12 +34,12 @@ class PermissionService:
|
|
|
33
34
|
async def get_permissions(self, token: str) -> List[str]:
|
|
34
35
|
"""
|
|
35
36
|
Get user permissions with Redis caching.
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
Optimized to extract userId from token first to check cache before API call.
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
Args:
|
|
40
41
|
token: JWT token
|
|
41
|
-
|
|
42
|
+
|
|
42
43
|
Returns:
|
|
43
44
|
List of user permissions
|
|
44
45
|
"""
|
|
@@ -57,9 +58,7 @@ class PermissionService:
|
|
|
57
58
|
# If we don't have userId, get it from validate endpoint
|
|
58
59
|
if not user_id:
|
|
59
60
|
user_info = await self.http_client.authenticated_request(
|
|
60
|
-
"POST",
|
|
61
|
-
"/api/auth/validate",
|
|
62
|
-
token
|
|
61
|
+
"POST", "/api/auth/validate", token
|
|
63
62
|
)
|
|
64
63
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
65
64
|
if not user_id:
|
|
@@ -68,9 +67,7 @@ class PermissionService:
|
|
|
68
67
|
|
|
69
68
|
# Cache miss - fetch from controller
|
|
70
69
|
permission_result = await self.http_client.authenticated_request(
|
|
71
|
-
"GET",
|
|
72
|
-
"/api/auth/permissions", # Backend knows app/env from client token
|
|
73
|
-
token
|
|
70
|
+
"GET", "/api/auth/permissions", token # Backend knows app/env from client token
|
|
74
71
|
)
|
|
75
72
|
|
|
76
73
|
permission_data = PermissionResult(**permission_result)
|
|
@@ -81,11 +78,11 @@ class PermissionService:
|
|
|
81
78
|
await self.cache.set(
|
|
82
79
|
cache_key,
|
|
83
80
|
{"permissions": permissions, "timestamp": int(time.time() * 1000)},
|
|
84
|
-
self.permission_ttl
|
|
81
|
+
self.permission_ttl,
|
|
85
82
|
)
|
|
86
83
|
|
|
87
84
|
return permissions
|
|
88
|
-
|
|
85
|
+
|
|
89
86
|
except Exception:
|
|
90
87
|
# Failed to get permissions, return empty list
|
|
91
88
|
return []
|
|
@@ -93,11 +90,11 @@ class PermissionService:
|
|
|
93
90
|
async def has_permission(self, token: str, permission: str) -> bool:
|
|
94
91
|
"""
|
|
95
92
|
Check if user has specific permission.
|
|
96
|
-
|
|
93
|
+
|
|
97
94
|
Args:
|
|
98
95
|
token: JWT token
|
|
99
96
|
permission: Permission to check
|
|
100
|
-
|
|
97
|
+
|
|
101
98
|
Returns:
|
|
102
99
|
True if user has the permission, False otherwise
|
|
103
100
|
"""
|
|
@@ -107,11 +104,11 @@ class PermissionService:
|
|
|
107
104
|
async def has_any_permission(self, token: str, permissions: List[str]) -> bool:
|
|
108
105
|
"""
|
|
109
106
|
Check if user has any of the specified permissions.
|
|
110
|
-
|
|
107
|
+
|
|
111
108
|
Args:
|
|
112
109
|
token: JWT token
|
|
113
110
|
permissions: List of permissions to check
|
|
114
|
-
|
|
111
|
+
|
|
115
112
|
Returns:
|
|
116
113
|
True if user has any of the permissions, False otherwise
|
|
117
114
|
"""
|
|
@@ -121,11 +118,11 @@ class PermissionService:
|
|
|
121
118
|
async def has_all_permissions(self, token: str, permissions: List[str]) -> bool:
|
|
122
119
|
"""
|
|
123
120
|
Check if user has all of the specified permissions.
|
|
124
|
-
|
|
121
|
+
|
|
125
122
|
Args:
|
|
126
123
|
token: JWT token
|
|
127
124
|
permissions: List of permissions to check
|
|
128
|
-
|
|
125
|
+
|
|
129
126
|
Returns:
|
|
130
127
|
True if user has all permissions, False otherwise
|
|
131
128
|
"""
|
|
@@ -135,21 +132,19 @@ class PermissionService:
|
|
|
135
132
|
async def refresh_permissions(self, token: str) -> List[str]:
|
|
136
133
|
"""
|
|
137
134
|
Force refresh permissions from controller (bypass cache).
|
|
138
|
-
|
|
135
|
+
|
|
139
136
|
Args:
|
|
140
137
|
token: JWT token
|
|
141
|
-
|
|
138
|
+
|
|
142
139
|
Returns:
|
|
143
140
|
Fresh list of user permissions
|
|
144
141
|
"""
|
|
145
142
|
try:
|
|
146
143
|
# Get user info to extract userId
|
|
147
144
|
user_info = await self.http_client.authenticated_request(
|
|
148
|
-
"POST",
|
|
149
|
-
"/api/auth/validate",
|
|
150
|
-
token
|
|
145
|
+
"POST", "/api/auth/validate", token
|
|
151
146
|
)
|
|
152
|
-
|
|
147
|
+
|
|
153
148
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
154
149
|
if not user_id:
|
|
155
150
|
return []
|
|
@@ -158,9 +153,7 @@ class PermissionService:
|
|
|
158
153
|
|
|
159
154
|
# Fetch fresh permissions from controller using refresh endpoint
|
|
160
155
|
permission_result = await self.http_client.authenticated_request(
|
|
161
|
-
"GET",
|
|
162
|
-
"/api/auth/permissions/refresh",
|
|
163
|
-
token
|
|
156
|
+
"GET", "/api/auth/permissions/refresh", token
|
|
164
157
|
)
|
|
165
158
|
|
|
166
159
|
permission_data = PermissionResult(**permission_result)
|
|
@@ -170,11 +163,11 @@ class PermissionService:
|
|
|
170
163
|
await self.cache.set(
|
|
171
164
|
cache_key,
|
|
172
165
|
{"permissions": permissions, "timestamp": int(time.time() * 1000)},
|
|
173
|
-
self.permission_ttl
|
|
166
|
+
self.permission_ttl,
|
|
174
167
|
)
|
|
175
168
|
|
|
176
169
|
return permissions
|
|
177
|
-
|
|
170
|
+
|
|
178
171
|
except Exception:
|
|
179
172
|
# Failed to refresh permissions, return empty list
|
|
180
173
|
return []
|
|
@@ -182,18 +175,16 @@ class PermissionService:
|
|
|
182
175
|
async def clear_permissions_cache(self, token: str) -> None:
|
|
183
176
|
"""
|
|
184
177
|
Clear cached permissions for a user.
|
|
185
|
-
|
|
178
|
+
|
|
186
179
|
Args:
|
|
187
180
|
token: JWT token
|
|
188
181
|
"""
|
|
189
182
|
try:
|
|
190
183
|
# Get user info to extract userId
|
|
191
184
|
user_info = await self.http_client.authenticated_request(
|
|
192
|
-
"POST",
|
|
193
|
-
"/api/auth/validate",
|
|
194
|
-
token
|
|
185
|
+
"POST", "/api/auth/validate", token
|
|
195
186
|
)
|
|
196
|
-
|
|
187
|
+
|
|
197
188
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
198
189
|
if not user_id:
|
|
199
190
|
return
|
|
@@ -202,7 +193,7 @@ class PermissionService:
|
|
|
202
193
|
|
|
203
194
|
# Clear from cache (CacheService handles Redis + in-memory automatically)
|
|
204
195
|
await self.cache.delete(cache_key)
|
|
205
|
-
|
|
196
|
+
|
|
206
197
|
except Exception:
|
|
207
198
|
# Failed to clear cache, silently continue
|
|
208
199
|
pass
|
miso_client/services/redis.py
CHANGED
|
@@ -5,18 +5,20 @@ This module provides Redis connectivity with graceful degradation when Redis
|
|
|
5
5
|
is unavailable. It handles caching of roles and permissions, and log queuing.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
import redis.asyncio as redis
|
|
9
8
|
from typing import Optional
|
|
9
|
+
|
|
10
|
+
import redis.asyncio as redis
|
|
11
|
+
|
|
10
12
|
from ..models.config import RedisConfig
|
|
11
13
|
|
|
12
14
|
|
|
13
15
|
class RedisService:
|
|
14
16
|
"""Redis service for caching and log queuing."""
|
|
15
|
-
|
|
17
|
+
|
|
16
18
|
def __init__(self, config: Optional[RedisConfig] = None):
|
|
17
19
|
"""
|
|
18
20
|
Initialize Redis service.
|
|
19
|
-
|
|
21
|
+
|
|
20
22
|
Args:
|
|
21
23
|
config: Optional Redis configuration
|
|
22
24
|
"""
|
|
@@ -27,7 +29,7 @@ class RedisService:
|
|
|
27
29
|
async def connect(self) -> None:
|
|
28
30
|
"""
|
|
29
31
|
Connect to Redis.
|
|
30
|
-
|
|
32
|
+
|
|
31
33
|
Raises:
|
|
32
34
|
Exception: If connection fails and config is provided
|
|
33
35
|
"""
|
|
@@ -46,7 +48,7 @@ class RedisService:
|
|
|
46
48
|
socket_connect_timeout=5,
|
|
47
49
|
socket_timeout=5,
|
|
48
50
|
)
|
|
49
|
-
|
|
51
|
+
|
|
50
52
|
# Test connection
|
|
51
53
|
# Some redis stubs type ping as possibly non-awaitable; support both
|
|
52
54
|
resp = self.redis.ping()
|
|
@@ -54,7 +56,7 @@ class RedisService:
|
|
|
54
56
|
await resp # type: ignore[misc]
|
|
55
57
|
self.connected = True
|
|
56
58
|
print("Connected to Redis")
|
|
57
|
-
|
|
59
|
+
|
|
58
60
|
except Exception as error:
|
|
59
61
|
print(f"Failed to connect to Redis: {error}")
|
|
60
62
|
self.connected = False
|
|
@@ -71,7 +73,7 @@ class RedisService:
|
|
|
71
73
|
def is_connected(self) -> bool:
|
|
72
74
|
"""
|
|
73
75
|
Check if Redis is connected.
|
|
74
|
-
|
|
76
|
+
|
|
75
77
|
Returns:
|
|
76
78
|
True if connected, False otherwise
|
|
77
79
|
"""
|
|
@@ -80,10 +82,10 @@ class RedisService:
|
|
|
80
82
|
async def get(self, key: str) -> Optional[str]:
|
|
81
83
|
"""
|
|
82
84
|
Get value from Redis.
|
|
83
|
-
|
|
85
|
+
|
|
84
86
|
Args:
|
|
85
87
|
key: Redis key
|
|
86
|
-
|
|
88
|
+
|
|
87
89
|
Returns:
|
|
88
90
|
Value if found, None otherwise
|
|
89
91
|
"""
|
|
@@ -106,12 +108,12 @@ class RedisService:
|
|
|
106
108
|
async def set(self, key: str, value: str, ttl: int) -> bool:
|
|
107
109
|
"""
|
|
108
110
|
Set value in Redis with TTL.
|
|
109
|
-
|
|
111
|
+
|
|
110
112
|
Args:
|
|
111
113
|
key: Redis key
|
|
112
114
|
value: Value to store
|
|
113
115
|
ttl: Time to live in seconds
|
|
114
|
-
|
|
116
|
+
|
|
115
117
|
Returns:
|
|
116
118
|
True if successful, False otherwise
|
|
117
119
|
"""
|
|
@@ -132,10 +134,10 @@ class RedisService:
|
|
|
132
134
|
async def delete(self, key: str) -> bool:
|
|
133
135
|
"""
|
|
134
136
|
Delete key from Redis.
|
|
135
|
-
|
|
137
|
+
|
|
136
138
|
Args:
|
|
137
139
|
key: Redis key
|
|
138
|
-
|
|
140
|
+
|
|
139
141
|
Returns:
|
|
140
142
|
True if successful, False otherwise
|
|
141
143
|
"""
|
|
@@ -156,11 +158,11 @@ class RedisService:
|
|
|
156
158
|
async def rpush(self, queue: str, value: str) -> bool:
|
|
157
159
|
"""
|
|
158
160
|
Push value to Redis list (for log queuing).
|
|
159
|
-
|
|
161
|
+
|
|
160
162
|
Args:
|
|
161
163
|
queue: Queue name
|
|
162
164
|
value: Value to push
|
|
163
|
-
|
|
165
|
+
|
|
164
166
|
Returns:
|
|
165
167
|
True if successful, False otherwise
|
|
166
168
|
"""
|
miso_client/services/role.py
CHANGED
|
@@ -8,6 +8,7 @@ Optimized to extract userId from JWT token before API calls for cache optimizati
|
|
|
8
8
|
|
|
9
9
|
import time
|
|
10
10
|
from typing import List, cast
|
|
11
|
+
|
|
11
12
|
from ..models.config import RoleResult
|
|
12
13
|
from ..services.cache import CacheService
|
|
13
14
|
from ..utils.http_client import HttpClient
|
|
@@ -16,11 +17,11 @@ from ..utils.jwt_tools import extract_user_id
|
|
|
16
17
|
|
|
17
18
|
class RoleService:
|
|
18
19
|
"""Role service for user authorization with caching."""
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
def __init__(self, http_client: HttpClient, cache: CacheService):
|
|
21
22
|
"""
|
|
22
23
|
Initialize role service.
|
|
23
|
-
|
|
24
|
+
|
|
24
25
|
Args:
|
|
25
26
|
http_client: HTTP client instance
|
|
26
27
|
cache: Cache service instance (handles Redis + in-memory fallback)
|
|
@@ -33,12 +34,12 @@ class RoleService:
|
|
|
33
34
|
async def get_roles(self, token: str) -> List[str]:
|
|
34
35
|
"""
|
|
35
36
|
Get user roles with Redis caching.
|
|
36
|
-
|
|
37
|
+
|
|
37
38
|
Optimized to extract userId from token first to check cache before API call.
|
|
38
|
-
|
|
39
|
+
|
|
39
40
|
Args:
|
|
40
41
|
token: JWT token
|
|
41
|
-
|
|
42
|
+
|
|
42
43
|
Returns:
|
|
43
44
|
List of user roles
|
|
44
45
|
"""
|
|
@@ -57,9 +58,7 @@ class RoleService:
|
|
|
57
58
|
# If we don't have userId, get it from validate endpoint
|
|
58
59
|
if not user_id:
|
|
59
60
|
user_info = await self.http_client.authenticated_request(
|
|
60
|
-
"POST",
|
|
61
|
-
"/api/auth/validate",
|
|
62
|
-
token
|
|
61
|
+
"POST", "/api/auth/validate", token
|
|
63
62
|
)
|
|
64
63
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
65
64
|
if not user_id:
|
|
@@ -68,24 +67,20 @@ class RoleService:
|
|
|
68
67
|
|
|
69
68
|
# Cache miss - fetch from controller
|
|
70
69
|
role_result = await self.http_client.authenticated_request(
|
|
71
|
-
"GET",
|
|
72
|
-
"/api/auth/roles", # Backend knows app/env from client token
|
|
73
|
-
token
|
|
70
|
+
"GET", "/api/auth/roles", token # Backend knows app/env from client token
|
|
74
71
|
)
|
|
75
|
-
|
|
72
|
+
|
|
76
73
|
role_data = RoleResult(**role_result)
|
|
77
74
|
roles = role_data.roles or []
|
|
78
75
|
|
|
79
76
|
# Cache the result (CacheService handles Redis + in-memory automatically)
|
|
80
77
|
assert cache_key is not None
|
|
81
78
|
await self.cache.set(
|
|
82
|
-
cache_key,
|
|
83
|
-
{"roles": roles, "timestamp": int(time.time() * 1000)},
|
|
84
|
-
self.role_ttl
|
|
79
|
+
cache_key, {"roles": roles, "timestamp": int(time.time() * 1000)}, self.role_ttl
|
|
85
80
|
)
|
|
86
81
|
|
|
87
82
|
return roles
|
|
88
|
-
|
|
83
|
+
|
|
89
84
|
except Exception:
|
|
90
85
|
# Failed to get roles, return empty list
|
|
91
86
|
return []
|
|
@@ -93,11 +88,11 @@ class RoleService:
|
|
|
93
88
|
async def has_role(self, token: str, role: str) -> bool:
|
|
94
89
|
"""
|
|
95
90
|
Check if user has specific role.
|
|
96
|
-
|
|
91
|
+
|
|
97
92
|
Args:
|
|
98
93
|
token: JWT token
|
|
99
94
|
role: Role to check
|
|
100
|
-
|
|
95
|
+
|
|
101
96
|
Returns:
|
|
102
97
|
True if user has the role, False otherwise
|
|
103
98
|
"""
|
|
@@ -107,11 +102,11 @@ class RoleService:
|
|
|
107
102
|
async def has_any_role(self, token: str, roles: List[str]) -> bool:
|
|
108
103
|
"""
|
|
109
104
|
Check if user has any of the specified roles.
|
|
110
|
-
|
|
105
|
+
|
|
111
106
|
Args:
|
|
112
107
|
token: JWT token
|
|
113
108
|
roles: List of roles to check
|
|
114
|
-
|
|
109
|
+
|
|
115
110
|
Returns:
|
|
116
111
|
True if user has any of the roles, False otherwise
|
|
117
112
|
"""
|
|
@@ -121,11 +116,11 @@ class RoleService:
|
|
|
121
116
|
async def has_all_roles(self, token: str, roles: List[str]) -> bool:
|
|
122
117
|
"""
|
|
123
118
|
Check if user has all of the specified roles.
|
|
124
|
-
|
|
119
|
+
|
|
125
120
|
Args:
|
|
126
121
|
token: JWT token
|
|
127
122
|
roles: List of roles to check
|
|
128
|
-
|
|
123
|
+
|
|
129
124
|
Returns:
|
|
130
125
|
True if user has all roles, False otherwise
|
|
131
126
|
"""
|
|
@@ -135,21 +130,19 @@ class RoleService:
|
|
|
135
130
|
async def refresh_roles(self, token: str) -> List[str]:
|
|
136
131
|
"""
|
|
137
132
|
Force refresh roles from controller (bypass cache).
|
|
138
|
-
|
|
133
|
+
|
|
139
134
|
Args:
|
|
140
135
|
token: JWT token
|
|
141
|
-
|
|
136
|
+
|
|
142
137
|
Returns:
|
|
143
138
|
Fresh list of user roles
|
|
144
139
|
"""
|
|
145
140
|
try:
|
|
146
141
|
# Get user info to extract userId
|
|
147
142
|
user_info = await self.http_client.authenticated_request(
|
|
148
|
-
"POST",
|
|
149
|
-
"/api/auth/validate",
|
|
150
|
-
token
|
|
143
|
+
"POST", "/api/auth/validate", token
|
|
151
144
|
)
|
|
152
|
-
|
|
145
|
+
|
|
153
146
|
user_id = user_info.get("user", {}).get("id") if user_info else None
|
|
154
147
|
if not user_id:
|
|
155
148
|
return []
|
|
@@ -158,23 +151,19 @@ class RoleService:
|
|
|
158
151
|
|
|
159
152
|
# Fetch fresh roles from controller using refresh endpoint
|
|
160
153
|
role_result = await self.http_client.authenticated_request(
|
|
161
|
-
"GET",
|
|
162
|
-
"/api/auth/roles/refresh",
|
|
163
|
-
token
|
|
154
|
+
"GET", "/api/auth/roles/refresh", token
|
|
164
155
|
)
|
|
165
|
-
|
|
156
|
+
|
|
166
157
|
role_data = RoleResult(**role_result)
|
|
167
158
|
roles = role_data.roles or []
|
|
168
159
|
|
|
169
160
|
# Update cache with fresh data (CacheService handles Redis + in-memory automatically)
|
|
170
161
|
await self.cache.set(
|
|
171
|
-
cache_key,
|
|
172
|
-
{"roles": roles, "timestamp": int(time.time() * 1000)},
|
|
173
|
-
self.role_ttl
|
|
162
|
+
cache_key, {"roles": roles, "timestamp": int(time.time() * 1000)}, self.role_ttl
|
|
174
163
|
)
|
|
175
164
|
|
|
176
165
|
return roles
|
|
177
|
-
|
|
166
|
+
|
|
178
167
|
except Exception:
|
|
179
168
|
# Failed to refresh roles, return empty list
|
|
180
169
|
return []
|
miso_client/utils/__init__.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"""Utility modules for MisoClient SDK."""
|
|
2
2
|
|
|
3
|
-
from .http_client import HttpClient
|
|
4
3
|
from .config_loader import load_config
|
|
5
4
|
from .data_masker import DataMasker
|
|
6
|
-
from .
|
|
5
|
+
from .http_client import HttpClient
|
|
6
|
+
from .jwt_tools import decode_token, extract_session_id, extract_user_id
|
|
7
7
|
|
|
8
8
|
__all__ = [
|
|
9
9
|
"HttpClient",
|
|
@@ -12,4 +12,4 @@ __all__ = [
|
|
|
12
12
|
"decode_token",
|
|
13
13
|
"extract_user_id",
|
|
14
14
|
"extract_session_id",
|
|
15
|
-
]
|
|
15
|
+
]
|
|
@@ -6,50 +6,55 @@ Automatically loads environment variables with sensible defaults.
|
|
|
6
6
|
|
|
7
7
|
import os
|
|
8
8
|
from typing import Literal, cast
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
from ..errors import ConfigurationError
|
|
11
|
+
from ..models.config import MisoClientConfig, RedisConfig
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
def load_config() -> MisoClientConfig:
|
|
14
15
|
"""
|
|
15
16
|
Load configuration from environment variables with defaults.
|
|
16
|
-
|
|
17
|
+
|
|
17
18
|
Required environment variables:
|
|
18
19
|
- MISO_CONTROLLER_URL (or default to https://controller.aifabrix.ai)
|
|
19
20
|
- MISO_CLIENTID or MISO_CLIENT_ID
|
|
20
21
|
- MISO_CLIENTSECRET or MISO_CLIENT_SECRET
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
Optional environment variables:
|
|
23
24
|
- MISO_LOG_LEVEL (debug, info, warn, error)
|
|
25
|
+
- API_KEY (for testing - bypasses OAuth2 authentication)
|
|
24
26
|
- REDIS_HOST (if Redis is used)
|
|
25
27
|
- REDIS_PORT (default: 6379)
|
|
26
28
|
- REDIS_PASSWORD
|
|
27
29
|
- REDIS_DB (default: 0)
|
|
28
30
|
- REDIS_KEY_PREFIX (default: miso:)
|
|
29
|
-
|
|
31
|
+
|
|
30
32
|
Returns:
|
|
31
33
|
MisoClientConfig instance
|
|
32
|
-
|
|
34
|
+
|
|
33
35
|
Raises:
|
|
34
36
|
ConfigurationError: If required environment variables are missing
|
|
35
37
|
"""
|
|
36
38
|
# Load dotenv if available (similar to TypeScript dotenv/config)
|
|
37
39
|
try:
|
|
38
40
|
from dotenv import load_dotenv
|
|
41
|
+
|
|
39
42
|
load_dotenv()
|
|
40
43
|
except ImportError:
|
|
41
44
|
pass # dotenv not installed, continue without it
|
|
42
|
-
|
|
45
|
+
|
|
43
46
|
controller_url = os.environ.get("MISO_CONTROLLER_URL") or "https://controller.aifabrix.ai"
|
|
44
|
-
|
|
47
|
+
|
|
45
48
|
client_id = os.environ.get("MISO_CLIENTID") or os.environ.get("MISO_CLIENT_ID") or ""
|
|
46
49
|
if not client_id:
|
|
47
50
|
raise ConfigurationError("MISO_CLIENTID environment variable is required")
|
|
48
|
-
|
|
49
|
-
client_secret =
|
|
51
|
+
|
|
52
|
+
client_secret = (
|
|
53
|
+
os.environ.get("MISO_CLIENTSECRET") or os.environ.get("MISO_CLIENT_SECRET") or ""
|
|
54
|
+
)
|
|
50
55
|
if not client_secret:
|
|
51
56
|
raise ConfigurationError("MISO_CLIENTSECRET environment variable is required")
|
|
52
|
-
|
|
57
|
+
|
|
53
58
|
log_level_str = os.environ.get("MISO_LOG_LEVEL", "info")
|
|
54
59
|
if log_level_str not in ["debug", "info", "warn", "error"]:
|
|
55
60
|
log_level_str = "info"
|
|
@@ -57,14 +62,18 @@ def load_config() -> MisoClientConfig:
|
|
|
57
62
|
log_level: Literal["debug", "info", "warn", "error"] = cast(
|
|
58
63
|
Literal["debug", "info", "warn", "error"], log_level_str
|
|
59
64
|
)
|
|
60
|
-
|
|
65
|
+
|
|
66
|
+
# Optional API_KEY for testing
|
|
67
|
+
api_key = os.environ.get("API_KEY")
|
|
68
|
+
|
|
61
69
|
config: MisoClientConfig = MisoClientConfig(
|
|
62
70
|
controller_url=controller_url,
|
|
63
71
|
client_id=client_id,
|
|
64
72
|
client_secret=client_secret,
|
|
65
73
|
log_level=log_level,
|
|
74
|
+
api_key=api_key,
|
|
66
75
|
)
|
|
67
|
-
|
|
76
|
+
|
|
68
77
|
# Optional Redis configuration
|
|
69
78
|
redis_host = os.environ.get("REDIS_HOST")
|
|
70
79
|
if redis_host:
|
|
@@ -72,7 +81,7 @@ def load_config() -> MisoClientConfig:
|
|
|
72
81
|
redis_password = os.environ.get("REDIS_PASSWORD")
|
|
73
82
|
redis_db = int(os.environ.get("REDIS_DB", "0")) if os.environ.get("REDIS_DB") else 0
|
|
74
83
|
redis_key_prefix = os.environ.get("REDIS_KEY_PREFIX", "miso:")
|
|
75
|
-
|
|
84
|
+
|
|
76
85
|
redis_config = RedisConfig(
|
|
77
86
|
host=redis_host,
|
|
78
87
|
port=redis_port,
|
|
@@ -80,8 +89,7 @@ def load_config() -> MisoClientConfig:
|
|
|
80
89
|
db=redis_db,
|
|
81
90
|
key_prefix=redis_key_prefix,
|
|
82
91
|
)
|
|
83
|
-
|
|
92
|
+
|
|
84
93
|
config.redis = redis_config
|
|
85
|
-
|
|
86
|
-
return config
|
|
87
94
|
|
|
95
|
+
return config
|