haste_fhir_client/
http.rs

1use crate::{
2    FHIRClient,
3    middleware::{Context, Middleware, MiddlewareChain, Next},
4    request::{
5        self, DeleteRequest, DeleteResponse, FHIRCreateResponse, FHIRPatchResponse,
6        FHIRReadResponse, FHIRRequest, FHIRResponse, HistoryRequest, HistoryResponse,
7        InvocationRequest, InvokeResponse, Operation, SearchRequest, SearchResponse, UpdateRequest,
8    },
9    url::{ParsedParameter, ParsedParameters},
10};
11use haste_fhir_model::r4::generated::{
12    resources::{
13        Bundle, CapabilityStatement, OperationOutcome, Parameters, Resource, ResourceType,
14    },
15    terminology::IssueType,
16};
17use haste_fhir_operation_error::{OperationOutcomeError, derive::OperationOutcomeError};
18use haste_jwt::VersionId;
19use http::HeaderValue;
20use reqwest::Url;
21use std::{pin::Pin, sync::Arc};
22
23pub struct FHIRHttpState {
24    client: reqwest::Client,
25    api_url: Url,
26    get_access_token: Option<
27        Arc<
28            dyn Fn() -> Pin<
29                    Box<dyn Future<Output = Result<String, OperationOutcomeError>> + Send + Sync>,
30                > + Sync
31                + Send,
32        >,
33    >,
34}
35
36impl FHIRHttpState {
37    pub fn new(
38        api_url: &str,
39        get_access_token: Option<
40            Arc<
41                dyn Fn() -> Pin<
42                        Box<
43                            dyn Future<Output = Result<String, OperationOutcomeError>>
44                                + Send
45                                + Sync,
46                        >,
47                    > + Sync
48                    + Send,
49            >,
50        >,
51    ) -> Result<Self, OperationOutcomeError> {
52        let url =
53            Url::parse(api_url).map_err(|_| FHIRHTTPError::UrlParseError(api_url.to_string()))?;
54        Ok(FHIRHttpState {
55            client: reqwest::Client::new(),
56            api_url: url,
57            get_access_token,
58        })
59    }
60}
61
62pub struct FHIRHttpClient<CTX> {
63    state: Arc<FHIRHttpState>,
64    middleware:
65        Middleware<Arc<FHIRHttpState>, CTX, FHIRRequest, FHIRResponse, OperationOutcomeError>,
66}
67
68#[derive(Debug, OperationOutcomeError)]
69pub enum FHIRHTTPError {
70    #[error(code = "exception", diagnostic = "Reqwest failed.")]
71    ReqwestError(#[from] reqwest::Error),
72    #[error(code = "not-supported", diagnostic = "Operation not supported.")]
73    NotSupported,
74    #[fatal(code = "exception", diagnostic = "No response received.")]
75    NoResponse,
76    #[fatal(
77        code = "exception",
78        diagnostic = "Invalid url that could not be parsed {arg0}"
79    )]
80    UrlParseError(String),
81    #[error(code = "invalid", diagnostic = "FHIR Deserialization Error.")]
82    DeserializeError(#[from] haste_fhir_serialization_json::errors::DeserializeError),
83    #[error(code = "invalid", diagnostic = "FHIR Serialization Error.")]
84    SerializeError(#[from] haste_fhir_serialization_json::SerializeError),
85    #[error(code = "invalid", diagnostic = "FHIR Serialization Error.")]
86    JSONSerializeError(#[from] serde_json::Error),
87}
88
89fn fhir_parameter_to_query_parameters(http_url: &mut reqwest::Url, parameters: &ParsedParameters) {
90    let mut query_parameters = http_url.query_pairs_mut();
91    for parameter in parameters.parameters() {
92        let parameter = match parameter {
93            ParsedParameter::Result(parameter) | ParsedParameter::Resource(parameter) => parameter,
94        };
95
96        let mut query_param_name = parameter.name.clone();
97
98        if let Some(chains) = parameter.chains.as_ref() {
99            query_param_name = format!("{}.{}", query_param_name, chains.join("."));
100        }
101
102        if let Some(modifier) = parameter.modifier.as_ref() {
103            query_param_name = format!("{}:{}", query_param_name, modifier);
104        }
105
106        query_parameters.append_pair(&query_param_name, parameter.value.join(",").as_str());
107    }
108}
109
110async fn fhir_request_to_http_request(
111    state: &FHIRHttpState,
112    request: &FHIRRequest,
113) -> Result<reqwest::Request, OperationOutcomeError> {
114    let request: Result<reqwest::Request, OperationOutcomeError> = match request {
115        FHIRRequest::Read(read_request) => {
116            let read_request_url = state
117                .api_url
118                .join(&format!(
119                    "{}/{}/{}",
120                    state.api_url.path(),
121                    read_request.resource_type.as_ref(),
122                    read_request.id
123                ))
124                .map_err(|_e| FHIRHTTPError::UrlParseError("Read request".to_string()))?;
125
126            let request = state
127                .client
128                .get(read_request_url)
129                .header("Accept", "application/fhir+json")
130                .header("Content-Type", "application/fhir+json, application/json")
131                .build()
132                .map_err(FHIRHTTPError::from)?;
133
134            Ok(request)
135        }
136        FHIRRequest::Create(create_request) => {
137            let create_request_url = state
138                .api_url
139                .join(&format!(
140                    "{}/{}",
141                    state.api_url.path(),
142                    create_request.resource_type.as_ref(),
143                ))
144                .map_err(|_e| FHIRHTTPError::UrlParseError("Create request".to_string()))?;
145
146            let body = haste_fhir_serialization_json::to_string(&create_request.resource)
147                .map_err(FHIRHTTPError::from)?;
148
149            let request = state
150                .client
151                .post(create_request_url)
152                .header("Accept", "application/fhir+json")
153                .header("Content-Type", "application/fhir+json, application/json")
154                .body(body)
155                .build()
156                .map_err(FHIRHTTPError::from)?;
157
158            Ok(request)
159        }
160        FHIRRequest::Patch(patch_request) => {
161            let patch_request_url = state
162                .api_url
163                .join(&format!(
164                    "{}/{}/{}",
165                    state.api_url.path(),
166                    patch_request.resource_type.as_ref(),
167                    patch_request.id
168                ))
169                .map_err(|_e| FHIRHTTPError::UrlParseError("Patch request".to_string()))?;
170
171            let patch_body =
172                serde_json::to_string(&patch_request.patch).map_err(FHIRHTTPError::from)?;
173
174            let request = state
175                .client
176                .patch(patch_request_url)
177                .header("Accept", "application/fhir+json")
178                .header("Content-Type", "application/fhir+json, application/json")
179                .body(patch_body)
180                .build()
181                .map_err(FHIRHTTPError::from)?;
182
183            Ok(request)
184        }
185        FHIRRequest::Transaction(transaction_request) => {
186            let body = haste_fhir_serialization_json::to_string(&transaction_request.resource)
187                .map_err(FHIRHTTPError::from)?;
188
189            let request = state
190                .client
191                .post(state.api_url.clone())
192                .header("Accept", "application/fhir+json")
193                .header("Content-Type", "application/fhir+json, application/json")
194                .body(body)
195                .build()
196                .map_err(FHIRHTTPError::from)?;
197
198            Ok(request)
199        }
200        FHIRRequest::VersionRead(version_request) => {
201            let version_request_url = state
202                .api_url
203                .join(&format!(
204                    "{}/{}/{}/_history/{}",
205                    state.api_url.path(),
206                    version_request.resource_type.as_ref(),
207                    version_request.id,
208                    version_request.version_id.as_ref(),
209                ))
210                .map_err(|_e| FHIRHTTPError::UrlParseError("Patch request".to_string()))?;
211
212            let request = state
213                .client
214                .get(version_request_url)
215                .header("Accept", "application/fhir+json")
216                .header("Content-Type", "application/fhir+json, application/json")
217                .build()
218                .map_err(FHIRHTTPError::from)?;
219
220            Ok(request)
221        }
222
223        FHIRRequest::Update(update_request) => match &update_request {
224            UpdateRequest::Instance(update_request) => {
225                let update_request_url = state
226                    .api_url
227                    .join(&format!(
228                        "{}/{}/{}",
229                        state.api_url.path(),
230                        update_request.resource_type.as_ref(),
231                        update_request.id
232                    ))
233                    .map_err(|_e| FHIRHTTPError::UrlParseError("Update request".to_string()))?;
234
235                let request = state
236                    .client
237                    .put(update_request_url)
238                    .header("Accept", "application/fhir+json")
239                    .header("Content-Type", "application/fhir+json, application/json")
240                    .body(
241                        haste_fhir_serialization_json::to_string(&update_request.resource)
242                            .map_err(FHIRHTTPError::from)?,
243                    )
244                    .build()
245                    .map_err(FHIRHTTPError::from)?;
246
247                Ok(request)
248            }
249            UpdateRequest::Conditional(fhirconditional_update_request) => {
250                let mut request_url = state
251                    .api_url
252                    .join(&format!(
253                        "{}/{}",
254                        state.api_url.path(),
255                        fhirconditional_update_request.resource_type.as_ref(),
256                    ))
257                    .map_err(|_e| {
258                        FHIRHTTPError::UrlParseError("ConditionalUpdate request".to_string())
259                    })?;
260                fhir_parameter_to_query_parameters(
261                    &mut request_url,
262                    &fhirconditional_update_request.parameters,
263                );
264
265                let request = state
266                    .client
267                    .put(request_url)
268                    .header("Accept", "application/fhir+json")
269                    .header("Content-Type", "application/fhir+json, application/json")
270                    .body(
271                        haste_fhir_serialization_json::to_string(
272                            &fhirconditional_update_request.resource,
273                        )
274                        .map_err(FHIRHTTPError::from)?,
275                    )
276                    .build()
277                    .map_err(FHIRHTTPError::from)?;
278
279                Ok(request)
280            }
281        },
282
283        FHIRRequest::Search(search_request) => match &search_request {
284            SearchRequest::Type(search_type_request) => {
285                let mut request_url = state
286                    .api_url
287                    .join(&format!(
288                        "{}/{}",
289                        state.api_url.path(),
290                        search_type_request.resource_type.as_ref(),
291                    ))
292                    .map_err(|_e| FHIRHTTPError::UrlParseError("SearchType request".to_string()))?;
293
294                fhir_parameter_to_query_parameters(
295                    &mut request_url,
296                    &search_type_request.parameters,
297                );
298
299                let request = state
300                    .client
301                    .get(request_url)
302                    .header("Accept", "application/fhir+json")
303                    .header("Content-Type", "application/fhir+json, application/json")
304                    .build()
305                    .map_err(FHIRHTTPError::from)?;
306
307                Ok(request)
308            }
309            SearchRequest::System(fhirsearch_system_request) => {
310                let mut request_url = state.api_url.join(state.api_url.path()).map_err(|_e| {
311                    FHIRHTTPError::UrlParseError("SearchSystem request".to_string())
312                })?;
313
314                fhir_parameter_to_query_parameters(
315                    &mut request_url,
316                    &fhirsearch_system_request.parameters,
317                );
318
319                let request = state
320                    .client
321                    .get(request_url)
322                    .header("Accept", "application/fhir+json")
323                    .header("Content-Type", "application/fhir+json, application/json")
324                    .build()
325                    .map_err(FHIRHTTPError::from)?;
326
327                Ok(request)
328            }
329        },
330        FHIRRequest::Delete(delete_request) => match delete_request {
331            DeleteRequest::Instance(fhirdelete_instance_request) => {
332                let delete_request_url = state
333                    .api_url
334                    .join(&format!(
335                        "{}/{}/{}",
336                        state.api_url.path(),
337                        fhirdelete_instance_request.resource_type.as_ref(),
338                        fhirdelete_instance_request.id
339                    ))
340                    .map_err(|_e| {
341                        FHIRHTTPError::UrlParseError("DeleteInstance request".to_string())
342                    })?;
343
344                let request = state
345                    .client
346                    .delete(delete_request_url)
347                    .header("Accept", "application/fhir+json")
348                    .header("Content-Type", "application/fhir+json, application/json")
349                    .build()
350                    .map_err(FHIRHTTPError::from)?;
351
352                Ok(request)
353            }
354            DeleteRequest::Type(fhirdelete_type_request) => {
355                let mut request_url = state
356                    .api_url
357                    .join(&format!(
358                        "{}/{}",
359                        state.api_url.path(),
360                        fhirdelete_type_request.resource_type.as_ref(),
361                    ))
362                    .map_err(|_e| FHIRHTTPError::UrlParseError("DeleteType request".to_string()))?;
363
364                fhir_parameter_to_query_parameters(
365                    &mut request_url,
366                    &fhirdelete_type_request.parameters,
367                );
368
369                let request = state
370                    .client
371                    .delete(request_url)
372                    .header("Accept", "application/fhir+json")
373                    .header("Content-Type", "application/fhir+json, application/json")
374                    .build()
375                    .map_err(FHIRHTTPError::from)?;
376
377                Ok(request)
378            }
379            DeleteRequest::System(fhirdelete_system_request) => {
380                let mut request_url = state.api_url.join(state.api_url.path()).map_err(|_e| {
381                    FHIRHTTPError::UrlParseError("DeleteSystem request".to_string())
382                })?;
383
384                fhir_parameter_to_query_parameters(
385                    &mut request_url,
386                    &fhirdelete_system_request.parameters,
387                );
388
389                let request = state
390                    .client
391                    .delete(request_url)
392                    .header("Accept", "application/fhir+json")
393                    .header("Content-Type", "application/fhir+json, application/json")
394                    .build()
395                    .map_err(FHIRHTTPError::from)?;
396
397                Ok(request)
398            }
399        },
400        FHIRRequest::Capabilities => {
401            let request = state
402                .client
403                .get(format!("{}/metadata", state.api_url))
404                .header("Accept", "application/fhir+json")
405                .header("Content-Type", "application/fhir+json, application/json")
406                .build()
407                .map_err(FHIRHTTPError::from)?;
408
409            Ok(request)
410        }
411
412        FHIRRequest::History(history_request) => match history_request {
413            HistoryRequest::Instance(fhirhistory_instance_request) => {
414                let mut request_url = state
415                    .api_url
416                    .join(&format!(
417                        "{}/{}/{}/_history",
418                        state.api_url.path(),
419                        fhirhistory_instance_request.resource_type.as_ref(),
420                        fhirhistory_instance_request.id
421                    ))
422                    .map_err(|_e| {
423                        FHIRHTTPError::UrlParseError("HistoryInstance request".to_string())
424                    })?;
425
426                fhir_parameter_to_query_parameters(
427                    &mut request_url,
428                    &fhirhistory_instance_request.parameters,
429                );
430
431                let request = state
432                    .client
433                    .get(request_url)
434                    .header("Accept", "application/fhir+json")
435                    .header("Content-Type", "application/fhir+json, application/json")
436                    .build()
437                    .map_err(FHIRHTTPError::from)?;
438
439                Ok(request)
440            }
441            HistoryRequest::Type(fhirhistory_type_request) => {
442                let mut request_url = state
443                    .api_url
444                    .join(&format!(
445                        "{}/{}/_history",
446                        state.api_url.path(),
447                        fhirhistory_type_request.resource_type.as_ref(),
448                    ))
449                    .map_err(|_e| {
450                        FHIRHTTPError::UrlParseError("HistoryType request".to_string())
451                    })?;
452
453                fhir_parameter_to_query_parameters(
454                    &mut request_url,
455                    &fhirhistory_type_request.parameters,
456                );
457
458                let request = state
459                    .client
460                    .get(request_url)
461                    .header("Accept", "application/fhir+json")
462                    .header("Content-Type", "application/fhir+json, application/json")
463                    .build()
464                    .map_err(FHIRHTTPError::from)?;
465
466                Ok(request)
467            }
468            HistoryRequest::System(fhirhistory_system_request) => {
469                let mut request_url = state
470                    .api_url
471                    .join(&format!("{}/_history", state.api_url.path()))
472                    .map_err(|_e| {
473                        FHIRHTTPError::UrlParseError("HistorySystem request".to_string())
474                    })?;
475
476                fhir_parameter_to_query_parameters(
477                    &mut request_url,
478                    &fhirhistory_system_request.parameters,
479                );
480
481                let request = state
482                    .client
483                    .get(request_url)
484                    .header("Accept", "application/fhir+json")
485                    .header("Content-Type", "application/fhir+json, application/json")
486                    .build()
487                    .map_err(FHIRHTTPError::from)?;
488
489                Ok(request)
490            }
491        },
492
493        FHIRRequest::Invocation(invoke_request) => match invoke_request {
494            InvocationRequest::Instance(fhirinvoke_instance_request) => {
495                let request_url = state
496                    .api_url
497                    .join(&format!(
498                        "{}/{}/{}/${}",
499                        state.api_url.path(),
500                        fhirinvoke_instance_request.resource_type.as_ref(),
501                        fhirinvoke_instance_request.id,
502                        fhirinvoke_instance_request.operation.name(),
503                    ))
504                    .map_err(|_e| {
505                        FHIRHTTPError::UrlParseError("InvokeInstance request".to_string())
506                    })?;
507
508                // Parameters for invoke are passed in the body as a Parameters resource.
509                let body = haste_fhir_serialization_json::to_string(
510                    &fhirinvoke_instance_request.parameters,
511                )
512                .map_err(FHIRHTTPError::from)?;
513
514                let request = state
515                    .client
516                    .post(request_url)
517                    .header("Accept", "application/fhir+json")
518                    .header("Content-Type", "application/fhir+json, application/json")
519                    .body(body)
520                    .build()
521                    .map_err(FHIRHTTPError::from)?;
522
523                Ok(request)
524            }
525            InvocationRequest::Type(fhirinvoke_type_request) => {
526                let request_url = state
527                    .api_url
528                    .join(&format!(
529                        "{}/{}/${}",
530                        state.api_url.path(),
531                        fhirinvoke_type_request.resource_type.as_ref(),
532                        fhirinvoke_type_request.operation.name(),
533                    ))
534                    .map_err(|_e| FHIRHTTPError::UrlParseError("InvokeType request".to_string()))?;
535
536                // Parameters for invoke are passed in the body as a Parameters resource.
537                let body =
538                    haste_fhir_serialization_json::to_string(&fhirinvoke_type_request.parameters)
539                        .map_err(FHIRHTTPError::from)?;
540
541                let request = state
542                    .client
543                    .post(request_url)
544                    .header("Accept", "application/fhir+json")
545                    .header("Content-Type", "application/fhir+json, application/json")
546                    .body(body)
547                    .build()
548                    .map_err(FHIRHTTPError::from)?;
549
550                Ok(request)
551            }
552            InvocationRequest::System(fhirinvoke_system_request) => {
553                let request_url = state
554                    .api_url
555                    .join(&format!(
556                        "{}/${}",
557                        state.api_url.path(),
558                        fhirinvoke_system_request.operation.name(),
559                    ))
560                    .map_err(|_e| {
561                        FHIRHTTPError::UrlParseError("InvokeSystem request".to_string())
562                    })?;
563
564                // Parameters for invoke are passed in the body as a Parameters resource.
565                let body =
566                    haste_fhir_serialization_json::to_string(&fhirinvoke_system_request.parameters)
567                        .map_err(FHIRHTTPError::from)?;
568
569                let request = state
570                    .client
571                    .post(request_url)
572                    .header("Accept", "application/fhir+json")
573                    .header("Content-Type", "application/fhir+json, application/json")
574                    .body(body)
575                    .build()
576                    .map_err(FHIRHTTPError::from)?;
577
578                Ok(request)
579            }
580        },
581        FHIRRequest::Batch(fhirbatch_request) => {
582            let body = haste_fhir_serialization_json::to_string(&fhirbatch_request.resource)
583                .map_err(FHIRHTTPError::from)?;
584
585            let request = state
586                .client
587                .post(state.api_url.clone())
588                .header("Accept", "application/fhir+json")
589                .header("Content-Type", "application/fhir+json, application/json")
590                .body(body)
591                .build()
592                .map_err(FHIRHTTPError::from)?;
593
594            Ok(request)
595        }
596    };
597
598    let mut request = request?;
599
600    if let Some(get_access_token) = state.get_access_token.as_ref() {
601        let token = get_access_token().await?;
602
603        request.headers_mut().insert(
604            "Authorization",
605            HeaderValue::from_str(&format!("Bearer {}", token)).map_err(|_| {
606                OperationOutcomeError::error(
607                    IssueType::Invalid(None),
608                    "Failed to create Authorization header.".to_string(),
609                )
610            })?,
611        );
612    }
613
614    Ok(request)
615}
616
617async fn check_for_errors(
618    status: &reqwest::StatusCode,
619    body: Option<&[u8]>,
620) -> Result<(), OperationOutcomeError> {
621    if !status.is_success() {
622        if let Some(body) = body
623            && let Ok(operation_outcome) =
624                haste_fhir_serialization_json::from_bytes::<OperationOutcome>(&body)
625        {
626            return Err(OperationOutcomeError::new(None, operation_outcome));
627        }
628
629        return Err(OperationOutcomeError::error(
630            IssueType::Exception(None),
631            format!("HTTP returned error '{}'.", status),
632        ));
633    }
634    Ok(())
635}
636
637async fn http_response_to_fhir_response(
638    fhir_request: &FHIRRequest,
639    response: reqwest::Response,
640) -> Result<FHIRResponse, OperationOutcomeError> {
641    match fhir_request {
642        FHIRRequest::Read(_) => {
643            let status = response.status();
644            let body = response
645                .bytes()
646                .await
647                .map_err(FHIRHTTPError::ReqwestError)?;
648
649            check_for_errors(&status, Some(&body)).await?;
650
651            let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
652                .map_err(FHIRHTTPError::from)?;
653            Ok(FHIRResponse::Read(FHIRReadResponse {
654                resource: Some(resource),
655            }))
656        }
657        FHIRRequest::Create(_) => {
658            let status = response.status();
659            let body = response
660                .bytes()
661                .await
662                .map_err(FHIRHTTPError::ReqwestError)?;
663
664            check_for_errors(&status, Some(&body)).await?;
665
666            let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
667                .map_err(FHIRHTTPError::from)?;
668            Ok(FHIRResponse::Create(FHIRCreateResponse { resource }))
669        }
670        FHIRRequest::Patch(_) => {
671            let status = response.status();
672            let body = response
673                .bytes()
674                .await
675                .map_err(FHIRHTTPError::ReqwestError)?;
676
677            check_for_errors(&status, Some(&body)).await?;
678
679            let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
680                .map_err(FHIRHTTPError::from)?;
681            Ok(FHIRResponse::Patch(FHIRPatchResponse { resource }))
682        }
683        FHIRRequest::Transaction(_) => {
684            let status = response.status();
685            let body = response
686                .bytes()
687                .await
688                .map_err(FHIRHTTPError::ReqwestError)?;
689
690            check_for_errors(&status, Some(&body)).await?;
691
692            let resource = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
693                .map_err(FHIRHTTPError::from)?;
694
695            Ok(FHIRResponse::Transaction(
696                request::FHIRTransactionResponse { resource },
697            ))
698        }
699        FHIRRequest::VersionRead(_) => {
700            let status = response.status();
701            let body = response
702                .bytes()
703                .await
704                .map_err(FHIRHTTPError::ReqwestError)?;
705
706            check_for_errors(&status, Some(&body)).await?;
707
708            let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
709                .map_err(FHIRHTTPError::from)?;
710            Ok(FHIRResponse::VersionRead(
711                request::FHIRVersionReadResponse { resource },
712            ))
713        }
714        FHIRRequest::Update(update_request) => match &update_request {
715            UpdateRequest::Instance(_) => {
716                let status = response.status();
717                let body = response
718                    .bytes()
719                    .await
720                    .map_err(FHIRHTTPError::ReqwestError)?;
721
722                check_for_errors(&status, Some(&body)).await?;
723
724                let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
725                    .map_err(FHIRHTTPError::from)?;
726
727                Ok(FHIRResponse::Update(request::FHIRUpdateResponse {
728                    resource,
729                }))
730            }
731            UpdateRequest::Conditional(_) => {
732                let status = response.status();
733                let body = response
734                    .bytes()
735                    .await
736                    .map_err(FHIRHTTPError::ReqwestError)?;
737
738                check_for_errors(&status, Some(&body)).await?;
739
740                let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
741                    .map_err(FHIRHTTPError::from)?;
742
743                Ok(FHIRResponse::Update(request::FHIRUpdateResponse {
744                    resource,
745                }))
746            }
747        },
748
749        FHIRRequest::Delete(delete_request) => match delete_request {
750            DeleteRequest::Instance(_) => {
751                let status = response.status();
752                let body = response
753                    .bytes()
754                    .await
755                    .map_err(FHIRHTTPError::ReqwestError)?;
756
757                check_for_errors(&status, Some(&body)).await?;
758
759                let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
760                    .map_err(FHIRHTTPError::from)?;
761
762                Ok(FHIRResponse::Delete(DeleteResponse::Instance(
763                    request::FHIRDeleteInstanceResponse { resource },
764                )))
765            }
766            DeleteRequest::Type(_) => {
767                let status = response.status();
768                let body = response
769                    .bytes()
770                    .await
771                    .map_err(FHIRHTTPError::ReqwestError)?;
772
773                check_for_errors(&status, Some(&body)).await?;
774
775                let resources = haste_fhir_serialization_json::from_bytes::<Vec<Resource>>(&body)
776                    .map_err(FHIRHTTPError::from)?;
777
778                Ok(FHIRResponse::Delete(DeleteResponse::Type(
779                    request::FHIRDeleteTypeResponse {
780                        resource: resources,
781                    },
782                )))
783            }
784            DeleteRequest::System(_) => {
785                let status = response.status();
786                let body = response
787                    .bytes()
788                    .await
789                    .map_err(FHIRHTTPError::ReqwestError)?;
790
791                check_for_errors(&status, Some(&body)).await?;
792
793                let resources = haste_fhir_serialization_json::from_bytes::<Vec<Resource>>(&body)
794                    .map_err(FHIRHTTPError::from)?;
795
796                Ok(FHIRResponse::Delete(DeleteResponse::System(
797                    request::FHIRDeleteSystemResponse {
798                        resource: resources,
799                    },
800                )))
801            }
802        },
803        FHIRRequest::Capabilities => {
804            let status = response.status();
805            let body = response
806                .bytes()
807                .await
808                .map_err(FHIRHTTPError::ReqwestError)?;
809
810            check_for_errors(&status, Some(&body)).await?;
811
812            let capabilities =
813                haste_fhir_serialization_json::from_bytes::<CapabilityStatement>(&body)
814                    .map_err(FHIRHTTPError::from)?;
815
816            Ok(FHIRResponse::Capabilities(
817                request::FHIRCapabilitiesResponse { capabilities },
818            ))
819        }
820
821        FHIRRequest::Search(search_request) => match search_request {
822            SearchRequest::Type(_) => {
823                let status = response.status();
824                let body = response
825                    .bytes()
826                    .await
827                    .map_err(FHIRHTTPError::ReqwestError)?;
828
829                check_for_errors(&status, Some(&body)).await?;
830
831                let bundle = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
832                    .map_err(FHIRHTTPError::from)?;
833
834                Ok(FHIRResponse::Search(SearchResponse::Type(
835                    request::FHIRSearchTypeResponse { bundle },
836                )))
837            }
838            SearchRequest::System(_) => {
839                let status = response.status();
840                let body = response
841                    .bytes()
842                    .await
843                    .map_err(FHIRHTTPError::ReqwestError)?;
844
845                check_for_errors(&status, Some(&body)).await?;
846
847                let bundle = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
848                    .map_err(FHIRHTTPError::from)?;
849
850                Ok(FHIRResponse::Search(SearchResponse::System(
851                    request::FHIRSearchSystemResponse { bundle },
852                )))
853            }
854        },
855
856        FHIRRequest::History(history_request) => match history_request {
857            HistoryRequest::Instance(_) => {
858                let status = response.status();
859                let body = response
860                    .bytes()
861                    .await
862                    .map_err(FHIRHTTPError::ReqwestError)?;
863
864                check_for_errors(&status, Some(&body)).await?;
865
866                let bundle = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
867                    .map_err(FHIRHTTPError::from)?;
868
869                Ok(FHIRResponse::History(HistoryResponse::Instance(
870                    request::FHIRHistoryInstanceResponse { bundle },
871                )))
872            }
873            HistoryRequest::Type(_) => {
874                let status = response.status();
875                let body = response
876                    .bytes()
877                    .await
878                    .map_err(FHIRHTTPError::ReqwestError)?;
879
880                check_for_errors(&status, Some(&body)).await?;
881
882                let bundle = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
883                    .map_err(FHIRHTTPError::from)?;
884
885                Ok(FHIRResponse::History(HistoryResponse::Type(
886                    request::FHIRHistoryTypeResponse { bundle },
887                )))
888            }
889            HistoryRequest::System(_) => {
890                let status = response.status();
891                let body = response
892                    .bytes()
893                    .await
894                    .map_err(FHIRHTTPError::ReqwestError)?;
895
896                check_for_errors(&status, Some(&body)).await?;
897
898                let bundle = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
899                    .map_err(FHIRHTTPError::from)?;
900
901                Ok(FHIRResponse::History(HistoryResponse::System(
902                    request::FHIRHistorySystemResponse { bundle },
903                )))
904            }
905        },
906
907        FHIRRequest::Invocation(invoke_request) => match invoke_request {
908            InvocationRequest::Instance(_) => {
909                let status = response.status();
910                let body = response
911                    .bytes()
912                    .await
913                    .map_err(FHIRHTTPError::ReqwestError)?;
914
915                check_for_errors(&status, Some(&body)).await?;
916
917                let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
918                    .map_err(FHIRHTTPError::from)?;
919
920                Ok(FHIRResponse::Invoke(InvokeResponse::Instance(
921                    request::FHIRInvokeInstanceResponse { resource },
922                )))
923            }
924            InvocationRequest::Type(_) => {
925                let status = response.status();
926                let body = response
927                    .bytes()
928                    .await
929                    .map_err(FHIRHTTPError::ReqwestError)?;
930
931                check_for_errors(&status, Some(&body)).await?;
932
933                let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
934                    .map_err(FHIRHTTPError::from)?;
935
936                Ok(FHIRResponse::Invoke(InvokeResponse::Type(
937                    request::FHIRInvokeTypeResponse { resource },
938                )))
939            }
940            InvocationRequest::System(_) => {
941                let status = response.status();
942                let body = response
943                    .bytes()
944                    .await
945                    .map_err(FHIRHTTPError::ReqwestError)?;
946
947                check_for_errors(&status, Some(&body)).await?;
948
949                let resource = haste_fhir_serialization_json::from_bytes::<Resource>(&body)
950                    .map_err(FHIRHTTPError::from)?;
951
952                Ok(FHIRResponse::Invoke(InvokeResponse::System(
953                    request::FHIRInvokeSystemResponse { resource },
954                )))
955            }
956        },
957
958        FHIRRequest::Batch(_) => {
959            let status = response.status();
960            let body = response
961                .bytes()
962                .await
963                .map_err(FHIRHTTPError::ReqwestError)?;
964
965            check_for_errors(&status, Some(&body)).await?;
966
967            let resource = haste_fhir_serialization_json::from_bytes::<Bundle>(&body)
968                .map_err(FHIRHTTPError::from)?;
969
970            Ok(FHIRResponse::Batch(request::FHIRBatchResponse { resource }))
971        }
972    }
973}
974
975struct HTTPMiddleware {}
976impl HTTPMiddleware {
977    fn new() -> Self {
978        HTTPMiddleware {}
979    }
980}
981impl<CTX: Send + 'static>
982    MiddlewareChain<Arc<FHIRHttpState>, CTX, FHIRRequest, FHIRResponse, OperationOutcomeError>
983    for HTTPMiddleware
984{
985    fn call(
986        &self,
987        state: Arc<FHIRHttpState>,
988        context: Context<CTX, FHIRRequest, FHIRResponse>,
989        _next: Option<
990            Arc<
991                Next<
992                    Arc<FHIRHttpState>,
993                    Context<CTX, FHIRRequest, FHIRResponse>,
994                    OperationOutcomeError,
995                >,
996            >,
997        >,
998    ) -> Pin<
999        Box<
1000            dyn Future<
1001                    Output = Result<Context<CTX, FHIRRequest, FHIRResponse>, OperationOutcomeError>,
1002                > + Send,
1003        >,
1004    > {
1005        Box::pin(async move {
1006            let http_request = fhir_request_to_http_request(&state, &context.request).await?;
1007            let response = state
1008                .client
1009                .execute(http_request)
1010                .await
1011                .map_err(FHIRHTTPError::ReqwestError)?;
1012
1013            let mut next_context = context;
1014            let fhir_response =
1015                http_response_to_fhir_response(&next_context.request, response).await?;
1016            next_context.response = Some(fhir_response);
1017
1018            Ok(next_context)
1019        })
1020    }
1021}
1022
1023impl<CTX: 'static + Send + Sync> FHIRHttpClient<CTX> {
1024    pub fn new(state: FHIRHttpState) -> Self {
1025        let middleware = Middleware::new(vec![Box::new(HTTPMiddleware::new())]);
1026        FHIRHttpClient {
1027            state: Arc::new(state),
1028            middleware,
1029        }
1030    }
1031}
1032
1033impl<CTX: 'static + Send + Sync> FHIRClient<CTX, OperationOutcomeError> for FHIRHttpClient<CTX> {
1034    async fn request(
1035        &self,
1036        ctx: CTX,
1037        request: crate::request::FHIRRequest,
1038    ) -> Result<crate::request::FHIRResponse, OperationOutcomeError> {
1039        let response = self
1040            .middleware
1041            .call(self.state.clone(), ctx, request)
1042            .await?;
1043
1044        response
1045            .response
1046            .ok_or_else(|| FHIRHTTPError::NoResponse.into())
1047    }
1048
1049    async fn capabilities(&self, _ctx: CTX) -> Result<CapabilityStatement, OperationOutcomeError> {
1050        let res = self
1051            .middleware
1052            .call(self.state.clone(), _ctx, FHIRRequest::Capabilities)
1053            .await?;
1054
1055        match res.response {
1056            Some(FHIRResponse::Capabilities(capabilities_response)) => {
1057                Ok(capabilities_response.capabilities)
1058            }
1059            _ => Err(FHIRHTTPError::NoResponse.into()),
1060        }
1061    }
1062
1063    async fn search_system(
1064        &self,
1065        ctx: CTX,
1066        parameters: crate::ParsedParameters,
1067    ) -> Result<Bundle, OperationOutcomeError> {
1068        let res = self
1069            .middleware
1070            .call(
1071                self.state.clone(),
1072                ctx,
1073                FHIRRequest::Search(SearchRequest::System(request::FHIRSearchSystemRequest {
1074                    parameters,
1075                })),
1076            )
1077            .await?;
1078        match res.response {
1079            Some(FHIRResponse::Search(SearchResponse::System(search_system_response))) => {
1080                Ok(search_system_response.bundle)
1081            }
1082            _ => Err(FHIRHTTPError::NoResponse.into()),
1083        }
1084    }
1085
1086    async fn search_type(
1087        &self,
1088        ctx: CTX,
1089        resource_type: ResourceType,
1090        parameters: crate::ParsedParameters,
1091    ) -> Result<Bundle, OperationOutcomeError> {
1092        let res = self
1093            .middleware
1094            .call(
1095                self.state.clone(),
1096                ctx,
1097                FHIRRequest::Search(SearchRequest::Type(request::FHIRSearchTypeRequest {
1098                    resource_type,
1099                    parameters,
1100                })),
1101            )
1102            .await?;
1103        match res.response {
1104            Some(FHIRResponse::Search(SearchResponse::Type(search_type_response))) => {
1105                Ok(search_type_response.bundle)
1106            }
1107            _ => Err(FHIRHTTPError::NoResponse.into()),
1108        }
1109    }
1110
1111    async fn create(
1112        &self,
1113        ctx: CTX,
1114        resource_type: ResourceType,
1115        resource: Resource,
1116    ) -> Result<Resource, OperationOutcomeError> {
1117        let res = self
1118            .middleware
1119            .call(
1120                self.state.clone(),
1121                ctx,
1122                FHIRRequest::Create(request::FHIRCreateRequest {
1123                    resource_type,
1124                    resource,
1125                }),
1126            )
1127            .await?;
1128
1129        match res.response {
1130            Some(FHIRResponse::Create(create_response)) => Ok(create_response.resource),
1131            _ => Err(FHIRHTTPError::NoResponse.into()),
1132        }
1133    }
1134
1135    async fn update(
1136        &self,
1137        ctx: CTX,
1138        resource_type: ResourceType,
1139        id: String,
1140        resource: Resource,
1141    ) -> Result<Resource, OperationOutcomeError> {
1142        let res = self
1143            .middleware
1144            .call(
1145                self.state.clone(),
1146                ctx,
1147                FHIRRequest::Update(UpdateRequest::Instance(
1148                    request::FHIRUpdateInstanceRequest {
1149                        resource_type,
1150                        id,
1151                        resource,
1152                    },
1153                )),
1154            )
1155            .await?;
1156        match res.response {
1157            Some(FHIRResponse::Update(update_response)) => Ok(update_response.resource),
1158            _ => Err(FHIRHTTPError::NoResponse.into()),
1159        }
1160    }
1161
1162    async fn conditional_update(
1163        &self,
1164        ctx: CTX,
1165        resource_type: ResourceType,
1166        parameters: crate::ParsedParameters,
1167        resource: Resource,
1168    ) -> Result<Resource, OperationOutcomeError> {
1169        let res = self
1170            .middleware
1171            .call(
1172                self.state.clone(),
1173                ctx,
1174                FHIRRequest::Update(UpdateRequest::Conditional(
1175                    request::FHIRConditionalUpdateRequest {
1176                        resource_type,
1177                        parameters,
1178                        resource,
1179                    },
1180                )),
1181            )
1182            .await?;
1183        match res.response {
1184            Some(FHIRResponse::Update(update_response)) => Ok(update_response.resource),
1185            _ => Err(FHIRHTTPError::NoResponse.into()),
1186        }
1187    }
1188
1189    async fn patch(
1190        &self,
1191        ctx: CTX,
1192        resource_type: ResourceType,
1193        id: String,
1194        patch: json_patch::Patch,
1195    ) -> Result<Resource, OperationOutcomeError> {
1196        let res = self
1197            .middleware
1198            .call(
1199                self.state.clone(),
1200                ctx,
1201                FHIRRequest::Patch(request::FHIRPatchRequest {
1202                    resource_type,
1203                    id,
1204                    patch,
1205                }),
1206            )
1207            .await?;
1208
1209        match res.response {
1210            Some(FHIRResponse::Patch(patch_response)) => Ok(patch_response.resource),
1211            _ => Err(FHIRHTTPError::NoResponse.into()),
1212        }
1213    }
1214
1215    async fn read(
1216        &self,
1217        ctx: CTX,
1218        resource_type: ResourceType,
1219        id: String,
1220    ) -> Result<Option<Resource>, OperationOutcomeError> {
1221        let res = self
1222            .middleware
1223            .call(
1224                self.state.clone(),
1225                ctx,
1226                FHIRRequest::Read(request::FHIRReadRequest { resource_type, id }),
1227            )
1228            .await?;
1229
1230        match res.response {
1231            Some(FHIRResponse::Read(read_response)) => Ok(read_response.resource),
1232            _ => Err(FHIRHTTPError::NoResponse.into()),
1233        }
1234    }
1235
1236    async fn vread(
1237        &self,
1238        ctx: CTX,
1239        resource_type: ResourceType,
1240        id: String,
1241        version_id: String,
1242    ) -> Result<Option<Resource>, OperationOutcomeError> {
1243        let res = self
1244            .middleware
1245            .call(
1246                self.state.clone(),
1247                ctx,
1248                FHIRRequest::VersionRead(request::FHIRVersionReadRequest {
1249                    resource_type,
1250                    id,
1251                    version_id: VersionId::new(version_id),
1252                }),
1253            )
1254            .await?;
1255
1256        match res.response {
1257            Some(FHIRResponse::VersionRead(version_read_response)) => {
1258                Ok(Some(version_read_response.resource))
1259            }
1260            _ => Err(FHIRHTTPError::NoResponse.into()),
1261        }
1262    }
1263
1264    async fn delete_instance(
1265        &self,
1266        ctx: CTX,
1267        resource_type: ResourceType,
1268        id: String,
1269    ) -> Result<(), OperationOutcomeError> {
1270        let res = self
1271            .middleware
1272            .call(
1273                self.state.clone(),
1274                ctx,
1275                FHIRRequest::Delete(DeleteRequest::Instance(
1276                    request::FHIRDeleteInstanceRequest { resource_type, id },
1277                )),
1278            )
1279            .await?;
1280
1281        match res.response {
1282            Some(FHIRResponse::Delete(_delete_instance_response)) => Ok(()),
1283            _ => Err(FHIRHTTPError::NoResponse.into()),
1284        }
1285    }
1286
1287    async fn delete_type(
1288        &self,
1289        ctx: CTX,
1290        resource_type: ResourceType,
1291        parameters: crate::ParsedParameters,
1292    ) -> Result<(), OperationOutcomeError> {
1293        let res = self
1294            .middleware
1295            .call(
1296                self.state.clone(),
1297                ctx,
1298                FHIRRequest::Delete(DeleteRequest::Type(request::FHIRDeleteTypeRequest {
1299                    resource_type,
1300                    parameters,
1301                })),
1302            )
1303            .await?;
1304        match res.response {
1305            Some(FHIRResponse::Delete(_delete_type_response)) => Ok(()),
1306            _ => Err(FHIRHTTPError::NoResponse.into()),
1307        }
1308    }
1309
1310    async fn delete_system(
1311        &self,
1312        ctx: CTX,
1313        parameters: crate::ParsedParameters,
1314    ) -> Result<(), OperationOutcomeError> {
1315        let res = self
1316            .middleware
1317            .call(
1318                self.state.clone(),
1319                ctx,
1320                FHIRRequest::Delete(DeleteRequest::System(request::FHIRDeleteSystemRequest {
1321                    parameters,
1322                })),
1323            )
1324            .await?;
1325        match res.response {
1326            Some(FHIRResponse::Delete(_delete_system_response)) => Ok(()),
1327            _ => Err(FHIRHTTPError::NoResponse.into()),
1328        }
1329    }
1330
1331    async fn history_system(
1332        &self,
1333        ctx: CTX,
1334        parameters: crate::ParsedParameters,
1335    ) -> Result<Bundle, OperationOutcomeError> {
1336        let res = self
1337            .middleware
1338            .call(
1339                self.state.clone(),
1340                ctx,
1341                FHIRRequest::History(HistoryRequest::System(request::FHIRHistorySystemRequest {
1342                    parameters,
1343                })),
1344            )
1345            .await?;
1346
1347        match res.response {
1348            Some(FHIRResponse::History(HistoryResponse::System(history_system_response))) => {
1349                Ok(history_system_response.bundle)
1350            }
1351            _ => Err(FHIRHTTPError::NoResponse.into()),
1352        }
1353    }
1354
1355    async fn history_type(
1356        &self,
1357        ctx: CTX,
1358        resource_type: ResourceType,
1359        parameters: crate::ParsedParameters,
1360    ) -> Result<Bundle, OperationOutcomeError> {
1361        let res = self
1362            .middleware
1363            .call(
1364                self.state.clone(),
1365                ctx,
1366                FHIRRequest::History(HistoryRequest::Type(request::FHIRHistoryTypeRequest {
1367                    resource_type,
1368                    parameters,
1369                })),
1370            )
1371            .await?;
1372
1373        match res.response {
1374            Some(FHIRResponse::History(HistoryResponse::Type(history_type_response))) => {
1375                Ok(history_type_response.bundle)
1376            }
1377            _ => Err(FHIRHTTPError::NoResponse.into()),
1378        }
1379    }
1380
1381    async fn history_instance(
1382        &self,
1383        ctx: CTX,
1384        resource_type: ResourceType,
1385        id: String,
1386        parameters: crate::ParsedParameters,
1387    ) -> Result<Bundle, OperationOutcomeError> {
1388        let res = self
1389            .middleware
1390            .call(
1391                self.state.clone(),
1392                ctx,
1393                FHIRRequest::History(HistoryRequest::Instance(
1394                    request::FHIRHistoryInstanceRequest {
1395                        resource_type,
1396                        id,
1397                        parameters,
1398                    },
1399                )),
1400            )
1401            .await?;
1402
1403        match res.response {
1404            Some(FHIRResponse::History(HistoryResponse::Instance(history_instance_response))) => {
1405                Ok(history_instance_response.bundle)
1406            }
1407            _ => Err(FHIRHTTPError::NoResponse.into()),
1408        }
1409    }
1410
1411    async fn invoke_instance(
1412        &self,
1413        ctx: CTX,
1414        resource_type: ResourceType,
1415        id: String,
1416        operation: String,
1417        parameters: Parameters,
1418    ) -> Result<Resource, OperationOutcomeError> {
1419        let res = self
1420            .middleware
1421            .call(
1422                self.state.clone(),
1423                ctx,
1424                FHIRRequest::Invocation(InvocationRequest::Instance(
1425                    request::FHIRInvokeInstanceRequest {
1426                        resource_type,
1427                        id,
1428                        operation: Operation::new(&operation).map_err(|_e| {
1429                            OperationOutcomeError::error(
1430                                IssueType::Exception(None),
1431                                "invalid operation".to_string(),
1432                            )
1433                        })?,
1434                        parameters,
1435                    },
1436                )),
1437            )
1438            .await?;
1439
1440        match res.response {
1441            Some(FHIRResponse::Invoke(InvokeResponse::Instance(invoke_instance_response))) => {
1442                Ok(invoke_instance_response.resource)
1443            }
1444            _ => Err(FHIRHTTPError::NoResponse.into()),
1445        }
1446    }
1447
1448    async fn invoke_type(
1449        &self,
1450        ctx: CTX,
1451        resource_type: ResourceType,
1452        operation: String,
1453        parameters: Parameters,
1454    ) -> Result<Resource, OperationOutcomeError> {
1455        let res = self
1456            .middleware
1457            .call(
1458                self.state.clone(),
1459                ctx,
1460                FHIRRequest::Invocation(InvocationRequest::Type(request::FHIRInvokeTypeRequest {
1461                    resource_type,
1462                    operation: Operation::new(&operation).map_err(|_e| {
1463                        OperationOutcomeError::error(
1464                            IssueType::Exception(None),
1465                            "invalid operation".to_string(),
1466                        )
1467                    })?,
1468                    parameters,
1469                })),
1470            )
1471            .await?;
1472
1473        match res.response {
1474            Some(FHIRResponse::Invoke(InvokeResponse::Type(invoke_type_response))) => {
1475                Ok(invoke_type_response.resource)
1476            }
1477            _ => Err(FHIRHTTPError::NoResponse.into()),
1478        }
1479    }
1480
1481    async fn invoke_system(
1482        &self,
1483        ctx: CTX,
1484        operation: String,
1485        parameters: Parameters,
1486    ) -> Result<Resource, OperationOutcomeError> {
1487        let res = self
1488            .middleware
1489            .call(
1490                self.state.clone(),
1491                ctx,
1492                FHIRRequest::Invocation(InvocationRequest::System(
1493                    request::FHIRInvokeSystemRequest {
1494                        operation: Operation::new(&operation).map_err(|_e| {
1495                            OperationOutcomeError::error(
1496                                IssueType::Exception(None),
1497                                "invalid operation".to_string(),
1498                            )
1499                        })?,
1500                        parameters,
1501                    },
1502                )),
1503            )
1504            .await?;
1505
1506        match res.response {
1507            Some(FHIRResponse::Invoke(InvokeResponse::System(invoke_system_response))) => {
1508                Ok(invoke_system_response.resource)
1509            }
1510            _ => Err(FHIRHTTPError::NoResponse.into()),
1511        }
1512    }
1513
1514    async fn transaction(&self, ctx: CTX, bundle: Bundle) -> Result<Bundle, OperationOutcomeError> {
1515        let res = self
1516            .middleware
1517            .call(
1518                self.state.clone(),
1519                ctx,
1520                FHIRRequest::Transaction(request::FHIRTransactionRequest { resource: bundle }),
1521            )
1522            .await?;
1523
1524        match res.response {
1525            Some(FHIRResponse::Transaction(transaction_response)) => {
1526                Ok(transaction_response.resource)
1527            }
1528            _ => Err(FHIRHTTPError::NoResponse.into()),
1529        }
1530    }
1531
1532    async fn batch(&self, ctx: CTX, bundle: Bundle) -> Result<Bundle, OperationOutcomeError> {
1533        let res = self
1534            .middleware
1535            .call(
1536                self.state.clone(),
1537                ctx,
1538                FHIRRequest::Batch(request::FHIRBatchRequest { resource: bundle }),
1539            )
1540            .await?;
1541
1542        match res.response {
1543            Some(FHIRResponse::Batch(batch_response)) => Ok(batch_response.resource),
1544            _ => Err(FHIRHTTPError::NoResponse.into()),
1545        }
1546    }
1547}