jararaca 0.3.14__py3-none-any.whl → 0.3.16__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 jararaca might be problematic. Click here for more details.

README.md CHANGED
@@ -37,6 +37,7 @@ Jararaca is an async-first microservice framework designed to simplify the devel
37
37
  - Command-line tool for generating TypeScript types
38
38
  - Support for REST endpoints, WebSocket events, and message bus payloads
39
39
  - Type-safe frontend-backend communication
40
+ - **`@ExposeType` decorator** - Explicitly expose types for TypeScript generation without needing them in endpoints
40
41
 
41
42
  ### Hexagonal Architecture
42
43
  - Clear separation of concerns
jararaca/__init__.py CHANGED
@@ -171,6 +171,7 @@ if TYPE_CHECKING:
171
171
  from .scheduler.decorators import ScheduledAction
172
172
  from .tools.app_config.interceptor import AppConfigurationInterceptor
173
173
  from .tools.typescript.decorators import (
174
+ ExposeType,
174
175
  MutationEndpoint,
175
176
  QueryEndpoint,
176
177
  SplitInputOutput,
@@ -279,6 +280,7 @@ if TYPE_CHECKING:
279
280
  "MessageBusPublisherInterceptor",
280
281
  "RedisWebSocketConnectionBackend",
281
282
  "AppConfigurationInterceptor",
283
+ "ExposeType",
282
284
  "QueryEndpoint",
283
285
  "MutationEndpoint",
284
286
  "SplitInputOutput",
@@ -510,6 +512,7 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
510
512
  "tools.app_config.interceptor",
511
513
  None,
512
514
  ),
515
+ "ExposeType": (__SPEC_PARENT__, "tools.typescript.decorators", None),
513
516
  "QueryEndpoint": (__SPEC_PARENT__, "tools.typescript.decorators", None),
514
517
  "MutationEndpoint": (__SPEC_PARENT__, "tools.typescript.decorators", None),
515
518
  "SplitInputOutput": (__SPEC_PARENT__, "tools.typescript.decorators", None),
@@ -11,6 +11,8 @@ from jararaca.scheduler.decorators import ScheduledAction, ScheduledActionData
11
11
 
12
12
  DECORATED_FUNC = TypeVar("DECORATED_FUNC", bound=Callable[..., Any])
13
13
  DECORATED_T = TypeVar("DECORATED_T", bound=Any)
14
+ INSTANCE_T = TypeVar("INSTANCE_T", bound=Any)
15
+ RETURN_T = TypeVar("RETURN_T", bound=Any)
14
16
 
15
17
 
16
18
  class MessageHandler(Generic[INHERITS_MESSAGE_CO]):
@@ -35,8 +37,9 @@ class MessageHandler(Generic[INHERITS_MESSAGE_CO]):
35
37
  self.name = name
36
38
 
37
39
  def __call__(
38
- self, func: Callable[[Any, MessageOf[INHERITS_MESSAGE_CO]], Awaitable[None]]
39
- ) -> Callable[[Any, MessageOf[INHERITS_MESSAGE_CO]], Awaitable[None]]:
40
+ self,
41
+ func: Callable[[INSTANCE_T, MessageOf[INHERITS_MESSAGE_CO]], Awaitable[None]],
42
+ ) -> Callable[[INSTANCE_T, MessageOf[INHERITS_MESSAGE_CO]], Awaitable[None]]:
40
43
 
41
44
  MessageHandler[Any].register(func, self)
42
45
 
@@ -52,7 +55,7 @@ class MessageHandler(Generic[INHERITS_MESSAGE_CO]):
52
55
 
53
56
  @staticmethod
54
57
  def get_message_incoming(
55
- func: Callable[[MessageOf[Any]], Awaitable[Any]],
58
+ func: Callable[[Any, MessageOf[Any]], Awaitable[None]],
56
59
  ) -> "MessageHandler[Message] | None":
57
60
  if not hasattr(func, MessageHandler.MESSAGE_INCOMING_ATTR):
58
61
  return None
