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