haste_fhir_terminology/resolvers/
remote.rs1use dashmap::DashMap;
2use haste_fhir_client::request::{FHIRSearchTypeRequest, SearchRequest};
3use haste_fhir_client::url::{Parameter, ParsedParameter, ParsedParameters};
4use haste_fhir_model::r4::generated::resources::{Resource, ResourceType};
5use haste_fhir_model::r4::generated::terminology::IssueType;
6use haste_fhir_operation_error::OperationOutcomeError;
7use haste_fhir_search::SearchEngine;
8use haste_jwt::{ProjectId, TenantId};
9use haste_repository::Repository;
10use haste_repository::types::SupportedFHIRVersions::R4;
11use std::pin::Pin;
12use std::sync::Arc;
13
14use crate::resolvers::CanonicalResolver;
15
16fn generate_key(resource_type: &ResourceType, url: &str) -> String {
17 format!("{:?}::{}", resource_type, url)
18}
19
20pub struct LRUCanonicalRemoteResolver<
21 Repo: Repository + Send + Sync + 'static,
22 Search: SearchEngine + Send + Sync + 'static,
23> {
24 cache: Arc<DashMap<String, Resource>>,
25 search: Arc<Search>,
26 repository: Arc<Repo>,
27}
28
29impl<Repo: Repository + Send + Sync + 'static, Search: SearchEngine + Send + Sync + 'static>
30 LRUCanonicalRemoteResolver<Repo, Search>
31{
32 pub fn new(repository: Arc<Repo>, search: Arc<Search>) -> Self {
33 Self {
34 cache: Arc::new(DashMap::new()),
35 search,
36 repository,
37 }
38 }
39}
40
41impl<Repo: Repository + Send + Sync + 'static, Search: SearchEngine + Send + Sync + 'static>
42 CanonicalResolver for LRUCanonicalRemoteResolver<Repo, Search>
43{
44 fn resolve(
45 &self,
46 resource_type: ResourceType,
47 canonical_url: String,
48 ) -> Pin<Box<dyn Future<Output = Result<Resource, OperationOutcomeError>> + Send>> {
49 let cache = self.cache.clone();
50 let search = self.search.clone();
51 let repository = self.repository.clone();
52 Box::pin(async move {
53 let key = generate_key(&resource_type, &canonical_url);
54 if let Some(cached) = cache.get(&key) {
55 Ok(cached.clone())
56 } else {
57 if let Some(url) = canonical_url.split('|').next()
58 && let Some(entry) = search
60 .search(
61 &R4,
62 &TenantId::System,
63 &ProjectId::System,
64 &SearchRequest::Type(FHIRSearchTypeRequest {
65 resource_type: resource_type.clone(),
66 parameters: ParsedParameters::new(vec![ParsedParameter::Resource(Parameter {
67 name: "url".to_string(),
68 value: vec![url.to_string()],
69 modifier: None,
70 chains: None,
71 })]),
72 }),
73 None,
74 )
75 .await?
76 .entries
77 .first()
78 && let Some(resource) = repository
80 .read_by_version_ids(
81 &TenantId::System,
82 &ProjectId::System,
83 &[&entry.version_id],
84 haste_repository::fhir::CachePolicy::Cache,
85 )
86 .await?
87 .pop()
88 {
89 cache.insert(key, resource.clone());
90 return Ok(resource);
91 }
92 Err(OperationOutcomeError::error(
93 IssueType::NotFound(None),
94 format!(
95 "Could not find resource of type '{:?}' with url '{}'",
96 resource_type, canonical_url
97 ),
98 ))
99 }
100 })
101 }
102}