jararaca 0.3.12a4__py3-none-any.whl → 0.3.12a6__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.

jararaca/__init__.py CHANGED
@@ -102,6 +102,8 @@ if TYPE_CHECKING:
102
102
  from .messagebus.publisher import use_publisher
103
103
  from .microservice import (
104
104
  Microservice,
105
+ is_shutting_down,
106
+ request_shutdown,
105
107
  use_app_context,
106
108
  use_app_transaction_context,
107
109
  use_app_tx_ctx_data,
@@ -158,6 +160,7 @@ if TYPE_CHECKING:
158
160
  from .presentation.websocket.websocket_interceptor import WebSocketInterceptor
159
161
  from .scheduler.decorators import ScheduledAction
160
162
  from .tools.app_config.interceptor import AppConfigurationInterceptor
163
+ from .tools.typescript.decorators import MutationEndpoint, QueryEndpoint
161
164
 
162
165
  __all__ = [
163
166
  "SetMetadata",
@@ -207,6 +210,12 @@ if TYPE_CHECKING:
207
210
  "QueryInjector",
208
211
  "HttpMicroservice",
209
212
  "use_current_container",
213
+ "use_app_context",
214
+ "use_app_transaction_context",
215
+ "use_app_tx_ctx_data",
216
+ "is_shutting_down",
217
+ "request_shutdown",
218
+ "Microservice",
210
219
  "T_BASEMODEL",
211
220
  "DatedEntity",
212
221
  "BaseEntity",
@@ -229,7 +238,6 @@ if TYPE_CHECKING:
229
238
  "MessageBusController",
230
239
  "MessageHandler",
231
240
  "ScheduledAction",
232
- "Microservice",
233
241
  "ProviderSpec",
234
242
  "Token",
235
243
  "AIOSqlAlchemySessionInterceptor",
@@ -254,6 +262,8 @@ if TYPE_CHECKING:
254
262
  "MessageBusPublisherInterceptor",
255
263
  "RedisWebSocketConnectionBackend",
256
264
  "AppConfigurationInterceptor",
265
+ "QueryEndpoint",
266
+ "MutationEndpoint",
257
267
  "UseMiddleware",
258
268
  "UseDependency",
259
269
  "GlobalHttpErrorHandler",
@@ -284,9 +294,6 @@ if TYPE_CHECKING:
284
294
  "RetryConfig",
285
295
  # Exception classes
286
296
  "TimeoutException",
287
- "use_app_context",
288
- "use_app_transaction_context",
289
- "use_app_tx_ctx_data",
290
297
  "AppTransactionContext",
291
298
  "AppContext",
292
299
  "ControllerMemberReflect",
@@ -469,6 +476,8 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
469
476
  "tools.app_config.interceptor",
470
477
  None,
471
478
  ),
479
+ "QueryEndpoint": (__SPEC_PARENT__, "tools.typescript.decorators", None),
480
+ "MutationEndpoint": (__SPEC_PARENT__, "tools.typescript.decorators", None),
472
481
  "UseMiddleware": (__SPEC_PARENT__, "presentation.decorators", None),
473
482
  "UseDependency": (__SPEC_PARENT__, "presentation.decorators", None),
474
483
  "GlobalHttpErrorHandler": (__SPEC_PARENT__, "rpc.http.decorators", None),
@@ -501,6 +510,8 @@ _dynamic_imports: "dict[str, tuple[str, str, str | None]]" = {
501
510
  "use_app_context": (__SPEC_PARENT__, "microservice", None),
502
511
  "use_app_transaction_context": (__SPEC_PARENT__, "microservice", None),
503
512
  "use_app_tx_ctx_data": (__SPEC_PARENT__, "microservice", None),
513
+ "is_shutting_down": (__SPEC_PARENT__, "microservice", None),
514
+ "request_shutdown": (__SPEC_PARENT__, "microservice", None),
504
515
  "AppContext": (__SPEC_PARENT__, "microservice", None),
505
516
  "AppInterceptor": (__SPEC_PARENT__, "microservice", None),
506
517
  "AppTransactionContext": (__SPEC_PARENT__, "microservice", None),
jararaca/cli.py CHANGED
@@ -5,6 +5,7 @@ import multiprocessing
5
5
  import os
6
6
  import sys
7
7
  import time
8
+ import traceback
8
9
  from codecs import StreamWriter
9
10
  from pathlib import Path
10
11
  from typing import Any, Callable
@@ -615,6 +616,7 @@ def generate_interfaces(
615
616
  return content
616
617
  except Exception as e:
617
618
  click.echo(f"Error generating TypeScript interfaces: {e}", file=sys.stderr)
619
+ traceback.print_exc(file=sys.stderr)
618
620
  return ""
619
621
 
620
622
 
@@ -49,6 +49,8 @@ from jararaca.microservice import (
49
49
  MessageBusTransactionData,
50
50
  Microservice,
51
51
  SchedulerTransactionData,
52
+ ShutdownState,
53
+ provide_shutdown_state,
52
54
  )
53
55
  from jararaca.scheduler.decorators import ScheduledActionData
54
56
  from jararaca.utils.rabbitmq_utils import RabbitmqUtils
@@ -129,6 +131,17 @@ class MessageBusConsumer(ABC):
129
131
  """Close all resources related to the consumer"""
130
132
 
131
133
 
134
+ class _WorkerShutdownState(ShutdownState):
135
+ def __init__(self, shutdown_event: asyncio.Event):
136
+ self.shutdown_event = shutdown_event
137
+
138
+ def request_shutdown(self) -> None:
139
+ self.shutdown_event.set()
140
+
141
+ def is_shutdown_requested(self) -> bool:
142
+ return self.shutdown_event.is_set()
143
+
144
+
132
145
  class AioPikaMicroserviceConsumer(MessageBusConsumer):
133
146
  def __init__(
134
147
  self,
@@ -146,6 +159,7 @@ class AioPikaMicroserviceConsumer(MessageBusConsumer):
146
159
  self.incoming_map: dict[str, MessageHandlerData] = {}
147
160
  self.uow_context_provider = uow_context_provider
148
161
  self.shutdown_event = asyncio.Event()
162
+ self.shutdown_state = _WorkerShutdownState(self.shutdown_event)
149
163
  self.lock = asyncio.Lock()
150
164
  self.tasks: set[asyncio.Task[Any]] = set()
151
165
  self.connection: aio_pika.abc.AbstractConnection | None = None
@@ -832,18 +846,19 @@ class ScheduledMessageHandlerCallback:
832
846
  args: tuple[Any, ...],
833
847
  kwargs: dict[str, Any],
834
848
  ) -> None:
835
- async with self.consumer.uow_context_provider(
836
- AppTransactionContext(
837
- controller_member_reflect=scheduled_action.controller_member,
838
- transaction_data=SchedulerTransactionData(
839
- scheduled_to=datetime.now(UTC),
840
- cron_expression=scheduled_action.spec.cron,
841
- triggered_at=datetime.now(UTC),
842
- ),
843
- )
844
- ):
849
+ with provide_shutdown_state(self.consumer.shutdown_state):
850
+ async with self.consumer.uow_context_provider(
851
+ AppTransactionContext(
852
+ controller_member_reflect=scheduled_action.controller_member,
853
+ transaction_data=SchedulerTransactionData(
854
+ scheduled_to=datetime.now(UTC),
855
+ cron_expression=scheduled_action.spec.cron,
856
+ triggered_at=datetime.now(UTC),
857
+ ),
858
+ )
859
+ ):
845
860
 
846
- await scheduled_action.callable(*args, **kwargs)
861
+ await scheduled_action.callable(*args, **kwargs)
847
862
 
848
863
 
849
864
  class MessageHandlerCallback:
@@ -1133,83 +1148,86 @@ class MessageHandlerCallback:
1133
1148
  incoming_message_spec = MessageHandler.get_message_incoming(handler)
1134
1149
  assert incoming_message_spec is not None
1135
1150
 
1136
- async with self.consumer.uow_context_provider(
1137
- AppTransactionContext(
1138
- controller_member_reflect=handler_data.controller_member,
1139
- transaction_data=MessageBusTransactionData(
1140
- message=builded_message,
1141
- topic=routing_key,
1142
- ),
1143
- )
1144
- ):
1145
- ctx: AsyncContextManager[Any]
1146
- if incoming_message_spec.timeout is not None:
1147
- ctx = asyncio.timeout(incoming_message_spec.timeout)
1148
- else:
1149
- ctx = none_context()
1150
- async with ctx:
1151
- try:
1152
- with provide_bus_message_controller(
1153
- AioPikaMessageBusController(aio_pika_message)
1154
- ):
1155
- await handler(builded_message)
1156
- if not incoming_message_spec.auto_ack:
1157
- with suppress(aio_pika.MessageProcessError):
1158
- # Use channel context for acknowledgement
1159
- async with self.consumer.get_channel_ctx(self.queue_name):
1160
- await aio_pika_message.ack()
1161
- except BaseException as base_exc:
1162
- # Get message id for logging
1163
- message_id = aio_pika_message.message_id or str(uuid.uuid4())
1164
-
1165
- # Extract retry count from headers if available
1166
- headers = aio_pika_message.headers or {}
1167
- retry_count = int(str(headers.get("x-retry-count", 0)))
1168
-
1169
- # Process exception handler if configured
1170
- if incoming_message_spec.exception_handler is not None:
1171
- try:
1172
- incoming_message_spec.exception_handler(base_exc)
1173
- except Exception as nested_exc:
1151
+ with provide_shutdown_state(self.consumer.shutdown_state):
1152
+ async with self.consumer.uow_context_provider(
1153
+ AppTransactionContext(
1154
+ controller_member_reflect=handler_data.controller_member,
1155
+ transaction_data=MessageBusTransactionData(
1156
+ message=builded_message,
1157
+ topic=routing_key,
1158
+ ),
1159
+ )
1160
+ ):
1161
+ ctx: AsyncContextManager[Any]
1162
+ if incoming_message_spec.timeout is not None:
1163
+ ctx = asyncio.timeout(incoming_message_spec.timeout)
1164
+ else:
1165
+ ctx = none_context()
1166
+ async with ctx:
1167
+ try:
1168
+ with provide_bus_message_controller(
1169
+ AioPikaMessageBusController(aio_pika_message)
1170
+ ):
1171
+ await handler(builded_message)
1172
+ if not incoming_message_spec.auto_ack:
1173
+ with suppress(aio_pika.MessageProcessError):
1174
+ # Use channel context for acknowledgement
1175
+ async with self.consumer.get_channel_ctx(
1176
+ self.queue_name
1177
+ ):
1178
+ await aio_pika_message.ack()
1179
+ except BaseException as base_exc:
1180
+ # Get message id for logging
1181
+ message_id = aio_pika_message.message_id or str(uuid.uuid4())
1182
+
1183
+ # Extract retry count from headers if available
1184
+ headers = aio_pika_message.headers or {}
1185
+ retry_count = int(str(headers.get("x-retry-count", 0)))
1186
+
1187
+ # Process exception handler if configured
1188
+ if incoming_message_spec.exception_handler is not None:
1189
+ try:
1190
+ incoming_message_spec.exception_handler(base_exc)
1191
+ except Exception as nested_exc:
1192
+ logger.exception(
1193
+ f"Error processing exception handler for message {message_id}: {base_exc} | {nested_exc}"
1194
+ )
1195
+ else:
1174
1196
  logger.exception(
1175
- f"Error processing exception handler for message {message_id}: {base_exc} | {nested_exc}"
1197
+ f"Error processing message {message_id} on topic {routing_key}: {str(base_exc)}"
1176
1198
  )
1177
- else:
1178
- logger.exception(
1179
- f"Error processing message {message_id} on topic {routing_key}: {str(base_exc)}"
1180
- )
1181
1199
 
1182
- # Handle rejection with retry logic
1183
- if incoming_message_spec.requeue_on_exception:
1184
- # Use our retry with backoff mechanism
1185
- await self.handle_reject_message(
1186
- aio_pika_message,
1187
- requeue=False, # Don't requeue directly, use our backoff mechanism
1188
- retry_count=retry_count,
1189
- exception=base_exc,
1190
- )
1191
- else:
1192
- # Message shouldn't be retried, reject it
1193
- await self.handle_reject_message(
1194
- aio_pika_message, requeue=False, exception=base_exc
1195
- )
1196
- else:
1197
- # Message processed successfully, log and clean up any retry state
1198
- message_id = aio_pika_message.message_id or str(uuid.uuid4())
1199
- if message_id in self.retry_state:
1200
- del self.retry_state[message_id]
1201
-
1202
- # Log success with retry information if applicable
1203
- headers = aio_pika_message.headers or {}
1204
- if "x-retry-count" in headers:
1205
- retry_count = int(str(headers.get("x-retry-count", 0)))
1206
- logger.info(
1207
- f"Message {message_id}#{self.queue_name} processed successfully after {retry_count} retries"
1208
- )
1200
+ # Handle rejection with retry logic
1201
+ if incoming_message_spec.requeue_on_exception:
1202
+ # Use our retry with backoff mechanism
1203
+ await self.handle_reject_message(
1204
+ aio_pika_message,
1205
+ requeue=False, # Don't requeue directly, use our backoff mechanism
1206
+ retry_count=retry_count,
1207
+ exception=base_exc,
1208
+ )
1209
+ else:
1210
+ # Message shouldn't be retried, reject it
1211
+ await self.handle_reject_message(
1212
+ aio_pika_message, requeue=False, exception=base_exc
1213
+ )
1209
1214
  else:
1210
- logger.info(
1211
- f"Message {message_id}#{self.queue_name} processed successfully"
1212
- )
1215
+ # Message processed successfully, log and clean up any retry state
1216
+ message_id = aio_pika_message.message_id or str(uuid.uuid4())
1217
+ if message_id in self.retry_state:
1218
+ del self.retry_state[message_id]
1219
+
1220
+ # Log success with retry information if applicable
1221
+ headers = aio_pika_message.headers or {}
1222
+ if "x-retry-count" in headers:
1223
+ retry_count = int(str(headers.get("x-retry-count", 0)))
1224
+ logger.info(
1225
+ f"Message {message_id}#{self.queue_name} processed successfully after {retry_count} retries"
1226
+ )
1227
+ else:
1228
+ logger.info(
1229
+ f"Message {message_id}#{self.queue_name} processed successfully"
1230
+ )
1213
1231
 
1214
1232
 
1215
1233
  @asynccontextmanager
jararaca/microservice.py CHANGED
@@ -325,6 +325,48 @@ def provide_container(container: Container) -> Generator[None, None, None]:
325
325
  current_container_ctx.reset(token)
326
326
 
327
327
 
328
+ class ShutdownState(Protocol):
329
+
330
+ def request_shutdown(self) -> None: ...
331
+
332
+ def is_shutdown_requested(self) -> bool: ...
333
+
334
+
335
+ shutdown_state_ctx = ContextVar[ShutdownState]("shutdown_state")
336
+
337
+
338
+ def is_shutting_down() -> bool:
339
+ """
340
+ Check if the application is in the process of shutting down.
341
+ """
342
+ return shutdown_state_ctx.get().is_shutdown_requested()
343
+
344
+
345
+ def request_shutdown() -> None:
346
+ """
347
+ Request the application to shut down.
348
+ This will set the shutdown event, allowing the application to gracefully shut down.
349
+ """
350
+ shutdown_state_ctx.get().request_shutdown()
351
+
352
+
353
+ @contextmanager
354
+ def provide_shutdown_state(
355
+ state: ShutdownState,
356
+ ) -> Generator[None, None, None]:
357
+ """
358
+ Context manager to provide the shutdown state.
359
+ This is used to manage the shutdown event for the application.
360
+ """
361
+
362
+ token = shutdown_state_ctx.set(state)
363
+ try:
364
+ yield
365
+ finally:
366
+ with suppress(ValueError):
367
+ shutdown_state_ctx.reset(token)
368
+
369
+
328
370
  __all__ = [
329
371
  "AppTransactionContext",
330
372
  "AppInterceptor",
@@ -1,4 +1,8 @@
1
+ import os
2
+ import signal
3
+ import threading
1
4
  from contextlib import asynccontextmanager
5
+ from signal import SIGINT, SIGTERM
2
6
  from typing import Any, AsyncGenerator
3
7
 
4
8
  from fastapi import Depends, FastAPI, Request, WebSocket
@@ -10,7 +14,9 @@ from jararaca.lifecycle import AppLifecycle
10
14
  from jararaca.microservice import (
11
15
  AppTransactionContext,
12
16
  HttpTransactionData,
17
+ ShutdownState,
13
18
  WebSocketTransactionData,
19
+ provide_shutdown_state,
14
20
  )
15
21
  from jararaca.presentation.decorators import RestController
16
22
  from jararaca.presentation.http_microservice import HttpMicroservice
@@ -76,10 +82,49 @@ class HttpAppLifecycle:
76
82
  yield
77
83
 
78
84
 
85
+ class HttpShutdownState(ShutdownState):
86
+ def __init__(self) -> None:
87
+ self._requested = False
88
+ self.old_signal_handlers = {
89
+ SIGINT: signal.getsignal(SIGINT),
90
+ SIGTERM: signal.getsignal(SIGTERM),
91
+ }
92
+ self.thread_lock = threading.Lock()
93
+
94
+ def request_shutdown(self) -> None:
95
+ if not self._requested:
96
+ self._requested = True
97
+ os.kill(os.getpid(), SIGINT)
98
+
99
+ def is_shutdown_requested(self) -> bool:
100
+ return self._requested
101
+
102
+ def handle_signal(self, signum: int, frame: Any) -> None:
103
+ print(f"Received signal {signum}, initiating shutdown...")
104
+ if self._requested:
105
+ print("Shutdown already requested, ignoring signal.")
106
+ return
107
+ print("Requesting shutdown...")
108
+ self._requested = True
109
+
110
+ # remove the signal handler to prevent recursion
111
+ for sig in (SIGINT, SIGTERM):
112
+ if self.old_signal_handlers[sig] is not None:
113
+ signal.signal(sig, self.old_signal_handlers[sig])
114
+
115
+ signal.raise_signal(signum)
116
+
117
+ def setup_signal_handlers(self) -> None:
118
+ signal.signal(SIGINT, self.handle_signal)
119
+ signal.signal(SIGTERM, self.handle_signal)
120
+
121
+
79
122
  class HttpUowContextProviderDependency:
80
123
 
81
124
  def __init__(self, uow_provider: UnitOfWorkContextProvider) -> None:
82
125
  self.uow_provider = uow_provider
126
+ self.shutdown_state = HttpShutdownState()
127
+ self.shutdown_state.setup_signal_handlers()
83
128
 
84
129
  async def __call__(
85
130
  self, websocket: WebSocket = None, request: Request = None # type: ignore
@@ -101,17 +146,18 @@ class HttpUowContextProviderDependency:
101
146
  "ControllerMemberReflect, but got: {}".format(type(member))
102
147
  )
103
148
 
104
- async with self.uow_provider(
105
- AppTransactionContext(
106
- controller_member_reflect=member,
107
- transaction_data=(
108
- HttpTransactionData(request=request)
109
- if request
110
- else WebSocketTransactionData(websocket=websocket)
111
- ),
112
- )
113
- ):
114
- yield
149
+ with provide_shutdown_state(self.shutdown_state):
150
+ async with self.uow_provider(
151
+ AppTransactionContext(
152
+ controller_member_reflect=member,
153
+ transaction_data=(
154
+ HttpTransactionData(request=request)
155
+ if request
156
+ else WebSocketTransactionData(websocket=websocket)
157
+ ),
158
+ )
159
+ ):
160
+ yield
115
161
 
116
162
 
117
163
  def create_http_server(
File without changes
@@ -0,0 +1,51 @@
1
+ from typing import Any, Callable, TypeVar
2
+
3
+ DECORATED_FUNC = TypeVar("DECORATED_FUNC", bound=Callable[..., Any])
4
+
5
+
6
+ class QueryEndpoint:
7
+ """
8
+ Decorator to mark a endpoint function as a query endpoint for Typescript generation.
9
+ """
10
+
11
+ METADATA_KEY = "__jararaca_query_endpoint__"
12
+
13
+ def __init__(self) -> None: ...
14
+
15
+ def __call__(self, func: DECORATED_FUNC) -> DECORATED_FUNC:
16
+ """
17
+ Decorate the function to mark it as a query endpoint.
18
+ """
19
+ setattr(func, self.METADATA_KEY, True)
20
+ return func
21
+
22
+ @staticmethod
23
+ def is_query(func: Any) -> bool:
24
+ """
25
+ Check if the function is marked as a query endpoint.
26
+ """
27
+ return getattr(func, QueryEndpoint.METADATA_KEY, False)
28
+
29
+
30
+ class MutationEndpoint:
31
+ """
32
+ Decorator to mark a endpoint function as a mutation endpoint for Typescript generation.
33
+ """
34
+
35
+ METADATA_KEY = "__jararaca_mutation_endpoint__"
36
+
37
+ def __init__(self) -> None: ...
38
+
39
+ def __call__(self, func: DECORATED_FUNC) -> DECORATED_FUNC:
40
+ """
41
+ Decorate the function to mark it as a mutation endpoint.
42
+ """
43
+ setattr(func, self.METADATA_KEY, True)
44
+ return func
45
+
46
+ @staticmethod
47
+ def is_mutation(func: Any) -> bool:
48
+ """
49
+ Check if the function is marked as a mutation endpoint.
50
+ """
51
+ return getattr(func, MutationEndpoint.METADATA_KEY, False)
@@ -8,7 +8,7 @@ from datetime import date, datetime, time
8
8
  from decimal import Decimal
9
9
  from enum import Enum
10
10
  from io import StringIO
11
- from types import NoneType, UnionType
11
+ from types import FunctionType, NoneType, UnionType
12
12
  from typing import (
13
13
  IO,
14
14
  Annotated,
@@ -35,6 +35,7 @@ from jararaca.presentation.websocket.decorators import RegisterWebSocketMessage
35
35
  from jararaca.presentation.websocket.websocket_interceptor import (
36
36
  WebSocketMessageWrapper,
37
37
  )
38
+ from jararaca.tools.typescript.decorators import MutationEndpoint, QueryEndpoint
38
39
 
39
40
  CONSTANT_PATTERN = re.compile(r"^[A-Z_]+$")
40
41
 
@@ -67,6 +68,13 @@ def snake_to_camel(snake_str: str) -> str:
67
68
  return components[0] + "".join(x.title() for x in components[1:])
68
69
 
69
70
 
71
+ def pascal_to_camel(pascal_str: str) -> str:
72
+ """Convert a PascalCase string to camelCase."""
73
+ if not pascal_str:
74
+ return pascal_str
75
+ return pascal_str[0].lower() + pascal_str[1:]
76
+
77
+
70
78
  def parse_literal_value(value: Any) -> str:
71
79
  if value is None:
72
80
  return "null"
@@ -354,12 +362,17 @@ def write_microservice_to_typescript_interface(
354
362
  if rest_controller is None:
355
363
  continue
356
364
 
357
- controller_class_str, types = write_rest_controller_to_typescript_interface(
358
- rest_controller, controller
365
+ controller_class_strio, types, hooks_strio = (
366
+ write_rest_controller_to_typescript_interface(
367
+ rest_controller,
368
+ controller,
369
+ )
359
370
  )
360
371
 
361
372
  mapped_types_set.update(types)
362
- rest_controller_buffer.write(controller_class_str)
373
+ rest_controller_buffer.write(controller_class_strio.getvalue())
374
+ if hooks_strio is not None:
375
+ rest_controller_buffer.write(hooks_strio.getvalue())
363
376
 
364
377
  registered = RegisterWebSocketMessage.get(controller)
365
378
 
@@ -376,6 +389,7 @@ def write_microservice_to_typescript_interface(
376
389
 
377
390
  final_buffer.write(
378
391
  """
392
+ import { createClassQueryHooks , createClassMutationHooks, createClassInfiniteQueryHooks } from "@jararaca/core";
379
393
  export type WebSocketMessageMap = {
380
394
  %s
381
395
  }
@@ -494,11 +508,16 @@ def is_primitive(field_type: Any) -> bool:
494
508
 
495
509
  def write_rest_controller_to_typescript_interface(
496
510
  rest_controller: RestController, controller: type
497
- ) -> tuple[str, set[Any]]:
511
+ ) -> tuple[StringIO, set[Any], StringIO | None]:
512
+
513
+ class_name = controller.__name__
514
+
515
+ decorated_queries: list[tuple[str, FunctionType]] = []
516
+ decorated_mutations: list[tuple[str, FunctionType]] = []
498
517
 
499
518
  class_buffer = StringIO()
500
519
 
501
- class_buffer.write(f"export class {controller.__name__} extends HttpService {{\n")
520
+ class_buffer.write(f"export class {class_name} extends HttpService {{\n")
502
521
 
503
522
  mapped_types: set[Any] = set()
504
523
 
@@ -514,6 +533,11 @@ def write_rest_controller_to_typescript_interface(
514
533
  if return_type is None:
515
534
  return_type = NoneType
516
535
 
536
+ if QueryEndpoint.is_query(member):
537
+ decorated_queries.append((name, member))
538
+ if MutationEndpoint.is_mutation(member):
539
+ decorated_mutations.append((name, member))
540
+
517
541
  mapped_types.update(extract_all_envolved_types(return_type))
518
542
 
519
543
  return_value_repr = get_field_type_for_ts(return_type)
@@ -588,7 +612,31 @@ def write_rest_controller_to_typescript_interface(
588
612
 
589
613
  class_buffer.write("}\n")
590
614
 
591
- return class_buffer.getvalue(), mapped_types
615
+ controller_hooks_builder: StringIO | None = None
616
+
617
+ if decorated_queries or decorated_mutations:
618
+ controller_hooks_builder = StringIO()
619
+ controller_hooks_builder.write(
620
+ f"export const {pascal_to_camel(class_name)} = {{\n"
621
+ )
622
+
623
+ if decorated_queries:
624
+ controller_hooks_builder.write(
625
+ f"\t...createClassQueryHooks({class_name},\n"
626
+ )
627
+ for name, member in decorated_queries:
628
+ controller_hooks_builder.write(f'\t\t"{snake_to_camel(name)}",\n')
629
+ controller_hooks_builder.write("\t),\n")
630
+ if decorated_mutations:
631
+ controller_hooks_builder.write(
632
+ f"\t...createClassMutationHooks({class_name},\n"
633
+ )
634
+ for name, member in decorated_mutations:
635
+ controller_hooks_builder.write(f'\t\t"{snake_to_camel(name)}",\n')
636
+ controller_hooks_builder.write("\t),\n")
637
+ controller_hooks_builder.write("};\n")
638
+
639
+ return class_buffer, mapped_types, controller_hooks_builder
592
640
 
593
641
 
594
642
  EXCLUDED_REQUESTS_TYPES = [Request, Response]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jararaca
3
- Version: 0.3.12a4
3
+ Version: 0.3.12a6
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
@@ -1,12 +1,12 @@
1
1
  LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
2
  README.md,sha256=2qMM__t_MoLKZr4IY9tXjo-Jn6LKjuHMb1qbyXpgL08,3401
3
- pyproject.toml,sha256=52W7n20RKPz82FFphOmYyMWBVaYSyaAsIDUYwh9YACQ,2040
4
- jararaca/__init__.py,sha256=Jszgq9WCKnriBezBtvhj35aSsJWnTUXm9-UfThkDT3s,20701
3
+ pyproject.toml,sha256=3e1dOSeKg1pARRUHFxJ0d5wOPiGtoe4YiAqf3VngI6o,2040
4
+ jararaca/__init__.py,sha256=jN3paW2ujI97485DNZTcRe_8ORwO-OQbJUG5mmSI9LI,21226
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
8
8
  jararaca/broker_backend/redis_broker_backend.py,sha256=a7DHchy3NAiD71Ix8SwmQOUnniu7uup-Woa4ON_4J7I,5786
9
- jararaca/cli.py,sha256=uLwLwM7OSjx2OoIczOqjq7fDBwsoq5vdGzdvXQobs5Q,31943
9
+ jararaca/cli.py,sha256=n3fTOVSNFNICmbZrLCJNGctpbqLWp39xTSkqlIB6Rds,32005
10
10
  jararaca/common/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  jararaca/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
12
  jararaca/core/providers.py,sha256=wktH84FK7c1s2wNq-fudf1uMfi3CQBR0neU2czJ_L0U,434
@@ -23,8 +23,8 @@ jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py,sha256=_DEHwIH
23
23
  jararaca/messagebus/interceptors/publisher_interceptor.py,sha256=ojy1bRhqMgrkQljcGGS8cd8-8pUjL8ZHjIUkdmaAnNM,1325
24
24
  jararaca/messagebus/message.py,sha256=U6cyd2XknX8mtm0333slz5fanky2PFLWCmokAO56vvU,819
25
25
  jararaca/messagebus/publisher.py,sha256=JTkxdKbvxvDWT8nK8PVEyyX061vYYbKQMxRHXrZtcEY,2173
26
- jararaca/messagebus/worker.py,sha256=CrSIejWMGII4_JK0aH4jxdj0oBJX4hSXY0SmVa6KURA,54187
27
- jararaca/microservice.py,sha256=rRIimfeP2-wf289PKoUbk9wrSdA0ga_qWz5JNgQ5IE0,9667
26
+ jararaca/messagebus/worker.py,sha256=736rl6mBVxo-w5Y-jJe1w1ORPgVk16jlmTyVJueeAKs,55156
27
+ jararaca/microservice.py,sha256=4uQWPH1Ytxl2tg4IK-M_6T-28X89ku4ObJXJyg9CDr4,10663
28
28
  jararaca/observability/decorators.py,sha256=MOIr2PttPYYvRwEdfQZEwD5RxKHOTv8UEy9n1YQVoKw,2281
29
29
  jararaca/observability/interceptor.py,sha256=U4ZLM0f8j6Q7gMUKKnA85bnvD-Qa0ii79Qa_X8KsXAQ,1498
30
30
  jararaca/observability/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -40,7 +40,7 @@ jararaca/presentation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3h
40
40
  jararaca/presentation/decorators.py,sha256=dNcK1xYWhdHVBeWDa8dhqh4z8yQcPkJZTQkfgLXz6RI,11655
41
41
  jararaca/presentation/hooks.py,sha256=WBbU5DG3-MAm2Ro2YraQyYG_HENfizYfyShL2ktHi6k,1980
42
42
  jararaca/presentation/http_microservice.py,sha256=g771JosV6jTY3hQtG-HkLOo-T0e-r3b3rp1ddt99Qf0,533
43
- jararaca/presentation/server.py,sha256=5joE17nnSiwR497Nerr-vcthKL4NRBzyf1KWM8DFgwQ,4682
43
+ jararaca/presentation/server.py,sha256=Im-tei9EynKm1DO-63UIGeTJK5ZvxIhpAxbsYGn7ZW0,6244
44
44
  jararaca/presentation/websocket/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
45
45
  jararaca/presentation/websocket/base_types.py,sha256=AvUeeZ1TFhSiRMcYqZU1HaQNqSrcgTkC5R0ArP5dGmA,146
46
46
  jararaca/presentation/websocket/context.py,sha256=A6K5W3kqo9Hgeh1m6JiI7Cdz5SfbXcaICSVX7u1ARZo,1903
@@ -66,12 +66,14 @@ jararaca/scheduler/types.py,sha256=4HEQOmVIDp-BYLSzqmqSFIio1bd51WFmgFPIzPpVu04,1
66
66
  jararaca/tools/app_config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
67
67
  jararaca/tools/app_config/decorators.py,sha256=-ckkMZ1dswOmECdo1rFrZ15UAku--txaNXMp8fd1Ndk,941
68
68
  jararaca/tools/app_config/interceptor.py,sha256=HV8h4AxqUc_ACs5do4BSVlyxlRXzx7HqJtoVO9tfRnQ,2611
69
- jararaca/tools/typescript/interface_parser.py,sha256=foZ_A_lkyacmAq9TU6hUFv5ZnZcPZbbxcjtzFUu1A_o,31479
69
+ jararaca/tools/typescript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
70
+ jararaca/tools/typescript/decorators.py,sha256=F-4de0OKN4VrXCwtfsts1XaycGkXqSGuDKW6ElryMsY,1409
71
+ jararaca/tools/typescript/interface_parser.py,sha256=dc-h74yaySqXklqIDXuIQUN9wwqEE7q4uGWQ4nByI84,33462
70
72
  jararaca/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
71
73
  jararaca/utils/rabbitmq_utils.py,sha256=ytdAFUyv-OBkaVnxezuJaJoLrmN7giZgtKeet_IsMBs,10918
72
74
  jararaca/utils/retry.py,sha256=DzPX_fXUvTqej6BQ8Mt2dvLo9nNlTBm7Kx2pFZ26P2Q,4668
73
- jararaca-0.3.12a4.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
74
- jararaca-0.3.12a4.dist-info/METADATA,sha256=ZhivHS3NMe_L-0vt0uS_jilL4cSNkPGUtGLhX4ygD7U,4995
75
- jararaca-0.3.12a4.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
76
- jararaca-0.3.12a4.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
77
- jararaca-0.3.12a4.dist-info/RECORD,,
75
+ jararaca-0.3.12a6.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
76
+ jararaca-0.3.12a6.dist-info/METADATA,sha256=XPCPH0zrpKaicneY3XJSnwbgVvLlDlS4kfSWblCorWE,4995
77
+ jararaca-0.3.12a6.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
78
+ jararaca-0.3.12a6.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
79
+ jararaca-0.3.12a6.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "jararaca"
3
- version = "0.3.12a4"
3
+ version = "0.3.12a6"
4
4
  description = "A simple and fast API framework for Python"
5
5
  authors = ["Lucas S <me@luscasleo.dev>"]
6
6
  readme = "README.md"