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 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}