use axum::Json;
use axum::extract::State;
use axum::http::StatusCode;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use uuid::Uuid;
use vti_common::error::AppError;
use crate::join::{HolderBinding, JoinTransport, submit_inner};
use crate::server::AppState;
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
#[derive(utoipa::ToSchema)]
pub struct SubmitRequestBody {
pub applicant_did: String,
pub vp: JsonValue,
#[serde(default)]
pub registry_consent: bool,
#[serde(default)]
pub extensions: JsonValue,
pub audience: String,
pub created: i64,
pub signature: String,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
#[derive(utoipa::ToSchema)]
pub struct SubmitResponse {
pub request_id: Uuid,
pub status: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub vmc: Option<JsonValue>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub role_vec: Option<JsonValue>,
}
#[utoipa::path(
post, path = "/join-requests", tag = "join-requests",
request_body = SubmitRequestBody,
responses(
(status = 201, description = "Join request submitted", body = SubmitResponse),
(status = 400, description = "Holder-binding / audience / freshness validation failed"),
(status = 409, description = "An open join request already exists for this applicant"),
),
)]
pub async fn submit(
State(state): State<AppState>,
Json(req): Json<SubmitRequestBody>,
) -> Result<(StatusCode, Json<SubmitResponse>), AppError> {
let outcome = submit_inner(
&state,
req.applicant_did,
req.vp,
req.registry_consent,
req.extensions,
Some(HolderBinding {
signature_hex: &req.signature,
audience: &req.audience,
created: req.created,
}),
JoinTransport::Rest,
)
.await?;
let (vmc, role_vec) = match &outcome.admit {
Some(a) => (
Some(
serde_json::to_value(&a.vmc)
.map_err(|e| AppError::Internal(format!("serialise VMC: {e}")))?,
),
Some(
serde_json::to_value(&a.role_vec)
.map_err(|e| AppError::Internal(format!("serialise VEC: {e}")))?,
),
),
None => (None, None),
};
Ok((
StatusCode::CREATED,
Json(SubmitResponse {
request_id: outcome.request.id,
status: outcome.request.status.to_string(),
vmc,
role_vec,
}),
))
}