1use crate::{
2 auth_n::middleware::jwt::User,
3 config::ServerConfig,
4 fhir_client::{
5 middleware::{Handler, Output, Pipeline},
6 utilities::request_to_resource_type,
7 },
8};
9use derivative::Derivative;
10use haste_fhir_client::{
11 FHIRClient,
12 request::{
13 DeleteRequest, DeleteResponse, FHIRBatchRequest, FHIRConditionalUpdateRequest,
14 FHIRCreateRequest, FHIRDeleteInstanceRequest, FHIRDeleteSystemRequest,
15 FHIRDeleteTypeRequest, FHIRHistoryInstanceRequest, FHIRHistorySystemRequest,
16 FHIRHistoryTypeRequest, FHIRInvokeInstanceRequest, FHIRInvokeSystemRequest,
17 FHIRInvokeTypeRequest, FHIRPatchRequest, FHIRReadRequest, FHIRRequest, FHIRResponse,
18 FHIRSearchSystemRequest, FHIRSearchTypeRequest, FHIRTransactionRequest,
19 FHIRUpdateInstanceRequest, FHIRVersionReadRequest, HistoryRequest, HistoryResponse,
20 InvocationRequest, InvokeResponse, Operation, SearchRequest, SearchResponse, UpdateRequest,
21 },
22 url::ParsedParameters,
23};
24use haste_fhir_model::r4::generated::resources::{
25 Bundle, CapabilityStatement, Parameters, Resource, ResourceType,
26};
27use haste_fhir_operation_error::{OperationOutcomeError, derive::OperationOutcomeError};
28use haste_fhir_search::SearchEngine;
29use haste_fhir_terminology::FHIRTerminology;
30use haste_jwt::{
31 AuthorId, AuthorKind, ProjectId, TenantId, UserRole, VersionId,
32 claims::SubscriptionTier,
33 scopes::{
34 SMARTResourceScope, Scope, Scopes, SmartResourceScopeLevel, SmartResourceScopePermission,
35 SmartResourceScopePermissions, SmartResourceScopeUser, SmartScope,
36 },
37};
38use haste_operation_executor::providers::deno_embedded::pool::DenoPool;
39use haste_repository::{Repository, types::SupportedFHIRVersions};
40use middleware::{
41 auditing::Auditing,
42 auth_z::{access_control::AccessControl, scope_check::SmartScopeAccess},
43 canonical_cache::CanonicalCacheInvalidation,
44 capabilities::Capabilities,
45 check_project::{CheckProject, SetProjectReadOnly},
46 custom_models::{
47 access_policy_v3::AccessPolicyV3Validation,
48 deidentification_policy::DeidentificationPolicyValidation, membership::MembershipSync,
49 project::ProjectSync, user::UserSync,
50 },
51 element_filtering::ElementFiltering,
52 operation_definitions::{Builtins, OperationDefinitions},
53 operations::Operations,
54 patch_as_update::PatchAsUpdate,
55 rate_limit::RateLimit,
56 set_artifact_tenant::SetArtifactTenant,
57 storage::Storage,
58 tenant_tier_limits::TenantTierLimits,
59 transaction::Transaction,
60 validation::Validation,
61};
62use std::{fmt::Debug, sync::Arc};
63
64mod batch_transaction_processing;
65mod compartment;
66mod middleware;
67mod resolver;
68mod subscription_limits;
69mod utilities;
70
71pub use middleware::{Backend, operations::operation_definition};
72
73#[derive(OperationOutcomeError, Debug)]
74pub enum StorageError {
75 #[error(
76 code = "not-supported",
77 diagnostic = "Storage not supported for fhir method."
78 )]
79 NotSupported,
80 #[error(
81 code = "exception",
82 diagnostic = "The request returned a response of another kind."
83 )]
84 UnexpectedResponse,
85 #[error(
86 code = "not-found",
87 diagnostic = "Resource '{arg0:?}' with id '{arg1}' not found."
88 )]
89 NotFound(ResourceType, String),
90 #[error(code = "invalid", diagnostic = "Invalid resource type.")]
91 InvalidType,
92 #[error(
93 code = "invalid",
94 diagnostic = "The resource's type '{arg0}' does not match the requested type '{arg1}'."
95 )]
96 TypeMismatch(String, String),
97}
98
99#[derive(Derivative)]
100#[derivative(Debug)]
101pub struct ServerCTX<Client: FHIRClient<Arc<Self>, OperationOutcomeError>> {
102 pub tenant: TenantId,
103 pub project: ProjectId,
104 pub fhir_version: SupportedFHIRVersions,
105 pub user: Arc<User>,
106 #[derivative(Debug = "ignore")]
107 pub client: Arc<Client>,
108 #[derivative(Debug = "ignore")]
109 pub rate_limit: Arc<dyn haste_rate_limit::RateLimit>,
110 pub tracing_id: Option<String>,
111 pub compartment: Option<String>,
115 pub prefer_async: bool,
119}
120
121impl<Client: FHIRClient<Arc<Self>, OperationOutcomeError>> ServerCTX<Client> {
122 pub fn swap_client<NewClient: FHIRClient<Arc<ServerCTX<NewClient>>, OperationOutcomeError>>(
123 &self,
124 new_client: Arc<NewClient>,
125 ) -> ServerCTX<NewClient> {
126 ServerCTX {
127 tenant: self.tenant.clone(),
128 project: self.project.clone(),
129 fhir_version: self.fhir_version.clone(),
130 user: self.user.clone(),
131 client: new_client,
132 rate_limit: self.rate_limit.clone(),
133 tracing_id: self.tracing_id.clone(),
134 compartment: self.compartment.clone(),
135 prefer_async: self.prefer_async,
136 }
137 }
138
139 #[must_use]
141 pub fn with_compartment(&self, patient_id: String) -> Self {
142 ServerCTX {
143 tenant: self.tenant.clone(),
144 project: self.project.clone(),
145 fhir_version: self.fhir_version.clone(),
146 user: self.user.clone(),
147 client: self.client.clone(),
148 rate_limit: self.rate_limit.clone(),
149 tracing_id: self.tracing_id.clone(),
150 compartment: Some(patient_id),
151 prefer_async: self.prefer_async,
152 }
153 }
154
155 pub fn new(
156 tenant: TenantId,
157 project: ProjectId,
158 fhir_version: SupportedFHIRVersions,
159 user: Arc<User>,
160 client: Arc<Client>,
161 rate_limit: Arc<dyn haste_rate_limit::RateLimit>,
162 ) -> Self {
163 ServerCTX {
164 tenant,
165 project,
166 fhir_version,
167 user,
168 client,
169 rate_limit,
170 tracing_id: None,
171 compartment: None,
172 prefer_async: false,
173 }
174 }
175
176 pub fn system(
177 tenant: TenantId,
178 project: ProjectId,
179 client: Arc<Client>,
180 rate_limit: Arc<dyn haste_rate_limit::RateLimit>,
181 ) -> Self {
182 ServerCTX {
183 tenant: tenant.clone(),
184 project: project.clone(),
185 fhir_version: SupportedFHIRVersions::R4,
186 user: Arc::new(User {
187 token: None,
188 claims: haste_jwt::claims::UserTokenClaims {
189 sub: AuthorId::System,
190 exp: 0,
191 aud: AuthorKind::System.to_string(),
192 user_role: UserRole::Owner,
193 project: Some(project),
194 tenant,
195 subscription_tier: SubscriptionTier::Unlimited,
196 scope: Scopes(vec![Scope::SMART(SmartScope::Resource(
197 SMARTResourceScope {
198 user: SmartResourceScopeUser::System,
199 level: SmartResourceScopeLevel::AllResources,
200 permissions: SmartResourceScopePermissions::new(vec![
201 SmartResourceScopePermission::Create,
202 SmartResourceScopePermission::Read,
203 SmartResourceScopePermission::Update,
204 SmartResourceScopePermission::Delete,
205 SmartResourceScopePermission::Search,
206 ]),
207 },
208 ))]),
209 fhir_user: None,
210 patient: None,
211 encounter: None,
212 client_id: None,
213 user_id: AuthorId::System,
214 resource_type: AuthorKind::System,
215 access_policy_version_ids: vec![],
216 membership: None,
217 fhir_version: SupportedFHIRVersions::R4,
218 },
219 }),
220 client,
221 rate_limit,
222 tracing_id: None,
223 compartment: None,
224 prefer_async: false,
225 }
226 }
227
228 pub fn with_tracing_id(mut self, tracing_id: Option<String>) -> Self {
229 self.tracing_id = tracing_id;
230 self
231 }
232
233 #[must_use]
234 pub fn with_prefer_async(mut self, prefer_async: bool) -> Self {
235 self.prefer_async = prefer_async;
236 self
237 }
238}
239
240pub struct ClientState<B: Backend> {
243 repo: Arc<B::Repo>,
244 search: Arc<B::Search>,
245 terminology: Arc<B::Terminology>,
246 audit_repo: Option<Arc<B::Repo>>,
247 config: Arc<ServerConfig>,
248 deno_pool: Arc<DenoPool>,
249}
250
251impl<B: Backend> ClientState<B> {
252 fn with_repo(&self, repo: Arc<B::Repo>) -> Self {
254 ClientState {
255 repo,
256 search: self.search.clone(),
257 terminology: self.terminology.clone(),
258 audit_repo: self.audit_repo.clone(),
259 config: self.config.clone(),
260 deno_pool: self.deno_pool.clone(),
261 }
262 }
263}
264
265impl<B: Backend> Clone for ClientState<B> {
266 fn clone(&self) -> Self {
267 self.with_repo(self.repo.clone())
268 }
269}
270
271#[derive(Clone, Copy, Debug, PartialEq, Eq)]
274enum Lane {
275 Clinical,
276 Artifact,
277 OperationDefinition,
278 ProjectAuth,
279 TenantAuth,
280 AccessPolicy,
281 DeidentificationPolicy,
282 Operation,
283 Capabilities,
284}
285
286impl Lane {
287 fn of(resource_type: &ResourceType) -> Lane {
289 match resource_type {
290 ResourceType::ValueSet
291 | ResourceType::CodeSystem
292 | ResourceType::StructureDefinition
293 | ResourceType::SearchParameter => Lane::Artifact,
294 ResourceType::OperationDefinition => Lane::OperationDefinition,
295 ResourceType::Membership => Lane::ProjectAuth,
296 ResourceType::AccessPolicyV3 => Lane::AccessPolicy,
297 ResourceType::DeidentificationPolicy => Lane::DeidentificationPolicy,
298 ResourceType::User | ResourceType::Project | ResourceType::IdentityProvider => {
299 Lane::TenantAuth
300 }
301 _ => Lane::Clinical,
302 }
303 }
304
305 fn allows(self, request: &FHIRRequest) -> bool {
308 match self {
309 Lane::Artifact => matches!(
310 request,
311 FHIRRequest::Create(_)
312 | FHIRRequest::Read(_)
313 | FHIRRequest::Update(_)
314 | FHIRRequest::Patch(_)
315 | FHIRRequest::Delete(_)
316 | FHIRRequest::Search(SearchRequest::Type(_))
317 ),
318 _ => true,
319 }
320 }
321}
322
323fn check_body_type(request: &FHIRRequest) -> Result<(), StorageError> {
328 let (requested, resource) = match request {
329 FHIRRequest::Create(create) => (&create.resource_type, &create.resource),
330 FHIRRequest::Update(UpdateRequest::Instance(update)) => {
331 (&update.resource_type, &update.resource)
332 }
333 FHIRRequest::Update(UpdateRequest::Conditional(update)) => {
334 (&update.resource_type, &update.resource)
335 }
336 _ => return Ok(()),
337 };
338
339 let actual = resource.resource_type();
340 if &actual == requested {
341 Ok(())
342 } else {
343 Err(StorageError::TypeMismatch(
344 actual.as_ref().to_string(),
345 requested.as_ref().to_string(),
346 ))
347 }
348}
349
350fn route(request: &FHIRRequest) -> Result<Lane, StorageError> {
353 match request {
354 FHIRRequest::Capabilities => Ok(Lane::Capabilities),
355 FHIRRequest::Invocation(_) => Ok(Lane::Operation),
356 FHIRRequest::Batch(_)
360 | FHIRRequest::Transaction(_)
361 | FHIRRequest::Compartment(_)
362 | FHIRRequest::Search(SearchRequest::System(_))
363 | FHIRRequest::History(HistoryRequest::System(_))
364 | FHIRRequest::Delete(DeleteRequest::System(_)) => Ok(Lane::Clinical),
365 FHIRRequest::Create(_)
366 | FHIRRequest::Read(_)
367 | FHIRRequest::VersionRead(_)
368 | FHIRRequest::Update(_)
369 | FHIRRequest::Patch(_)
370 | FHIRRequest::Delete(DeleteRequest::Instance(_) | DeleteRequest::Type(_))
371 | FHIRRequest::History(HistoryRequest::Instance(_) | HistoryRequest::Type(_))
372 | FHIRRequest::Search(SearchRequest::Type(_)) => {
373 let resource_type =
374 request_to_resource_type(request).ok_or(StorageError::InvalidType)?;
375 let lane = Lane::of(resource_type);
376 if lane.allows(request) {
377 Ok(lane)
378 } else {
379 Err(StorageError::NotSupported)
380 }
381 }
382 }
383}
384
385struct Router<B: Backend> {
387 clinical: Pipeline<B>,
388 artifact: Pipeline<B>,
389 operation_definition: Pipeline<B>,
390 project_auth: Pipeline<B>,
391 tenant_auth: Pipeline<B>,
392 access_policy: Pipeline<B>,
393 deidentification_policy: Pipeline<B>,
394 operation: Pipeline<B>,
395 capabilities: Pipeline<B>,
396}
397
398impl<B: Backend> Handler<B> for Router<B> {
399 fn handle<'a>(
400 &'a self,
401 state: &'a ClientState<B>,
402 ctx: Arc<ServerCTX<B>>,
403 request: FHIRRequest,
404 ) -> Output<'a> {
405 match route(&request) {
406 Ok(Lane::Clinical) => &self.clinical,
407 Ok(Lane::Artifact) => &self.artifact,
408 Ok(Lane::OperationDefinition) => &self.operation_definition,
409 Ok(Lane::ProjectAuth) => &self.project_auth,
410 Ok(Lane::TenantAuth) => &self.tenant_auth,
411 Ok(Lane::AccessPolicy) => &self.access_policy,
412 Ok(Lane::DeidentificationPolicy) => &self.deidentification_policy,
413 Ok(Lane::Operation) => &self.operation,
414 Ok(Lane::Capabilities) => &self.capabilities,
415 Err(error) => return Box::pin(std::future::ready(Err(error.into()))),
416 }
417 .run(state, ctx, request)
418 }
419}
420
421pub struct FHIRServerClient<
422 Repo: Repository + Send + Sync + 'static,
423 Search: SearchEngine + Send + Sync + 'static,
424 Terminology: FHIRTerminology + Send + Sync + 'static,
425> {
426 state: ClientState<Self>,
427 pipeline: Pipeline<Self>,
428 builtin_operations: Builtins,
429}
430
431impl<
432 Repo: Repository + Send + Sync + 'static,
433 Search: SearchEngine + Send + Sync + 'static,
434 Terminology: FHIRTerminology + Send + Sync + 'static,
435> Backend for FHIRServerClient<Repo, Search, Terminology>
436{
437 type Repo = Repo;
438 type Search = Search;
439 type Terminology = Terminology;
440
441 fn builtin_operations(&self) -> &Builtins {
442 &self.builtin_operations
443 }
444}
445
446pub struct ServerClientConfig<
447 Repo: Repository + Send + Sync + 'static,
448 Search: SearchEngine + Send + Sync + 'static,
449 Terminology: FHIRTerminology + Send + Sync + 'static,
450> {
451 pub repo: Arc<Repo>,
452 pub audit_repo: Option<Arc<Repo>>,
453 pub search: Arc<Search>,
454 pub terminology: Arc<Terminology>,
455 pub mutate_artifacts: bool,
456 pub config: Arc<ServerConfig>,
457 pub deno_pool: Arc<DenoPool>,
458}
459
460impl<
461 Repo: Repository + Send + Sync + 'static,
462 Search: SearchEngine + Send + Sync + 'static,
463 Terminology: FHIRTerminology + Send + Sync + 'static,
464> ServerClientConfig<Repo, Search, Terminology>
465{
466 pub fn new(
467 repo: Arc<Repo>,
468 search: Arc<Search>,
469 terminology: Arc<Terminology>,
470 config: Arc<ServerConfig>,
471 deno_pool: Arc<DenoPool>,
472 ) -> Self {
473 ServerClientConfig {
474 repo,
475 audit_repo: None,
476 search,
477 terminology,
478 mutate_artifacts: false,
479 config,
480 deno_pool,
481 }
482 }
483
484 pub fn with_mutate_artifacts(mut self, mutate_artifacts: bool) -> Self {
485 self.mutate_artifacts = mutate_artifacts;
486 self
487 }
488
489 pub fn with_audit_repo(mut self, audit_repo: Option<Arc<Repo>>) -> Self {
490 self.audit_repo = audit_repo;
491 self
492 }
493}
494
495impl<
496 Repo: Repository + Send + Sync + 'static,
497 Search: SearchEngine + Send + Sync + 'static,
498 Terminology: FHIRTerminology + Send + Sync + 'static,
499> FHIRServerClient<Repo, Search, Terminology>
500{
501 pub fn new(config: ServerClientConfig<Repo, Search, Terminology>) -> Self {
502 let operations = Operations::new(config.deno_pool.clone());
503 let builtin_operations = operations.operation_definitions();
504
505 let router = Router {
506 clinical: Pipeline::new(vec![], Storage),
507 artifact: Pipeline::new(
508 vec![
509 Box::new(CanonicalCacheInvalidation),
510 Box::new(SetArtifactTenant),
511 ],
512 Storage,
513 ),
514 operation_definition: Pipeline::new(
516 vec![
517 Box::new(PatchAsUpdate),
520 Box::new(OperationDefinitions::new(builtin_operations.clone())),
521 ],
522 Storage,
523 ),
524 project_auth: Pipeline::new(
525 vec![
526 Box::new(Transaction),
527 Box::new(PatchAsUpdate),
529 Box::new(MembershipSync),
530 ],
531 Storage,
532 ),
533 tenant_auth: Pipeline::new(
534 vec![
535 Box::new(SetProjectReadOnly::new(ProjectId::System)),
536 Box::new(CheckProject::new(ProjectId::System)),
538 Box::new(Transaction),
539 Box::new(PatchAsUpdate),
542 Box::new(ProjectSync),
543 Box::new(UserSync),
544 ],
545 Storage,
546 ),
547 access_policy: Pipeline::new(
548 vec![
549 Box::new(PatchAsUpdate),
551 Box::new(AccessPolicyV3Validation),
552 ],
553 Storage,
554 ),
555 deidentification_policy: Pipeline::new(
556 vec![
557 Box::new(PatchAsUpdate),
558 Box::new(DeidentificationPolicyValidation),
559 ],
560 Storage,
561 ),
562 operation: Pipeline::new(vec![], operations),
563 capabilities: Pipeline::new(vec![], Capabilities),
564 };
565
566 FHIRServerClient {
567 state: ClientState {
568 repo: config.repo,
569 audit_repo: config.audit_repo,
570 search: config.search,
571 terminology: config.terminology,
572 config: config.config,
573 deno_pool: config.deno_pool,
574 },
575 pipeline: Pipeline::new(
576 vec![
577 Box::new(Auditing),
578 Box::new(TenantTierLimits),
579 Box::new(RateLimit),
580 Box::new(ElementFiltering),
581 Box::new(SmartScopeAccess),
582 Box::new(AccessControl),
583 Box::new(Validation),
584 ],
585 router,
586 ),
587 builtin_operations,
588 }
589 }
590
591 pub fn deno_pool(&self) -> Arc<DenoPool> {
597 self.state.deno_pool.clone()
598 }
599
600 fn run(&self, ctx: Arc<ServerCTX<Self>>, request: FHIRRequest) -> Output<'_> {
602 if let Err(error) = check_body_type(&request) {
603 return Box::pin(async move { Err(error.into()) });
604 }
605 self.pipeline.run(&self.state, ctx, request)
606 }
607}
608
609impl<
610 Repo: Repository + Send + Sync + 'static,
611 Search: SearchEngine + Send + Sync + 'static,
612 Terminology: FHIRTerminology + Send + Sync + 'static,
613> FHIRClient<Arc<ServerCTX<Self>>, OperationOutcomeError>
614 for FHIRServerClient<Repo, Search, Terminology>
615{
616 #[tracing::instrument(name = "fhir_server_client", skip(self))]
617 async fn request(
618 &self,
619 _ctx: Arc<ServerCTX<Self>>,
620 request: FHIRRequest,
621 ) -> Result<FHIRResponse, OperationOutcomeError> {
622 tracing::info!("Processing request");
623 self.run(_ctx, request).await
624 }
625
626 async fn capabilities(
627 &self,
628 _ctx: Arc<ServerCTX<Self>>,
629 ) -> Result<CapabilityStatement, OperationOutcomeError> {
630 match self.run(_ctx, FHIRRequest::Capabilities).await? {
631 FHIRResponse::Capabilities(response) => Ok(response.capabilities),
632 _ => Err(StorageError::UnexpectedResponse.into()),
633 }
634 }
635
636 async fn search_system(
637 &self,
638 ctx: Arc<ServerCTX<Self>>,
639 parameters: ParsedParameters,
640 ) -> Result<Bundle, OperationOutcomeError> {
641 let request = FHIRRequest::Search(SearchRequest::System(FHIRSearchSystemRequest {
642 parameters,
643 }));
644 match self.run(ctx, request).await? {
645 FHIRResponse::Search(SearchResponse::System(response)) => Ok(response.bundle),
646 _ => Err(StorageError::UnexpectedResponse.into()),
647 }
648 }
649
650 async fn search_type(
651 &self,
652 ctx: Arc<ServerCTX<Self>>,
653 resource_type: ResourceType,
654 parameters: ParsedParameters,
655 ) -> Result<Bundle, OperationOutcomeError> {
656 let request = FHIRRequest::Search(SearchRequest::Type(FHIRSearchTypeRequest {
657 resource_type,
658 parameters,
659 }));
660 match self.run(ctx, request).await? {
661 FHIRResponse::Search(SearchResponse::Type(response)) => Ok(response.bundle),
662 _ => Err(StorageError::UnexpectedResponse.into()),
663 }
664 }
665
666 async fn create(
667 &self,
668 ctx: Arc<ServerCTX<Self>>,
669 resource_type: ResourceType,
670 resource: Resource,
671 ) -> Result<Resource, OperationOutcomeError> {
672 let request = FHIRRequest::Create(FHIRCreateRequest {
673 resource_type,
674 resource,
675 });
676 match self.run(ctx, request).await? {
677 FHIRResponse::Create(response) => Ok(response.resource),
678 _ => Err(StorageError::UnexpectedResponse.into()),
679 }
680 }
681
682 async fn update(
683 &self,
684 ctx: Arc<ServerCTX<Self>>,
685 resource_type: ResourceType,
686 id: String,
687 resource: Resource,
688 ) -> Result<Resource, OperationOutcomeError> {
689 let request = FHIRRequest::Update(UpdateRequest::Instance(FHIRUpdateInstanceRequest {
690 resource_type,
691 id,
692 resource,
693 }));
694 match self.run(ctx, request).await? {
695 FHIRResponse::Create(response) => Ok(response.resource),
696 FHIRResponse::Update(response) => Ok(response.resource),
697 _ => Err(StorageError::UnexpectedResponse.into()),
698 }
699 }
700
701 async fn conditional_update(
702 &self,
703 ctx: Arc<ServerCTX<Self>>,
704 resource_type: ResourceType,
705 parameters: ParsedParameters,
706 resource: Resource,
707 ) -> Result<Resource, OperationOutcomeError> {
708 let request =
709 FHIRRequest::Update(UpdateRequest::Conditional(FHIRConditionalUpdateRequest {
710 resource_type,
711 parameters,
712 resource,
713 }));
714 match self.run(ctx, request).await? {
715 FHIRResponse::Create(response) => Ok(response.resource),
716 FHIRResponse::Update(response) => Ok(response.resource),
717 _ => Err(StorageError::UnexpectedResponse.into()),
718 }
719 }
720
721 async fn patch(
722 &self,
723 ctx: Arc<ServerCTX<Self>>,
724 resource_type: ResourceType,
725 id: String,
726 patches: json_patch::Patch,
727 ) -> Result<Resource, OperationOutcomeError> {
728 let request = FHIRRequest::Patch(FHIRPatchRequest {
729 resource_type,
730 id,
731 patch: patches,
732 });
733 match self.run(ctx, request).await? {
734 FHIRResponse::Patch(response) => Ok(response.resource),
735 _ => Err(StorageError::UnexpectedResponse.into()),
736 }
737 }
738
739 async fn read(
740 &self,
741 ctx: Arc<ServerCTX<Self>>,
742 resource_type: ResourceType,
743 id: String,
744 ) -> Result<Option<Resource>, OperationOutcomeError> {
745 let request = FHIRRequest::Read(FHIRReadRequest { resource_type, id });
746 match self.run(ctx, request).await? {
747 FHIRResponse::Read(response) => Ok(response.resource),
748 _ => Err(StorageError::UnexpectedResponse.into()),
749 }
750 }
751
752 async fn vread(
753 &self,
754 ctx: Arc<ServerCTX<Self>>,
755 resource_type: ResourceType,
756 id: String,
757 version_id: String,
758 ) -> Result<Option<Resource>, OperationOutcomeError> {
759 let request = FHIRRequest::VersionRead(FHIRVersionReadRequest {
760 resource_type,
761 id,
762 version_id: VersionId::new(version_id),
763 });
764 match self.run(ctx, request).await? {
765 FHIRResponse::VersionRead(response) => Ok(Some(response.resource)),
766 _ => Err(StorageError::UnexpectedResponse.into()),
767 }
768 }
769
770 async fn delete_instance(
771 &self,
772 ctx: Arc<ServerCTX<Self>>,
773 resource_type: ResourceType,
774 id: String,
775 ) -> Result<(), OperationOutcomeError> {
776 let request = FHIRRequest::Delete(DeleteRequest::Instance(FHIRDeleteInstanceRequest {
777 resource_type,
778 id,
779 }));
780 match self.run(ctx, request).await? {
781 FHIRResponse::Delete(DeleteResponse::Instance(_)) => Ok(()),
782 _ => Err(StorageError::UnexpectedResponse.into()),
783 }
784 }
785
786 async fn delete_type(
787 &self,
788 ctx: Arc<ServerCTX<Self>>,
789 resource_type: ResourceType,
790 parameters: ParsedParameters,
791 ) -> Result<(), OperationOutcomeError> {
792 let request = FHIRRequest::Delete(DeleteRequest::Type(FHIRDeleteTypeRequest {
793 resource_type,
794 parameters,
795 }));
796 match self.run(ctx, request).await? {
797 FHIRResponse::Delete(DeleteResponse::Type(_)) => Ok(()),
798 _ => Err(StorageError::UnexpectedResponse.into()),
799 }
800 }
801
802 async fn delete_system(
803 &self,
804 ctx: Arc<ServerCTX<Self>>,
805 parameters: ParsedParameters,
806 ) -> Result<(), OperationOutcomeError> {
807 let request = FHIRRequest::Delete(DeleteRequest::System(FHIRDeleteSystemRequest {
808 parameters,
809 }));
810 match self.run(ctx, request).await? {
811 FHIRResponse::Delete(DeleteResponse::System(_)) => Ok(()),
812 _ => Err(StorageError::UnexpectedResponse.into()),
813 }
814 }
815
816 async fn history_system(
817 &self,
818 ctx: Arc<ServerCTX<Self>>,
819 parameters: ParsedParameters,
820 ) -> Result<Bundle, OperationOutcomeError> {
821 let request = FHIRRequest::History(HistoryRequest::System(FHIRHistorySystemRequest {
822 parameters,
823 }));
824 match self.run(ctx, request).await? {
825 FHIRResponse::History(HistoryResponse::System(response)) => Ok(response.bundle),
826 _ => Err(StorageError::UnexpectedResponse.into()),
827 }
828 }
829
830 async fn history_type(
831 &self,
832 ctx: Arc<ServerCTX<Self>>,
833 resource_type: ResourceType,
834 parameters: ParsedParameters,
835 ) -> Result<Bundle, OperationOutcomeError> {
836 let request = FHIRRequest::History(HistoryRequest::Type(FHIRHistoryTypeRequest {
837 resource_type,
838 parameters,
839 }));
840 match self.run(ctx, request).await? {
841 FHIRResponse::History(HistoryResponse::Type(response)) => Ok(response.bundle),
842 _ => Err(StorageError::UnexpectedResponse.into()),
843 }
844 }
845
846 async fn history_instance(
847 &self,
848 ctx: Arc<ServerCTX<Self>>,
849 resource_type: ResourceType,
850 id: String,
851 parameters: ParsedParameters,
852 ) -> Result<Bundle, OperationOutcomeError> {
853 let request = FHIRRequest::History(HistoryRequest::Instance(FHIRHistoryInstanceRequest {
854 resource_type,
855 id,
856 parameters,
857 }));
858 match self.run(ctx, request).await? {
859 FHIRResponse::History(HistoryResponse::Instance(response)) => Ok(response.bundle),
860 _ => Err(StorageError::UnexpectedResponse.into()),
861 }
862 }
863
864 async fn invoke_instance(
865 &self,
866 ctx: Arc<ServerCTX<Self>>,
867 resource_type: ResourceType,
868 id: String,
869 operation: String,
870 parameters: Parameters,
871 ) -> Result<Resource, OperationOutcomeError> {
872 let request =
873 FHIRRequest::Invocation(InvocationRequest::Instance(FHIRInvokeInstanceRequest {
874 operation: Operation::new(&operation),
875 resource_type,
876 id,
877 parameters,
878 }));
879 match self.run(ctx, request).await? {
880 FHIRResponse::Invoke(InvokeResponse::Instance(response)) => Ok(response.resource),
881 _ => Err(StorageError::UnexpectedResponse.into()),
882 }
883 }
884
885 async fn invoke_type(
886 &self,
887 ctx: Arc<ServerCTX<Self>>,
888 resource_type: ResourceType,
889 operation: String,
890 parameters: Parameters,
891 ) -> Result<Resource, OperationOutcomeError> {
892 let request = FHIRRequest::Invocation(InvocationRequest::Type(FHIRInvokeTypeRequest {
893 operation: Operation::new(&operation),
894 resource_type,
895 parameters,
896 }));
897 match self.run(ctx, request).await? {
898 FHIRResponse::Invoke(InvokeResponse::Type(response)) => Ok(response.resource),
899 _ => Err(StorageError::UnexpectedResponse.into()),
900 }
901 }
902
903 async fn invoke_system(
904 &self,
905 ctx: Arc<ServerCTX<Self>>,
906 operation: String,
907 parameters: Parameters,
908 ) -> Result<Resource, OperationOutcomeError> {
909 let request = FHIRRequest::Invocation(InvocationRequest::System(FHIRInvokeSystemRequest {
910 operation: Operation::new(&operation),
911 parameters,
912 }));
913 match self.run(ctx, request).await? {
914 FHIRResponse::Invoke(InvokeResponse::System(response)) => Ok(response.resource),
915 _ => Err(StorageError::UnexpectedResponse.into()),
916 }
917 }
918
919 async fn transaction(
920 &self,
921 ctx: Arc<ServerCTX<Self>>,
922 bundle: Bundle,
923 ) -> Result<Bundle, OperationOutcomeError> {
924 let request = FHIRRequest::Transaction(FHIRTransactionRequest { resource: bundle });
925 match self.run(ctx, request).await? {
926 FHIRResponse::Transaction(response) => Ok(response.resource),
927 _ => Err(StorageError::UnexpectedResponse.into()),
928 }
929 }
930
931 async fn batch(
932 &self,
933 ctx: Arc<ServerCTX<Self>>,
934 bundle: Bundle,
935 ) -> Result<Bundle, OperationOutcomeError> {
936 let request = FHIRRequest::Batch(FHIRBatchRequest { resource: bundle });
937 match self.run(ctx, request).await? {
938 FHIRResponse::Batch(response) => Ok(response.resource),
939 _ => Err(StorageError::UnexpectedResponse.into()),
940 }
941 }
942}