From aafd7f314d458ebed40ebc5fe5cadbb00c2b341a Mon Sep 17 00:00:00 2001 From: Paul-Christian Volkmer Date: Thu, 14 Mar 2024 10:14:55 +0100 Subject: [PATCH] feat: use clap to handle args and env vars closes #2 --- Cargo.toml | 7 +++++++ README.md | 22 ++++++++++++++++++---- src/cli.rs | 30 ++++++++++++++++++++++++++++++ src/main.rs | 38 ++++++++++++++++---------------------- 4 files changed, 71 insertions(+), 26 deletions(-) create mode 100644 src/cli.rs diff --git a/Cargo.toml b/Cargo.toml index 7fd8f8f..c4d5120 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,13 @@ description = "bwHC MTB-File REST Proxy für Kafka" # Dependencies +[dependencies.clap] +version = "4.5" +features = ["derive", "env"] + +[dependencies.lazy_static] +version = "1.4" + [dependencies.log] version = "0.4" diff --git a/README.md b/README.md index d4094f9..0b15208 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,21 @@ Verwendung im Zusammenspiel mit https://github.com/CCC-MF/etl-processor ## Konfiguration -Die Anwendung lässt sich mit Umgebungsvariablen konfigurieren. +Beim Start der Anwendung können Parameter angegeben werden. + +``` +Usage: bwhc-kafka-rest-proxy [OPTIONS] --token + +Options: + --bootstrap-server + Kafka Bootstrap-Server(s) [env: KAFKA_BOOTSTRAP_SERVERS=] [default: kafka:9094] + --topic + Kafka Topic [env: APP_KAFKA_TOPIC=] [default: etl-processor_input] + --token + bcrypt hashed Security Token [env: APP_SECURITY_TOKEN=] +``` + +Die Anwendung lässt sich auch mit Umgebungsvariablen konfigurieren. * `APP_KAFKA_SERVERS`: Zu verwendende Kafka-Bootstrap-Server als kommagetrennte Liste * `APP_KAFKA_TOPIC`: Zu verwendendes Topic zum Warten auf neue Anfragen. Standardwert: `etl-processor_input` @@ -81,10 +95,10 @@ Resultierender Kafka-Record: #### Löschen von Patienten -Anfrage auch hier mit **curl**: +Anfrage auch hier mit *curl*: ```bash -curl -v -u token:very-secret \ +curl -u token:very-secret \ -H "Content-Type: application/json" \ -X DELETE \ http://localhost:3000/mtbfile/P1 @@ -112,4 +126,4 @@ In optionaler Verbindung mit [Key-Based-Retention](https://github.com/CCC-MF/etl letzte und aktuelle Record, hier die Information eines Consent-Widerspruchs, in Kafka vorgehalten. Trifft dieser Kafka-Record im [ETL-Prozessor](https://github.com/CCC-MF/etl-processor) ein, so wird dort ebenfalls eine -Löschanfrage ausgelöst, da der Consent-Status den Wert `rejected` hat. \ No newline at end of file +Löschanfrage ausgelöst, da der Consent-Status den Wert `rejected` hat. diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 0000000..c591d3f --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,30 @@ +use clap::Parser; + +#[derive(Parser)] +#[command(author, version, about)] +#[command(arg_required_else_help(true))] +pub struct Cli { + #[arg( + long, + alias = "kafka-servers", + env = "APP_KAFKA_SERVERS", + default_value = "kafka:9094", + help = "Kafka Bootstrap Server" + )] + pub bootstrap_server: String, + #[arg( + long, + alias = "kafka-topic", + env = "APP_KAFKA_TOPIC", + default_value = "etl-processor_input", + help = "Kafka Topic" + )] + pub topic: String, + #[arg( + long, + alias = "security-token", + env = "APP_SECURITY_TOKEN", + help = "bcrypt hashed Security Token" + )] + pub token: String, +} diff --git a/src/main.rs b/src/main.rs index 7769a25..4159a86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,24 +1,28 @@ -use std::env; use std::time::Duration; +use axum::{Extension, Json, Router}; use axum::body::Body; use axum::extract::Path; -use axum::http::header::AUTHORIZATION; use axum::http::{Request, StatusCode}; +use axum::http::header::AUTHORIZATION; use axum::middleware::{from_fn, Next}; use axum::response::Response; use axum::routing::{delete, post}; -use axum::{Extension, Json, Router}; use bwhc_dto::MtbFile; +use clap::Parser; +use lazy_static::lazy_static; +use rdkafka::ClientConfig; use rdkafka::message::{Header, OwnedHeaders}; use rdkafka::producer::{FutureProducer, FutureRecord}; -use rdkafka::ClientConfig; use serde::{Deserialize, Serialize}; #[cfg(debug_assertions)] use tower_http::trace::TraceLayer; use uuid::Uuid; +use crate::cli::Cli; + mod auth; +mod cli; #[derive(Serialize, Deserialize)] struct RecordKey { @@ -26,10 +30,12 @@ struct RecordKey { patient_id: String, } +lazy_static! { + static ref CONFIG: Cli = Cli::parse(); +} + #[tokio::main] async fn main() { - let _ = bcrypt_hashed_token(); - #[cfg(debug_assertions)] { tracing_subscriber::fmt() @@ -37,10 +43,8 @@ async fn main() { .init(); } - let boostrap_servers = env::var("KAFKA_BOOTSTRAP_SERVERS").unwrap_or("kafka:9094".into()); - let producer: FutureProducer = ClientConfig::new() - .set("bootstrap.servers", boostrap_servers.as_str()) + .set("bootstrap.servers", CONFIG.bootstrap_server.as_str()) .set("message.timeout.ms", "5000") .create() .expect("Producer creation error"); @@ -60,7 +64,7 @@ async fn main() { async fn check_basic_auth(request: Request, next: Next) -> Response { if let Some(Ok(auth_header)) = request.headers().get(AUTHORIZATION).map(|x| x.to_str()) { - if auth::check_basic_auth(auth_header, &bcrypt_hashed_token()) { + if auth::check_basic_auth(auth_header, &CONFIG.token) { return next.run(request).await; } } @@ -76,7 +80,7 @@ async fn handle_delete( ) -> Response { let delete_mtb_file = MtbFile::new_with_consent_rejected(&patient_id); - match send_mtb_file(producer, &dst_topic(), delete_mtb_file).await { + match send_mtb_file(producer, &CONFIG.topic, delete_mtb_file).await { Ok(request_id) => success_response(&request_id), _ => error_response(), } @@ -86,7 +90,7 @@ async fn handle_post( Extension(producer): Extension, Json(mtb_file): Json, ) -> Response { - match send_mtb_file(producer, &dst_topic(), mtb_file).await { + match send_mtb_file(producer, &CONFIG.topic, mtb_file).await { Ok(request_id) => success_response(&request_id), _ => error_response(), } @@ -143,13 +147,3 @@ fn error_response() -> Response { .body(Body::empty()) .expect("response built") } - -fn dst_topic() -> String { - env::var("APP_KAFKA_TOPIC").unwrap_or("etl-processor_input".into()) -} - -fn bcrypt_hashed_token() -> String { - env::var("APP_SECURITY_TOKEN").unwrap_or_else(|_| { - panic!("Missing configuration 'APP_SECURITY_TOKEN'. Provide bcrypt hashed token value.") - }) -}