django-cfg 1.4.10__py3-none-any.whl → 1.4.13__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 (225) 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 +73 -49
  17. django_cfg/core/integration/display/startup.py +30 -22
  18. django_cfg/core/integration/url_integration.py +15 -16
  19. django_cfg/management/commands/check_endpoints.py +11 -160
  20. django_cfg/management/commands/check_settings.py +13 -348
  21. django_cfg/management/commands/clear_constance.py +13 -201
  22. django_cfg/management/commands/create_token.py +13 -321
  23. django_cfg/management/commands/generate_clients.py +23 -0
  24. django_cfg/management/commands/list_urls.py +13 -306
  25. django_cfg/management/commands/migrate_all.py +13 -126
  26. django_cfg/management/commands/migrator.py +13 -396
  27. django_cfg/management/commands/rundramatiq.py +15 -247
  28. django_cfg/management/commands/rundramatiq_simulator.py +12 -429
  29. django_cfg/management/commands/runserver_ngrok.py +15 -160
  30. django_cfg/management/commands/script.py +12 -488
  31. django_cfg/management/commands/show_config.py +12 -215
  32. django_cfg/management/commands/show_urls.py +12 -342
  33. django_cfg/management/commands/superuser.py +15 -295
  34. django_cfg/management/commands/task_clear.py +14 -217
  35. django_cfg/management/commands/task_status.py +13 -248
  36. django_cfg/management/commands/test_email.py +15 -86
  37. django_cfg/management/commands/test_telegram.py +14 -61
  38. django_cfg/management/commands/test_twilio.py +15 -105
  39. django_cfg/management/commands/tree.py +13 -383
  40. django_cfg/management/commands/validate_openapi.py +10 -0
  41. django_cfg/middleware/README.md +1 -1
  42. django_cfg/middleware/user_activity.py +3 -3
  43. django_cfg/models/__init__.py +2 -2
  44. django_cfg/models/api/drf/spectacular.py +6 -6
  45. django_cfg/models/django/__init__.py +2 -2
  46. django_cfg/models/django/openapi.py +162 -0
  47. django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
  48. django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
  49. django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
  50. django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
  51. django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
  52. django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
  53. django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
  54. django_cfg/modules/django_admin/management/commands/script.py +496 -0
  55. django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
  56. django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
  57. django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
  58. django_cfg/modules/django_admin/management/commands/tree.py +390 -0
  59. django_cfg/modules/django_client/__init__.py +20 -0
  60. django_cfg/modules/django_client/apps.py +35 -0
  61. django_cfg/modules/django_client/core/__init__.py +56 -0
  62. django_cfg/modules/django_client/core/archive/__init__.py +11 -0
  63. django_cfg/modules/django_client/core/archive/manager.py +134 -0
  64. django_cfg/modules/django_client/core/cli/__init__.py +12 -0
  65. django_cfg/modules/django_client/core/cli/main.py +235 -0
  66. django_cfg/modules/django_client/core/config/__init__.py +18 -0
  67. django_cfg/modules/django_client/core/config/config.py +208 -0
  68. django_cfg/modules/django_client/core/config/group.py +101 -0
  69. django_cfg/modules/django_client/core/config/service.py +209 -0
  70. django_cfg/modules/django_client/core/generator/__init__.py +115 -0
  71. django_cfg/modules/django_client/core/generator/base.py +838 -0
  72. django_cfg/modules/django_client/core/generator/python/__init__.py +16 -0
  73. django_cfg/modules/django_client/core/generator/python/async_client_gen.py +174 -0
  74. django_cfg/modules/django_client/core/generator/python/files_generator.py +180 -0
  75. django_cfg/modules/django_client/core/generator/python/generator.py +182 -0
  76. django_cfg/modules/django_client/core/generator/python/models_generator.py +318 -0
  77. django_cfg/modules/django_client/core/generator/python/operations_generator.py +278 -0
  78. django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +102 -0
  79. django_cfg/modules/django_client/core/generator/python/templates/__init__.py.jinja +9 -0
  80. django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +153 -0
  81. django_cfg/modules/django_client/core/generator/python/templates/app_init.py.jinja +6 -0
  82. django_cfg/modules/django_client/core/generator/python/templates/client/app_client.py.jinja +18 -0
  83. django_cfg/modules/django_client/core/generator/python/templates/client/flat_client.py.jinja +38 -0
  84. django_cfg/modules/django_client/core/generator/python/templates/client/main_client.py.jinja +68 -0
  85. django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja +14 -0
  86. django_cfg/modules/django_client/core/generator/python/templates/client/operation_method.py.jinja +9 -0
  87. django_cfg/modules/django_client/core/generator/python/templates/client/sub_client.py.jinja +18 -0
  88. django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja +50 -0
  89. django_cfg/modules/django_client/core/generator/python/templates/client/sync_operation_method.py.jinja +9 -0
  90. django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja +18 -0
  91. django_cfg/modules/django_client/core/generator/python/templates/client_file.py.jinja +13 -0
  92. django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +52 -0
  93. django_cfg/modules/django_client/core/generator/python/templates/models/app_models.py.jinja +17 -0
  94. django_cfg/modules/django_client/core/generator/python/templates/models/enum_class.py.jinja +17 -0
  95. django_cfg/modules/django_client/core/generator/python/templates/models/enums.py.jinja +8 -0
  96. django_cfg/modules/django_client/core/generator/python/templates/models/models.py.jinja +17 -0
  97. django_cfg/modules/django_client/core/generator/python/templates/models/schema_class.py.jinja +21 -0
  98. django_cfg/modules/django_client/core/generator/python/templates/pyproject.toml.jinja +55 -0
  99. django_cfg/modules/django_client/core/generator/python/templates/utils/logger.py.jinja +255 -0
  100. django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.jinja +271 -0
  101. django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja +12 -0
  102. django_cfg/modules/django_client/core/generator/typescript/__init__.py +14 -0
  103. django_cfg/modules/django_client/core/generator/typescript/client_generator.py +165 -0
  104. django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +428 -0
  105. django_cfg/modules/django_client/core/generator/typescript/files_generator.py +207 -0
  106. django_cfg/modules/django_client/core/generator/typescript/generator.py +432 -0
  107. django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +536 -0
  108. django_cfg/modules/django_client/core/generator/typescript/models_generator.py +245 -0
  109. django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +298 -0
  110. django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +329 -0
  111. django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja +131 -0
  112. django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja +2 -0
  113. django_cfg/modules/django_client/core/generator/typescript/templates/client/app_client.ts.jinja +18 -0
  114. django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +403 -0
  115. django_cfg/modules/django_client/core/generator/typescript/templates/client/flat_client.ts.jinja +109 -0
  116. django_cfg/modules/django_client/core/generator/typescript/templates/client/main_client_file.ts.jinja +10 -0
  117. django_cfg/modules/django_client/core/generator/typescript/templates/client/operation.ts.jinja +61 -0
  118. django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_client.ts.jinja +15 -0
  119. django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja +9 -0
  120. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +45 -0
  121. django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja +30 -0
  122. django_cfg/modules/django_client/core/generator/typescript/templates/index.ts.jinja +5 -0
  123. django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +268 -0
  124. django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja +8 -0
  125. django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja +4 -0
  126. django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja +8 -0
  127. django_cfg/modules/django_client/core/generator/typescript/templates/package.json.jinja +52 -0
  128. django_cfg/modules/django_client/core/generator/typescript/templates/schemas/index.ts.jinja +21 -0
  129. django_cfg/modules/django_client/core/generator/typescript/templates/schemas/schema.ts.jinja +24 -0
  130. django_cfg/modules/django_client/core/generator/typescript/templates/tsconfig.json.jinja +20 -0
  131. django_cfg/modules/django_client/core/generator/typescript/templates/utils/errors.ts.jinja +116 -0
  132. django_cfg/modules/django_client/core/generator/typescript/templates/utils/http.ts.jinja +98 -0
  133. django_cfg/modules/django_client/core/generator/typescript/templates/utils/logger.ts.jinja +259 -0
  134. django_cfg/modules/django_client/core/generator/typescript/templates/utils/retry.ts.jinja +175 -0
  135. django_cfg/modules/django_client/core/generator/typescript/templates/utils/schema.ts.jinja +7 -0
  136. django_cfg/modules/django_client/core/generator/typescript/templates/utils/storage.ts.jinja +158 -0
  137. django_cfg/modules/django_client/core/groups/__init__.py +13 -0
  138. django_cfg/modules/django_client/core/groups/detector.py +178 -0
  139. django_cfg/modules/django_client/core/groups/manager.py +314 -0
  140. django_cfg/modules/django_client/core/ir/__init__.py +57 -0
  141. django_cfg/modules/django_client/core/ir/context.py +387 -0
  142. django_cfg/modules/django_client/core/ir/operation.py +518 -0
  143. django_cfg/modules/django_client/core/ir/schema.py +353 -0
  144. django_cfg/modules/django_client/core/parser/__init__.py +74 -0
  145. django_cfg/modules/django_client/core/parser/base.py +648 -0
  146. django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
  147. django_cfg/modules/django_client/core/parser/models/base.py +212 -0
  148. django_cfg/modules/django_client/core/parser/models/components.py +160 -0
  149. django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
  150. django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
  151. django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
  152. django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
  153. django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
  154. django_cfg/modules/django_client/core/validation/__init__.py +22 -0
  155. django_cfg/modules/django_client/core/validation/checker.py +134 -0
  156. django_cfg/modules/django_client/core/validation/fixer.py +216 -0
  157. django_cfg/modules/django_client/core/validation/reporter.py +480 -0
  158. django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
  159. django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
  160. django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
  161. django_cfg/modules/django_client/core/validation/safety.py +266 -0
  162. django_cfg/modules/django_client/management/__init__.py +3 -0
  163. django_cfg/modules/django_client/management/commands/__init__.py +3 -0
  164. django_cfg/modules/django_client/management/commands/generate_client.py +427 -0
  165. django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
  166. django_cfg/modules/django_client/pytest.ini +30 -0
  167. django_cfg/modules/django_client/spectacular/__init__.py +10 -0
  168. django_cfg/modules/django_client/spectacular/async_detection.py +187 -0
  169. django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
  170. django_cfg/modules/django_client/urls.py +72 -0
  171. django_cfg/{dashboard → modules/django_dashboard}/DEBUG_README.md +2 -2
  172. django_cfg/{dashboard → modules/django_dashboard}/REFACTORING_SUMMARY.md +1 -1
  173. django_cfg/modules/django_dashboard/management/__init__.py +0 -0
  174. django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
  175. django_cfg/{dashboard → modules/django_dashboard}/management/commands/debug_dashboard.py +5 -5
  176. django_cfg/modules/django_dashboard/sections/documentation.py +391 -0
  177. django_cfg/modules/django_email/management/__init__.py +0 -0
  178. django_cfg/modules/django_email/management/commands/__init__.py +0 -0
  179. django_cfg/modules/django_email/management/commands/test_email.py +93 -0
  180. django_cfg/modules/django_logging/LOGGING_GUIDE.md +1 -1
  181. django_cfg/modules/django_logging/django_logger.py +6 -6
  182. django_cfg/modules/django_ngrok/management/__init__.py +0 -0
  183. django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
  184. django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
  185. django_cfg/modules/django_tasks/management/__init__.py +0 -0
  186. django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
  187. django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
  188. django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
  189. django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
  190. django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
  191. django_cfg/modules/django_telegram/management/__init__.py +0 -0
  192. django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
  193. django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
  194. django_cfg/modules/django_twilio/management/__init__.py +0 -0
  195. django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
  196. django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
  197. django_cfg/modules/django_unfold/callbacks/main.py +21 -10
  198. django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
  199. django_cfg/pyproject.toml +2 -6
  200. django_cfg/registry/third_party.py +5 -7
  201. django_cfg/routing/callbacks.py +1 -1
  202. django_cfg/static/admin/css/prose-unfold.css +666 -0
  203. django_cfg/templates/admin/index.html +8 -0
  204. django_cfg/templates/admin/index_new.html +13 -0
  205. django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
  206. django_cfg/templates/admin/sections/documentation_section.html +172 -0
  207. django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
  208. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/METADATA +2 -2
  209. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/RECORD +224 -74
  210. django_cfg/management/commands/generate.py +0 -107
  211. /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
  212. /django_cfg/{dashboard → modules/django_admin}/management/__init__.py +0 -0
  213. /django_cfg/{dashboard → modules/django_admin}/management/commands/__init__.py +0 -0
  214. /django_cfg/{dashboard → modules/django_dashboard}/__init__.py +0 -0
  215. /django_cfg/{dashboard → modules/django_dashboard}/components.py +0 -0
  216. /django_cfg/{dashboard → modules/django_dashboard}/debug.py +0 -0
  217. /django_cfg/{dashboard → modules/django_dashboard}/sections/__init__.py +0 -0
  218. /django_cfg/{dashboard → modules/django_dashboard}/sections/base.py +0 -0
  219. /django_cfg/{dashboard → modules/django_dashboard}/sections/commands.py +0 -0
  220. /django_cfg/{dashboard → modules/django_dashboard}/sections/overview.py +0 -0
  221. /django_cfg/{dashboard → modules/django_dashboard}/sections/stats.py +0 -0
  222. /django_cfg/{dashboard → modules/django_dashboard}/sections/system.py +0 -0
  223. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/WHEEL +0 -0
  224. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/entry_points.txt +0 -0
  225. {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,207 @@
1
+ """
2
+ OpenAPI Input Models - Operation types.
3
+
4
+ These models represent OpenAPI operations (endpoints) as they appear in the spec.
5
+
6
+ Reference: https://spec.openapis.org/oas/v3.1.0
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from typing import Any, Literal
12
+
13
+ from pydantic import BaseModel, ConfigDict, Field
14
+
15
+ from .base import ExampleObject, ExternalDocumentationObject, ReferenceObject, ServerObject
16
+ from .schema import SchemaObject
17
+
18
+
19
+ class ParameterObject(BaseModel):
20
+ """
21
+ OpenAPI Parameter Object.
22
+
23
+ Reference: https://spec.openapis.org/oas/v3.1.0#parameter-object
24
+ """
25
+
26
+ model_config = ConfigDict(extra="allow")
27
+
28
+ name: str = Field(..., description="Parameter name")
29
+ in_: Literal["query", "header", "path", "cookie"] = Field(
30
+ ..., alias="in", description="Parameter location"
31
+ )
32
+ description: str | None = None
33
+ required: bool = False
34
+ deprecated: bool = False
35
+ allowEmptyValue: bool = False
36
+
37
+ # Schema (simplified - can also use content for complex types)
38
+ schema_: SchemaObject | ReferenceObject | None = Field(None, alias="schema")
39
+
40
+ # Style and explode (for serialization)
41
+ style: str | None = None
42
+ explode: bool | None = None
43
+ allowReserved: bool = False
44
+
45
+ # Example
46
+ example: Any | None = None
47
+ examples: dict[str, ExampleObject | ReferenceObject] | None = None
48
+
49
+
50
+ class EncodingObject(BaseModel):
51
+ """
52
+ OpenAPI Encoding Object.
53
+
54
+ Reference: https://spec.openapis.org/oas/v3.1.0#encoding-object
55
+ """
56
+
57
+ model_config = ConfigDict(extra="allow")
58
+
59
+ contentType: str | None = None
60
+ headers: dict[str, Any] | None = None # HeaderObject | ReferenceObject
61
+ style: str | None = None
62
+ explode: bool | None = None
63
+ allowReserved: bool = False
64
+
65
+
66
+ class MediaTypeObject(BaseModel):
67
+ """
68
+ OpenAPI Media Type Object.
69
+
70
+ Reference: https://spec.openapis.org/oas/v3.1.0#media-type-object
71
+ """
72
+
73
+ model_config = ConfigDict(extra="allow")
74
+
75
+ schema_: SchemaObject | ReferenceObject | None = Field(None, alias="schema")
76
+ example: Any | None = None
77
+ examples: dict[str, ExampleObject | ReferenceObject] | None = None
78
+ encoding: dict[str, EncodingObject] | None = None
79
+
80
+
81
+ class RequestBodyObject(BaseModel):
82
+ """
83
+ OpenAPI Request Body Object.
84
+
85
+ Reference: https://spec.openapis.org/oas/v3.1.0#request-body-object
86
+ """
87
+
88
+ model_config = ConfigDict(extra="allow")
89
+
90
+ description: str | None = None
91
+ content: dict[str, MediaTypeObject] = Field(
92
+ ..., description="Content by media type (application/json, etc.)"
93
+ )
94
+ required: bool = False
95
+
96
+
97
+ class ResponseObject(BaseModel):
98
+ """
99
+ OpenAPI Response Object.
100
+
101
+ Reference: https://spec.openapis.org/oas/v3.1.0#response-object
102
+ """
103
+
104
+ model_config = ConfigDict(extra="allow")
105
+
106
+ description: str = Field(..., description="Response description")
107
+ headers: dict[str, Any] | None = None # HeaderObject | ReferenceObject
108
+ content: dict[str, MediaTypeObject] | None = None
109
+ links: dict[str, Any] | None = None # LinkObject | ReferenceObject
110
+
111
+
112
+ class CallbackObject(BaseModel):
113
+ """
114
+ OpenAPI Callback Object.
115
+
116
+ Reference: https://spec.openapis.org/oas/v3.1.0#callback-object
117
+ """
118
+
119
+ model_config = ConfigDict(extra="allow", populate_by_name=True)
120
+
121
+ # Callbacks are PathItemObjects keyed by expression
122
+ # We'll keep it simple as dict for now
123
+
124
+
125
+ class OperationObject(BaseModel):
126
+ """
127
+ OpenAPI Operation Object.
128
+
129
+ Reference: https://spec.openapis.org/oas/v3.1.0#operation-object
130
+ """
131
+
132
+ model_config = ConfigDict(extra="allow")
133
+
134
+ # Identification
135
+ operationId: str | None = None
136
+ summary: str | None = None
137
+ description: str | None = None
138
+ tags: list[str] | None = None
139
+
140
+ # External docs
141
+ externalDocs: ExternalDocumentationObject | None = None
142
+
143
+ # Parameters
144
+ parameters: list[ParameterObject | ReferenceObject] | None = None
145
+
146
+ # Request body
147
+ requestBody: RequestBodyObject | ReferenceObject | None = None
148
+
149
+ # Responses
150
+ responses: dict[str, ResponseObject | ReferenceObject] = Field(
151
+ ..., description="Responses by status code ('200', '404', 'default', etc.)"
152
+ )
153
+
154
+ # Callbacks
155
+ callbacks: dict[str, CallbackObject | ReferenceObject] | None = None
156
+
157
+ # Security
158
+ security: list[dict[str, list[str]]] | None = None
159
+
160
+ # Servers
161
+ servers: list[ServerObject] | None = None
162
+
163
+ # Deprecation
164
+ deprecated: bool = False
165
+
166
+
167
+ class PathItemObject(BaseModel):
168
+ """
169
+ OpenAPI Path Item Object.
170
+
171
+ Reference: https://spec.openapis.org/oas/v3.1.0#path-item-object
172
+ """
173
+
174
+ model_config = ConfigDict(extra="allow")
175
+
176
+ # Reference (allows $ref at path level)
177
+ ref: str | None = Field(None, alias="$ref")
178
+
179
+ # Summary and description
180
+ summary: str | None = None
181
+ description: str | None = None
182
+
183
+ # Operations
184
+ get: OperationObject | None = None
185
+ put: OperationObject | None = None
186
+ post: OperationObject | None = None
187
+ delete: OperationObject | None = None
188
+ options: OperationObject | None = None
189
+ head: OperationObject | None = None
190
+ patch: OperationObject | None = None
191
+ trace: OperationObject | None = None
192
+
193
+ # Servers
194
+ servers: list[ServerObject] | None = None
195
+
196
+ # Parameters (apply to all operations)
197
+ parameters: list[ParameterObject | ReferenceObject] | None = None
198
+
199
+ @property
200
+ def operations(self) -> dict[str, OperationObject]:
201
+ """Get all operations in this path."""
202
+ result = {}
203
+ for method in ("get", "post", "put", "patch", "delete", "head", "options", "trace"):
204
+ operation = getattr(self, method, None)
205
+ if operation:
206
+ result[method.upper()] = operation
207
+ return result
@@ -0,0 +1,266 @@
1
+ """
2
+ OpenAPI Input Models - Schema types.
3
+
4
+ These models represent OpenAPI Schema Objects as they appear in the spec.
5
+ Supports both OpenAPI 3.0.3 and 3.1.0 (with JSON Schema extensions).
6
+
7
+ Reference: https://spec.openapis.org/oas/v3.1.0#schema-object
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from typing import Any, Literal
13
+
14
+ from pydantic import BaseModel, ConfigDict, Field
15
+
16
+ from .base import ExternalDocumentationObject, ReferenceObject
17
+
18
+
19
+ class Discriminator(BaseModel):
20
+ """
21
+ OpenAPI Discriminator Object.
22
+
23
+ Reference: https://spec.openapis.org/oas/v3.1.0#discriminator-object
24
+ """
25
+
26
+ model_config = ConfigDict(extra="allow")
27
+
28
+ propertyName: str = Field(..., description="Property name for discrimination")
29
+ mapping: dict[str, str] | None = None
30
+
31
+
32
+ class XMLObject(BaseModel):
33
+ """
34
+ OpenAPI XML Object.
35
+
36
+ Reference: https://spec.openapis.org/oas/v3.1.0#xml-object
37
+ """
38
+
39
+ model_config = ConfigDict(extra="allow")
40
+
41
+ name: str | None = None
42
+ namespace: str | None = None
43
+ prefix: str | None = None
44
+ attribute: bool = False
45
+ wrapped: bool = False
46
+
47
+
48
+ class SchemaObject(BaseModel):
49
+ """
50
+ OpenAPI Schema Object (raw from spec).
51
+
52
+ This represents a schema as it appears in the OpenAPI spec, before
53
+ normalization to IR. Supports both OAS 3.0.3 and 3.1.0.
54
+
55
+ Key Differences:
56
+ - OAS 3.0.3: Uses nullable: true (proprietary extension)
57
+ - OAS 3.1.0: Uses type: ['string', 'null'] (JSON Schema standard)
58
+
59
+ Extensions:
60
+ - x-enum-varnames: drf-spectacular enum variable names
61
+ - x-choices: Django choices metadata
62
+
63
+ Reference: https://spec.openapis.org/oas/v3.1.0#schema-object
64
+ """
65
+
66
+ model_config = ConfigDict(extra="allow") # Allow x-* extensions
67
+
68
+ # ===== Core JSON Schema Keywords =====
69
+ type: str | list[str] | None = Field(
70
+ None,
71
+ description="JSON Schema type (string, integer, object, array, etc.)",
72
+ )
73
+ format: str | None = Field(
74
+ None,
75
+ description="Format hint (date-time, email, uuid, binary, etc.)",
76
+ )
77
+ title: str | None = None
78
+ description: str | None = None
79
+ default: Any | None = None
80
+ example: Any | None = Field(
81
+ None, description="Example value (deprecated in OAS 3.1.0, use examples)"
82
+ )
83
+ examples: list[Any] | None = Field(
84
+ None, description="Example values (OAS 3.1.0)"
85
+ )
86
+
87
+ # ===== Nullable (OAS 3.0.3 only) =====
88
+ nullable: bool | None = Field(
89
+ None,
90
+ description="Can be null (OAS 3.0.3 only, use type: ['x', 'null'] in 3.1.0)",
91
+ )
92
+
93
+ # ===== Enum and Const =====
94
+ enum: list[Any] | None = None
95
+ const: Any | None = Field(None, description="Constant value (OAS 3.1.0)")
96
+
97
+ # ===== Object Properties =====
98
+ properties: dict[str, SchemaObject | ReferenceObject] | None = None
99
+ required: list[str] | None = None
100
+ additionalProperties: bool | SchemaObject | ReferenceObject | None = None
101
+ maxProperties: int | None = None
102
+ minProperties: int | None = None
103
+
104
+ # ===== Array Items =====
105
+ items: SchemaObject | ReferenceObject | None = Field(
106
+ None, description="Array item schema"
107
+ )
108
+ prefixItems: list[SchemaObject | ReferenceObject] | None = Field(
109
+ None, description="Tuple validation (OAS 3.1.0)"
110
+ )
111
+ contains: SchemaObject | ReferenceObject | None = Field(
112
+ None, description="At least one item matches (OAS 3.1.0)"
113
+ )
114
+ maxItems: int | None = None
115
+ minItems: int | None = None
116
+ uniqueItems: bool | None = None
117
+
118
+ # ===== String Validation =====
119
+ maxLength: int | None = None
120
+ minLength: int | None = None
121
+ pattern: str | None = None
122
+
123
+ # ===== Numeric Validation =====
124
+ maximum: float | None = None
125
+ minimum: float | None = None
126
+ exclusiveMaximum: float | bool | None = Field(
127
+ None, description="Exclusive maximum (number in 3.1.0, boolean in 3.0.3)"
128
+ )
129
+ exclusiveMinimum: float | bool | None = Field(
130
+ None, description="Exclusive minimum (number in 3.1.0, boolean in 3.0.3)"
131
+ )
132
+ multipleOf: float | None = None
133
+
134
+ # ===== Composition =====
135
+ allOf: list[SchemaObject | ReferenceObject] | None = None
136
+ oneOf: list[SchemaObject | ReferenceObject] | None = None
137
+ anyOf: list[SchemaObject | ReferenceObject] | None = None
138
+ not_: SchemaObject | ReferenceObject | None = Field(None, alias="not")
139
+
140
+ # ===== OpenAPI-Specific =====
141
+ readOnly: bool = False
142
+ writeOnly: bool = False
143
+ deprecated: bool = False
144
+ discriminator: Discriminator | None = None
145
+ xml: XMLObject | None = None
146
+ externalDocs: ExternalDocumentationObject | None = None
147
+
148
+ # ===== OAS 3.1.0 Content Metadata =====
149
+ contentMediaType: str | None = Field(
150
+ None, description="Media type (OAS 3.1.0, e.g., 'application/json')"
151
+ )
152
+ contentEncoding: str | None = Field(
153
+ None, description="Encoding (OAS 3.1.0, e.g., 'base64')"
154
+ )
155
+ contentSchema: SchemaObject | ReferenceObject | None = Field(
156
+ None, description="Schema for content (OAS 3.1.0)"
157
+ )
158
+
159
+ # ===== Django/drf-spectacular Extensions =====
160
+ x_enum_varnames: list[str] | None = Field(
161
+ None,
162
+ alias="x-enum-varnames",
163
+ description="Enum variable names from drf-spectacular",
164
+ )
165
+ x_choices: list[dict[str, Any]] | None = Field(
166
+ None,
167
+ alias="x-choices",
168
+ description="Django choices from drf-spectacular",
169
+ )
170
+
171
+ @property
172
+ def is_nullable_30(self) -> bool:
173
+ """Check if nullable via OAS 3.0.3 style (nullable: true)."""
174
+ return self.nullable is True
175
+
176
+ @property
177
+ def is_nullable_31(self) -> bool:
178
+ """
179
+ Check if nullable via OAS 3.1.0 style (type: ['string', 'null']).
180
+
181
+ Examples:
182
+ >>> schema = SchemaObject(type=['string', 'null'])
183
+ >>> schema.is_nullable_31
184
+ True
185
+
186
+ >>> schema = SchemaObject(type='string')
187
+ >>> schema.is_nullable_31
188
+ False
189
+ """
190
+ if isinstance(self.type, list):
191
+ return "null" in self.type
192
+ return False
193
+
194
+ @property
195
+ def base_type(self) -> str | None:
196
+ """
197
+ Get base type (excluding 'null').
198
+
199
+ For OAS 3.1.0 type: ['string', 'null'], returns 'string'.
200
+
201
+ Examples:
202
+ >>> SchemaObject(type='string').base_type
203
+ 'string'
204
+ >>> SchemaObject(type=['string', 'null']).base_type
205
+ 'string'
206
+ >>> SchemaObject(type=['integer', 'null']).base_type
207
+ 'integer'
208
+ """
209
+ if isinstance(self.type, list):
210
+ types = [t for t in self.type if t != "null"]
211
+ return types[0] if types else None
212
+ return self.type
213
+
214
+ @property
215
+ def has_enum_varnames(self) -> bool:
216
+ """Check if x-enum-varnames extension is present."""
217
+ return (
218
+ self.enum is not None
219
+ and self.x_enum_varnames is not None
220
+ and len(self.enum) == len(self.x_enum_varnames)
221
+ )
222
+
223
+ @property
224
+ def is_object(self) -> bool:
225
+ """Check if schema is object type."""
226
+ return self.base_type == "object" or self.properties is not None
227
+
228
+ @property
229
+ def is_array(self) -> bool:
230
+ """Check if schema is array type."""
231
+ return self.base_type == "array" or self.items is not None
232
+
233
+ @property
234
+ def is_primitive(self) -> bool:
235
+ """Check if schema is primitive type."""
236
+ return self.base_type in ("string", "integer", "number", "boolean")
237
+
238
+ @property
239
+ def is_binary(self) -> bool:
240
+ """
241
+ Check if schema represents binary data.
242
+
243
+ Handles both OAS 3.0.3 (format: binary) and OAS 3.1.0 (contentEncoding).
244
+ """
245
+ return self.format == "binary" or self.contentEncoding in ("base64", "binary")
246
+
247
+ def __repr__(self) -> str:
248
+ """String representation for debugging."""
249
+ parts = ["SchemaObject("]
250
+
251
+ if self.type:
252
+ parts.append(f"type={self.type!r}")
253
+
254
+ if self.format:
255
+ parts.append(f"format={self.format!r}")
256
+
257
+ if self.nullable:
258
+ parts.append("nullable=True")
259
+
260
+ if self.enum:
261
+ parts.append(f"enum={self.enum}")
262
+
263
+ if self.x_enum_varnames:
264
+ parts.append(f"x-enum-varnames={self.x_enum_varnames}")
265
+
266
+ return ", ".join(parts) + ")"
@@ -0,0 +1,56 @@
1
+ """
2
+ OpenAPI 3.0.3 Parser - Handles nullable: true.
3
+
4
+ This parser handles OpenAPI 3.0.x specifications which use the proprietary
5
+ `nullable: true` extension for nullable fields.
6
+
7
+ Reference: https://swagger.io/docs/specification/data-models/data-types/#null
8
+ """
9
+
10
+ from .base import BaseParser
11
+ from .models import SchemaObject
12
+
13
+
14
+ class OpenAPI30Parser(BaseParser):
15
+ """
16
+ Parser for OpenAPI 3.0.x specifications.
17
+
18
+ Key differences from 3.1.0:
19
+ - Uses nullable: true (proprietary extension)
20
+ - exclusiveMinimum/exclusiveMaximum are booleans (not numbers)
21
+ - No const keyword
22
+ - No contentMediaType/contentEncoding
23
+
24
+ Examples:
25
+ >>> from django_cfg.modules.django_client.core.parser.models import OpenAPISpec
26
+ >>> spec_dict = {...} # OAS 3.0.3 spec
27
+ >>> spec = OpenAPISpec.model_validate(spec_dict)
28
+ >>> parser = OpenAPI30Parser(spec)
29
+ >>> context = parser.parse()
30
+ >>> context.openapi_info.version
31
+ '3.0.3'
32
+ """
33
+
34
+ def _detect_nullable(self, schema: SchemaObject) -> bool:
35
+ """
36
+ Detect if schema is nullable using OAS 3.0.3 style.
37
+
38
+ In OpenAPI 3.0.x, nullable is indicated by:
39
+ nullable: true
40
+
41
+ Examples:
42
+ >>> schema = SchemaObject(type='string', nullable=True)
43
+ >>> parser._detect_nullable(schema)
44
+ True
45
+
46
+ >>> schema = SchemaObject(type='string')
47
+ >>> parser._detect_nullable(schema)
48
+ False
49
+
50
+ Args:
51
+ schema: Raw SchemaObject from spec
52
+
53
+ Returns:
54
+ True if nullable, False otherwise
55
+ """
56
+ return schema.is_nullable_30
@@ -0,0 +1,64 @@
1
+ """
2
+ OpenAPI 3.1.0 Parser - Handles type: ['string', 'null'].
3
+
4
+ This parser handles OpenAPI 3.1.0 specifications which use JSON Schema 2020-12
5
+ standard for nullable fields (type arrays with 'null').
6
+
7
+ Reference: https://spec.openapis.org/oas/v3.1.0
8
+ """
9
+
10
+ from .base import BaseParser
11
+ from .models import SchemaObject
12
+
13
+
14
+ class OpenAPI31Parser(BaseParser):
15
+ """
16
+ Parser for OpenAPI 3.1.0 specifications.
17
+
18
+ Key differences from 3.0.x:
19
+ - Uses type: ['string', 'null'] (JSON Schema standard)
20
+ - exclusiveMinimum/exclusiveMaximum are numbers (not booleans)
21
+ - Supports const keyword
22
+ - Supports contentMediaType/contentEncoding
23
+ - Supports $schema and $vocabulary
24
+ - Aligned with JSON Schema 2020-12
25
+
26
+ Examples:
27
+ >>> from django_cfg.modules.django_client.core.parser.models import OpenAPISpec
28
+ >>> spec_dict = {...} # OAS 3.1.0 spec
29
+ >>> spec = OpenAPISpec.model_validate(spec_dict)
30
+ >>> parser = OpenAPI31Parser(spec)
31
+ >>> context = parser.parse()
32
+ >>> context.openapi_info.version
33
+ '3.1.0'
34
+ """
35
+
36
+ def _detect_nullable(self, schema: SchemaObject) -> bool:
37
+ """
38
+ Detect if schema is nullable using OAS 3.1.0 style.
39
+
40
+ In OpenAPI 3.1.0, nullable is indicated by:
41
+ type: ['string', 'null']
42
+ type: ['integer', 'null']
43
+ etc.
44
+
45
+ Examples:
46
+ >>> schema = SchemaObject(type=['string', 'null'])
47
+ >>> parser._detect_nullable(schema)
48
+ True
49
+
50
+ >>> schema = SchemaObject(type='string')
51
+ >>> parser._detect_nullable(schema)
52
+ False
53
+
54
+ >>> schema = SchemaObject(type=['integer', 'null'])
55
+ >>> parser._detect_nullable(schema)
56
+ True
57
+
58
+ Args:
59
+ schema: Raw SchemaObject from spec
60
+
61
+ Returns:
62
+ True if nullable, False otherwise
63
+ """
64
+ return schema.is_nullable_31
@@ -0,0 +1,22 @@
1
+ """
2
+ OpenAPI Schema Validation Module.
3
+
4
+ Provides safe validation and fixing of Django REST Framework serializers
5
+ to improve OpenAPI schema quality.
6
+ """
7
+
8
+ from .checker import ValidationChecker
9
+ from .fixer import SafeFixer
10
+ from .reporter import IssueReporter
11
+ from .safety import SafetyManager
12
+ from .rules.base import Issue, Severity, ValidationRule
13
+
14
+ __all__ = [
15
+ 'ValidationChecker',
16
+ 'SafeFixer',
17
+ 'IssueReporter',
18
+ 'SafetyManager',
19
+ 'Issue',
20
+ 'Severity',
21
+ 'ValidationRule',
22
+ ]