@@ -93,3 +93,49 @@ class SplitInputOutput:
93
93
  Check if the Pydantic model is marked for split interface generation.
94
94
  """
95
95
  return getattr(cls, SplitInputOutput.METADATA_KEY, False)
96
+
97
+
98
+ class ExposeType:
99
+ """
100
+ Decorator to explicitly expose types for TypeScript interface generation.
101
+
102
+ Use this decorator to include types in the generated TypeScript output without
103
+ needing them as request/response bodies or indirect dependencies.
104
+
105
+ Example:
106
+ @ExposeType()
107
+ class UserRole(BaseModel):
108
+ id: str
109
+ name: str
110
+
111
+ # This ensures UserRole interface is generated even if it's not
112
+ # directly referenced in any REST endpoint
113
+ """
114
+
115
+ METADATA_KEY = "__jararaca_expose_type__"
116
+ _exposed_types: set[type] = set()
117
+
118
+ def __init__(self) -> None:
119
+ pass
120
+
121
+ def __call__(self, cls: type[BASEMODEL_T]) -> type[BASEMODEL_T]:
122
+ """
123
+ Decorate the type to mark it for explicit TypeScript generation.
124
+ """
125
+ setattr(cls, self.METADATA_KEY, True)
126
+ ExposeType._exposed_types.add(cls)
127
+ return cls
128
+
129
+ @staticmethod
130
+ def is_exposed_type(cls: type) -> bool:
131
+ """
132
+ Check if the type is marked for explicit exposure.
133
+ """
134
+ return getattr(cls, ExposeType.METADATA_KEY, False)
135
+
136
+ @staticmethod
137
+ def get_all_exposed_types() -> set[type]:
138
+ """
139
+ Get all types that have been marked for explicit exposure.
140
+ """
141
+ return ExposeType._exposed_types.copy()
@@ -36,6 +36,7 @@ from jararaca.presentation.websocket.websocket_interceptor import (
36
36
  WebSocketMessageWrapper,
37
37
  )
38
38
  from jararaca.tools.typescript.decorators import (
39
+ ExposeType,
39
40
  MutationEndpoint,
40
41
  QueryEndpoint,
41
42
  SplitInputOutput,
@@ -661,6 +662,9 @@ def write_microservice_to_typescript_interface(
661
662
  websocket_registries: set[RegisterWebSocketMessage] = set()
662
663
  mapped_types_set.add(WebSocketMessageWrapper)
663
664
 
665
+ # Add all explicitly exposed types
666
+ mapped_types_set.update(ExposeType.get_all_exposed_types())
667
+
664
668
  for controller in microservice.controllers:
665
669
  rest_controller = RestController.get_controller(controller)
666
670
 
@@ -700,7 +704,7 @@ def write_microservice_to_typescript_interface(
700
704
 
701
705
  // noinspection JSUnusedGlobalSymbols
702
706
 
703
- import { HttpService, HttpBackend, HttpBackendRequest, ResponseType, createClassQueryHooks , createClassMutationHooks, createClassInfiniteQueryHooks, paginationModelByFirstArgPaginationFilter } from "@jararaca/core";
707
+ import { HttpService, HttpBackend, HttpBackendRequest, ResponseType, createClassQueryHooks , createClassMutationHooks, createClassInfiniteQueryHooks, paginationModelByFirstArgPaginationFilter, recursiveCamelToSnakeCase } from "@jararaca/core";
704
708
 
705
709
  function makeFormData(data: Record<string, any>): FormData {
706
710
  const formData = new FormData();
@@ -723,7 +727,9 @@ function* genFormDataValue(value: any): any {
723
727
  } else if (typeof value === "object" && value.constructor === Object) {
724
728
  // Stringify plain objects as JSON
725
729
  // formData.append(key, JSON.stringify(value));
726
- yield JSON.stringify(value);
730
+ yield JSON.stringify(
731
+ recursiveCamelToSnakeCase(value)
732
+ );
727
733
  } else {
728
734
  // For primitives (string, number, boolean), append as-is
729
735
  yield value;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jararaca
3
- Version: 0.3.14
3
+ Version: 0.3.16
4
4
  Summary: A simple and fast API framework for Python
5
5
  Home-page: https://github.com/LuscasLeo/jararaca
6
6
  Author: Lucas S
@@ -74,6 +74,7 @@ Jararaca is an async-first microservice framework designed to simplify the devel
74
74
  - Command-line tool for generating TypeScript types
75
75
  - Support for REST endpoints, WebSocket events, and message bus payloads
76
76
  - Type-safe frontend-backend communication
77
+ - **`@ExposeType` decorator** - Explicitly expose types for TypeScript generation without needing them in endpoints
77
78
 
78
79
  ### Hexagonal Architecture
79
80
  - Clear separation of concerns
@@ -1,7 +1,7 @@
1
1
  LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
- README.md,sha256=2qMM__t_MoLKZr4IY9tXjo-Jn6LKjuHMb1qbyXpgL08,3401
3
- pyproject.toml,sha256=IfsmxqO5BSGKKD-BllRRLcf9hrjv2Oxmup2_3dq5iWk,2057
4
- jararaca/__init__.py,sha256=qWLfavFIGRC3dfcTmiNP3kDh1Iih9wPMTTUOtLjtT_M,22186
2
+ README.md,sha256=YmCngjU8llW0l7L3tuXkkfr8qH7V9aBMgfp2jEzeiKg,3517
3
+ pyproject.toml,sha256=Qn_u7qKJbAetHjXlx0gwVX_gnib76NkIQd6zQMYfkro,2832
4
+ jararaca/__init__.py,sha256=IMnvfDoyNWTGVittF_wq2Uxtv_BY_wLN5Om6C3vUsCw,22302
5
5
  jararaca/__main__.py,sha256=-O3vsB5lHdqNFjUtoELDF81IYFtR-DSiiFMzRaiSsv4,67
6
6
  jararaca/broker_backend/__init__.py,sha256=GzEIuHR1xzgCJD4FE3harNjoaYzxHMHoEL0_clUaC-k,3528
7
7
  jararaca/broker_backend/mapper.py,sha256=vTsi7sWpNvlga1PWPFg0rCJ5joJ0cdzykkIc2Tuvenc,696
@@ -17,7 +17,7 @@ jararaca/lifecycle.py,sha256=qKlzLQQioS8QkxNJ_FC_5WbmT77cNbc_S7OcQeOoHkI,1895
17
17
  jararaca/messagebus/__init__.py,sha256=5jAqPqdcEMYBfQyfZDWPnplYdrfMyJLMcacf3qLyUhk,56
18
18
  jararaca/messagebus/bus_message_controller.py,sha256=Xd_qwnX5jUvgBTCarHR36fvtol9lPTsYp2IIGKyQQaE,1487
19
19
  jararaca/messagebus/consumers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- jararaca/messagebus/decorators.py,sha256=jkVkN-NNIefKjMGq0JcAjEprdoo04jZtHm7YMsja1to,5896
20
+ jararaca/messagebus/decorators.py,sha256=P5z0BBL4hJfgCJHKbsDhSZmlRwzV4PlCPKvEZ4sguXM,6013
21
21
  jararaca/messagebus/interceptors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
22
  jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py,sha256=_DEHwIH9LYsA26Hu1mo9oHzLZuATgjilU9E3o-ecDjs,6520
23
23
  jararaca/messagebus/interceptors/publisher_interceptor.py,sha256=ojy1bRhqMgrkQljcGGS8cd8-8pUjL8ZHjIUkdmaAnNM,1325
@@ -69,13 +69,13 @@ jararaca/tools/app_config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJW
69
69
  jararaca/tools/app_config/decorators.py,sha256=-ckkMZ1dswOmECdo1rFrZ15UAku--txaNXMp8fd1Ndk,941
70
70
  jararaca/tools/app_config/interceptor.py,sha256=HV8h4AxqUc_ACs5do4BSVlyxlRXzx7HqJtoVO9tfRnQ,2611
71
71
  jararaca/tools/typescript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
72
- jararaca/tools/typescript/decorators.py,sha256=y1zBq8mBZ8CBXlZ0nKy2RyIgCvP9kp4elACbaC6dptQ,2946
73
- jararaca/tools/typescript/interface_parser.py,sha256=iSkE6fEAdWejIOsiOy4xCxGv9HYs1BeaDZB7RzzwwCQ,54988
72
+ jararaca/tools/typescript/decorators.py,sha256=2NXFI6MiqpjM8rmrje38dR5StRlqdzYOmPASgyLIHeo,4267
73
+ jararaca/tools/typescript/interface_parser.py,sha256=yOSuOXKOeG0soGFo0fKiZIabu4YwnvIKk-Zss8UPAuE,55174
74
74
  jararaca/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
75
75
  jararaca/utils/rabbitmq_utils.py,sha256=ytdAFUyv-OBkaVnxezuJaJoLrmN7giZgtKeet_IsMBs,10918
76
76
  jararaca/utils/retry.py,sha256=DzPX_fXUvTqej6BQ8Mt2dvLo9nNlTBm7Kx2pFZ26P2Q,4668
77
- jararaca-0.3.14.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
78
- jararaca-0.3.14.dist-info/METADATA,sha256=_PnQKjQqxnI81jI9mjdmzzoEh_O1pB5PVd1dOVInuHM,5033
79
- jararaca-0.3.14.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
80
- jararaca-0.3.14.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
81
- jararaca-0.3.14.dist-info/RECORD,,
77
+ jararaca-0.3.16.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
78
+ jararaca-0.3.16.dist-info/METADATA,sha256=mIIZae49Gn54udR4m7qk3ZwTjx66iSQpgIp9Abm_Ymo,5149
79
+ jararaca-0.3.16.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
80
+ jararaca-0.3.16.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
81
+ jararaca-0.3.16.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "jararaca"
3
- version = "0.3.14"
3
+ version = "0.3.16"
4
4
  description = "A simple and fast API framework for Python"
5
5
  authors = ["Lucas S <me@luscasleo.dev>"]
6
6
  readme = "README.md"
@@ -62,6 +62,10 @@ mkdocs-mermaid2-plugin = "^1.2.1"
62
62
  [tool.poetry.group.dev.dependencies]
63
63
  httptools = "^0.6.1"
64
64
  httpx = "^0.27.2"
65
+ pytest = "^8.0.0"
66
+ pytest-asyncio = "^0.23.0"
67
+ pytest-cov = "^4.1.0"
68
+ pytest-mock = "^3.12.0"
65
69
 
66
70
  [build-system]
67
71
  requires = ["poetry-core"]
@@ -84,3 +88,39 @@ jararaca = "jararaca.cli:cli"
84
88
  [[tool.mypy.overrides]]
85
89
  module = "mako.*"
86
90
  ignore_missing_imports = true
91
+
92
+ [tool.pytest.ini_options]
93
+ testpaths = ["tests"]
94
+ python_files = ["test_*.py"]
95
+ python_classes = ["Test*"]
96
+ python_functions = ["test_*"]
97
+ asyncio_mode = "auto"
98
+ addopts = [
99
+ "--strict-markers",
100
+ "--strict-config",
101
+ "--showlocals",
102
+ ]
103
+ markers = [
104
+ "unit: Unit tests",
105
+ "integration: Integration tests",
106
+ "slow: Slow tests",
107
+ ]
108
+
109
+ [tool.coverage.run]
110
+ source = ["src/jararaca"]
111
+ omit = [
112
+ "*/tests/*",
113
+ "*/__pycache__/*",
114
+ "*/.venv/*",
115
+ ]
116
+
117
+ [tool.coverage.report]
118
+ exclude_lines = [
119
+ "pragma: no cover",
120
+ "def __repr__",
121
+ "raise AssertionError",
122
+ "raise NotImplementedError",
123
+ "if __name__ == .__main__.:",
124
+ "if TYPE_CHECKING:",
125
+ "@abstractmethod",
126
+ ]