← snapshot
2738 bytes
//! The commit endpoint.
use axum::Json;
use axum::extract::{Path, State};
use axum::http::StatusCode;
use jac_object::ObjectStore as _;
use jac_repo::RefName;
use jac_telemetry::Envelope;
use serde::Deserialize;
use serde_json::{Value, json};
use crate::dto::{self, ActorRef, SnapshotDto};
use crate::error::ApiError;
use crate::routes::with_repo_mut;
use crate::state::SharedState;
/// A commit request. `files` is the **entire** tree, not a delta —
/// milestone-1 honesty, straight from the engine's contract.
#[derive(Debug, Deserialize)]
pub(crate) struct CommitRequest {
pub r#ref: String,
pub actor: ActorRef,
/// `human`, `agent`, or `mixed` — whose hands made it, hashed into the
/// snapshot's identity.
pub provenance: String,
pub message: String,
pub files: Vec<CommitFile>,
}
#[derive(Debug, Deserialize)]
pub(crate) struct CommitFile {
pub path: String,
pub content: String,
}
/// `POST /api/repos/{slug}/commits`.
pub(crate) async fn create(
State(state): State<SharedState>,
Path(slug): Path<String>,
Json(req): Json<CommitRequest>,
) -> Result<(StatusCode, Json<Value>), ApiError> {
let file_count = req.files.len();
let provenance_label = req.provenance.clone();
let ref_label = req.r#ref.clone();
let actor_kind = req.actor.kind.clone();
let body = with_repo_mut(&state, &slug, |entry| {
let name = RefName::parse(&req.r#ref)?;
let author = req.actor.resolve(entry)?;
let provenance = dto::parse_provenance(&req.provenance)?;
let owned: Vec<(String, Vec<u8>)> = req
.files
.iter()
.map(|f| (f.path.clone(), f.content.clone().into_bytes()))
.collect();
let files: Vec<(&str, &[u8])> = owned
.iter()
.map(|(p, c)| (p.as_str(), c.as_slice()))
.collect();
let id = entry
.repo
.commit(&name, &files, author, provenance, &req.message)?;
let snapshot = entry.repo.store().snapshot(id)?.clone();
let at = snapshot.at.as_millis();
Ok((
json!({
"snapshot": SnapshotDto::of(id, &snapshot, entry),
"ref": name.as_str(),
}),
at,
))
});
let (body, at) = body?;
state.telemetry.emit(
Envelope::event("jac.commit.created", at)
.attr("repo", slug)
.attr("ref", ref_label)
.attr("provenance", provenance_label)
.attr("actor_kind", actor_kind)
.measurement(
"files",
f64::from(u32::try_from(file_count).unwrap_or(u32::MAX)),
),
);
Ok((StatusCode::CREATED, Json(body)))
}