jararaca 0.3.25__py3-none-any.whl → 0.3.26__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.

@@ -30,7 +30,7 @@ from pydantic import BaseModel, PlainValidator, RootModel
30
30
  from pydantic_core import PydanticUndefined
31
31
 
32
32
  from jararaca.microservice import Microservice
33
- from jararaca.presentation.decorators import HttpMapping, RestController
33
+ from jararaca.presentation.decorators import HttpMapping, RestController, UseMiddleware
34
34
  from jararaca.presentation.websocket.decorators import RegisterWebSocketMessage
35
35
  from jararaca.presentation.websocket.websocket_interceptor import (
36
36
  WebSocketMessageWrapper,
@@ -841,6 +841,8 @@ def write_rest_controller_to_typescript_interface(
841
841
  inspect.getmembers(controller, predicate=inspect.isfunction), key=lambda x: x[0]
842
842
  )
843
843
 
844
+ class_usemiddlewares = UseMiddleware.get_middlewares(controller)
845
+
844
846
  for name, member in member_items:
845
847
  if (mapping := HttpMapping.get_http_mapping(member)) is not None:
846
848
  return_type = member.__annotations__.get("return")
@@ -862,6 +864,39 @@ def write_rest_controller_to_typescript_interface(
862
864
  member, rest_controller, mapping
863
865
  )
864
866
 
867
+ # Extract parameters from controller-level middlewares
868
+ for middleware_type in rest_controller.middlewares:
869
+ middleware_params, middleware_mapped_types = (
870
+ extract_middleware_parameters(
871
+ middleware_type, rest_controller, mapping
872
+ )
873
+ )
874
+ arg_params_spec.extend(middleware_params)
875
+ parametes_mapped_types.update(middleware_mapped_types)
876
+
877
+ # Extract parameters from class-level UseMiddleware decorators
878
+ for middleware_instance in class_usemiddlewares:
879
+ middleware_params, middleware_mapped_types = (
880
+ extract_middleware_parameters(
881
+ middleware_instance.middleware, rest_controller, mapping
882
+ )
883
+ )
884
+ arg_params_spec.extend(middleware_params)
885
+ parametes_mapped_types.update(middleware_mapped_types)
886
+
887
+ # Extract parameters from method-level middlewares (UseMiddleware)
888
+ # Get the method from the class to access its middleware decorators
889
+ class_method = getattr(controller, name, None)
890
+ if class_method:
891
+ for middleware_instance in UseMiddleware.get_middlewares(class_method):
892
+ middleware_params, middleware_mapped_types = (
893
+ extract_middleware_parameters(
894
+ middleware_instance.middleware, rest_controller, mapping
895
+ )
896
+ )
897
+ arg_params_spec.extend(middleware_params)
898
+ parametes_mapped_types.update(middleware_mapped_types)
899
+
865
900
  for param in parametes_mapped_types:
866
901
  mapped_types.update(extract_all_envolved_types(param))
867
902
 
@@ -882,6 +917,8 @@ def write_rest_controller_to_typescript_interface(
882
917
 
883
918
  # Properly handle path joining to avoid double slashes
884
919
  controller_path = rest_controller.path or ""
920
+ # Also apply path transformation to the controller path
921
+ controller_path = parse_path_with_params(controller_path, arg_params_spec)
885
922
  path_parts = []
886
923
 
887
924
  if controller_path and controller_path.strip("/"):
@@ -1008,8 +1045,6 @@ class HttpParemeterSpec:
1008
1045
  def parse_path_with_params(path: str, parameters: list[HttpParemeterSpec]) -> str:
1009
1046
  # Use a regular expression to match both simple parameters {param} and
1010
1047
  # parameters with converters {param:converter}
1011
- import re
1012
-
1013
1048
  pattern = re.compile(r"{([^:}]+)(?::[^}]*)?}")
1014
1049
 
1015
1050
  # For each parameter found in the path, replace it with :param format
@@ -1022,6 +1057,212 @@ def parse_path_with_params(path: str, parameters: list[HttpParemeterSpec]) -> st
1022
1057
  return path
1023
1058
 
1024
1059
 
1060
+ def extract_middleware_parameters(
1061
+ middleware_type: type,
1062
+ controller: RestController,
1063
+ mapping: HttpMapping,
1064
+ ) -> tuple[list[HttpParemeterSpec], set[Any]]:
1065
+ """
1066
+ Extract parameters from a middleware class's intercept method.
1067
+ """
1068
+ parameters_list: list[HttpParemeterSpec] = []
1069
+ mapped_types: set[Any] = set()
1070
+
1071
+ # Get the intercept method from the middleware class
1072
+ if not hasattr(middleware_type, "intercept"):
1073
+ return parameters_list, mapped_types
1074
+
1075
+ intercept_method = getattr(middleware_type, "intercept")
1076
+
1077
+ # Use the same logic as extract_parameters but specifically for the intercept method
1078
+ try:
1079
+ signature = inspect.signature(intercept_method)
1080
+ for parameter_name, parameter in signature.parameters.items():
1081
+ # Skip 'self' parameter
1082
+ if parameter_name == "self":
1083
+ continue
1084
+
1085
+ parameter_type = parameter.annotation
1086
+ if parameter_type == inspect.Parameter.empty:
1087
+ continue
1088
+
1089
+ if parameter_type in EXCLUDED_REQUESTS_TYPES:
1090
+ continue
1091
+
1092
+ if get_origin(parameter_type) == Annotated:
1093
+ unwrapped_type, all_metadata = unwrap_annotated_type(parameter_type)
1094
+ # Look for FastAPI parameter annotations in all metadata layers
1095
+ annotated_type_hook = None
1096
+ for metadata in all_metadata:
1097
+ if isinstance(
1098
+ metadata, (Header, Cookie, Form, Body, Query, Path, Depends)
1099
+ ):
1100
+ annotated_type_hook = metadata
1101
+ break
1102
+
1103
+ if annotated_type_hook is None and all_metadata:
1104
+ # Fallback to first metadata if no FastAPI annotation found
1105
+ annotated_type_hook = all_metadata[0]
1106
+
1107
+ annotated_type = unwrapped_type
1108
+ if isinstance(annotated_type_hook, Header):
1109
+ mapped_types.add(str)
1110
+ parameters_list.append(
1111
+ HttpParemeterSpec(
1112
+ type_="header",
1113
+ name=parameter_name,
1114
+ required=True,
1115
+ argument_type_str=get_field_type_for_ts(str),
1116
+ )
1117
+ )
1118
+ elif isinstance(annotated_type_hook, Cookie):
1119
+ mapped_types.add(str)
1120
+ parameters_list.append(
1121
+ HttpParemeterSpec(
1122
+ type_="cookie",
1123
+ name=parameter_name,
1124
+ required=True,
1125
+ argument_type_str=get_field_type_for_ts(str),
1126
+ )
1127
+ )
1128
+ elif isinstance(annotated_type_hook, Form):
1129
+ mapped_types.add(annotated_type)
1130
+ parameters_list.append(
1131
+ HttpParemeterSpec(
1132
+ type_="form",
1133
+ name=parameter_name,
1134
+ required=True,
1135
+ argument_type_str=get_field_type_for_ts(annotated_type),
1136
+ )
1137
+ )
1138
+ elif isinstance(annotated_type_hook, Body):
1139
+ mapped_types.update(extract_all_envolved_types(parameter_type))
1140
+ # For body parameters, use Input suffix if it's a split model
1141
+ context_suffix = (
1142
+ "Input"
1143
+ if (
1144
+ inspect.isclass(parameter_type)
1145
+ and hasattr(parameter_type, "__dict__")
1146
+ and SplitInputOutput.is_split_model(parameter_type)
1147
+ )
1148
+ else ""
1149
+ )
1150
+ parameters_list.append(
1151
+ HttpParemeterSpec(
1152
+ type_="body",
1153
+ name=parameter_name,
1154
+ required=True,
1155
+ argument_type_str=get_field_type_for_ts(
1156
+ parameter_type, context_suffix
1157
+ ),
1158
+ )
1159
+ )
1160
+ elif isinstance(annotated_type_hook, Query):
1161
+ mapped_types.add(parameter_type)
1162
+ # For query parameters, use Input suffix if it's a split model
1163
+ context_suffix = (
1164
+ "Input"
1165
+ if (
1166
+ inspect.isclass(parameter_type)
1167
+ and hasattr(parameter_type, "__dict__")
1168
+ and SplitInputOutput.is_split_model(parameter_type)
1169
+ )
1170
+ else ""
1171
+ )
1172
+ parameters_list.append(
1173
+ HttpParemeterSpec(
1174
+ type_="query",
1175
+ name=parameter_name,
1176
+ required=True,
1177
+ argument_type_str=get_field_type_for_ts(
1178
+ parameter_type, context_suffix
1179
+ ),
1180
+ )
1181
+ )
1182
+ elif isinstance(annotated_type_hook, Path):
1183
+ mapped_types.add(parameter_type)
1184
+ # For path parameters, use Input suffix if it's a split model
1185
+ context_suffix = (
1186
+ "Input"
1187
+ if (
1188
+ inspect.isclass(parameter_type)
1189
+ and hasattr(parameter_type, "__dict__")
1190
+ and SplitInputOutput.is_split_model(parameter_type)
1191
+ )
1192
+ else ""
1193
+ )
1194
+ parameters_list.append(
1195
+ HttpParemeterSpec(
1196
+ type_="path",
1197
+ name=parameter_name,
1198
+ required=True,
1199
+ argument_type_str=get_field_type_for_ts(
1200
+ parameter_type, context_suffix
1201
+ ),
1202
+ )
1203
+ )
1204
+ elif isinstance(annotated_type_hook, Depends):
1205
+ # For Dependencies, recursively extract parameters
1206
+ depends_hook = (
1207
+ annotated_type_hook.dependency or parameter_type.__args__[0]
1208
+ )
1209
+ if isinstance(depends_hook, HTTPBase):
1210
+ # Skip HTTP authentication dependencies
1211
+ pass
1212
+ else:
1213
+ # TODO: We might need to recursively extract from dependencies
1214
+ # For now, skip to avoid infinite recursion
1215
+ pass
1216
+ else:
1217
+ # Handle non-annotated parameters - check if they are path parameters
1218
+ mapped_types.add(parameter_type)
1219
+
1220
+ # Check if parameter matches path parameters in controller or method paths
1221
+ if (
1222
+ # Match both simple parameters {param} and parameters with converters {param:converter}
1223
+ re.search(f"{{{parameter_name}(:.*?)?}}", controller.path)
1224
+ is not None
1225
+ or re.search(f"{{{parameter_name}(:.*?)?}}", mapping.path)
1226
+ is not None
1227
+ ):
1228
+ # This is a path parameter
1229
+ context_suffix = (
1230
+ "Input"
1231
+ if (
1232
+ inspect.isclass(parameter_type)
1233
+ and hasattr(parameter_type, "__dict__")
1234
+ and SplitInputOutput.is_split_model(parameter_type)
1235
+ )
1236
+ else ""
1237
+ )
1238
+ parameters_list.append(
1239
+ HttpParemeterSpec(
1240
+ type_="path",
1241
+ name=parameter_name,
1242
+ required=True,
1243
+ argument_type_str=get_field_type_for_ts(
1244
+ parameter_type, context_suffix
1245
+ ),
1246
+ )
1247
+ )
1248
+ elif is_primitive(parameter_type):
1249
+ # Default to query parameters for simple types that aren't in the path
1250
+ parameters_list.append(
1251
+ HttpParemeterSpec(
1252
+ type_="query",
1253
+ name=parameter_name,
1254
+ required=True,
1255
+ argument_type_str=get_field_type_for_ts(parameter_type),
1256
+ )
1257
+ )
1258
+
1259
+ except (ValueError, TypeError):
1260
+ # If we can't inspect the signature, return empty
1261
+ pass
1262
+
1263
+ return parameters_list, mapped_types
1264
+
1265
+
1025
1266
  def mount_parametes_arguments(parameters: list[HttpParemeterSpec]) -> str:
1026
1267
  return ", ".join(
1027
1268
  [f"{parameter.name}: {parameter.argument_type_str}" for parameter in parameters]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: jararaca
3
- Version: 0.3.25
3
+ Version: 0.3.26
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,6 +1,6 @@
1
1
  LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
2
2
  README.md,sha256=YmCngjU8llW0l7L3tuXkkfr8qH7V9aBMgfp2jEzeiKg,3517
3
- pyproject.toml,sha256=iPlQyxxgFBxw9owR11Cxr3UFWKstcQErqL_eGuOXxFg,2739
3
+ pyproject.toml,sha256=JXte3h2WM1G5riLSBoE2nBXWnybbJhqtoFl-N6Vt3ZI,2739
4
4
  jararaca/__init__.py,sha256=zQRbt5BrXFVvJW8pKaA9_KdkGFB6Njmr-9SyX0ZfenM,23331
5
5
  jararaca/__main__.py,sha256=-O3vsB5lHdqNFjUtoELDF81IYFtR-DSiiFMzRaiSsv4,67
6
6
  jararaca/broker_backend/__init__.py,sha256=GzEIuHR1xzgCJD4FE3harNjoaYzxHMHoEL0_clUaC-k,3528
@@ -72,12 +72,12 @@ jararaca/tools/app_config/decorators.py,sha256=-ckkMZ1dswOmECdo1rFrZ15UAku--txaN
72
72
  jararaca/tools/app_config/interceptor.py,sha256=HV8h4AxqUc_ACs5do4BSVlyxlRXzx7HqJtoVO9tfRnQ,2611
73
73
  jararaca/tools/typescript/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  jararaca/tools/typescript/decorators.py,sha256=2NXFI6MiqpjM8rmrje38dR5StRlqdzYOmPASgyLIHeo,4267
75
- jararaca/tools/typescript/interface_parser.py,sha256=yOSuOXKOeG0soGFo0fKiZIabu4YwnvIKk-Zss8UPAuE,55174
75
+ jararaca/tools/typescript/interface_parser.py,sha256=VBx-TYQPjAiHLiYgzGdRl_p4xDbdx4GvJtQhDBrQx7g,66210
76
76
  jararaca/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
77
77
  jararaca/utils/rabbitmq_utils.py,sha256=ytdAFUyv-OBkaVnxezuJaJoLrmN7giZgtKeet_IsMBs,10918
78
78
  jararaca/utils/retry.py,sha256=DzPX_fXUvTqej6BQ8Mt2dvLo9nNlTBm7Kx2pFZ26P2Q,4668
79
- jararaca-0.3.25.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
80
- jararaca-0.3.25.dist-info/METADATA,sha256=NfiyjoAO1CUVU8LTDyIImqCdgUiX7IKrU3XRhcMPTgI,5149
81
- jararaca-0.3.25.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
82
- jararaca-0.3.25.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
83
- jararaca-0.3.25.dist-info/RECORD,,
79
+ jararaca-0.3.26.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
80
+ jararaca-0.3.26.dist-info/METADATA,sha256=tQ8LaLhplQLYw8LPWadDAQ-lJw7Air5NCI_UGhAN550,5149
81
+ jararaca-0.3.26.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
82
+ jararaca-0.3.26.dist-info/entry_points.txt,sha256=WIh3aIvz8LwUJZIDfs4EeH3VoFyCGEk7cWJurW38q0I,45
83
+ jararaca-0.3.26.dist-info/RECORD,,
pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "jararaca"
3
- version = "0.3.25"
3
+ version = "0.3.26"
4
4
  description = "A simple and fast API framework for Python"
5
5
  authors = ["Lucas S <me@luscasleo.dev>"]
6
6
  readme = "README.md"