jararaca 0.3.14__tar.gz → 0.3.16__tar.gz
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-0.3.14 → jararaca-0.3.16}/PKG-INFO +2 -1
- {jararaca-0.3.14 → jararaca-0.3.16}/README.md +1 -0
- jararaca-0.3.16/docs/expose-type.md +221 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/pyproject.toml +41 -1
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/__init__.py +3 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/decorators.py +6 -3
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/tools/typescript/decorators.py +46 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/tools/typescript/interface_parser.py +8 -2
- {jararaca-0.3.14 → jararaca-0.3.16}/LICENSE +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/CNAME +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/architecture.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.jpeg +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/assets/_f04774c9-7e05-4da4-8b17-8be23f6a1475.webp +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/assets/tracing_example.png +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/http-rpc.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/index.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/interceptors.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/messagebus.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/retry.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/scheduler.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/stylesheets/custom.css +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/docs/websocket.md +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/__main__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/broker_backend/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/broker_backend/mapper.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/broker_backend/redis_broker_backend.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/cli.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/common/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/core/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/core/providers.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/core/uow.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/di.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/files/entity.py.mako +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/lifecycle.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/bus_message_controller.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/consumers/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/interceptors/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/interceptors/aiopika_publisher_interceptor.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/interceptors/publisher_interceptor.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/message.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/publisher.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/worker.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/microservice.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/observability/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/observability/interceptor.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/observability/providers/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/observability/providers/otel.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/base.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/exports.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/interceptors/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/interceptors/aiosqa_interceptor.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/interceptors/constants.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/interceptors/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/session.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/sort_filter.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/utilities.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/hooks.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/http_microservice.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/server.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/base_types.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/context.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/redis.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/types.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/websocket_interceptor.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/py.typed +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/reflect/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/reflect/controller_inspect.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/reflect/metadata.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/http/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/http/backends/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/http/backends/httpx.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/http/backends/otel.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/http/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/rpc/http/httpx.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/scheduler/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/scheduler/beat_worker.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/scheduler/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/scheduler/types.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/tools/app_config/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/tools/app_config/decorators.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/tools/app_config/interceptor.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/tools/typescript/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/utils/__init__.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/utils/rabbitmq_utils.py +0 -0
- {jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/utils/retry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: jararaca
|
|
3
|
-
Version: 0.3.
|
|
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
|
|
@@ -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
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
# Exposing Types for TypeScript Generation
|
|
2
|
+
|
|
3
|
+
The `@ExposeType` decorator allows you to explicitly expose types for TypeScript interface generation without requiring them to be directly referenced in REST endpoints, WebSocket messages, or as indirect dependencies.
|
|
4
|
+
|
|
5
|
+
## Use Cases
|
|
6
|
+
|
|
7
|
+
The `@ExposeType` decorator is useful when you have types that:
|
|
8
|
+
|
|
9
|
+
1. **Are used only on the frontend** - Types that exist for frontend state management, validation, or business logic
|
|
10
|
+
2. **Are part of a shared schema** - Common types that multiple parts of your application use but aren't directly in API contracts
|
|
11
|
+
3. **Need to be pre-generated** - Types that you want available immediately even if they're not yet used in any endpoints
|
|
12
|
+
4. **Are utility types** - Helper types, enums, or constants that the frontend needs to know about
|
|
13
|
+
|
|
14
|
+
## Basic Usage
|
|
15
|
+
|
|
16
|
+
Simply decorate any Pydantic model with `@ExposeType()`:
|
|
17
|
+
|
|
18
|
+
```python
|
|
19
|
+
from pydantic import BaseModel
|
|
20
|
+
|
|
21
|
+
from jararaca import ExposeType
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@ExposeType()
|
|
25
|
+
class UserPermission(BaseModel):
|
|
26
|
+
id: str
|
|
27
|
+
name: str
|
|
28
|
+
description: str
|
|
29
|
+
resource: str
|
|
30
|
+
action: str
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
This type will now be included in the generated TypeScript output when you run:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
jararaca gen-tsi app:app output.ts
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Example: Frontend-Only Types
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from pydantic import BaseModel
|
|
43
|
+
|
|
44
|
+
from jararaca import ExposeType
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@ExposeType()
|
|
48
|
+
class FilterState(BaseModel):
|
|
49
|
+
"""Frontend state for table filtering."""
|
|
50
|
+
search_query: str
|
|
51
|
+
sort_column: str
|
|
52
|
+
sort_direction: str
|
|
53
|
+
page: int
|
|
54
|
+
page_size: int
|
|
55
|
+
|
|
56
|
+
@ExposeType()
|
|
57
|
+
class UITheme(BaseModel):
|
|
58
|
+
"""Frontend theme configuration."""
|
|
59
|
+
primary_color: str
|
|
60
|
+
secondary_color: str
|
|
61
|
+
dark_mode: bool
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Example: Error Codes and Constants
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from enum import Enum
|
|
68
|
+
|
|
69
|
+
from pydantic import BaseModel
|
|
70
|
+
|
|
71
|
+
from jararaca import ExposeType
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@ExposeType()
|
|
75
|
+
class ErrorCode(str, Enum):
|
|
76
|
+
"""Standard error codes."""
|
|
77
|
+
UNAUTHORIZED = "UNAUTHORIZED"
|
|
78
|
+
NOT_FOUND = "NOT_FOUND"
|
|
79
|
+
VALIDATION_ERROR = "VALIDATION_ERROR"
|
|
80
|
+
INTERNAL_ERROR = "INTERNAL_ERROR"
|
|
81
|
+
|
|
82
|
+
@ExposeType()
|
|
83
|
+
class ApiErrorDetail(BaseModel):
|
|
84
|
+
"""Detailed error information."""
|
|
85
|
+
code: ErrorCode
|
|
86
|
+
message: str
|
|
87
|
+
field: str | None = None
|
|
88
|
+
details: dict[str, str] | None = None
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Example: Complex Nested Types
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from pydantic import BaseModel
|
|
95
|
+
|
|
96
|
+
from jararaca import ExposeType
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
@ExposeType()
|
|
100
|
+
class Address(BaseModel):
|
|
101
|
+
street: str
|
|
102
|
+
city: str
|
|
103
|
+
country: str
|
|
104
|
+
postal_code: str
|
|
105
|
+
|
|
106
|
+
@ExposeType()
|
|
107
|
+
class ContactInfo(BaseModel):
|
|
108
|
+
email: str
|
|
109
|
+
phone: str | None = None
|
|
110
|
+
address: Address
|
|
111
|
+
|
|
112
|
+
@ExposeType()
|
|
113
|
+
class Organization(BaseModel):
|
|
114
|
+
"""Complete organization structure."""
|
|
115
|
+
id: str
|
|
116
|
+
name: str
|
|
117
|
+
contacts: list[ContactInfo]
|
|
118
|
+
settings: dict[str, str]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
When you expose a type with nested structures, the decorator ensures that all related types are also included in the TypeScript generation.
|
|
122
|
+
|
|
123
|
+
## Comparison: With vs Without @ExposeType
|
|
124
|
+
|
|
125
|
+
### Without @ExposeType
|
|
126
|
+
|
|
127
|
+
```python
|
|
128
|
+
class UserRole(BaseModel):
|
|
129
|
+
"""Only generated if used in an endpoint or as a dependency."""
|
|
130
|
+
id: str
|
|
131
|
+
name: str
|
|
132
|
+
|
|
133
|
+
@RestController("/api/users")
|
|
134
|
+
class UserController:
|
|
135
|
+
@Get("/{user_id}")
|
|
136
|
+
async def get_user(self, user_id: str) -> UserResponse:
|
|
137
|
+
# UserRole is only generated if UserResponse references it
|
|
138
|
+
return UserResponse(...)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### With @ExposeType
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
@ExposeType()
|
|
145
|
+
class UserRole(BaseModel):
|
|
146
|
+
"""Always generated, available immediately."""
|
|
147
|
+
id: str
|
|
148
|
+
name: str
|
|
149
|
+
|
|
150
|
+
@RestController("/api/users")
|
|
151
|
+
class UserController:
|
|
152
|
+
@Get("/{user_id}")
|
|
153
|
+
async def get_user(self, user_id: str) -> UserResponse:
|
|
154
|
+
# UserRole is available in TypeScript even if not used yet
|
|
155
|
+
return UserResponse(...)
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Integration with Other Decorators
|
|
159
|
+
|
|
160
|
+
The `@ExposeType` decorator works seamlessly with other TypeScript generation decorators:
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
from jararaca import ExposeType, SplitInputOutput
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@ExposeType()
|
|
167
|
+
@SplitInputOutput()
|
|
168
|
+
class UserProfile(BaseModel):
|
|
169
|
+
"""Generates UserProfileInput and UserProfileOutput interfaces."""
|
|
170
|
+
id: str
|
|
171
|
+
username: str
|
|
172
|
+
email: str
|
|
173
|
+
created_at: str
|
|
174
|
+
updated_at: str
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
This creates both `UserProfileInput` and `UserProfileOutput` TypeScript interfaces.
|
|
178
|
+
|
|
179
|
+
## Best Practices
|
|
180
|
+
|
|
181
|
+
1. **Use for shared types**: Apply `@ExposeType` to types that are used across multiple parts of your application
|
|
182
|
+
2. **Document the purpose**: Add clear docstrings explaining why a type is exposed
|
|
183
|
+
3. **Avoid overuse**: Only expose types that the frontend actually needs - don't expose internal implementation details
|
|
184
|
+
4. **Combine with other decorators**: Use alongside `@SplitInputOutput` when appropriate
|
|
185
|
+
5. **Group related types**: Keep exposed types in dedicated modules (e.g., `shared_types.py`)
|
|
186
|
+
|
|
187
|
+
## Viewing Exposed Types
|
|
188
|
+
|
|
189
|
+
All types decorated with `@ExposeType` are tracked globally. You can check which types are exposed:
|
|
190
|
+
|
|
191
|
+
```python
|
|
192
|
+
from jararaca.tools.typescript.decorators import ExposeType
|
|
193
|
+
|
|
194
|
+
# Get all exposed types
|
|
195
|
+
exposed = ExposeType.get_all_exposed_types()
|
|
196
|
+
print(f"Exposed {len(exposed)} types: {[t.__name__ for t in exposed]}")
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Generated TypeScript
|
|
200
|
+
|
|
201
|
+
Given this Python code:
|
|
202
|
+
|
|
203
|
+
```python
|
|
204
|
+
@ExposeType()
|
|
205
|
+
class NotificationPreference(BaseModel):
|
|
206
|
+
email_enabled: bool
|
|
207
|
+
push_enabled: bool
|
|
208
|
+
frequency: str
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
The generated TypeScript will be:
|
|
212
|
+
|
|
213
|
+
```typescript
|
|
214
|
+
export interface NotificationPreference {
|
|
215
|
+
emailEnabled: boolean;
|
|
216
|
+
pushEnabled: boolean;
|
|
217
|
+
frequency: string;
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
The type is available in your TypeScript code even if no REST endpoint uses it yet.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "jararaca"
|
|
3
|
-
version = "0.3.
|
|
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
|
+
]
|
|
@@ -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,
|
|
39
|
-
|
|
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[
|
|
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(
|
|
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;
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/messagebus/interceptors/publisher_interceptor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/persistence/interceptors/aiosqa_interceptor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{jararaca-0.3.14 → jararaca-0.3.16}/src/jararaca/presentation/websocket/websocket_interceptor.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|