use super::helpers::TrustTaskOutcome;
use serde_json::Value;
use trust_tasks_rs::TrustTask;
use vta_sdk::protocols::audit_management::list::ListAuditLogsBody;
use vta_sdk::protocols::audit_management::retention::{GetRetentionBody, UpdateRetentionBody};
use crate::auth::AuthClaims;
use crate::operations;
use crate::server::AppState;
use super::helpers::{TRANSPORT_TRUST_TASK, app_error_to_reject, parse_payload, success_response};
pub(super) async fn handle_list_logs(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: ListAuditLogsBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::audit::list_audit_logs(&state.audit_ks, auth, &req, TRANSPORT_TRUST_TASK)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_get_retention(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let _req: GetRetentionBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::audit::get_retention(&state.config, auth, TRANSPORT_TRUST_TASK).await {
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
pub(super) async fn handle_update_retention(
state: &AppState,
auth: &AuthClaims,
doc: TrustTask<Value>,
) -> TrustTaskOutcome {
let req: UpdateRetentionBody = match parse_payload(&doc) {
Ok(r) => r,
Err(resp) => return resp,
};
match operations::audit::update_retention(
&state.config,
&state.audit_ks,
auth,
req.retention_days,
TRANSPORT_TRUST_TASK,
)
.await
{
Ok(body) => success_response(&doc, body),
Err(e) => app_error_to_reject(&doc, e),
}
}
#[cfg(test)]
mod tests {
use super::*;
use trust_tasks_rs::specs::audit::list::v0_1 as canonical;
use vta_sdk::protocols::audit_management::list::{
AuditEnvelope, AuditLogEntry, ListAuditLogsResultBody,
};
#[test]
fn request_and_response_conform_to_the_canonical_schema() {
let req = ListAuditLogsBody {
from: Some("2026-07-01T00:00:00Z".parse().unwrap()),
to: Some("2026-07-28T00:00:00Z".parse().unwrap()),
action: Some("acl.create".into()),
actor: Some("did:key:z6MkActor".into()),
outcome: Some("success".into()),
context_id: Some("ctx1".into()),
page_size: Some(25),
cursor: Some("opaque".into()),
};
let json = serde_json::to_value(&req).expect("serialize request");
serde_json::from_value::<canonical::Payload>(json.clone())
.unwrap_or_else(|e| panic!("request is not canonical `audit/list/0.1`: {e}\n{json:#}"));
let empty = serde_json::to_value(ListAuditLogsBody::default()).expect("serialize");
assert_eq!(
empty,
serde_json::json!({}),
"omitted filters must not serialize"
);
serde_json::from_value::<canonical::Payload>(empty).expect("empty request is canonical");
let row = AuditLogEntry {
id: "e1".into(),
timestamp: 1_785_239_374,
action: "acl.create".into(),
actor: "did:key:z6MkActor".into(),
resource: Some("did:key:z6MkSubject".into()),
outcome: "success".into(),
channel: Some("rest".into()),
context_id: Some("ctx1".into()),
detail: Some("because".into()),
};
let resp = ListAuditLogsResultBody {
entries: vec![AuditEnvelope::from(&row)],
truncated: true,
cursor: Some("opaque".into()),
};
let json = serde_json::to_value(&resp).expect("serialize response");
serde_json::from_value::<canonical::Response>(json.clone()).unwrap_or_else(|e| {
panic!("response is not canonical `audit/list/0.1`: {e}\n{json:#}")
});
let mut drifted = json;
drifted["totalPages"] = serde_json::json!(3);
assert!(
serde_json::from_value::<canonical::Response>(drifted).is_err(),
"the canonical Response must reject members the spec does not define"
);
}
}