use serde::{Deserialize, Serialize};
use crate::did_secrets::{DidSecretsBundle, SecretEntry};
use crate::keys::KeyType;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GetContextSecretsBody {
pub id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContextSecretsResultBody {
pub did: String,
pub secrets: Vec<ContextSecretEntry>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ContextSecretEntry {
pub key_id: String,
pub key_type: KeyType,
pub private_key_multibase: String,
}
impl From<SecretEntry> for ContextSecretEntry {
fn from(e: SecretEntry) -> Self {
Self {
key_id: e.key_id,
key_type: e.key_type,
private_key_multibase: e.private_key_multibase,
}
}
}
impl From<ContextSecretEntry> for SecretEntry {
fn from(e: ContextSecretEntry) -> Self {
Self {
key_id: e.key_id,
key_type: e.key_type,
private_key_multibase: e.private_key_multibase,
}
}
}
impl From<DidSecretsBundle> for ContextSecretsResultBody {
fn from(b: DidSecretsBundle) -> Self {
Self {
did: b.did,
secrets: b.secrets.into_iter().map(Into::into).collect(),
}
}
}
impl From<ContextSecretsResultBody> for DidSecretsBundle {
fn from(b: ContextSecretsResultBody) -> Self {
Self {
did: b.did,
secrets: b.secrets.into_iter().map(Into::into).collect(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_wire_form_is_lower_camel_case() {
let body = ContextSecretsResultBody {
did: "did:webvh:QmExample:example.com:rooms:host-1".into(),
secrets: vec![ContextSecretEntry {
key_id: "did:webvh:QmExample:example.com:rooms:host-1#key-0".into(),
key_type: KeyType::Ed25519,
private_key_multibase: "z3u2en7t5LR2WtQH5PfFqMqtVcSdd7ELrcFtnP63HKq4KLg".into(),
}],
};
let json = serde_json::to_value(&body).unwrap();
let entry = &json["secrets"][0];
assert!(entry.get("keyId").is_some(), "expected keyId, got {entry}");
assert!(entry.get("keyType").is_some());
assert!(entry.get("privateKeyMultibase").is_some());
assert!(
entry.get("key_id").is_none(),
"snake_case members must not appear on the wire"
);
assert_eq!(entry["keyType"], "ed25519");
}
#[test]
fn a_bundle_round_trips_through_the_wire_form() {
let bundle = DidSecretsBundle {
did: "did:webvh:QmExample:example.com:rooms:host-1".into(),
secrets: vec![
SecretEntry {
key_id: "did:webvh:QmExample:example.com:rooms:host-1#key-0".into(),
key_type: KeyType::Ed25519,
private_key_multibase: "zSigning".into(),
},
SecretEntry {
key_id: "did:webvh:QmExample:example.com:rooms:host-1#key-1".into(),
key_type: KeyType::X25519,
private_key_multibase: "zAgreement".into(),
},
],
};
let back: DidSecretsBundle = ContextSecretsResultBody::from(bundle.clone()).into();
assert_eq!(back.did, bundle.did);
assert_eq!(back.secrets.len(), 2);
for (a, b) in back.secrets.iter().zip(&bundle.secrets) {
assert_eq!(a.key_id, b.key_id);
assert_eq!(a.key_type, b.key_type);
assert_eq!(a.private_key_multibase, b.private_key_multibase);
}
}
#[test]
fn an_empty_bundle_stays_an_empty_array() {
let body = ContextSecretsResultBody::from(DidSecretsBundle {
did: "did:example:123".into(),
secrets: vec![],
});
let json = serde_json::to_value(&body).unwrap();
assert_eq!(json["secrets"], serde_json::json!([]));
}
}