pub mod services;
use serde::{Deserialize, Serialize};
#[cfg(feature = "client")]
use crate::client::VtaClient;
#[cfg(feature = "client")]
use crate::error::VtaError;
#[cfg(feature = "client")]
use crate::protocols::protocol_management;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub struct EnableDidcommRequest {
pub mediator_did: String,
#[serde(default)]
pub force: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handshake_timeout_secs: Option<u64>,
}
impl EnableDidcommRequest {
pub fn new(mediator_did: impl Into<String>) -> Self {
Self {
mediator_did: mediator_did.into(),
force: false,
handshake_timeout_secs: None,
}
}
pub fn force(mut self, force: bool) -> Self {
self.force = force;
self
}
pub fn handshake_timeout_secs(mut self, secs: u64) -> Self {
self.handshake_timeout_secs = Some(secs);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct EnableDidcommResponse {
pub new_version_id: String,
pub mediator_did: String,
pub mediator_endpoint: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub vta_did: String,
#[serde(default)]
pub serverless: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DidcommStatusResponse {
pub enabled: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub mediator_did: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub websocket_status: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct EnableDidcommConflictBody {
pub error: String,
#[serde(default)]
pub mediator_did: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisableDidcommRequest {
pub drain_ttl_secs: u64,
}
impl DisableDidcommRequest {
pub fn new(drain_ttl_secs: u64) -> Self {
Self { drain_ttl_secs }
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DisableDidcommResponse {
pub new_version_id: String,
pub prior_mediator_did: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub drains_until: Option<String>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub vta_did: String,
#[serde(default)]
pub serverless: bool,
}
#[cfg(feature = "client")]
impl VtaClient {
pub async fn enable_didcomm(
&self,
req: EnableDidcommRequest,
) -> Result<EnableDidcommResponse, VtaError> {
match &self.transport {
crate::client::Transport::Rest {
client,
base_url,
auth,
} => {
Self::ensure_token_valid(client, base_url, auth).await?;
let token = auth.lock().await.token.clone();
let req = client
.post(format!("{base_url}/services/didcomm/enable"))
.json(&req);
let resp = Self::with_auth_token(req, &token).send().await?;
Self::handle_response(resp).await
}
#[cfg(feature = "session")]
crate::client::Transport::DIDComm { .. } => Err(VtaError::UnsupportedTransport(
"enable_didcomm is REST-only".into(),
)),
}
}
pub async fn didcomm_status(&self) -> Result<DidcommStatusResponse, VtaError> {
match &self.transport {
crate::client::Transport::Rest {
client,
base_url,
auth,
} => {
crate::client::VtaClient::ensure_token_valid(client, base_url, auth).await?;
let token = auth.lock().await.token.clone();
let req = client.get(format!("{base_url}/services/didcomm"));
let resp = crate::client::VtaClient::with_auth_token(req, &token)
.send()
.await?;
crate::client::VtaClient::handle_response(resp).await
}
#[cfg(feature = "session")]
crate::client::Transport::DIDComm { .. } => Err(VtaError::UnsupportedTransport(
"didcomm status is REST-only in the SDK".into(),
)),
}
}
pub async fn disable_didcomm(
&self,
req: DisableDidcommRequest,
) -> Result<DisableDidcommResponse, VtaError> {
self.rpc(
protocol_management::DISABLE_DIDCOMM,
serde_json::to_value(&req)?,
protocol_management::DISABLE_DIDCOMM_RESULT,
30,
|c, url| c.post(format!("{url}/services/didcomm/disable")).json(&req),
)
.await
}
pub async fn update_didcomm(
&self,
req: UpdateDidcommRequest,
) -> Result<UpdateDidcommResponse, VtaError> {
self.rpc(
protocol_management::UPDATE_DIDCOMM,
serde_json::to_value(&req)?,
protocol_management::UPDATE_DIDCOMM_RESULT,
120,
|c, url| c.post(format!("{url}/services/didcomm/update")).json(&req),
)
.await
}
pub async fn enable_rest(
&self,
req: services::EnableRestRequest,
) -> Result<services::ServiceMutationResponse, VtaError> {
self.rpc(
protocol_management::ENABLE_REST,
serde_json::to_value(&req)?,
protocol_management::ENABLE_REST_RESULT,
30,
|c, url| c.post(format!("{url}/services/rest/enable")).json(&req),
)
.await
}
pub async fn update_rest(
&self,
req: services::UpdateRestRequest,
) -> Result<services::ServiceMutationResponse, VtaError> {
self.rpc(
protocol_management::UPDATE_REST,
serde_json::to_value(&req)?,
protocol_management::UPDATE_REST_RESULT,
30,
|c, url| c.post(format!("{url}/services/rest/update")).json(&req),
)
.await
}
pub async fn disable_rest(
&self,
req: services::DisableRestRequest,
) -> Result<services::ServiceMutationResponse, VtaError> {
self.rpc(
protocol_management::DISABLE_REST,
serde_json::to_value(&req)?,
protocol_management::DISABLE_REST_RESULT,
30,
|c, url| c.post(format!("{url}/services/rest/disable")).json(&req),
)
.await
}
pub async fn rollback_rest(
&self,
req: services::RollbackRestRequest,
) -> Result<services::RollbackResponse, VtaError> {
self.rpc(
protocol_management::ROLLBACK_REST,
serde_json::to_value(&req)?,
protocol_management::ROLLBACK_REST_RESULT,
60,
|c, url| c.post(format!("{url}/services/rest/rollback")).json(&req),
)
.await
}
pub async fn enable_webauthn(
&self,
req: services::EnableWebauthnRequest,
) -> Result<services::ServiceMutationResponse, VtaError> {
self.rpc(
protocol_management::ENABLE_WEBAUTHN,
serde_json::to_value(&req)?,
protocol_management::ENABLE_WEBAUTHN_RESULT,
30,
|c, url| c.post(format!("{url}/services/webauthn/enable")).json(&req),
)
.await
}
pub async fn update_webauthn(
&self,
req: services::UpdateWebauthnRequest,
) -> Result<services::ServiceMutationResponse, VtaError> {
self.rpc(
protocol_management::UPDATE_WEBAUTHN,
serde_json::to_value(&req)?,
protocol_management::UPDATE_WEBAUTHN_RESULT,
30,
|c, url| c.post(format!("{url}/services/webauthn/update")).json(&req),
)
.await
}
pub async fn disable_webauthn(
&self,
req: services::DisableWebauthnRequest,
) -> Result<services::ServiceMutationResponse, VtaError> {
self.rpc(
protocol_management::DISABLE_WEBAUTHN,
serde_json::to_value(&req)?,
protocol_management::DISABLE_WEBAUTHN_RESULT,
300,
|c, url| {
c.post(format!("{url}/services/webauthn/disable"))
.json(&req)
},
)
.await
}
pub async fn rollback_webauthn(
&self,
req: services::RollbackWebauthnRequest,
) -> Result<services::RollbackResponse, VtaError> {
self.rpc(
protocol_management::ROLLBACK_WEBAUTHN,
serde_json::to_value(&req)?,
protocol_management::ROLLBACK_WEBAUTHN_RESULT,
300,
|c, url| {
c.post(format!("{url}/services/webauthn/rollback"))
.json(&req)
},
)
.await
}
pub async fn rollback_didcomm(
&self,
req: services::RollbackDidcommRequest,
) -> Result<services::RollbackResponse, VtaError> {
self.rpc(
protocol_management::ROLLBACK_DIDCOMM,
serde_json::to_value(&req)?,
protocol_management::ROLLBACK_DIDCOMM_RESULT,
120,
|c, url| {
c.post(format!("{url}/services/didcomm/rollback"))
.json(&req)
},
)
.await
}
pub async fn list_services(&self) -> Result<services::ServicesListResponse, VtaError> {
self.rpc(
protocol_management::LIST_SERVICES,
serde_json::Value::Null,
protocol_management::LIST_SERVICES_RESULT,
30,
|c, url| c.get(format!("{url}/services")),
)
.await
}
pub async fn list_drain(&self) -> Result<services::DrainListResponse, VtaError> {
self.rpc(
protocol_management::LIST_DRAIN,
serde_json::Value::Null,
protocol_management::LIST_DRAIN_RESULT,
30,
|c, url| c.get(format!("{url}/services/didcomm/drain")),
)
.await
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[must_use]
pub struct UpdateDidcommRequest {
pub new_mediator_did: String,
pub drain_ttl_secs: u64,
#[serde(default)]
pub force: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub handshake_timeout_secs: Option<u64>,
#[serde(default)]
pub rollback: bool,
}
impl UpdateDidcommRequest {
pub fn new(new_mediator_did: impl Into<String>, drain_ttl_secs: u64) -> Self {
Self {
new_mediator_did: new_mediator_did.into(),
drain_ttl_secs,
force: false,
handshake_timeout_secs: None,
rollback: false,
}
}
pub fn force(mut self, force: bool) -> Self {
self.force = force;
self
}
pub fn rollback(mut self, rollback: bool) -> Self {
self.rollback = rollback;
self
}
pub fn handshake_timeout_secs(mut self, secs: u64) -> Self {
self.handshake_timeout_secs = Some(secs);
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UpdateDidcommResponse {
pub new_version_id: String,
pub prior_mediator_did: String,
pub active_mediator_did: String,
pub active_mediator_endpoint: String,
pub drains_until: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub vta_did: String,
#[serde(default)]
pub serverless: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DrainCancelRequest {
pub mediator_did: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DrainCancelResponse {
pub mediator_did: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediatorStats {
pub mediator_did: String,
pub inbound_count: u64,
pub first_seen: String,
pub last_seen: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SenderLastSeen {
pub sender_did: String,
pub last_seen_mediator: String,
pub last_seen_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MediatorReport {
#[serde(default)]
pub since: Option<String>,
pub until: String,
pub mediators: Vec<MediatorStats>,
pub senders: Vec<SenderLastSeen>,
}
#[cfg(feature = "client")]
impl VtaClient {
pub async fn drain_cancel(
&self,
req: DrainCancelRequest,
) -> Result<DrainCancelResponse, VtaError> {
self.rpc(
protocol_management::DRAIN_CANCEL,
serde_json::to_value(&req)?,
protocol_management::DRAIN_CANCEL_RESULT,
30,
|c, url| c.post(format!("{url}/mediators/drain/cancel")).json(&req),
)
.await
}
pub async fn mediator_report(
&self,
since: Option<&str>,
until: Option<&str>,
) -> Result<MediatorReport, VtaError> {
let since_owned = since.map(str::to_string);
let until_owned = until.map(str::to_string);
let qs = build_report_query(since_owned.as_deref(), until_owned.as_deref());
self.rpc(
protocol_management::MEDIATOR_REPORT,
serde_json::json!({
"since": since_owned,
"until": until_owned,
}),
protocol_management::MEDIATOR_REPORT_RESULT,
30,
move |c, url| {
let url = if qs.is_empty() {
format!("{url}/mediators/report")
} else {
format!("{url}/mediators/report?{qs}")
};
c.get(url)
},
)
.await
}
}
#[cfg(feature = "client")]
fn build_report_query(since: Option<&str>, until: Option<&str>) -> String {
let mut parts: Vec<String> = Vec::new();
if let Some(s) = since {
parts.push(format!("since={}", url_encode(s)));
}
if let Some(u) = until {
parts.push(format!("until={}", url_encode(u)));
}
parts.join("&")
}
#[cfg(feature = "client")]
fn url_encode(s: &str) -> String {
s.chars()
.flat_map(|c| match c {
':' => "%3A".chars().collect::<Vec<_>>(),
'+' => "%2B".chars().collect::<Vec<_>>(),
_ => vec![c],
})
.collect()
}