miso-client 0.1.0__py3-none-any.whl → 3.7.2__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.
- miso_client/__init__.py +523 -130
- miso_client/api/__init__.py +35 -0
- miso_client/api/auth_api.py +367 -0
- miso_client/api/logs_api.py +91 -0
- miso_client/api/permissions_api.py +88 -0
- miso_client/api/roles_api.py +88 -0
- miso_client/api/types/__init__.py +75 -0
- miso_client/api/types/auth_types.py +183 -0
- miso_client/api/types/logs_types.py +71 -0
- miso_client/api/types/permissions_types.py +31 -0
- miso_client/api/types/roles_types.py +31 -0
- miso_client/errors.py +30 -4
- miso_client/models/__init__.py +4 -0
- miso_client/models/config.py +275 -72
- miso_client/models/error_response.py +39 -0
- miso_client/models/filter.py +255 -0
- miso_client/models/pagination.py +44 -0
- miso_client/models/sort.py +25 -0
- miso_client/services/__init__.py +6 -5
- miso_client/services/auth.py +496 -87
- miso_client/services/cache.py +42 -41
- miso_client/services/encryption.py +18 -17
- miso_client/services/logger.py +467 -328
- miso_client/services/logger_chain.py +288 -0
- miso_client/services/permission.py +130 -67
- miso_client/services/redis.py +28 -23
- miso_client/services/role.py +145 -62
- miso_client/utils/__init__.py +3 -3
- miso_client/utils/audit_log_queue.py +222 -0
- miso_client/utils/auth_strategy.py +88 -0
- miso_client/utils/auth_utils.py +65 -0
- miso_client/utils/circuit_breaker.py +125 -0
- miso_client/utils/client_token_manager.py +244 -0
- miso_client/utils/config_loader.py +88 -17
- miso_client/utils/controller_url_resolver.py +80 -0
- miso_client/utils/data_masker.py +104 -33
- miso_client/utils/environment_token.py +126 -0
- miso_client/utils/error_utils.py +216 -0
- miso_client/utils/fastapi_endpoints.py +166 -0
- miso_client/utils/filter.py +364 -0
- miso_client/utils/filter_applier.py +143 -0
- miso_client/utils/filter_parser.py +110 -0
- miso_client/utils/flask_endpoints.py +169 -0
- miso_client/utils/http_client.py +494 -262
- miso_client/utils/http_client_logging.py +352 -0
- miso_client/utils/http_client_logging_helpers.py +197 -0
- miso_client/utils/http_client_query_helpers.py +138 -0
- miso_client/utils/http_error_handler.py +92 -0
- miso_client/utils/http_log_formatter.py +115 -0
- miso_client/utils/http_log_masker.py +203 -0
- miso_client/utils/internal_http_client.py +435 -0
- miso_client/utils/jwt_tools.py +125 -16
- miso_client/utils/logger_helpers.py +206 -0
- miso_client/utils/logging_helpers.py +70 -0
- miso_client/utils/origin_validator.py +128 -0
- miso_client/utils/pagination.py +275 -0
- miso_client/utils/request_context.py +285 -0
- miso_client/utils/sensitive_fields_loader.py +116 -0
- miso_client/utils/sort.py +116 -0
- miso_client/utils/token_utils.py +114 -0
- miso_client/utils/url_validator.py +66 -0
- miso_client/utils/user_token_refresh.py +245 -0
- miso_client-3.7.2.dist-info/METADATA +1021 -0
- miso_client-3.7.2.dist-info/RECORD +68 -0
- miso_client-0.1.0.dist-info/METADATA +0 -551
- miso_client-0.1.0.dist-info/RECORD +0 -23
- {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/WHEEL +0 -0
- {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/licenses/LICENSE +0 -0
- {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1021 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: miso-client
|
|
3
|
+
Version: 3.7.2
|
|
4
|
+
Summary: Python client SDK for AI Fabrix authentication, authorization, and logging
|
|
5
|
+
Home-page: https://github.com/aifabrix/miso-client-python
|
|
6
|
+
Author: AI Fabrix Team
|
|
7
|
+
Author-email: AI Fabrix Team <team@aifabrix.ai>
|
|
8
|
+
Maintainer-email: AI Fabrix Team <team@aifabrix.ai>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
Project-URL: Homepage, https://github.com/aifabrix/miso-client-python
|
|
11
|
+
Project-URL: Documentation, https://docs.aifabrix.ai/miso-client-python
|
|
12
|
+
Project-URL: Repository, https://github.com/aifabrix/miso-client-python
|
|
13
|
+
Project-URL: Issues, https://github.com/aifabrix/miso-client-python/issues
|
|
14
|
+
Keywords: authentication,authorization,rbac,jwt,redis,logging,aifabrix,miso
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
25
|
+
Classifier: Topic :: Security
|
|
26
|
+
Classifier: Topic :: System :: Logging
|
|
27
|
+
Requires-Python: >=3.8
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: pydantic>=2.0.0
|
|
31
|
+
Requires-Dist: httpx>=0.25.0
|
|
32
|
+
Requires-Dist: redis[hiredis]>=5.0.0
|
|
33
|
+
Requires-Dist: PyJWT>=2.8.0
|
|
34
|
+
Requires-Dist: cryptography>=41.0.0
|
|
35
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
36
|
+
Provides-Extra: dev
|
|
37
|
+
Requires-Dist: pytest>=7.4.3; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-asyncio>=0.21.1; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-mock>=3.12.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-cov>=4.1.0; extra == "dev"
|
|
41
|
+
Requires-Dist: black>=23.0.0; extra == "dev"
|
|
42
|
+
Requires-Dist: isort>=5.12.0; extra == "dev"
|
|
43
|
+
Requires-Dist: mypy>=1.5.0; extra == "dev"
|
|
44
|
+
Requires-Dist: ruff>=0.1.0; extra == "dev"
|
|
45
|
+
Provides-Extra: flask
|
|
46
|
+
Requires-Dist: flask>=2.0.0; extra == "flask"
|
|
47
|
+
Provides-Extra: fastapi
|
|
48
|
+
Requires-Dist: fastapi>=0.100.0; extra == "fastapi"
|
|
49
|
+
Dynamic: author
|
|
50
|
+
Dynamic: home-page
|
|
51
|
+
Dynamic: license-file
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
|
|
54
|
+
# AI Fabrix Miso Client SDK (Python)
|
|
55
|
+
|
|
56
|
+
[](https://badge.fury.io/py/miso-client)
|
|
57
|
+
[](https://opensource.org/licenses/MIT)
|
|
58
|
+
|
|
59
|
+
The **AI Fabrix Miso Client SDK** provides authentication, authorization, and logging for Python applications integrated with the AI Fabrix platform.
|
|
60
|
+
|
|
61
|
+
## ✨ Benefits
|
|
62
|
+
|
|
63
|
+
### 🔐 Enterprise Security
|
|
64
|
+
|
|
65
|
+
### **SSO and Federated Identity**
|
|
66
|
+
|
|
67
|
+
- Single Sign-On (SSO) with Keycloak
|
|
68
|
+
- OAuth 2.0 and OpenID Connect (OIDC) support
|
|
69
|
+
- Multi-factor authentication (MFA) ready
|
|
70
|
+
- Social login integration (Google, Microsoft, etc.)
|
|
71
|
+
|
|
72
|
+
### **Centralized Access Control**
|
|
73
|
+
|
|
74
|
+
- Role-based access control (RBAC)
|
|
75
|
+
- Fine-grained permissions
|
|
76
|
+
- Dynamic policy enforcement
|
|
77
|
+
- Attribute-based access control (ABAC)
|
|
78
|
+
|
|
79
|
+
### **API Security**
|
|
80
|
+
|
|
81
|
+
- JWT token validation
|
|
82
|
+
- API key authentication
|
|
83
|
+
- Token revocation support
|
|
84
|
+
- Secure token storage
|
|
85
|
+
- Data encryption/decryption (AES-256-GCM)
|
|
86
|
+
|
|
87
|
+
### 📊 Compliance & Audit
|
|
88
|
+
|
|
89
|
+
### **ISO 27001 Compliance**
|
|
90
|
+
|
|
91
|
+
- Comprehensive audit trails for all user actions and HTTP requests
|
|
92
|
+
- Automatic data masking for all sensitive information in logs
|
|
93
|
+
- HTTP request/response audit logging with masked sensitive data
|
|
94
|
+
- Data access logging and monitoring
|
|
95
|
+
- Security event tracking
|
|
96
|
+
- Accountability and non-repudiation
|
|
97
|
+
- Configurable sensitive fields via JSON configuration
|
|
98
|
+
|
|
99
|
+
### **Regulatory Compliance**
|
|
100
|
+
|
|
101
|
+
- GDPR-ready data protection
|
|
102
|
+
- HIPAA-compliant audit logging
|
|
103
|
+
- SOC 2 audit trail requirements
|
|
104
|
+
- Industry-standard security controls
|
|
105
|
+
|
|
106
|
+
### **Audit Capabilities**
|
|
107
|
+
|
|
108
|
+
- Real-time audit event logging
|
|
109
|
+
- Immutable audit records
|
|
110
|
+
- Forensic analysis support
|
|
111
|
+
- Compliance reporting automation
|
|
112
|
+
|
|
113
|
+
### ⚡ Performance & Scalability
|
|
114
|
+
|
|
115
|
+
### **Intelligent Caching**
|
|
116
|
+
|
|
117
|
+
- Redis-based role and permission caching
|
|
118
|
+
- Generic cache service with Redis and in-memory fallback
|
|
119
|
+
- Configurable cache TTL (default: 15 minutes)
|
|
120
|
+
- Automatic cache invalidation
|
|
121
|
+
- Fallback to controller when Redis unavailable
|
|
122
|
+
|
|
123
|
+
### **High Availability**
|
|
124
|
+
|
|
125
|
+
- Automatic failover to controller
|
|
126
|
+
- Redundant infrastructure support
|
|
127
|
+
- Load balancing compatible
|
|
128
|
+
- Zero-downtime deployments
|
|
129
|
+
|
|
130
|
+
### **Optimized Network**
|
|
131
|
+
|
|
132
|
+
- Efficient API calls with caching
|
|
133
|
+
- Batch operations support
|
|
134
|
+
- Connection pooling
|
|
135
|
+
- Minimal latency
|
|
136
|
+
|
|
137
|
+
### 🛠️ Developer Experience
|
|
138
|
+
|
|
139
|
+
### **Easy Integration**
|
|
140
|
+
|
|
141
|
+
- Progressive activation (6-step setup)
|
|
142
|
+
- Works with any framework (FastAPI, Django, Flask, Starlette)
|
|
143
|
+
- Python 3.8+ support with full type hints
|
|
144
|
+
- Async/await support throughout
|
|
145
|
+
|
|
146
|
+
### **Flexible Configuration**
|
|
147
|
+
|
|
148
|
+
- Environment-based configuration
|
|
149
|
+
- Support for dev, test, and production environments
|
|
150
|
+
- Docker and Kubernetes ready
|
|
151
|
+
- CI/CD friendly
|
|
152
|
+
|
|
153
|
+
### **Observability**
|
|
154
|
+
|
|
155
|
+
- Centralized logging with correlation IDs
|
|
156
|
+
- Automatic HTTP request/response audit logging (ISO 27001 compliant)
|
|
157
|
+
- Debug logging with detailed request/response information (when `log_level='debug'`)
|
|
158
|
+
- Performance tracking and metrics
|
|
159
|
+
- Error tracking and debugging
|
|
160
|
+
- Health monitoring
|
|
161
|
+
- Automatic data masking for sensitive information in logs
|
|
162
|
+
- Configurable sensitive fields via JSON configuration
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## 🚀 Quick Start
|
|
167
|
+
|
|
168
|
+
Get your application secured in 30 seconds.
|
|
169
|
+
|
|
170
|
+
### Step 1: Install
|
|
171
|
+
|
|
172
|
+
```bash
|
|
173
|
+
pip install miso-client
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Step 2: Create `.env`
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
MISO_CLIENTID=ctrl-dev-my-app
|
|
180
|
+
MISO_CLIENTSECRET=your-secret
|
|
181
|
+
MISO_CONTROLLER_URL=http://localhost:3000
|
|
182
|
+
REDIS_HOST=localhost
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Step 3: Use It
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
from miso_client import MisoClient, load_config
|
|
189
|
+
|
|
190
|
+
client = MisoClient(load_config())
|
|
191
|
+
await client.initialize()
|
|
192
|
+
|
|
193
|
+
is_valid = await client.validate_token(token)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**That's it!** You now have authentication, roles, and logging.
|
|
197
|
+
|
|
198
|
+
→ [Full Getting Started Guide](docs/getting-started.md)
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
### Infrastructure Setup
|
|
203
|
+
|
|
204
|
+
**First time?** You'll need Keycloak and Miso Controller running.
|
|
205
|
+
|
|
206
|
+
Use the [AI Fabrix Builder](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/QUICK-START.md):
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
# Start infrastructure (Postgres, Redis)
|
|
210
|
+
aifabrix up
|
|
211
|
+
|
|
212
|
+
# Install Keycloak for authentication
|
|
213
|
+
aifabrix create keycloak --port 8082 --database --template platform
|
|
214
|
+
aifabrix build keycloak
|
|
215
|
+
aifabrix run keycloak
|
|
216
|
+
|
|
217
|
+
# Install Miso Controller
|
|
218
|
+
aifabrix create miso-controller --port 3000 --database --redis --template platform
|
|
219
|
+
aifabrix build miso-controller
|
|
220
|
+
aifabrix run miso-controller
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
→ [Infrastructure Guide](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/INFRASTRUCTURE.md)
|
|
224
|
+
|
|
225
|
+
**Already have Keycloak and Controller?** Use the Quick Start above.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## 📚 Documentation
|
|
230
|
+
|
|
231
|
+
**What happens:** Your app validates user tokens from Keycloak.
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
from miso_client import MisoClient, load_config
|
|
235
|
+
|
|
236
|
+
# Create client (loads from .env automatically)
|
|
237
|
+
client = MisoClient(load_config())
|
|
238
|
+
await client.initialize()
|
|
239
|
+
|
|
240
|
+
# Get token from request (helper method)
|
|
241
|
+
token = client.get_token(req)
|
|
242
|
+
|
|
243
|
+
if token:
|
|
244
|
+
is_valid = await client.validate_token(token)
|
|
245
|
+
if is_valid:
|
|
246
|
+
user = await client.get_user(token)
|
|
247
|
+
print('User:', user)
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**Where to get tokens?** Users authenticate via Keycloak, then your app receives JWTs in the `Authorization` header.
|
|
251
|
+
|
|
252
|
+
→ [Complete authentication example](examples/step-3-authentication.py)
|
|
253
|
+
|
|
254
|
+
---
|
|
255
|
+
|
|
256
|
+
### Step 4: Activate RBAC (Roles)
|
|
257
|
+
|
|
258
|
+
**What happens:** Check user roles to control access. Roles are cached in Redis for performance.
|
|
259
|
+
|
|
260
|
+
```python
|
|
261
|
+
from miso_client import MisoClient, load_config
|
|
262
|
+
|
|
263
|
+
# Build on Step 3 - add Redis in .env file
|
|
264
|
+
client = MisoClient(load_config())
|
|
265
|
+
await client.initialize()
|
|
266
|
+
|
|
267
|
+
token = client.get_token(req)
|
|
268
|
+
|
|
269
|
+
# Check if user has role
|
|
270
|
+
is_admin = await client.has_role(token, 'admin')
|
|
271
|
+
roles = await client.get_roles(token)
|
|
272
|
+
|
|
273
|
+
# Gate features by role
|
|
274
|
+
if is_admin:
|
|
275
|
+
# Show admin panel
|
|
276
|
+
pass
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
**Pro tip:** Without Redis, checks go to the controller. Add Redis to cache role lookups (15-minute default TTL).
|
|
280
|
+
|
|
281
|
+
→ [Complete RBAC example](examples/step-4-rbac.py)
|
|
282
|
+
→ [AI Fabrix Builder Quick Start](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/QUICK-START.md)
|
|
283
|
+
|
|
284
|
+
---
|
|
285
|
+
|
|
286
|
+
### Step 5: Activate Logging
|
|
287
|
+
|
|
288
|
+
**What happens:** Application logs are sent to the Miso Controller with client token authentication. All HTTP requests are automatically audited with ISO 27001 compliant data masking.
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from miso_client import MisoClient, load_config
|
|
292
|
+
|
|
293
|
+
# Client token is automatically managed - no API key needed
|
|
294
|
+
client = MisoClient(load_config())
|
|
295
|
+
await client.initialize()
|
|
296
|
+
|
|
297
|
+
token = client.get_token(req)
|
|
298
|
+
user = await client.get_user(token)
|
|
299
|
+
|
|
300
|
+
# Log messages
|
|
301
|
+
await client.log.info('User accessed dashboard', {'userId': user.id if user else None})
|
|
302
|
+
await client.log.error('Operation failed', {'error': str(err)})
|
|
303
|
+
await client.log.warn('Unusual activity', {'details': '...'})
|
|
304
|
+
|
|
305
|
+
# HTTP requests are automatically audited
|
|
306
|
+
# All sensitive data is automatically masked before logging
|
|
307
|
+
result = await client.http_client.get('/api/users')
|
|
308
|
+
# This automatically creates an audit log: http.request.GET with masked sensitive data
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**What happens to logs?** They're sent to the Miso Controller for centralized monitoring and analysis. Client token is automatically included. Audit logs are automatically batched using `AuditLogQueue` for improved performance (configurable via `AuditConfig`).
|
|
312
|
+
|
|
313
|
+
**ISO 27001 Compliance:** All HTTP requests are automatically audited with sensitive data masked. Configure audit logging behavior using `AuditConfig`:
|
|
314
|
+
|
|
315
|
+
- **Audit Levels**: Choose from `minimal`, `standard`, `detailed`, or `full` (default: `detailed`)
|
|
316
|
+
- `minimal`: Only metadata, no masking
|
|
317
|
+
- `standard`: Metadata + basic context
|
|
318
|
+
- `detailed`: Full context with request/response sizes (default)
|
|
319
|
+
- `full`: Complete audit trail with all available data
|
|
320
|
+
- **Performance Optimizations**:
|
|
321
|
+
- Response body truncation based on `maxResponseSize` configuration (default: 10000 bytes)
|
|
322
|
+
- Size-based masking skip for large objects (prevents performance degradation)
|
|
323
|
+
- Automatic batching via `AuditLogQueue` reduces HTTP overhead for high-volume logging
|
|
324
|
+
- Set `log_level='debug'` to enable detailed request/response logging (all sensitive data is still masked).
|
|
325
|
+
|
|
326
|
+
→ [Complete logging example](examples/step-5-logging.py)
|
|
327
|
+
→ [Logging Reference](docs/api-reference.md#logger-service)
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
### Step 6: Activate Audit
|
|
332
|
+
|
|
333
|
+
**What happens:** Create audit trails for compliance and security monitoring.
|
|
334
|
+
|
|
335
|
+
```python
|
|
336
|
+
from miso_client import MisoClient, load_config
|
|
337
|
+
|
|
338
|
+
# Complete configuration (all in .env)
|
|
339
|
+
client = MisoClient(load_config())
|
|
340
|
+
await client.initialize()
|
|
341
|
+
|
|
342
|
+
token = client.get_token(req)
|
|
343
|
+
is_valid = await client.validate_token(token)
|
|
344
|
+
can_edit = await client.has_permission(token, 'edit:content')
|
|
345
|
+
user = await client.get_user(token)
|
|
346
|
+
|
|
347
|
+
# Audit: User actions
|
|
348
|
+
await client.log.audit('user.login', 'authentication', {
|
|
349
|
+
'userId': user.id if user else None,
|
|
350
|
+
'ip': req.get('ip', ''),
|
|
351
|
+
'userAgent': req.get('headers', {}).get('user-agent', ''),
|
|
352
|
+
})
|
|
353
|
+
|
|
354
|
+
# Audit: Content changes
|
|
355
|
+
await client.log.audit('post.created', 'content', {
|
|
356
|
+
'userId': user.id if user else None,
|
|
357
|
+
'postId': 'post-123',
|
|
358
|
+
'postTitle': req.get('body', {}).get('title', ''),
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
# Audit: Permission checks
|
|
362
|
+
await client.log.audit('access.denied', 'authorization', {
|
|
363
|
+
'userId': user.id if user else None,
|
|
364
|
+
'requiredPermission': 'edit:content',
|
|
365
|
+
'resource': 'posts',
|
|
366
|
+
})
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
**What to audit:** Login/logout, permission checks, content creation/deletion, role changes, sensitive operations.
|
|
370
|
+
|
|
371
|
+
→ [Complete audit example](examples/step-6-audit.py)
|
|
372
|
+
→ [Best Practices](docs/getting-started.md#common-patterns)
|
|
373
|
+
|
|
374
|
+
---
|
|
375
|
+
|
|
376
|
+
### Encryption and Caching
|
|
377
|
+
|
|
378
|
+
**What happens:** Use encryption for sensitive data and generic caching for improved performance.
|
|
379
|
+
|
|
380
|
+
```python
|
|
381
|
+
from miso_client import MisoClient, load_config
|
|
382
|
+
|
|
383
|
+
client = MisoClient(load_config())
|
|
384
|
+
await client.initialize()
|
|
385
|
+
|
|
386
|
+
# Encryption (requires ENCRYPTION_KEY in .env)
|
|
387
|
+
encrypted = client.encrypt('sensitive-data')
|
|
388
|
+
decrypted = client.decrypt(encrypted)
|
|
389
|
+
print('Decrypted:', decrypted)
|
|
390
|
+
|
|
391
|
+
# Generic caching (automatically uses Redis if available, falls back to memory)
|
|
392
|
+
await client.cache_set('user:123', {'name': 'John', 'age': 30}, 600) # 10 minutes TTL
|
|
393
|
+
user = await client.cache_get('user:123')
|
|
394
|
+
if user:
|
|
395
|
+
print('Cached user:', user)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
**Configuration:**
|
|
399
|
+
|
|
400
|
+
```bash
|
|
401
|
+
# Add to .env
|
|
402
|
+
ENCRYPTION_KEY=your-32-byte-encryption-key
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
→ [API Reference](docs/api-reference.md#encryption-methods)
|
|
406
|
+
→ [Cache Methods](docs/api-reference.md#cache-methods)
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
### Testing with API Key
|
|
411
|
+
|
|
412
|
+
**What happens:** When `API_KEY` is set in your `.env` file, you can authenticate requests using the API key as a bearer token, bypassing OAuth2 authentication. This is useful for testing without setting up Keycloak.
|
|
413
|
+
|
|
414
|
+
```python
|
|
415
|
+
from miso_client import MisoClient, load_config
|
|
416
|
+
|
|
417
|
+
client = MisoClient(load_config())
|
|
418
|
+
await client.initialize()
|
|
419
|
+
|
|
420
|
+
# Use API_KEY as bearer token (for testing only)
|
|
421
|
+
api_key_token = "your-api-key-from-env"
|
|
422
|
+
is_valid = await client.validate_token(api_key_token)
|
|
423
|
+
# Returns True if token matches API_KEY from .env
|
|
424
|
+
|
|
425
|
+
user = await client.get_user(api_key_token)
|
|
426
|
+
# Returns None (API key auth doesn't provide user info)
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
**Configuration:**
|
|
430
|
+
|
|
431
|
+
```bash
|
|
432
|
+
# Add to .env for testing
|
|
433
|
+
API_KEY=your-test-api-key-here
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Important:**
|
|
437
|
+
|
|
438
|
+
- API_KEY authentication bypasses OAuth2 validation completely
|
|
439
|
+
- User information methods (`get_user()`, `get_user_info()`) return `None` when using API_KEY
|
|
440
|
+
- Token validation returns `True` if the bearer token matches the configured `API_KEY`
|
|
441
|
+
- This feature is intended for testing and development only
|
|
442
|
+
|
|
443
|
+
---
|
|
444
|
+
|
|
445
|
+
## 🔧 Configuration
|
|
446
|
+
|
|
447
|
+
```python
|
|
448
|
+
from miso_client import MisoClientConfig, RedisConfig, AuditConfig
|
|
449
|
+
|
|
450
|
+
config = MisoClientConfig(
|
|
451
|
+
controller_url="http://localhost:3000", # Required: Controller URL
|
|
452
|
+
client_id="ctrl-dev-my-app", # Required: Client ID
|
|
453
|
+
client_secret="your-secret", # Required: Client secret
|
|
454
|
+
redis=RedisConfig( # Optional: For caching
|
|
455
|
+
host="localhost",
|
|
456
|
+
port=6379,
|
|
457
|
+
),
|
|
458
|
+
log_level="info", # Optional: 'debug' | 'info' | 'warn' | 'error'
|
|
459
|
+
# Set to 'debug' for detailed HTTP request/response logging
|
|
460
|
+
api_key="your-test-api-key", # Optional: API key for testing (bypasses OAuth2)
|
|
461
|
+
cache={ # Optional: Cache TTL settings
|
|
462
|
+
"role_ttl": 900, # Role cache TTL (default: 900s)
|
|
463
|
+
"permission_ttl": 900, # Permission cache TTL (default: 900s)
|
|
464
|
+
},
|
|
465
|
+
audit=AuditConfig( # Optional: Audit logging configuration
|
|
466
|
+
enabled=True, # Enable/disable audit logging (default: true)
|
|
467
|
+
level="detailed", # Audit detail level: 'minimal' | 'standard' | 'detailed' | 'full' (default: 'detailed')
|
|
468
|
+
maxResponseSize=10000, # Truncate responses larger than this in bytes (default: 10000)
|
|
469
|
+
maxMaskingSize=50000, # Skip masking for objects larger than this in bytes (default: 50000)
|
|
470
|
+
batchSize=10, # Batch size for queued logs (default: 10)
|
|
471
|
+
batchInterval=100, # Flush interval in milliseconds (default: 100)
|
|
472
|
+
skipEndpoints=None # Array of endpoint patterns to exclude from audit logging
|
|
473
|
+
)
|
|
474
|
+
)
|
|
475
|
+
```
|
|
476
|
+
|
|
477
|
+
**Recommended:** Use `load_config()` to load from `.env` file automatically.
|
|
478
|
+
|
|
479
|
+
**ISO 27001 Data Masking Configuration:**
|
|
480
|
+
|
|
481
|
+
Sensitive fields are configured via `miso_client/utils/sensitive_fields_config.json`. You can customize this by:
|
|
482
|
+
|
|
483
|
+
1. Setting `MISO_SENSITIVE_FIELDS_CONFIG` environment variable to point to a custom JSON file
|
|
484
|
+
2. Using `DataMasker.set_config_path()` to set a custom path programmatically
|
|
485
|
+
|
|
486
|
+
The default configuration includes ISO 27001 compliant sensitive fields:
|
|
487
|
+
|
|
488
|
+
- Authentication: password, token, secret, key, auth, authorization
|
|
489
|
+
- PII: ssn, creditcard, cc, cvv, pin, otp
|
|
490
|
+
- Security: apikey, accesstoken, refreshtoken, privatekey, secretkey, cookie, session
|
|
491
|
+
|
|
492
|
+
**Audit Logging Configuration:**
|
|
493
|
+
|
|
494
|
+
Configure audit logging behavior using `AuditConfig` (see Configuration section above):
|
|
495
|
+
|
|
496
|
+
- **Audit Levels**: Control detail level (`minimal`, `standard`, `detailed`, `full`)
|
|
497
|
+
- **Response Truncation**: Configure `maxResponseSize` to truncate large responses (default: 10000 bytes)
|
|
498
|
+
- **Performance**: Set `maxMaskingSize` to skip masking for very large objects (default: 50000 bytes)
|
|
499
|
+
- **Batching**: Configure `batchSize` and `batchInterval` for audit log queuing (reduces HTTP overhead)
|
|
500
|
+
|
|
501
|
+
→ [Complete Configuration Reference](docs/configuration.md)
|
|
502
|
+
|
|
503
|
+
---
|
|
504
|
+
|
|
505
|
+
## 📚 Read more
|
|
506
|
+
|
|
507
|
+
- **[Getting Started](docs/getting-started.md)** - Detailed setup guide
|
|
508
|
+
- **[API Reference](docs/api-reference.md)** - Complete API documentation
|
|
509
|
+
- **[Configuration](docs/configuration.md)** - Configuration options
|
|
510
|
+
- **[Examples](docs/examples.md)** - Framework-specific examples
|
|
511
|
+
- **[Troubleshooting](docs/troubleshooting.md)** - Common issues and solutions
|
|
512
|
+
|
|
513
|
+
---
|
|
514
|
+
|
|
515
|
+
## 🏗️ Architecture
|
|
516
|
+
|
|
517
|
+
The SDK consists of five core services:
|
|
518
|
+
|
|
519
|
+
- **AuthService** - Token validation and user authentication
|
|
520
|
+
- **RoleService** - Role management with Redis caching
|
|
521
|
+
- **PermissionService** - Fine-grained permissions
|
|
522
|
+
- **LoggerService** - Centralized logging with API key authentication
|
|
523
|
+
- **RedisService** - Caching and queue management (optional)
|
|
524
|
+
|
|
525
|
+
### HTTP Client Architecture
|
|
526
|
+
|
|
527
|
+
The SDK uses a two-layer HTTP client architecture for ISO 27001 compliance:
|
|
528
|
+
|
|
529
|
+
- **InternalHttpClient** - Core HTTP functionality with automatic client token management (internal)
|
|
530
|
+
- **HttpClient** - Public wrapper that adds automatic ISO 27001 compliant audit and debug logging
|
|
531
|
+
|
|
532
|
+
**Features:**
|
|
533
|
+
|
|
534
|
+
- Automatic audit logging for all HTTP requests (`http.request.{METHOD}`)
|
|
535
|
+
- Configurable audit levels (`minimal`, `standard`, `detailed`, `full`) via `AuditConfig`
|
|
536
|
+
- Debug logging when `log_level === 'debug'` with detailed request/response information
|
|
537
|
+
- Automatic data masking using `DataMasker` before logging (ISO 27001 compliant)
|
|
538
|
+
- Sensitive endpoints (`/api/logs`, `/api/auth/token`) are excluded from audit logging to prevent infinite loops
|
|
539
|
+
- All sensitive data (headers, bodies, query params) is automatically masked before logging
|
|
540
|
+
- `AuditLogQueue` integration for automatic batching of audit logs (reduces HTTP overhead)
|
|
541
|
+
- Performance optimizations: response body truncation and size-based masking skip for large objects
|
|
542
|
+
|
|
543
|
+
**ISO 27001 Compliance:**
|
|
544
|
+
|
|
545
|
+
- All request headers are masked (Authorization, x-client-token, Cookie, etc.)
|
|
546
|
+
- All request bodies are recursively masked for sensitive fields (password, token, secret, SSN, etc.)
|
|
547
|
+
- All response bodies are masked and truncated based on `maxResponseSize` configuration (default: 10000 bytes)
|
|
548
|
+
- Query parameters are automatically masked
|
|
549
|
+
- Error messages are masked if they contain sensitive data
|
|
550
|
+
- Sensitive fields configuration can be customized via `sensitive_fields_config.json`
|
|
551
|
+
- Configurable audit levels control the detail level of audit logs (minimal, standard, detailed, full)
|
|
552
|
+
|
|
553
|
+
→ [Architecture Details](docs/api-reference.md#architecture)
|
|
554
|
+
|
|
555
|
+
---
|
|
556
|
+
|
|
557
|
+
## 🌐 Setup Your Application
|
|
558
|
+
|
|
559
|
+
**First time setup?** Use the AI Fabrix Builder:
|
|
560
|
+
|
|
561
|
+
1. **Create your app:**
|
|
562
|
+
|
|
563
|
+
```bash
|
|
564
|
+
aifabrix create myapp --port 3000 --database --language python
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
2. **Login to controller:**
|
|
568
|
+
|
|
569
|
+
```bash
|
|
570
|
+
aifabrix login
|
|
571
|
+
```
|
|
572
|
+
|
|
573
|
+
3. **Register your application:**
|
|
574
|
+
|
|
575
|
+
```bash
|
|
576
|
+
aifabrix app register myapp --environment dev
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
4. **Start development** and then deploy to Docker or Azure.
|
|
580
|
+
|
|
581
|
+
→ [Full Quick Start Guide](https://github.com/esystemsdev/aifabrix-builder/blob/main/docs/QUICK-START.md)
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## 💡 Next Steps
|
|
586
|
+
|
|
587
|
+
### Learn More
|
|
588
|
+
|
|
589
|
+
- [FastAPI Integration](docs/examples.md#fastapi-integration) - Protect API routes
|
|
590
|
+
- [Django Middleware](docs/examples.md#django-middleware) - Django integration
|
|
591
|
+
- [Flask Decorators](docs/examples.md#flask-decorators) - Decorator-based auth
|
|
592
|
+
- [Error Handling](docs/examples.md#error-handling) - Best practices
|
|
593
|
+
|
|
594
|
+
---
|
|
595
|
+
|
|
596
|
+
### Structured Error Responses
|
|
597
|
+
|
|
598
|
+
**What happens:** The SDK automatically parses structured error responses from the API (RFC 7807-style format) and makes them available through the `MisoClientError` and `ApiErrorException` exceptions.
|
|
599
|
+
|
|
600
|
+
```python
|
|
601
|
+
from miso_client import MisoClient, MisoClientError, ApiErrorException, ErrorResponse, load_config, handleApiError
|
|
602
|
+
|
|
603
|
+
client = MisoClient(load_config())
|
|
604
|
+
await client.initialize()
|
|
605
|
+
|
|
606
|
+
try:
|
|
607
|
+
result = await client.http_client.get("/api/some-endpoint")
|
|
608
|
+
except MisoClientError as e:
|
|
609
|
+
# Check if structured error response is available
|
|
610
|
+
if e.error_response:
|
|
611
|
+
print(f"Error Type: {e.error_response.type}")
|
|
612
|
+
print(f"Error Title: {e.error_response.title}")
|
|
613
|
+
print(f"Status Code: {e.error_response.statusCode}")
|
|
614
|
+
print(f"Errors: {e.error_response.errors}")
|
|
615
|
+
print(f"Instance: {e.error_response.instance}")
|
|
616
|
+
else:
|
|
617
|
+
# Fallback to traditional error handling
|
|
618
|
+
print(f"Error: {e.message}")
|
|
619
|
+
print(f"Status Code: {e.status_code}")
|
|
620
|
+
print(f"Error Body: {e.error_body}")
|
|
621
|
+
|
|
622
|
+
# Using handleApiError() for structured error handling
|
|
623
|
+
try:
|
|
624
|
+
response_data = {"errors": ["Validation failed"], "type": "/Errors/Validation", "title": "Validation Error", "statusCode": 422}
|
|
625
|
+
error = handleApiError(response_data, 422, "/api/endpoint")
|
|
626
|
+
# handleApiError() returns ApiErrorException (extends MisoClientError)
|
|
627
|
+
if isinstance(error, ApiErrorException):
|
|
628
|
+
print(f"Structured Error: {error.error_response.title}")
|
|
629
|
+
except ApiErrorException as e:
|
|
630
|
+
# ApiErrorException provides better structured error information
|
|
631
|
+
print(f"API Error: {e.error_response.title}")
|
|
632
|
+
print(f"Errors: {e.error_response.errors}")
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
**Error Response Structure:**
|
|
636
|
+
|
|
637
|
+
The `ErrorResponse` model follows RFC 7807-style format:
|
|
638
|
+
|
|
639
|
+
```json
|
|
640
|
+
{
|
|
641
|
+
"errors": [
|
|
642
|
+
"The user has provided input that the browser is unable to convert.",
|
|
643
|
+
"There are multiple rows in the database for the same value"
|
|
644
|
+
],
|
|
645
|
+
"type": "/Errors/Bad Input",
|
|
646
|
+
"title": "Bad Request",
|
|
647
|
+
"statusCode": 400,
|
|
648
|
+
"instance": "/OpenApi/rest/Xzy"
|
|
649
|
+
}
|
|
650
|
+
```
|
|
651
|
+
|
|
652
|
+
**Features:**
|
|
653
|
+
|
|
654
|
+
- **Automatic Parsing**: Structured error responses are automatically parsed from HTTP responses
|
|
655
|
+
- **ApiErrorException**: New exception class (extends `MisoClientError`) for better structured error handling
|
|
656
|
+
- `handleApiError()` returns `ApiErrorException` with structured error response support
|
|
657
|
+
- Legacy `handle_api_error_snake_case()` still returns `MisoClientError` for backward compatibility
|
|
658
|
+
- **Backward Compatible**: Falls back to traditional error handling when structured format is not available
|
|
659
|
+
- **Type Safety**: Full type hints with Pydantic models for reliable error handling
|
|
660
|
+
- **Generic Interface**: `ErrorResponse` model can be reused across different applications
|
|
661
|
+
- **Instance URI**: Automatically extracted from request URL if not provided in response
|
|
662
|
+
|
|
663
|
+
**Using ErrorResponse directly:**
|
|
664
|
+
|
|
665
|
+
```python
|
|
666
|
+
from miso_client import ErrorResponse
|
|
667
|
+
|
|
668
|
+
# Create ErrorResponse from dict
|
|
669
|
+
error_data = {
|
|
670
|
+
"errors": ["Validation failed"],
|
|
671
|
+
"type": "/Errors/Validation",
|
|
672
|
+
"title": "Validation Error",
|
|
673
|
+
"statusCode": 422,
|
|
674
|
+
"instance": "/api/endpoint"
|
|
675
|
+
}
|
|
676
|
+
error_response = ErrorResponse(**error_data)
|
|
677
|
+
|
|
678
|
+
# Access fields
|
|
679
|
+
print(error_response.errors) # ["Validation failed"]
|
|
680
|
+
print(error_response.type) # "/Errors/Validation"
|
|
681
|
+
print(error_response.title) # "Validation Error"
|
|
682
|
+
print(error_response.statusCode) # 422
|
|
683
|
+
print(error_response.instance) # "/api/endpoint"
|
|
684
|
+
```
|
|
685
|
+
|
|
686
|
+
---
|
|
687
|
+
|
|
688
|
+
### Pagination, Filtering, and Sorting Utilities
|
|
689
|
+
|
|
690
|
+
**What happens:** The SDK provides reusable utilities for pagination, filtering, sorting, and error handling that work with any API endpoint.
|
|
691
|
+
|
|
692
|
+
#### Pagination
|
|
693
|
+
|
|
694
|
+
**Pagination Parameters:**
|
|
695
|
+
|
|
696
|
+
- `page`: Page number (1-based, defaults to 1)
|
|
697
|
+
- `page_size`: Number of items per page (defaults to 20)
|
|
698
|
+
|
|
699
|
+
```python
|
|
700
|
+
from miso_client import (
|
|
701
|
+
parse_pagination_params,
|
|
702
|
+
parsePaginationParams, # camelCase alternative
|
|
703
|
+
create_paginated_list_response,
|
|
704
|
+
createPaginatedListResponse, # camelCase alternative
|
|
705
|
+
PaginatedListResponse,
|
|
706
|
+
)
|
|
707
|
+
|
|
708
|
+
# Parse pagination from query parameters (snake_case - returns tuple)
|
|
709
|
+
params = {"page": "1", "page_size": "20"}
|
|
710
|
+
current_page, page_size = parse_pagination_params(params)
|
|
711
|
+
|
|
712
|
+
# Or use camelCase function (returns dict with currentPage/pageSize keys)
|
|
713
|
+
pagination = parsePaginationParams(params)
|
|
714
|
+
# Returns: {"currentPage": 1, "pageSize": 20}
|
|
715
|
+
|
|
716
|
+
# Create paginated response
|
|
717
|
+
items = [{"id": 1}, {"id": 2}]
|
|
718
|
+
response = create_paginated_list_response(
|
|
719
|
+
items,
|
|
720
|
+
total_items=120,
|
|
721
|
+
current_page=1,
|
|
722
|
+
page_size=20,
|
|
723
|
+
type="item"
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
# Response structure:
|
|
727
|
+
# {
|
|
728
|
+
# "meta": {
|
|
729
|
+
# "total_items": 120,
|
|
730
|
+
# "current_page": 1,
|
|
731
|
+
# "page_size": 20,
|
|
732
|
+
# "type": "item"
|
|
733
|
+
# },
|
|
734
|
+
# "data": [{"id": 1}, {"id": 2}]
|
|
735
|
+
# }
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
#### Filtering
|
|
739
|
+
|
|
740
|
+
**Filter Operators:** `eq`, `neq`, `in`, `nin`, `gt`, `lt`, `gte`, `lte`, `contains`, `like`
|
|
741
|
+
|
|
742
|
+
**Filter Format:** `field:op:value` (e.g., `status:eq:active`)
|
|
743
|
+
|
|
744
|
+
```python
|
|
745
|
+
from miso_client import FilterBuilder, parse_filter_params, build_query_string
|
|
746
|
+
|
|
747
|
+
# Dynamic filter building with FilterBuilder
|
|
748
|
+
filter_builder = FilterBuilder() \
|
|
749
|
+
.add('status', 'eq', 'active') \
|
|
750
|
+
.add('region', 'in', ['eu', 'us']) \
|
|
751
|
+
.add('created_at', 'gte', '2024-01-01')
|
|
752
|
+
|
|
753
|
+
# Get query string
|
|
754
|
+
query_string = filter_builder.to_query_string()
|
|
755
|
+
# Returns: "filter=status:eq:active&filter=region:in:eu,us&filter=created_at:gte:2024-01-01"
|
|
756
|
+
|
|
757
|
+
# Parse existing filter parameters
|
|
758
|
+
params = {'filter': ['status:eq:active', 'region:in:eu,us']}
|
|
759
|
+
filters = parse_filter_params(params)
|
|
760
|
+
# Returns: [FilterOption(field='status', op='eq', value='active'), ...]
|
|
761
|
+
|
|
762
|
+
# Use with HTTP client
|
|
763
|
+
response = await client.http_client.get_with_filters(
|
|
764
|
+
'/api/items',
|
|
765
|
+
filter_builder=filter_builder
|
|
766
|
+
)
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
**Building Complete Filter Queries:**
|
|
770
|
+
|
|
771
|
+
```python
|
|
772
|
+
from miso_client import FilterQuery, FilterOption, build_query_string
|
|
773
|
+
|
|
774
|
+
# Create filter query with filters, sort, pagination, and fields
|
|
775
|
+
filter_query = FilterQuery(
|
|
776
|
+
filters=[
|
|
777
|
+
FilterOption(field='status', op='eq', value='active'),
|
|
778
|
+
FilterOption(field='region', op='in', value=['eu', 'us'])
|
|
779
|
+
],
|
|
780
|
+
sort=['-updated_at', 'created_at'],
|
|
781
|
+
page=1,
|
|
782
|
+
page_size=20,
|
|
783
|
+
fields=['id', 'name', 'status']
|
|
784
|
+
)
|
|
785
|
+
|
|
786
|
+
# Build query string
|
|
787
|
+
query_string = build_query_string(filter_query)
|
|
788
|
+
```
|
|
789
|
+
|
|
790
|
+
#### Sorting
|
|
791
|
+
|
|
792
|
+
**Sort Format:** `-field` for descending, `field` for ascending (e.g., `-updated_at`, `created_at`)
|
|
793
|
+
|
|
794
|
+
```python
|
|
795
|
+
from miso_client import parse_sort_params, build_sort_string, SortOption
|
|
796
|
+
|
|
797
|
+
# Parse sort parameters
|
|
798
|
+
params = {'sort': '-updated_at'}
|
|
799
|
+
sort_options = parse_sort_params(params)
|
|
800
|
+
# Returns: [SortOption(field='updated_at', order='desc')]
|
|
801
|
+
|
|
802
|
+
# Parse multiple sorts
|
|
803
|
+
params = {'sort': ['-updated_at', 'created_at']}
|
|
804
|
+
sort_options = parse_sort_params(params)
|
|
805
|
+
# Returns: [
|
|
806
|
+
# SortOption(field='updated_at', order='desc'),
|
|
807
|
+
# SortOption(field='created_at', order='asc')
|
|
808
|
+
# ]
|
|
809
|
+
|
|
810
|
+
# Build sort string
|
|
811
|
+
sort_options = [
|
|
812
|
+
SortOption(field='updated_at', order='desc'),
|
|
813
|
+
SortOption(field='created_at', order='asc')
|
|
814
|
+
]
|
|
815
|
+
sort_string = build_sort_string(sort_options)
|
|
816
|
+
# Returns: "-updated_at,created_at"
|
|
817
|
+
```
|
|
818
|
+
|
|
819
|
+
#### Combined Usage
|
|
820
|
+
|
|
821
|
+
**Pagination + Filter + Sort:**
|
|
822
|
+
|
|
823
|
+
```python
|
|
824
|
+
from miso_client import (
|
|
825
|
+
FilterBuilder,
|
|
826
|
+
FilterQuery,
|
|
827
|
+
build_query_string,
|
|
828
|
+
parse_pagination_params,
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
# Build filters
|
|
832
|
+
filter_builder = FilterBuilder() \
|
|
833
|
+
.add('status', 'eq', 'active') \
|
|
834
|
+
.add('region', 'in', ['eu', 'us'])
|
|
835
|
+
|
|
836
|
+
# Parse pagination
|
|
837
|
+
params = {'page': '1', 'page_size': '20'}
|
|
838
|
+
current_page, page_size = parse_pagination_params(params)
|
|
839
|
+
|
|
840
|
+
# Create complete query
|
|
841
|
+
filter_query = FilterQuery(
|
|
842
|
+
filters=filter_builder.build(),
|
|
843
|
+
sort=['-updated_at'],
|
|
844
|
+
page=current_page,
|
|
845
|
+
page_size=page_size
|
|
846
|
+
)
|
|
847
|
+
|
|
848
|
+
# Build query string
|
|
849
|
+
query_string = build_query_string(filter_query)
|
|
850
|
+
|
|
851
|
+
# Use with HTTP client
|
|
852
|
+
response = await client.http_client.get_with_filters(
|
|
853
|
+
'/api/items',
|
|
854
|
+
filter_builder=filter_builder,
|
|
855
|
+
params={'page': current_page, 'page_size': page_size}
|
|
856
|
+
)
|
|
857
|
+
```
|
|
858
|
+
|
|
859
|
+
**Or use pagination helper:**
|
|
860
|
+
|
|
861
|
+
```python
|
|
862
|
+
# Get paginated response
|
|
863
|
+
response = await client.http_client.get_paginated(
|
|
864
|
+
'/api/items',
|
|
865
|
+
page=1,
|
|
866
|
+
page_size=20
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
# Response is automatically parsed as PaginatedListResponse
|
|
870
|
+
print(response.meta.total_items) # 120
|
|
871
|
+
print(response.meta.current_page) # 1
|
|
872
|
+
print(len(response.data)) # 25
|
|
873
|
+
```
|
|
874
|
+
|
|
875
|
+
#### Metadata Filter Integration
|
|
876
|
+
|
|
877
|
+
**Working with `/metadata/filter` endpoint:**
|
|
878
|
+
|
|
879
|
+
```python
|
|
880
|
+
# Get metadata filters from endpoint
|
|
881
|
+
metadata_response = await client.http_client.post(
|
|
882
|
+
"/api/v1/metadata/filter",
|
|
883
|
+
{"documentStorageKey": "my-doc-storage"}
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
# Convert AccessFieldFilter to FilterBuilder
|
|
887
|
+
filter_builder = FilterBuilder()
|
|
888
|
+
for access_filter in metadata_response.mandatoryFilters:
|
|
889
|
+
filter_builder.add(access_filter.field, 'in', access_filter.values)
|
|
890
|
+
|
|
891
|
+
# Use with query utilities
|
|
892
|
+
query_string = filter_builder.to_query_string()
|
|
893
|
+
|
|
894
|
+
# Apply to API requests
|
|
895
|
+
response = await client.http_client.get_with_filters(
|
|
896
|
+
'/api/items',
|
|
897
|
+
filter_builder=filter_builder
|
|
898
|
+
)
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
**Features:**
|
|
902
|
+
|
|
903
|
+
- **Snake_case Convention**: All utilities use snake_case to match Miso/Dataplane API
|
|
904
|
+
- **camelCase Alternatives**: camelCase function names are available for all utilities (backward compatible)
|
|
905
|
+
- `parsePaginationParams()` - Returns dict with `currentPage`/`pageSize` keys (alias: `parse_pagination_params()`)
|
|
906
|
+
- `createMetaObject()` - Creates `Meta` objects with camelCase fields (alias: `create_meta_object()`)
|
|
907
|
+
- `applyPaginationToArray()` - Applies pagination to arrays (alias: `apply_pagination_to_array()`)
|
|
908
|
+
- `createPaginatedListResponse()` - Creates paginated list responses (alias: `create_paginated_list_response()`)
|
|
909
|
+
- `transformError()` - Transforms error dictionaries to `ErrorResponse` objects (alias: `transform_error_to_snake_case()`)
|
|
910
|
+
- `handleApiError()` - Creates `ApiErrorException` from API error responses (alias: `handle_api_error_snake_case()`)
|
|
911
|
+
- **Type Safety**: Full type hints with Pydantic models
|
|
912
|
+
- **Dynamic Filtering**: FilterBuilder supports method chaining for complex filters
|
|
913
|
+
- **Local Testing**: `apply_filters()` and `apply_pagination_to_array()` for local filtering/pagination in tests
|
|
914
|
+
- **URL Encoding**: Automatic URL encoding for field names and values
|
|
915
|
+
- **Backward Compatible**: Works alongside existing HTTP client methods
|
|
916
|
+
|
|
917
|
+
---
|
|
918
|
+
|
|
919
|
+
### Common Tasks
|
|
920
|
+
|
|
921
|
+
**Add authentication middleware (FastAPI):**
|
|
922
|
+
|
|
923
|
+
```python
|
|
924
|
+
from fastapi import Depends, HTTPException, Security
|
|
925
|
+
from fastapi.security import HTTPBearer
|
|
926
|
+
from miso_client import MisoClient
|
|
927
|
+
|
|
928
|
+
security = HTTPBearer()
|
|
929
|
+
client = MisoClient(load_config())
|
|
930
|
+
|
|
931
|
+
async def get_current_user(credentials = Security(security)):
|
|
932
|
+
token = credentials.credentials
|
|
933
|
+
is_valid = await client.validate_token(token)
|
|
934
|
+
if not is_valid:
|
|
935
|
+
raise HTTPException(status_code=401, detail="Invalid token")
|
|
936
|
+
return await client.get_user(token)
|
|
937
|
+
```
|
|
938
|
+
|
|
939
|
+
**Protect routes by role (FastAPI):**
|
|
940
|
+
|
|
941
|
+
```python
|
|
942
|
+
@app.get('/admin')
|
|
943
|
+
async def admin_panel(user = Depends(get_current_user), credentials = Security(security)):
|
|
944
|
+
token = credentials.credentials
|
|
945
|
+
is_admin = await client.has_role(token, 'admin')
|
|
946
|
+
if not is_admin:
|
|
947
|
+
raise HTTPException(status_code=403, detail="Forbidden")
|
|
948
|
+
|
|
949
|
+
# Admin only code
|
|
950
|
+
return {"message": "Admin panel"}
|
|
951
|
+
```
|
|
952
|
+
|
|
953
|
+
**Use environment variables:**
|
|
954
|
+
|
|
955
|
+
```bash
|
|
956
|
+
MISO_CLIENTID=ctrl-dev-my-app
|
|
957
|
+
MISO_CLIENTSECRET=your-secret
|
|
958
|
+
MISO_CONTROLLER_URL=http://localhost:3000
|
|
959
|
+
REDIS_HOST=localhost
|
|
960
|
+
REDIS_PORT=6379
|
|
961
|
+
MISO_LOG_LEVEL=info
|
|
962
|
+
API_KEY=your-test-api-key # Optional: For testing (bypasses OAuth2)
|
|
963
|
+
```
|
|
964
|
+
|
|
965
|
+
---
|
|
966
|
+
|
|
967
|
+
## 🐛 Troubleshooting
|
|
968
|
+
|
|
969
|
+
**"Cannot connect to controller"**
|
|
970
|
+
→ Verify `controllerUrl` is correct and accessible
|
|
971
|
+
→ Check network connectivity
|
|
972
|
+
|
|
973
|
+
**"Redis connection failed"**
|
|
974
|
+
→ SDK falls back to controller-only mode (slower but works)
|
|
975
|
+
→ Fix: `aifabrix up` to start Redis
|
|
976
|
+
|
|
977
|
+
**"Client token fetch failed"**
|
|
978
|
+
→ Check `MISO_CLIENTID` and `MISO_CLIENTSECRET` are correct
|
|
979
|
+
→ Verify credentials are configured in controller
|
|
980
|
+
→ Ensure `ENCRYPTION_KEY` environment variable is set (required for encryption service)
|
|
981
|
+
|
|
982
|
+
**"Token validation fails"**
|
|
983
|
+
→ Ensure Keycloak is running and configured correctly
|
|
984
|
+
→ Verify token is from correct Keycloak instance
|
|
985
|
+
→ Check that `python-dotenv` is installed if using `.env` files
|
|
986
|
+
|
|
987
|
+
→ [More Help](docs/troubleshooting.md)
|
|
988
|
+
|
|
989
|
+
---
|
|
990
|
+
|
|
991
|
+
## 📦 Installation
|
|
992
|
+
|
|
993
|
+
```bash
|
|
994
|
+
# pip
|
|
995
|
+
pip install miso-client
|
|
996
|
+
|
|
997
|
+
# Development mode
|
|
998
|
+
pip install -e .
|
|
999
|
+
|
|
1000
|
+
# With dev dependencies
|
|
1001
|
+
pip install "miso-client[dev]"
|
|
1002
|
+
```
|
|
1003
|
+
|
|
1004
|
+
---
|
|
1005
|
+
|
|
1006
|
+
## 🔗 Links
|
|
1007
|
+
|
|
1008
|
+
- **GitHub Repository**: [https://github.com/esystemsdev/aifabrix-miso-client-python](https://github.com/esystemsdev/aifabrix-miso-client-python)
|
|
1009
|
+
- **PyPI Package**: [https://pypi.org/project/miso-client/](https://pypi.org/project/miso-client/)
|
|
1010
|
+
- **Builder Documentation**: [https://github.com/esystemsdev/aifabrix-builder](https://github.com/esystemsdev/aifabrix-builder)
|
|
1011
|
+
- **Issues**: [https://github.com/esystemsdev/aifabrix-miso-client-python/issues](https://github.com/esystemsdev/aifabrix-miso-client-python/issues)
|
|
1012
|
+
|
|
1013
|
+
---
|
|
1014
|
+
|
|
1015
|
+
## 📄 License
|
|
1016
|
+
|
|
1017
|
+
This project is licensed under the MIT License - see the [LICENSE](./LICENSE) file for details.
|
|
1018
|
+
|
|
1019
|
+
---
|
|
1020
|
+
|
|
1021
|
+
**Made with ❤️ by eSystems Nordic Ltd.**
|