mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-08-25 04:44:12 +02:00
Respect OpenAPI parameter serialization rules (#588)
This commit is contained in:
@@ -28,21 +28,59 @@ pub(crate) use duplicate_name::conflict_free_name;
|
||||
const MAX_HISTORY_ITEMS: usize = 20;
|
||||
|
||||
use crate::models::HttpRequestHeader;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
|
||||
/// Deduplicate headers by name (case-insensitive), keeping the latest (most specific) value.
|
||||
/// Preserves the order of first occurrence for each header name.
|
||||
pub(crate) fn dedupe_headers(headers: Vec<HttpRequestHeader>) -> Vec<HttpRequestHeader> {
|
||||
let mut index_by_name: HashMap<String, usize> = HashMap::new();
|
||||
let mut deduped: Vec<HttpRequestHeader> = Vec::new();
|
||||
for header in headers {
|
||||
let key = header.name.to_lowercase();
|
||||
if let Some(&idx) = index_by_name.get(&key) {
|
||||
deduped[idx] = header;
|
||||
} else {
|
||||
index_by_name.insert(key, deduped.len());
|
||||
deduped.push(header);
|
||||
}
|
||||
}
|
||||
deduped
|
||||
/// Merge a more-specific header layer over its parent. Names in the child replace
|
||||
/// inherited values case-insensitively, while duplicates declared together in
|
||||
/// either layer remain independent entries.
|
||||
pub(crate) fn merge_headers(
|
||||
mut parent: Vec<HttpRequestHeader>,
|
||||
child: Vec<HttpRequestHeader>,
|
||||
) -> Vec<HttpRequestHeader> {
|
||||
let child_names = child.iter().map(|header| header.name.to_lowercase()).collect::<HashSet<_>>();
|
||||
parent.retain(|header| !child_names.contains(&header.name.to_lowercase()));
|
||||
parent.extend(child);
|
||||
parent
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::merge_headers;
|
||||
use crate::models::HttpRequestHeader;
|
||||
|
||||
fn header(name: &str, value: &str) -> HttpRequestHeader {
|
||||
HttpRequestHeader { name: name.to_string(), value: value.to_string(), ..Default::default() }
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserves_duplicate_headers_declared_in_one_layer() {
|
||||
let merged = merge_headers(
|
||||
vec![header("Cookie", "inherited=1")],
|
||||
vec![
|
||||
header("Cookie", "required=1"),
|
||||
header("cookie", "optional=1"),
|
||||
],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
merged.iter().map(|header| header.value.as_str()).collect::<Vec<_>>(),
|
||||
vec!["required=1", "optional=1"],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn child_names_override_parent_names_without_affecting_other_headers() {
|
||||
let merged = merge_headers(
|
||||
vec![header("Accept", "*/*"), header("X-Parent", "kept")],
|
||||
vec![header("accept", "application/json")],
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
merged
|
||||
.iter()
|
||||
.map(|header| (header.name.as_str(), header.value.as_str()))
|
||||
.collect::<Vec<_>>(),
|
||||
vec![("X-Parent", "kept"), ("accept", "application/json")],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user