Skip to content

Cli refactor #43

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ chrono = { version = "0.4.37", default-features = false, features = ["serde", "n
clap = { version = "4.5.3", features = ["env", "derive"] }
fhir-sdk = { version = "0.14.1", default-features = false, features = ["builders", "r4b"] }
futures-util = { version = "0.3", default-features = false }
once_cell = "1.19.0"
reqwest = { version = "0.12.2", features = ["json", "rustls-tls"], default-features = false }
serde = { version = "1.0.197", features = ["derive"] }
serde_json = "1"
Expand Down
3 changes: 2 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ RUN chmod +x /app/*
FROM gcr.io/distroless/cc-debian12
ARG COMPONENT
COPY --from=chmodder /app/$COMPONENT /usr/local/bin/samply
ENTRYPOINT [ "/usr/local/bin/samply" ]
ENTRYPOINT [ "/usr/local/bin/samply" ]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if we actually want to include this CMD instruction here it preserves backwards compatibility with the no ttp usecase but its probably not worth it as the other ttp use cases have require the linked bridgehead PR anyway

CMD [ "dic" ]
2 changes: 1 addition & 1 deletion dev/test
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ function start_bg() {
done
done
chmod +x artifacts/binaries-amd64/transfair
artifacts/binaries-amd64/transfair &
artifacts/binaries-amd64/transfair dic mainzelliste &
sleep 10
}

Expand Down
103 changes: 48 additions & 55 deletions src/config.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,60 @@
use std::{collections::HashMap, fs, path::PathBuf, str::FromStr, sync::LazyLock, time::{Duration, Instant}};

use clap::{Args, CommandFactory, FromArgMatches, Parser};
use clap::Parser;
use reqwest::{Certificate, Client, Url};
use anyhow::anyhow;
use tokio::sync::RwLock;
use tracing::info;

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

#[derive(Parser, Clone, Debug)]
#[derive(Debug, Parser)]
#[clap(author, version, about, long_about = None)]
pub struct Config {
pub struct CliArgs {
#[clap(subcommand)]
pub subcommand: SubCommand,

/// Trusted tls root certificates
#[clap(long, env)]
pub tls_ca_certificates_dir: Option<PathBuf>,
/// Disable TLS verification
#[clap(long, env, default_value_t = false)]
pub tls_disable: bool,
}

impl CliArgs {
pub fn build_client(&self) -> Client {
let mut client_builder = Client::builder();
client_builder = client_builder
.danger_accept_invalid_hostnames(self.tls_disable)
.danger_accept_invalid_certs(self.tls_disable);
if let Some(tls_ca_dir) = &self.tls_ca_certificates_dir {
info!("Loading available custom ca certificates from {:?}", self.tls_ca_certificates_dir);
for path_res in tls_ca_dir.read_dir().expect(&format!("Unable to read {:?}", self.tls_ca_certificates_dir)) {
if let Ok(path_buf) = path_res {
info!("Adding custom ca certificate {:?}", path_buf.path());
client_builder = client_builder.add_root_certificate(
Certificate::from_pem(
&fs::read(path_buf.path()).expect(&format!("Unable to read file provided: {:?}", path_buf.path()))
).expect(&format!("Unable to convert {:?} to a certificate. Please verify it is a valid pem file", path_buf.path()))
);
}
}
}

client_builder.build().expect("Unable to initially build reqwest client")
}
}

#[derive(Debug, clap::Subcommand)]
pub enum SubCommand {
Dic(DicConfig)
}

#[derive(Parser, Clone, Debug)]
pub struct DicConfig {
// Definition of necessary parameters for communicating with a ttp
#[clap(skip)]
#[clap(subcommand)]
pub ttp: Option<Ttp>,
// Either an id well-known to both, project and dic, or a temporary identifier created by the ttp
#[clap(long, env, default_value = "TOKEN")]
Expand All @@ -35,55 +77,6 @@ pub struct Config {
pub fhir_output_url: Url,
#[clap(long, env, default_value = "")]
pub fhir_output_credentials: Auth,
/// Trusted tls root certificates
#[clap(long, env)]
pub tls_ca_certificates_dir: Option<PathBuf>,
/// Disable TLS verification
#[clap(long, env, default_value_t = false)]
pub tls_disable: bool,

#[clap(skip)]
pub client: Client,
}

impl Config {
pub fn parse() -> Self {
let cmd = Config::command();
let ttp_cmd = ttp::Ttp::augment_args(cmd.clone());
let args_matches = cmd.get_matches();
let mut this = Self::from_arg_matches(&args_matches).map_err(|e| e.exit()).unwrap();
let ca_client = build_client(&this.tls_ca_certificates_dir, this.tls_disable);
this.client = ca_client.clone();
let mut ttp = ttp_cmd.try_get_matches().ok().and_then(|matches| Ttp::from_arg_matches(&matches).ok());
if let Some(ref mut ttp) = ttp {
let (Ttp::Mainzelliste(MlConfig {base, ..}) | Ttp::Greifswald(GreifswaldConfig {base, ..})) = ttp;
base.client = ca_client.clone();
}
this.ttp = ttp;
this
}
}

fn build_client(tls_ca_certificates_dir: &Option<PathBuf>, disable_tls: bool) -> Client {
let mut client_builder = Client::builder();
client_builder = client_builder
.danger_accept_invalid_hostnames(disable_tls)
.danger_accept_invalid_certs(disable_tls);
if let Some(tls_ca_dir) = tls_ca_certificates_dir {
info!("Loading available custom ca certificates from {:?}", tls_ca_certificates_dir);
for path_res in tls_ca_dir.read_dir().expect(&format!("Unable to read {:?}", tls_ca_certificates_dir)) {
if let Ok(path_buf) = path_res {
info!("Adding custom ca certificate {:?}", path_buf.path());
client_builder = client_builder.add_root_certificate(
Certificate::from_pem(
&fs::read(path_buf.path()).expect(&format!("Unable to read file provided: {:?}", path_buf.path()))
).expect(&format!("Unable to convert {:?} to a certificate. Please verify it is a valid pem file", path_buf.path()))
);
}
}
}

client_builder.build().expect("Unable to initially build reqwest client")
}

#[derive(Debug, Clone)]
Expand Down Expand Up @@ -144,7 +137,7 @@ impl ClientBuilderExt for reqwest::RequestBuilder {
expires_in: u64,
access_token: String,
}
let TokenRes { expires_in, access_token } = CONFIG.client
let TokenRes { expires_in, access_token } = CLIENT
.post(token_url.clone())
.form(&serde_json::json!({
"grant_type": "client_credentials",
Expand Down
23 changes: 11 additions & 12 deletions src/fhir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,20 @@ use fhir_sdk::r4b::{
resources::{Bundle, BundleEntry, BundleEntryRequest, Patient, Resource},
types::Identifier,
};
use reqwest::{header, Client, StatusCode, Url};
use reqwest::{header, StatusCode, Url};
use tracing::debug;

use crate::{config::{Auth, ClientBuilderExt}, requests::DataRequestPayload, CONFIG};
use crate::{config::{Auth, ClientBuilderExt}, requests::DataRequestPayload, CLIENT};

#[derive(Clone, Debug)]
pub struct FhirServer {
url: Url,
pub url: Url,
auth: Auth,
client: Client,
}

impl FhirServer {
pub fn new(url: Url, auth: Auth) -> Self {
Self { url, auth, client: CONFIG.client.clone()}
Self { url, auth }
}

pub async fn post_data_request(
Expand All @@ -31,7 +30,7 @@ impl FhirServer {

let bundle: Bundle = payload.into();

let response = self.client
let response = CLIENT
.post(bundle_endpoint)
.add_auth(&self.auth)
.await?
Expand All @@ -58,7 +57,7 @@ impl FhirServer {
let bundle_endpoint = format!("{}fhir/Bundle", self.url);
debug!("Fetching new data from: {}", bundle_endpoint);
let query = vec![("_lastUpdated", format!("gt{}", last_update.format("%Y-%m-%dT%H:%M:%S").to_string()))];
let response = self.client
let response = CLIENT
.get(bundle_endpoint)
.add_auth(&self.auth)
.await?
Expand All @@ -77,7 +76,7 @@ impl FhirServer {
pub async fn post_data(&self, bundle: &Bundle) -> anyhow::Result<reqwest::Response> {
let bundle_endpoint = format!("{}fhir", self.url);
debug!("Posting data to output fhir server: {}", bundle_endpoint);
self.client
CLIENT
.post(bundle_endpoint)
.add_auth(&self.auth)
.await?
Expand All @@ -89,7 +88,7 @@ impl FhirServer {
}

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

fn pseudonymize(self) -> axum::response::Result<Self> {
fn pseudonymize(self, exchange_id_system: &str) -> axum::response::Result<Self> {
let Some(exchange_identifier) = self
.identifier
.iter()
.find(|x| {
x.as_ref().is_some_and(|y| y.system.as_deref() == Some(&CONFIG.exchange_id_system))
x.as_ref().is_some_and(|y| y.system.as_deref() == Some(exchange_id_system))
}) else {
return Err((
StatusCode::BAD_REQUEST,
format!("Request did not contain identifier of system {}", &CONFIG.exchange_id_system)
format!("Request did not contain identifier of system {exchange_id_system}")
).into());
};
let pseudonymized_patient = Patient::builder()
Expand Down
Loading
Loading