django-cfg 1.4.62__py3-none-any.whl → 1.4.64__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.
Potentially problematic release.
This version of django-cfg might be problematic. Click here for more details.
- django_cfg/__init__.py +1 -1
- django_cfg/apps/accounts/services/otp_service.py +3 -14
- django_cfg/apps/centrifugo/__init__.py +57 -0
- django_cfg/apps/centrifugo/admin/__init__.py +13 -0
- django_cfg/apps/centrifugo/admin/centrifugo_log.py +249 -0
- django_cfg/apps/centrifugo/admin/config.py +82 -0
- django_cfg/apps/centrifugo/apps.py +31 -0
- django_cfg/apps/centrifugo/codegen/IMPLEMENTATION_SUMMARY.md +475 -0
- django_cfg/apps/centrifugo/codegen/README.md +242 -0
- django_cfg/apps/centrifugo/codegen/USAGE.md +616 -0
- django_cfg/apps/centrifugo/codegen/__init__.py +19 -0
- django_cfg/apps/centrifugo/codegen/discovery.py +246 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/__init__.py +5 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/generator.py +174 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/README.md.j2 +182 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/client.go.j2 +64 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/go.mod.j2 +10 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2 +300 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2.old +267 -0
- django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/types.go.j2 +16 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/__init__.py +7 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/generator.py +241 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/README.md.j2 +128 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/__init__.py.j2 +22 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/client.py.j2 +73 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/models.py.j2 +19 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/requirements.txt.j2 +8 -0
- django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/rpc_client.py.j2 +193 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/__init__.py +5 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/generator.py +124 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/README.md.j2 +38 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/client.ts.j2 +25 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/index.ts.j2 +12 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/package.json.j2 +13 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +137 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/tsconfig.json.j2 +14 -0
- django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/types.ts.j2 +9 -0
- django_cfg/apps/centrifugo/codegen/utils/__init__.py +37 -0
- django_cfg/apps/centrifugo/codegen/utils/naming.py +155 -0
- django_cfg/apps/centrifugo/codegen/utils/type_converter.py +349 -0
- django_cfg/apps/centrifugo/decorators.py +137 -0
- django_cfg/apps/centrifugo/management/__init__.py +1 -0
- django_cfg/apps/centrifugo/management/commands/__init__.py +1 -0
- django_cfg/apps/centrifugo/management/commands/generate_centrifugo_clients.py +254 -0
- django_cfg/apps/centrifugo/managers/__init__.py +12 -0
- django_cfg/apps/centrifugo/managers/centrifugo_log.py +264 -0
- django_cfg/apps/centrifugo/migrations/0001_initial.py +164 -0
- django_cfg/apps/centrifugo/migrations/__init__.py +3 -0
- django_cfg/apps/centrifugo/models/__init__.py +11 -0
- django_cfg/apps/centrifugo/models/centrifugo_log.py +210 -0
- django_cfg/apps/centrifugo/registry.py +106 -0
- django_cfg/apps/centrifugo/router.py +125 -0
- django_cfg/apps/centrifugo/serializers/__init__.py +40 -0
- django_cfg/apps/centrifugo/serializers/admin_api.py +264 -0
- django_cfg/apps/centrifugo/serializers/channels.py +26 -0
- django_cfg/apps/centrifugo/serializers/health.py +17 -0
- django_cfg/apps/centrifugo/serializers/publishes.py +16 -0
- django_cfg/apps/centrifugo/serializers/stats.py +21 -0
- django_cfg/apps/centrifugo/services/__init__.py +12 -0
- django_cfg/apps/centrifugo/services/client/__init__.py +29 -0
- django_cfg/apps/centrifugo/services/client/client.py +582 -0
- django_cfg/apps/centrifugo/services/client/config.py +236 -0
- django_cfg/apps/centrifugo/services/client/exceptions.py +212 -0
- django_cfg/apps/centrifugo/services/config_helper.py +63 -0
- django_cfg/apps/centrifugo/services/dashboard_notifier.py +157 -0
- django_cfg/apps/centrifugo/services/logging.py +677 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +260 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +313 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +803 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +333 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +432 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +33 -0
- django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +210 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +46 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +123 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +45 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +84 -0
- django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/stat_cards.html +23 -20
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +91 -0
- django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/tab_navigation.html +15 -15
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +415 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +61 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +58 -0
- django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +48 -0
- django_cfg/apps/centrifugo/templatetags/__init__.py +1 -0
- django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +81 -0
- django_cfg/apps/centrifugo/urls.py +31 -0
- django_cfg/apps/{ipc → centrifugo}/urls_admin.py +4 -4
- django_cfg/apps/centrifugo/views/__init__.py +15 -0
- django_cfg/apps/centrifugo/views/admin_api.py +380 -0
- django_cfg/apps/centrifugo/views/dashboard.py +15 -0
- django_cfg/apps/centrifugo/views/monitoring.py +286 -0
- django_cfg/apps/centrifugo/views/testing_api.py +422 -0
- django_cfg/apps/support/utils/support_email_service.py +5 -18
- django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -2
- django_cfg/apps/urls.py +5 -5
- django_cfg/core/base/config_model.py +4 -44
- django_cfg/core/builders/apps_builder.py +2 -2
- django_cfg/core/generation/integration_generators/third_party.py +8 -8
- django_cfg/core/utils/__init__.py +5 -0
- django_cfg/core/utils/url_helpers.py +73 -0
- django_cfg/modules/base.py +7 -7
- django_cfg/modules/django_client/core/__init__.py +2 -1
- django_cfg/modules/django_client/core/config/config.py +8 -0
- django_cfg/modules/django_client/core/generator/__init__.py +42 -2
- django_cfg/modules/django_client/core/generator/go/__init__.py +14 -0
- django_cfg/modules/django_client/core/generator/go/client_generator.py +124 -0
- django_cfg/modules/django_client/core/generator/go/files_generator.py +133 -0
- django_cfg/modules/django_client/core/generator/go/generator.py +203 -0
- django_cfg/modules/django_client/core/generator/go/models_generator.py +304 -0
- django_cfg/modules/django_client/core/generator/go/naming.py +193 -0
- django_cfg/modules/django_client/core/generator/go/operations_generator.py +134 -0
- django_cfg/modules/django_client/core/generator/go/templates/Makefile.j2 +38 -0
- django_cfg/modules/django_client/core/generator/go/templates/README.md.j2 +55 -0
- django_cfg/modules/django_client/core/generator/go/templates/client.go.j2 +122 -0
- django_cfg/modules/django_client/core/generator/go/templates/enums.go.j2 +49 -0
- django_cfg/modules/django_client/core/generator/go/templates/errors.go.j2 +182 -0
- django_cfg/modules/django_client/core/generator/go/templates/go.mod.j2 +6 -0
- django_cfg/modules/django_client/core/generator/go/templates/main_client.go.j2 +60 -0
- django_cfg/modules/django_client/core/generator/go/templates/middleware.go.j2 +388 -0
- django_cfg/modules/django_client/core/generator/go/templates/models.go.j2 +28 -0
- django_cfg/modules/django_client/core/generator/go/templates/operations_client.go.j2 +142 -0
- django_cfg/modules/django_client/core/generator/go/templates/validation.go.j2 +217 -0
- django_cfg/modules/django_client/core/generator/go/type_mapper.py +380 -0
- django_cfg/modules/django_client/management/commands/generate_client.py +53 -3
- django_cfg/modules/django_client/system/generate_mjs_clients.py +3 -1
- django_cfg/modules/django_client/system/schema_parser.py +5 -1
- django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +1 -0
- django_cfg/modules/django_twilio/sendgrid_service.py +7 -4
- django_cfg/modules/django_unfold/dashboard.py +25 -19
- django_cfg/pyproject.toml +1 -1
- django_cfg/registry/core.py +2 -0
- django_cfg/registry/modules.py +2 -2
- django_cfg/static/js/api/centrifugo/client.mjs +164 -0
- django_cfg/static/js/api/centrifugo/index.mjs +13 -0
- django_cfg/static/js/api/index.mjs +5 -5
- django_cfg/static/js/api/types.mjs +89 -26
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/METADATA +1 -1
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/RECORD +142 -70
- django_cfg/apps/ipc/README.md +0 -346
- django_cfg/apps/ipc/RPC_LOGGING.md +0 -321
- django_cfg/apps/ipc/TESTING.md +0 -539
- django_cfg/apps/ipc/__init__.py +0 -60
- django_cfg/apps/ipc/admin.py +0 -232
- django_cfg/apps/ipc/apps.py +0 -98
- django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
- django_cfg/apps/ipc/migrations/0002_rpclog_is_event.py +0 -23
- django_cfg/apps/ipc/migrations/__init__.py +0 -0
- django_cfg/apps/ipc/models.py +0 -229
- django_cfg/apps/ipc/serializers/__init__.py +0 -29
- django_cfg/apps/ipc/serializers/serializers.py +0 -343
- django_cfg/apps/ipc/services/__init__.py +0 -7
- django_cfg/apps/ipc/services/client/__init__.py +0 -23
- django_cfg/apps/ipc/services/client/client.py +0 -621
- django_cfg/apps/ipc/services/client/config.py +0 -214
- django_cfg/apps/ipc/services/client/exceptions.py +0 -201
- django_cfg/apps/ipc/services/logging.py +0 -239
- django_cfg/apps/ipc/services/monitor.py +0 -466
- django_cfg/apps/ipc/services/rpc_log_consumer.py +0 -330
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +0 -269
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +0 -259
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +0 -375
- django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard.mjs.old +0 -441
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +0 -22
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +0 -9
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +0 -9
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +0 -23
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +0 -47
- django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +0 -184
- django_cfg/apps/ipc/templates/django_cfg_ipc/layout/base.html +0 -71
- django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +0 -56
- django_cfg/apps/ipc/urls.py +0 -23
- django_cfg/apps/ipc/views/__init__.py +0 -13
- django_cfg/apps/ipc/views/dashboard.py +0 -15
- django_cfg/apps/ipc/views/monitoring.py +0 -251
- django_cfg/apps/ipc/views/testing.py +0 -285
- django_cfg/static/js/api/ipc/client.mjs +0 -114
- django_cfg/static/js/api/ipc/index.mjs +0 -13
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/WHEEL +0 -0
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Naming conventions for Go code generation.
|
|
3
|
+
|
|
4
|
+
Handles conversions between Python/OpenAPI naming and Go naming conventions:
|
|
5
|
+
- snake_case → PascalCase (for exports)
|
|
6
|
+
- snake_case → camelCase (for unexported)
|
|
7
|
+
- Proper Go identifier sanitization
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def to_pascal_case(snake_str: str) -> str:
|
|
14
|
+
"""
|
|
15
|
+
Convert snake_case to PascalCase.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
snake_str: snake_case string
|
|
19
|
+
|
|
20
|
+
Returns:
|
|
21
|
+
PascalCase string
|
|
22
|
+
|
|
23
|
+
Examples:
|
|
24
|
+
>>> to_pascal_case("user_profile")
|
|
25
|
+
'UserProfile'
|
|
26
|
+
>>> to_pascal_case("api_key_id")
|
|
27
|
+
'APIKeyID'
|
|
28
|
+
>>> to_pascal_case("http_response")
|
|
29
|
+
'HTTPResponse'
|
|
30
|
+
"""
|
|
31
|
+
if not snake_str:
|
|
32
|
+
return ""
|
|
33
|
+
|
|
34
|
+
# Handle special acronyms that should be uppercase
|
|
35
|
+
acronyms = {'id', 'api', 'http', 'https', 'url', 'uri', 'json', 'xml', 'html', 'css', 'sql', 'uuid'}
|
|
36
|
+
|
|
37
|
+
# Split by underscore
|
|
38
|
+
parts = snake_str.split('_')
|
|
39
|
+
|
|
40
|
+
# Capitalize each part, with special handling for acronyms
|
|
41
|
+
result_parts = []
|
|
42
|
+
for part in parts:
|
|
43
|
+
if not part:
|
|
44
|
+
continue
|
|
45
|
+
|
|
46
|
+
# If entire part is an acronym, uppercase it
|
|
47
|
+
if part.lower() in acronyms:
|
|
48
|
+
result_parts.append(part.upper())
|
|
49
|
+
else:
|
|
50
|
+
# Check if part ends with an acronym
|
|
51
|
+
found_acronym = False
|
|
52
|
+
for acronym in acronyms:
|
|
53
|
+
if part.lower().endswith(acronym):
|
|
54
|
+
# Split off the acronym
|
|
55
|
+
prefix = part[:-len(acronym)]
|
|
56
|
+
if prefix:
|
|
57
|
+
result_parts.append(prefix.capitalize())
|
|
58
|
+
result_parts.append(acronym.upper())
|
|
59
|
+
found_acronym = True
|
|
60
|
+
break
|
|
61
|
+
|
|
62
|
+
if not found_acronym:
|
|
63
|
+
result_parts.append(part.capitalize())
|
|
64
|
+
|
|
65
|
+
return ''.join(result_parts)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def to_camel_case(snake_str: str) -> str:
|
|
69
|
+
"""
|
|
70
|
+
Convert snake_case to camelCase (unexported).
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
snake_str: snake_case string
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
camelCase string
|
|
77
|
+
|
|
78
|
+
Examples:
|
|
79
|
+
>>> to_camel_case("user_profile")
|
|
80
|
+
'userProfile'
|
|
81
|
+
>>> to_camel_case("http_client")
|
|
82
|
+
'httpClient'
|
|
83
|
+
"""
|
|
84
|
+
pascal = to_pascal_case(snake_str)
|
|
85
|
+
if not pascal:
|
|
86
|
+
return ""
|
|
87
|
+
|
|
88
|
+
# Lowercase the first character
|
|
89
|
+
return pascal[0].lower() + pascal[1:]
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def sanitize_go_identifier(name: str) -> str:
|
|
93
|
+
"""
|
|
94
|
+
Sanitize string to valid Go identifier.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
name: Raw identifier name
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
Valid Go identifier
|
|
101
|
+
|
|
102
|
+
Examples:
|
|
103
|
+
>>> sanitize_go_identifier("user-profile")
|
|
104
|
+
'UserProfile'
|
|
105
|
+
>>> sanitize_go_identifier("2users")
|
|
106
|
+
'N2users'
|
|
107
|
+
>>> sanitize_go_identifier("class")
|
|
108
|
+
'Class'
|
|
109
|
+
"""
|
|
110
|
+
# Replace hyphens with underscores
|
|
111
|
+
name = name.replace('-', '_')
|
|
112
|
+
|
|
113
|
+
# Remove invalid characters
|
|
114
|
+
name = re.sub(r'[^a-zA-Z0-9_]', '', name)
|
|
115
|
+
|
|
116
|
+
# If starts with digit, prefix with 'N'
|
|
117
|
+
if name and name[0].isdigit():
|
|
118
|
+
name = f'N{name}'
|
|
119
|
+
|
|
120
|
+
# Convert to PascalCase
|
|
121
|
+
return to_pascal_case(name) if name else 'Unknown'
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def to_snake_case(camel_str: str) -> str:
|
|
125
|
+
"""
|
|
126
|
+
Convert PascalCase/camelCase to snake_case.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
camel_str: PascalCase or camelCase string
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
snake_case string
|
|
133
|
+
|
|
134
|
+
Examples:
|
|
135
|
+
>>> to_snake_case("UserProfile")
|
|
136
|
+
'user_profile'
|
|
137
|
+
>>> to_snake_case("APIKey")
|
|
138
|
+
'api_key'
|
|
139
|
+
>>> to_snake_case("HTTPSConnection")
|
|
140
|
+
'https_connection'
|
|
141
|
+
"""
|
|
142
|
+
# Insert underscore before uppercase letters (except first)
|
|
143
|
+
s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', camel_str)
|
|
144
|
+
# Insert underscore before uppercase letters followed by lowercase
|
|
145
|
+
return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def get_go_field_name(field_name: str) -> str:
|
|
149
|
+
"""
|
|
150
|
+
Get Go struct field name (exported PascalCase).
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
field_name: Original field name (snake_case)
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Go field name (PascalCase)
|
|
157
|
+
|
|
158
|
+
Examples:
|
|
159
|
+
>>> get_go_field_name("user_id")
|
|
160
|
+
'UserID'
|
|
161
|
+
>>> get_go_field_name("created_at")
|
|
162
|
+
'CreatedAt'
|
|
163
|
+
"""
|
|
164
|
+
return to_pascal_case(field_name)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def get_go_package_name(name: str) -> str:
|
|
168
|
+
"""
|
|
169
|
+
Get valid Go package name (lowercase, no underscores).
|
|
170
|
+
|
|
171
|
+
Args:
|
|
172
|
+
name: Package name
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Valid Go package name
|
|
176
|
+
|
|
177
|
+
Examples:
|
|
178
|
+
>>> get_go_package_name("api_client")
|
|
179
|
+
'apiclient'
|
|
180
|
+
>>> get_go_package_name("UserService")
|
|
181
|
+
'userservice'
|
|
182
|
+
"""
|
|
183
|
+
# Convert to lowercase
|
|
184
|
+
name = name.lower()
|
|
185
|
+
|
|
186
|
+
# Remove invalid characters (keep only letters and digits)
|
|
187
|
+
name = re.sub(r'[^a-z0-9]', '', name)
|
|
188
|
+
|
|
189
|
+
# Must not start with digit
|
|
190
|
+
if name and name[0].isdigit():
|
|
191
|
+
name = f'pkg{name}'
|
|
192
|
+
|
|
193
|
+
return name or 'client'
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Operations Generator - Generates Go client methods from IR operations.
|
|
3
|
+
|
|
4
|
+
Handles generation of type-safe client methods for each API operation.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from .naming import to_pascal_case
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from jinja2 import Environment
|
|
15
|
+
|
|
16
|
+
from ...ir import IRContext, IROperationObject
|
|
17
|
+
from .generator import GoGenerator
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class OperationsGenerator:
|
|
21
|
+
"""Generates Go client operation methods from IR operations."""
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
jinja_env: Environment,
|
|
26
|
+
context: IRContext,
|
|
27
|
+
generator: GoGenerator,
|
|
28
|
+
):
|
|
29
|
+
"""
|
|
30
|
+
Initialize operations generator.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
jinja_env: Jinja2 environment
|
|
34
|
+
context: IRContext from parser
|
|
35
|
+
generator: Parent GoGenerator instance
|
|
36
|
+
"""
|
|
37
|
+
self.jinja_env = jinja_env
|
|
38
|
+
self.context = context
|
|
39
|
+
self.generator = generator
|
|
40
|
+
|
|
41
|
+
def generate_operation_method(
|
|
42
|
+
self,
|
|
43
|
+
operation: IROperationObject,
|
|
44
|
+
remove_tag_prefix: bool = False,
|
|
45
|
+
) -> dict:
|
|
46
|
+
"""
|
|
47
|
+
Generate Go method definition for an operation.
|
|
48
|
+
|
|
49
|
+
Args:
|
|
50
|
+
operation: IROperationObject to generate
|
|
51
|
+
remove_tag_prefix: Remove tag prefix from operation name
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
Dictionary with operation method info
|
|
55
|
+
"""
|
|
56
|
+
# Get method name
|
|
57
|
+
op_id = operation.operation_id
|
|
58
|
+
if remove_tag_prefix and operation.tags:
|
|
59
|
+
op_id = self.generator.remove_tag_prefix(op_id, operation.tags[0])
|
|
60
|
+
|
|
61
|
+
method_name = to_pascal_case(op_id)
|
|
62
|
+
|
|
63
|
+
# Get request/response types
|
|
64
|
+
request_type = None
|
|
65
|
+
if operation.request_body and operation.request_body.schema_name:
|
|
66
|
+
request_type = operation.request_body.schema_name
|
|
67
|
+
# Handle inline request bodies (multipart/form-data, etc.)
|
|
68
|
+
if request_type == "InlineRequestBody":
|
|
69
|
+
request_type = "map[string]interface{}"
|
|
70
|
+
|
|
71
|
+
response_type = "interface{}"
|
|
72
|
+
if operation.responses.get("200") and operation.responses["200"].schema_name:
|
|
73
|
+
response_type = operation.responses["200"].schema_name
|
|
74
|
+
elif operation.responses.get("201") and operation.responses["201"].schema_name:
|
|
75
|
+
response_type = operation.responses["201"].schema_name
|
|
76
|
+
|
|
77
|
+
# Build parameters
|
|
78
|
+
params = []
|
|
79
|
+
|
|
80
|
+
# Path parameters
|
|
81
|
+
for param in operation.parameters:
|
|
82
|
+
if param.location == "path":
|
|
83
|
+
params.append({
|
|
84
|
+
"name": param.name,
|
|
85
|
+
"type": self._get_param_go_type(param.schema_type),
|
|
86
|
+
"location": "path",
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
# Query parameters struct (if any)
|
|
90
|
+
query_params = [p for p in operation.parameters if p.location == "query"]
|
|
91
|
+
query_params_struct = None
|
|
92
|
+
if query_params:
|
|
93
|
+
params_struct_name = f"{method_name}Params"
|
|
94
|
+
params.append({
|
|
95
|
+
"name": "params",
|
|
96
|
+
"type": f"*{params_struct_name}",
|
|
97
|
+
"location": "query",
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
# Build query params struct definition
|
|
101
|
+
query_params_struct = {
|
|
102
|
+
"name": params_struct_name,
|
|
103
|
+
"fields": [
|
|
104
|
+
{
|
|
105
|
+
"name": to_pascal_case(p.name),
|
|
106
|
+
"type": self._get_param_go_type(p.schema_type),
|
|
107
|
+
"json_name": p.name,
|
|
108
|
+
"required": p.required,
|
|
109
|
+
}
|
|
110
|
+
for p in query_params
|
|
111
|
+
]
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return {
|
|
115
|
+
"name": method_name,
|
|
116
|
+
"http_method": operation.http_method.upper(),
|
|
117
|
+
"path": operation.path,
|
|
118
|
+
"parameters": params,
|
|
119
|
+
"request_type": request_type,
|
|
120
|
+
"response_type": response_type,
|
|
121
|
+
"description": operation.summary or operation.description or f"{method_name} operation",
|
|
122
|
+
"operation_id": operation.operation_id,
|
|
123
|
+
"query_params_struct": query_params_struct,
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
def _get_param_go_type(self, schema_type: str) -> str:
|
|
127
|
+
"""Get Go type for parameter schema type."""
|
|
128
|
+
type_map = {
|
|
129
|
+
"string": "string",
|
|
130
|
+
"integer": "int64",
|
|
131
|
+
"number": "float64",
|
|
132
|
+
"boolean": "bool",
|
|
133
|
+
}
|
|
134
|
+
return type_map.get(schema_type, "string")
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Code generated by django-cfg/django_client - DO NOT EDIT.
|
|
2
|
+
# Generated at: {{ generated_at }}
|
|
3
|
+
|
|
4
|
+
.PHONY: deps build test fmt lint clean all
|
|
5
|
+
|
|
6
|
+
# Download dependencies
|
|
7
|
+
deps:
|
|
8
|
+
go mod download
|
|
9
|
+
go mod tidy
|
|
10
|
+
|
|
11
|
+
# Build (if there's a cmd/)
|
|
12
|
+
build:
|
|
13
|
+
@if [ -d "cmd" ]; then \
|
|
14
|
+
go build -o bin/ ./cmd/...; \
|
|
15
|
+
else \
|
|
16
|
+
echo "No cmd/ directory found, skipping build"; \
|
|
17
|
+
fi
|
|
18
|
+
|
|
19
|
+
# Run tests
|
|
20
|
+
test:
|
|
21
|
+
go test -v -race -cover ./...
|
|
22
|
+
|
|
23
|
+
# Format code
|
|
24
|
+
fmt:
|
|
25
|
+
go fmt ./...
|
|
26
|
+
goimports -w .
|
|
27
|
+
|
|
28
|
+
# Run linter
|
|
29
|
+
lint:
|
|
30
|
+
golangci-lint run
|
|
31
|
+
|
|
32
|
+
# Clean build artifacts
|
|
33
|
+
clean:
|
|
34
|
+
rm -rf bin/
|
|
35
|
+
go clean
|
|
36
|
+
|
|
37
|
+
# Run all checks
|
|
38
|
+
all: fmt lint test
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# {{ api_title }} - Go Client
|
|
2
|
+
|
|
3
|
+
Auto-generated Go client for {{ api_title }} (v{{ api_version }}).
|
|
4
|
+
|
|
5
|
+
Generated at: {{ generated_at }}
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
go get {{ module_name }}
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```go
|
|
16
|
+
package main
|
|
17
|
+
|
|
18
|
+
import (
|
|
19
|
+
"context"
|
|
20
|
+
"fmt"
|
|
21
|
+
"log"
|
|
22
|
+
|
|
23
|
+
"{{ module_name }}"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
func main() {
|
|
27
|
+
// Create client
|
|
28
|
+
client := {{ module_name }}.NewClient(
|
|
29
|
+
"https://api.example.com",
|
|
30
|
+
{{ module_name }}.WithToken("your-api-token"),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
// Make requests
|
|
34
|
+
ctx := context.Background()
|
|
35
|
+
|
|
36
|
+
// Example: List users
|
|
37
|
+
// users, err := client.UsersList(ctx, nil)
|
|
38
|
+
// if err != nil {
|
|
39
|
+
// log.Fatal(err)
|
|
40
|
+
// }
|
|
41
|
+
// fmt.Printf("Users: %+v\n", users)
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Features
|
|
46
|
+
|
|
47
|
+
- ✅ Type-safe Go structs
|
|
48
|
+
- ✅ Context-aware requests
|
|
49
|
+
- ✅ Bearer token authentication
|
|
50
|
+
- ✅ Comprehensive error handling
|
|
51
|
+
- ✅ Auto-generated from OpenAPI spec
|
|
52
|
+
|
|
53
|
+
## Documentation
|
|
54
|
+
|
|
55
|
+
See the OpenAPI specification for full API documentation.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Code generated by django-cfg/django_client - DO NOT EDIT.
|
|
2
|
+
// Generated at: {{ generated_at }}
|
|
3
|
+
|
|
4
|
+
package {{ package_name }}
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"bytes"
|
|
8
|
+
"context"
|
|
9
|
+
"encoding/json"
|
|
10
|
+
"fmt"
|
|
11
|
+
"io"
|
|
12
|
+
"net/http"
|
|
13
|
+
"net/url"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
// Client is the HTTP API client.
|
|
17
|
+
type Client struct {
|
|
18
|
+
BaseURL string
|
|
19
|
+
HTTPClient *http.Client
|
|
20
|
+
Token string // Bearer token for authentication
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Option is a functional option for configuring the Client.
|
|
24
|
+
type Option func(*Client)
|
|
25
|
+
|
|
26
|
+
// WithHTTPClient sets a custom HTTP client.
|
|
27
|
+
func WithHTTPClient(client *http.Client) Option {
|
|
28
|
+
return func(c *Client) {
|
|
29
|
+
c.HTTPClient = client
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// WithToken sets the authentication token.
|
|
34
|
+
func WithToken(token string) Option {
|
|
35
|
+
return func(c *Client) {
|
|
36
|
+
c.Token = token
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// NewClient creates a new API client.
|
|
41
|
+
func NewClient(baseURL string, options ...Option) *Client {
|
|
42
|
+
client := &Client{
|
|
43
|
+
BaseURL: baseURL,
|
|
44
|
+
HTTPClient: &http.Client{},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
for _, opt := range options {
|
|
48
|
+
opt(client)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return client
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// doRequest performs HTTP request with error handling.
|
|
55
|
+
func (c *Client) doRequest(ctx context.Context, method, path string, body interface{}, response interface{}) error {
|
|
56
|
+
var reqBody io.Reader
|
|
57
|
+
|
|
58
|
+
if body != nil {
|
|
59
|
+
data, err := json.Marshal(body)
|
|
60
|
+
if err != nil {
|
|
61
|
+
return fmt.Errorf("failed to marshal request body: %w", err)
|
|
62
|
+
}
|
|
63
|
+
reqBody = bytes.NewReader(data)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
req, err := http.NewRequestWithContext(ctx, method, c.BaseURL+path, reqBody)
|
|
67
|
+
if err != nil {
|
|
68
|
+
return fmt.Errorf("failed to create request: %w", err)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
req.Header.Set("Content-Type", "application/json")
|
|
72
|
+
if c.Token != "" {
|
|
73
|
+
req.Header.Set("Authorization", "Bearer "+c.Token)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
resp, err := c.HTTPClient.Do(req)
|
|
77
|
+
if err != nil {
|
|
78
|
+
return fmt.Errorf("HTTP request failed: %w", err)
|
|
79
|
+
}
|
|
80
|
+
defer resp.Body.Close()
|
|
81
|
+
|
|
82
|
+
if resp.StatusCode >= 400 {
|
|
83
|
+
var apiErr APIError
|
|
84
|
+
apiErr.StatusCode = resp.StatusCode
|
|
85
|
+
if err := json.NewDecoder(resp.Body).Decode(&apiErr); err != nil {
|
|
86
|
+
apiErr.Message = resp.Status
|
|
87
|
+
}
|
|
88
|
+
return &apiErr
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if response != nil {
|
|
92
|
+
if err := json.NewDecoder(resp.Body).Decode(response); err != nil {
|
|
93
|
+
return fmt.Errorf("failed to decode response: %w", err)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return nil
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
{% for operation in operations %}
|
|
101
|
+
// {{ operation.name }} - {{ operation.description }}
|
|
102
|
+
func (c *Client) {{ operation.name }}(ctx context.Context{% for param in operation.parameters %}, {{ param.name }} {{ param.type }}{% endfor %}{% if operation.request_type %}, req *{{ operation.request_type }}{% endif %}) ({% if operation.response_type != 'interface{}' %}*{{ operation.response_type }}{% else %}interface{}{% endif %}, error) {
|
|
103
|
+
{% if operation.response_type != 'interface{}' %}var response {{ operation.response_type }}{% endif %}
|
|
104
|
+
|
|
105
|
+
path := "{{ operation.path }}"
|
|
106
|
+
{% if operation.parameters %}
|
|
107
|
+
// TODO: Build path with parameters and query string
|
|
108
|
+
{% endif %}
|
|
109
|
+
|
|
110
|
+
err := c.doRequest(ctx, "{{ operation.http_method }}", path, {% if operation.request_type %}req{% else %}nil{% endif %}, {% if operation.response_type != 'interface{}' %}&response{% else %}nil{% endif %})
|
|
111
|
+
if err != nil {
|
|
112
|
+
return nil, err
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
{% if operation.response_type != 'interface{}' %}
|
|
116
|
+
return &response, nil
|
|
117
|
+
{% else %}
|
|
118
|
+
return nil, nil
|
|
119
|
+
{% endif %}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
{% endfor %}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
// Code generated by django-cfg/django_client - DO NOT EDIT.
|
|
2
|
+
// Generated at: {{ generated_at }}
|
|
3
|
+
|
|
4
|
+
package {{ package_name }}
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"encoding/json"
|
|
8
|
+
"fmt"
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
{% for enum in enums %}
|
|
12
|
+
// {{ (enum.doc or enum.name) | replace('\n', '\n// ') }}
|
|
13
|
+
type {{ enum.name }} {{ enum.base_type }}
|
|
14
|
+
|
|
15
|
+
const (
|
|
16
|
+
{% for value in enum['values'] %}
|
|
17
|
+
// {{ (value.description or value.name) | replace('\n', '\n\t// ') }}
|
|
18
|
+
{{ value.name }} {{ enum.name }} = {{ value.value if not enum.is_string_enum else '"%s"' % value.value }}
|
|
19
|
+
{% endfor %}
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
// String returns string representation of {{ enum.name }}.
|
|
23
|
+
func (e {{ enum.name }}) String() string {
|
|
24
|
+
switch e {
|
|
25
|
+
{% for value in enum['values'] %}
|
|
26
|
+
case {{ value.name }}:
|
|
27
|
+
return "{{ value.name }}"
|
|
28
|
+
{% endfor %}
|
|
29
|
+
default:
|
|
30
|
+
return fmt.Sprintf("{{ enum.name }}(%v)", {{ enum.base_type }}(e))
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// MarshalJSON implements json.Marshaler.
|
|
35
|
+
func (e {{ enum.name }}) MarshalJSON() ([]byte, error) {
|
|
36
|
+
return json.Marshal({{ enum.base_type }}(e))
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// UnmarshalJSON implements json.Unmarshaler.
|
|
40
|
+
func (e *{{ enum.name }}) UnmarshalJSON(data []byte) error {
|
|
41
|
+
var v {{ enum.base_type }}
|
|
42
|
+
if err := json.Unmarshal(data, &v); err != nil {
|
|
43
|
+
return err
|
|
44
|
+
}
|
|
45
|
+
*e = {{ enum.name }}(v)
|
|
46
|
+
return nil
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
{% endfor %}
|