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.
Files changed (69) hide show
  1. miso_client/__init__.py +523 -130
  2. miso_client/api/__init__.py +35 -0
  3. miso_client/api/auth_api.py +367 -0
  4. miso_client/api/logs_api.py +91 -0
  5. miso_client/api/permissions_api.py +88 -0
  6. miso_client/api/roles_api.py +88 -0
  7. miso_client/api/types/__init__.py +75 -0
  8. miso_client/api/types/auth_types.py +183 -0
  9. miso_client/api/types/logs_types.py +71 -0
  10. miso_client/api/types/permissions_types.py +31 -0
  11. miso_client/api/types/roles_types.py +31 -0
  12. miso_client/errors.py +30 -4
  13. miso_client/models/__init__.py +4 -0
  14. miso_client/models/config.py +275 -72
  15. miso_client/models/error_response.py +39 -0
  16. miso_client/models/filter.py +255 -0
  17. miso_client/models/pagination.py +44 -0
  18. miso_client/models/sort.py +25 -0
  19. miso_client/services/__init__.py +6 -5
  20. miso_client/services/auth.py +496 -87
  21. miso_client/services/cache.py +42 -41
  22. miso_client/services/encryption.py +18 -17
  23. miso_client/services/logger.py +467 -328
  24. miso_client/services/logger_chain.py +288 -0
  25. miso_client/services/permission.py +130 -67
  26. miso_client/services/redis.py +28 -23
  27. miso_client/services/role.py +145 -62
  28. miso_client/utils/__init__.py +3 -3
  29. miso_client/utils/audit_log_queue.py +222 -0
  30. miso_client/utils/auth_strategy.py +88 -0
  31. miso_client/utils/auth_utils.py +65 -0
  32. miso_client/utils/circuit_breaker.py +125 -0
  33. miso_client/utils/client_token_manager.py +244 -0
  34. miso_client/utils/config_loader.py +88 -17
  35. miso_client/utils/controller_url_resolver.py +80 -0
  36. miso_client/utils/data_masker.py +104 -33
  37. miso_client/utils/environment_token.py +126 -0
  38. miso_client/utils/error_utils.py +216 -0
  39. miso_client/utils/fastapi_endpoints.py +166 -0
  40. miso_client/utils/filter.py +364 -0
  41. miso_client/utils/filter_applier.py +143 -0
  42. miso_client/utils/filter_parser.py +110 -0
  43. miso_client/utils/flask_endpoints.py +169 -0
  44. miso_client/utils/http_client.py +494 -262
  45. miso_client/utils/http_client_logging.py +352 -0
  46. miso_client/utils/http_client_logging_helpers.py +197 -0
  47. miso_client/utils/http_client_query_helpers.py +138 -0
  48. miso_client/utils/http_error_handler.py +92 -0
  49. miso_client/utils/http_log_formatter.py +115 -0
  50. miso_client/utils/http_log_masker.py +203 -0
  51. miso_client/utils/internal_http_client.py +435 -0
  52. miso_client/utils/jwt_tools.py +125 -16
  53. miso_client/utils/logger_helpers.py +206 -0
  54. miso_client/utils/logging_helpers.py +70 -0
  55. miso_client/utils/origin_validator.py +128 -0
  56. miso_client/utils/pagination.py +275 -0
  57. miso_client/utils/request_context.py +285 -0
  58. miso_client/utils/sensitive_fields_loader.py +116 -0
  59. miso_client/utils/sort.py +116 -0
  60. miso_client/utils/token_utils.py +114 -0
  61. miso_client/utils/url_validator.py +66 -0
  62. miso_client/utils/user_token_refresh.py +245 -0
  63. miso_client-3.7.2.dist-info/METADATA +1021 -0
  64. miso_client-3.7.2.dist-info/RECORD +68 -0
  65. miso_client-0.1.0.dist-info/METADATA +0 -551
  66. miso_client-0.1.0.dist-info/RECORD +0 -23
  67. {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/WHEEL +0 -0
  68. {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/licenses/LICENSE +0 -0
  69. {miso_client-0.1.0.dist-info → miso_client-3.7.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,110 @@
1
+ """
2
+ Filter parsing utilities for MisoClient SDK.
3
+
4
+ This module provides utilities for parsing filter parameters from query strings
5
+ and converting them into FilterOption objects.
6
+ """
7
+
8
+ from typing import Any, List, Optional, Union, cast
9
+ from urllib.parse import unquote
10
+
11
+ from ..models.filter import FilterOperator, FilterOption
12
+
13
+
14
+ def parse_filter_params(params: dict) -> List[FilterOption]:
15
+ """
16
+ Parse filter query parameters into FilterOption list.
17
+
18
+ Parses `?filter=field:op:value` format into FilterOption objects.
19
+ Supports multiple filter parameters (array of filter strings).
20
+
21
+ Args:
22
+ params: Dictionary with query parameters (e.g., {'filter': ['status:eq:active', 'region:in:eu,us']})
23
+
24
+ Returns:
25
+ List of FilterOption objects
26
+
27
+ Examples:
28
+ >>> parse_filter_params({'filter': ['status:eq:active']})
29
+ [FilterOption(field='status', op='eq', value='active')]
30
+ >>> parse_filter_params({'filter': ['region:in:eu,us']})
31
+ [FilterOption(field='region', op='in', value=['eu', 'us'])]
32
+ """
33
+ filters: List[FilterOption] = []
34
+
35
+ # Get filter parameter (can be string or list)
36
+ filter_param = params.get("filter") or params.get("filters")
37
+ if not filter_param:
38
+ return filters
39
+
40
+ # Normalize to list
41
+ if isinstance(filter_param, str):
42
+ filter_strings = [filter_param]
43
+ elif isinstance(filter_param, list):
44
+ filter_strings = filter_param
45
+ else:
46
+ return filters
47
+
48
+ # Parse each filter string
49
+ for filter_str in filter_strings:
50
+ if not isinstance(filter_str, str):
51
+ continue
52
+
53
+ # Split by colon (field:op:value)
54
+ # For isNull/isNotNull, value part may be empty or missing
55
+ parts = filter_str.split(":", 2)
56
+ if len(parts) < 2:
57
+ continue # Skip invalid filter format
58
+
59
+ field = unquote(parts[0].strip())
60
+ op = parts[1].strip()
61
+ value_str = unquote(parts[2].strip()) if len(parts) > 2 else ""
62
+
63
+ # Validate operator
64
+ valid_operators = [
65
+ "eq",
66
+ "neq",
67
+ "in",
68
+ "nin",
69
+ "gt",
70
+ "lt",
71
+ "gte",
72
+ "lte",
73
+ "contains",
74
+ "like",
75
+ "isNull",
76
+ "isNotNull",
77
+ ]
78
+ if op not in valid_operators:
79
+ continue # Skip invalid operator
80
+
81
+ # Parse value based on operator
82
+ parsed_value: Optional[Union[str, int, float, bool, List[Any]]] = None
83
+ if op in ("isNull", "isNotNull"):
84
+ # Null check operators don't need values
85
+ parsed_value = None
86
+ elif op in ("in", "nin"):
87
+ # Array values: comma-separated
88
+ parsed_value = [v.strip() for v in value_str.split(",") if v.strip()]
89
+ else:
90
+ # Single value: try to parse as number/boolean, fallback to string
91
+ single_value: Union[str, int, float, bool] = value_str
92
+ # Try to parse as integer
93
+ try:
94
+ if "." not in value_str:
95
+ single_value = int(value_str)
96
+ else:
97
+ single_value = float(value_str)
98
+ except (ValueError, TypeError):
99
+ # Try boolean
100
+ if value_str.lower() in ("true", "false"):
101
+ single_value = value_str.lower() == "true"
102
+ else:
103
+ single_value = value_str
104
+ parsed_value = single_value
105
+
106
+ value = parsed_value
107
+
108
+ filters.append(FilterOption(field=field, op=cast(FilterOperator, op), value=value))
109
+
110
+ return filters
@@ -0,0 +1,169 @@
1
+ """
2
+ Flask endpoint utilities for client token endpoint.
3
+
4
+ Provides server-side route handlers for creating client token endpoints
5
+ that return client token + DataClient configuration to frontend clients.
6
+ """
7
+
8
+ import asyncio
9
+ from typing import Any, Callable, Optional
10
+
11
+ from ..errors import AuthenticationError
12
+ from ..models.config import (
13
+ ClientTokenEndpointOptions,
14
+ ClientTokenEndpointResponse,
15
+ DataClientConfigResponse,
16
+ MisoClientConfig,
17
+ )
18
+ from ..utils.environment_token import get_environment_token
19
+
20
+
21
+ def create_flask_client_token_endpoint(
22
+ miso_client: Any, options: Optional[ClientTokenEndpointOptions] = None
23
+ ) -> Callable[[], Any]:
24
+ """
25
+ Create Flask route handler for client-token endpoint.
26
+
27
+ Automatically enriches response with DataClient configuration including
28
+ controllerPublicUrl for frontend client initialization.
29
+
30
+ Args:
31
+ miso_client: MisoClient instance (must be initialized)
32
+ options: Optional configuration for endpoint
33
+
34
+ Returns:
35
+ Flask route handler function
36
+
37
+ Example:
38
+ >>> from flask import Flask
39
+ >>> from miso_client import MisoClient, create_flask_client_token_endpoint, load_config
40
+ >>>
41
+ >>> app = Flask(__name__)
42
+ >>> client = MisoClient(load_config())
43
+ >>> await client.initialize()
44
+ >>>
45
+ >>> app.post('/api/v1/auth/client-token')(create_flask_client_token_endpoint(client))
46
+ """
47
+ opts = ClientTokenEndpointOptions(
48
+ clientTokenUri=options.clientTokenUri if options else "/api/v1/auth/client-token",
49
+ expiresIn=options.expiresIn if options else 1800,
50
+ includeConfig=options.includeConfig if options else True,
51
+ )
52
+
53
+ def handler() -> tuple[dict[str, Any], int]:
54
+ """
55
+ Flask route handler for client token endpoint.
56
+
57
+ Returns:
58
+ Tuple of (response_dict, status_code)
59
+ """
60
+ try:
61
+ # Check if misoClient is initialized
62
+ if not miso_client.is_initialized():
63
+ return (
64
+ {
65
+ "error": "Service Unavailable",
66
+ "message": "MisoClient is not initialized",
67
+ },
68
+ 503,
69
+ )
70
+
71
+ # Get Flask request object
72
+ try:
73
+ from flask import request
74
+ except ImportError:
75
+ return (
76
+ {
77
+ "error": "Internal Server Error",
78
+ "message": "Flask is not installed",
79
+ },
80
+ 500,
81
+ )
82
+
83
+ # Get token with origin validation (raises AuthenticationError if validation fails)
84
+ # Run async function in sync context
85
+ # Handle both sync and async Flask contexts
86
+ try:
87
+ # Try to get existing event loop
88
+ _ = asyncio.get_running_loop()
89
+ # If we get here, we're in an async context (Flask 2.0+ async handler)
90
+ # In this case, we need to await, but Flask async handlers handle this
91
+ # For now, create a new event loop in a thread
92
+ import concurrent.futures
93
+
94
+ with concurrent.futures.ThreadPoolExecutor() as executor:
95
+ future = executor.submit(
96
+ asyncio.run, get_environment_token(miso_client, request.headers)
97
+ )
98
+ token = future.result()
99
+ except RuntimeError:
100
+ # No running loop, safe to use asyncio.run()
101
+ token = asyncio.run(get_environment_token(miso_client, request.headers))
102
+
103
+ # Build response
104
+ response: ClientTokenEndpointResponse = ClientTokenEndpointResponse(
105
+ token=token, expiresIn=opts.expiresIn or 1800
106
+ )
107
+
108
+ # Include config if requested
109
+ if opts.includeConfig:
110
+ config: MisoClientConfig = miso_client.config
111
+
112
+ # Derive baseUrl from request
113
+ base_url = f"{request.scheme}://{request.host or 'localhost'}"
114
+
115
+ # Get controller URL (prefer controllerPublicUrl for browser, fallback to controller_url)
116
+ controller_url = config.controllerPublicUrl or config.controller_url
117
+
118
+ if not controller_url:
119
+ return (
120
+ {
121
+ "error": "Internal Server Error",
122
+ "message": "Controller URL not configured",
123
+ },
124
+ 500,
125
+ )
126
+
127
+ response.config = DataClientConfigResponse(
128
+ baseUrl=base_url,
129
+ controllerUrl=controller_url,
130
+ controllerPublicUrl=config.controllerPublicUrl,
131
+ clientId=config.client_id,
132
+ clientTokenUri=opts.clientTokenUri or "/api/v1/auth/client-token",
133
+ )
134
+
135
+ return response.model_dump(exclude_none=True), 200
136
+
137
+ except AuthenticationError as error:
138
+ # Origin validation failed (403)
139
+ error_message = str(error)
140
+ if "Origin validation failed" in error_message:
141
+ return (
142
+ {
143
+ "error": "Forbidden",
144
+ "message": error_message,
145
+ },
146
+ 403,
147
+ )
148
+
149
+ # Other authentication errors (500)
150
+ return (
151
+ {
152
+ "error": "Internal Server Error",
153
+ "message": error_message,
154
+ },
155
+ 500,
156
+ )
157
+
158
+ except Exception as error:
159
+ # Other errors (500)
160
+ error_message = str(error) if error else "Unknown error"
161
+ return (
162
+ {
163
+ "error": "Internal Server Error",
164
+ "message": error_message,
165
+ },
166
+ 500,
167
+ )
168
+
169
+ return handler