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,75 @@
1
+ """
2
+ API type definitions.
3
+
4
+ Exports all request and response types for the API layer.
5
+ """
6
+
7
+ from .auth_types import (
8
+ DeviceCodeRequest,
9
+ DeviceCodeResponse,
10
+ DeviceCodeResponseWrapper,
11
+ DeviceCodeTokenPollRequest,
12
+ DeviceCodeTokenPollResponse,
13
+ DeviceCodeTokenResponse,
14
+ GetUserResponse,
15
+ GetUserResponseData,
16
+ LoginResponse,
17
+ LoginResponseData,
18
+ LogoutResponse,
19
+ RefreshTokenRequest,
20
+ RefreshTokenResponse,
21
+ ValidateTokenRequest,
22
+ ValidateTokenResponse,
23
+ ValidateTokenResponseData,
24
+ )
25
+ from .logs_types import (
26
+ AuditLogData,
27
+ BatchLogError,
28
+ BatchLogRequest,
29
+ BatchLogResponse,
30
+ GeneralLogData,
31
+ LogRequest,
32
+ LogResponse,
33
+ )
34
+ from .permissions_types import GetPermissionsResponse as PermissionsGetPermissionsResponse
35
+ from .permissions_types import GetPermissionsResponseData as PermissionsGetPermissionsResponseData
36
+ from .permissions_types import RefreshPermissionsResponse as PermissionsRefreshPermissionsResponse
37
+ from .roles_types import GetRolesResponse as RolesGetRolesResponse
38
+ from .roles_types import GetRolesResponseData as RolesGetRolesResponseData
39
+ from .roles_types import RefreshRolesResponse as RolesRefreshRolesResponse
40
+
41
+ __all__ = [
42
+ # Auth types
43
+ "LoginResponse",
44
+ "LoginResponseData",
45
+ "ValidateTokenRequest",
46
+ "ValidateTokenResponse",
47
+ "ValidateTokenResponseData",
48
+ "GetUserResponse",
49
+ "GetUserResponseData",
50
+ "LogoutResponse",
51
+ "RefreshTokenRequest",
52
+ "RefreshTokenResponse",
53
+ "DeviceCodeTokenResponse",
54
+ "DeviceCodeRequest",
55
+ "DeviceCodeResponse",
56
+ "DeviceCodeResponseWrapper",
57
+ "DeviceCodeTokenPollRequest",
58
+ "DeviceCodeTokenPollResponse",
59
+ # Roles types
60
+ "RolesGetRolesResponse",
61
+ "RolesGetRolesResponseData",
62
+ "RolesRefreshRolesResponse",
63
+ # Permissions types
64
+ "PermissionsGetPermissionsResponse",
65
+ "PermissionsGetPermissionsResponseData",
66
+ "PermissionsRefreshPermissionsResponse",
67
+ # Logs types
68
+ "GeneralLogData",
69
+ "AuditLogData",
70
+ "LogRequest",
71
+ "BatchLogRequest",
72
+ "LogResponse",
73
+ "BatchLogResponse",
74
+ "BatchLogError",
75
+ ]
@@ -0,0 +1,183 @@
1
+ """
2
+ Auth API request and response types.
3
+
4
+ All types follow OpenAPI specification with camelCase field names.
5
+ """
6
+
7
+ from typing import List, Optional
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from ...models.config import UserInfo
12
+
13
+
14
+ class LoginResponse(BaseModel):
15
+ """Login response with login URL."""
16
+
17
+ success: bool = Field(..., description="Whether request was successful")
18
+ data: "LoginResponseData" = Field(..., description="Login data")
19
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
20
+
21
+
22
+ class LoginResponseData(BaseModel):
23
+ """Login response data."""
24
+
25
+ loginUrl: str = Field(..., description="Login URL for OAuth2 flow")
26
+
27
+
28
+ class ValidateTokenRequest(BaseModel):
29
+ """Token validation request."""
30
+
31
+ token: str = Field(..., description="JWT token to validate")
32
+ environment: Optional[str] = Field(default=None, description="Optional environment key")
33
+ application: Optional[str] = Field(default=None, description="Optional application key")
34
+
35
+
36
+ class ValidateTokenResponse(BaseModel):
37
+ """Token validation response."""
38
+
39
+ success: bool = Field(..., description="Whether request was successful")
40
+ data: "ValidateTokenResponseData" = Field(..., description="Validation data")
41
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
42
+
43
+
44
+ class ValidateTokenResponseData(BaseModel):
45
+ """Token validation response data."""
46
+
47
+ authenticated: bool = Field(..., description="Whether token is authenticated")
48
+ user: Optional[UserInfo] = Field(default=None, description="User information if authenticated")
49
+ expiresAt: Optional[str] = Field(default=None, description="Token expiration timestamp")
50
+
51
+
52
+ class GetUserResponse(BaseModel):
53
+ """Get user response."""
54
+
55
+ success: bool = Field(..., description="Whether request was successful")
56
+ data: "GetUserResponseData" = Field(..., description="User data")
57
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
58
+
59
+
60
+ class GetUserResponseData(BaseModel):
61
+ """Get user response data."""
62
+
63
+ user: UserInfo = Field(..., description="User information")
64
+ authenticated: bool = Field(..., description="Whether user is authenticated")
65
+
66
+
67
+ class LogoutResponse(BaseModel):
68
+ """Logout response."""
69
+
70
+ success: bool = Field(..., description="Whether request was successful")
71
+ message: str = Field(..., description="Logout message")
72
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
73
+
74
+
75
+ class RefreshTokenRequest(BaseModel):
76
+ """Refresh token request."""
77
+
78
+ refreshToken: str = Field(..., description="Refresh token")
79
+
80
+
81
+ class DeviceCodeTokenResponse(BaseModel):
82
+ """Device code token response."""
83
+
84
+ accessToken: str = Field(..., description="JWT access token")
85
+ refreshToken: Optional[str] = Field(default=None, description="Refresh token")
86
+ expiresIn: int = Field(..., description="Token expiration in seconds")
87
+
88
+
89
+ class RefreshTokenResponse(BaseModel):
90
+ """Refresh token response."""
91
+
92
+ success: bool = Field(..., description="Whether request was successful")
93
+ data: DeviceCodeTokenResponse = Field(..., description="Token data")
94
+ message: Optional[str] = Field(default=None, description="Optional message")
95
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
96
+
97
+
98
+ class DeviceCodeRequest(BaseModel):
99
+ """Device code initiation request."""
100
+
101
+ environment: Optional[str] = Field(default=None, description="Environment key")
102
+ scope: Optional[str] = Field(default=None, description="OAuth2 scope string")
103
+
104
+
105
+ class DeviceCodeResponse(BaseModel):
106
+ """Device code response."""
107
+
108
+ deviceCode: str = Field(..., description="Device code for polling")
109
+ userCode: str = Field(..., description="User code to enter")
110
+ verificationUri: str = Field(..., description="Verification URI")
111
+ verificationUriComplete: Optional[str] = Field(
112
+ default=None, description="Complete URI with user code"
113
+ )
114
+ expiresIn: int = Field(..., description="Device code expiration in seconds")
115
+ interval: int = Field(..., description="Polling interval in seconds")
116
+
117
+
118
+ class DeviceCodeResponseWrapper(BaseModel):
119
+ """Device code response wrapper."""
120
+
121
+ success: bool = Field(..., description="Whether request was successful")
122
+ data: DeviceCodeResponse = Field(..., description="Device code data")
123
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
124
+
125
+
126
+ class DeviceCodeTokenPollRequest(BaseModel):
127
+ """Device code token poll request."""
128
+
129
+ deviceCode: str = Field(..., description="Device code from initiation")
130
+
131
+
132
+ class DeviceCodeTokenPollResponse(BaseModel):
133
+ """Device code token poll response."""
134
+
135
+ success: bool = Field(..., description="Whether request was successful")
136
+ data: Optional[DeviceCodeTokenResponse] = Field(default=None, description="Token data if ready")
137
+ error: Optional[str] = Field(default=None, description="Error code if pending")
138
+ errorDescription: Optional[str] = Field(default=None, description="Error description")
139
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
140
+
141
+
142
+ class GetRolesResponse(BaseModel):
143
+ """Get roles response."""
144
+
145
+ success: bool = Field(..., description="Whether request was successful")
146
+ data: "GetRolesResponseData" = Field(..., description="Roles data")
147
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
148
+
149
+
150
+ class GetRolesResponseData(BaseModel):
151
+ """Get roles response data."""
152
+
153
+ roles: List[str] = Field(..., description="List of user roles")
154
+
155
+
156
+ class RefreshRolesResponse(BaseModel):
157
+ """Refresh roles response."""
158
+
159
+ success: bool = Field(..., description="Whether request was successful")
160
+ data: GetRolesResponseData = Field(..., description="Roles data")
161
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
162
+
163
+
164
+ class GetPermissionsResponse(BaseModel):
165
+ """Get permissions response."""
166
+
167
+ success: bool = Field(..., description="Whether request was successful")
168
+ data: "GetPermissionsResponseData" = Field(..., description="Permissions data")
169
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
170
+
171
+
172
+ class GetPermissionsResponseData(BaseModel):
173
+ """Get permissions response data."""
174
+
175
+ permissions: List[str] = Field(..., description="List of user permissions")
176
+
177
+
178
+ class RefreshPermissionsResponse(BaseModel):
179
+ """Refresh permissions response."""
180
+
181
+ success: bool = Field(..., description="Whether request was successful")
182
+ data: GetPermissionsResponseData = Field(..., description="Permissions data")
183
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
@@ -0,0 +1,71 @@
1
+ """
2
+ Logs API request and response types.
3
+
4
+ All types follow OpenAPI specification with camelCase field names.
5
+ """
6
+
7
+ from typing import Any, Dict, List, Literal, Optional, Union
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+ from ...models.config import LogEntry
12
+
13
+
14
+ class GeneralLogData(BaseModel):
15
+ """General log data structure."""
16
+
17
+ level: Literal["error", "warn", "info", "debug"] = Field(..., description="Log level")
18
+ message: str = Field(..., description="Log message")
19
+ context: Optional[Dict[str, Any]] = Field(default=None, description="Additional context")
20
+ correlationId: Optional[str] = Field(default=None, description="Correlation ID")
21
+
22
+
23
+ class AuditLogData(BaseModel):
24
+ """Audit log data structure."""
25
+
26
+ entityType: str = Field(..., description="Entity type")
27
+ entityId: str = Field(..., description="Entity ID")
28
+ action: str = Field(..., description="Action performed")
29
+ oldValues: Optional[Dict[str, Any]] = Field(default=None, description="Previous values")
30
+ newValues: Optional[Dict[str, Any]] = Field(default=None, description="New values")
31
+ correlationId: Optional[str] = Field(default=None, description="Correlation ID")
32
+
33
+
34
+ class LogRequest(BaseModel):
35
+ """Log request with type and data."""
36
+
37
+ type: Literal["error", "general", "audit"] = Field(..., description="Log entry type")
38
+ data: Union[GeneralLogData, AuditLogData] = Field(..., description="Log data")
39
+
40
+
41
+ class BatchLogRequest(BaseModel):
42
+ """Batch log request."""
43
+
44
+ logs: List[LogEntry] = Field(..., description="List of log entries")
45
+
46
+
47
+ class LogResponse(BaseModel):
48
+ """Log response."""
49
+
50
+ success: bool = Field(..., description="Whether request was successful")
51
+ message: str = Field(..., description="Response message")
52
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
53
+
54
+
55
+ class BatchLogError(BaseModel):
56
+ """Batch log error entry."""
57
+
58
+ index: int = Field(..., description="Index of failed log entry")
59
+ error: str = Field(..., description="Error message")
60
+ log: Dict[str, Any] = Field(..., description="Failed log entry")
61
+
62
+
63
+ class BatchLogResponse(BaseModel):
64
+ """Batch log response."""
65
+
66
+ success: bool = Field(..., description="Whether request was successful")
67
+ message: str = Field(..., description="Response message")
68
+ processed: int = Field(..., description="Number of logs successfully processed")
69
+ failed: int = Field(..., description="Number of logs that failed")
70
+ errors: Optional[List[BatchLogError]] = Field(default=None, description="Error details")
71
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
@@ -0,0 +1,31 @@
1
+ """
2
+ Permissions API request and response types.
3
+
4
+ All types follow OpenAPI specification with camelCase field names.
5
+ """
6
+
7
+ from typing import List
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class GetPermissionsResponse(BaseModel):
13
+ """Get permissions response."""
14
+
15
+ success: bool = Field(..., description="Whether request was successful")
16
+ data: "GetPermissionsResponseData" = Field(..., description="Permissions data")
17
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
18
+
19
+
20
+ class GetPermissionsResponseData(BaseModel):
21
+ """Get permissions response data."""
22
+
23
+ permissions: List[str] = Field(..., description="List of user permissions")
24
+
25
+
26
+ class RefreshPermissionsResponse(BaseModel):
27
+ """Refresh permissions response."""
28
+
29
+ success: bool = Field(..., description="Whether request was successful")
30
+ data: GetPermissionsResponseData = Field(..., description="Permissions data")
31
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
@@ -0,0 +1,31 @@
1
+ """
2
+ Roles API request and response types.
3
+
4
+ All types follow OpenAPI specification with camelCase field names.
5
+ """
6
+
7
+ from typing import List
8
+
9
+ from pydantic import BaseModel, Field
10
+
11
+
12
+ class GetRolesResponse(BaseModel):
13
+ """Get roles response."""
14
+
15
+ success: bool = Field(..., description="Whether request was successful")
16
+ data: "GetRolesResponseData" = Field(..., description="Roles data")
17
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
18
+
19
+
20
+ class GetRolesResponseData(BaseModel):
21
+ """Get roles response data."""
22
+
23
+ roles: List[str] = Field(..., description="List of user roles")
24
+
25
+
26
+ class RefreshRolesResponse(BaseModel):
27
+ """Refresh roles response."""
28
+
29
+ success: bool = Field(..., description="Whether request was successful")
30
+ data: GetRolesResponseData = Field(..., description="Roles data")
31
+ timestamp: str = Field(..., description="Response timestamp (ISO 8601)")
miso_client/errors.py CHANGED
@@ -4,41 +4,67 @@ SDK exceptions and error handling.
4
4
  This module defines custom exceptions for the MisoClient SDK.
