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.
- 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/views/api/currencies.py +49 -6
- 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 +72 -49
- 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 -348
- 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/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/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.10.dist-info → django_cfg-1.4.11.dist-info}/METADATA +2 -2
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/RECORD +180 -59
- django_cfg/management/commands/generate.py +0 -107
- /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,74 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI Input Models - Pydantic 2 models for raw OpenAPI specs.
|
3
|
+
|
4
|
+
These models represent the OpenAPI specification structure as it appears
|
5
|
+
in JSON/YAML files, before normalization to IR.
|
6
|
+
|
7
|
+
Supports both OpenAPI 3.0.3 and 3.1.0.
|
8
|
+
|
9
|
+
Usage:
|
10
|
+
>>> from django_cfg.modules.django_client.core.parser.models import OpenAPISpec
|
11
|
+
>>> spec = OpenAPISpec.model_validate(openapi_dict)
|
12
|
+
>>> spec.openapi_version
|
13
|
+
'3.1.0'
|
14
|
+
>>> spec.all_schema_names
|
15
|
+
['User', 'UserRequest', 'PatchedUser', ...]
|
16
|
+
"""
|
17
|
+
|
18
|
+
from .base import (
|
19
|
+
ContactObject,
|
20
|
+
ExampleObject,
|
21
|
+
ExternalDocumentationObject,
|
22
|
+
InfoObject,
|
23
|
+
LicenseObject,
|
24
|
+
LinkObject,
|
25
|
+
ReferenceObject,
|
26
|
+
ServerObject,
|
27
|
+
ServerVariableObject,
|
28
|
+
TagObject,
|
29
|
+
)
|
30
|
+
from .components import ComponentsObject, SecuritySchemeObject
|
31
|
+
from .openapi import OpenAPISpec
|
32
|
+
from .operation import (
|
33
|
+
CallbackObject,
|
34
|
+
EncodingObject,
|
35
|
+
MediaTypeObject,
|
36
|
+
OperationObject,
|
37
|
+
ParameterObject,
|
38
|
+
PathItemObject,
|
39
|
+
RequestBodyObject,
|
40
|
+
ResponseObject,
|
41
|
+
)
|
42
|
+
from .schema import Discriminator, SchemaObject, XMLObject
|
43
|
+
|
44
|
+
__all__ = [
|
45
|
+
# Root
|
46
|
+
"OpenAPISpec",
|
47
|
+
# Base
|
48
|
+
"InfoObject",
|
49
|
+
"ContactObject",
|
50
|
+
"LicenseObject",
|
51
|
+
"ServerObject",
|
52
|
+
"ServerVariableObject",
|
53
|
+
"TagObject",
|
54
|
+
"ExternalDocumentationObject",
|
55
|
+
"ReferenceObject",
|
56
|
+
"ExampleObject",
|
57
|
+
"LinkObject",
|
58
|
+
# Schema
|
59
|
+
"SchemaObject",
|
60
|
+
"Discriminator",
|
61
|
+
"XMLObject",
|
62
|
+
# Operation
|
63
|
+
"ParameterObject",
|
64
|
+
"MediaTypeObject",
|
65
|
+
"EncodingObject",
|
66
|
+
"RequestBodyObject",
|
67
|
+
"ResponseObject",
|
68
|
+
"OperationObject",
|
69
|
+
"PathItemObject",
|
70
|
+
"CallbackObject",
|
71
|
+
# Components
|
72
|
+
"ComponentsObject",
|
73
|
+
"SecuritySchemeObject",
|
74
|
+
]
|
@@ -0,0 +1,212 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI Input Models - Base types.
|
3
|
+
|
4
|
+
These models represent the raw OpenAPI specification structure as it appears
|
5
|
+
in the JSON/YAML file. They are version-agnostic where possible.
|
6
|
+
|
7
|
+
Reference: https://spec.openapis.org/oas/v3.1.0
|
8
|
+
"""
|
9
|
+
|
10
|
+
from __future__ import annotations
|
11
|
+
|
12
|
+
from typing import Any
|
13
|
+
|
14
|
+
from pydantic import BaseModel, ConfigDict, Field
|
15
|
+
|
16
|
+
|
17
|
+
class ContactObject(BaseModel):
|
18
|
+
"""
|
19
|
+
OpenAPI Contact Object.
|
20
|
+
|
21
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#contact-object
|
22
|
+
"""
|
23
|
+
|
24
|
+
model_config = ConfigDict(extra="allow")
|
25
|
+
|
26
|
+
name: str | None = None
|
27
|
+
url: str | None = None
|
28
|
+
email: str | None = None
|
29
|
+
|
30
|
+
|
31
|
+
class LicenseObject(BaseModel):
|
32
|
+
"""
|
33
|
+
OpenAPI License Object.
|
34
|
+
|
35
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#license-object
|
36
|
+
"""
|
37
|
+
|
38
|
+
model_config = ConfigDict(extra="allow")
|
39
|
+
|
40
|
+
name: str = Field(..., description="License name (e.g., 'MIT', 'Apache 2.0')")
|
41
|
+
identifier: str | None = Field(
|
42
|
+
None, description="SPDX license identifier (OAS 3.1.0)"
|
43
|
+
)
|
44
|
+
url: str | None = None
|
45
|
+
|
46
|
+
|
47
|
+
class InfoObject(BaseModel):
|
48
|
+
"""
|
49
|
+
OpenAPI Info Object.
|
50
|
+
|
51
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#info-object
|
52
|
+
"""
|
53
|
+
|
54
|
+
model_config = ConfigDict(extra="allow")
|
55
|
+
|
56
|
+
title: str = Field(..., description="API title")
|
57
|
+
version: str = Field(..., description="API version (e.g., '1.0.0')")
|
58
|
+
summary: str | None = Field(None, description="Short summary (OAS 3.1.0)")
|
59
|
+
description: str | None = None
|
60
|
+
termsOfService: str | None = None
|
61
|
+
contact: ContactObject | None = None
|
62
|
+
license: LicenseObject | None = None
|
63
|
+
|
64
|
+
|
65
|
+
class ServerVariableObject(BaseModel):
|
66
|
+
"""
|
67
|
+
OpenAPI Server Variable Object.
|
68
|
+
|
69
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#server-variable-object
|
70
|
+
"""
|
71
|
+
|
72
|
+
model_config = ConfigDict(extra="allow")
|
73
|
+
|
74
|
+
enum: list[str] | None = None
|
75
|
+
default: str = Field(..., description="Default value for substitution")
|
76
|
+
description: str | None = None
|
77
|
+
|
78
|
+
|
79
|
+
class ServerObject(BaseModel):
|
80
|
+
"""
|
81
|
+
OpenAPI Server Object.
|
82
|
+
|
83
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#server-object
|
84
|
+
"""
|
85
|
+
|
86
|
+
model_config = ConfigDict(extra="allow")
|
87
|
+
|
88
|
+
url: str = Field(..., description="Server URL (e.g., 'https://api.example.com')")
|
89
|
+
description: str | None = None
|
90
|
+
variables: dict[str, ServerVariableObject] | None = None
|
91
|
+
|
92
|
+
|
93
|
+
class ExternalDocumentationObject(BaseModel):
|
94
|
+
"""
|
95
|
+
OpenAPI External Documentation Object.
|
96
|
+
|
97
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#external-documentation-object
|
98
|
+
"""
|
99
|
+
|
100
|
+
model_config = ConfigDict(extra="allow")
|
101
|
+
|
102
|
+
url: str = Field(..., description="Documentation URL")
|
103
|
+
description: str | None = None
|
104
|
+
|
105
|
+
|
106
|
+
class TagObject(BaseModel):
|
107
|
+
"""
|
108
|
+
OpenAPI Tag Object.
|
109
|
+
|
110
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#tag-object
|
111
|
+
"""
|
112
|
+
|
113
|
+
model_config = ConfigDict(extra="allow")
|
114
|
+
|
115
|
+
name: str = Field(..., description="Tag name")
|
116
|
+
description: str | None = None
|
117
|
+
externalDocs: ExternalDocumentationObject | None = None
|
118
|
+
|
119
|
+
|
120
|
+
class ReferenceObject(BaseModel):
|
121
|
+
"""
|
122
|
+
OpenAPI Reference Object ($ref).
|
123
|
+
|
124
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#reference-object
|
125
|
+
|
126
|
+
Examples:
|
127
|
+
>>> ref = ReferenceObject(ref="#/components/schemas/User")
|
128
|
+
>>> ref.ref
|
129
|
+
'#/components/schemas/User'
|
130
|
+
"""
|
131
|
+
|
132
|
+
model_config = ConfigDict(extra="allow")
|
133
|
+
|
134
|
+
ref: str = Field(..., alias="$ref", description="Reference URI")
|
135
|
+
summary: str | None = Field(None, description="Summary (OAS 3.1.0)")
|
136
|
+
description: str | None = Field(None, description="Description (OAS 3.1.0)")
|
137
|
+
|
138
|
+
@property
|
139
|
+
def ref_name(self) -> str:
|
140
|
+
"""
|
141
|
+
Extract referenced name from $ref.
|
142
|
+
|
143
|
+
Examples:
|
144
|
+
>>> ReferenceObject(ref="#/components/schemas/User").ref_name
|
145
|
+
'User'
|
146
|
+
>>> ReferenceObject(ref="#/components/responses/NotFound").ref_name
|
147
|
+
'NotFound'
|
148
|
+
"""
|
149
|
+
return self.ref.split("/")[-1]
|
150
|
+
|
151
|
+
@property
|
152
|
+
def ref_type(self) -> str:
|
153
|
+
"""
|
154
|
+
Extract reference type from $ref.
|
155
|
+
|
156
|
+
Examples:
|
157
|
+
>>> ReferenceObject(ref="#/components/schemas/User").ref_type
|
158
|
+
'schemas'
|
159
|
+
>>> ReferenceObject(ref="#/components/responses/NotFound").ref_type
|
160
|
+
'responses'
|
161
|
+
"""
|
162
|
+
parts = self.ref.split("/")
|
163
|
+
if len(parts) >= 3 and parts[1] == "components":
|
164
|
+
return parts[2]
|
165
|
+
return "unknown"
|
166
|
+
|
167
|
+
|
168
|
+
class ExampleObject(BaseModel):
|
169
|
+
"""
|
170
|
+
OpenAPI Example Object.
|
171
|
+
|
172
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#example-object
|
173
|
+
"""
|
174
|
+
|
175
|
+
model_config = ConfigDict(extra="allow")
|
176
|
+
|
177
|
+
summary: str | None = None
|
178
|
+
description: str | None = None
|
179
|
+
value: Any | None = None
|
180
|
+
externalValue: str | None = None
|
181
|
+
|
182
|
+
|
183
|
+
class HeaderObject(BaseModel):
|
184
|
+
"""
|
185
|
+
OpenAPI Header Object.
|
186
|
+
|
187
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#header-object
|
188
|
+
"""
|
189
|
+
|
190
|
+
model_config = ConfigDict(extra="allow")
|
191
|
+
|
192
|
+
description: str | None = None
|
193
|
+
required: bool = False
|
194
|
+
deprecated: bool = False
|
195
|
+
# Schema will be added as SchemaObject | ReferenceObject in schema.py
|
196
|
+
|
197
|
+
|
198
|
+
class LinkObject(BaseModel):
|
199
|
+
"""
|
200
|
+
OpenAPI Link Object.
|
201
|
+
|
202
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#link-object
|
203
|
+
"""
|
204
|
+
|
205
|
+
model_config = ConfigDict(extra="allow")
|
206
|
+
|
207
|
+
operationRef: str | None = None
|
208
|
+
operationId: str | None = None
|
209
|
+
parameters: dict[str, Any] | None = None
|
210
|
+
requestBody: Any | None = None
|
211
|
+
description: str | None = None
|
212
|
+
server: ServerObject | None = None
|
@@ -0,0 +1,160 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI Input Models - Components Object.
|
3
|
+
|
4
|
+
ComponentsObject holds reusable schemas, responses, parameters, etc.
|
5
|
+
|
6
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#components-object
|
7
|
+
"""
|
8
|
+
|
9
|
+
from __future__ import annotations
|
10
|
+
|
11
|
+
from typing import Any
|
12
|
+
|
13
|
+
from pydantic import BaseModel, ConfigDict, Field
|
14
|
+
|
15
|
+
from .base import ExampleObject, LinkObject, ReferenceObject
|
16
|
+
from .operation import (
|
17
|
+
CallbackObject,
|
18
|
+
ParameterObject,
|
19
|
+
RequestBodyObject,
|
20
|
+
ResponseObject,
|
21
|
+
)
|
22
|
+
from .schema import SchemaObject
|
23
|
+
|
24
|
+
|
25
|
+
class SecuritySchemeObject(BaseModel):
|
26
|
+
"""
|
27
|
+
OpenAPI Security Scheme Object.
|
28
|
+
|
29
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#security-scheme-object
|
30
|
+
"""
|
31
|
+
|
32
|
+
model_config = ConfigDict(extra="allow")
|
33
|
+
|
34
|
+
type: str = Field(
|
35
|
+
...,
|
36
|
+
description="Security type: apiKey, http, oauth2, openIdConnect",
|
37
|
+
)
|
38
|
+
description: str | None = None
|
39
|
+
|
40
|
+
# apiKey
|
41
|
+
name: str | None = Field(None, description="Header/query/cookie name (for apiKey)")
|
42
|
+
in_: str | None = Field(None, alias="in", description="Location: query, header, cookie")
|
43
|
+
|
44
|
+
# http
|
45
|
+
scheme: str | None = Field(None, description="HTTP scheme: basic, bearer, etc.")
|
46
|
+
bearerFormat: str | None = None
|
47
|
+
|
48
|
+
# oauth2
|
49
|
+
flows: dict[str, Any] | None = None # OAuthFlowsObject
|
50
|
+
|
51
|
+
# openIdConnect
|
52
|
+
openIdConnectUrl: str | None = None
|
53
|
+
|
54
|
+
|
55
|
+
class ComponentsObject(BaseModel):
|
56
|
+
"""
|
57
|
+
OpenAPI Components Object.
|
58
|
+
|
59
|
+
Contains reusable schemas, responses, parameters, etc.
|
60
|
+
|
61
|
+
Reference: https://spec.openapis.org/oas/v3.1.0#components-object
|
62
|
+
"""
|
63
|
+
|
64
|
+
model_config = ConfigDict(extra="allow")
|
65
|
+
|
66
|
+
# ===== Schemas (most important for us) =====
|
67
|
+
schemas: dict[str, SchemaObject | ReferenceObject] | None = Field(
|
68
|
+
None,
|
69
|
+
description="Reusable schemas (User, UserRequest, PatchedUser, etc.)",
|
70
|
+
)
|
71
|
+
|
72
|
+
# ===== Responses =====
|
73
|
+
responses: dict[str, ResponseObject | ReferenceObject] | None = Field(
|
74
|
+
None,
|
75
|
+
description="Reusable responses (NotFound, ValidationError, etc.)",
|
76
|
+
)
|
77
|
+
|
78
|
+
# ===== Parameters =====
|
79
|
+
parameters: dict[str, ParameterObject | ReferenceObject] | None = Field(
|
80
|
+
None,
|
81
|
+
description="Reusable parameters",
|
82
|
+
)
|
83
|
+
|
84
|
+
# ===== Examples =====
|
85
|
+
examples: dict[str, ExampleObject | ReferenceObject] | None = Field(
|
86
|
+
None,
|
87
|
+
description="Reusable examples",
|
88
|
+
)
|
89
|
+
|
90
|
+
# ===== Request Bodies =====
|
91
|
+
requestBodies: dict[str, RequestBodyObject | ReferenceObject] | None = Field(
|
92
|
+
None,
|
93
|
+
description="Reusable request bodies",
|
94
|
+
)
|
95
|
+
|
96
|
+
# ===== Headers =====
|
97
|
+
headers: dict[str, Any] | None = Field(
|
98
|
+
None,
|
99
|
+
description="Reusable headers (HeaderObject | ReferenceObject)",
|
100
|
+
)
|
101
|
+
|
102
|
+
# ===== Security Schemes =====
|
103
|
+
securitySchemes: dict[str, SecuritySchemeObject | ReferenceObject] | None = Field(
|
104
|
+
None,
|
105
|
+
description="Security schemes (basicAuth, bearerAuth, etc.)",
|
106
|
+
)
|
107
|
+
|
108
|
+
# ===== Links =====
|
109
|
+
links: dict[str, LinkObject | ReferenceObject] | None = Field(
|
110
|
+
None,
|
111
|
+
description="Reusable links",
|
112
|
+
)
|
113
|
+
|
114
|
+
# ===== Callbacks =====
|
115
|
+
callbacks: dict[str, CallbackObject | ReferenceObject] | None = Field(
|
116
|
+
None,
|
117
|
+
description="Reusable callbacks",
|
118
|
+
)
|
119
|
+
|
120
|
+
# ===== Path Items (OAS 3.1.0) =====
|
121
|
+
pathItems: dict[str, Any] | None = Field(
|
122
|
+
None,
|
123
|
+
description="Reusable path items (OAS 3.1.0)",
|
124
|
+
)
|
125
|
+
|
126
|
+
@property
|
127
|
+
def has_schemas(self) -> bool:
|
128
|
+
"""Check if components contain schemas."""
|
129
|
+
return self.schemas is not None and len(self.schemas) > 0
|
130
|
+
|
131
|
+
@property
|
132
|
+
def schema_names(self) -> list[str]:
|
133
|
+
"""Get all schema names."""
|
134
|
+
if not self.schemas:
|
135
|
+
return []
|
136
|
+
return list(self.schemas.keys())
|
137
|
+
|
138
|
+
def get_schema(self, name: str) -> SchemaObject | ReferenceObject | None:
|
139
|
+
"""Get schema by name."""
|
140
|
+
if not self.schemas:
|
141
|
+
return None
|
142
|
+
return self.schemas.get(name)
|
143
|
+
|
144
|
+
def __repr__(self) -> str:
|
145
|
+
"""String representation for debugging."""
|
146
|
+
parts = ["ComponentsObject("]
|
147
|
+
|
148
|
+
if self.schemas:
|
149
|
+
parts.append(f"schemas={len(self.schemas)}")
|
150
|
+
|
151
|
+
if self.responses:
|
152
|
+
parts.append(f"responses={len(self.responses)}")
|
153
|
+
|
154
|
+
if self.parameters:
|
155
|
+
parts.append(f"parameters={len(self.parameters)}")
|
156
|
+
|
157
|
+
if self.securitySchemes:
|
158
|
+
parts.append(f"securitySchemes={len(self.securitySchemes)}")
|
159
|
+
|
160
|
+
return ", ".join(parts) + ")"
|
@@ -0,0 +1,203 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI Input Models - Root OpenAPI Spec.
|
3
|
+
|
4
|
+
This module defines the root OpenAPISpec model that represents a complete
|
5
|
+
OpenAPI specification (version 3.0.3 or 3.1.0).
|
6
|
+
|
7
|
+
Reference: https://spec.openapis.org/oas/v3.1.0
|
8
|
+
"""
|
9
|
+
|
10
|
+
from __future__ import annotations
|
11
|
+
|
12
|
+
from typing import Any, Literal
|
13
|
+
|
14
|
+
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
15
|
+
|
16
|
+
from .base import ExternalDocumentationObject, InfoObject, ServerObject, TagObject
|
17
|
+
from .components import ComponentsObject
|
18
|
+
from .operation import PathItemObject
|
19
|
+
|
20
|
+
|
21
|
+
class OpenAPISpec(BaseModel):
|
22
|
+
"""
|
23
|
+
OpenAPI Specification root object.
|
24
|
+
|
25
|
+
This is the top-level object representing a complete OpenAPI specification.
|
26
|
+
Supports both OpenAPI 3.0.3 and 3.1.0.
|
27
|
+
|
28
|
+
Reference: https://spec.openapis.org/oas/v3.1.0
|
29
|
+
|
30
|
+
Examples:
|
31
|
+
>>> spec = OpenAPISpec(
|
32
|
+
... openapi="3.1.0",
|
33
|
+
... info=InfoObject(title="My API", version="1.0.0"),
|
34
|
+
... paths={"/users/": PathItemObject(...)},
|
35
|
+
... )
|
36
|
+
>>> spec.openapi_version
|
37
|
+
'3.1.0'
|
38
|
+
>>> spec.is_openapi_31
|
39
|
+
True
|
40
|
+
"""
|
41
|
+
|
42
|
+
model_config = ConfigDict(
|
43
|
+
extra="allow", # Allow x-* extensions
|
44
|
+
validate_assignment=True,
|
45
|
+
)
|
46
|
+
|
47
|
+
# ===== Required Fields =====
|
48
|
+
openapi: str = Field(
|
49
|
+
...,
|
50
|
+
description="OpenAPI version (3.0.0, 3.0.1, 3.0.2, 3.0.3, 3.1.0)",
|
51
|
+
)
|
52
|
+
info: InfoObject = Field(..., description="API metadata")
|
53
|
+
paths: dict[str, PathItemObject] | None = Field(
|
54
|
+
None,
|
55
|
+
description="API paths (/users/, /tasks/{id}/, etc.)",
|
56
|
+
)
|
57
|
+
|
58
|
+
# ===== Optional Fields =====
|
59
|
+
jsonSchemaDialect: str | None = Field(
|
60
|
+
None,
|
61
|
+
description="JSON Schema dialect (OAS 3.1.0 only)",
|
62
|
+
)
|
63
|
+
servers: list[ServerObject] | None = Field(
|
64
|
+
None,
|
65
|
+
description="Server URLs",
|
66
|
+
)
|
67
|
+
components: ComponentsObject | None = Field(
|
68
|
+
None,
|
69
|
+
description="Reusable components (schemas, responses, etc.)",
|
70
|
+
)
|
71
|
+
security: list[dict[str, list[str]]] | None = Field(
|
72
|
+
None,
|
73
|
+
description="Global security requirements",
|
74
|
+
)
|
75
|
+
tags: list[TagObject] | None = Field(
|
76
|
+
None,
|
77
|
+
description="API tags for grouping operations",
|
78
|
+
)
|
79
|
+
externalDocs: ExternalDocumentationObject | None = None
|
80
|
+
|
81
|
+
# ===== drf-spectacular Extensions =====
|
82
|
+
x_tagGroups: list[dict[str, Any]] | None = Field(
|
83
|
+
None,
|
84
|
+
alias="x-tagGroups",
|
85
|
+
description="Tag groups from drf-spectacular",
|
86
|
+
)
|
87
|
+
|
88
|
+
# ===== Validation =====
|
89
|
+
|
90
|
+
@field_validator("openapi")
|
91
|
+
@classmethod
|
92
|
+
def validate_openapi_version(cls, v: str) -> str:
|
93
|
+
"""Validate OpenAPI version is 3.0.x or 3.1.0."""
|
94
|
+
valid_versions = ["3.0.0", "3.0.1", "3.0.2", "3.0.3", "3.1.0"]
|
95
|
+
if v not in valid_versions:
|
96
|
+
raise ValueError(
|
97
|
+
f"Unsupported OpenAPI version: {v}. "
|
98
|
+
f"Supported versions: {', '.join(valid_versions)}"
|
99
|
+
)
|
100
|
+
return v
|
101
|
+
|
102
|
+
# ===== Computed Properties =====
|
103
|
+
|
104
|
+
@property
|
105
|
+
def openapi_version(self) -> str:
|
106
|
+
"""Get OpenAPI version string."""
|
107
|
+
return self.openapi
|
108
|
+
|
109
|
+
@property
|
110
|
+
def is_openapi_30(self) -> bool:
|
111
|
+
"""Check if OpenAPI 3.0.x."""
|
112
|
+
return self.openapi.startswith("3.0.")
|
113
|
+
|
114
|
+
@property
|
115
|
+
def is_openapi_31(self) -> bool:
|
116
|
+
"""Check if OpenAPI 3.1.0."""
|
117
|
+
return self.openapi == "3.1.0"
|
118
|
+
|
119
|
+
@property
|
120
|
+
def normalized_version(self) -> Literal["3.0.3", "3.1.0"]:
|
121
|
+
"""
|
122
|
+
Get normalized OpenAPI version (3.0.3 or 3.1.0).
|
123
|
+
|
124
|
+
All 3.0.x versions are normalized to 3.0.3.
|
125
|
+
"""
|
126
|
+
if self.is_openapi_30:
|
127
|
+
return "3.0.3"
|
128
|
+
return "3.1.0"
|
129
|
+
|
130
|
+
@property
|
131
|
+
def has_components(self) -> bool:
|
132
|
+
"""Check if spec has components."""
|
133
|
+
return self.components is not None and self.components.has_schemas
|
134
|
+
|
135
|
+
@property
|
136
|
+
def has_paths(self) -> bool:
|
137
|
+
"""Check if spec has paths."""
|
138
|
+
return self.paths is not None and len(self.paths) > 0
|
139
|
+
|
140
|
+
@property
|
141
|
+
def all_paths(self) -> dict[str, PathItemObject]:
|
142
|
+
"""Get all paths (empty dict if none)."""
|
143
|
+
return self.paths or {}
|
144
|
+
|
145
|
+
@property
|
146
|
+
def all_operations(self) -> dict[str, tuple[str, str, Any]]:
|
147
|
+
"""
|
148
|
+
Get all operations from all paths.
|
149
|
+
|
150
|
+
Returns:
|
151
|
+
Dict of {operation_id: (http_method, path, OperationObject)}
|
152
|
+
|
153
|
+
Examples:
|
154
|
+
>>> spec = OpenAPISpec(...)
|
155
|
+
>>> ops = spec.all_operations
|
156
|
+
>>> ops['users_list']
|
157
|
+
('GET', '/api/users/', OperationObject(...))
|
158
|
+
"""
|
159
|
+
result = {}
|
160
|
+
if not self.paths:
|
161
|
+
return result
|
162
|
+
|
163
|
+
for path, path_item in self.paths.items():
|
164
|
+
for method, operation in path_item.operations.items():
|
165
|
+
if operation.operationId:
|
166
|
+
result[operation.operationId] = (method, path, operation)
|
167
|
+
|
168
|
+
return result
|
169
|
+
|
170
|
+
@property
|
171
|
+
def all_schema_names(self) -> list[str]:
|
172
|
+
"""Get all schema names from components."""
|
173
|
+
if not self.components or not self.components.schemas:
|
174
|
+
return []
|
175
|
+
return list(self.components.schemas.keys())
|
176
|
+
|
177
|
+
@property
|
178
|
+
def server_urls(self) -> list[str]:
|
179
|
+
"""Get all server URLs."""
|
180
|
+
if not self.servers:
|
181
|
+
return []
|
182
|
+
return [server.url for server in self.servers]
|
183
|
+
|
184
|
+
def get_schema(self, name: str) -> Any:
|
185
|
+
"""Get schema from components by name."""
|
186
|
+
if not self.components:
|
187
|
+
return None
|
188
|
+
return self.components.get_schema(name)
|
189
|
+
|
190
|
+
def __repr__(self) -> str:
|
191
|
+
"""String representation for debugging."""
|
192
|
+
parts = [
|
193
|
+
f"OpenAPISpec(openapi={self.openapi!r}",
|
194
|
+
f"title={self.info.title!r}",
|
195
|
+
]
|
196
|
+
|
197
|
+
if self.paths:
|
198
|
+
parts.append(f"paths={len(self.paths)}")
|
199
|
+
|
200
|
+
if self.components and self.components.schemas:
|
201
|
+
parts.append(f"schemas={len(self.components.schemas)}")
|
202
|
+
|
203
|
+
return ", ".join(parts) + ")"
|