django-cfg 1.4.15__py3-none-any.whl → 1.4.19__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/leads/urls.py +2 -1
- django_cfg/apps/payments/urls.py +4 -4
- django_cfg/core/base/config_model.py +7 -7
- django_cfg/core/generation/core_generators/settings.py +2 -3
- django_cfg/core/generation/core_generators/static.py +9 -9
- django_cfg/core/generation/integration_generators/api.py +1 -1
- django_cfg/models/infrastructure/security.py +33 -2
- django_cfg/modules/django_client/core/generator/base.py +18 -20
- django_cfg/modules/django_client/core/generator/typescript/fetchers_generator.py +56 -113
- django_cfg/modules/django_client/core/generator/typescript/generator.py +1 -1
- django_cfg/modules/django_client/core/generator/typescript/hooks_generator.py +102 -232
- django_cfg/modules/django_client/core/generator/typescript/models_generator.py +9 -4
- django_cfg/modules/django_client/core/generator/typescript/naming.py +83 -0
- django_cfg/modules/django_client/core/generator/typescript/operations_generator.py +19 -7
- django_cfg/modules/django_client/core/generator/typescript/schemas_generator.py +40 -33
- django_cfg/modules/django_client/core/generator/typescript/templates/fetchers/function.ts.jinja +25 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/hooks.ts.jinja +31 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/index.ts.jinja +29 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/mutation_hook.ts.jinja +25 -0
- django_cfg/modules/django_client/core/generator/typescript/templates/hooks/query_hook.ts.jinja +20 -0
- django_cfg/modules/django_client/core/groups/manager.py +51 -26
- django_cfg/modules/django_client/spectacular/__init__.py +3 -2
- django_cfg/modules/django_client/spectacular/schema.py +50 -0
- django_cfg/pyproject.toml +1 -1
- {django_cfg-1.4.15.dist-info → django_cfg-1.4.19.dist-info}/METADATA +1 -1
- {django_cfg-1.4.15.dist-info → django_cfg-1.4.19.dist-info}/RECORD +29 -22
- {django_cfg-1.4.15.dist-info → django_cfg-1.4.19.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.15.dist-info → django_cfg-1.4.19.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.15.dist-info → django_cfg-1.4.19.dist-info}/licenses/LICENSE +0 -0
@@ -19,7 +19,7 @@ from __future__ import annotations
|
|
19
19
|
from jinja2 import Environment
|
20
20
|
from ..base import BaseGenerator, GeneratedFile
|
21
21
|
from ...ir import IROperationObject, IRContext
|
22
|
-
|
22
|
+
from .naming import operation_to_method_name
|
23
23
|
|
24
24
|
class HooksGenerator:
|
25
25
|
"""
|
@@ -39,7 +39,7 @@ class HooksGenerator:
|
|
39
39
|
|
40
40
|
def generate_query_hook(self, operation: IROperationObject) -> str:
|
41
41
|
"""
|
42
|
-
Generate useSWR hook for GET operation.
|
42
|
+
Generate useSWR hook for GET operation using Jinja2 template.
|
43
43
|
|
44
44
|
Examples:
|
45
45
|
>>> generate_query_hook(users_list)
|
@@ -65,44 +65,21 @@ class HooksGenerator:
|
|
65
65
|
# Get SWR key
|
66
66
|
swr_key = self._generate_swr_key(operation)
|
67
67
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
# Hook signature
|
81
|
-
if param_info['func_params']:
|
82
|
-
lines.append(f"export function {hook_name}({param_info['func_params']}) {{")
|
83
|
-
else:
|
84
|
-
lines.append(f"export function {hook_name}() {{")
|
85
|
-
|
86
|
-
# useSWR call
|
87
|
-
fetcher_params = param_info['fetcher_params']
|
88
|
-
if fetcher_params:
|
89
|
-
lines.append(f" return useSWR<{response_type}>(")
|
90
|
-
lines.append(f" {swr_key},")
|
91
|
-
lines.append(f" () => Fetchers.{fetcher_name}({fetcher_params})")
|
92
|
-
lines.append(" )")
|
93
|
-
else:
|
94
|
-
lines.append(f" return useSWR<{response_type}>(")
|
95
|
-
lines.append(f" {swr_key},")
|
96
|
-
lines.append(f" () => Fetchers.{fetcher_name}()")
|
97
|
-
lines.append(" )")
|
98
|
-
|
99
|
-
lines.append("}")
|
100
|
-
|
101
|
-
return "\n".join(lines)
|
68
|
+
# Render template
|
69
|
+
template = self.jinja_env.get_template('hooks/query_hook.ts.jinja')
|
70
|
+
return template.render(
|
71
|
+
operation=operation,
|
72
|
+
hook_name=hook_name,
|
73
|
+
fetcher_name=fetcher_name,
|
74
|
+
func_params=param_info['func_params'],
|
75
|
+
fetcher_params=param_info['fetcher_params'],
|
76
|
+
response_type=response_type,
|
77
|
+
swr_key=swr_key
|
78
|
+
)
|
102
79
|
|
103
80
|
def generate_mutation_hook(self, operation: IROperationObject) -> str:
|
104
81
|
"""
|
105
|
-
Generate mutation hook for POST/PUT/PATCH/DELETE.
|
82
|
+
Generate mutation hook for POST/PUT/PATCH/DELETE using Jinja2 template.
|
106
83
|
|
107
84
|
Examples:
|
108
85
|
>>> generate_mutation_hook(users_create)
|
@@ -131,114 +108,84 @@ class HooksGenerator:
|
|
131
108
|
# Get revalidation keys
|
132
109
|
revalidation_keys = self._get_revalidation_keys(operation)
|
133
110
|
|
134
|
-
#
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
# Hook signature
|
147
|
-
lines.append(f"export function {hook_name}() {{")
|
148
|
-
lines.append(" const { mutate } = useSWRConfig()")
|
149
|
-
lines.append("")
|
150
|
-
|
151
|
-
# Return async function
|
152
|
-
if param_info['func_params']:
|
153
|
-
lines.append(f" return async ({param_info['func_params']}): Promise<{response_type}> => {{")
|
154
|
-
else:
|
155
|
-
lines.append(f" return async (): Promise<{response_type}> => {{")
|
156
|
-
|
157
|
-
# Call fetcher
|
158
|
-
fetcher_params = param_info['fetcher_params']
|
159
|
-
if fetcher_params:
|
160
|
-
lines.append(f" const result = await Fetchers.{fetcher_name}({fetcher_params})")
|
161
|
-
else:
|
162
|
-
lines.append(f" const result = await Fetchers.{fetcher_name}()")
|
163
|
-
|
164
|
-
# Revalidate
|
165
|
-
if revalidation_keys:
|
166
|
-
lines.append("")
|
167
|
-
lines.append(" // Revalidate related queries")
|
168
|
-
for key in revalidation_keys:
|
169
|
-
lines.append(f" mutate('{key}')")
|
170
|
-
|
171
|
-
lines.append("")
|
172
|
-
lines.append(" return result")
|
173
|
-
lines.append(" }")
|
174
|
-
lines.append("}")
|
175
|
-
|
176
|
-
return "\n".join(lines)
|
111
|
+
# Render template
|
112
|
+
template = self.jinja_env.get_template('hooks/mutation_hook.ts.jinja')
|
113
|
+
return template.render(
|
114
|
+
operation=operation,
|
115
|
+
hook_name=hook_name,
|
116
|
+
fetcher_name=fetcher_name,
|
117
|
+
func_params=param_info['func_params'],
|
118
|
+
fetcher_params=param_info['fetcher_params'],
|
119
|
+
response_type=response_type,
|
120
|
+
revalidation_keys=revalidation_keys
|
121
|
+
)
|
177
122
|
|
178
123
|
def _operation_to_hook_name(self, operation: IROperationObject) -> str:
|
179
124
|
"""
|
180
125
|
Convert operation to hook name.
|
181
|
-
|
126
|
+
|
127
|
+
Hooks are organized into tag-specific files but also exported globally,
|
128
|
+
so we include the tag in the name to avoid collisions.
|
129
|
+
|
182
130
|
Examples:
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
users_update (PUT) -> useUpdateUsers
|
187
|
-
users_partial_update (PATCH) -> usePartialUpdateUsers
|
188
|
-
users_destroy (DELETE) -> useDeleteUsers
|
131
|
+
cfg_support_tickets_list -> useSupportTicketsList
|
132
|
+
cfg_health_drf_retrieve -> useHealthDrf
|
133
|
+
cfg_accounts_otp_request_create -> useCreateAccountsOtpRequest
|
189
134
|
"""
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
if
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
elif
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
resource = op_id.removesuffix("_destroy")
|
211
|
-
return f"useDelete{self._to_pascal_case(resource)}"
|
135
|
+
|
136
|
+
# Remove cfg_ prefix but keep tag + resource for uniqueness (same as fetchers)
|
137
|
+
operation_id = operation.operation_id
|
138
|
+
if operation_id.startswith('django_cfg_'):
|
139
|
+
operation_id = operation_id.replace('django_cfg_', '', 1)
|
140
|
+
elif operation_id.startswith('cfg_'):
|
141
|
+
operation_id = operation_id.replace('cfg_', '', 1)
|
142
|
+
|
143
|
+
# Determine prefix based on HTTP method
|
144
|
+
if operation.http_method == 'GET':
|
145
|
+
prefix = 'use'
|
146
|
+
elif operation.http_method == 'POST':
|
147
|
+
prefix = 'useCreate'
|
148
|
+
elif operation.http_method in ('PUT', 'PATCH'):
|
149
|
+
if '_partial_update' in operation_id:
|
150
|
+
prefix = 'usePartialUpdate'
|
151
|
+
else:
|
152
|
+
prefix = 'useUpdate'
|
153
|
+
elif operation.http_method == 'DELETE':
|
154
|
+
prefix = 'useDelete'
|
212
155
|
else:
|
213
|
-
|
214
|
-
|
156
|
+
prefix = 'use'
|
157
|
+
|
158
|
+
# For hooks, path is not critical but pass for consistency
|
159
|
+
return operation_to_method_name(operation_id, operation.http_method, prefix, self.base, operation.path)
|
215
160
|
|
216
161
|
def _operation_to_fetcher_name(self, operation: IROperationObject) -> str:
|
217
162
|
"""Get corresponding fetcher function name (must match fetchers_generator logic)."""
|
218
|
-
|
219
|
-
|
220
|
-
#
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
elif
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
elif
|
238
|
-
|
239
|
-
return f"delete{self._to_pascal_case(resource)}"
|
163
|
+
|
164
|
+
|
165
|
+
# Remove cfg_ prefix but keep tag + resource (must match fetchers_generator exactly)
|
166
|
+
operation_id = operation.operation_id
|
167
|
+
if operation_id.startswith('django_cfg_'):
|
168
|
+
operation_id = operation_id.replace('django_cfg_', '', 1)
|
169
|
+
elif operation_id.startswith('cfg_'):
|
170
|
+
operation_id = operation_id.replace('cfg_', '', 1)
|
171
|
+
|
172
|
+
# Determine prefix (must match fetchers_generator exactly)
|
173
|
+
if operation.http_method == 'GET':
|
174
|
+
prefix = 'get'
|
175
|
+
elif operation.http_method == 'POST':
|
176
|
+
prefix = 'create'
|
177
|
+
elif operation.http_method in ('PUT', 'PATCH'):
|
178
|
+
if '_partial_update' in operation_id:
|
179
|
+
prefix = 'partialUpdate'
|
180
|
+
else:
|
181
|
+
prefix = 'update'
|
182
|
+
elif operation.http_method == 'DELETE':
|
183
|
+
prefix = 'delete'
|
240
184
|
else:
|
241
|
-
|
185
|
+
prefix = ''
|
186
|
+
|
187
|
+
# Must match fetchers exactly, including path
|
188
|
+
return operation_to_method_name(operation_id, operation.http_method, prefix, self.base, operation.path)
|
242
189
|
|
243
190
|
def _get_param_info(self, operation: IROperationObject) -> dict:
|
244
191
|
"""
|
@@ -385,22 +332,13 @@ class HooksGenerator:
|
|
385
332
|
|
386
333
|
return keys
|
387
334
|
|
388
|
-
def _to_pascal_case(self, snake_str: str) -> str:
|
389
|
-
"""Convert snake_case to PascalCase."""
|
390
|
-
return ''.join(word.capitalize() for word in snake_str.split('_'))
|
391
|
-
|
392
|
-
def _to_camel_case(self, snake_str: str) -> str:
|
393
|
-
"""Convert snake_case to camelCase."""
|
394
|
-
components = snake_str.split('_')
|
395
|
-
return components[0] + ''.join(x.capitalize() for x in components[1:])
|
396
|
-
|
397
335
|
def generate_tag_hooks_file(
|
398
336
|
self,
|
399
337
|
tag: str,
|
400
338
|
operations: list[IROperationObject],
|
401
339
|
) -> GeneratedFile:
|
402
340
|
"""
|
403
|
-
Generate hooks file for a specific tag/resource.
|
341
|
+
Generate hooks file for a specific tag/resource using Jinja2 template.
|
404
342
|
|
405
343
|
Args:
|
406
344
|
tag: Tag name (e.g., "shop_products")
|
@@ -410,8 +348,7 @@ class HooksGenerator:
|
|
410
348
|
GeneratedFile with hooks
|
411
349
|
"""
|
412
350
|
# Separate queries and mutations & collect schema names
|
413
|
-
|
414
|
-
mutation_hooks = []
|
351
|
+
hooks = []
|
415
352
|
schema_names = set()
|
416
353
|
|
417
354
|
for operation in operations:
|
@@ -430,69 +367,28 @@ class HooksGenerator:
|
|
430
367
|
|
431
368
|
# Generate hook
|
432
369
|
if operation.http_method == "GET":
|
433
|
-
|
370
|
+
hooks.append(self.generate_query_hook(operation))
|
434
371
|
else:
|
435
|
-
|
372
|
+
hooks.append(self.generate_mutation_hook(operation))
|
436
373
|
|
437
374
|
# Get display name for documentation
|
438
375
|
tag_display_name = self.base.tag_to_display_name(tag)
|
439
|
-
|
440
|
-
#
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
lines.append(" * configureAPI({ baseUrl: 'https://api.example.com' })")
|
454
|
-
lines.append(" * ```")
|
455
|
-
lines.append(" *")
|
456
|
-
lines.append(" * Usage:")
|
457
|
-
lines.append(" * ```typescript")
|
458
|
-
lines.append(" * // Query hook")
|
459
|
-
lines.append(" * const { data, error, mutate } = useShopProducts({ page: 1 })")
|
460
|
-
lines.append(" *")
|
461
|
-
lines.append(" * // Mutation hook")
|
462
|
-
lines.append(" * const createProduct = useCreateShopProduct()")
|
463
|
-
lines.append(" * await createProduct({ name: 'Product', price: 99 })")
|
464
|
-
lines.append(" * ```")
|
465
|
-
lines.append(" */")
|
466
|
-
|
467
|
-
# Import types from schemas
|
468
|
-
for schema_name in sorted(schema_names):
|
469
|
-
lines.append(f"import type {{ {schema_name} }} from '../schemas/{schema_name}.schema'")
|
470
|
-
|
471
|
-
lines.append("import useSWR from 'swr'")
|
472
|
-
lines.append("import { useSWRConfig } from 'swr'")
|
473
|
-
lines.append("import * as Fetchers from '../fetchers'")
|
474
|
-
lines.append("")
|
475
|
-
|
476
|
-
# Query hooks
|
477
|
-
if query_hooks:
|
478
|
-
lines.append("// ===== Query Hooks (GET) =====")
|
479
|
-
lines.append("")
|
480
|
-
for hook in query_hooks:
|
481
|
-
lines.append(hook)
|
482
|
-
lines.append("")
|
483
|
-
|
484
|
-
# Mutation hooks
|
485
|
-
if mutation_hooks:
|
486
|
-
lines.append("// ===== Mutation Hooks (POST/PUT/PATCH/DELETE) =====")
|
487
|
-
lines.append("")
|
488
|
-
for hook in mutation_hooks:
|
489
|
-
lines.append(hook)
|
490
|
-
lines.append("")
|
491
|
-
|
492
|
-
content = "\n".join(lines)
|
376
|
+
|
377
|
+
# Get tag file name for fetchers import
|
378
|
+
folder_name = self.base.tag_and_app_to_folder_name(tag, operations)
|
379
|
+
tag_file = folder_name
|
380
|
+
|
381
|
+
# Render template
|
382
|
+
template = self.jinja_env.get_template('hooks/hooks.ts.jinja')
|
383
|
+
content = template.render(
|
384
|
+
tag_display_name=tag_display_name,
|
385
|
+
tag_file=tag_file,
|
386
|
+
has_schemas=bool(schema_names),
|
387
|
+
schema_names=sorted(schema_names),
|
388
|
+
hooks=hooks
|
389
|
+
)
|
493
390
|
|
494
391
|
# Get file path (use same naming as APIClient)
|
495
|
-
folder_name = self.base.tag_and_app_to_folder_name(tag, operations)
|
496
392
|
file_path = f"_utils/hooks/{folder_name}.ts"
|
497
393
|
|
498
394
|
return GeneratedFile(
|
@@ -502,35 +398,9 @@ class HooksGenerator:
|
|
502
398
|
)
|
503
399
|
|
504
400
|
def generate_hooks_index_file(self, module_names: list[str]) -> GeneratedFile:
|
505
|
-
"""Generate index.ts for hooks folder."""
|
506
|
-
|
507
|
-
|
508
|
-
lines.append("/**")
|
509
|
-
lines.append(" * SWR Hooks - React hooks for data fetching")
|
510
|
-
lines.append(" *")
|
511
|
-
lines.append(" * Auto-generated from OpenAPI specification.")
|
512
|
-
lines.append(" * These hooks use SWR for data fetching and caching.")
|
513
|
-
lines.append(" *")
|
514
|
-
lines.append(" * Usage:")
|
515
|
-
lines.append(" * ```typescript")
|
516
|
-
lines.append(" * import { useShopProducts } from './_utils/hooks'")
|
517
|
-
lines.append(" *")
|
518
|
-
lines.append(" * function ProductsPage() {")
|
519
|
-
lines.append(" * const { data, error } = useShopProducts({ page: 1 })")
|
520
|
-
lines.append(" * if (error) return <Error />")
|
521
|
-
lines.append(" * if (!data) return <Loading />")
|
522
|
-
lines.append(" * return <ProductList products={data.results} />")
|
523
|
-
lines.append(" * }")
|
524
|
-
lines.append(" * ```")
|
525
|
-
lines.append(" */")
|
526
|
-
lines.append("")
|
527
|
-
|
528
|
-
for module_name in module_names:
|
529
|
-
lines.append(f"export * from './{module_name}'")
|
530
|
-
|
531
|
-
lines.append("")
|
532
|
-
|
533
|
-
content = "\n".join(lines)
|
401
|
+
"""Generate index.ts for hooks folder using Jinja2 template."""
|
402
|
+
template = self.jinja_env.get_template('hooks/index.ts.jinja')
|
403
|
+
content = template.render(modules=module_names)
|
534
404
|
|
535
405
|
return GeneratedFile(
|
536
406
|
path="_utils/hooks/index.ts",
|
@@ -162,17 +162,22 @@ class ModelsGenerator:
|
|
162
162
|
if ts_type.endswith(" | null"):
|
163
163
|
ts_type = ts_type[:-7] # Remove " | null"
|
164
164
|
else:
|
165
|
-
# Get TypeScript type
|
165
|
+
# Get TypeScript type
|
166
166
|
ts_type = schema.typescript_type
|
167
|
+
# Remove | null suffix to rebuild it properly based on schema.nullable
|
167
168
|
if ts_type.endswith(" | null"):
|
168
169
|
ts_type = ts_type[:-7] # Remove " | null"
|
169
170
|
|
170
171
|
# Check if required
|
171
172
|
is_required = name in required_fields
|
172
173
|
|
173
|
-
#
|
174
|
-
#
|
175
|
-
|
174
|
+
# Handle nullable and optional separately
|
175
|
+
# - nullable: add | null to type
|
176
|
+
# - not required: add ? optional marker
|
177
|
+
if schema.nullable:
|
178
|
+
ts_type = f"{ts_type} | null"
|
179
|
+
|
180
|
+
optional_marker = "" if is_required else "?"
|
176
181
|
|
177
182
|
# Comment
|
178
183
|
if schema.description:
|
@@ -0,0 +1,83 @@
|
|
1
|
+
"""
|
2
|
+
Simple naming strategy for TypeScript code generation.
|
3
|
+
|
4
|
+
Strategy: Use full operation_id, remove tag prefix, convert to camelCase/PascalCase.
|
5
|
+
"""
|
6
|
+
|
7
|
+
import re
|
8
|
+
|
9
|
+
|
10
|
+
def to_camel_case(s: str) -> str:
|
11
|
+
"""Convert snake_case or kebab-case to camelCase."""
|
12
|
+
s = s.replace('-', '_')
|
13
|
+
parts = s.split('_')
|
14
|
+
if not parts:
|
15
|
+
return ''
|
16
|
+
return parts[0].lower() + ''.join(p.capitalize() for p in parts[1:])
|
17
|
+
|
18
|
+
|
19
|
+
def to_pascal_case(s: str) -> str:
|
20
|
+
"""Convert snake_case or kebab-case to PascalCase."""
|
21
|
+
s = s.replace('-', '_')
|
22
|
+
return ''.join(p.capitalize() for p in s.split('_'))
|
23
|
+
|
24
|
+
|
25
|
+
def remove_tag_prefix(operation_id: str) -> str:
|
26
|
+
"""
|
27
|
+
Remove common tag prefixes from operation_id.
|
28
|
+
|
29
|
+
Examples:
|
30
|
+
cfg_newsletter_campaigns_list -> newsletter_campaigns_list
|
31
|
+
django_cfg_accounts_login -> accounts_login
|
32
|
+
newsletter_campaigns_send_create -> newsletter_campaigns_send_create
|
33
|
+
"""
|
34
|
+
# Remove cfg_ or django_cfg_ prefix
|
35
|
+
if operation_id.startswith('django_cfg_'):
|
36
|
+
return operation_id[11:] # len('django_cfg_')
|
37
|
+
elif operation_id.startswith('cfg_'):
|
38
|
+
return operation_id[4:] # len('cfg_')
|
39
|
+
return operation_id
|
40
|
+
|
41
|
+
|
42
|
+
def operation_to_method_name(
|
43
|
+
operation_id: str,
|
44
|
+
http_method: str,
|
45
|
+
prefix: str,
|
46
|
+
base_generator,
|
47
|
+
path: str = ''
|
48
|
+
) -> str:
|
49
|
+
"""
|
50
|
+
Simple naming: remove tag prefix, convert to camelCase/PascalCase.
|
51
|
+
|
52
|
+
Args:
|
53
|
+
operation_id: Full operation ID from OpenAPI
|
54
|
+
http_method: HTTP method (GET, POST, PUT, PATCH, DELETE)
|
55
|
+
prefix: Function prefix ('', 'get', 'create', 'use', etc.)
|
56
|
+
base_generator: Base generator instance (unused)
|
57
|
+
path: URL path (unused)
|
58
|
+
|
59
|
+
Returns:
|
60
|
+
Method name in camelCase (for client) or PascalCase (for fetchers/hooks)
|
61
|
+
|
62
|
+
Examples:
|
63
|
+
# Client methods (prefix='')
|
64
|
+
cfg_newsletter_campaigns_list -> newsletterCampaignsList
|
65
|
+
cfg_newsletter_campaigns_send_create -> newsletterCampaignsSendCreate
|
66
|
+
cfg_accounts_otp_request_create -> accountsOtpRequestCreate
|
67
|
+
|
68
|
+
# Fetchers (prefix='get')
|
69
|
+
cfg_newsletter_campaigns_list -> getNewsletterCampaignsList
|
70
|
+
|
71
|
+
# Hooks (prefix='use')
|
72
|
+
cfg_newsletter_campaigns_list -> useNewsletterCampaignsList
|
73
|
+
"""
|
74
|
+
# Remove tag prefix
|
75
|
+
clean_id = remove_tag_prefix(operation_id)
|
76
|
+
|
77
|
+
# For client methods (no prefix): camelCase
|
78
|
+
if not prefix:
|
79
|
+
return to_camel_case(clean_id)
|
80
|
+
|
81
|
+
# For fetchers/hooks (with prefix): prefix + PascalCase
|
82
|
+
return f"{prefix}{to_pascal_case(clean_id)}"
|
83
|
+
|
@@ -6,7 +6,7 @@ from __future__ import annotations
|
|
6
6
|
|
7
7
|
from jinja2 import Environment
|
8
8
|
from ...ir import IROperationObject
|
9
|
-
|
9
|
+
from .naming import operation_to_method_name
|
10
10
|
|
11
11
|
class OperationsGenerator:
|
12
12
|
"""Generates TypeScript async operation methods."""
|
@@ -18,15 +18,19 @@ class OperationsGenerator:
|
|
18
18
|
|
19
19
|
def generate_operation(self, operation: IROperationObject, remove_tag_prefix: bool = False, in_subclient: bool = False) -> str:
|
20
20
|
"""Generate async method for operation."""
|
21
|
-
|
21
|
+
|
22
|
+
|
23
|
+
# Get method name using universal logic
|
24
|
+
# For client methods, we use empty prefix to get short names: list, create, retrieve
|
22
25
|
operation_id = operation.operation_id
|
23
26
|
if remove_tag_prefix and operation.tags:
|
24
27
|
# Remove tag prefix using base class method
|
25
28
|
tag = operation.tags[0]
|
26
29
|
operation_id = self.base.remove_tag_prefix(operation_id, tag)
|
27
30
|
|
28
|
-
#
|
29
|
-
|
31
|
+
# Use universal naming function with empty prefix for client methods
|
32
|
+
# Pass path to distinguish custom actions
|
33
|
+
method_name = operation_to_method_name(operation_id, operation.http_method, '', self.base, operation.path)
|
30
34
|
|
31
35
|
# Request method prefix
|
32
36
|
request_prefix = "this.client" if in_subclient else "this"
|
@@ -94,7 +98,9 @@ class OperationsGenerator:
|
|
94
98
|
# Return type
|
95
99
|
primary_response = operation.primary_success_response
|
96
100
|
if primary_response and primary_response.schema_name:
|
97
|
-
if
|
101
|
+
# Check if response is paginated (has 'results' field)
|
102
|
+
is_paginated = primary_response.schema_name.startswith('Paginated')
|
103
|
+
if operation.is_list_operation and not is_paginated:
|
98
104
|
return_type = f"Models.{primary_response.schema_name}[]"
|
99
105
|
else:
|
100
106
|
return_type = f"Models.{primary_response.schema_name}"
|
@@ -238,8 +244,14 @@ class OperationsGenerator:
|
|
238
244
|
|
239
245
|
# Handle response
|
240
246
|
if operation.is_list_operation and primary_response:
|
241
|
-
#
|
242
|
-
|
247
|
+
# Check if response is paginated
|
248
|
+
is_paginated = primary_response.schema_name.startswith('Paginated')
|
249
|
+
if is_paginated:
|
250
|
+
# Return full paginated response object
|
251
|
+
body_lines.append("return response;")
|
252
|
+
else:
|
253
|
+
# Extract results from array response
|
254
|
+
body_lines.append("return (response as any).results || [];")
|
243
255
|
elif return_type != "void":
|
244
256
|
body_lines.append("return response;")
|
245
257
|
else:
|