haste_fhir_terminology/resolvers/
remote.rs

1use 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                    // Perform search for an entry with the given canonical URL.
59                    && 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                    // Read the repository for the search result.
79                    && 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}