haste_fhir_client/
axum.rs1use crate::request::{
2 DeleteResponse, FHIRResponse, HistoryResponse, InvokeResponse, SearchResponse,
3};
4use axum::response::IntoResponse;
5use haste_fhir_model::r4::generated::{
6 resources::Resource,
7 terminology::IssueType,
8 types::{FHIRId, FHIRInstant},
9};
10use haste_fhir_operation_error::OperationOutcomeError;
11use haste_reflect::MetaValue;
12use http::{HeaderMap, StatusCode};
13
14fn add_resource_headers(headers: &mut HeaderMap, resource: &Resource) {
15 let _id = resource
16 .get_field("id")
17 .and_then(|id| id.as_any().downcast_ref::<String>());
18
19 let meta = resource.get_field("meta");
20
21 let last_modified = meta
22 .and_then(|meta| meta.get_field("lastUpdated"))
23 .and_then(|lu| lu.as_any().downcast_ref::<Box<FHIRInstant>>())
24 .and_then(|lu| lu.value.as_ref());
25
26 let version_id = meta
27 .and_then(|meta| meta.get_field("versionId"))
28 .and_then(|vid| vid.as_any().downcast_ref::<Box<FHIRId>>())
29 .and_then(|vid| vid.value.as_ref());
30
31 if let Some(last_modified) = last_modified {
32 headers.insert(
33 axum::http::header::LAST_MODIFIED,
34 last_modified
35 .format("%a, %d %b %Y %H:%M:%S GMT")
36 .parse()
37 .unwrap(),
38 );
39 }
40 if let Some(version_id) = version_id {
41 headers.insert(
42 axum::http::header::ETAG,
43 format!("W/\"{version_id}\"").parse().unwrap(),
44 );
45 }
46}
47
48fn add_headers(response: &FHIRResponse) -> HeaderMap {
49 let mut header = HeaderMap::new();
50 header.insert(
51 axum::http::header::CONTENT_TYPE,
52 "application/fhir+json; charset=utf-8".parse().unwrap(),
53 );
54
55 match response {
56 FHIRResponse::Create(resp) => {
57 add_resource_headers(&mut header, &resp.resource);
58 }
59 FHIRResponse::Read(resp) => {
60 if let Some(resource) = &resp.resource {
61 add_resource_headers(&mut header, resource);
62 }
63 }
64 FHIRResponse::VersionRead(resp) => {
65 add_resource_headers(&mut header, &resp.resource);
66 }
67 FHIRResponse::Update(resp) => {
68 add_resource_headers(&mut header, &resp.resource);
69 }
70 FHIRResponse::Patch(fhirpatch_response) => {
71 add_resource_headers(&mut header, &fhirpatch_response.resource);
72 }
73 _ => {}
74 }
75
76 header
77}
78
79impl IntoResponse for FHIRResponse {
80 fn into_response(self) -> axum::response::Response {
81 let header = add_headers(&self);
82
83 match self {
84 FHIRResponse::Create(response) => {
85 FHIRResponse::created_json(header, &response.resource)
86 }
87
88 FHIRResponse::Read(response) => match response.resource {
89 Some(resource) => FHIRResponse::ok_json(header, &resource),
90 None => FHIRResponse::not_found(),
91 },
92
93 FHIRResponse::VersionRead(response) => {
94 FHIRResponse::ok_json(header, &response.resource)
95 }
96
97 FHIRResponse::Update(response) => FHIRResponse::ok_json(header, &response.resource),
98
99 FHIRResponse::Capabilities(response) => {
100 FHIRResponse::ok_json(header, &response.capabilities)
101 }
102
103 FHIRResponse::History(history_response) => {
104 FHIRResponse::history_response(header, history_response)
105 }
106
107 FHIRResponse::Search(search_response) => {
108 FHIRResponse::search_response(header, search_response)
109 }
110
111 FHIRResponse::Batch(response) => FHIRResponse::ok_json(header, &response.resource),
112
113 FHIRResponse::Invoke(invoke_response) => {
114 FHIRResponse::invoke_response(header, invoke_response)
115 }
116
117 FHIRResponse::Delete(delete_response) => {
118 FHIRResponse::delete_response(header, delete_response)
119 }
120
121 FHIRResponse::Patch(response) => FHIRResponse::ok_json(header, &response.resource),
122
123 FHIRResponse::Transaction(response) => {
124 FHIRResponse::ok_json(header, &response.resource)
125 }
126 }
127 }
128}
129
130impl FHIRResponse {
131 fn json_response<T: serde::Serialize>(
132 status: StatusCode,
133 header: HeaderMap,
134 value: &T,
135 ) -> axum::response::Response {
136 (
137 status,
138 header,
139 serde_json::to_string(value).unwrap(),
141 )
142 .into_response()
143 }
144
145 fn ok_json<T: serde::Serialize>(header: HeaderMap, value: &T) -> axum::response::Response {
146 Self::json_response(StatusCode::OK, header, value)
147 }
148
149 fn created_json<T: serde::Serialize>(header: HeaderMap, value: &T) -> axum::response::Response {
150 Self::json_response(StatusCode::CREATED, header, value)
151 }
152
153 fn no_content(header: HeaderMap) -> axum::response::Response {
154 (StatusCode::NO_CONTENT, header, "").into_response()
155 }
156
157 fn not_found() -> axum::response::Response {
158 OperationOutcomeError::error(IssueType::not_found(), "Resource not found.".to_string())
159 .into_response()
160 }
161
162 fn history_response(header: HeaderMap, response: HistoryResponse) -> axum::response::Response {
163 match response {
164 HistoryResponse::Instance(response) => Self::ok_json(header, &response.bundle),
165 HistoryResponse::Type(response) => Self::ok_json(header, &response.bundle),
166 HistoryResponse::System(response) => Self::ok_json(header, &response.bundle),
167 }
168 }
169
170 fn search_response(header: HeaderMap, response: SearchResponse) -> axum::response::Response {
171 match response {
172 SearchResponse::Type(response) => Self::ok_json(header, &response.bundle),
173 SearchResponse::System(response) => Self::ok_json(header, &response.bundle),
174 }
175 }
176
177 fn invoke_response(header: HeaderMap, response: InvokeResponse) -> axum::response::Response {
178 match response {
179 InvokeResponse::Instance(response) => Self::ok_json(header, &response.resource),
180 InvokeResponse::Type(response) => Self::ok_json(header, &response.resource),
181 InvokeResponse::System(response) => Self::ok_json(header, &response.resource),
182 }
183 }
184
185 fn delete_response(header: HeaderMap, response: DeleteResponse) -> axum::response::Response {
186 match response {
187 DeleteResponse::Instance(response) => Self::ok_json(header, &response.resource),
188 DeleteResponse::Type(_) | DeleteResponse::System(_) => Self::no_content(header),
189 }
190 }
191}