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