use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use crate::keys::{KeyOrigin, KeyStatus, KeyType};
#[derive(Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[serde(rename_all = "camelCase")]
pub struct CreateKeyBody {
#[serde(alias = "key_type")]
pub key_type: KeyType,
#[serde(alias = "derivation_path")]
pub derivation_path: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mnemonic: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, alias = "context_id", skip_serializing_if = "Option::is_none")]
pub context_id: Option<String>,
}
impl std::fmt::Debug for CreateKeyBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CreateKeyBody")
.field("key_type", &self.key_type)
.field("derivation_path", &self.derivation_path)
.field("mnemonic", &self.mnemonic.as_ref().map(|_| "<redacted>"))
.field("label", &self.label)
.field("context_id", &self.context_id)
.finish()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateKeyResultBody {
#[serde(alias = "key_id")]
pub key_id: String,
#[serde(alias = "key_type")]
pub key_type: KeyType,
#[serde(alias = "derivation_path")]
pub derivation_path: String,
#[serde(alias = "public_key")]
pub public_key: String,
pub status: KeyStatus,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default = "default_derived")]
pub origin: KeyOrigin,
#[serde(alias = "created_at")]
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CreateKeyResponseBody {
pub key: CreateKeyResultBody,
}
fn default_derived() -> KeyOrigin {
KeyOrigin::Derived
}
#[cfg(test)]
mod null_member_tests {
use super::*;
#[test]
fn an_unset_member_is_absent_from_the_wire_not_null() {
let minimal = CreateKeyBody {
key_type: KeyType::Ed25519,
derivation_path: String::new(),
mnemonic: None,
label: None,
context_id: None,
};
assert_eq!(
serde_json::to_value(&minimal).expect("serialises"),
serde_json::json!({"keyType": "ed25519", "derivationPath": ""}),
"an unset member must be absent, not null"
);
}
#[test]
fn a_set_member_still_serialises() {
let labelled = CreateKeyBody {
key_type: KeyType::Ed25519,
derivation_path: "m/26'/2'/0'/1'".into(),
mnemonic: None,
label: Some("persona-signing".into()),
context_id: Some("openvtc".into()),
};
assert_eq!(
serde_json::to_value(&labelled).expect("serialises"),
serde_json::json!({
"keyType": "ed25519",
"derivationPath": "m/26'/2'/0'/1'",
"label": "persona-signing",
"contextId": "openvtc",
})
);
}
}