1
0
mirror of https://github.com/CCC-MF/bwhc-kafka-rest-proxy.git synced 2025-04-19 19:16:51 +00:00

feat: use clap to handle args and env vars

closes #2
This commit is contained in:
Paul-Christian Volkmer 2024-03-14 10:14:55 +01:00
parent d5bc8779a0
commit aafd7f314d
4 changed files with 71 additions and 26 deletions

View File

@ -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"

View File

@ -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 <TOKEN>
Options:
--bootstrap-server <BOOTSTRAP_SERVER>
Kafka Bootstrap-Server(s) [env: KAFKA_BOOTSTRAP_SERVERS=] [default: kafka:9094]
--topic <TOPIC>
Kafka Topic [env: APP_KAFKA_TOPIC=] [default: etl-processor_input]
--token <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.
Löschanfrage ausgelöst, da der Consent-Status den Wert `rejected` hat.

30
src/cli.rs Normal file
View File

@ -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,
}

View File

@ -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<Body>, 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<FutureProducer>,
Json(mtb_file): Json<MtbFile>,
) -> 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.")
})
}