66import gfmodules .logging as gflog
77from pydantic import ValidationError
88
9- from app .errors .handlers import log_rejection
9+ from app .errors .handlers import log_invalid_request , log_rejection , summarize_reason
1010from app .errors .mapping import DEFAULT_SPEC , UNEXPECTED_ERROR_MESSAGE , spec_for
1111from app .logging .events import Log
1212from app .models .auth .context import AuthContext
2626logger = logging .getLogger (__name__ )
2727
2828
29+ def _invalid_entry (msg : str , status : int = 422 , code : str = "required" , reason : str | None = None ) -> BundleEntry [Any ]:
30+ log_invalid_request (status , reason or msg )
31+ return BundleEntry (response = EntryResponse .make_error_response (msg , str (status ), code ))
32+
33+
2934class BundleService :
3035 def __init__ (
3136 self ,
@@ -35,14 +40,10 @@ def __init__(
3540
3641 def process_entry (self , ctx : AuthContext , entry : BundleEntry [Any ], index : int ) -> BundleEntry [Any ]:
3742 if entry .request is None :
38- return BundleEntry (
39- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request is required" )
40- )
43+ return _invalid_entry (f"Bundle.entry.{ index } .request is required" )
4144
4245 if entry .request .method is None :
43- return BundleEntry (
44- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request.method is required" )
45- )
46+ return _invalid_entry (f"Bundle.entry.{ index } .request.method is required" )
4647
4748 method = entry .request .method
4849 resolved_url = self .resolve_request_url (entry .request .url , index )
@@ -149,6 +150,9 @@ def _entry_result(self, index: int, action: Callable[[], BundleEntry[Any]]) -> B
149150 else :
150151 log_rejection (spec .http_status , exc )
151152
153+ if spec .invalid_input :
154+ return _invalid_entry (f"Bundle.entry.{ index } : { exc } " , spec .http_status , spec .fhir_code )
155+
152156 return BundleEntry (
153157 response = EntryResponse .make_error_response (
154158 msg = f"Bundle.entry.{ index } : { UNEXPECTED_ERROR_MESSAGE if unexpected else exc } " ,
@@ -186,11 +190,11 @@ def _parse_params(
186190 missing = all (err ["type" ] == "missing" for err in e .errors ())
187191 else :
188192 missing = True
189- return BundleEntry (
190- response = EntryResponse . make_validation_response (
191- f"Bundle.entry. { index } .request: invalid url parameter : { e } " ,
192- code = "required" if missing else "invalid" ,
193- )
193+ prefix = f"Bundle.entry. { index } .request: invalid url parameter"
194+ return _invalid_entry (
195+ f" { prefix } : { e } " ,
196+ code = "required" if missing else "invalid" ,
197+ reason = f" { prefix } : { summarize_reason ( e ) } " ,
194198 )
195199
196200 def _deny_mismatched_source (
@@ -211,13 +215,7 @@ def _deny_mismatched_source(
211215 # A source built on the wrong naming system, which the standalone route rejects
212216 # before it reaches the service too.
213217 spec = spec_for (e )
214- return BundleEntry (
215- response = EntryResponse .make_error_response (
216- msg = f"Bundle.entry.{ index } : { e } " ,
217- status = str (spec .http_status ),
218- code = spec .fhir_code ,
219- )
220- )
218+ return _invalid_entry (f"Bundle.entry.{ index } : { e } " , spec .http_status , spec .fhir_code )
221219
222220 try :
223221 AuthContextService .assert_source_matches (ctx , entry_source )
@@ -234,14 +232,10 @@ def _deny_mismatched_source(
234232 def _validate_post_resource (self , entry : BundleEntry [Any ], index : int ) -> LocalizationList | BundleEntry [Any ]:
235233 """Return the validated ``LocalizationList`` or an error ``BundleEntry``."""
236234 if entry .resource is None :
237- return BundleEntry (
238- response = EntryResponse .make_validation_response (msg = f"Bundle.entry.{ index } : resource cannot be empty" )
239- )
235+ return _invalid_entry (f"Bundle.entry.{ index } : resource cannot be empty" )
240236
241237 if not isinstance (entry .resource , LocalizationList ):
242- return BundleEntry (
243- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } : invalid List resource" )
244- )
238+ return _invalid_entry (f"Bundle.entry.{ index } : invalid List resource" )
245239
246240 return entry .resource
247241
@@ -279,27 +273,15 @@ def resolve_request_url(self, url: str, index: int) -> EntryRequestDto | BundleE
279273 try :
280274 request_dto = EntryRequestDto .from_url (url )
281275 except ValueError :
282- return BundleEntry (
283- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request.url is malformed" )
284- )
276+ return _invalid_entry (f"Bundle.entry.{ index } .request.url is malformed" )
285277
286278 if request_dto .resource is None :
287- return BundleEntry (
288- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request.url: resource not found" )
289- )
279+ return _invalid_entry (f"Bundle.entry.{ index } .request.url: resource not found" )
290280
291281 if request_dto .resource != "List" :
292- return BundleEntry (
293- response = EntryResponse .make_validation_response (
294- f"Bundle.entry.{ index } .request.url unsupported { request_dto .resource } resource"
295- )
296- )
282+ return _invalid_entry (f"Bundle.entry.{ index } .request.url unsupported { request_dto .resource } resource" )
297283
298284 if request_dto .id is not None and request_dto .params is not None :
299- return BundleEntry (
300- response = EntryResponse .make_validation_response (
301- f"Bundle.entry.{ index } .request.url: unsupported url for requested transaction"
302- )
303- )
285+ return _invalid_entry (f"Bundle.entry.{ index } .request.url: unsupported url for requested transaction" )
304286
305287 return request_dto
0 commit comments