55
66import gfmodules .logging as gflog
77
8- from app .errors .handlers import log_rejection
8+ from app .errors .handlers import log_invalid_request , log_rejection , summarize_reason
99from app .errors .mapping import DEFAULT_SPEC , UNEXPECTED_ERROR_MESSAGE , spec_for
1010from app .logging .events import Log
1111from app .models .auth .context import AuthContext
2828logger = logging .getLogger (__name__ )
2929
3030
31+ def _invalid_entry (msg : str , status : int = 422 , code : str = "required" , reason : str | None = None ) -> BundleEntry [Any ]:
32+ log_invalid_request (status , reason or msg )
33+ return BundleEntry (response = EntryResponse .make_error_response (msg , str (status ), code ))
34+
35+
3136class BundleService :
3237 def __init__ (
3338 self ,
@@ -37,14 +42,10 @@ def __init__(
3742
3843 def process_entry (self , ctx : AuthContext , entry : BundleEntry [Any ], index : int ) -> BundleEntry [Any ]:
3944 if entry .request is None :
40- return BundleEntry (
41- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request is required" )
42- )
45+ return _invalid_entry (f"Bundle.entry.{ index } .request is required" )
4346
4447 if entry .request .method is None :
45- return BundleEntry (
46- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request.method is required" )
47- )
48+ return _invalid_entry (f"Bundle.entry.{ index } .request.method is required" )
4849
4950 method = entry .request .method
5051 resolved_url = self .resolve_request_url (entry .request .url , index )
@@ -151,6 +152,9 @@ def _entry_result(self, index: int, action: Callable[[], BundleEntry[Any]]) -> B
151152 else :
152153 log_rejection (spec .http_status , exc )
153154
155+ if spec .invalid_input :
156+ return _invalid_entry (f"Bundle.entry.{ index } : { exc } " , spec .http_status , spec .fhir_code )
157+
154158 return BundleEntry (
155159 response = EntryResponse .make_error_response (
156160 msg = f"Bundle.entry.{ index } : { UNEXPECTED_ERROR_MESSAGE if unexpected else exc } " ,
@@ -184,11 +188,8 @@ def _parse_params(
184188 try :
185189 return model .model_validate (resolved_url .params )
186190 except ValueError as e :
187- return BundleEntry (
188- response = EntryResponse .make_validation_response (
189- f"Bundle.entry.{ index } .request: invalid url parameter: { e } "
190- )
191- )
191+ prefix = f"Bundle.entry.{ index } .request: invalid url parameter"
192+ return _invalid_entry (f"{ prefix } : { e } " , reason = f"{ prefix } : { summarize_reason (e )} " )
192193
193194 def _deny_mismatched_source (
194195 self ,
@@ -208,13 +209,7 @@ def _deny_mismatched_source(
208209 # A source built on the wrong naming system, which the standalone route rejects
209210 # before it reaches the service too.
210211 spec = spec_for (e )
211- return BundleEntry (
212- response = EntryResponse .make_error_response (
213- msg = f"Bundle.entry.{ index } : { e } " ,
214- status = str (spec .http_status ),
215- code = spec .fhir_code ,
216- )
217- )
212+ return _invalid_entry (f"Bundle.entry.{ index } : { e } " , spec .http_status , spec .fhir_code )
218213
219214 try :
220215 AuthContextService .assert_source_matches (ctx , entry_source )
@@ -231,14 +226,10 @@ def _deny_mismatched_source(
231226 def _validate_post_resource (self , entry : BundleEntry [Any ], index : int ) -> LocalizationList | BundleEntry [Any ]:
232227 """Return the validated ``LocalizationList`` or an error ``BundleEntry``."""
233228 if entry .resource is None :
234- return BundleEntry (
235- response = EntryResponse .make_validation_response (msg = f"Bundle.entry.{ index } : resource cannot be empty" )
236- )
229+ return _invalid_entry (f"Bundle.entry.{ index } : resource cannot be empty" )
237230
238231 if not isinstance (entry .resource , LocalizationList ):
239- return BundleEntry (
240- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } : invalid List resource" )
241- )
232+ return _invalid_entry (f"Bundle.entry.{ index } : invalid List resource" )
242233
243234 return entry .resource
244235
@@ -276,27 +267,15 @@ def resolve_request_url(self, url: str, index: int) -> EntryRequestDto | BundleE
276267 try :
277268 request_dto = EntryRequestDto .from_url (url )
278269 except ValueError :
279- return BundleEntry (
280- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request.url is malformed" )
281- )
270+ return _invalid_entry (f"Bundle.entry.{ index } .request.url is malformed" )
282271
283272 if request_dto .resource is None :
284- return BundleEntry (
285- response = EntryResponse .make_validation_response (f"Bundle.entry.{ index } .request.url: resource not found" )
286- )
273+ return _invalid_entry (f"Bundle.entry.{ index } .request.url: resource not found" )
287274
288275 if request_dto .resource != "List" :
289- return BundleEntry (
290- response = EntryResponse .make_validation_response (
291- f"Bundle.entry.{ index } .request.url unsupported { request_dto .resource } resource"
292- )
293- )
276+ return _invalid_entry (f"Bundle.entry.{ index } .request.url unsupported { request_dto .resource } resource" )
294277
295278 if request_dto .id is not None and request_dto .params is not None :
296- return BundleEntry (
297- response = EntryResponse .make_validation_response (
298- f"Bundle.entry.{ index } .request.url: unsupported url for requested transaction"
299- )
300- )
279+ return _invalid_entry (f"Bundle.entry.{ index } .request.url: unsupported url for requested transaction" )
301280
302281 return request_dto
0 commit comments