use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VaultDeleteBody {
pub id: String,
pub force: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expected_version: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub reason: Option<String>,
}
#[cfg(test)]
mod vault_tests {
use super::*;
#[test]
fn an_unset_delete_member_is_absent() {
let body = VaultDeleteBody {
id: "secret-1".into(),
force: false,
expected_version: None,
reason: None,
};
assert_eq!(
serde_json::to_value(&body).expect("serialises"),
serde_json::json!({ "id": "secret-1", "force": false })
);
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VaultUpsertBody {
pub context_id: String,
pub targets: Vec<serde_json::Value>,
pub label: String,
pub secret_kind: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expected_version: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tags: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub notes: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub expires_at: Option<String>,
#[serde(flatten, default, skip_serializing_if = "serde_json::Map::is_empty")]
pub extra: serde_json::Map<String, serde_json::Value>,
}
#[cfg(test)]
mod upsert_tests {
use super::*;
#[test]
fn a_minimal_upsert_carries_only_what_was_set() {
let body = VaultUpsertBody {
context_id: "openvtc".into(),
targets: vec![
serde_json::json!({"kind": "web-origin", "origin": "https://example.test"}),
],
label: "example login".into(),
secret_kind: "password".into(),
id: None,
expected_version: None,
tags: None,
notes: None,
expires_at: None,
extra: serde_json::Map::new(),
};
assert_eq!(
serde_json::to_value(&body).expect("serialises"),
serde_json::json!({
"contextId": "openvtc",
"targets": [{"kind": "web-origin", "origin": "https://example.test"}],
"label": "example login",
"secretKind": "password",
})
);
}
#[test]
fn unmodelled_members_flatten_alongside() {
let mut extra = serde_json::Map::new();
extra.insert(
"favicon".into(),
serde_json::json!("data:image/png;base64,AA"),
);
let body = VaultUpsertBody {
context_id: "openvtc".into(),
targets: vec![
serde_json::json!({"kind": "web-origin", "origin": "https://example.test"}),
],
label: "example login".into(),
secret_kind: "password".into(),
id: None,
expected_version: None,
tags: None,
notes: None,
expires_at: None,
extra,
};
let v = serde_json::to_value(&body).expect("serialises");
assert_eq!(
v.get("favicon").and_then(|f| f.as_str()),
Some("data:image/png;base64,AA"),
"an unmodelled member sits beside the modelled ones"
);
assert!(
v.get("extra").is_none(),
"the hatch itself is never a member"
);
}
}