use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct BundleDescriptor {
pub bundle_id: String,
pub algorithm: String,
pub transport_url: String,
pub transport_token: String,
pub expected_sha256: String,
pub expected_size_bytes: u64,
pub expires_at: DateTime<Utc>,
}
fn default_stream() -> String {
"stream".into()
}
fn default_true() -> bool {
true
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct InitiateExportBody {
pub password: String,
#[serde(default)]
pub include_audit: bool,
#[serde(default = "default_stream")]
pub algorithm: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct InitiateExportResultBody {
pub descriptor: BundleDescriptor,
pub completion_hint: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CompleteExportBody {
pub bundle_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct CompleteExportResultBody {
pub bundle_id: String,
pub downloaded: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct InitiateImportBody {
pub expected_sha256: String,
pub expected_size_bytes: u64,
#[serde(default = "default_stream")]
pub algorithm: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct InitiateImportResultBody {
pub descriptor: BundleDescriptor,
pub completion_hint: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct FinalizeImportBody {
pub bundle_id: String,
pub password: String,
#[serde(default = "default_true")]
pub confirm: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct FinalizeImportResultBody {
pub bundle_id: String,
pub status: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub source_did: Option<String>,
pub key_count: usize,
pub acl_count: usize,
pub context_count: usize,
pub audit_count: usize,
#[serde(default)]
pub imported_secret_count: usize,
#[serde(skip_serializing_if = "Option::is_none")]
pub message: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AbortBundleBody {
pub bundle_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct AbortBundleResultBody {
pub bundle_id: String,
pub aborted: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn initiate_export_body_round_trips() {
let body = InitiateExportBody {
password: "correct-horse-battery-staple".into(),
include_audit: true,
algorithm: "stream".into(),
};
let json = serde_json::to_string(&body).unwrap();
let restored: InitiateExportBody = serde_json::from_str(&json).unwrap();
assert!(restored.include_audit);
assert_eq!(restored.algorithm, "stream");
}
#[test]
fn algorithm_defaults_to_stream() {
let body: InitiateExportBody =
serde_json::from_str(r#"{"password": "twelve-chars"}"#).unwrap();
assert_eq!(body.algorithm, "stream");
assert!(!body.include_audit);
}
#[test]
fn finalize_import_confirm_defaults_to_true() {
let body: FinalizeImportBody =
serde_json::from_str(r#"{"bundle_id": "abc", "password": "twelve-chars"}"#).unwrap();
assert!(body.confirm);
}
#[test]
fn descriptor_round_trips() {
let descriptor = BundleDescriptor {
bundle_id: "00000000-0000-4000-8000-000000000000".into(),
algorithm: "stream".into(),
transport_url: "https://vta.example/backup/blob/00000000-0000-4000-8000-000000000000"
.into(),
transport_token: "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA".into(),
expected_sha256: "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
.into(),
expected_size_bytes: 1024,
expires_at: chrono::Utc::now(),
};
let json = serde_json::to_string(&descriptor).unwrap();
let restored: BundleDescriptor = serde_json::from_str(&json).unwrap();
assert_eq!(restored.bundle_id, descriptor.bundle_id);
assert_eq!(restored.expected_size_bytes, 1024);
}
}