django-cfg 1.4.10__py3-none-any.whl → 1.4.11__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 (181) hide show
  1. django_cfg/apps/agents/management/commands/create_agent.py +1 -1
  2. django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
  3. django_cfg/apps/newsletter/serializers.py +40 -3
  4. django_cfg/apps/newsletter/views/campaigns.py +12 -3
  5. django_cfg/apps/newsletter/views/emails.py +14 -3
  6. django_cfg/apps/newsletter/views/subscriptions.py +12 -2
  7. django_cfg/apps/payments/views/api/currencies.py +49 -6
  8. django_cfg/apps/payments/views/api/webhooks.py +72 -7
  9. django_cfg/apps/payments/views/overview/serializers.py +34 -1
  10. django_cfg/apps/payments/views/overview/views.py +2 -1
  11. django_cfg/apps/payments/views/serializers/payments.py +6 -6
  12. django_cfg/apps/urls.py +106 -45
  13. django_cfg/core/base/config_model.py +2 -2
  14. django_cfg/core/constants.py +1 -1
  15. django_cfg/core/generation/integration_generators/__init__.py +1 -1
  16. django_cfg/core/generation/integration_generators/api.py +72 -49
  17. django_cfg/core/integration/display/startup.py +30 -22
  18. django_cfg/core/integration/url_integration.py +15 -16
  19. django_cfg/dashboard/sections/documentation.py +391 -0
  20. django_cfg/management/commands/check_endpoints.py +11 -160
  21. django_cfg/management/commands/check_settings.py +13 -348
  22. django_cfg/management/commands/clear_constance.py +13 -201
  23. django_cfg/management/commands/create_token.py +13 -321
  24. django_cfg/management/commands/generate_clients.py +23 -0
  25. django_cfg/management/commands/list_urls.py +13 -306
  26. django_cfg/management/commands/migrate_all.py +13 -126
  27. django_cfg/management/commands/migrator.py +13 -396
  28. django_cfg/management/commands/rundramatiq.py +15 -247
  29. django_cfg/management/commands/rundramatiq_simulator.py +12 -429
  30. django_cfg/management/commands/runserver_ngrok.py +15 -160
  31. django_cfg/management/commands/script.py +12 -488
  32. django_cfg/management/commands/show_config.py +12 -215
  33. django_cfg/management/commands/show_urls.py +12 -342
  34. django_cfg/management/commands/superuser.py +15 -295
  35. django_cfg/management/commands/task_clear.py +14 -217
  36. django_cfg/management/commands/task_status.py +13 -248
  37. django_cfg/management/commands/test_email.py +15 -86
  38. django_cfg/management/commands/test_telegram.py +14 -61
  39. django_cfg/management/commands/test_twilio.py +15 -105
  40. django_cfg/management/commands/tree.py +13 -383
  41. django_cfg/management/commands/validate_openapi.py +10 -0
  42. django_cfg/middleware/README.md +1 -1
  43. django_cfg/middleware/user_activity.py +3 -3
  44. django_cfg/models/__init__.py +2 -2
  45. django_cfg/models/api/drf/spectacular.py +6 -6
  46. django_cfg/models/django/__init__.py +2 -2
  47. django_cfg/models/django/openapi.py +238 -0
  48. django_cfg/modules/django_admin/management/__init__.py +0 -0
  49. django_cfg/modules/django_admin/management/commands/__init__.py +0 -0
  50. django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
  51. django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
  52. django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
  53. django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
  54. django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
  55. django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
  56. django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
  57. django_cfg/modules/django_admin/management/commands/script.py +496 -0
  58. django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
  59. django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
  60. django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
  61. django_cfg/modules/django_admin/management/commands/tree.py +390 -0
  62. django_cfg/modules/django_client/__init__.py +20 -0
  63. django_cfg/modules/django_client/apps.py +35 -0
  64. django_cfg/modules/django_client/core/__init__.py +56 -0
  65. django_cfg/modules/django_client/core/archive/__init__.py +11 -0
  66. django_cfg/modules/django_client/core/archive/manager.py +134 -0
  67. django_cfg/modules/django_client/core/cli/__init__.py +12 -0
  68. django_cfg/modules/django_client/core/cli/main.py +235 -0
  69. django_cfg/modules/django_client/core/config/__init__.py +18 -0
  70. django_cfg/modules/django_client/core/config/config.py +188 -0
  71. django_cfg/modules/django_client/core/config/group.py +101 -0
  72. django_cfg/modules/django_client/core/config/service.py +209 -0
  73. django_cfg/modules/django_client/core/generator/__init__.py +115 -0
  74. django_cfg/modules/django_client/core/generator/base.py +767 -0
  75. django_cfg/modules/django_client/core/generator/python.py +751 -0
  76. django_cfg/modules/django_client/core/generator/templates/python/__init__.py.jinja +9 -0
  77. django_cfg/modules/django_client/core/generator/templates/python/api_wrapper.py.jinja +130 -0
  78. django_cfg/modules/django_client/core/generator/templates/python/app_init.py.jinja +6 -0
  79. django_cfg/modules/django_client/core/generator/templates/python/client/app_client.py.jinja +18 -0
  80. django_cfg/modules/django_client/core/generator/templates/python/client/flat_client.py.jinja +38 -0
  81. django_cfg/modules/django_client/core/generator/templates/python/client/main_client.py.jinja +50 -0
  82. django_cfg/modules/django_client/core/generator/templates/python/client/main_client_file.py.jinja +13 -0
  83. django_cfg/modules/django_client/core/generator/templates/python/client/operation_method.py.jinja +7 -0
  84. django_cfg/modules/django_client/core/generator/templates/python/client/sub_client.py.jinja +11 -0
  85. django_cfg/modules/django_client/core/generator/templates/python/client_file.py.jinja +13 -0
  86. django_cfg/modules/django_client/core/generator/templates/python/main_init.py.jinja +50 -0
  87. django_cfg/modules/django_client/core/generator/templates/python/models/app_models.py.jinja +17 -0
  88. django_cfg/modules/django_client/core/generator/templates/python/models/enum_class.py.jinja +15 -0
  89. django_cfg/modules/django_client/core/generator/templates/python/models/enums.py.jinja +8 -0
  90. django_cfg/modules/django_client/core/generator/templates/python/models/models.py.jinja +17 -0
  91. django_cfg/modules/django_client/core/generator/templates/python/models/schema_class.py.jinja +19 -0
  92. django_cfg/modules/django_client/core/generator/templates/python/utils/logger.py.jinja +255 -0
  93. django_cfg/modules/django_client/core/generator/templates/python/utils/schema.py.jinja +12 -0
  94. django_cfg/modules/django_client/core/generator/templates/typescript/app_index.ts.jinja +2 -0
  95. django_cfg/modules/django_client/core/generator/templates/typescript/client/app_client.ts.jinja +18 -0
  96. django_cfg/modules/django_client/core/generator/templates/typescript/client/client.ts.jinja +327 -0
  97. django_cfg/modules/django_client/core/generator/templates/typescript/client/flat_client.ts.jinja +109 -0
  98. django_cfg/modules/django_client/core/generator/templates/typescript/client/main_client_file.ts.jinja +9 -0
  99. django_cfg/modules/django_client/core/generator/templates/typescript/client/operation.ts.jinja +61 -0
  100. django_cfg/modules/django_client/core/generator/templates/typescript/client/sub_client.ts.jinja +15 -0
  101. django_cfg/modules/django_client/core/generator/templates/typescript/client_file.ts.jinja +9 -0
  102. django_cfg/modules/django_client/core/generator/templates/typescript/index.ts.jinja +5 -0
  103. django_cfg/modules/django_client/core/generator/templates/typescript/main_index.ts.jinja +206 -0
  104. django_cfg/modules/django_client/core/generator/templates/typescript/models/app_models.ts.jinja +8 -0
  105. django_cfg/modules/django_client/core/generator/templates/typescript/models/enums.ts.jinja +4 -0
  106. django_cfg/modules/django_client/core/generator/templates/typescript/models/models.ts.jinja +8 -0
  107. django_cfg/modules/django_client/core/generator/templates/typescript/utils/errors.ts.jinja +114 -0
  108. django_cfg/modules/django_client/core/generator/templates/typescript/utils/http.ts.jinja +98 -0
  109. django_cfg/modules/django_client/core/generator/templates/typescript/utils/logger.ts.jinja +251 -0
  110. django_cfg/modules/django_client/core/generator/templates/typescript/utils/schema.ts.jinja +7 -0
  111. django_cfg/modules/django_client/core/generator/templates/typescript/utils/storage.ts.jinja +114 -0
  112. django_cfg/modules/django_client/core/generator/typescript.py +872 -0
  113. django_cfg/modules/django_client/core/groups/__init__.py +13 -0
  114. django_cfg/modules/django_client/core/groups/detector.py +178 -0
  115. django_cfg/modules/django_client/core/groups/manager.py +314 -0
  116. django_cfg/modules/django_client/core/ir/__init__.py +57 -0
  117. django_cfg/modules/django_client/core/ir/context.py +387 -0
  118. django_cfg/modules/django_client/core/ir/operation.py +518 -0
  119. django_cfg/modules/django_client/core/ir/schema.py +353 -0
  120. django_cfg/modules/django_client/core/parser/__init__.py +74 -0
  121. django_cfg/modules/django_client/core/parser/base.py +648 -0
  122. django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
  123. django_cfg/modules/django_client/core/parser/models/base.py +212 -0
  124. django_cfg/modules/django_client/core/parser/models/components.py +160 -0
  125. django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
  126. django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
  127. django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
  128. django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
  129. django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
  130. django_cfg/modules/django_client/core/validation/__init__.py +22 -0
  131. django_cfg/modules/django_client/core/validation/checker.py +134 -0
  132. django_cfg/modules/django_client/core/validation/fixer.py +216 -0
  133. django_cfg/modules/django_client/core/validation/reporter.py +480 -0
  134. django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
  135. django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
  136. django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
  137. django_cfg/modules/django_client/core/validation/safety.py +266 -0
  138. django_cfg/modules/django_client/management/__init__.py +3 -0
  139. django_cfg/modules/django_client/management/commands/__init__.py +3 -0
  140. django_cfg/modules/django_client/management/commands/generate_client.py +422 -0
  141. django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
  142. django_cfg/modules/django_client/spectacular/__init__.py +9 -0
  143. django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
  144. django_cfg/modules/django_client/urls.py +72 -0
  145. django_cfg/modules/django_email/management/__init__.py +0 -0
  146. django_cfg/modules/django_email/management/commands/__init__.py +0 -0
  147. django_cfg/modules/django_email/management/commands/test_email.py +93 -0
  148. django_cfg/modules/django_logging/django_logger.py +6 -6
  149. django_cfg/modules/django_ngrok/management/__init__.py +0 -0
  150. django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
  151. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
  152. django_cfg/modules/django_tasks/management/__init__.py +0 -0
  153. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  154. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
  155. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
  156. django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
  157. django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
  158. django_cfg/modules/django_telegram/management/__init__.py +0 -0
  159. django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
  160. django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
  161. django_cfg/modules/django_twilio/management/__init__.py +0 -0
  162. django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
  163. django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
  164. django_cfg/modules/django_unfold/callbacks/main.py +16 -5
  165. django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
  166. django_cfg/pyproject.toml +2 -6
  167. django_cfg/registry/third_party.py +5 -7
  168. django_cfg/routing/callbacks.py +1 -1
  169. django_cfg/static/admin/css/prose-unfold.css +666 -0
  170. django_cfg/templates/admin/index.html +8 -0
  171. django_cfg/templates/admin/index_new.html +13 -0
  172. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
  173. django_cfg/templates/admin/sections/documentation_section.html +172 -0
  174. django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
  175. {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/METADATA +2 -2
  176. {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/RECORD +180 -59
  177. django_cfg/management/commands/generate.py +0 -107
  178. /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
  179. {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/WHEEL +0 -0
  180. {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/entry_points.txt +0 -0
  181. {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,238 @@
1
+ """
2
+ Django Client (OpenAPI) Configuration with DRF Integration
3
+
4
+ Extended configuration model that integrates openapi_client for automatic
5
+ TypeScript and Python client generation from Django REST Framework endpoints.
6
+
7
+ This replaces django-revolution with a cleaner, faster, type-safe implementation.
8
+ """
9
+
10
+ from typing import Dict, Any, Optional
11
+ from pydantic import BaseModel, Field
12
+ from django_cfg.modules.django_client.core.config import OpenAPIConfig, OpenAPIGroupConfig
13
+
14
+
15
+ class ExtendedOpenAPIConfig(OpenAPIConfig):
16
+ """
17
+ Extended OpenAPI configuration with DRF parameters for django-cfg integration.
18
+
19
+ This extends the base OpenAPIConfig to include DRF-specific
20
+ parameters and django-cfg integration helpers.
21
+
22
+ Example:
23
+ ```python
24
+ from django_cfg import ExtendedOpenAPIConfig, OpenAPIGroupConfig
25
+
26
+ config = ExtendedOpenAPIConfig(
27
+ enabled=True,
28
+ groups=[
29
+ OpenAPIGroupConfig(
30
+ name='api',
31
+ apps=['users', 'posts'],
32
+ title='Main API',
33
+ version='v1',
34
+ ),
35
+ ],
36
+ drf_title='My API',
37
+ drf_description='REST API for my project',
38
+ )
39
+ ```
40
+ """
41
+
42
+ # DRF Configuration parameters for automatic DRF setup
43
+ drf_title: str = Field(
44
+ default="API",
45
+ description="API title for DRF Spectacular"
46
+ )
47
+ drf_description: str = Field(
48
+ default="RESTful API",
49
+ description="API description for DRF Spectacular"
50
+ )
51
+ drf_version: str = Field(
52
+ default="1.0.0",
53
+ description="API version for DRF Spectacular"
54
+ )
55
+ drf_schema_path_prefix: Optional[str] = Field(
56
+ default=None, # Will default to "/api/" if None
57
+ description="Schema path prefix for DRF Spectacular"
58
+ )
59
+ drf_enable_browsable_api: bool = Field(
60
+ default=False,
61
+ description="Enable DRF browsable API"
62
+ )
63
+ drf_enable_throttling: bool = Field(
64
+ default=False,
65
+ description="Enable DRF throttling"
66
+ )
67
+ drf_serve_include_schema: bool = Field(
68
+ default=False,
69
+ description="Include schema in Spectacular UI"
70
+ )
71
+
72
+ # Django-cfg specific integration
73
+ api_prefix: str = Field(
74
+ default="api",
75
+ description="API prefix for URL routing (e.g., 'api' -> /api/...)"
76
+ )
77
+
78
+ def get_drf_schema_path_prefix(self) -> str:
79
+ """Get the schema path prefix, defaulting to api_prefix if not set."""
80
+ if self.drf_schema_path_prefix:
81
+ return self.drf_schema_path_prefix
82
+ return f"/{self.api_prefix}/"
83
+
84
+ def get_drf_config_kwargs(self) -> Dict[str, Any]:
85
+ """
86
+ Get kwargs for DRF configuration from this config.
87
+
88
+ Returns:
89
+ Dict of parameters for DRF + Spectacular setup
90
+ """
91
+ return {
92
+ "title": self.drf_title,
93
+ "description": self.drf_description,
94
+ "version": self.drf_version,
95
+ "schema_path_prefix": self.get_drf_schema_path_prefix(),
96
+ "enable_browsable_api": self.drf_enable_browsable_api,
97
+ "enable_throttling": self.drf_enable_throttling,
98
+ "serve_include_schema": self.drf_serve_include_schema,
99
+ # REQUIRED by django-client for correct Request/Response split
100
+ "component_split_request": True,
101
+ "component_split_patch": True,
102
+ }
103
+
104
+ def get_groups_with_defaults(self) -> Dict[str, OpenAPIGroupConfig]:
105
+ """
106
+ Get groups with django-cfg default groups automatically added.
107
+
108
+ Returns:
109
+ Dict of groups including default django-cfg groups
110
+ """
111
+ # Convert list to dict for compatibility
112
+ groups_dict = {group.name: group for group in self.groups}
113
+
114
+ # Add default django-cfg groups if enabled
115
+ try:
116
+ from django_cfg.modules.base import BaseCfgModule
117
+ base_module = BaseCfgModule()
118
+
119
+ support_enabled = base_module.is_support_enabled()
120
+ accounts_enabled = base_module.is_accounts_enabled()
121
+ newsletter_enabled = base_module.is_newsletter_enabled()
122
+ leads_enabled = base_module.is_leads_enabled()
123
+ knowbase_enabled = base_module.is_knowbase_enabled()
124
+ agents_enabled = base_module.is_agents_enabled()
125
+ tasks_enabled = base_module.should_enable_tasks()
126
+ payments_enabled = base_module.is_payments_enabled()
127
+
128
+ # Collect all enabled django-cfg apps for unified group
129
+ enabled_cfg_apps = []
130
+ if support_enabled:
131
+ enabled_cfg_apps.append("django_cfg.apps.support")
132
+ if accounts_enabled:
133
+ enabled_cfg_apps.append("django_cfg.apps.accounts")
134
+ if newsletter_enabled:
135
+ enabled_cfg_apps.append("django_cfg.apps.newsletter")
136
+ if leads_enabled:
137
+ enabled_cfg_apps.append("django_cfg.apps.leads")
138
+ if knowbase_enabled:
139
+ enabled_cfg_apps.append("django_cfg.apps.knowbase")
140
+ if agents_enabled:
141
+ enabled_cfg_apps.append("django_cfg.apps.agents")
142
+ if tasks_enabled:
143
+ enabled_cfg_apps.append("django_cfg.apps.tasks")
144
+ if payments_enabled:
145
+ enabled_cfg_apps.append("django_cfg.apps.payments")
146
+
147
+ # Add unified 'cfg' group with all enabled apps
148
+ if enabled_cfg_apps and 'cfg' not in groups_dict:
149
+ groups_dict['cfg'] = OpenAPIGroupConfig(
150
+ name="cfg",
151
+ apps=enabled_cfg_apps,
152
+ title="Django-CFG API",
153
+ description="All django-cfg built-in applications",
154
+ )
155
+
156
+ # Skip individual cfg_* groups - use unified 'cfg' instead
157
+ return groups_dict
158
+
159
+ # Add Support group if enabled
160
+ if support_enabled and 'cfg_support' not in groups_dict:
161
+ groups_dict['cfg_support'] = OpenAPIGroupConfig(
162
+ name="cfg_support",
163
+ apps=["django_cfg.apps.support"],
164
+ title="Support API",
165
+ description="Support tickets and messages API",
166
+ )
167
+
168
+ # Add Accounts group if enabled
169
+ if accounts_enabled and 'cfg_accounts' not in groups_dict:
170
+ groups_dict['cfg_accounts'] = OpenAPIGroupConfig(
171
+ name="cfg_accounts",
172
+ apps=["django_cfg.apps.accounts"],
173
+ title="Accounts API",
174
+ description="User management, OTP, profiles, and activity tracking API",
175
+ )
176
+
177
+ # Add Newsletter group if enabled
178
+ if newsletter_enabled and 'cfg_newsletter' not in groups_dict:
179
+ groups_dict['cfg_newsletter'] = OpenAPIGroupConfig(
180
+ name="cfg_newsletter",
181
+ apps=["django_cfg.apps.newsletter"],
182
+ title="Newsletter API",
183
+ description="Email campaigns, subscriptions, and newsletter management API",
184
+ )
185
+
186
+ # Add Leads group if enabled
187
+ if leads_enabled and 'cfg_leads' not in groups_dict:
188
+ groups_dict['cfg_leads'] = OpenAPIGroupConfig(
189
+ name="cfg_leads",
190
+ apps=["django_cfg.apps.leads"],
191
+ title="Leads API",
192
+ description="Lead collection, contact forms, and CRM integration API",
193
+ )
194
+
195
+ # Add Knowbase group if enabled
196
+ if knowbase_enabled and 'cfg_knowbase' not in groups_dict:
197
+ groups_dict['cfg_knowbase'] = OpenAPIGroupConfig(
198
+ name="cfg_knowbase",
199
+ apps=["django_cfg.apps.knowbase"],
200
+ title="Knowbase API",
201
+ description="Knowledge base, AI chat, embeddings, and search API",
202
+ )
203
+
204
+ # Add Agents group if enabled
205
+ if agents_enabled and 'cfg_agents' not in groups_dict:
206
+ groups_dict['cfg_agents'] = OpenAPIGroupConfig(
207
+ name="cfg_agents",
208
+ apps=["django_cfg.apps.agents"],
209
+ title="Agents API",
210
+ description="Agent definitions, executions, workflows, and tools API",
211
+ )
212
+
213
+ # Add Tasks group if enabled
214
+ if tasks_enabled and 'cfg_tasks' not in groups_dict:
215
+ groups_dict['cfg_tasks'] = OpenAPIGroupConfig(
216
+ name="cfg_tasks",
217
+ apps=["django_cfg.apps.tasks"],
218
+ title="Tasks API",
219
+ description="Tasks, workflows, and automation API",
220
+ )
221
+
222
+ # Add Payments group if enabled
223
+ if payments_enabled and 'cfg_payments' not in groups_dict:
224
+ groups_dict['cfg_payments'] = OpenAPIGroupConfig(
225
+ name="cfg_payments",
226
+ apps=["django_cfg.apps.payments"],
227
+ title="Payments API",
228
+ description="Payments, subscriptions, and billing API",
229
+ )
230
+
231
+ except Exception:
232
+ pass
233
+
234
+ return groups_dict
235
+
236
+
237
+ # Alias for easier import
238
+ OpenAPIClientConfig = ExtendedOpenAPIConfig
File without changes
@@ -0,0 +1,169 @@
1
+ """
2
+ Django management command to check all API endpoints status.
3
+
4
+ Usage:
5
+ python manage.py check_endpoints
6
+ python manage.py check_endpoints --include-unnamed
7
+ python manage.py check_endpoints --timeout 10
8
+ python manage.py check_endpoints --json
9
+ """
10
+
11
+ from django.core.management.base import BaseCommand
12
+ from django.urls import reverse
13
+ from django_cfg.apps.api.endpoints.checker import check_all_endpoints
14
+ import json
15
+
16
+
17
+ class Command(BaseCommand):
18
+ help = 'Check status of all Django CFG API endpoints'
19
+
20
+ def add_arguments(self, parser):
21
+ parser.add_argument(
22
+ '--include-unnamed',
23
+ action='store_true',
24
+ help='Include unnamed URL patterns in the check',
25
+ )
26
+ parser.add_argument(
27
+ '--timeout',
28
+ type=int,
29
+ default=5,
30
+ help='Request timeout in seconds (default: 5)',
31
+ )
32
+ parser.add_argument(
33
+ '--json',
34
+ action='store_true',
35
+ help='Output results as JSON',
36
+ )
37
+ parser.add_argument(
38
+ '--url',
39
+ type=str,
40
+ help='Check specific endpoint by URL name (e.g., "endpoints_status")',
41
+ )
42
+ parser.add_argument(
43
+ '--no-auth',
44
+ action='store_true',
45
+ help='Disable automatic JWT authentication retry (default: enabled)',
46
+ )
47
+
48
+ def handle(self, *args, **options):
49
+ include_unnamed = options['include_unnamed']
50
+ timeout = options['timeout']
51
+ output_json = options['json']
52
+ url_name = options.get('url')
53
+ auto_auth = not options['no_auth'] # Auto-auth enabled by default
54
+
55
+ # If specific URL requested, just resolve and display it
56
+ if url_name:
57
+ try:
58
+ url = reverse(url_name)
59
+ self.stdout.write(self.style.SUCCESS(f'✅ URL name "{url_name}" resolves to: {url}'))
60
+ return
61
+ except Exception as e:
62
+ self.stdout.write(self.style.ERROR(f'❌ Error resolving URL "{url_name}": {e}'))
63
+ return
64
+
65
+ # Check all endpoints
66
+ auth_msg = "with auto-auth" if auto_auth else "without auth"
67
+ self.stdout.write(self.style.WARNING(f'🔍 Checking endpoints (timeout: {timeout}s, {auth_msg})...'))
68
+
69
+ status_data = check_all_endpoints(
70
+ include_unnamed=include_unnamed,
71
+ timeout=timeout,
72
+ auto_auth=auto_auth
73
+ )
74
+
75
+ # Output as JSON if requested
76
+ if output_json:
77
+ self.stdout.write(json.dumps(status_data, indent=2))
78
+ return
79
+
80
+ # Pretty print results
81
+ self._print_results(status_data)
82
+
83
+ def _print_results(self, data):
84
+ """Print formatted results to console."""
85
+
86
+ # Overall status
87
+ status = data['status']
88
+ if status == 'healthy':
89
+ status_style = self.style.SUCCESS
90
+ emoji = '✅'
91
+ elif status == 'degraded':
92
+ status_style = self.style.WARNING
93
+ emoji = '⚠️'
94
+ else:
95
+ status_style = self.style.ERROR
96
+ emoji = '❌'
97
+
98
+ self.stdout.write('')
99
+ self.stdout.write(status_style(f'{emoji} Overall Status: {status.upper()}'))
100
+ self.stdout.write('')
101
+
102
+ # Summary
103
+ self.stdout.write(self.style.HTTP_INFO('📊 Summary:'))
104
+ self.stdout.write(f' Total endpoints: {data["total_endpoints"]}')
105
+ self.stdout.write(self.style.SUCCESS(f' ✅ Healthy: {data["healthy"]}'))
106
+ self.stdout.write(self.style.WARNING(f' ⚠️ Warnings: {data["warnings"]}'))
107
+ self.stdout.write(self.style.ERROR(f' ❌ Unhealthy: {data["unhealthy"]}'))
108
+ self.stdout.write(self.style.ERROR(f' ❌ Errors: {data["errors"]}'))
109
+ self.stdout.write(f' ⏭️ Skipped: {data["skipped"]}')
110
+ self.stdout.write('')
111
+
112
+ # Endpoints details
113
+ self.stdout.write(self.style.HTTP_INFO('🔗 Endpoints:'))
114
+
115
+ for endpoint in data['endpoints']:
116
+ name = endpoint.get('url_name') or 'unnamed'
117
+ url = endpoint['url']
118
+ status = endpoint['status']
119
+
120
+ if status == 'healthy':
121
+ icon = '✅'
122
+ style = self.style.SUCCESS
123
+ elif status == 'degraded':
124
+ icon = '⚠️'
125
+ style = self.style.WARNING
126
+ else:
127
+ icon = '❌'
128
+ style = self.style.ERROR
129
+
130
+ self.stdout.write(f' {icon} {name}')
131
+
132
+ # Show both pattern and resolved URL for parametrized endpoints
133
+ if endpoint.get('has_parameters') and endpoint.get('url_pattern'):
134
+ self.stdout.write(f' Pattern: {endpoint["url_pattern"]}')
135
+ self.stdout.write(f' Resolved: {url}')
136
+ else:
137
+ self.stdout.write(f' URL: {url}')
138
+
139
+ # Show status with status code
140
+ status_code = endpoint.get('status_code')
141
+ if status_code:
142
+ self.stdout.write(style(f' Status: {status} ({status_code})'))
143
+ else:
144
+ self.stdout.write(style(f' Status: {status}'))
145
+
146
+ if endpoint.get('response_time_ms'):
147
+ self.stdout.write(f' Response time: {endpoint["response_time_ms"]:.2f}ms')
148
+
149
+ if endpoint.get('error'):
150
+ error_type = endpoint.get('error_type', 'general')
151
+ if error_type == 'database':
152
+ self.stdout.write(self.style.WARNING(f' ⚠️ DB Error (multi-db): {endpoint["error"]}'))
153
+ else:
154
+ self.stdout.write(self.style.ERROR(f' Error: {endpoint["error"]}'))
155
+
156
+ # Show reason for warnings (e.g., 404 explanations)
157
+ if endpoint.get('reason') and status == 'warning':
158
+ self.stdout.write(self.style.WARNING(f' ⚠️ {endpoint["reason"]}'))
159
+
160
+ if endpoint.get('required_auth'):
161
+ self.stdout.write(f' 🔐 Required JWT authentication')
162
+
163
+ if endpoint.get('rate_limited'):
164
+ self.stdout.write(f' ⏱️ Rate limited (429)')
165
+
166
+ self.stdout.write('')
167
+
168
+ # Timestamp
169
+ self.stdout.write(self.style.HTTP_INFO(f'🕐 Checked at: {data["timestamp"]}'))