django-cfg 1.4.9__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.
- django_cfg/apps/agents/management/commands/create_agent.py +1 -1
- django_cfg/apps/agents/management/commands/orchestrator_status.py +3 -3
- django_cfg/apps/newsletter/serializers.py +40 -3
- django_cfg/apps/newsletter/views/campaigns.py +12 -3
- django_cfg/apps/newsletter/views/emails.py +14 -3
- django_cfg/apps/newsletter/views/subscriptions.py +12 -2
- django_cfg/apps/payments/middleware/api_access.py +6 -2
- django_cfg/apps/payments/middleware/rate_limiting.py +2 -1
- django_cfg/apps/payments/middleware/usage_tracking.py +5 -1
- django_cfg/apps/payments/models/managers/api_key_managers.py +0 -1
- django_cfg/apps/payments/models/managers/subscription_managers.py +0 -1
- django_cfg/apps/payments/services/core/balance_service.py +5 -5
- django_cfg/apps/payments/services/core/subscription_service.py +1 -2
- django_cfg/apps/payments/views/api/balances.py +8 -7
- django_cfg/apps/payments/views/api/base.py +10 -6
- django_cfg/apps/payments/views/api/currencies.py +53 -10
- django_cfg/apps/payments/views/api/payments.py +3 -1
- django_cfg/apps/payments/views/api/subscriptions.py +2 -5
- django_cfg/apps/payments/views/api/webhooks.py +72 -7
- django_cfg/apps/payments/views/overview/serializers.py +34 -1
- django_cfg/apps/payments/views/overview/views.py +2 -1
- django_cfg/apps/payments/views/serializers/payments.py +6 -6
- django_cfg/apps/urls.py +106 -45
- django_cfg/core/base/config_model.py +2 -2
- django_cfg/core/constants.py +1 -1
- django_cfg/core/generation/integration_generators/__init__.py +1 -1
- django_cfg/core/generation/integration_generators/api.py +82 -41
- django_cfg/core/integration/display/startup.py +30 -22
- django_cfg/core/integration/url_integration.py +15 -16
- django_cfg/dashboard/sections/documentation.py +391 -0
- django_cfg/management/commands/check_endpoints.py +11 -160
- django_cfg/management/commands/check_settings.py +13 -265
- django_cfg/management/commands/clear_constance.py +13 -201
- django_cfg/management/commands/create_token.py +13 -321
- django_cfg/management/commands/generate_clients.py +23 -0
- django_cfg/management/commands/list_urls.py +13 -306
- django_cfg/management/commands/migrate_all.py +13 -126
- django_cfg/management/commands/migrator.py +13 -396
- django_cfg/management/commands/rundramatiq.py +15 -247
- django_cfg/management/commands/rundramatiq_simulator.py +12 -429
- django_cfg/management/commands/runserver_ngrok.py +15 -160
- django_cfg/management/commands/script.py +12 -488
- django_cfg/management/commands/show_config.py +12 -215
- django_cfg/management/commands/show_urls.py +12 -342
- django_cfg/management/commands/superuser.py +15 -295
- django_cfg/management/commands/task_clear.py +14 -217
- django_cfg/management/commands/task_status.py +13 -248
- django_cfg/management/commands/test_email.py +15 -86
- django_cfg/management/commands/test_telegram.py +14 -61
- django_cfg/management/commands/test_twilio.py +15 -105
- django_cfg/management/commands/tree.py +13 -383
- django_cfg/management/commands/validate_openapi.py +10 -0
- django_cfg/middleware/README.md +1 -1
- django_cfg/middleware/user_activity.py +3 -3
- django_cfg/models/__init__.py +2 -2
- django_cfg/models/api/drf/spectacular.py +6 -6
- django_cfg/models/django/__init__.py +2 -2
- django_cfg/models/django/openapi.py +238 -0
- django_cfg/models/django/{revolution.py → revolution_legacy.py} +8 -0
- django_cfg/modules/django_admin/management/__init__.py +0 -0
- django_cfg/modules/django_admin/management/commands/__init__.py +0 -0
- django_cfg/modules/django_admin/management/commands/check_endpoints.py +169 -0
- django_cfg/modules/django_admin/management/commands/check_settings.py +355 -0
- django_cfg/modules/django_admin/management/commands/clear_constance.py +208 -0
- django_cfg/modules/django_admin/management/commands/create_token.py +328 -0
- django_cfg/modules/django_admin/management/commands/list_urls.py +313 -0
- django_cfg/modules/django_admin/management/commands/migrate_all.py +133 -0
- django_cfg/modules/django_admin/management/commands/migrator.py +403 -0
- django_cfg/modules/django_admin/management/commands/script.py +496 -0
- django_cfg/modules/django_admin/management/commands/show_config.py +225 -0
- django_cfg/modules/django_admin/management/commands/show_urls.py +361 -0
- django_cfg/modules/django_admin/management/commands/superuser.py +302 -0
- django_cfg/modules/django_admin/management/commands/tree.py +390 -0
- django_cfg/modules/django_client/__init__.py +20 -0
- django_cfg/modules/django_client/apps.py +35 -0
- django_cfg/modules/django_client/core/__init__.py +56 -0
- django_cfg/modules/django_client/core/archive/__init__.py +11 -0
- django_cfg/modules/django_client/core/archive/manager.py +134 -0
- django_cfg/modules/django_client/core/cli/__init__.py +12 -0
- django_cfg/modules/django_client/core/cli/main.py +235 -0
- django_cfg/modules/django_client/core/config/__init__.py +18 -0
- django_cfg/modules/django_client/core/config/config.py +188 -0
- django_cfg/modules/django_client/core/config/group.py +101 -0
- django_cfg/modules/django_client/core/config/service.py +209 -0
- django_cfg/modules/django_client/core/generator/__init__.py +115 -0
- django_cfg/modules/django_client/core/generator/base.py +767 -0
- django_cfg/modules/django_client/core/generator/python.py +751 -0
- django_cfg/modules/django_client/core/generator/templates/python/__init__.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/python/api_wrapper.py.jinja +130 -0
- django_cfg/modules/django_client/core/generator/templates/python/app_init.py.jinja +6 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/app_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/flat_client.py.jinja +38 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/main_client.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/main_client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/operation_method.py.jinja +7 -0
- django_cfg/modules/django_client/core/generator/templates/python/client/sub_client.py.jinja +11 -0
- django_cfg/modules/django_client/core/generator/templates/python/client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/templates/python/main_init.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/app_models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/enum_class.py.jinja +15 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/enums.py.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/templates/python/models/schema_class.py.jinja +19 -0
- django_cfg/modules/django_client/core/generator/templates/python/utils/logger.py.jinja +255 -0
- django_cfg/modules/django_client/core/generator/templates/python/utils/schema.py.jinja +12 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/app_index.ts.jinja +2 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/app_client.ts.jinja +18 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/client.ts.jinja +327 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/flat_client.ts.jinja +109 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/main_client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/operation.ts.jinja +61 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client/sub_client.ts.jinja +15 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/index.ts.jinja +5 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/main_index.ts.jinja +206 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/app_models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/enums.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/models/models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/errors.ts.jinja +114 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/http.ts.jinja +98 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/logger.ts.jinja +251 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/schema.ts.jinja +7 -0
- django_cfg/modules/django_client/core/generator/templates/typescript/utils/storage.ts.jinja +114 -0
- django_cfg/modules/django_client/core/generator/typescript.py +872 -0
- django_cfg/modules/django_client/core/groups/__init__.py +13 -0
- django_cfg/modules/django_client/core/groups/detector.py +178 -0
- django_cfg/modules/django_client/core/groups/manager.py +314 -0
- django_cfg/modules/django_client/core/ir/__init__.py +57 -0
- django_cfg/modules/django_client/core/ir/context.py +387 -0
- django_cfg/modules/django_client/core/ir/operation.py +518 -0
- django_cfg/modules/django_client/core/ir/schema.py +353 -0
- django_cfg/modules/django_client/core/parser/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/base.py +648 -0
- django_cfg/modules/django_client/core/parser/models/__init__.py +74 -0
- django_cfg/modules/django_client/core/parser/models/base.py +212 -0
- django_cfg/modules/django_client/core/parser/models/components.py +160 -0
- django_cfg/modules/django_client/core/parser/models/openapi.py +203 -0
- django_cfg/modules/django_client/core/parser/models/operation.py +207 -0
- django_cfg/modules/django_client/core/parser/models/schema.py +266 -0
- django_cfg/modules/django_client/core/parser/openapi30.py +56 -0
- django_cfg/modules/django_client/core/parser/openapi31.py +64 -0
- django_cfg/modules/django_client/core/validation/__init__.py +22 -0
- django_cfg/modules/django_client/core/validation/checker.py +134 -0
- django_cfg/modules/django_client/core/validation/fixer.py +216 -0
- django_cfg/modules/django_client/core/validation/reporter.py +480 -0
- django_cfg/modules/django_client/core/validation/rules/__init__.py +11 -0
- django_cfg/modules/django_client/core/validation/rules/base.py +96 -0
- django_cfg/modules/django_client/core/validation/rules/type_hints.py +288 -0
- django_cfg/modules/django_client/core/validation/safety.py +266 -0
- django_cfg/modules/django_client/management/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/__init__.py +3 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +422 -0
- django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
- django_cfg/modules/django_client/spectacular/__init__.py +9 -0
- django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
- django_cfg/modules/django_client/urls.py +72 -0
- django_cfg/modules/django_email/management/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/__init__.py +0 -0
- django_cfg/modules/django_email/management/commands/test_email.py +93 -0
- django_cfg/modules/django_logging/django_logger.py +6 -6
- django_cfg/modules/django_ngrok/management/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/__init__.py +0 -0
- django_cfg/modules/django_ngrok/management/commands/runserver_ngrok.py +167 -0
- django_cfg/modules/django_tasks/management/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/__init__.py +0 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq.py +254 -0
- django_cfg/modules/django_tasks/management/commands/rundramatiq_simulator.py +437 -0
- django_cfg/modules/django_tasks/management/commands/task_clear.py +226 -0
- django_cfg/modules/django_tasks/management/commands/task_status.py +257 -0
- django_cfg/modules/django_telegram/management/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/__init__.py +0 -0
- django_cfg/modules/django_telegram/management/commands/test_telegram.py +68 -0
- django_cfg/modules/django_twilio/management/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/__init__.py +0 -0
- django_cfg/modules/django_twilio/management/commands/test_twilio.py +112 -0
- django_cfg/modules/django_unfold/callbacks/main.py +16 -5
- django_cfg/modules/django_unfold/callbacks/revolution.py +41 -36
- django_cfg/modules/django_unfold/dashboard.py +1 -1
- django_cfg/pyproject.toml +2 -6
- django_cfg/registry/third_party.py +5 -7
- django_cfg/routing/callbacks.py +1 -1
- django_cfg/static/admin/css/prose-unfold.css +666 -0
- django_cfg/templates/admin/index.html +8 -0
- django_cfg/templates/admin/index_new.html +13 -0
- django_cfg/templates/admin/layouts/dashboard_with_tabs.html +15 -3
- django_cfg/templates/admin/sections/documentation_section.html +172 -0
- django_cfg/templates/admin/snippets/tabs/documentation_tab.html +231 -0
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/METADATA +2 -2
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/RECORD +192 -71
- django_cfg/management/commands/generate.py +0 -107
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.9.dist-info → django_cfg-1.4.11.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
|
+
]
|