Skip to main content

haste_server/fhir_client/
mod.rs

1use crate::{
2    ServerEnvironmentVariables,
3    auth_n::middleware::jwt::User,
4    fhir_client::{
5        middleware::{
6            ServerMiddlewareContext, ServerMiddlewareNext, ServerMiddlewareOutput,
7            ServerMiddlewareState,
8        },
9        utilities::request_to_resource_type,
10    },
11};
12use derivative::Derivative;
13use haste_config::Config;
14use haste_fhir_client::{
15    FHIRClient,
16    middleware::{Middleware, MiddlewareChain},
17    request::{
18        DeleteRequest, DeleteResponse, FHIRBatchRequest, FHIRConditionalUpdateRequest,
19        FHIRCreateRequest, FHIRDeleteInstanceRequest, FHIRDeleteSystemRequest,
20        FHIRDeleteTypeRequest, FHIRReadRequest, FHIRRequest, FHIRResponse, FHIRSearchTypeRequest,
21        FHIRTransactionRequest, FHIRUpdateInstanceRequest, FHIRVersionReadRequest, SearchRequest,
22        SearchResponse, UpdateRequest,
23    },
24    url::ParsedParameters,
25};
26use haste_fhir_model::r4::generated::resources::{
27    Bundle, CapabilityStatement, Parameters, Resource, ResourceType,
28};
29use haste_fhir_operation_error::{OperationOutcomeError, derive::OperationOutcomeError};
30use haste_fhir_search::SearchEngine;
31use haste_fhir_terminology::FHIRTerminology;
32use haste_jwt::{
33    AuthorId, AuthorKind, ProjectId, TenantId, UserRole, VersionId,
34    claims::SubscriptionTier,
35    scopes::{
36        SMARTResourceScope, Scope, Scopes, SmartResourceScopeLevel, SmartResourceScopePermission,
37        SmartResourceScopePermissions, SmartResourceScopeUser, SmartScope,
38    },
39};
40use haste_repository::{Repository, types::SupportedFHIRVersions};
41use std::{
42    fmt::Debug,
43    sync::{Arc, LazyLock},
44};
45
46mod batch_transaction_processing;
47mod compartment;
48mod middleware;
49mod resolver;
50mod subscription_limits;
51mod utilities;
52
53#[derive(OperationOutcomeError, Debug)]
54pub enum StorageError {
55    #[error(
56        code = "not-supported",
57        diagnostic = "Storage not supported for fhir method."
58    )]
59    NotSupported,
60    #[error(
61        code = "exception",
62        diagnostic = "No response was returned from the request."
63    )]
64    NoResponse,
65    #[error(
66        code = "not-found",
67        diagnostic = "Resource '{arg0:?}' with id '{arg1}' not found."
68    )]
69    NotFound(ResourceType, String),
70    #[error(code = "invalid", diagnostic = "Invalid resource type.")]
71    InvalidType,
72}
73
74#[derive(Derivative)]
75#[derivative(Debug)]
76pub struct ServerCTX<Client: FHIRClient<Arc<Self>, OperationOutcomeError>> {
77    pub tenant: TenantId,
78    pub project: ProjectId,
79    pub fhir_version: SupportedFHIRVersions,
80    pub user: Arc<User>,
81    #[derivative(Debug = "ignore")]
82    pub client: Arc<Client>,
83    #[derivative(Debug = "ignore")]
84    pub rate_limit: Arc<dyn haste_rate_limit::RateLimit>,
85}
86
87impl<Client: FHIRClient<Arc<Self>, OperationOutcomeError>> ServerCTX<Client> {
88    pub fn swap_client<NewClient: FHIRClient<Arc<ServerCTX<NewClient>>, OperationOutcomeError>>(
89        &self,
90        new_client: Arc<NewClient>,
91    ) -> ServerCTX<NewClient> {
92        ServerCTX {
93            tenant: self.tenant.clone(),
94            project: self.project.clone(),
95            fhir_version: self.fhir_version.clone(),
96            user: self.user.clone(),
97            client: new_client,
98            rate_limit: self.rate_limit.clone(),
99        }
100    }
101
102    pub fn new(
103        tenant: TenantId,
104        project: ProjectId,
105        fhir_version: SupportedFHIRVersions,
106        user: Arc<User>,
107        client: Arc<Client>,
108        rate_limit: Arc<dyn haste_rate_limit::RateLimit>,
109    ) -> Self {
110        ServerCTX {
111            tenant,
112            project,
113            fhir_version,
114            user,
115            client,
116            rate_limit,
117        }
118    }
119
120    pub fn system(
121        tenant: TenantId,
122        project: ProjectId,
123        client: Arc<Client>,
124        rate_limit: Arc<dyn haste_rate_limit::RateLimit>,
125    ) -> Self {
126        ServerCTX {
127            tenant: tenant.clone(),
128            project: project.clone(),
129            fhir_version: SupportedFHIRVersions::R4,
130            user: Arc::new(User {
131                token: None,
132                claims: haste_jwt::claims::UserTokenClaims {
133                    sub: AuthorId::System,
134                    exp: 0,
135                    aud: AuthorKind::System.to_string(),
136                    user_role: UserRole::Owner,
137                    project: Some(project),
138                    tenant,
139                    subscription_tier: SubscriptionTier::Unlimited,
140                    scope: Scopes(vec![Scope::SMART(SmartScope::Resource(
141                        SMARTResourceScope {
142                            user: SmartResourceScopeUser::System,
143                            level: SmartResourceScopeLevel::AllResources,
144                            permissions: SmartResourceScopePermissions::new(vec![
145                                SmartResourceScopePermission::Create,
146                                SmartResourceScopePermission::Read,
147                                SmartResourceScopePermission::Update,
148                                SmartResourceScopePermission::Delete,
149                                SmartResourceScopePermission::Search,
150                            ]),
151                        },
152                    ))]),
153                    user_id: AuthorId::System,
154                    resource_type: AuthorKind::System,
155                    access_policy_version_ids: vec![],
156                    membership: None,
157                },
158            }),
159            client,
160            rate_limit,
161        }
162    }
163}
164
165struct ClientState<
166    Repo: Repository + Send + Sync,
167    Search: SearchEngine + Send + Sync,
168    Terminology: FHIRTerminology + Send + Sync,
169> {
170    repo: Arc<Repo>,
171    search: Arc<Search>,
172    terminology: Arc<Terminology>,
173    config: Arc<dyn Config<ServerEnvironmentVariables>>,
174}
175
176pub struct Route<State, Client: FHIRClient<Arc<ServerCTX<Client>>, OperationOutcomeError>> {
177    filter: Box<dyn Fn(&FHIRRequest) -> bool + Send + Sync>,
178    middleware: Middleware<
179        Arc<State>,
180        Arc<ServerCTX<Client>>,
181        FHIRRequest,
182        FHIRResponse,
183        OperationOutcomeError,
184    >,
185}
186
187pub struct FHIRServerClient<
188    Repo: Repository + Send + Sync + 'static,
189    Search: SearchEngine + Send + Sync + 'static,
190    Terminology: FHIRTerminology + Send + Sync + 'static,
191> {
192    state: Arc<ClientState<Repo, Search, Terminology>>,
193    middleware: Middleware<
194        Arc<ClientState<Repo, Search, Terminology>>,
195        Arc<ServerCTX<Self>>,
196        FHIRRequest,
197        FHIRResponse,
198        OperationOutcomeError,
199    >,
200}
201
202pub struct RouterMiddleware<
203    State,
204    Client: FHIRClient<Arc<ServerCTX<Client>>, OperationOutcomeError>,
205> {
206    routes: Arc<Vec<Route<State, Client>>>,
207}
208
209impl<State, Client: FHIRClient<Arc<ServerCTX<Client>>, OperationOutcomeError>>
210    RouterMiddleware<State, Client>
211{
212    pub fn new(routes: Arc<Vec<Route<State, Client>>>) -> Self {
213        RouterMiddleware { routes }
214    }
215}
216
217impl<
218    Repo: Repository + Send + Sync + 'static,
219    Search: SearchEngine + Send + Sync + 'static,
220    Terminology: FHIRTerminology + Send + Sync + 'static,
221    Client: FHIRClient<Arc<ServerCTX<Client>>, OperationOutcomeError> + 'static,
222>
223    MiddlewareChain<
224        ServerMiddlewareState<Repo, Search, Terminology>,
225        Arc<ServerCTX<Client>>,
226        FHIRRequest,
227        FHIRResponse,
228        OperationOutcomeError,
229    > for RouterMiddleware<ClientState<Repo, Search, Terminology>, Client>
230{
231    fn call(
232        &self,
233        state: ServerMiddlewareState<Repo, Search, Terminology>,
234        context: ServerMiddlewareContext<Client>,
235        next: Option<
236            Arc<ServerMiddlewareNext<Client, ServerMiddlewareState<Repo, Search, Terminology>>>,
237        >,
238    ) -> ServerMiddlewareOutput<Client> {
239        let routes = self.routes.clone();
240        Box::pin(async move {
241            let route = routes.iter().find(|r| (r.filter)(&context.request));
242
243            match route {
244                Some(route) => {
245                    let context = route
246                        .middleware
247                        .call(state.clone(), context.ctx, context.request)
248                        .await?;
249                    if let Some(next) = next {
250                        next(state, context).await
251                    } else {
252                        Ok(context)
253                    }
254                }
255                None => {
256                    if let Some(next) = next {
257                        next(state, context).await
258                    } else {
259                        Ok(context)
260                    }
261                }
262            }
263        })
264    }
265}
266
267static ARTIFACT_TYPES: &[ResourceType] = &[
268    ResourceType::ValueSet,
269    ResourceType::CodeSystem,
270    ResourceType::StructureDefinition,
271    ResourceType::SearchParameter,
272];
273
274static TENANT_AUTH_TYPES: &[ResourceType] = &[
275    ResourceType::User,
276    ResourceType::Project,
277    ResourceType::IdentityProvider,
278];
279static PROJECT_AUTH_TYPES: &[ResourceType] = &[ResourceType::Membership];
280
281static SPECIAL_TYPES: LazyLock<Vec<ResourceType>> = LazyLock::new(|| {
282    [
283        &TENANT_AUTH_TYPES[..],
284        &PROJECT_AUTH_TYPES[..],
285        &ARTIFACT_TYPES[..],
286    ]
287    .concat()
288});
289
290pub struct ServerClientConfig<
291    Repo: Repository + Send + Sync + 'static,
292    Search: SearchEngine + Send + Sync + 'static,
293    Terminology: FHIRTerminology + Send + Sync + 'static,
294> {
295    pub repo: Arc<Repo>,
296    pub search: Arc<Search>,
297    pub terminology: Arc<Terminology>,
298    pub mutate_artifacts: bool,
299    pub config: Arc<dyn Config<ServerEnvironmentVariables>>,
300}
301
302impl<
303    Repo: Repository + Send + Sync + 'static,
304    Search: SearchEngine + Send + Sync + 'static,
305    Terminology: FHIRTerminology + Send + Sync + 'static,
306> ServerClientConfig<Repo, Search, Terminology>
307{
308    pub fn new(
309        repo: Arc<Repo>,
310        search: Arc<Search>,
311        terminology: Arc<Terminology>,
312        config: Arc<dyn Config<ServerEnvironmentVariables>>,
313    ) -> Self {
314        ServerClientConfig {
315            repo,
316            search,
317            terminology,
318            mutate_artifacts: false,
319            config,
320        }
321    }
322
323    pub fn allow_mutate_artifacts(
324        repo: Arc<Repo>,
325        search: Arc<Search>,
326        terminology: Arc<Terminology>,
327        config: Arc<dyn Config<ServerEnvironmentVariables>>,
328    ) -> Self {
329        Self {
330            repo,
331            search,
332            terminology,
333            config,
334            mutate_artifacts: true,
335        }
336    }
337}
338
339impl<
340    Repo: Repository + Send + Sync + 'static,
341    Search: SearchEngine + Send + Sync + 'static,
342    Terminology: FHIRTerminology + Send + Sync + 'static,
343> FHIRServerClient<Repo, Search, Terminology>
344{
345    pub fn new(config: ServerClientConfig<Repo, Search, Terminology>) -> Self {
346        let route_middleware = RouterMiddleware::new(Arc::new(vec![
347            // Clinical resources.
348            Route {
349                filter: Box::new(|req: &FHIRRequest| match req {
350                    FHIRRequest::Invocation(_) | FHIRRequest::Capabilities => false,
351                    _ => {
352                        if let Some(resource_type) = request_to_resource_type(req) {
353                            !SPECIAL_TYPES.contains(&resource_type)
354                        } else {
355                            true
356                        }
357                    }
358                }),
359                middleware: Middleware::new(vec![Box::new(middleware::storage::Middleware::new())]),
360            },
361            // Artifact routes.
362            Route {
363                filter: Box::new(|req: &FHIRRequest| match req {
364                    FHIRRequest::Create(_)
365                    | FHIRRequest::Update(_)
366                    | FHIRRequest::Delete(_)
367                    | FHIRRequest::Read(_)
368                    | FHIRRequest::Search(SearchRequest::Type(_)) => {
369                        if let Some(resource_type) = request_to_resource_type(req) {
370                            ARTIFACT_TYPES.contains(&resource_type)
371                        } else {
372                            false
373                        }
374                    }
375                    _ => false,
376                }),
377
378                middleware: Middleware::new(vec![
379                    Box::new(middleware::set_artifact_tenant::Middleware::new()),
380                    Box::new(middleware::storage::Middleware::new()),
381                ]),
382            },
383            // Operation routes
384            Route {
385                filter: Box::new(|req: &FHIRRequest| match req {
386                    FHIRRequest::Invocation(_) => true,
387                    _ => false,
388                }),
389                middleware: Middleware::new(vec![Box::new(
390                    middleware::operations::Middleware::new(),
391                )]),
392            },
393            // Authentication routes.
394            Route {
395                filter: Box::new(|req: &FHIRRequest| match req {
396                    FHIRRequest::Invocation(_) => false,
397                    _ => request_to_resource_type(req)
398                        .map_or(false, |rt| PROJECT_AUTH_TYPES.contains(rt)),
399                }),
400                middleware: Middleware::new(vec![
401                    Box::new(middleware::transaction::Middleware::new()),
402                    Box::new(middleware::custom_models::membership::Middleware::new()),
403                    Box::new(middleware::storage::Middleware::new()),
404                ]),
405            },
406            // Tenant auth routes.
407            Route {
408                filter: Box::new(|req: &FHIRRequest| match req {
409                    FHIRRequest::Invocation(_) => false,
410                    _ => request_to_resource_type(req)
411                        .map_or(false, |rt| TENANT_AUTH_TYPES.contains(rt)),
412                }),
413                middleware: Middleware::new(vec![
414                    Box::new(
415                        middleware::check_project::SetProjectReadOnlyMiddleware::new(
416                            ProjectId::System,
417                        ),
418                    ),
419                    // Confirm in system project as above will only set to system if readonly.
420                    Box::new(middleware::check_project::Middleware::new(
421                        ProjectId::System,
422                    )),
423                    Box::new(middleware::transaction::Middleware::new()),
424                    Box::new(middleware::custom_models::project::Middleware::new()),
425                    Box::new(middleware::custom_models::user::Middleware::new()),
426                    Box::new(middleware::storage::Middleware::new()),
427                ]),
428            },
429        ]));
430
431        FHIRServerClient {
432            state: Arc::new(ClientState {
433                repo: config.repo,
434                search: config.search,
435                terminology: config.terminology,
436                config: config.config,
437            }),
438            middleware: Middleware::new(vec![
439                Box::new(middleware::tenant_tier_limits::Middleware::new()),
440                Box::new(middleware::rate_limit::Middleware::new()),
441                Box::new(middleware::auth_z::scope_check::SMARTScopeAccessMiddleware::new()),
442                Box::new(middleware::auth_z::access_control::AccessControlMiddleware::new()),
443                Box::new(middleware::validation::Middleware::new()),
444                Box::new(route_middleware),
445                Box::new(middleware::capabilities::Middleware::new()),
446            ]),
447        }
448    }
449}
450
451impl<
452    Repo: Repository + Send + Sync + 'static,
453    Search: SearchEngine + Send + Sync + 'static,
454    Terminology: FHIRTerminology + Send + Sync + 'static,
455> FHIRClient<Arc<ServerCTX<Self>>, OperationOutcomeError>
456    for FHIRServerClient<Repo, Search, Terminology>
457{
458    #[tracing::instrument(name = "fhir_server_client", skip(self))]
459    async fn request(
460        &self,
461        _ctx: Arc<ServerCTX<Self>>,
462        request: FHIRRequest,
463    ) -> Result<FHIRResponse, OperationOutcomeError> {
464        tracing::info!("Processing request");
465        let response = self
466            .middleware
467            .call(self.state.clone(), _ctx, request)
468            .await?;
469
470        response
471            .response
472            .ok_or_else(|| StorageError::NoResponse.into())
473    }
474
475    async fn capabilities(
476        &self,
477        _ctx: Arc<ServerCTX<Self>>,
478    ) -> Result<CapabilityStatement, OperationOutcomeError> {
479        let res = self
480            .middleware
481            .call(self.state.clone(), _ctx, FHIRRequest::Capabilities)
482            .await?;
483
484        match res.response {
485            Some(FHIRResponse::Capabilities(capabilities_response)) => {
486                Ok(capabilities_response.capabilities)
487            }
488            _ => panic!("Unexpected response type"),
489        }
490    }
491
492    async fn search_system(
493        &self,
494        _ctx: Arc<ServerCTX<Self>>,
495        _parameters: ParsedParameters,
496    ) -> Result<Bundle, OperationOutcomeError> {
497        todo!()
498    }
499
500    async fn search_type(
501        &self,
502        ctx: Arc<ServerCTX<Self>>,
503        resource_type: ResourceType,
504        parameters: ParsedParameters,
505    ) -> Result<Bundle, OperationOutcomeError> {
506        let res = self
507            .middleware
508            .call(
509                self.state.clone(),
510                ctx,
511                FHIRRequest::Search(SearchRequest::Type(FHIRSearchTypeRequest {
512                    resource_type,
513                    parameters,
514                })),
515            )
516            .await?;
517
518        match res.response {
519            Some(FHIRResponse::Search(SearchResponse::Type(search_response))) => {
520                Ok(search_response.bundle)
521            }
522            _ => panic!("Unexpected response type"),
523        }
524    }
525
526    async fn create(
527        &self,
528        ctx: Arc<ServerCTX<Self>>,
529        resource_type: ResourceType,
530        resource: Resource,
531    ) -> Result<Resource, OperationOutcomeError> {
532        let res = self
533            .middleware
534            .call(
535                self.state.clone(),
536                ctx,
537                FHIRRequest::Create(FHIRCreateRequest {
538                    resource_type,
539                    resource,
540                }),
541            )
542            .await?;
543
544        match res.response {
545            Some(FHIRResponse::Create(create_response)) => Ok(create_response.resource),
546            _ => panic!("Unexpected response type"),
547        }
548    }
549
550    async fn update(
551        &self,
552        ctx: Arc<ServerCTX<Self>>,
553        resource_type: ResourceType,
554        id: String,
555        resource: Resource,
556    ) -> Result<Resource, OperationOutcomeError> {
557        let res = self
558            .middleware
559            .call(
560                self.state.clone(),
561                ctx,
562                FHIRRequest::Update(UpdateRequest::Instance(FHIRUpdateInstanceRequest {
563                    resource_type,
564                    id,
565                    resource,
566                })),
567            )
568            .await?;
569
570        match res.response {
571            Some(FHIRResponse::Create(create_response)) => Ok(create_response.resource),
572            Some(FHIRResponse::Update(update_response)) => Ok(update_response.resource),
573            _ => panic!("Unexpected response type {:?}", res.response),
574        }
575    }
576
577    async fn conditional_update(
578        &self,
579        ctx: Arc<ServerCTX<Self>>,
580        resource_type: ResourceType,
581        parameters: ParsedParameters,
582        resource: Resource,
583    ) -> Result<Resource, OperationOutcomeError> {
584        let res = self
585            .middleware
586            .call(
587                self.state.clone(),
588                ctx,
589                FHIRRequest::Update(UpdateRequest::Conditional(FHIRConditionalUpdateRequest {
590                    resource_type,
591                    parameters,
592                    resource,
593                })),
594            )
595            .await?;
596
597        match res.response {
598            Some(FHIRResponse::Create(create_response)) => Ok(create_response.resource),
599            Some(FHIRResponse::Update(update_response)) => Ok(update_response.resource),
600            _ => panic!("Unexpected response type {:?}", res.response),
601        }
602    }
603
604    async fn patch(
605        &self,
606        _ctx: Arc<ServerCTX<Self>>,
607        _resource_type: ResourceType,
608        _id: String,
609        _patches: json_patch::Patch,
610    ) -> Result<Resource, OperationOutcomeError> {
611        todo!()
612    }
613
614    async fn read(
615        &self,
616        ctx: Arc<ServerCTX<Self>>,
617        resource_type: ResourceType,
618        id: String,
619    ) -> Result<Option<Resource>, OperationOutcomeError> {
620        let res = self
621            .middleware
622            .call(
623                self.state.clone(),
624                ctx,
625                FHIRRequest::Read(FHIRReadRequest { resource_type, id }),
626            )
627            .await?;
628
629        match res.response {
630            Some(FHIRResponse::Read(read_response)) => Ok(read_response.resource),
631            _ => panic!("Unexpected response type"),
632        }
633    }
634
635    async fn vread(
636        &self,
637        ctx: Arc<ServerCTX<Self>>,
638        resource_type: ResourceType,
639        id: String,
640        version_id: String,
641    ) -> Result<Option<Resource>, OperationOutcomeError> {
642        let res = self
643            .middleware
644            .call(
645                self.state.clone(),
646                ctx,
647                FHIRRequest::VersionRead(FHIRVersionReadRequest {
648                    resource_type,
649                    id,
650                    version_id: VersionId::new(version_id),
651                }),
652            )
653            .await?;
654
655        match res.response {
656            Some(FHIRResponse::VersionRead(version_read_response)) => {
657                Ok(Some(version_read_response.resource))
658            }
659            _ => panic!("Unexpected response type"),
660        }
661    }
662
663    async fn delete_instance(
664        &self,
665        ctx: Arc<ServerCTX<Self>>,
666        resource_type: ResourceType,
667        id: String,
668    ) -> Result<(), OperationOutcomeError> {
669        let res = self
670            .middleware
671            .call(
672                self.state.clone(),
673                ctx,
674                FHIRRequest::Delete(DeleteRequest::Instance(FHIRDeleteInstanceRequest {
675                    resource_type,
676                    id,
677                })),
678            )
679            .await?;
680
681        match res.response {
682            Some(FHIRResponse::Delete(DeleteResponse::Instance(_delete_response))) => Ok(()),
683            _ => panic!("Unexpected response type"),
684        }
685    }
686
687    async fn delete_type(
688        &self,
689        ctx: Arc<ServerCTX<Self>>,
690        resource_type: ResourceType,
691        parameters: ParsedParameters,
692    ) -> Result<(), OperationOutcomeError> {
693        let res = self
694            .middleware
695            .call(
696                self.state.clone(),
697                ctx,
698                FHIRRequest::Delete(DeleteRequest::Type(FHIRDeleteTypeRequest {
699                    resource_type,
700                    parameters,
701                })),
702            )
703            .await?;
704
705        match res.response {
706            Some(FHIRResponse::Delete(DeleteResponse::Type(_delete_response))) => Ok(()),
707            _ => panic!("Unexpected response type"),
708        }
709    }
710
711    async fn delete_system(
712        &self,
713        ctx: Arc<ServerCTX<Self>>,
714        parameters: ParsedParameters,
715    ) -> Result<(), OperationOutcomeError> {
716        let res = self
717            .middleware
718            .call(
719                self.state.clone(),
720                ctx,
721                FHIRRequest::Delete(DeleteRequest::System(FHIRDeleteSystemRequest {
722                    parameters,
723                })),
724            )
725            .await?;
726
727        match res.response {
728            Some(FHIRResponse::Delete(DeleteResponse::System(_delete_response))) => Ok(()),
729            _ => panic!("Unexpected response type"),
730        }
731    }
732
733    async fn history_system(
734        &self,
735        _ctx: Arc<ServerCTX<Self>>,
736        _parameters: ParsedParameters,
737    ) -> Result<Bundle, OperationOutcomeError> {
738        todo!()
739    }
740
741    async fn history_type(
742        &self,
743        _ctx: Arc<ServerCTX<Self>>,
744        _resource_type: ResourceType,
745        _parameters: ParsedParameters,
746    ) -> Result<Bundle, OperationOutcomeError> {
747        todo!()
748    }
749
750    async fn history_instance(
751        &self,
752        _ctx: Arc<ServerCTX<Self>>,
753        _resource_type: ResourceType,
754        _id: String,
755        _parameters: ParsedParameters,
756    ) -> Result<Bundle, OperationOutcomeError> {
757        todo!()
758    }
759
760    async fn invoke_instance(
761        &self,
762        _ctx: Arc<ServerCTX<Self>>,
763        _resource_type: ResourceType,
764        _id: String,
765        _operation: String,
766        _parameters: Parameters,
767    ) -> Result<Resource, OperationOutcomeError> {
768        todo!()
769    }
770
771    async fn invoke_type(
772        &self,
773        _ctx: Arc<ServerCTX<Self>>,
774        _resource_type: ResourceType,
775        _operation: String,
776        _parameters: Parameters,
777    ) -> Result<Resource, OperationOutcomeError> {
778        todo!()
779    }
780
781    async fn invoke_system(
782        &self,
783        _ctx: Arc<ServerCTX<Self>>,
784        _operation: String,
785        _parameters: Parameters,
786    ) -> Result<Resource, OperationOutcomeError> {
787        todo!()
788    }
789
790    async fn transaction(
791        &self,
792        ctx: Arc<ServerCTX<Self>>,
793        bundle: Bundle,
794    ) -> Result<Bundle, OperationOutcomeError> {
795        let res = self
796            .middleware
797            .call(
798                self.state.clone(),
799                ctx,
800                FHIRRequest::Transaction(FHIRTransactionRequest { resource: bundle }),
801            )
802            .await?;
803
804        match res.response {
805            Some(FHIRResponse::Transaction(transaction_response)) => {
806                Ok(transaction_response.resource)
807            }
808            _ => panic!("Unexpected response type"),
809        }
810    }
811
812    async fn batch(
813        &self,
814        ctx: Arc<ServerCTX<Self>>,
815        bundle: Bundle,
816    ) -> Result<Bundle, OperationOutcomeError> {
817        let res = self
818            .middleware
819            .call(
820                self.state.clone(),
821                ctx,
822                FHIRRequest::Batch(FHIRBatchRequest { resource: bundle }),
823            )
824            .await?;
825
826        match res.response {
827            Some(FHIRResponse::Batch(batch_response)) => Ok(batch_response.resource),
828            _ => panic!("Unexpected response type"),
829        }
830    }
831}