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,12 @@
|
|
1
|
+
"""
|
2
|
+
OpenAPI Schema
|
3
|
+
|
4
|
+
This file contains the complete OpenAPI specification for this API.
|
5
|
+
It can be used for documentation, validation, or code generation.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Any, Dict
|
9
|
+
|
10
|
+
OPENAPI_SCHEMA: Dict[str, Any] = {{ schema_dict }}
|
11
|
+
|
12
|
+
__all__ = ["OPENAPI_SCHEMA"]
|
django_cfg/modules/django_client/core/generator/templates/typescript/client/app_client.ts.jinja
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
import * as Models from "./models";
|
2
|
+
|
3
|
+
|
4
|
+
/**
|
5
|
+
* API endpoints for {{ tag }}.
|
6
|
+
*/
|
7
|
+
export class {{ class_name }} {
|
8
|
+
private client: any;
|
9
|
+
|
10
|
+
constructor(client: any) {
|
11
|
+
this.client = client;
|
12
|
+
}
|
13
|
+
|
14
|
+
{% for operation in operations %}
|
15
|
+
{{ operation }}
|
16
|
+
|
17
|
+
{% endfor %}
|
18
|
+
}
|
@@ -0,0 +1,327 @@
|
|
1
|
+
{% if sub_clients %}
|
2
|
+
{# Namespaced client with sub-clients #}
|
3
|
+
{% if include_imports %}
|
4
|
+
{% for tag in tags %}
|
5
|
+
import { {{ tag.class_name }} } from "./{{ tag.slug }}";
|
6
|
+
{% endfor %}
|
7
|
+
import { HttpClientAdapter, FetchAdapter } from "./http";
|
8
|
+
import { APIError, NetworkError } from "./errors";
|
9
|
+
import { APILogger, type LoggerConfig } from "./logger";
|
10
|
+
|
11
|
+
|
12
|
+
{% endif %}
|
13
|
+
/**
|
14
|
+
* Async API client for {{ info.title }}.
|
15
|
+
*
|
16
|
+
* Usage:
|
17
|
+
* ```typescript
|
18
|
+
* const client = new APIClient('https://api.example.com');
|
19
|
+
* const users = await client.users.list();
|
20
|
+
* const post = await client.posts.create(newPost);
|
21
|
+
*
|
22
|
+
* // Custom HTTP adapter (e.g., Axios)
|
23
|
+
* const client = new APIClient('https://api.example.com', {
|
24
|
+
* httpClient: new AxiosAdapter()
|
25
|
+
* });
|
26
|
+
* ```
|
27
|
+
*/
|
28
|
+
export class APIClient {
|
29
|
+
private baseUrl: string;
|
30
|
+
private httpClient: HttpClientAdapter;
|
31
|
+
private logger: APILogger | null = null;
|
32
|
+
|
33
|
+
// Sub-clients
|
34
|
+
{% for tag in tags %}
|
35
|
+
public {{ tag.property }}: {{ tag.class_name }};
|
36
|
+
{% endfor %}
|
37
|
+
|
38
|
+
constructor(
|
39
|
+
baseUrl: string,
|
40
|
+
options?: {
|
41
|
+
httpClient?: HttpClientAdapter;
|
42
|
+
loggerConfig?: Partial<LoggerConfig>;
|
43
|
+
}
|
44
|
+
) {
|
45
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
46
|
+
this.httpClient = options?.httpClient || new FetchAdapter();
|
47
|
+
|
48
|
+
// Initialize logger if config provided
|
49
|
+
if (options?.loggerConfig !== undefined) {
|
50
|
+
this.logger = new APILogger(options.loggerConfig);
|
51
|
+
}
|
52
|
+
|
53
|
+
// Initialize sub-clients
|
54
|
+
{% for tag in tags %}
|
55
|
+
this.{{ tag.property }} = new {{ tag.class_name }}(this);
|
56
|
+
{% endfor %}
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* Get CSRF token from cookies.
|
61
|
+
*/
|
62
|
+
getCsrfToken(): string | null {
|
63
|
+
const name = 'csrftoken';
|
64
|
+
const value = `; ${document.cookie}`;
|
65
|
+
const parts = value.split(`; ${name}=`);
|
66
|
+
if (parts.length === 2) {
|
67
|
+
return parts.pop()?.split(';').shift() || null;
|
68
|
+
}
|
69
|
+
return null;
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* Make HTTP request with Django CSRF and session handling.
|
74
|
+
*/
|
75
|
+
async request<T>(
|
76
|
+
method: string,
|
77
|
+
path: string,
|
78
|
+
options?: {
|
79
|
+
params?: Record<string, any>;
|
80
|
+
body?: any;
|
81
|
+
formData?: FormData;
|
82
|
+
}
|
83
|
+
): Promise<T> {
|
84
|
+
const url = new URL(path, this.baseUrl);
|
85
|
+
const startTime = Date.now();
|
86
|
+
|
87
|
+
// Build headers
|
88
|
+
const headers: Record<string, string> = {};
|
89
|
+
|
90
|
+
// Don't set Content-Type for FormData (browser will set it with boundary)
|
91
|
+
if (!options?.formData) {
|
92
|
+
headers['Content-Type'] = 'application/json';
|
93
|
+
}
|
94
|
+
|
95
|
+
// Add CSRF token for non-GET requests
|
96
|
+
if (method !== 'GET') {
|
97
|
+
const csrfToken = this.getCsrfToken();
|
98
|
+
if (csrfToken) {
|
99
|
+
headers['X-CSRFToken'] = csrfToken;
|
100
|
+
}
|
101
|
+
}
|
102
|
+
|
103
|
+
// Log request
|
104
|
+
if (this.logger) {
|
105
|
+
this.logger.logRequest({
|
106
|
+
method,
|
107
|
+
url: url.toString(),
|
108
|
+
headers,
|
109
|
+
body: options?.formData || options?.body,
|
110
|
+
timestamp: startTime,
|
111
|
+
});
|
112
|
+
}
|
113
|
+
|
114
|
+
try {
|
115
|
+
// Make request via HTTP adapter
|
116
|
+
const response = await this.httpClient.request<T>({
|
117
|
+
method,
|
118
|
+
url: url.toString(),
|
119
|
+
headers,
|
120
|
+
params: options?.params,
|
121
|
+
body: options?.body,
|
122
|
+
formData: options?.formData,
|
123
|
+
});
|
124
|
+
|
125
|
+
const duration = Date.now() - startTime;
|
126
|
+
|
127
|
+
// Check for HTTP errors
|
128
|
+
if (response.status >= 400) {
|
129
|
+
const error = new APIError(
|
130
|
+
response.status,
|
131
|
+
response.statusText,
|
132
|
+
response.data,
|
133
|
+
url.toString()
|
134
|
+
);
|
135
|
+
|
136
|
+
// Log error
|
137
|
+
if (this.logger) {
|
138
|
+
this.logger.logError(
|
139
|
+
{
|
140
|
+
method,
|
141
|
+
url: url.toString(),
|
142
|
+
headers,
|
143
|
+
body: options?.formData || options?.body,
|
144
|
+
timestamp: startTime,
|
145
|
+
},
|
146
|
+
{
|
147
|
+
message: error.message,
|
148
|
+
statusCode: response.status,
|
149
|
+
duration,
|
150
|
+
timestamp: Date.now(),
|
151
|
+
}
|
152
|
+
);
|
153
|
+
}
|
154
|
+
|
155
|
+
throw error;
|
156
|
+
}
|
157
|
+
|
158
|
+
// Log successful response
|
159
|
+
if (this.logger) {
|
160
|
+
this.logger.logResponse(
|
161
|
+
{
|
162
|
+
method,
|
163
|
+
url: url.toString(),
|
164
|
+
headers,
|
165
|
+
body: options?.formData || options?.body,
|
166
|
+
timestamp: startTime,
|
167
|
+
},
|
168
|
+
{
|
169
|
+
status: response.status,
|
170
|
+
statusText: response.statusText,
|
171
|
+
data: response.data,
|
172
|
+
duration,
|
173
|
+
timestamp: Date.now(),
|
174
|
+
}
|
175
|
+
);
|
176
|
+
}
|
177
|
+
|
178
|
+
return response.data as T;
|
179
|
+
} catch (error) {
|
180
|
+
const duration = Date.now() - startTime;
|
181
|
+
|
182
|
+
// Re-throw APIError as-is
|
183
|
+
if (error instanceof APIError) {
|
184
|
+
throw error;
|
185
|
+
}
|
186
|
+
|
187
|
+
// Wrap other errors as NetworkError
|
188
|
+
const networkError = error instanceof Error
|
189
|
+
? new NetworkError(error.message, url.toString(), error)
|
190
|
+
: new NetworkError('Unknown error', url.toString());
|
191
|
+
|
192
|
+
// Log network error
|
193
|
+
if (this.logger) {
|
194
|
+
this.logger.logError(
|
195
|
+
{
|
196
|
+
method,
|
197
|
+
url: url.toString(),
|
198
|
+
headers,
|
199
|
+
body: options?.formData || options?.body,
|
200
|
+
timestamp: startTime,
|
201
|
+
},
|
202
|
+
{
|
203
|
+
message: networkError.message,
|
204
|
+
duration,
|
205
|
+
timestamp: Date.now(),
|
206
|
+
}
|
207
|
+
);
|
208
|
+
}
|
209
|
+
|
210
|
+
throw networkError;
|
211
|
+
}
|
212
|
+
}
|
213
|
+
}
|
214
|
+
{% else %}
|
215
|
+
{# Flat client without sub-clients #}
|
216
|
+
import * as Enums from "./enums";
|
217
|
+
import { HttpClientAdapter, FetchAdapter } from "./http";
|
218
|
+
import { APIError, NetworkError } from "./errors";
|
219
|
+
|
220
|
+
|
221
|
+
/**
|
222
|
+
* Async API client for {{ info.title }}.
|
223
|
+
*
|
224
|
+
* Usage:
|
225
|
+
* ```typescript
|
226
|
+
* const client = new APIClient('https://api.example.com');
|
227
|
+
* const users = await client.usersList();
|
228
|
+
*
|
229
|
+
* // Custom HTTP adapter
|
230
|
+
* const client = new APIClient('https://api.example.com', {
|
231
|
+
* httpClient: new AxiosAdapter()
|
232
|
+
* });
|
233
|
+
* ```
|
234
|
+
*/
|
235
|
+
export class APIClient {
|
236
|
+
private baseUrl: string;
|
237
|
+
private httpClient: HttpClientAdapter;
|
238
|
+
|
239
|
+
constructor(
|
240
|
+
baseUrl: string,
|
241
|
+
options?: { httpClient?: HttpClientAdapter }
|
242
|
+
) {
|
243
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
244
|
+
this.httpClient = options?.httpClient || new FetchAdapter();
|
245
|
+
}
|
246
|
+
|
247
|
+
/**
|
248
|
+
* Get CSRF token from cookies.
|
249
|
+
*/
|
250
|
+
private getCsrfToken(): string | null {
|
251
|
+
const name = 'csrftoken';
|
252
|
+
const value = `; ${document.cookie}`;
|
253
|
+
const parts = value.split(`; ${name}=`);
|
254
|
+
if (parts.length === 2) {
|
255
|
+
return parts.pop()?.split(';').shift() || null;
|
256
|
+
}
|
257
|
+
return null;
|
258
|
+
}
|
259
|
+
|
260
|
+
/**
|
261
|
+
* Make HTTP request with Django CSRF and session handling.
|
262
|
+
*/
|
263
|
+
private async request<T>(
|
264
|
+
method: string,
|
265
|
+
path: string,
|
266
|
+
options?: {
|
267
|
+
params?: Record<string, any>;
|
268
|
+
body?: any;
|
269
|
+
formData?: FormData;
|
270
|
+
}
|
271
|
+
): Promise<T> {
|
272
|
+
const url = new URL(path, this.baseUrl);
|
273
|
+
|
274
|
+
// Build headers
|
275
|
+
const headers: Record<string, string> = {};
|
276
|
+
|
277
|
+
// Don't set Content-Type for FormData (browser will set it with boundary)
|
278
|
+
if (!options?.formData) {
|
279
|
+
headers['Content-Type'] = 'application/json';
|
280
|
+
}
|
281
|
+
|
282
|
+
// Add CSRF token for non-GET requests
|
283
|
+
if (method !== 'GET') {
|
284
|
+
const csrfToken = this.getCsrfToken();
|
285
|
+
if (csrfToken) {
|
286
|
+
headers['X-CSRFToken'] = csrfToken;
|
287
|
+
}
|
288
|
+
}
|
289
|
+
|
290
|
+
try {
|
291
|
+
// Make request via HTTP adapter
|
292
|
+
const response = await this.httpClient.request<T>({
|
293
|
+
method,
|
294
|
+
url: url.toString(),
|
295
|
+
headers,
|
296
|
+
params: options?.params,
|
297
|
+
body: options?.body,
|
298
|
+
formData: options?.formData,
|
299
|
+
});
|
300
|
+
|
301
|
+
// Check for HTTP errors
|
302
|
+
if (response.status >= 400) {
|
303
|
+
throw new APIError(
|
304
|
+
response.status,
|
305
|
+
response.statusText,
|
306
|
+
response.data,
|
307
|
+
url.toString()
|
308
|
+
);
|
309
|
+
}
|
310
|
+
|
311
|
+
return response.data as T;
|
312
|
+
} catch (error) {
|
313
|
+
// Re-throw APIError as-is
|
314
|
+
if (error instanceof APIError) {
|
315
|
+
throw error;
|
316
|
+
}
|
317
|
+
|
318
|
+
// Wrap other errors as NetworkError
|
319
|
+
throw error instanceof Error
|
320
|
+
? new NetworkError(error.message, url.toString(), error)
|
321
|
+
: new NetworkError('Unknown error', url.toString());
|
322
|
+
}
|
323
|
+
}
|
324
|
+
|
325
|
+
{# Operations will be added here by generator #}
|
326
|
+
}
|
327
|
+
{% endif %}
|
django_cfg/modules/django_client/core/generator/templates/typescript/client/flat_client.ts.jinja
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
/**
|
2
|
+
* Async API client for {{ api_title }}.
|
3
|
+
*
|
4
|
+
* Usage:
|
5
|
+
* ```typescript
|
6
|
+
* const client = new APIClient('https://api.example.com');
|
7
|
+
* const users = await client.usersList();
|
8
|
+
*
|
9
|
+
* // Custom HTTP adapter
|
10
|
+
* const client = new APIClient('https://api.example.com', {
|
11
|
+
* httpClient: new AxiosAdapter()
|
12
|
+
* });
|
13
|
+
* ```
|
14
|
+
*/
|
15
|
+
export class APIClient {
|
16
|
+
private baseUrl: string;
|
17
|
+
private httpClient: HttpClientAdapter;
|
18
|
+
|
19
|
+
constructor(
|
20
|
+
baseUrl: string,
|
21
|
+
options?: { httpClient?: HttpClientAdapter }
|
22
|
+
) {
|
23
|
+
this.baseUrl = baseUrl.replace(/\/$/, '');
|
24
|
+
this.httpClient = options?.httpClient || new FetchAdapter();
|
25
|
+
}
|
26
|
+
|
27
|
+
/**
|
28
|
+
* Get CSRF token from cookies.
|
29
|
+
*/
|
30
|
+
private getCsrfToken(): string | null {
|
31
|
+
const name = 'csrftoken';
|
32
|
+
const value = `; ${document.cookie}`;
|
33
|
+
const parts = value.split(`; ${name}=`);
|
34
|
+
if (parts.length === 2) {
|
35
|
+
return parts.pop()?.split(';').shift() || null;
|
36
|
+
}
|
37
|
+
return null;
|
38
|
+
}
|
39
|
+
|
40
|
+
/**
|
41
|
+
* Make HTTP request with Django CSRF and session handling.
|
42
|
+
*/
|
43
|
+
private async request<T>(
|
44
|
+
method: string,
|
45
|
+
path: string,
|
46
|
+
options?: {
|
47
|
+
params?: Record<string, any>;
|
48
|
+
body?: any;
|
49
|
+
formData?: FormData;
|
50
|
+
}
|
51
|
+
): Promise<T> {
|
52
|
+
const url = new URL(path, this.baseUrl);
|
53
|
+
|
54
|
+
// Build headers
|
55
|
+
const headers: Record<string, string> = {};
|
56
|
+
|
57
|
+
// Don't set Content-Type for FormData (browser will set it with boundary)
|
58
|
+
if (!options?.formData) {
|
59
|
+
headers['Content-Type'] = 'application/json';
|
60
|
+
}
|
61
|
+
|
62
|
+
// Add CSRF token for non-GET requests
|
63
|
+
if (method !== 'GET') {
|
64
|
+
const csrfToken = this.getCsrfToken();
|
65
|
+
if (csrfToken) {
|
66
|
+
headers['X-CSRFToken'] = csrfToken;
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
try {
|
71
|
+
// Make request via HTTP adapter
|
72
|
+
const response = await this.httpClient.request<T>({
|
73
|
+
method,
|
74
|
+
url: url.toString(),
|
75
|
+
headers,
|
76
|
+
params: options?.params,
|
77
|
+
body: options?.body,
|
78
|
+
formData: options?.formData,
|
79
|
+
});
|
80
|
+
|
81
|
+
// Check for HTTP errors
|
82
|
+
if (response.status >= 400) {
|
83
|
+
throw new APIError(
|
84
|
+
response.status,
|
85
|
+
response.statusText,
|
86
|
+
response.data,
|
87
|
+
url.toString()
|
88
|
+
);
|
89
|
+
}
|
90
|
+
|
91
|
+
return response.data as T;
|
92
|
+
} catch (error) {
|
93
|
+
// Re-throw APIError as-is
|
94
|
+
if (error instanceof APIError) {
|
95
|
+
throw error;
|
96
|
+
}
|
97
|
+
|
98
|
+
// Wrap other errors as NetworkError
|
99
|
+
throw error instanceof Error
|
100
|
+
? new NetworkError(error.message, url.toString(), error)
|
101
|
+
: new NetworkError('Unknown error', url.toString());
|
102
|
+
}
|
103
|
+
}
|
104
|
+
|
105
|
+
{% for operation in operations %}
|
106
|
+
{{ operation }}
|
107
|
+
|
108
|
+
{% endfor %}
|
109
|
+
}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
{% for tag in tags %}
|
2
|
+
import { {{ tag.class_name }} } from "./{{ tag.slug }}";
|
3
|
+
{% endfor %}
|
4
|
+
import { HttpClientAdapter, FetchAdapter } from "./http";
|
5
|
+
import { APIError, NetworkError } from "./errors";
|
6
|
+
import { APILogger, type LoggerConfig } from "./logger";
|
7
|
+
|
8
|
+
|
9
|
+
{{ client_code }}
|
django_cfg/modules/django_client/core/generator/templates/typescript/client/operation.ts.jinja
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
{# Macro for generating TypeScript operation method #}
|
2
|
+
{% macro render_operation(operation, in_subclient=False) %}
|
3
|
+
{%- set request_prefix = "this.client" if in_subclient else "this" -%}
|
4
|
+
|
5
|
+
{%- if operation.summary or operation.description -%}
|
6
|
+
/**
|
7
|
+
{%- if operation.summary %}
|
8
|
+
* {{ operation.summary }}
|
9
|
+
{%- endif %}
|
10
|
+
{%- if operation.description %}
|
11
|
+
*
|
12
|
+
{%- for line in operation.description.split('\n') %}
|
13
|
+
* {{ line }}
|
14
|
+
{%- endfor %}
|
15
|
+
{%- endif %}
|
16
|
+
*/
|
17
|
+
{%- endif %}
|
18
|
+
async {{ operation.method_name }}(
|
19
|
+
{%- for param in operation.params -%}
|
20
|
+
{{ param.name }}{{ '?' if param.optional else '' }}: {{ param.type }}{{ ', ' if not loop.last else '' }}
|
21
|
+
{%- endfor -%}
|
22
|
+
): Promise<{{ operation.return_type }}> {
|
23
|
+
{%- if operation.is_multipart %}
|
24
|
+
const formData = new FormData();
|
25
|
+
{%- for field in operation.multipart_fields %}
|
26
|
+
{%- if field.is_file %}
|
27
|
+
formData.append('{{ field.name }}', {{ field.name }});
|
28
|
+
{%- else %}
|
29
|
+
{%- if field.optional %}
|
30
|
+
if ({{ field.name }} !== undefined) formData.append('{{ field.name }}', String({{ field.name }}));
|
31
|
+
{%- else %}
|
32
|
+
formData.append('{{ field.name }}', String({{ field.name }}));
|
33
|
+
{%- endif %}
|
34
|
+
{%- endif %}
|
35
|
+
{%- endfor %}
|
36
|
+
{%- endif %}
|
37
|
+
const response = await {{ request_prefix }}.request<{{ operation.return_type }}>(
|
38
|
+
'{{ operation.http_method }}',
|
39
|
+
{{ operation.path_expr }}
|
40
|
+
{%- if operation.has_options -%}
|
41
|
+
, {
|
42
|
+
{%- if operation.query_params %}
|
43
|
+
params: { {{ operation.query_params }} }{{ ',' if operation.has_body or operation.is_multipart else '' }}
|
44
|
+
{%- endif %}
|
45
|
+
{%- if operation.is_multipart %}
|
46
|
+
formData
|
47
|
+
{%- elif operation.has_body %}
|
48
|
+
body: data
|
49
|
+
{%- endif %}
|
50
|
+
}
|
51
|
+
{%- endif -%}
|
52
|
+
);
|
53
|
+
{%- if operation.is_list %}
|
54
|
+
return (response as any).results || [];
|
55
|
+
{%- elif operation.return_type != 'void' %}
|
56
|
+
return response;
|
57
|
+
{%- else %}
|
58
|
+
return;
|
59
|
+
{%- endif %}
|
60
|
+
}
|
61
|
+
{% endmacro %}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
{% if has_enums %}
|
2
|
+
import * as Enums from "./enums";
|
3
|
+
{% endif %}
|
4
|
+
import { HttpClientAdapter, FetchAdapter } from "./http";
|
5
|
+
import { APIError, NetworkError } from "./errors";
|
6
|
+
import { APILogger, type LoggerConfig } from "./logger";
|
7
|
+
|
8
|
+
|
9
|
+
{{ client_code }}
|