spring-ready-python 0.1.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.
@@ -0,0 +1,30 @@
1
+ """
2
+ Spring-Ready Python
3
+ A lightweight library to make Python apps Spring Boot ecosystem compatible.
4
+
5
+ Provides:
6
+ - Eureka service registration
7
+ - Config Server integration with discovery
8
+ - Actuator endpoints (health, info, prometheus)
9
+ - FastAPI integration
10
+
11
+ Usage:
12
+ from spring_ready import SpringReadyApp
13
+
14
+ app = SpringReadyApp()
15
+ """
16
+
17
+ from .core import SpringReadyApp
18
+ from .exceptions import (
19
+ SpringReadyException,
20
+ EurekaRegistrationError,
21
+ ConfigServerError,
22
+ )
23
+
24
+ __version__ = "0.1.0"
25
+ __all__ = [
26
+ "SpringReadyApp",
27
+ "SpringReadyException",
28
+ "EurekaRegistrationError",
29
+ "ConfigServerError",
30
+ ]
@@ -0,0 +1,68 @@
1
+ """Spring Boot Actuator-compatible endpoints"""
2
+
3
+ from .health import HealthEndpoint, HealthIndicator, HealthStatus, create_default_health_endpoint
4
+ from .info import InfoEndpoint, create_default_info_endpoint
5
+ from .prometheus import PrometheusEndpoint, create_default_prometheus_endpoint
6
+ from .discovery import ActuatorDiscoveryEndpoint, create_default_discovery_endpoint
7
+ from .metrics import MetricsEndpoint, create_default_metrics_endpoint
8
+ from .env import EnvEndpoint, create_default_env_endpoint
9
+ from .loggers import LoggersEndpoint, create_default_loggers_endpoint
10
+ from .mappings import MappingsEndpoint, create_default_mappings_endpoint
11
+ from .threaddump import ThreadDumpEndpoint, create_default_threaddump_endpoint
12
+ from .httptrace import (
13
+ HttpTraceEndpoint,
14
+ HttpExchangesEndpoint,
15
+ create_default_httptrace_endpoint,
16
+ create_default_httpexchanges_endpoint
17
+ )
18
+ from .logfile import LogfileEndpoint, create_default_logfile_endpoint
19
+ from .refresh import RefreshEndpoint, create_default_refresh_endpoint
20
+ from .beans import BeansEndpoint, create_default_beans_endpoint
21
+ from .configprops import ConfigPropsEndpoint, create_default_configprops_endpoint
22
+ from .scheduledtasks import ScheduledTasksEndpoint, create_default_scheduledtasks_endpoint
23
+ from .heapdump import HeapdumpEndpoint, create_default_heapdump_endpoint
24
+ from .caches import CachesEndpoint, create_default_caches_endpoint
25
+ from .auditevents import AuditEventsEndpoint, create_default_auditevents_endpoint
26
+
27
+ __all__ = [
28
+ "HealthEndpoint",
29
+ "HealthIndicator",
30
+ "HealthStatus",
31
+ "create_default_health_endpoint",
32
+ "InfoEndpoint",
33
+ "create_default_info_endpoint",
34
+ "PrometheusEndpoint",
35
+ "create_default_prometheus_endpoint",
36
+ "ActuatorDiscoveryEndpoint",
37
+ "create_default_discovery_endpoint",
38
+ "MetricsEndpoint",
39
+ "create_default_metrics_endpoint",
40
+ "EnvEndpoint",
41
+ "create_default_env_endpoint",
42
+ "LoggersEndpoint",
43
+ "create_default_loggers_endpoint",
44
+ "MappingsEndpoint",
45
+ "create_default_mappings_endpoint",
46
+ "ThreadDumpEndpoint",
47
+ "create_default_threaddump_endpoint",
48
+ "HttpTraceEndpoint",
49
+ "HttpExchangesEndpoint",
50
+ "create_default_httptrace_endpoint",
51
+ "create_default_httpexchanges_endpoint",
52
+ "LogfileEndpoint",
53
+ "create_default_logfile_endpoint",
54
+ "RefreshEndpoint",
55
+ "create_default_refresh_endpoint",
56
+ "BeansEndpoint",
57
+ "create_default_beans_endpoint",
58
+ "ConfigPropsEndpoint",
59
+ "create_default_configprops_endpoint",
60
+ "ScheduledTasksEndpoint",
61
+ "create_default_scheduledtasks_endpoint",
62
+ "HeapdumpEndpoint",
63
+ "create_default_heapdump_endpoint",
64
+ "CachesEndpoint",
65
+ "create_default_caches_endpoint",
66
+ "AuditEventsEndpoint",
67
+ "create_default_auditevents_endpoint",
68
+ ]
@@ -0,0 +1,114 @@
1
+ """
2
+ Actuator AuditEvents Endpoint.
3
+ Provides information about security-related audit events.
4
+ """
5
+
6
+ import time
7
+ from typing import Dict, Any, List, Optional
8
+ from dataclasses import dataclass, field
9
+ from collections import deque
10
+
11
+
12
+ @dataclass
13
+ class AuditEvent:
14
+ """Single audit event"""
15
+ timestamp: str
16
+ principal: str
17
+ type: str
18
+ data: Dict[str, Any] = field(default_factory=dict)
19
+
20
+ def to_dict(self) -> Dict[str, Any]:
21
+ return {
22
+ "timestamp": self.timestamp,
23
+ "principal": self.principal,
24
+ "type": self.type,
25
+ "data": self.data
26
+ }
27
+
28
+
29
+ class AuditEventsEndpoint:
30
+ """
31
+ AuditEvents endpoint for Spring Boot Actuator compatibility.
32
+
33
+ Tracks security-related events like authentication, authorization, etc.
34
+ Stores events in memory with configurable max size.
35
+ """
36
+
37
+ def __init__(self, max_events: int = 1000):
38
+ """
39
+ Args:
40
+ max_events: Maximum number of events to keep in memory
41
+ """
42
+ self.max_events = max_events
43
+ self.events: deque = deque(maxlen=max_events)
44
+
45
+ def add_event(
46
+ self,
47
+ principal: str,
48
+ event_type: str,
49
+ data: Optional[Dict[str, Any]] = None
50
+ ) -> None:
51
+ """
52
+ Add an audit event.
53
+
54
+ Args:
55
+ principal: Principal (user/system) that triggered the event
56
+ event_type: Event type (e.g., "AUTHENTICATION_SUCCESS", "AUTHORIZATION_FAILURE")
57
+ data: Additional event data
58
+ """
59
+ timestamp = time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime())
60
+
61
+ event = AuditEvent(
62
+ timestamp=timestamp,
63
+ principal=principal,
64
+ type=event_type,
65
+ data=data or {}
66
+ )
67
+
68
+ self.events.append(event)
69
+
70
+ def get_events(
71
+ self,
72
+ principal: Optional[str] = None,
73
+ after: Optional[str] = None,
74
+ event_type: Optional[str] = None
75
+ ) -> Dict[str, Any]:
76
+ """
77
+ Get audit events with optional filtering.
78
+
79
+ Args:
80
+ principal: Filter by principal name
81
+ after: Filter events after this timestamp
82
+ event_type: Filter by event type
83
+
84
+ Returns:
85
+ Dictionary with filtered audit events
86
+ """
87
+ filtered_events = list(self.events)
88
+
89
+ # Apply filters
90
+ if principal:
91
+ filtered_events = [e for e in filtered_events if e.principal == principal]
92
+
93
+ if after:
94
+ filtered_events = [e for e in filtered_events if e.timestamp > after]
95
+
96
+ if event_type:
97
+ filtered_events = [e for e in filtered_events if e.type == event_type]
98
+
99
+ return {
100
+ "events": [event.to_dict() for event in filtered_events]
101
+ }
102
+
103
+
104
+ def create_default_auditevents_endpoint(max_events: int = 1000) -> AuditEventsEndpoint:
105
+ """
106
+ Create auditevents endpoint with default configuration.
107
+
108
+ Args:
109
+ max_events: Maximum number of events to keep
110
+
111
+ Returns:
112
+ AuditEventsEndpoint instance
113
+ """
114
+ return AuditEventsEndpoint(max_events=max_events)
@@ -0,0 +1,141 @@
1
+ """
2
+ Actuator Beans Endpoint.
3
+ Shows information about application beans/components.
4
+ """
5
+
6
+ import sys
7
+ from typing import Dict, Any, Optional, TYPE_CHECKING
8
+
9
+ if TYPE_CHECKING:
10
+ from fastapi import FastAPI
11
+
12
+
13
+ class BeansEndpoint:
14
+ """
15
+ Beans endpoint for Spring Boot Actuator compatibility.
16
+
17
+ Shows information about application components, similar to Spring's bean registry.
18
+ In Python/FastAPI context, this includes:
19
+ - Registered routes
20
+ - Middleware components
21
+ - Dependency injection components
22
+ """
23
+
24
+ def __init__(self, app: Optional['FastAPI'] = None):
25
+ """
26
+ Args:
27
+ app: FastAPI application instance
28
+ """
29
+ self.app = app
30
+
31
+ def get_beans(self) -> Dict[str, Any]:
32
+ """
33
+ Get all beans/components.
34
+
35
+ Returns:
36
+ Dictionary with application contexts and bean definitions
37
+ """
38
+ beans = {}
39
+
40
+ if self.app:
41
+ # Add FastAPI app itself
42
+ beans["fastapi_app"] = {
43
+ "type": "fastapi.applications.FastAPI",
44
+ "scope": "singleton",
45
+ "attributes": {
46
+ "title": self.app.title,
47
+ "version": self.app.version,
48
+ "openapi_url": self.app.openapi_url,
49
+ }
50
+ }
51
+
52
+ # Add middleware
53
+ middleware_beans = self._get_middleware_beans()
54
+ beans.update(middleware_beans)
55
+
56
+ # Add routes as beans
57
+ route_beans = self._get_route_beans()
58
+ beans.update(route_beans)
59
+
60
+ # Add Python system components
61
+ system_beans = self._get_system_beans()
62
+ beans.update(system_beans)
63
+
64
+ return {
65
+ "contexts": {
66
+ "application": {
67
+ "beans": beans,
68
+ "parentId": None
69
+ }
70
+ }
71
+ }
72
+
73
+ def _get_middleware_beans(self) -> Dict[str, Any]:
74
+ """Get middleware components as beans"""
75
+ middleware_beans = {}
76
+
77
+ if not self.app or not hasattr(self.app, 'user_middleware'):
78
+ return middleware_beans
79
+
80
+ for idx, middleware in enumerate(self.app.user_middleware):
81
+ middleware_cls = middleware.cls if hasattr(middleware, 'cls') else middleware
82
+ bean_name = f"middleware_{idx}_{middleware_cls.__name__}"
83
+
84
+ middleware_beans[bean_name] = {
85
+ "type": f"{middleware_cls.__module__}.{middleware_cls.__name__}",
86
+ "scope": "singleton",
87
+ "attributes": {}
88
+ }
89
+
90
+ return middleware_beans
91
+
92
+ def _get_route_beans(self) -> Dict[str, Any]:
93
+ """Get routes as beans"""
94
+ route_beans = {}
95
+
96
+ if not self.app:
97
+ return route_beans
98
+
99
+ for route in self.app.routes:
100
+ if hasattr(route, 'endpoint') and hasattr(route, 'path'):
101
+ endpoint_name = route.endpoint.__name__ if hasattr(route.endpoint, '__name__') else str(route.endpoint)
102
+ bean_name = f"route_{endpoint_name}_{route.path.replace('/', '_').replace('{', '').replace('}', '')}"
103
+
104
+ route_beans[bean_name] = {
105
+ "type": "fastapi.routing.APIRoute",
106
+ "scope": "singleton",
107
+ "attributes": {
108
+ "path": route.path,
109
+ "methods": list(route.methods) if hasattr(route, 'methods') else [],
110
+ "name": route.name if hasattr(route, 'name') else None
111
+ }
112
+ }
113
+
114
+ return route_beans
115
+
116
+ def _get_system_beans(self) -> Dict[str, Any]:
117
+ """Get Python system components"""
118
+ return {
119
+ "python_interpreter": {
120
+ "type": "system.python.interpreter",
121
+ "scope": "singleton",
122
+ "attributes": {
123
+ "version": sys.version.split()[0],
124
+ "executable": sys.executable,
125
+ "platform": sys.platform
126
+ }
127
+ }
128
+ }
129
+
130
+
131
+ def create_default_beans_endpoint(app: Optional['FastAPI'] = None) -> BeansEndpoint:
132
+ """
133
+ Create beans endpoint with default configuration.
134
+
135
+ Args:
136
+ app: FastAPI application instance
137
+
138
+ Returns:
139
+ BeansEndpoint instance
140
+ """
141
+ return BeansEndpoint(app=app)
@@ -0,0 +1,111 @@
1
+ """
2
+ Actuator Caches Endpoint.
3
+ Provides access to application caches.
4
+ """
5
+
6
+ from typing import Dict, Any, Optional, List
7
+
8
+
9
+ class CachesEndpoint:
10
+ """
11
+ Caches endpoint for Spring Boot Actuator compatibility.
12
+
13
+ Provides access to application caches.
14
+ Currently returns empty structure as no cache manager is configured by default.
15
+ Can be extended to integrate with Redis, Memcached, or in-memory caches.
16
+ """
17
+
18
+ def __init__(self):
19
+ """Initialize caches endpoint"""
20
+ self.cache_managers: Dict[str, Dict[str, Any]] = {}
21
+
22
+ def get_caches(self) -> Dict[str, Any]:
23
+ """
24
+ Get all caches.
25
+
26
+ Returns:
27
+ Dictionary with cache managers and caches
28
+ """
29
+ caches = {}
30
+
31
+ for manager_name, manager_info in self.cache_managers.items():
32
+ for cache_name in manager_info.get("caches", []):
33
+ caches[cache_name] = {
34
+ "target": f"{manager_name}::{cache_name}",
35
+ "cacheManager": manager_name
36
+ }
37
+
38
+ return {
39
+ "cacheManagers": self.cache_managers,
40
+ "caches": caches
41
+ }
42
+
43
+ def get_cache(self, cache_name: str, cache_manager: Optional[str] = None) -> Optional[Dict[str, Any]]:
44
+ """
45
+ Get a specific cache.
46
+
47
+ Args:
48
+ cache_name: Cache name
49
+ cache_manager: Optional cache manager name
50
+
51
+ Returns:
52
+ Cache details or None if not found
53
+ """
54
+ for manager_name, manager_info in self.cache_managers.items():
55
+ if cache_manager and manager_name != cache_manager:
56
+ continue
57
+
58
+ if cache_name in manager_info.get("caches", []):
59
+ return {
60
+ "target": f"{manager_name}::{cache_name}",
61
+ "name": cache_name,
62
+ "cacheManager": manager_name
63
+ }
64
+
65
+ return None
66
+
67
+ def evict_cache(self, cache_name: str, cache_manager: Optional[str] = None) -> bool:
68
+ """
69
+ Evict a specific cache.
70
+
71
+ Args:
72
+ cache_name: Cache name
73
+ cache_manager: Optional cache manager name
74
+
75
+ Returns:
76
+ True if cache was evicted, False otherwise
77
+ """
78
+ # Placeholder - actual eviction would depend on cache implementation
79
+ return self.get_cache(cache_name, cache_manager) is not None
80
+
81
+ def evict_all_caches(self) -> bool:
82
+ """
83
+ Evict all caches.
84
+
85
+ Returns:
86
+ True if successful
87
+ """
88
+ # Placeholder - actual eviction would depend on cache implementation
89
+ return True
90
+
91
+ def add_cache_manager(self, name: str, caches: List[str]) -> None:
92
+ """
93
+ Register a cache manager.
94
+
95
+ Args:
96
+ name: Cache manager name
97
+ caches: List of cache names
98
+ """
99
+ self.cache_managers[name] = {
100
+ "caches": caches
101
+ }
102
+
103
+
104
+ def create_default_caches_endpoint() -> CachesEndpoint:
105
+ """
106
+ Create caches endpoint with default configuration.
107
+
108
+ Returns:
109
+ CachesEndpoint instance
110
+ """
111
+ return CachesEndpoint()
@@ -0,0 +1,109 @@
1
+ """
2
+ Actuator ConfigProps Endpoint.
3
+ Shows configuration properties grouped by prefix.
4
+ """
5
+
6
+ import os
7
+ from typing import Dict, Any
8
+
9
+
10
+ class ConfigPropsEndpoint:
11
+ """
12
+ ConfigProps endpoint for Spring Boot Actuator compatibility.
13
+
14
+ Shows configuration properties similar to Spring Boot's @ConfigurationProperties.
15
+ Groups environment variables and settings by common prefixes.
16
+ """
17
+
18
+ def get_config_props(self) -> Dict[str, Any]:
19
+ """
20
+ Get all configuration properties grouped by prefix.
21
+
22
+ Returns:
23
+ Dictionary with configuration property beans
24
+ """
25
+ # Group environment variables by common prefixes
26
+ grouped_props = self._group_by_prefix(dict(os.environ))
27
+
28
+ beans = {}
29
+ for prefix, props in grouped_props.items():
30
+ bean_name = f"{prefix}_properties"
31
+ beans[bean_name] = {
32
+ "prefix": prefix,
33
+ "properties": props
34
+ }
35
+
36
+ return {
37
+ "contexts": {
38
+ "application": {
39
+ "beans": beans
40
+ }
41
+ }
42
+ }
43
+
44
+ def get_config_props_by_prefix(self, prefix: str) -> Dict[str, Any]:
45
+ """
46
+ Get configuration properties filtered by prefix.
47
+
48
+ Args:
49
+ prefix: Prefix to filter by
50
+
51
+ Returns:
52
+ Dictionary with matching configuration properties
53
+ """
54
+ matching_props = {}
55
+
56
+ for key, value in os.environ.items():
57
+ if key.startswith(prefix.upper()) or key.lower().startswith(prefix.lower()):
58
+ matching_props[key] = value
59
+
60
+ if not matching_props:
61
+ return {}
62
+
63
+ bean_name = f"{prefix}_properties"
64
+ return {
65
+ "contexts": {
66
+ "application": {
67
+ "beans": {
68
+ bean_name: {
69
+ "prefix": prefix,
70
+ "properties": matching_props
71
+ }
72
+ }
73
+ }
74
+ }
75
+ }
76
+
77
+ def _group_by_prefix(self, env_vars: Dict[str, str]) -> Dict[str, Dict[str, str]]:
78
+ """
79
+ Group environment variables by common prefixes.
80
+
81
+ Args:
82
+ env_vars: Environment variables dictionary
83
+
84
+ Returns:
85
+ Dictionary grouped by prefix
86
+ """
87
+ grouped = {}
88
+
89
+ for key, value in env_vars.items():
90
+ # Extract prefix (first part before _)
91
+ parts = key.split('_', 1)
92
+ prefix = parts[0] if parts else key
93
+
94
+ if prefix not in grouped:
95
+ grouped[prefix] = {}
96
+
97
+ grouped[prefix][key] = value
98
+
99
+ return grouped
100
+
101
+
102
+ def create_default_configprops_endpoint() -> ConfigPropsEndpoint:
103
+ """
104
+ Create configprops endpoint with default configuration.
105
+
106
+ Returns:
107
+ ConfigPropsEndpoint instance
108
+ """
109
+ return ConfigPropsEndpoint()