use serde_json::json;
use tracing::info;
use trust_tasks_rs::specs::did_management::agent_name::{check, list, remove, resolve, update};
use super::{Cx, TaskError, at, spec_record, typed};
use crate::did_ops;
use crate::error::AppError;
use crate::server_push;
fn did_log(did_data: &serde_json::Value) -> Result<String, TaskError> {
match did_data {
serde_json::Value::String(s) => Ok(s.clone()),
_ => Err(AppError::Validation(
"[log] didData must be the slot's signed did.jsonl, as a string".into(),
)
.into()),
}
}
pub(crate) async fn update(
cx: &Cx<'_>,
p: update::v0_1::Payload,
) -> Result<update::v0_1::Response, TaskError> {
let auth = cx.auth().await?;
let state = cx.state;
let log = did_log(&serde_json::to_value(&p.did_data)?)?;
let desired = match p.state {
update::v0_1::PayloadState::Active => did_ops::AgentNameState::Active,
update::v0_1::PayloadState::Parked => did_ops::AgentNameState::Parked,
_ => {
return Err(AppError::Validation("unsupported agent-name state".into()).into());
}
};
let record = did_ops::update_agent_name(
&auth,
state,
&p.mnemonic,
&p.name,
&log,
p.domain.as_deref(),
desired,
)
.await?;
server_push::notify_servers_did(state, p.mnemonic.to_string());
info!(did = %auth.did, mnemonic = %p.mnemonic.as_str(), name = %p.name.as_str(), state = ?desired, "agent name updated");
typed(
json!({ "record": spec_record::<update::v0_1::DidRecord>(state, &record)? }),
"agent-name update response",
)
}
pub(crate) async fn remove(
cx: &Cx<'_>,
p: remove::v0_1::Payload,
) -> Result<remove::v0_1::Response, TaskError> {
let auth = cx.auth().await?;
let state = cx.state;
let log = did_log(&serde_json::to_value(&p.did_data)?)?;
let record = did_ops::remove_agent_name(
&auth,
state,
&p.mnemonic,
&p.name,
&log,
p.domain.as_deref(),
)
.await?;
server_push::notify_servers_did(state, p.mnemonic.to_string());
info!(did = %auth.did, mnemonic = %p.mnemonic.as_str(), name = %p.name.as_str(), "agent name removed");
typed(
json!({ "record": spec_record::<remove::v0_1::DidRecord>(state, &record)? }),
"agent-name remove response",
)
}
pub(crate) async fn list(
cx: &Cx<'_>,
p: list::v0_1::Payload,
) -> Result<list::v0_1::Response, TaskError> {
let auth = cx.auth().await?;
let (domain, names) =
did_ops::list_agent_names(&auth, cx.state, &p.mnemonic, p.domain.as_deref()).await?;
let entries: Vec<_> = names
.iter()
.map(|e| {
json!({
"name": e.name,
"enabled": e.enabled,
"createdAt": at(e.created_at),
})
})
.collect();
let mut body = serde_json::Map::new();
body.insert("mnemonic".into(), json!(p.mnemonic.as_str()));
if !domain.is_empty() {
body.insert("domain".into(), json!(domain));
}
body.insert("agentNames".into(), json!(entries));
typed(serde_json::Value::Object(body), "agent-name list response")
}
pub(crate) async fn check(
cx: &Cx<'_>,
p: check::v0_1::Payload,
) -> Result<check::v0_1::Response, TaskError> {
let auth = cx.auth().await?;
let state = cx.state;
let domain = super::queries::resolve_agent_name_domain(&auth, state, p.domain.as_deref())
.await
.map_err(|e| {
TaskError::Declared(
check::v0_1::error_codes::UNKNOWN_DOMAIN,
AppErrorMessage(e).to_string(),
)
})?;
let result = did_ops::check_agent_name(state, &domain, &p.name)
.await
.map_err(|e| match e {
AppError::Validation(message) => {
TaskError::Declared(check::v0_1::error_codes::INVALID_NAME, message)
}
other => other.into(),
})?;
typed(serde_json::to_value(result)?, "agent-name check response")
}
struct AppErrorMessage(AppError);
impl std::fmt::Display for AppErrorMessage {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0.user_message())
}
}
pub(crate) async fn resolve(
cx: &Cx<'_>,
p: resolve::v0_1::Payload,
) -> Result<resolve::v0_1::Response, TaskError> {
let auth = cx.auth().await?;
let dids: Vec<String> = p.dids.iter().map(|d| d.to_string()).collect();
let names = did_ops::resolve_agent_names(cx.state, &auth, &dids).await?;
let mut seen = std::collections::HashSet::new();
let entries: Vec<_> = dids
.iter()
.filter(|did| seen.insert(did.as_str()))
.filter_map(|did| {
names
.get(did)
.map(|names| json!({ "did": did, "names": names }))
})
.collect();
typed(json!({ "entries": entries }), "agent-name resolve response")
}