1515 _normalize_errors ,
1616 _regenerate_error_with_loc ,
1717 get_missing_field_error ,
18+ lenient_issubclass ,
1819)
1920from aws_lambda_powertools .event_handler .openapi .dependant import is_scalar_field
2021from aws_lambda_powertools .event_handler .openapi .encoders import jsonable_encoder
@@ -64,7 +65,7 @@ def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) ->
6465 )
6566
6667 # Normalize query values before validate this
67- query_string = _normalize_multi_query_string_with_param (
68+ query_string = _normalize_multi_params (
6869 app .current_event .resolved_query_string_parameters ,
6970 route .dependant .query_params ,
7071 )
@@ -76,7 +77,7 @@ def handler(self, app: EventHandlerInstance, next_middleware: NextMiddleware) ->
7677 )
7778
7879 # Normalize header values before validate this
79- headers = _normalize_multi_header_values_with_param (
80+ headers = _normalize_multi_params (
8081 app .current_event .resolved_headers_field ,
8182 route .dependant .header_params ,
8283 )
@@ -439,116 +440,43 @@ def _get_embed_body(
439440 return received_body , field_alias_omitted
440441
441442
442- def _normalize_multi_query_string_with_param (
443- query_string : dict [str , list [ str ] ],
443+ def _normalize_multi_params (
444+ input_dict : MutableMapping [str , Any ],
444445 params : Sequence [ModelField ],
445- ) -> dict [str , Any ]:
446+ ) -> MutableMapping [str , Any ]:
446447 """
447- Extract and normalize resolved_query_string_parameters with Pydantic model support
448-
449- Parameters
450- ----------
451- query_string: dict
452- A dictionary containing the initial query string parameters.
453- params: Sequence[ModelField]
454- A sequence of ModelField objects representing parameters.
455-
456- Returns
457- -------
458- A dictionary containing the processed multi_query_string_parameters.
448+ Generic normalization for query string or header parameters with Pydantic model support.
449+ No key transformation is performed.
459450 """
460- resolved_query_string : dict [str , Any ] = query_string
461-
462451 for param in params :
463- # Handle scalar fields (existing logic)
464452 if is_scalar_field (param ):
465453 try :
466- resolved_query_string [param .alias ] = query_string [param .alias ][0 ]
454+ val = input_dict [param .alias ]
455+ if isinstance (val , list ) and len (val ) == 1 :
456+ input_dict [param .alias ] = val [0 ]
457+ elif isinstance (val , list ):
458+ pass # leave as list for multi-value
459+ # If it's a string, leave as is
467460 except KeyError :
468461 pass
469- # Handle Pydantic models
470- elif isinstance (param .field_info , Query ) and hasattr (param .field_info , "annotation" ):
471- from pydantic import BaseModel
472-
473- from aws_lambda_powertools .event_handler .openapi .compat import lenient_issubclass
474-
475- if lenient_issubclass (param .field_info .annotation , BaseModel ):
476- model_class = param .field_info .annotation
477- model_data = {}
478-
479- # Collect all fields for the Pydantic model
480- for field_name , field_def in model_class .model_fields .items ():
481- field_alias = field_def .alias or field_name
482- try :
483- model_data [field_alias ] = query_string [field_alias ][0 ]
484- except KeyError :
485- if model_class .model_config .get ("validate_by_name" ) or model_class .model_config .get (
486- "populate_by_name" ,
487- ):
488- try :
489- model_data [field_alias ] = query_string [field_name ][0 ]
490- except KeyError :
491- pass
492-
493- # Store the collected data under the param alias
494- resolved_query_string [param .alias ] = model_data
495-
496- return resolved_query_string
497-
498-
499- def _normalize_multi_header_values_with_param (headers : MutableMapping [str , Any ], params : Sequence [ModelField ]):
500- """
501- Extract and normalize resolved_headers_field with Pydantic model support
502-
503- Parameters
504- ----------
505- headers: MutableMapping[str, Any]
506- A dictionary containing the initial header parameters.
507- params: Sequence[ModelField]
508- A sequence of ModelField objects representing parameters.
509-
510- Returns
511- -------
512- A dictionary containing the processed headers.
513- """
514- if headers :
515- for param in params :
516- # Handle scalar fields (existing logic)
517- if is_scalar_field (param ):
518- try :
519- if len (headers [param .alias ]) == 1 :
520- headers [param .alias ] = headers [param .alias ][0 ]
521- except KeyError :
522- pass
523- # Handle Pydantic models
524- elif isinstance (param .field_info , Header ) and hasattr (param .field_info , "annotation" ):
525- from pydantic import BaseModel
526-
527- from aws_lambda_powertools .event_handler .openapi .compat import lenient_issubclass
528-
529- if lenient_issubclass (param .field_info .annotation , BaseModel ):
530- model_class = param .field_info .annotation
531- model_data = {}
532-
533- # Collect all fields for the Pydantic model
534- for field_name , field_def in model_class .model_fields .items ():
535- field_alias = field_def .alias or field_name
536-
537- # Convert snake_case to kebab-case for headers (HTTP convention)
538- header_key = field_alias .replace ("_" , "-" )
539-
540- try :
541- header_value = headers [header_key ]
542- if isinstance (header_value , list ):
543- if len (header_value ) == 1 :
544- model_data [field_alias ] = header_value [0 ]
545- else :
546- model_data [field_alias ] = header_value
547- else :
548- model_data [field_alias ] = header_value
549- except KeyError :
550- pass
551-
552- # Store the collected data under the param alias
553- headers [param .alias ] = model_data
554- return headers
462+ elif lenient_issubclass (param .field_info .annotation , BaseModel ):
463+ model_class = param .field_info .annotation
464+ model_data = {}
465+ from typing import get_origin
466+
467+ for field_name , field_def in model_class .model_fields .items ():
468+ field_alias = field_def .alias or field_name
469+ value = input_dict .get (field_alias )
470+ if value is None and (
471+ model_class .model_config .get ("validate_by_name" ) or model_class .model_config .get ("populate_by_name" )
472+ ):
473+ value = input_dict .get (field_name )
474+ if value is not None :
475+ if get_origin (field_def .annotation ) is list :
476+ model_data [field_alias ] = value
477+ elif isinstance (value , list ):
478+ model_data [field_alias ] = value [0 ]
479+ else :
480+ model_data [field_alias ] = value
481+ input_dict [param .alias ] = model_data
482+ return input_dict
0 commit comments