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.
- 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 +73 -49
- django_cfg/core/integration/display/startup.py +30 -22
- django_cfg/core/integration/url_integration.py +15 -16
- 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 +162 -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 +208 -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 +838 -0
- django_cfg/modules/django_client/core/generator/python/__init__.py +16 -0
- django_cfg/modules/django_client/core/generator/python/async_client_gen.py +174 -0
- django_cfg/modules/django_client/core/generator/python/files_generator.py +180 -0
- django_cfg/modules/django_client/core/generator/python/generator.py +182 -0
- django_cfg/modules/django_client/core/generator/python/models_generator.py +318 -0
- django_cfg/modules/django_client/core/generator/python/operations_generator.py +278 -0
- django_cfg/modules/django_client/core/generator/python/sync_client_gen.py +102 -0
- django_cfg/modules/django_client/core/generator/python/templates/__init__.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/api_wrapper.py.jinja +153 -0
- django_cfg/modules/django_client/core/generator/python/templates/app_init.py.jinja +6 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/app_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/flat_client.py.jinja +38 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/main_client.py.jinja +68 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja +14 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/operation_method.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sub_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja +50 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_operation_method.py.jinja +9 -0
- django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja +18 -0
- django_cfg/modules/django_client/core/generator/python/templates/client_file.py.jinja +13 -0
- django_cfg/modules/django_client/core/generator/python/templates/main_init.py.jinja +52 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/app_models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/enum_class.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/enums.py.jinja +8 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/models.py.jinja +17 -0
- django_cfg/modules/django_client/core/generator/python/templates/models/schema_class.py.jinja +21 -0
- django_cfg/modules/django_client/core/generator/python/templates/pyproject.toml.jinja +55 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/logger.py.jinja +255 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/retry.py.jinja +271 -0
- django_cfg/modules/django_client/core/generator/python/templates/utils/schema.py.jinja +12 -0
- django_cfg/modules/django_client/core/generator/typescript/__init__.py +14 -0
- django_cfg/modules/django_client/core/generator/typescript/client_generator.py +165 -0
- django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +428 -0
- django_cfg/modules/django_client/core/generator/typescript/files_generator.py +207 -0
- django_cfg/modules/django_client/core/generator/typescript/generator.py +432 -0
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +536 -0
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +245 -0
- django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +298 -0
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +329 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/api_instance.ts.jinja +131 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/app_index.ts.jinja +2 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/app_client.ts.jinja +18 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/client.ts.jinja +403 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/flat_client.ts.jinja +109 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/main_client_file.ts.jinja +10 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/operation.ts.jinja +61 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client/sub_client.ts.jinja +15 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/client_file.ts.jinja +9 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/fetchers.ts.jinja +45 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/index.ts.jinja +30 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/index.ts.jinja +5 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/main_index.ts.jinja +268 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/app_models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/enums.ts.jinja +4 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/models/models.ts.jinja +8 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/package.json.jinja +52 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/schemas/index.ts.jinja +21 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/schemas/schema.ts.jinja +24 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/tsconfig.json.jinja +20 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/errors.ts.jinja +116 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/http.ts.jinja +98 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/logger.ts.jinja +259 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/retry.ts.jinja +175 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/schema.ts.jinja +7 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/utils/storage.ts.jinja +158 -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 +427 -0
- django_cfg/modules/django_client/management/commands/validate_openapi.py +343 -0
- django_cfg/modules/django_client/pytest.ini +30 -0
- django_cfg/modules/django_client/spectacular/__init__.py +10 -0
- django_cfg/modules/django_client/spectacular/async_detection.py +187 -0
- django_cfg/modules/django_client/spectacular/enum_naming.py +192 -0
- django_cfg/modules/django_client/urls.py +72 -0
- django_cfg/{dashboard → modules/django_dashboard}/DEBUG_README.md +2 -2
- django_cfg/{dashboard → modules/django_dashboard}/REFACTORING_SUMMARY.md +1 -1
- django_cfg/modules/django_dashboard/management/__init__.py +0 -0
- django_cfg/modules/django_dashboard/management/commands/__init__.py +0 -0
- django_cfg/{dashboard → modules/django_dashboard}/management/commands/debug_dashboard.py +5 -5
- django_cfg/modules/django_dashboard/sections/documentation.py +391 -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/LOGGING_GUIDE.md +1 -1
- 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 +21 -10
- 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.13.dist-info}/METADATA +2 -2
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/RECORD +224 -74
- django_cfg/management/commands/generate.py +0 -107
- /django_cfg/models/django/{revolution.py → revolution_legacy.py} +0 -0
- /django_cfg/{dashboard → modules/django_admin}/management/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_admin}/management/commands/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/components.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/debug.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/__init__.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/base.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/commands.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/overview.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/stats.py +0 -0
- /django_cfg/{dashboard → modules/django_dashboard}/sections/system.py +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.10.dist-info → django_cfg-1.4.13.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,153 @@
|
|
1
|
+
class API:
|
2
|
+
"""
|
3
|
+
API Client wrapper with JWT token management.
|
4
|
+
|
5
|
+
This class provides:
|
6
|
+
- Thread-safe JWT token storage
|
7
|
+
- Automatic Authorization header injection
|
8
|
+
- Context manager support for async operations
|
9
|
+
- Optional retry and logging configuration
|
10
|
+
|
11
|
+
Example:
|
12
|
+
>>> api = API('https://api.example.com')
|
13
|
+
>>> api.set_token('jwt-token')
|
14
|
+
>>> async with api:
|
15
|
+
... users = await api.users.list()
|
16
|
+
>>>
|
17
|
+
>>> # With retry and logging
|
18
|
+
>>> api = API(
|
19
|
+
... 'https://api.example.com',
|
20
|
+
... retry_config=RetryConfig(max_attempts=5),
|
21
|
+
... logger_config=LoggerConfig(enabled=True)
|
22
|
+
... )
|
23
|
+
"""
|
24
|
+
|
25
|
+
def __init__(
|
26
|
+
self,
|
27
|
+
base_url: str,
|
28
|
+
logger_config: LoggerConfig | None = None,
|
29
|
+
retry_config: RetryConfig | None = None,
|
30
|
+
**kwargs: Any
|
31
|
+
):
|
32
|
+
"""
|
33
|
+
Initialize API client.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
base_url: Base API URL (e.g., 'https://api.example.com')
|
37
|
+
logger_config: Logger configuration (None to disable logging)
|
38
|
+
retry_config: Retry configuration (None to disable retry)
|
39
|
+
**kwargs: Additional httpx.AsyncClient kwargs
|
40
|
+
"""
|
41
|
+
self.base_url = base_url.rstrip('/')
|
42
|
+
self._kwargs = kwargs
|
43
|
+
self._logger_config = logger_config
|
44
|
+
self._retry_config = retry_config
|
45
|
+
self._token: str | None = None
|
46
|
+
self._refresh_token: str | None = None
|
47
|
+
self._lock = threading.Lock()
|
48
|
+
self._client: APIClient | None = None
|
49
|
+
self._init_clients()
|
50
|
+
|
51
|
+
def _init_clients(self) -> None:
|
52
|
+
"""Initialize API client with current token."""
|
53
|
+
# Create httpx client with auth header if token exists
|
54
|
+
headers = {}
|
55
|
+
if self._token:
|
56
|
+
headers['Authorization'] = f'Bearer {self._token}'
|
57
|
+
|
58
|
+
kwargs = {**self._kwargs}
|
59
|
+
if headers:
|
60
|
+
kwargs['headers'] = headers
|
61
|
+
|
62
|
+
# Create new APIClient
|
63
|
+
self._client = APIClient(
|
64
|
+
self.base_url,
|
65
|
+
logger_config=self._logger_config,
|
66
|
+
retry_config=self._retry_config,
|
67
|
+
**kwargs
|
68
|
+
)
|
69
|
+
|
70
|
+
{% for prop in properties %}
|
71
|
+
@property
|
72
|
+
def {{ prop.property }}(self) -> {{ prop.class_name }}:
|
73
|
+
"""Access {{ prop.tag }} endpoints."""
|
74
|
+
return self._client.{{ prop.property }}
|
75
|
+
|
76
|
+
{% endfor %}
|
77
|
+
def get_token(self) -> str | None:
|
78
|
+
"""Get current JWT token."""
|
79
|
+
with self._lock:
|
80
|
+
return self._token
|
81
|
+
|
82
|
+
def get_refresh_token(self) -> str | None:
|
83
|
+
"""Get current refresh token."""
|
84
|
+
with self._lock:
|
85
|
+
return self._refresh_token
|
86
|
+
|
87
|
+
def set_token(self, token: str, refresh_token: str | None = None) -> None:
|
88
|
+
"""
|
89
|
+
Set JWT token and refresh token.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
token: JWT access token
|
93
|
+
refresh_token: JWT refresh token (optional)
|
94
|
+
"""
|
95
|
+
with self._lock:
|
96
|
+
self._token = token
|
97
|
+
if refresh_token:
|
98
|
+
self._refresh_token = refresh_token
|
99
|
+
|
100
|
+
# Reinitialize clients with new token
|
101
|
+
self._init_clients()
|
102
|
+
|
103
|
+
def clear_tokens(self) -> None:
|
104
|
+
"""Clear all tokens."""
|
105
|
+
with self._lock:
|
106
|
+
self._token = None
|
107
|
+
self._refresh_token = None
|
108
|
+
|
109
|
+
# Reinitialize clients without token
|
110
|
+
self._init_clients()
|
111
|
+
|
112
|
+
def is_authenticated(self) -> bool:
|
113
|
+
"""Check if user is authenticated."""
|
114
|
+
return self.get_token() is not None
|
115
|
+
|
116
|
+
def set_base_url(self, url: str) -> None:
|
117
|
+
"""
|
118
|
+
Update base URL and reinitialize clients.
|
119
|
+
|
120
|
+
Args:
|
121
|
+
url: New base URL
|
122
|
+
"""
|
123
|
+
self.base_url = url.rstrip('/')
|
124
|
+
self._init_clients()
|
125
|
+
|
126
|
+
def get_base_url(self) -> str:
|
127
|
+
"""Get current base URL."""
|
128
|
+
return self.base_url
|
129
|
+
|
130
|
+
def get_schema(self) -> dict[str, Any]:
|
131
|
+
"""
|
132
|
+
Get OpenAPI schema.
|
133
|
+
|
134
|
+
Returns:
|
135
|
+
Complete OpenAPI specification for this API
|
136
|
+
"""
|
137
|
+
return OPENAPI_SCHEMA
|
138
|
+
|
139
|
+
async def __aenter__(self) -> 'API':
|
140
|
+
"""Async context manager entry."""
|
141
|
+
if self._client:
|
142
|
+
await self._client.__aenter__()
|
143
|
+
return self
|
144
|
+
|
145
|
+
async def __aexit__(self, *args: Any) -> None:
|
146
|
+
"""Async context manager exit."""
|
147
|
+
if self._client:
|
148
|
+
await self._client.__aexit__(*args)
|
149
|
+
|
150
|
+
async def close(self) -> None:
|
151
|
+
"""Close HTTP client."""
|
152
|
+
if self._client:
|
153
|
+
await self._client.close()
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from .models import *
|
6
|
+
|
7
|
+
|
8
|
+
class {{ class_name }}:
|
9
|
+
"""API endpoints for {{ tag }}."""
|
10
|
+
|
11
|
+
def __init__(self, client: httpx.AsyncClient):
|
12
|
+
"""Initialize sub-client with shared httpx client."""
|
13
|
+
self._client = client
|
14
|
+
|
15
|
+
{% for operation in operations %}
|
16
|
+
{{ operation | indent(4, first=True) }}
|
17
|
+
|
18
|
+
{% endfor %}
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class APIClient:
|
2
|
+
"""
|
3
|
+
Async API client for {{ api_title }}.
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
>>> async with APIClient(base_url='https://api.example.com') as client:
|
7
|
+
... users = await client.users_list()
|
8
|
+
"""
|
9
|
+
|
10
|
+
def __init__(self, base_url: str, **kwargs: Any):
|
11
|
+
"""
|
12
|
+
Initialize API client.
|
13
|
+
|
14
|
+
Args:
|
15
|
+
base_url: Base API URL (e.g., 'https://api.example.com')
|
16
|
+
**kwargs: Additional httpx.AsyncClient kwargs
|
17
|
+
"""
|
18
|
+
self.base_url = base_url.rstrip('/')
|
19
|
+
self._client = httpx.AsyncClient(
|
20
|
+
base_url=self.base_url,
|
21
|
+
timeout=30.0,
|
22
|
+
**kwargs,
|
23
|
+
)
|
24
|
+
|
25
|
+
async def __aenter__(self) -> 'APIClient':
|
26
|
+
return self
|
27
|
+
|
28
|
+
async def __aexit__(self, *args: Any) -> None:
|
29
|
+
await self._client.aclose()
|
30
|
+
|
31
|
+
async def close(self) -> None:
|
32
|
+
"""Close HTTP client."""
|
33
|
+
await self._client.aclose()
|
34
|
+
|
35
|
+
{% for operation in operations %}
|
36
|
+
{{ operation }}
|
37
|
+
|
38
|
+
{% endfor %}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class APIClient:
|
2
|
+
"""
|
3
|
+
Async API client for {{ api_title }}.
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
>>> async with APIClient(base_url='https://api.example.com') as client:
|
7
|
+
... users = await client.users.list()
|
8
|
+
... post = await client.posts.create(data=new_post)
|
9
|
+
>>>
|
10
|
+
>>> # With retry configuration
|
11
|
+
>>> retry_config = RetryConfig(max_attempts=5, min_wait=2.0)
|
12
|
+
>>> async with APIClient(base_url='https://api.example.com', retry_config=retry_config) as client:
|
13
|
+
... users = await client.users.list()
|
14
|
+
"""
|
15
|
+
|
16
|
+
def __init__(
|
17
|
+
self,
|
18
|
+
base_url: str,
|
19
|
+
logger_config: Optional[LoggerConfig] = None,
|
20
|
+
retry_config: Optional[RetryConfig] = None,
|
21
|
+
**kwargs: Any,
|
22
|
+
):
|
23
|
+
"""
|
24
|
+
Initialize API client.
|
25
|
+
|
26
|
+
Args:
|
27
|
+
base_url: Base API URL (e.g., 'https://api.example.com')
|
28
|
+
logger_config: Logger configuration (None to disable logging)
|
29
|
+
retry_config: Retry configuration (None to disable retry)
|
30
|
+
**kwargs: Additional httpx.AsyncClient kwargs
|
31
|
+
"""
|
32
|
+
self.base_url = base_url.rstrip('/')
|
33
|
+
|
34
|
+
# Create HTTP client with or without retry
|
35
|
+
if retry_config is not None:
|
36
|
+
self._client = RetryAsyncClient(
|
37
|
+
base_url=self.base_url,
|
38
|
+
retry_config=retry_config,
|
39
|
+
timeout=30.0,
|
40
|
+
**kwargs,
|
41
|
+
)
|
42
|
+
else:
|
43
|
+
self._client = httpx.AsyncClient(
|
44
|
+
base_url=self.base_url,
|
45
|
+
timeout=30.0,
|
46
|
+
**kwargs,
|
47
|
+
)
|
48
|
+
|
49
|
+
# Initialize logger
|
50
|
+
self.logger: Optional[APILogger] = None
|
51
|
+
if logger_config is not None:
|
52
|
+
self.logger = APILogger(logger_config)
|
53
|
+
|
54
|
+
# Initialize sub-clients
|
55
|
+
{% for tag in tags %}
|
56
|
+
self.{{ tag.property }} = {{ tag.class_name }}(self._client)
|
57
|
+
{% endfor %}
|
58
|
+
|
59
|
+
async def __aenter__(self) -> 'APIClient':
|
60
|
+
await self._client.__aenter__()
|
61
|
+
return self
|
62
|
+
|
63
|
+
async def __aexit__(self, *args: Any) -> None:
|
64
|
+
await self._client.__aexit__(*args)
|
65
|
+
|
66
|
+
async def close(self) -> None:
|
67
|
+
"""Close HTTP client."""
|
68
|
+
await self._client.aclose()
|
django_cfg/modules/django_client/core/generator/python/templates/client/main_client_file.py.jinja
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from typing import Any, Optional
|
4
|
+
|
5
|
+
import httpx
|
6
|
+
|
7
|
+
{% for tag in tags %}
|
8
|
+
from .{{ tag.slug }} import {{ tag.class_name }}
|
9
|
+
{% endfor %}
|
10
|
+
from .logger import APILogger, LoggerConfig
|
11
|
+
from .retry import RetryConfig, RetryAsyncClient
|
12
|
+
|
13
|
+
|
14
|
+
{{ client_code }}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from .models import *
|
6
|
+
|
7
|
+
|
8
|
+
class {{ class_name }}:
|
9
|
+
"""API endpoints for {{ tag }}."""
|
10
|
+
|
11
|
+
def __init__(self, client: httpx.AsyncClient):
|
12
|
+
"""Initialize sub-client with shared httpx client."""
|
13
|
+
self._client = client
|
14
|
+
|
15
|
+
{% for operation in operations %}
|
16
|
+
{{ operation | indent(4, first=True) }}
|
17
|
+
|
18
|
+
{% endfor %}
|
django_cfg/modules/django_client/core/generator/python/templates/client/sync_main_client.py.jinja
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
class SyncAPIClient:
|
2
|
+
"""
|
3
|
+
Synchronous API client for {{ api_title }}.
|
4
|
+
|
5
|
+
Usage:
|
6
|
+
>>> with SyncAPIClient(base_url='https://api.example.com') as client:
|
7
|
+
... users = client.users.list()
|
8
|
+
... post = client.posts.create(data=new_post)
|
9
|
+
"""
|
10
|
+
|
11
|
+
def __init__(
|
12
|
+
self,
|
13
|
+
base_url: str,
|
14
|
+
logger_config: Optional[LoggerConfig] = None,
|
15
|
+
**kwargs: Any,
|
16
|
+
):
|
17
|
+
"""
|
18
|
+
Initialize sync API client.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
base_url: Base API URL (e.g., 'https://api.example.com')
|
22
|
+
logger_config: Logger configuration (None to disable logging)
|
23
|
+
**kwargs: Additional httpx.Client kwargs
|
24
|
+
"""
|
25
|
+
self.base_url = base_url.rstrip('/')
|
26
|
+
self._client = httpx.Client(
|
27
|
+
base_url=self.base_url,
|
28
|
+
timeout=30.0,
|
29
|
+
**kwargs,
|
30
|
+
)
|
31
|
+
|
32
|
+
# Initialize logger
|
33
|
+
self.logger: Optional[APILogger] = None
|
34
|
+
if logger_config is not None:
|
35
|
+
self.logger = APILogger(logger_config)
|
36
|
+
|
37
|
+
# Initialize sub-clients
|
38
|
+
{% for tag in tags %}
|
39
|
+
self.{{ tag.property }} = Sync{{ tag.class_name }}(self._client)
|
40
|
+
{% endfor %}
|
41
|
+
|
42
|
+
def __enter__(self) -> 'SyncAPIClient':
|
43
|
+
return self
|
44
|
+
|
45
|
+
def __exit__(self, *args: Any) -> None:
|
46
|
+
self._client.close()
|
47
|
+
|
48
|
+
def close(self) -> None:
|
49
|
+
"""Close HTTP client."""
|
50
|
+
self._client.close()
|
django_cfg/modules/django_client/core/generator/python/templates/client/sync_sub_client.py.jinja
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import httpx
|
4
|
+
|
5
|
+
from .models import *
|
6
|
+
|
7
|
+
|
8
|
+
class Sync{{ class_name }}:
|
9
|
+
"""Synchronous API endpoints for {{ tag }}."""
|
10
|
+
|
11
|
+
def __init__(self, client: httpx.Client):
|
12
|
+
"""Initialize sync sub-client with shared httpx client."""
|
13
|
+
self._client = client
|
14
|
+
|
15
|
+
{% for operation in operations %}
|
16
|
+
{{ operation | indent(4, first=True) }}
|
17
|
+
|
18
|
+
{% endfor %}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
"""
|
2
|
+
{{ api_title }} - API Client with JWT Management
|
3
|
+
|
4
|
+
Usage:
|
5
|
+
>>> from api import API
|
6
|
+
>>>
|
7
|
+
>>> api = API('https://api.example.com')
|
8
|
+
>>>
|
9
|
+
>>> # Set JWT token
|
10
|
+
>>> api.set_token('your-jwt-token', 'refresh-token')
|
11
|
+
>>>
|
12
|
+
>>> # Use API
|
13
|
+
>>> async with api:
|
14
|
+
... posts = await api.posts.list()
|
15
|
+
... user = await api.users.retrieve(1)
|
16
|
+
>>>
|
17
|
+
>>> # Check authentication
|
18
|
+
>>> if api.is_authenticated():
|
19
|
+
... # ...
|
20
|
+
>>>
|
21
|
+
>>> # Get OpenAPI schema
|
22
|
+
>>> schema = api.get_schema()
|
23
|
+
"""
|
24
|
+
|
25
|
+
from __future__ import annotations
|
26
|
+
|
27
|
+
import threading
|
28
|
+
from typing import Any
|
29
|
+
|
30
|
+
import httpx
|
31
|
+
|
32
|
+
from .client import APIClient
|
33
|
+
from .schema import OPENAPI_SCHEMA
|
34
|
+
from .logger import LoggerConfig
|
35
|
+
from .retry import RetryConfig
|
36
|
+
{% for tag in tags %}
|
37
|
+
from .{{ tag.slug }} import {{ tag.class_name }}
|
38
|
+
{% endfor %}
|
39
|
+
{% if has_enums %}
|
40
|
+
from . import enums
|
41
|
+
from .enums import {{ enum_names | join(', ') }}
|
42
|
+
{% endif %}
|
43
|
+
|
44
|
+
TOKEN_KEY = "auth_token"
|
45
|
+
REFRESH_TOKEN_KEY = "refresh_token"
|
46
|
+
|
47
|
+
{{ api_class }}
|
48
|
+
|
49
|
+
__all__ = [
|
50
|
+
"API",
|
51
|
+
"APIClient",
|
52
|
+
]
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from datetime import datetime
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
7
|
+
{% if has_enums and enum_names %}
|
8
|
+
|
9
|
+
from ..enums import {{ enum_names | join(', ') }}
|
10
|
+
{% endif %}
|
11
|
+
|
12
|
+
|
13
|
+
{% for schema in schemas %}
|
14
|
+
{{ schema }}
|
15
|
+
|
16
|
+
|
17
|
+
{% endfor %}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class {{ name }}({{ base_class }}):
|
2
|
+
{% if docstring %}
|
3
|
+
"""
|
4
|
+
{{ docstring | indent(4, first=True) }}
|
5
|
+
"""
|
6
|
+
{% endif %}
|
7
|
+
{% if members %}
|
8
|
+
{% if docstring %}
|
9
|
+
|
10
|
+
{% endif %}
|
11
|
+
{% for member in members %}
|
12
|
+
{{ member }}
|
13
|
+
{% endfor %}
|
14
|
+
{% else %}
|
15
|
+
|
16
|
+
pass
|
17
|
+
{% endif %}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from datetime import datetime
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from pydantic import BaseModel, ConfigDict, Field
|
7
|
+
{% if has_enums %}
|
8
|
+
|
9
|
+
from .enums import *
|
10
|
+
{% endif %}
|
11
|
+
|
12
|
+
|
13
|
+
{% for schema in schemas %}
|
14
|
+
{{ schema }}
|
15
|
+
|
16
|
+
|
17
|
+
{% endfor %}
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class {{ name }}(BaseModel):
|
2
|
+
{% if docstring %}
|
3
|
+
"""
|
4
|
+
{{ docstring | indent(4, first=True) }}
|
5
|
+
"""
|
6
|
+
{% endif %}
|
7
|
+
|
8
|
+
model_config = ConfigDict(
|
9
|
+
validate_assignment=True,
|
10
|
+
extra="forbid",
|
11
|
+
frozen=False,
|
12
|
+
)
|
13
|
+
{% if fields %}
|
14
|
+
|
15
|
+
{% for field in fields %}
|
16
|
+
{{ field }}
|
17
|
+
{% endfor %}
|
18
|
+
{% else %}
|
19
|
+
|
20
|
+
pass
|
21
|
+
{% endif %}
|
@@ -0,0 +1,55 @@
|
|
1
|
+
[tool.poetry]
|
2
|
+
name = "{{ package_name }}"
|
3
|
+
version = "{{ version }}"
|
4
|
+
description = "{{ description }}"
|
5
|
+
authors = {{ authors | tojson }}
|
6
|
+
license = "{{ license }}"
|
7
|
+
readme = "README.md"
|
8
|
+
{% if repository_url %}repository = "{{ repository_url }}"
|
9
|
+
{% endif %}keywords = {{ keywords | tojson }}
|
10
|
+
classifiers = [
|
11
|
+
"Development Status :: 5 - Production/Stable",
|
12
|
+
"Intended Audience :: Developers",
|
13
|
+
"License :: OSI Approved :: MIT License",
|
14
|
+
"Programming Language :: Python :: 3",
|
15
|
+
"Programming Language :: Python :: 3.12",
|
16
|
+
"Typing :: Typed",
|
17
|
+
]
|
18
|
+
|
19
|
+
[tool.poetry.dependencies]
|
20
|
+
python = "{{ python_version }}"
|
21
|
+
pydantic = "^2.12"
|
22
|
+
httpx = "^0.28"
|
23
|
+
tenacity = "^9.1"
|
24
|
+
rich = "^14.1.0"
|
25
|
+
|
26
|
+
[tool.poetry.group.dev.dependencies]
|
27
|
+
pytest = "^8.0"
|
28
|
+
pytest-asyncio = "^0.24"
|
29
|
+
pytest-cov = "^6.0"
|
30
|
+
mypy = "^1.18"
|
31
|
+
ruff = "^0.13"
|
32
|
+
|
33
|
+
[build-system]
|
34
|
+
requires = ["poetry-core"]
|
35
|
+
build-backend = "poetry.core.masonry.api"
|
36
|
+
|
37
|
+
[tool.mypy]
|
38
|
+
python_version = "3.12"
|
39
|
+
strict = true
|
40
|
+
warn_return_any = true
|
41
|
+
warn_unused_configs = true
|
42
|
+
disallow_untyped_defs = true
|
43
|
+
|
44
|
+
[tool.ruff]
|
45
|
+
line-length = 100
|
46
|
+
target-version = "py312"
|
47
|
+
|
48
|
+
[tool.ruff.lint]
|
49
|
+
select = ["E", "F", "I", "N", "UP", "B"]
|
50
|
+
ignore = []
|
51
|
+
|
52
|
+
[tool.pytest.ini_options]
|
53
|
+
asyncio_mode = "auto"
|
54
|
+
testpaths = ["tests"]
|
55
|
+
addopts = "--cov={{ package_name | replace('-', '_') }} --cov-report=html --cov-report=term"
|