5
5
  """
6
6
 
7
+ from typing import TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from ..models.error_response import ErrorResponse
11
+
7
12
 
8
13
  class MisoClientError(Exception):
9
14
  """Base exception for MisoClient SDK errors."""
10
-
11
- def __init__(self, message: str, status_code: int | None = None, error_body: dict | None = None):
15
+
16
+ def __init__(
17
+ self,
18
+ message: str,
19
+ status_code: int | None = None,
20
+ error_body: dict | None = None,
21
+ error_response: "ErrorResponse | None" = None,
22
+ ):
12
23
  """
13
24
  Initialize MisoClient error.
14
-
25
+
15
26
  Args:
16
27
  message: Error message
17
28
  status_code: HTTP status code if applicable
18
29
  error_body: Sanitized error response body (secrets masked)
30
+ error_response: Structured error response object (RFC 7807-style)
19
31
  """
20
32
  super().__init__(message)
21
33
  self.message = message
22
34
  self.status_code = status_code
23
35
  self.error_body = error_body if error_body is not None else None
36
+ self.error_response = error_response
37
+
38
+ # Enhance message with structured error information if available
39
+ if error_response and error_response.errors:
40
+ if len(error_response.errors) == 1:
41
+ self.message = error_response.errors[0]
42
+ else:
43
+ self.message = f"{error_response.title}: {'; '.join(error_response.errors)}"
44
+ # Override status_code from structured response if available
45
+ if error_response.statusCode:
46
+ self.status_code = error_response.statusCode
24
47
 
25
48
 
26
49
  class AuthenticationError(MisoClientError):
27
50
  """Raised when authentication fails."""
51
+
28
52
  pass
29
53
 
30
54
 
31
55
  class AuthorizationError(MisoClientError):
32
56
  """Raised when authorization check fails."""
57
+
33
58
  pass
34
59
 
35
60
 
36
61
  class ConnectionError(MisoClientError):
37
62
  """Raised when connection to controller or Redis fails."""
63
+
38
64
  pass
39
65
 
40
66
 
41
67
  class ConfigurationError(MisoClientError):
42
68
  """Raised when configuration is invalid."""
43
- pass
44
69
 
70
+ pass
@@ -1 +1,5 @@
1
1
  """Pydantic models for MisoClient configuration and data types."""
2
+
3
+ from .error_response import ErrorResponse
4
+
5
+ __all__ = ["ErrorResponse"]