use serde::{Deserialize, Serialize};
use super::BootstrapRequest;
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ProvisionIntegrationRequest {
pub request: BootstrapRequest,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub context: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub assertion: Option<AssertionMode>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "vcValiditySeconds"
)]
pub vc_validity_seconds: Option<i64>,
#[serde(default, skip_serializing_if = "is_false", alias = "createContext")]
pub create_context: bool,
}
fn is_false(b: &bool) -> bool {
!b
}
#[derive(Debug, Clone, Copy, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub enum AssertionMode {
#[default]
#[serde(alias = "didSigned")]
DidSigned,
#[serde(alias = "pinnedOnly")]
PinnedOnly,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ProvisionIntegrationResponse {
pub bundle: String,
pub digest: String,
pub summary: ProvisionSummary,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", deny_unknown_fields)]
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
pub struct ProvisionSummary {
#[serde(alias = "clientDid")]
pub client_did: String,
#[serde(default, alias = "adminDid")]
pub admin_did: String,
#[serde(default, alias = "adminRolledOver")]
pub admin_rolled_over: bool,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "integrationDid"
)]
pub integration_did: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "templateName"
)]
pub template_name: Option<String>,
#[serde(
default,
skip_serializing_if = "Option::is_none",
alias = "templateKind"
)]
pub template_kind: Option<String>,
#[serde(default, alias = "adminTemplateName")]
pub admin_template_name: Option<String>,
#[serde(alias = "bundleIdHex")]
pub bundle_id_hex: String,
#[serde(alias = "secretCount")]
pub secret_count: usize,
#[serde(alias = "outputCount")]
pub output_count: usize,
#[serde(default, alias = "webvhServerId")]
pub webvh_server_id: Option<String>,
#[serde(default, alias = "contextCreated")]
pub context_created: bool,
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn request_accepts_both_camel_and_snake_case() {
use crate::provision_integration::ProvisionRequestBuilder;
let (seed, pub_bytes) = crate::sealed_transfer::generate_ed25519_keypair();
let client_did = affinidi_crypto::did_key::ed25519_pub_to_did_key(&pub_bytes);
let vp = ProvisionRequestBuilder::new("didcomm-mediator")
.sign_with(&seed, &client_did)
.await
.expect("sign VP");
let request_json = serde_json::to_value(&vp).expect("serialize VP");
let camel = serde_json::json!({
"request": request_json,
"assertion": "pinnedOnly",
"vcValiditySeconds": 3600,
"createContext": true,
});
let req: ProvisionIntegrationRequest = serde_json::from_value(camel).expect("camelCase");
assert!(matches!(req.assertion, Some(AssertionMode::PinnedOnly)));
assert_eq!(req.vc_validity_seconds, Some(3600));
assert!(req.create_context);
let snake = serde_json::json!({
"request": request_json,
"assertion": "did-signed",
"vc_validity_seconds": 60,
"create_context": false,
});
let req: ProvisionIntegrationRequest = serde_json::from_value(snake).expect("snake_case");
assert!(matches!(req.assertion, Some(AssertionMode::DidSigned)));
assert_eq!(req.vc_validity_seconds, Some(60));
assert!(!req.create_context);
let out = serde_json::to_value(&req).unwrap();
assert!(out.get("vc_validity_seconds").is_some());
assert!(out.get("vcValiditySeconds").is_none());
}
#[test]
fn summary_accepts_camel_case() {
let camel = r#"{
"clientDid": "did:key:zClient",
"adminDid": "did:key:zAdmin",
"adminRolledOver": true,
"integrationDid": "did:webvh:x",
"templateName": "did-host-http",
"templateKind": "did-hosting-server",
"bundleIdHex": "deadbeef",
"secretCount": 2,
"outputCount": 1,
"webvhServerId": "srv-1",
"contextCreated": true
}"#;
let s: ProvisionSummary = serde_json::from_str(camel).expect("camelCase summary");
assert_eq!(s.client_did, "did:key:zClient");
assert_eq!(s.admin_did, "did:key:zAdmin");
assert!(s.admin_rolled_over);
assert_eq!(s.integration_did.as_deref(), Some("did:webvh:x"));
assert_eq!(s.secret_count, 2);
assert!(s.context_created);
let out = serde_json::to_value(&s).unwrap();
assert!(out.get("client_did").is_some());
assert!(out.get("clientDid").is_none());
}
}