django-cfg 1.4.119__py3-none-any.whl → 1.5.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +8 -4
- django_cfg/apps/centrifugo/admin/centrifugo_log.py +33 -71
- django_cfg/apps/grpc/__init__.py +9 -0
- django_cfg/apps/grpc/admin/__init__.py +11 -0
- django_cfg/apps/grpc/admin/config.py +89 -0
- django_cfg/apps/grpc/admin/grpc_request_log.py +252 -0
- django_cfg/apps/grpc/apps.py +28 -0
- django_cfg/apps/grpc/auth/__init__.py +9 -0
- django_cfg/apps/grpc/auth/jwt_auth.py +295 -0
- django_cfg/apps/grpc/interceptors/__init__.py +19 -0
- django_cfg/apps/grpc/interceptors/errors.py +241 -0
- django_cfg/apps/grpc/interceptors/logging.py +270 -0
- django_cfg/apps/grpc/interceptors/metrics.py +306 -0
- django_cfg/apps/grpc/interceptors/request_logger.py +515 -0
- django_cfg/apps/grpc/management/__init__.py +1 -0
- django_cfg/apps/grpc/management/commands/__init__.py +0 -0
- django_cfg/apps/grpc/management/commands/rungrpc.py +302 -0
- django_cfg/apps/grpc/managers/__init__.py +10 -0
- django_cfg/apps/grpc/managers/grpc_request_log.py +310 -0
- django_cfg/apps/grpc/migrations/0001_initial.py +69 -0
- django_cfg/apps/grpc/migrations/0002_rename_django_cfg__service_4c4a8e_idx_django_cfg__service_584308_idx_and_more.py +38 -0
- django_cfg/apps/grpc/migrations/__init__.py +0 -0
- django_cfg/apps/grpc/models/__init__.py +9 -0
- django_cfg/apps/grpc/models/grpc_request_log.py +219 -0
- django_cfg/apps/grpc/serializers/__init__.py +23 -0
- django_cfg/apps/grpc/serializers/health.py +18 -0
- django_cfg/apps/grpc/serializers/requests.py +18 -0
- django_cfg/apps/grpc/serializers/services.py +50 -0
- django_cfg/apps/grpc/serializers/stats.py +22 -0
- django_cfg/apps/grpc/services/__init__.py +16 -0
- django_cfg/apps/grpc/services/base.py +375 -0
- django_cfg/apps/grpc/services/discovery.py +415 -0
- django_cfg/apps/grpc/urls.py +23 -0
- django_cfg/apps/grpc/utils/__init__.py +13 -0
- django_cfg/apps/grpc/utils/proto_gen.py +423 -0
- django_cfg/apps/grpc/views/__init__.py +9 -0
- django_cfg/apps/grpc/views/monitoring.py +497 -0
- django_cfg/apps/maintenance/admin/api_key_admin.py +7 -8
- django_cfg/apps/maintenance/admin/site_admin.py +5 -4
- django_cfg/apps/payments/admin/balance_admin.py +26 -36
- django_cfg/apps/payments/admin/payment_admin.py +65 -85
- django_cfg/apps/payments/admin/withdrawal_admin.py +65 -100
- django_cfg/apps/tasks/admin/task_log.py +20 -47
- django_cfg/apps/urls.py +7 -1
- django_cfg/config.py +106 -0
- django_cfg/core/base/config_model.py +6 -0
- django_cfg/core/builders/apps_builder.py +3 -0
- django_cfg/core/generation/integration_generators/grpc_generator.py +318 -0
- django_cfg/core/generation/orchestrator.py +10 -0
- django_cfg/models/api/grpc/__init__.py +59 -0
- django_cfg/models/api/grpc/config.py +364 -0
- django_cfg/modules/base.py +15 -0
- django_cfg/modules/django_admin/__init__.py +2 -0
- django_cfg/modules/django_admin/base/pydantic_admin.py +2 -2
- django_cfg/modules/django_admin/config/__init__.py +2 -0
- django_cfg/modules/django_admin/config/field_config.py +24 -0
- django_cfg/modules/django_admin/utils/__init__.py +41 -3
- django_cfg/modules/django_admin/utils/badges/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{badges.py → badges/status_badges.py} +3 -3
- django_cfg/modules/django_admin/utils/displays/__init__.py +13 -0
- django_cfg/modules/django_admin/utils/{displays.py → displays/data_displays.py} +2 -2
- django_cfg/modules/django_admin/utils/html/__init__.py +26 -0
- django_cfg/modules/django_admin/utils/html/badges.py +47 -0
- django_cfg/modules/django_admin/utils/html/base.py +167 -0
- django_cfg/modules/django_admin/utils/html/code.py +87 -0
- django_cfg/modules/django_admin/utils/html/composition.py +198 -0
- django_cfg/modules/django_admin/utils/html/formatting.py +231 -0
- django_cfg/modules/django_admin/utils/html/keyvalue.py +219 -0
- django_cfg/modules/django_admin/utils/html/markdown_integration.py +108 -0
- django_cfg/modules/django_admin/utils/html/progress.py +127 -0
- django_cfg/modules/django_admin/utils/html_builder.py +55 -408
- django_cfg/modules/django_admin/utils/markdown/__init__.py +21 -0
- django_cfg/modules/django_admin/widgets/registry.py +42 -0
- django_cfg/modules/django_unfold/navigation.py +28 -0
- django_cfg/pyproject.toml +3 -5
- django_cfg/registry/modules.py +6 -0
- {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/METADATA +10 -1
- {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/RECORD +83 -34
- django_cfg/modules/django_admin/utils/CODE_BLOCK_DOCS.md +0 -396
- /django_cfg/modules/django_admin/utils/{mermaid_plugin.py → markdown/mermaid_plugin.py} +0 -0
- /django_cfg/modules/django_admin/utils/{markdown_renderer.py → markdown/renderer.py} +0 -0
- {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.119.dist-info → django_cfg-1.5.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Service Discovery for gRPC.
|
|
3
|
+
|
|
4
|
+
Automatically discovers and registers gRPC services from Django apps.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import importlib
|
|
8
|
+
import logging
|
|
9
|
+
from typing import Any, Dict, List, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
from django.apps import apps
|
|
12
|
+
from django.conf import settings
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ServiceDiscovery:
|
|
18
|
+
"""
|
|
19
|
+
Discovers gRPC services from Django applications.
|
|
20
|
+
|
|
21
|
+
Features:
|
|
22
|
+
- Auto-discovers services from enabled apps
|
|
23
|
+
- Supports custom service registration
|
|
24
|
+
- Configurable discovery paths
|
|
25
|
+
- Lazy loading support
|
|
26
|
+
- Integration with django-grpc-framework
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
```python
|
|
30
|
+
from django_cfg.apps.grpc.services.discovery import ServiceDiscovery
|
|
31
|
+
|
|
32
|
+
discovery = ServiceDiscovery()
|
|
33
|
+
services = discovery.discover_services()
|
|
34
|
+
|
|
35
|
+
for service_class, add_to_server_func in services:
|
|
36
|
+
add_to_server_func(service_class.as_servicer(), server)
|
|
37
|
+
```
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(self):
|
|
41
|
+
"""Initialize service discovery."""
|
|
42
|
+
self.grpc_config = getattr(settings, "GRPC_FRAMEWORK", {})
|
|
43
|
+
self.auto_register = self.grpc_config.get("AUTO_REGISTER_APPS", True)
|
|
44
|
+
self.enabled_apps = self.grpc_config.get("ENABLED_APPS", [])
|
|
45
|
+
self.custom_services = self.grpc_config.get("CUSTOM_SERVICES", {})
|
|
46
|
+
|
|
47
|
+
# Common module names where services might be defined
|
|
48
|
+
self.service_modules = [
|
|
49
|
+
"grpc_services",
|
|
50
|
+
"grpc_handlers",
|
|
51
|
+
"services.grpc",
|
|
52
|
+
"handlers.grpc",
|
|
53
|
+
"api.grpc",
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def discover_services(self) -> List[Tuple[Any, Any]]:
|
|
57
|
+
"""
|
|
58
|
+
Discover all gRPC services.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
List of (service_class, add_to_server_func) tuples
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
>>> discovery = ServiceDiscovery()
|
|
65
|
+
>>> services = discovery.discover_services()
|
|
66
|
+
>>> len(services)
|
|
67
|
+
5
|
|
68
|
+
"""
|
|
69
|
+
discovered_services = []
|
|
70
|
+
|
|
71
|
+
# Discover from enabled apps
|
|
72
|
+
if self.auto_register:
|
|
73
|
+
for app_label in self.enabled_apps:
|
|
74
|
+
services = self._discover_app_services(app_label)
|
|
75
|
+
discovered_services.extend(services)
|
|
76
|
+
|
|
77
|
+
# Add custom services
|
|
78
|
+
for service_path in self.custom_services.values():
|
|
79
|
+
service = self._load_custom_service(service_path)
|
|
80
|
+
if service:
|
|
81
|
+
discovered_services.append(service)
|
|
82
|
+
|
|
83
|
+
logger.info(f"Discovered {len(discovered_services)} gRPC service(s)")
|
|
84
|
+
return discovered_services
|
|
85
|
+
|
|
86
|
+
def _discover_app_services(self, app_label: str) -> List[Tuple[Any, Any]]:
|
|
87
|
+
"""
|
|
88
|
+
Discover services from a Django app.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
app_label: Django app label (e.g., 'accounts', 'support')
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
List of discovered services
|
|
95
|
+
"""
|
|
96
|
+
services = []
|
|
97
|
+
|
|
98
|
+
# Get app config
|
|
99
|
+
try:
|
|
100
|
+
app_config = apps.get_app_config(app_label)
|
|
101
|
+
except LookupError:
|
|
102
|
+
logger.warning(f"App '{app_label}' not found in INSTALLED_APPS")
|
|
103
|
+
return services
|
|
104
|
+
|
|
105
|
+
# Try to import service modules
|
|
106
|
+
for module_name in self.service_modules:
|
|
107
|
+
full_module_path = f"{app_config.name}.{module_name}"
|
|
108
|
+
|
|
109
|
+
try:
|
|
110
|
+
module = importlib.import_module(full_module_path)
|
|
111
|
+
logger.debug(f"Found gRPC module: {full_module_path}")
|
|
112
|
+
|
|
113
|
+
# Look for services in module
|
|
114
|
+
app_services = self._extract_services_from_module(module, full_module_path)
|
|
115
|
+
services.extend(app_services)
|
|
116
|
+
|
|
117
|
+
except ImportError:
|
|
118
|
+
# Module doesn't exist, that's okay
|
|
119
|
+
logger.debug(f"No gRPC module at {full_module_path}")
|
|
120
|
+
continue
|
|
121
|
+
except Exception as e:
|
|
122
|
+
logger.error(f"Error importing {full_module_path}: {e}", exc_info=True)
|
|
123
|
+
continue
|
|
124
|
+
|
|
125
|
+
if services:
|
|
126
|
+
logger.info(f"Discovered {len(services)} service(s) from app '{app_label}'")
|
|
127
|
+
|
|
128
|
+
return services
|
|
129
|
+
|
|
130
|
+
def _extract_services_from_module(
|
|
131
|
+
self, module: Any, module_path: str
|
|
132
|
+
) -> List[Tuple[Any, Any]]:
|
|
133
|
+
"""
|
|
134
|
+
Extract gRPC services from a module.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
module: Python module object
|
|
138
|
+
module_path: Full module path string
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
List of (service_class, add_to_server_func) tuples
|
|
142
|
+
"""
|
|
143
|
+
services = []
|
|
144
|
+
|
|
145
|
+
# Look for grpc_handlers() function (django-grpc-framework convention)
|
|
146
|
+
if hasattr(module, "grpc_handlers"):
|
|
147
|
+
handlers_func = getattr(module, "grpc_handlers")
|
|
148
|
+
if callable(handlers_func):
|
|
149
|
+
try:
|
|
150
|
+
# Call the handlers function to get list of services
|
|
151
|
+
handlers = handlers_func(None) # server argument not needed for discovery
|
|
152
|
+
logger.info(f"Found grpc_handlers() in {module_path}")
|
|
153
|
+
|
|
154
|
+
# handlers should be a list of tuples
|
|
155
|
+
if isinstance(handlers, list):
|
|
156
|
+
services.extend(handlers)
|
|
157
|
+
else:
|
|
158
|
+
logger.warning(
|
|
159
|
+
f"grpc_handlers() in {module_path} did not return a list"
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
logger.error(
|
|
164
|
+
f"Error calling grpc_handlers() in {module_path}: {e}",
|
|
165
|
+
exc_info=True,
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Look for individual service classes
|
|
169
|
+
for attr_name in dir(module):
|
|
170
|
+
# Skip private attributes
|
|
171
|
+
if attr_name.startswith("_"):
|
|
172
|
+
continue
|
|
173
|
+
|
|
174
|
+
attr = getattr(module, attr_name)
|
|
175
|
+
|
|
176
|
+
# Check if it's a gRPC service class
|
|
177
|
+
if self._is_grpc_service(attr):
|
|
178
|
+
logger.debug(f"Found gRPC service class: {module_path}.{attr_name}")
|
|
179
|
+
|
|
180
|
+
# Try to get add_to_server function
|
|
181
|
+
add_to_server_func = self._get_add_to_server_func(attr, module_path)
|
|
182
|
+
|
|
183
|
+
if add_to_server_func:
|
|
184
|
+
services.append((attr, add_to_server_func))
|
|
185
|
+
|
|
186
|
+
return services
|
|
187
|
+
|
|
188
|
+
def _is_grpc_service(self, obj: Any) -> bool:
|
|
189
|
+
"""
|
|
190
|
+
Check if object is a gRPC service class.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
obj: Object to check
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
True if object is a gRPC service class
|
|
197
|
+
"""
|
|
198
|
+
# Check if it's a class
|
|
199
|
+
if not isinstance(obj, type):
|
|
200
|
+
return False
|
|
201
|
+
|
|
202
|
+
# Check for django-grpc-framework service
|
|
203
|
+
try:
|
|
204
|
+
from django_grpc_framework import generics
|
|
205
|
+
|
|
206
|
+
# Check if it inherits from any generics service
|
|
207
|
+
if issubclass(obj, (
|
|
208
|
+
generics.Service,
|
|
209
|
+
generics.ModelService,
|
|
210
|
+
generics.ReadOnlyModelService,
|
|
211
|
+
)):
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
except ImportError:
|
|
215
|
+
logger.debug("django-grpc-framework not installed, skipping service check")
|
|
216
|
+
|
|
217
|
+
# Check for grpc servicer (has add_to_server method)
|
|
218
|
+
if hasattr(obj, "add_to_server") and callable(getattr(obj, "add_to_server")):
|
|
219
|
+
return True
|
|
220
|
+
|
|
221
|
+
return False
|
|
222
|
+
|
|
223
|
+
def _get_add_to_server_func(self, service_class: Any, module_path: str) -> Optional[Any]:
|
|
224
|
+
"""
|
|
225
|
+
Get add_to_server function for a service class.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
service_class: Service class
|
|
229
|
+
module_path: Module path for logging
|
|
230
|
+
|
|
231
|
+
Returns:
|
|
232
|
+
add_to_server function or None
|
|
233
|
+
"""
|
|
234
|
+
# For django-grpc-framework services
|
|
235
|
+
if hasattr(service_class, "add_to_server"):
|
|
236
|
+
return getattr(service_class, "add_to_server")
|
|
237
|
+
|
|
238
|
+
# Try to find matching _pb2_grpc module
|
|
239
|
+
# Convention: myservice.grpc_services.MyService -> myservice_pb2_grpc.add_MyServiceServicer_to_server
|
|
240
|
+
try:
|
|
241
|
+
# Get the module name without the service module part
|
|
242
|
+
base_module = module_path.rsplit(".", 1)[0]
|
|
243
|
+
pb2_grpc_module_name = f"{base_module}_pb2_grpc"
|
|
244
|
+
|
|
245
|
+
pb2_grpc_module = importlib.import_module(pb2_grpc_module_name)
|
|
246
|
+
|
|
247
|
+
# Look for add_<ServiceName>_to_server function
|
|
248
|
+
func_name = f"add_{service_class.__name__}_to_server"
|
|
249
|
+
|
|
250
|
+
if hasattr(pb2_grpc_module, func_name):
|
|
251
|
+
return getattr(pb2_grpc_module, func_name)
|
|
252
|
+
|
|
253
|
+
except ImportError:
|
|
254
|
+
logger.debug(f"No _pb2_grpc module found for {module_path}")
|
|
255
|
+
except Exception as e:
|
|
256
|
+
logger.debug(f"Error finding add_to_server for {service_class.__name__}: {e}")
|
|
257
|
+
|
|
258
|
+
return None
|
|
259
|
+
|
|
260
|
+
def _load_custom_service(self, service_path: str) -> Optional[Tuple[Any, Any]]:
|
|
261
|
+
"""
|
|
262
|
+
Load a custom service from dotted path.
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
service_path: Dotted path to service class (e.g., 'myapp.services.MyService')
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
(service_class, add_to_server_func) tuple or None
|
|
269
|
+
"""
|
|
270
|
+
try:
|
|
271
|
+
# Split module path and class name
|
|
272
|
+
module_path, class_name = service_path.rsplit(".", 1)
|
|
273
|
+
|
|
274
|
+
# Import module
|
|
275
|
+
module = importlib.import_module(module_path)
|
|
276
|
+
|
|
277
|
+
# Get service class
|
|
278
|
+
service_class = getattr(module, class_name)
|
|
279
|
+
|
|
280
|
+
# Get add_to_server function
|
|
281
|
+
add_to_server_func = self._get_add_to_server_func(service_class, module_path)
|
|
282
|
+
|
|
283
|
+
if not add_to_server_func:
|
|
284
|
+
logger.warning(
|
|
285
|
+
f"Custom service {service_path} has no add_to_server function"
|
|
286
|
+
)
|
|
287
|
+
return None
|
|
288
|
+
|
|
289
|
+
logger.info(f"Loaded custom service: {service_path}")
|
|
290
|
+
return (service_class, add_to_server_func)
|
|
291
|
+
|
|
292
|
+
except ImportError as e:
|
|
293
|
+
logger.error(f"Failed to import custom service {service_path}: {e}")
|
|
294
|
+
return None
|
|
295
|
+
except AttributeError as e:
|
|
296
|
+
logger.error(f"Service class not found in {service_path}: {e}")
|
|
297
|
+
return None
|
|
298
|
+
except Exception as e:
|
|
299
|
+
logger.error(f"Error loading custom service {service_path}: {e}", exc_info=True)
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
def get_handlers_hook(self) -> Optional[Any]:
|
|
303
|
+
"""
|
|
304
|
+
Get the handlers hook function from ROOT_HANDLERS_HOOK setting.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
Handlers hook function or None
|
|
308
|
+
|
|
309
|
+
Example:
|
|
310
|
+
>>> discovery = ServiceDiscovery()
|
|
311
|
+
>>> handlers_hook = discovery.get_handlers_hook()
|
|
312
|
+
>>> if handlers_hook:
|
|
313
|
+
... services = handlers_hook(server)
|
|
314
|
+
"""
|
|
315
|
+
handlers_hook_path = self.grpc_config.get("ROOT_HANDLERS_HOOK")
|
|
316
|
+
|
|
317
|
+
if not handlers_hook_path:
|
|
318
|
+
logger.debug("No ROOT_HANDLERS_HOOK configured")
|
|
319
|
+
return None
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
# Import the module containing the hook
|
|
323
|
+
module_path, func_name = handlers_hook_path.rsplit(".", 1)
|
|
324
|
+
module = importlib.import_module(module_path)
|
|
325
|
+
|
|
326
|
+
# Get the hook function
|
|
327
|
+
handlers_hook = getattr(module, func_name)
|
|
328
|
+
|
|
329
|
+
if not callable(handlers_hook):
|
|
330
|
+
logger.warning(f"ROOT_HANDLERS_HOOK {handlers_hook_path} is not callable")
|
|
331
|
+
return None
|
|
332
|
+
|
|
333
|
+
logger.info(f"Loaded handlers hook: {handlers_hook_path}")
|
|
334
|
+
return handlers_hook
|
|
335
|
+
|
|
336
|
+
except ImportError as e:
|
|
337
|
+
logger.error(f"Failed to import handlers hook {handlers_hook_path}: {e}")
|
|
338
|
+
return None
|
|
339
|
+
except AttributeError as e:
|
|
340
|
+
logger.error(f"Handlers hook function not found in {handlers_hook_path}: {e}")
|
|
341
|
+
return None
|
|
342
|
+
except Exception as e:
|
|
343
|
+
logger.error(
|
|
344
|
+
f"Error loading handlers hook {handlers_hook_path}: {e}",
|
|
345
|
+
exc_info=True,
|
|
346
|
+
)
|
|
347
|
+
return None
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
def discover_and_register_services(server: Any) -> int:
|
|
351
|
+
"""
|
|
352
|
+
Discover and register all gRPC services to a server.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
server: gRPC server instance
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Number of services registered
|
|
359
|
+
|
|
360
|
+
Example:
|
|
361
|
+
```python
|
|
362
|
+
import grpc
|
|
363
|
+
from concurrent import futures
|
|
364
|
+
from django_cfg.apps.grpc.services.discovery import discover_and_register_services
|
|
365
|
+
|
|
366
|
+
# Create server
|
|
367
|
+
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
|
|
368
|
+
|
|
369
|
+
# Auto-discover and register services
|
|
370
|
+
count = discover_and_register_services(server)
|
|
371
|
+
print(f"Registered {count} services")
|
|
372
|
+
|
|
373
|
+
# Start server
|
|
374
|
+
server.add_insecure_port('[::]:50051')
|
|
375
|
+
server.start()
|
|
376
|
+
```
|
|
377
|
+
"""
|
|
378
|
+
discovery = ServiceDiscovery()
|
|
379
|
+
count = 0
|
|
380
|
+
|
|
381
|
+
# Try handlers hook first
|
|
382
|
+
handlers_hook = discovery.get_handlers_hook()
|
|
383
|
+
if handlers_hook:
|
|
384
|
+
try:
|
|
385
|
+
handlers_hook(server)
|
|
386
|
+
logger.info("Successfully called handlers hook")
|
|
387
|
+
count += 1 # We don't know exact count, but at least 1
|
|
388
|
+
except Exception as e:
|
|
389
|
+
logger.error(f"Error calling handlers hook: {e}", exc_info=True)
|
|
390
|
+
|
|
391
|
+
# Discover and register services
|
|
392
|
+
services = discovery.discover_services()
|
|
393
|
+
|
|
394
|
+
for service_class, add_to_server_func in services:
|
|
395
|
+
try:
|
|
396
|
+
# Instantiate service
|
|
397
|
+
servicer = service_class()
|
|
398
|
+
|
|
399
|
+
# Register with server
|
|
400
|
+
add_to_server_func(servicer, server)
|
|
401
|
+
|
|
402
|
+
logger.debug(f"Registered service: {service_class.__name__}")
|
|
403
|
+
count += 1
|
|
404
|
+
|
|
405
|
+
except Exception as e:
|
|
406
|
+
logger.error(
|
|
407
|
+
f"Failed to register service {service_class.__name__}: {e}",
|
|
408
|
+
exc_info=True,
|
|
409
|
+
)
|
|
410
|
+
|
|
411
|
+
logger.info(f"Registered {count} gRPC service(s)")
|
|
412
|
+
return count
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
__all__ = ["ServiceDiscovery", "discover_and_register_services"]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""
|
|
2
|
+
URL patterns for gRPC module.
|
|
3
|
+
|
|
4
|
+
Public API endpoints for gRPC monitoring.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from django.urls import include, path
|
|
8
|
+
from rest_framework import routers
|
|
9
|
+
|
|
10
|
+
from .views.monitoring import GRPCMonitorViewSet
|
|
11
|
+
|
|
12
|
+
app_name = 'django_cfg_grpc'
|
|
13
|
+
|
|
14
|
+
# Create router
|
|
15
|
+
router = routers.DefaultRouter()
|
|
16
|
+
|
|
17
|
+
# Monitoring endpoints (Django logs based)
|
|
18
|
+
router.register(r'monitor', GRPCMonitorViewSet, basename='monitor')
|
|
19
|
+
|
|
20
|
+
urlpatterns = [
|
|
21
|
+
# Include router URLs
|
|
22
|
+
path('', include(router.urls)),
|
|
23
|
+
]
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
gRPC utilities.
|
|
3
|
+
|
|
4
|
+
Provides proto generation and other helper utilities.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from .proto_gen import ProtoFieldMapper, ProtoGenerator, generate_proto_for_app
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"ProtoFieldMapper",
|
|
11
|
+
"ProtoGenerator",
|
|
12
|
+
"generate_proto_for_app",
|
|
13
|
+
]
|