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.

Files changed (181) hide show
  1. django_cfg/__init__.py +1 -1
  2. django_cfg/apps/accounts/services/otp_service.py +3 -14
  3. django_cfg/apps/centrifugo/__init__.py +57 -0
  4. django_cfg/apps/centrifugo/admin/__init__.py +13 -0
  5. django_cfg/apps/centrifugo/admin/centrifugo_log.py +249 -0
  6. django_cfg/apps/centrifugo/admin/config.py +82 -0
  7. django_cfg/apps/centrifugo/apps.py +31 -0
  8. django_cfg/apps/centrifugo/codegen/IMPLEMENTATION_SUMMARY.md +475 -0
  9. django_cfg/apps/centrifugo/codegen/README.md +242 -0
  10. django_cfg/apps/centrifugo/codegen/USAGE.md +616 -0
  11. django_cfg/apps/centrifugo/codegen/__init__.py +19 -0
  12. django_cfg/apps/centrifugo/codegen/discovery.py +246 -0
  13. django_cfg/apps/centrifugo/codegen/generators/go_thin/__init__.py +5 -0
  14. django_cfg/apps/centrifugo/codegen/generators/go_thin/generator.py +174 -0
  15. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/README.md.j2 +182 -0
  16. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/client.go.j2 +64 -0
  17. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/go.mod.j2 +10 -0
  18. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2 +300 -0
  19. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/rpc_client.go.j2.old +267 -0
  20. django_cfg/apps/centrifugo/codegen/generators/go_thin/templates/types.go.j2 +16 -0
  21. django_cfg/apps/centrifugo/codegen/generators/python_thin/__init__.py +7 -0
  22. django_cfg/apps/centrifugo/codegen/generators/python_thin/generator.py +241 -0
  23. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/README.md.j2 +128 -0
  24. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/__init__.py.j2 +22 -0
  25. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/client.py.j2 +73 -0
  26. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/models.py.j2 +19 -0
  27. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/requirements.txt.j2 +8 -0
  28. django_cfg/apps/centrifugo/codegen/generators/python_thin/templates/rpc_client.py.j2 +193 -0
  29. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/__init__.py +5 -0
  30. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/generator.py +124 -0
  31. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/README.md.j2 +38 -0
  32. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/client.ts.j2 +25 -0
  33. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/index.ts.j2 +12 -0
  34. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/package.json.j2 +13 -0
  35. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/rpc-client.ts.j2 +137 -0
  36. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/tsconfig.json.j2 +14 -0
  37. django_cfg/apps/centrifugo/codegen/generators/typescript_thin/templates/types.ts.j2 +9 -0
  38. django_cfg/apps/centrifugo/codegen/utils/__init__.py +37 -0
  39. django_cfg/apps/centrifugo/codegen/utils/naming.py +155 -0
  40. django_cfg/apps/centrifugo/codegen/utils/type_converter.py +349 -0
  41. django_cfg/apps/centrifugo/decorators.py +137 -0
  42. django_cfg/apps/centrifugo/management/__init__.py +1 -0
  43. django_cfg/apps/centrifugo/management/commands/__init__.py +1 -0
  44. django_cfg/apps/centrifugo/management/commands/generate_centrifugo_clients.py +254 -0
  45. django_cfg/apps/centrifugo/managers/__init__.py +12 -0
  46. django_cfg/apps/centrifugo/managers/centrifugo_log.py +264 -0
  47. django_cfg/apps/centrifugo/migrations/0001_initial.py +164 -0
  48. django_cfg/apps/centrifugo/migrations/__init__.py +3 -0
  49. django_cfg/apps/centrifugo/models/__init__.py +11 -0
  50. django_cfg/apps/centrifugo/models/centrifugo_log.py +210 -0
  51. django_cfg/apps/centrifugo/registry.py +106 -0
  52. django_cfg/apps/centrifugo/router.py +125 -0
  53. django_cfg/apps/centrifugo/serializers/__init__.py +40 -0
  54. django_cfg/apps/centrifugo/serializers/admin_api.py +264 -0
  55. django_cfg/apps/centrifugo/serializers/channels.py +26 -0
  56. django_cfg/apps/centrifugo/serializers/health.py +17 -0
  57. django_cfg/apps/centrifugo/serializers/publishes.py +16 -0
  58. django_cfg/apps/centrifugo/serializers/stats.py +21 -0
  59. django_cfg/apps/centrifugo/services/__init__.py +12 -0
  60. django_cfg/apps/centrifugo/services/client/__init__.py +29 -0
  61. django_cfg/apps/centrifugo/services/client/client.py +582 -0
  62. django_cfg/apps/centrifugo/services/client/config.py +236 -0
  63. django_cfg/apps/centrifugo/services/client/exceptions.py +212 -0
  64. django_cfg/apps/centrifugo/services/config_helper.py +63 -0
  65. django_cfg/apps/centrifugo/services/dashboard_notifier.py +157 -0
  66. django_cfg/apps/centrifugo/services/logging.py +677 -0
  67. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/css/dashboard.css +260 -0
  68. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_channels.mjs +313 -0
  69. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/live_testing.mjs +803 -0
  70. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/main.mjs +333 -0
  71. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/overview.mjs +432 -0
  72. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/testing.mjs +33 -0
  73. django_cfg/apps/centrifugo/static/django_cfg_centrifugo/js/dashboard/websocket.mjs +210 -0
  74. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/channels_content.html +46 -0
  75. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/live_channels_content.html +123 -0
  76. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/overview_content.html +45 -0
  77. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/publishes_content.html +84 -0
  78. django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/stat_cards.html +23 -20
  79. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/system_status.html +91 -0
  80. django_cfg/apps/{ipc/templates/django_cfg_ipc → centrifugo/templates/django_cfg_centrifugo}/components/tab_navigation.html +15 -15
  81. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/components/testing_tools.html +415 -0
  82. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/layout/base.html +61 -0
  83. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/pages/dashboard.html +58 -0
  84. django_cfg/apps/centrifugo/templates/django_cfg_centrifugo/tags/connection_script.html +48 -0
  85. django_cfg/apps/centrifugo/templatetags/__init__.py +1 -0
  86. django_cfg/apps/centrifugo/templatetags/centrifugo_tags.py +81 -0
  87. django_cfg/apps/centrifugo/urls.py +31 -0
  88. django_cfg/apps/{ipc → centrifugo}/urls_admin.py +4 -4
  89. django_cfg/apps/centrifugo/views/__init__.py +15 -0
  90. django_cfg/apps/centrifugo/views/admin_api.py +380 -0
  91. django_cfg/apps/centrifugo/views/dashboard.py +15 -0
  92. django_cfg/apps/centrifugo/views/monitoring.py +286 -0
  93. django_cfg/apps/centrifugo/views/testing_api.py +422 -0
  94. django_cfg/apps/support/utils/support_email_service.py +5 -18
  95. django_cfg/apps/tasks/templates/tasks/layout/base.html +0 -2
  96. django_cfg/apps/urls.py +5 -5
  97. django_cfg/core/base/config_model.py +4 -44
  98. django_cfg/core/builders/apps_builder.py +2 -2
  99. django_cfg/core/generation/integration_generators/third_party.py +8 -8
  100. django_cfg/core/utils/__init__.py +5 -0
  101. django_cfg/core/utils/url_helpers.py +73 -0
  102. django_cfg/modules/base.py +7 -7
  103. django_cfg/modules/django_client/core/__init__.py +2 -1
  104. django_cfg/modules/django_client/core/config/config.py +8 -0
  105. django_cfg/modules/django_client/core/generator/__init__.py +42 -2
  106. django_cfg/modules/django_client/core/generator/go/__init__.py +14 -0
  107. django_cfg/modules/django_client/core/generator/go/client_generator.py +124 -0
  108. django_cfg/modules/django_client/core/generator/go/files_generator.py +133 -0
  109. django_cfg/modules/django_client/core/generator/go/generator.py +203 -0
  110. django_cfg/modules/django_client/core/generator/go/models_generator.py +304 -0
  111. django_cfg/modules/django_client/core/generator/go/naming.py +193 -0
  112. django_cfg/modules/django_client/core/generator/go/operations_generator.py +134 -0
  113. django_cfg/modules/django_client/core/generator/go/templates/Makefile.j2 +38 -0
  114. django_cfg/modules/django_client/core/generator/go/templates/README.md.j2 +55 -0
  115. django_cfg/modules/django_client/core/generator/go/templates/client.go.j2 +122 -0
  116. django_cfg/modules/django_client/core/generator/go/templates/enums.go.j2 +49 -0
  117. django_cfg/modules/django_client/core/generator/go/templates/errors.go.j2 +182 -0
  118. django_cfg/modules/django_client/core/generator/go/templates/go.mod.j2 +6 -0
  119. django_cfg/modules/django_client/core/generator/go/templates/main_client.go.j2 +60 -0
  120. django_cfg/modules/django_client/core/generator/go/templates/middleware.go.j2 +388 -0
  121. django_cfg/modules/django_client/core/generator/go/templates/models.go.j2 +28 -0
  122. django_cfg/modules/django_client/core/generator/go/templates/operations_client.go.j2 +142 -0
  123. django_cfg/modules/django_client/core/generator/go/templates/validation.go.j2 +217 -0
  124. django_cfg/modules/django_client/core/generator/go/type_mapper.py +380 -0
  125. django_cfg/modules/django_client/management/commands/generate_client.py +53 -3
  126. django_cfg/modules/django_client/system/generate_mjs_clients.py +3 -1
  127. django_cfg/modules/django_client/system/schema_parser.py +5 -1
  128. django_cfg/modules/django_tailwind/templates/django_tailwind/base.html +1 -0
  129. django_cfg/modules/django_twilio/sendgrid_service.py +7 -4
  130. django_cfg/modules/django_unfold/dashboard.py +25 -19
  131. django_cfg/pyproject.toml +1 -1
  132. django_cfg/registry/core.py +2 -0
  133. django_cfg/registry/modules.py +2 -2
  134. django_cfg/static/js/api/centrifugo/client.mjs +164 -0
  135. django_cfg/static/js/api/centrifugo/index.mjs +13 -0
  136. django_cfg/static/js/api/index.mjs +5 -5
  137. django_cfg/static/js/api/types.mjs +89 -26
  138. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/METADATA +1 -1
  139. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/RECORD +142 -70
  140. django_cfg/apps/ipc/README.md +0 -346
  141. django_cfg/apps/ipc/RPC_LOGGING.md +0 -321
  142. django_cfg/apps/ipc/TESTING.md +0 -539
  143. django_cfg/apps/ipc/__init__.py +0 -60
  144. django_cfg/apps/ipc/admin.py +0 -232
  145. django_cfg/apps/ipc/apps.py +0 -98
  146. django_cfg/apps/ipc/migrations/0001_initial.py +0 -137
  147. django_cfg/apps/ipc/migrations/0002_rpclog_is_event.py +0 -23
  148. django_cfg/apps/ipc/migrations/__init__.py +0 -0
  149. django_cfg/apps/ipc/models.py +0 -229
  150. django_cfg/apps/ipc/serializers/__init__.py +0 -29
  151. django_cfg/apps/ipc/serializers/serializers.py +0 -343
  152. django_cfg/apps/ipc/services/__init__.py +0 -7
  153. django_cfg/apps/ipc/services/client/__init__.py +0 -23
  154. django_cfg/apps/ipc/services/client/client.py +0 -621
  155. django_cfg/apps/ipc/services/client/config.py +0 -214
  156. django_cfg/apps/ipc/services/client/exceptions.py +0 -201
  157. django_cfg/apps/ipc/services/logging.py +0 -239
  158. django_cfg/apps/ipc/services/monitor.py +0 -466
  159. django_cfg/apps/ipc/services/rpc_log_consumer.py +0 -330
  160. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/main.mjs +0 -269
  161. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/overview.mjs +0 -259
  162. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard/testing.mjs +0 -375
  163. django_cfg/apps/ipc/static/django_cfg_ipc/js/dashboard.mjs.old +0 -441
  164. django_cfg/apps/ipc/templates/django_cfg_ipc/components/methods_content.html +0 -22
  165. django_cfg/apps/ipc/templates/django_cfg_ipc/components/notifications_content.html +0 -9
  166. django_cfg/apps/ipc/templates/django_cfg_ipc/components/overview_content.html +0 -9
  167. django_cfg/apps/ipc/templates/django_cfg_ipc/components/requests_content.html +0 -23
  168. django_cfg/apps/ipc/templates/django_cfg_ipc/components/system_status.html +0 -47
  169. django_cfg/apps/ipc/templates/django_cfg_ipc/components/testing_tools.html +0 -184
  170. django_cfg/apps/ipc/templates/django_cfg_ipc/layout/base.html +0 -71
  171. django_cfg/apps/ipc/templates/django_cfg_ipc/pages/dashboard.html +0 -56
  172. django_cfg/apps/ipc/urls.py +0 -23
  173. django_cfg/apps/ipc/views/__init__.py +0 -13
  174. django_cfg/apps/ipc/views/dashboard.py +0 -15
  175. django_cfg/apps/ipc/views/monitoring.py +0 -251
  176. django_cfg/apps/ipc/views/testing.py +0 -285
  177. django_cfg/static/js/api/ipc/client.mjs +0 -114
  178. django_cfg/static/js/api/ipc/index.mjs +0 -13
  179. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/WHEEL +0 -0
  180. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/entry_points.txt +0 -0
  181. {django_cfg-1.4.62.dist-info → django_cfg-1.4.64.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,133 @@
1
+ """
2
+ Files Generator - Generates auxiliary files (go.mod, README, Makefile, etc.).
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ from datetime import datetime
8
+ from typing import TYPE_CHECKING
9
+
10
+ if TYPE_CHECKING:
11
+ from jinja2 import Environment
12
+
13
+ from ...ir import IRContext
14
+ from ..base import GeneratedFile
15
+ from .generator import GoGenerator
16
+
17
+
18
+ class FilesGenerator:
19
+ """Generates auxiliary files for Go client."""
20
+
21
+ def __init__(
22
+ self,
23
+ jinja_env: Environment,
24
+ context: IRContext,
25
+ generator: GoGenerator,
26
+ ):
27
+ """Initialize files generator."""
28
+ self.jinja_env = jinja_env
29
+ self.context = context
30
+ self.generator = generator
31
+
32
+ def generate_go_mod(self) -> GeneratedFile:
33
+ """Generate go.mod file."""
34
+ template = self.jinja_env.get_template("go.mod.j2")
35
+
36
+ module_name = self.generator.package_config.get("module_name", "apiclient")
37
+ go_version = self.generator.package_config.get("go_version", "1.21")
38
+
39
+ content = template.render(
40
+ module_name=module_name,
41
+ go_version=go_version,
42
+ generated_at=datetime.now().isoformat(),
43
+ )
44
+
45
+ return self.generator._create_generated_file(
46
+ path="go.mod",
47
+ content=content,
48
+ description="Go module definition"
49
+ )
50
+
51
+ def generate_readme(self) -> GeneratedFile:
52
+ """Generate README.md file."""
53
+ template = self.jinja_env.get_template("README.md.j2")
54
+
55
+ content = template.render(
56
+ module_name=self.generator.package_config.get("module_name", "apiclient"),
57
+ api_title=self.context.openapi_info.title if self.context.openapi_info else "API Client",
58
+ api_version=self.context.openapi_info.api_version if self.context.openapi_info else "1.0.0",
59
+ generated_at=datetime.now().isoformat(),
60
+ )
61
+
62
+ return self.generator._create_generated_file(
63
+ path="README.md",
64
+ content=content,
65
+ description="Usage documentation"
66
+ )
67
+
68
+ def generate_makefile(self) -> GeneratedFile:
69
+ """Generate Makefile."""
70
+ template = self.jinja_env.get_template("Makefile.j2")
71
+
72
+ content = template.render(
73
+ generated_at=datetime.now().isoformat(),
74
+ )
75
+
76
+ return self.generator._create_generated_file(
77
+ path="Makefile",
78
+ content=content,
79
+ description="Build automation"
80
+ )
81
+
82
+ def generate_errors_file(self, shared: bool = False) -> GeneratedFile:
83
+ """
84
+ Generate errors.go with API error handling.
85
+
86
+ Args:
87
+ shared: If True, generate in shared/ package for namespaced structure
88
+ """
89
+ template = self.jinja_env.get_template("errors.go.j2")
90
+
91
+ package_name = "shared" if shared else self.generator.package_name
92
+ path = "shared/errors.go" if shared else "errors.go"
93
+
94
+ content = template.render(
95
+ package_name=package_name,
96
+ generated_at=datetime.now().isoformat(),
97
+ )
98
+
99
+ return self.generator._create_generated_file(
100
+ path=path,
101
+ content=content,
102
+ description="API error types"
103
+ )
104
+
105
+ def generate_middleware_file(self) -> GeneratedFile:
106
+ """Generate middleware.go with retry, logging, rate limiting."""
107
+ template = self.jinja_env.get_template("middleware.go.j2")
108
+
109
+ content = template.render(
110
+ package_name=self.generator.package_name,
111
+ generated_at=datetime.now().isoformat(),
112
+ )
113
+
114
+ return self.generator._create_generated_file(
115
+ path="middleware.go",
116
+ content=content,
117
+ description="HTTP middleware (retry, logging, rate limiting)"
118
+ )
119
+
120
+ def generate_validation_file(self) -> GeneratedFile:
121
+ """Generate validation.go with client-side validation helpers."""
122
+ template = self.jinja_env.get_template("validation.go.j2")
123
+
124
+ content = template.render(
125
+ package_name=self.generator.package_name,
126
+ generated_at=datetime.now().isoformat(),
127
+ )
128
+
129
+ return self.generator._create_generated_file(
130
+ path="validation.go",
131
+ content=content,
132
+ description="Client-side validation helpers"
133
+ )
@@ -0,0 +1,203 @@
1
+ """
2
+ Go Generator - Main orchestrator for Go client generation.
3
+
4
+ Coordinates all sub-generators:
5
+ - ModelsGenerator: Go structs and enums
6
+ - OperationsGenerator: Operation methods
7
+ - ClientGenerator: HTTP client
8
+ - FilesGenerator: Auxiliary files (go.mod, README, Makefile)
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import pathlib
14
+
15
+ from jinja2 import Environment, FileSystemLoader, select_autoescape
16
+
17
+ from ...ir import IROperationObject, IRSchemaObject
18
+ from ..base import BaseGenerator, GeneratedFile
19
+ from .client_generator import ClientGenerator
20
+ from .files_generator import FilesGenerator
21
+ from .models_generator import ModelsGenerator
22
+ from .naming import get_go_package_name
23
+ from .operations_generator import OperationsGenerator
24
+ from .type_mapper import GoTypeMapper
25
+
26
+
27
+ class GoGenerator(BaseGenerator):
28
+ """
29
+ Go client generator.
30
+
31
+ Generates:
32
+ - models.go: Go structs (User, UserRequest, PatchedUser)
33
+ - enums.go: Enum types with constants
34
+ - client.go: HTTP client with all operations
35
+ - errors.go: API error handling
36
+ - go.mod: Module definition
37
+ """
38
+
39
+ def __init__(self, *args, **kwargs):
40
+ super().__init__(*args, **kwargs)
41
+
42
+ # Setup Jinja2 environment
43
+ templates_dir = pathlib.Path(__file__).parent / "templates"
44
+ self.jinja_env = Environment(
45
+ loader=FileSystemLoader(str(templates_dir)),
46
+ autoescape=select_autoescape(['html', 'xml']),
47
+ trim_blocks=True,
48
+ lstrip_blocks=True,
49
+ )
50
+
51
+ # Initialize type mapper
52
+ # Use types package for namespaced structure to avoid import cycles
53
+ use_types_pkg = self.client_structure == "namespaced"
54
+ self.type_mapper = GoTypeMapper(use_types_package=use_types_pkg)
55
+
56
+ # Get package name
57
+ self.package_name = get_go_package_name(
58
+ self.package_config.get("name", "apiclient")
59
+ )
60
+
61
+ # Initialize sub-generators
62
+ self.models_gen = ModelsGenerator(self.jinja_env, self.context, self)
63
+ self.operations_gen = OperationsGenerator(self.jinja_env, self.context, self)
64
+ self.client_gen = ClientGenerator(self.jinja_env, self.context, self, self.operations_gen)
65
+ self.files_gen = FilesGenerator(self.jinja_env, self.context, self)
66
+
67
+ def generate(self) -> list[GeneratedFile]:
68
+ """Generate all Go client files."""
69
+ files = []
70
+
71
+ if self.client_structure == "namespaced":
72
+ # Generate per-app folders
73
+ ops_by_tag = self.group_operations_by_tag()
74
+
75
+ for tag, operations in sorted(ops_by_tag.items()):
76
+ # Generate app folder (models.go, operations.go)
77
+ app_files = self._generate_app_folder(tag, operations)
78
+ if app_files:
79
+ files.extend(app_files)
80
+
81
+ # Generate shared enums.go
82
+ all_schemas = self.context.schemas
83
+ all_enums = self._collect_enums_from_schemas(all_schemas)
84
+ if all_enums:
85
+ enum_file = self.models_gen.generate_shared_enums_file(all_enums)
86
+ if enum_file:
87
+ files.append(enum_file)
88
+
89
+ # Generate main client.go
90
+ files.append(self.client_gen.generate_main_client_file(ops_by_tag))
91
+
92
+ # Generate supporting files in shared/ package to avoid circular imports
93
+ files.append(self.files_gen.generate_errors_file(shared=True))
94
+ files.append(self.files_gen.generate_middleware_file())
95
+ files.append(self.files_gen.generate_validation_file())
96
+
97
+ if self.generate_package_files:
98
+ files.append(self.files_gen.generate_go_mod())
99
+ files.append(self.files_gen.generate_readme())
100
+ files.append(self.files_gen.generate_makefile())
101
+ else:
102
+ # Flat structure
103
+ files.append(self.models_gen.generate_models_file())
104
+
105
+ enum_schemas = self.get_enum_schemas()
106
+ if enum_schemas:
107
+ enum_file = self.models_gen.generate_enums_file()
108
+ if enum_file:
109
+ files.append(enum_file)
110
+
111
+ files.append(self.client_gen.generate_client_file())
112
+ files.append(self.files_gen.generate_errors_file())
113
+ files.append(self.files_gen.generate_middleware_file())
114
+ files.append(self.files_gen.generate_validation_file())
115
+
116
+ if self.generate_package_files:
117
+ files.append(self.files_gen.generate_go_mod())
118
+ files.append(self.files_gen.generate_readme())
119
+ files.append(self.files_gen.generate_makefile())
120
+
121
+ return files
122
+
123
+ def _generate_app_folder(self, tag: str, operations: list[IROperationObject]) -> list[GeneratedFile]:
124
+ """Generate folder for a specific app (tag)."""
125
+ files = []
126
+
127
+ # Get schemas used by this app
128
+ app_schemas = self._get_schemas_for_operations(operations)
129
+
130
+ # Generate models.go for this app
131
+ models_file = self.models_gen.generate_app_models_file(tag, app_schemas, operations)
132
+ if models_file:
133
+ files.append(models_file)
134
+
135
+ # Generate client.go with HTTP operations for this app
136
+ client_file = self.client_gen.generate_subpackage_client_file(tag, operations)
137
+ if client_file:
138
+ files.append(client_file)
139
+
140
+ return files
141
+
142
+ def _get_schemas_for_operations(self, operations: list[IROperationObject]) -> dict[str, IRSchemaObject]:
143
+ """Get all schemas used by given operations, including nested dependencies."""
144
+ schemas = {}
145
+ schemas_to_process = set()
146
+
147
+ # Collect top-level schemas from operations
148
+ for operation in operations:
149
+ # Request body schemas
150
+ if operation.request_body and operation.request_body.schema_name:
151
+ schemas_to_process.add(operation.request_body.schema_name)
152
+
153
+ # Patch request body schemas
154
+ if operation.patch_request_body and operation.patch_request_body.schema_name:
155
+ schemas_to_process.add(operation.patch_request_body.schema_name)
156
+
157
+ # Response schemas
158
+ for status_code, response in operation.responses.items():
159
+ if response.schema_name:
160
+ schemas_to_process.add(response.schema_name)
161
+
162
+ # Recursively collect all dependencies
163
+ processed = set()
164
+ while schemas_to_process:
165
+ schema_name = schemas_to_process.pop()
166
+ if schema_name in processed or schema_name not in self.context.schemas:
167
+ continue
168
+
169
+ processed.add(schema_name)
170
+ schema = self.context.schemas[schema_name]
171
+ schemas[schema_name] = schema
172
+
173
+ # Find nested schema references
174
+ if schema.properties:
175
+ for prop_name, prop_schema in schema.properties.items():
176
+ # Direct reference to another schema
177
+ if prop_schema.ref and prop_schema.ref in self.context.schemas:
178
+ schemas_to_process.add(prop_schema.ref)
179
+
180
+ # Array items reference
181
+ if prop_schema.items and prop_schema.items.ref:
182
+ if prop_schema.items.ref in self.context.schemas:
183
+ schemas_to_process.add(prop_schema.items.ref)
184
+
185
+ return schemas
186
+
187
+ def _create_generated_file(self, path: str, content: str, description: str) -> GeneratedFile:
188
+ """Create GeneratedFile instance."""
189
+ return GeneratedFile(path=path, content=content, description=description)
190
+
191
+ # Backward compatibility - delegate to sub-generators
192
+ def generate_schema(self, schema: IRSchemaObject) -> str:
193
+ """Generate Go struct for schema (delegates to ModelsGenerator)."""
194
+ return self.models_gen.generate_schema(schema)
195
+
196
+ def generate_enum(self, schema: IRSchemaObject) -> str:
197
+ """Generate enum type (not directly supported, returns empty)."""
198
+ return "" # Go enums are handled differently
199
+
200
+ def generate_operation(self, operation: IROperationObject, remove_tag_prefix: bool = False) -> str:
201
+ """Generate method for operation (for compatibility)."""
202
+ op_method = self.operations_gen.generate_operation_method(operation, remove_tag_prefix)
203
+ return f"func (c *Client) {op_method['name']}(...) (...) {{ ... }}"
@@ -0,0 +1,304 @@
1
+ """
2
+ Models Generator - Generates Go structs from IR schemas.
3
+
4
+ Handles generation of:
5
+ - Response models (User, Post, etc.)
6
+ - Request models (UserRequest, PostRequest)
7
+ - Patch models (PatchedUser, PatchedPost)
8
+ - Enum types with constants
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from datetime import datetime
14
+ from typing import TYPE_CHECKING
15
+
16
+ if TYPE_CHECKING:
17
+ from jinja2 import Environment
18
+
19
+ from ...ir import IRContext, IRSchemaObject
20
+ from ..base import GeneratedFile
21
+ from .generator import GoGenerator
22
+
23
+
24
+ class ModelsGenerator:
25
+ """Generates Go struct definitions from IR schemas."""
26
+
27
+ def __init__(
28
+ self,
29
+ jinja_env: Environment,
30
+ context: IRContext,
31
+ generator: GoGenerator,
32
+ ):
33
+ """
34
+ Initialize models generator.
35
+
36
+ Args:
37
+ jinja_env: Jinja2 environment
38
+ context: IRContext from parser
39
+ generator: Parent GoGenerator instance
40
+ """
41
+ self.jinja_env = jinja_env
42
+ self.context = context
43
+ self.generator = generator
44
+
45
+ def generate_models_file(self) -> GeneratedFile:
46
+ """
47
+ Generate models.go with all struct definitions.
48
+
49
+ Contains:
50
+ - All response models (User, Post, etc.)
51
+ - All request models (UserRequest, PostRequest)
52
+ - All patch models (PatchedUser, PatchedPost)
53
+
54
+ Returns:
55
+ GeneratedFile with models.go content
56
+ """
57
+ template = self.jinja_env.get_template("models.go.j2")
58
+
59
+ structs = []
60
+
61
+ # Generate response models
62
+ for schema_name, schema in sorted(self.context.response_models.items()):
63
+ struct_def = self.generator.type_mapper.ir_schema_to_struct(schema)
64
+ structs.append(struct_def)
65
+
66
+ # Generate request models
67
+ for schema_name, schema in sorted(self.context.request_models.items()):
68
+ struct_def = self.generator.type_mapper.ir_schema_to_struct(schema)
69
+ structs.append(struct_def)
70
+
71
+ # Generate patch models
72
+ for schema_name, schema in sorted(self.context.patch_models.items()):
73
+ struct_def = self.generator.type_mapper.ir_schema_to_struct(schema)
74
+ structs.append(struct_def)
75
+
76
+ # Collect imports
77
+ imports = self._collect_imports(structs)
78
+
79
+ content = template.render(
80
+ package_name=self.generator.package_name,
81
+ structs=structs,
82
+ imports=imports,
83
+ generated_at=datetime.now().isoformat(),
84
+ )
85
+
86
+ return self.generator._create_generated_file(
87
+ path="models.go",
88
+ content=content,
89
+ description="API data models"
90
+ )
91
+
92
+ def generate_enums_file(self) -> GeneratedFile:
93
+ """
94
+ Generate enums.go with enum type definitions.
95
+
96
+ Go approach to enums:
97
+ ```go
98
+ type StatusEnum int64
99
+
100
+ const (
101
+ StatusNew StatusEnum = 1
102
+ StatusInProgress StatusEnum = 2
103
+ StatusComplete StatusEnum = 3
104
+ )
105
+
106
+ func (s StatusEnum) String() string {
107
+ switch s {
108
+ case StatusNew:
109
+ return "STATUS_NEW"
110
+ case StatusInProgress:
111
+ return "STATUS_IN_PROGRESS"
112
+ case StatusComplete:
113
+ return "STATUS_COMPLETE"
114
+ default:
115
+ return fmt.Sprintf("StatusEnum(%d)", s)
116
+ }
117
+ }
118
+
119
+ func (s StatusEnum) MarshalJSON() ([]byte, error) {
120
+ return json.Marshal(int64(s))
121
+ }
122
+
123
+ func (s *StatusEnum) UnmarshalJSON(data []byte) error {
124
+ var v int64
125
+ if err := json.Unmarshal(data, &v); err != nil {
126
+ return err
127
+ }
128
+ *s = StatusEnum(v)
129
+ return nil
130
+ }
131
+ ```
132
+
133
+ Returns:
134
+ GeneratedFile with enums.go content
135
+ """
136
+ template = self.jinja_env.get_template("enums.go.j2")
137
+
138
+ enums = []
139
+ for schema_name, schema in sorted(self.context.enum_schemas.items()):
140
+ enum_def = self.generator.type_mapper.generate_enum_definition(schema)
141
+ enums.append(enum_def)
142
+
143
+ if not enums:
144
+ # No enums to generate
145
+ return None
146
+
147
+ content = template.render(
148
+ package_name=self.generator.package_name,
149
+ enums=enums,
150
+ generated_at=datetime.now().isoformat(),
151
+ )
152
+
153
+ return self.generator._create_generated_file(
154
+ path="enums.go",
155
+ content=content,
156
+ description="API enum types"
157
+ )
158
+
159
+ def generate_shared_enums_file(self, enums: dict[str, IRSchemaObject]) -> GeneratedFile:
160
+ """
161
+ Generate shared enums.go (for namespaced structure).
162
+
163
+ Args:
164
+ enums: Dictionary of enum schemas
165
+
166
+ Returns:
167
+ GeneratedFile with shared enums.go content
168
+ """
169
+ template = self.jinja_env.get_template("enums.go.j2")
170
+
171
+ enum_defs = []
172
+ for schema_name, schema in sorted(enums.items()):
173
+ enum_def = self.generator.type_mapper.generate_enum_definition(schema)
174
+ enum_defs.append(enum_def)
175
+
176
+ if not enum_defs:
177
+ return None
178
+
179
+ content = template.render(
180
+ package_name="types",
181
+ enums=enum_defs,
182
+ generated_at=datetime.now().isoformat(),
183
+ )
184
+
185
+ return self.generator._create_generated_file(
186
+ path="types/enums.go",
187
+ content=content,
188
+ description="Shared API enum types"
189
+ )
190
+
191
+ def generate_app_models_file(
192
+ self,
193
+ tag: str,
194
+ schemas: dict[str, IRSchemaObject],
195
+ operations: list,
196
+ ) -> GeneratedFile:
197
+ """
198
+ Generate models.go for specific app/tag (namespaced structure).
199
+
200
+ Args:
201
+ tag: Tag name
202
+ schemas: Schemas used by this app
203
+ operations: Operations for this app
204
+
205
+ Returns:
206
+ GeneratedFile with app-specific models.go
207
+ """
208
+ template = self.jinja_env.get_template("models.go.j2")
209
+
210
+ structs = []
211
+ for schema_name, schema in sorted(schemas.items()):
212
+ # Skip enum schemas (they go in shared enums.go)
213
+ if schema.enum:
214
+ continue
215
+
216
+ struct_def = self.generator.type_mapper.ir_schema_to_struct(schema)
217
+ structs.append(struct_def)
218
+
219
+ if not structs:
220
+ # No models for this app
221
+ return None
222
+
223
+ # Collect imports
224
+ imports = self._collect_imports(structs)
225
+
226
+ # Get folder name for this app
227
+ folder_name = self.generator.tag_and_app_to_folder_name(tag, operations)
228
+
229
+ content = template.render(
230
+ package_name=folder_name,
231
+ structs=structs,
232
+ imports=imports,
233
+ generated_at=datetime.now().isoformat(),
234
+ )
235
+
236
+ return self.generator._create_generated_file(
237
+ path=f"{folder_name}/models.go",
238
+ content=content,
239
+ description=f"{tag} API models"
240
+ )
241
+
242
+ def _collect_imports(self, structs: list[dict]) -> list[str]:
243
+ """
244
+ Collect required imports for structs.
245
+
246
+ Args:
247
+ structs: List of struct definitions
248
+
249
+ Returns:
250
+ List of import paths
251
+
252
+ Examples:
253
+ >>> structs = [{"needs_time_import": True}]
254
+ >>> imports = generator._collect_imports(structs)
255
+ >>> "time" in imports
256
+ True
257
+ """
258
+ imports = set()
259
+
260
+ # Check if time import is needed
261
+ for struct in structs:
262
+ if struct.get("needs_time_import"):
263
+ imports.add("time")
264
+
265
+ # Check if types package is used (for enums)
266
+ for field in struct.get("fields", []):
267
+ if "types." in field.get("type", ""):
268
+ module_name = self.generator.package_config.get("module_name", "apiclient")
269
+ imports.add(f"{module_name}/types")
270
+ break
271
+
272
+ return sorted(imports)
273
+
274
+ def generate_schema(self, schema: IRSchemaObject) -> str:
275
+ """
276
+ Generate Go struct code for a single schema (for backward compatibility).
277
+
278
+ Args:
279
+ schema: IRSchemaObject to generate
280
+
281
+ Returns:
282
+ Generated struct code as string
283
+ """
284
+ struct_def = self.generator.type_mapper.ir_schema_to_struct(schema)
285
+
286
+ lines = []
287
+
288
+ # Add doc comment
289
+ if struct_def["doc"]:
290
+ lines.append(f"// {struct_def['doc']}")
291
+
292
+ # Add struct definition
293
+ lines.append(f"type {struct_def['name']} struct {{")
294
+
295
+ # Add fields
296
+ for field in struct_def["fields"]:
297
+ if field["description"]:
298
+ lines.append(f"\t// {field['description']}")
299
+
300
+ lines.append(f"\t{field['name']} {field['type']} {field['json_tag']}")
301
+
302
+ lines.append("}")
303
+
304
+ return "\n".join(lines)