Skip to main content

haste_fhir_client/
http.rs

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