Skip to content

Commit

Permalink
Copied recent changes over to R4B and STU3
Browse files Browse the repository at this point in the history
  • Loading branch information
jarimayenburg committed Sep 28, 2024
1 parent 00f0eda commit d649ec8
Show file tree
Hide file tree
Showing 13 changed files with 475 additions and 259 deletions.
41 changes: 16 additions & 25 deletions crates/fhir-sdk/src/client/r4b/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ use fhir_model::{
TypedResource,
},
types::Reference,
JSON_MIME_TYPE,
},
ParsedReference,
};
Expand All @@ -28,28 +27,26 @@ use self::{
patch::{PatchViaFhir, PatchViaJson},
transaction::BatchTransaction,
};
use super::{misc, Client, Error, FhirR4B};

/// FHIR MIME-type this client uses.
const MIME_TYPE: &str = JSON_MIME_TYPE;
use super::{misc, Client, Error, FhirR4B, FhirVersion};

impl Client<FhirR4B> {
/// Get the server's capabilities. Fails if the respective FHIR version is
/// not supported at all.
pub async fn capabilities(&self) -> Result<CapabilityStatement, Error> {
let url = self.url(&["metadata"]);
let request = self.0.client.get(url).header(header::ACCEPT, MIME_TYPE);

let response = self.run_request(request).await?;
self.fetch_resource(url).await
}

async fn fetch_resource<R: TryFrom<Resource>>(&self, url: Url) -> Result<R, Error> {
let response = self.fetch_url(url).await?;

response.body().await
}

/// Read any resource from any URL.
async fn read_generic<R: TryFrom<Resource>>(&self, url: Url) -> Result<Option<R>, Error> {
let request = self.0.client.get(url).header(header::ACCEPT, MIME_TYPE);

let response = self.run_request(request).await?;
let response = self.fetch_url(url).await?;

if [StatusCode::NOT_FOUND, StatusCode::GONE].contains(&response.status()) {
return Ok(None);
Expand Down Expand Up @@ -118,8 +115,8 @@ impl Client<FhirR4B> {
.0
.client
.post(url)
.header(header::ACCEPT, MIME_TYPE)
.header(header::CONTENT_TYPE, MIME_TYPE)
.header(header::ACCEPT, FhirR4B::JSON_MIME_TYPE)
.header(header::CONTENT_TYPE, FhirR4B::JSON_MIME_TYPE)
.json(resource);

let response = self.run_request(request).await?;
Expand Down Expand Up @@ -147,8 +144,8 @@ impl Client<FhirR4B> {
.0
.client
.put(url)
.header(header::ACCEPT, MIME_TYPE)
.header(header::CONTENT_TYPE, MIME_TYPE)
.header(header::ACCEPT, FhirR4B::JSON_MIME_TYPE)
.header(header::CONTENT_TYPE, FhirR4B::JSON_MIME_TYPE)
.json(resource);
if conditional {
let version_id = resource
Expand Down Expand Up @@ -186,7 +183,7 @@ impl Client<FhirR4B> {
/// Delete a FHIR resource on the server.
pub async fn delete(&self, resource_type: ResourceType, id: &str) -> Result<(), Error> {
let url = self.url(&[resource_type.as_str(), id]);
let request = self.0.client.delete(url).header(header::ACCEPT, MIME_TYPE);
let request = self.0.client.delete(url).header(header::ACCEPT, FhirR4B::JSON_MIME_TYPE);

let response = self.run_request(request).await?;

Expand All @@ -207,22 +204,16 @@ impl Client<FhirR4B> {
/// resources for an `Encounter` record.
pub async fn operation_encounter_everything(&self, id: &str) -> Result<Bundle, Error> {
let url = self.url(&["Encounter", id, "$everything"]);
let request = self.0.client.get(url).header(header::ACCEPT, MIME_TYPE);

let response = self.run_request(request).await?;

response.body().await
self.fetch_resource(url).await
}

/// Operation `$everything` on `Patient`, returning a Bundle with all
/// resources for an `Patient` record.
pub async fn operation_patient_everything(&self, id: &str) -> Result<Bundle, Error> {
let url = self.url(&["Patient", id, "$everything"]);
let request = self.0.client.get(url).header(header::ACCEPT, MIME_TYPE);

let response = self.run_request(request).await?;

response.body().await
self.fetch_resource(url).await
}

/// Operation `$match` on `Patient`, returning matches for Patient records
Expand Down Expand Up @@ -266,8 +257,8 @@ impl Client<FhirR4B> {
.0
.client
.post(url)
.header(header::ACCEPT, MIME_TYPE)
.header(header::CONTENT_TYPE, MIME_TYPE)
.header(header::ACCEPT, FhirR4B::JSON_MIME_TYPE)
.header(header::CONTENT_TYPE, FhirR4B::JSON_MIME_TYPE)
.json(&parameters);

let response = self.run_request(request).await?;
Expand Down
10 changes: 6 additions & 4 deletions crates/fhir-sdk/src/client/r4b/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ use fhir_model::r4b::resources::{
use reqwest::header::{self, HeaderValue};
use serde::Serialize;

use super::{Client, Error, FhirR4B, MIME_TYPE};
use crate::client::FhirVersion;

use super::{Client, Error, FhirR4B};

/// Builder for a PATCH request via FHIRPath for a FHIR resource.
#[derive(Debug, Clone)]
Expand Down Expand Up @@ -234,8 +236,8 @@ impl<'a> PatchViaFhir<'a> {
.0
.client
.patch(url)
.header(header::ACCEPT, MIME_TYPE)
.header(header::CONTENT_TYPE, HeaderValue::from_static(MIME_TYPE))
.header(header::ACCEPT, FhirR4B::JSON_MIME_TYPE)
.header(header::CONTENT_TYPE, HeaderValue::from_static(FhirR4B::JSON_MIME_TYPE))
.json(&parameters);

let response = self.client.run_request(request).await?;
Expand Down Expand Up @@ -359,7 +361,7 @@ impl<'a> PatchViaJson<'a> {
.0
.client
.patch(url)
.header(header::ACCEPT, MIME_TYPE)
.header(header::ACCEPT, FhirR4B::JSON_MIME_TYPE)
.header(header::CONTENT_TYPE, HeaderValue::from_static("application/json-patch+json"))
.json(&self.operations);

Expand Down
62 changes: 56 additions & 6 deletions crates/fhir-sdk/src/client/r4b/search/mod.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,77 @@
//! Client search implementation.
use crate::client::search::NextPageCursor;
use crate::client::{search::SearchExecutor, Client, Error, FhirR4B, SearchParameters};
use fhir_model::r4b::resources::{DomainResource, NamedResource, Resource};
use futures::Stream;
use paging::Paged;
use async_trait::async_trait;
use fhir_model::r4b::resources::{Bundle, DomainResource, NamedResource, Resource};
use paging::{Page, Unpaged};

#[allow(unused_imports)]
pub use params::*;
use reqwest::Url;
use tracing::warn;

mod paging;
mod params;

#[async_trait]
impl<R> SearchExecutor<R> for Client<FhirR4B>
where
R: NamedResource + DomainResource + TryFrom<Resource> + 'static,
{
fn execute_search(
#[allow(refining_impl_trait)]
async fn search_paged(
self,
params: SearchParameters,
) -> impl Stream<Item = Result<R, Error>> + Send + 'static {
page_size: Option<u32>,
) -> Result<(Page<R>, Option<NextPageCursor<Self, R>>), Error> {
let mut url = self.url(&[R::TYPE.as_str()]);
url.query_pairs_mut().extend_pairs(params.into_queries()).finish();

Paged::new(self, url)
if let Some(page_size) = page_size {
url.query_pairs_mut().append_pair("_count", &page_size.to_string());
}

self.fetch_next_page(url).await
}

#[allow(refining_impl_trait)]
async fn fetch_next_page(
self,
url: Url,
) -> Result<(Page<R>, Option<NextPageCursor<Self, R>>), Error> {
let searchset: Bundle = self.fetch_resource(url).await?;

let cursor = match find_next_page_url(&searchset) {
Some(Ok(u)) => Some(NextPageCursor::new(self.clone(), u)),
Some(Err(e)) => {
warn!("Unable to parse next page URL: {e}");

None
}
_ => None,
};

let page = Page::from_searchset(self, searchset);

Ok((page, cursor))
}

#[allow(refining_impl_trait)]
async fn search_unpaged(self, params: SearchParameters) -> Result<Unpaged<R>, Error> {
let mut url = self.url(&[R::TYPE.as_str()]);
url.query_pairs_mut().extend_pairs(params.into_queries()).finish();

let searchset: Bundle = self.fetch_resource(url.clone()).await?;

Ok(Unpaged::from_searchset(self, searchset))
}
}

/// Find the URL of the next page of the results returned in the Bundle.
pub(self) fn find_next_page_url(bundle: &Bundle) -> Option<Result<Url, Error>> {
let url_str =
bundle.link.iter().flatten().find(|link| link.relation == "next").map(|link| &link.url)?;

Some(Url::parse(url_str).map_err(|_| Error::UrlParse(url_str.to_string())))
}
Loading

0 comments on commit d649ec8

Please sign in to comment.