Skip to content

Commit 6c8fe73

Browse files
committed
refactor: make transfair a cli with subcommands
1 parent 308c3a5 commit 6c8fe73

File tree

8 files changed

+168
-203
lines changed

8 files changed

+168
-203
lines changed

Dockerfile

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ RUN chmod +x /app/*
88
FROM gcr.io/distroless/cc-debian12
99
ARG COMPONENT
1010
COPY --from=chmodder /app/$COMPONENT /usr/local/bin/samply
11-
ENTRYPOINT [ "/usr/local/bin/samply" ]
11+
ENTRYPOINT [ "/usr/local/bin/samply" ]
12+
CMD [ "dic" ]

src/config.rs

Lines changed: 48 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,60 @@
11
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr, sync::LazyLock, time::{Duration, Instant}};
22

3-
use clap::{Args, CommandFactory, FromArgMatches, Parser};
3+
use clap::Parser;
44
use reqwest::{Certificate, Client, Url};
55
use anyhow::anyhow;
66
use tokio::sync::RwLock;
77
use tracing::info;
88

9-
use crate::{ttp::{self, greifswald::GreifswaldConfig, mainzelliste::MlConfig, Ttp}, CONFIG};
9+
use crate::{ttp::Ttp, CLIENT};
1010

11-
#[derive(Parser, Clone, Debug)]
11+
#[derive(Debug, Parser)]
1212
#[clap(author, version, about, long_about = None)]
13-
pub struct Config {
13+
pub struct CliArgs {
14+
#[clap(subcommand)]
15+
pub subcommand: SubCommand,
16+
17+
/// Trusted tls root certificates
18+
#[clap(long, env)]
19+
pub tls_ca_certificates_dir: Option<PathBuf>,
20+
/// Disable TLS verification
21+
#[clap(long, env, default_value_t = false)]
22+
pub tls_disable: bool,
23+
}
24+
25+
impl CliArgs {
26+
pub fn build_client(&self) -> Client {
27+
let mut client_builder = Client::builder();
28+
client_builder = client_builder
29+
.danger_accept_invalid_hostnames(self.tls_disable)
30+
.danger_accept_invalid_certs(self.tls_disable);
31+
if let Some(tls_ca_dir) = &self.tls_ca_certificates_dir {
32+
info!("Loading available custom ca certificates from {:?}", self.tls_ca_certificates_dir);
33+
for path_res in tls_ca_dir.read_dir().expect(&format!("Unable to read {:?}", self.tls_ca_certificates_dir)) {
34+
if let Ok(path_buf) = path_res {
35+
info!("Adding custom ca certificate {:?}", path_buf.path());
36+
client_builder = client_builder.add_root_certificate(
37+
Certificate::from_pem(
38+
&fs::read(path_buf.path()).expect(&format!("Unable to read file provided: {:?}", path_buf.path()))
39+
).expect(&format!("Unable to convert {:?} to a certificate. Please verify it is a valid pem file", path_buf.path()))
40+
);
41+
}
42+
}
43+
}
44+
45+
client_builder.build().expect("Unable to initially build reqwest client")
46+
}
47+
}
48+
49+
#[derive(Debug, clap::Subcommand)]
50+
pub enum SubCommand {
51+
Dic(DicConfig)
52+
}
53+
54+
#[derive(Parser, Clone, Debug)]
55+
pub struct DicConfig {
1456
// Definition of necessary parameters for communicating with a ttp
15-
#[clap(skip)]
57+
#[clap(subcommand)]
1658
pub ttp: Option<Ttp>,
1759
// Either an id well-known to both, project and dic, or a temporary identifier created by the ttp
1860
#[clap(long, env, default_value = "TOKEN")]
@@ -35,55 +77,6 @@ pub struct Config {
3577
pub fhir_output_url: Url,
3678
#[clap(long, env, default_value = "")]
3779
pub fhir_output_credentials: Auth,
38-
/// Trusted tls root certificates
39-
#[clap(long, env)]
40-
pub tls_ca_certificates_dir: Option<PathBuf>,
41-
/// Disable TLS verification
42-
#[clap(long, env, default_value_t = false)]
43-
pub tls_disable: bool,
44-
45-
#[clap(skip)]
46-
pub client: Client,
47-
}
48-
49-
impl Config {
50-
pub fn parse() -> Self {
51-
let cmd = Config::command();
52-
let ttp_cmd = ttp::Ttp::augment_args(cmd.clone());
53-
let args_matches = cmd.get_matches();
54-
let mut this = Self::from_arg_matches(&args_matches).map_err(|e| e.exit()).unwrap();
55-
let ca_client = build_client(&this.tls_ca_certificates_dir, this.tls_disable);
56-
this.client = ca_client.clone();
57-
let mut ttp = ttp_cmd.try_get_matches().ok().and_then(|matches| Ttp::from_arg_matches(&matches).ok());
58-
if let Some(ref mut ttp) = ttp {
59-
let (Ttp::Mainzelliste(MlConfig {base, ..}) | Ttp::Greifswald(GreifswaldConfig {base, ..})) = ttp;
60-
base.client = ca_client.clone();
61-
}
62-
this.ttp = ttp;
63-
this
64-
}
65-
}
66-
67-
fn build_client(tls_ca_certificates_dir: &Option<PathBuf>, disable_tls: bool) -> Client {
68-
let mut client_builder = Client::builder();
69-
client_builder = client_builder
70-
.danger_accept_invalid_hostnames(disable_tls)
71-
.danger_accept_invalid_certs(disable_tls);
72-
if let Some(tls_ca_dir) = tls_ca_certificates_dir {
73-
info!("Loading available custom ca certificates from {:?}", tls_ca_certificates_dir);
74-
for path_res in tls_ca_dir.read_dir().expect(&format!("Unable to read {:?}", tls_ca_certificates_dir)) {
75-
if let Ok(path_buf) = path_res {
76-
info!("Adding custom ca certificate {:?}", path_buf.path());
77-
client_builder = client_builder.add_root_certificate(
78-
Certificate::from_pem(
79-
&fs::read(path_buf.path()).expect(&format!("Unable to read file provided: {:?}", path_buf.path()))
80-
).expect(&format!("Unable to convert {:?} to a certificate. Please verify it is a valid pem file", path_buf.path()))
81-
);
82-
}
83-
}
84-
}
85-
86-
client_builder.build().expect("Unable to initially build reqwest client")
8780
}
8881

8982
#[derive(Debug, Clone)]
@@ -144,7 +137,7 @@ impl ClientBuilderExt for reqwest::RequestBuilder {
144137
expires_in: u64,
145138
access_token: String,
146139
}
147-
let TokenRes { expires_in, access_token } = CONFIG.client
140+
let TokenRes { expires_in, access_token } = CLIENT
148141
.post(token_url.clone())
149142
.form(&serde_json::json!({
150143
"grant_type": "client_credentials",

src/fhir.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,20 @@ use fhir_sdk::r4b::{
55
resources::{Bundle, BundleEntry, BundleEntryRequest, ParametersParameter, Patient, Resource},
66
types::Identifier,
77
};
8-
use reqwest::{header, Client, StatusCode, Url};
8+
use reqwest::{header, StatusCode, Url};
99
use tracing::debug;
1010

11-
use crate::{config::{Auth, ClientBuilderExt}, requests::DataRequestPayload, CONFIG};
11+
use crate::{config::{Auth, ClientBuilderExt}, requests::DataRequestPayload, CLIENT};
1212

1313
#[derive(Clone, Debug)]
1414
pub struct FhirServer {
15-
url: Url,
15+
pub url: Url,
1616
auth: Auth,
17-
client: Client,
1817
}
1918

2019
impl FhirServer {
2120
pub fn new(url: Url, auth: Auth) -> Self {
22-
Self { url, auth, client: CONFIG.client.clone()}
21+
Self { url, auth }
2322
}
2423

2524
pub async fn post_data_request(
@@ -31,7 +30,7 @@ impl FhirServer {
3130

3231
let bundle: Bundle = payload.into();
3332

34-
let response = self.client
33+
let response = CLIENT
3534
.post(bundle_endpoint)
3635
.add_auth(&self.auth)
3736
.await?
@@ -58,7 +57,7 @@ impl FhirServer {
5857
let bundle_endpoint = format!("{}fhir/Bundle", self.url);
5958
debug!("Fetching new data from: {}", bundle_endpoint);
6059
let query = vec![("_lastUpdated", format!("gt{}", last_update.format("%Y-%m-%dT%H:%M:%S").to_string()))];
61-
let response = self.client
60+
let response = CLIENT
6261
.get(bundle_endpoint)
6362
.add_auth(&self.auth)
6463
.await?
@@ -77,7 +76,7 @@ impl FhirServer {
7776
pub async fn post_data(&self, bundle: &Bundle) -> anyhow::Result<reqwest::Response> {
7877
let bundle_endpoint = format!("{}fhir", self.url);
7978
debug!("Posting data to output fhir server: {}", bundle_endpoint);
80-
self.client
79+
CLIENT
8180
.post(bundle_endpoint)
8281
.add_auth(&self.auth)
8382
.await?
@@ -89,7 +88,7 @@ impl FhirServer {
8988
}
9089

9190
pub trait PatientExt: Sized {
92-
fn pseudonymize(self) -> axum::response::Result<Self>;
91+
fn pseudonymize(self, exchange_id_system: &str) -> axum::response::Result<Self>;
9392
fn add_id_request(self, id: String) -> Self;
9493
fn get_identifier(&self, id_system: &str) -> Option<&Identifier>;
9594
fn get_identifier_mut(&mut self, id_system: &str) -> Option<&mut Identifier>;
@@ -120,16 +119,16 @@ impl PatientExt for Patient {
120119
.find(|x| x.system.as_deref() == Some(id_system))
121120
}
122121

123-
fn pseudonymize(self) -> axum::response::Result<Self> {
122+
fn pseudonymize(self, exchange_id_system: &str) -> axum::response::Result<Self> {
124123
let Some(exchange_identifier) = self
125124
.identifier
126125
.iter()
127126
.find(|x| {
128-
x.as_ref().is_some_and(|y| y.system.as_deref() == Some(&CONFIG.exchange_id_system))
127+
x.as_ref().is_some_and(|y| y.system.as_deref() == Some(exchange_id_system))
129128
}) else {
130129
return Err((
131130
StatusCode::BAD_REQUEST,
132-
format!("Request did not contain identifier of system {}", &CONFIG.exchange_id_system)
131+
format!("Request did not contain identifier of system {exchange_id_system}")
133132
).into());
134133
};
135134
let pseudonymized_patient = Patient::builder()

0 commit comments

Comments
 (0)