From ac92c53799e1ac45056f2962b2bdf5ae80e636bb Mon Sep 17 00:00:00 2001 From: alemi Date: Tue, 22 Oct 2024 20:24:31 +0200 Subject: [PATCH] feat: inherit env from parent collections --- postwoman.toml | 9 +++++---- src/fmt.rs | 6 +++--- src/main.rs | 32 +++++++++++++++++--------------- src/model/endpoint.rs | 4 ++-- src/model/mod.rs | 12 ++++++++---- 5 files changed, 35 insertions(+), 28 deletions(-) diff --git a/postwoman.toml b/postwoman.toml index e0f4ece..18b3a98 100644 --- a/postwoman.toml +++ b/postwoman.toml @@ -1,13 +1,14 @@ +include = ["other.toml", "api/auth.toml"] # include other postwoman collections relative to this one + +[env] # these will be replaced in fields and inherited by includes. environment vars overrule these +PW_TOKEN = "set-me-as-and-environment-variable!" + [client] # HTTP client configuration user_agent = "postwoman@sample/0.4.1" timeout = 60 # max time for each request to complete, in seconds redirects = 5 # allow up to five redirects, defaults to none base = "https://api.alemi.dev" # all route urls will be appended to this base -[env] # these will be replaced in routes options. environment vars overrule these -PW_TOKEN = "set-me-as-and-environment-variable!" - - [route.healthcheck] # the simplest possible route: just name and path path = "/" diff --git a/src/fmt.rs b/src/fmt.rs index eee657c..3023746 100644 --- a/src/fmt.rs +++ b/src/fmt.rs @@ -59,12 +59,12 @@ impl PrintableResult for ListResult { for (namespace, collection) in collections { println!("-> {namespace}"); - for (key, value) in collection.env.unwrap_or_default() { + for (key, value) in collection.env { println!(" + {key}={}", crate::ext::stringify_toml(&value)); } - for (name, endpoint) in collection.route.unwrap_or_default() { - let url = endpoint.url(collection.client.as_ref().and_then(|x| x.base.as_deref())) + for (name, endpoint) in collection.route { + let url = endpoint.url(collection.client.base.as_deref()) .split('?') .next() .unwrap_or_default() diff --git a/src/main.rs b/src/main.rs index 5e80288..2ffc78a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ use std::str::FromStr; use clap::{Parser, Subcommand}; +use ext::FillableFromEnvironment; use fmt::{PrintableResult, ReportableResult}; use indexmap::IndexMap; pub use model::PostWomanCollection; @@ -79,7 +80,7 @@ fn main() { let mut collections = IndexMap::new(); - if !load_collections(&mut collections, args.collection.clone()) { + if !load_collections(&mut collections, args.collection.clone(), &toml::Table::default()) { return; } @@ -147,10 +148,10 @@ async fn run_collection_endpoints( ) { // this is always safe to compile because we tested it beforehand let pattern = regex::Regex::new(&query).expect("tested it before and still failed here???"); - let client = std::sync::Arc::new(collection.client.unwrap_or_default()); - let env = std::sync::Arc::new(collection.env.unwrap_or_default()); + let env = std::sync::Arc::new(collection.env); + let client = std::sync::Arc::new(collection.client.fill(&env)); - for (name, mut endpoint) in collection.route.unwrap_or_default() { + for (name, mut endpoint) in collection.route { let full_name = ext::full_name(&namespace, &name); if pattern.find(&full_name).is_none() { continue }; @@ -195,7 +196,7 @@ async fn run_collection_endpoints( } } -fn load_collections(store: &mut IndexMap, mut path: std::path::PathBuf) -> bool { +fn load_collections(store: &mut IndexMap, mut path: std::path::PathBuf, parent_env: &toml::Table) -> bool { let collection_raw = match std::fs::read_to_string(&path) { Ok(x) => x, Err(e) => { @@ -204,7 +205,7 @@ fn load_collections(store: &mut IndexMap, mut path: }, }; - let collection: PostWomanCollection = match toml::from_str(&collection_raw) { + let mut collection: PostWomanCollection = match toml::from_str(&collection_raw) { Ok(x) => x, Err(e) => { eprintln!("! error parsing collection {path:?}: {e}"); @@ -212,23 +213,24 @@ fn load_collections(store: &mut IndexMap, mut path: }, }; + collection.env.extend(parent_env.iter().map(|(k, v)| (k.clone(), v.clone()))); + let name = path.to_string_lossy().replace(".toml", ""); let mut to_include = Vec::new(); - if let Some(ref includes) = collection.include { - path.pop(); - for include in includes { - let mut base = path.clone(); - let new = std::path::PathBuf::from_str(include).expect("infallible"); - base.push(new); - to_include.push(base); - } + path.pop(); + for include in &collection.include { + let mut base = path.clone(); + let new = std::path::PathBuf::from_str(include).expect("infallible"); + base.push(new); + to_include.push(base); } + let parent_env = collection.env.clone(); store.insert(name, collection); for base in to_include { - if !load_collections(store, base) { + if !load_collections(store, base, &parent_env) { return false; } } diff --git a/src/model/endpoint.rs b/src/model/endpoint.rs index 8851e11..ccb1cfb 100644 --- a/src/model/endpoint.rs +++ b/src/model/endpoint.rs @@ -1,4 +1,4 @@ -use std::{collections::HashMap, str::FromStr}; +use std::str::FromStr; use base64::{prelude::BASE64_STANDARD, Engine}; use http::method::InvalidMethod; @@ -8,7 +8,7 @@ use jaq_interpret::FilterT; use crate::errors::InvalidHeaderError; use crate::{PostWomanError, APP_USER_AGENT}; -use crate::ext::{stringify_json, stringify_toml, FillableFromEnvironment, StringOr}; +use crate::ext::{stringify_json, FillableFromEnvironment, StringOr}; use super::{ExtractorConfig, ClientConfig}; diff --git a/src/model/mod.rs b/src/model/mod.rs index 3a0d403..80d7c7b 100644 --- a/src/model/mod.rs +++ b/src/model/mod.rs @@ -8,9 +8,13 @@ pub use extractor::ExtractorConfig; #[derive(Debug, Default, Clone, serde::Serialize, serde::Deserialize)] pub struct PostWomanCollection { - pub client: Option, - pub env: Option, - pub include: Option>, + #[serde(default)] + pub client: ClientConfig, + #[serde(default)] + pub include: Vec, + #[serde(default)] + pub env: toml::Table, + #[serde(default)] + pub route: indexmap::IndexMap, // it's weird to name it singular but makes more sense in config - pub route: Option>, }