use crate::errors::OxenHttpError;
use crate::helpers::get_repo;
use crate::params::{app_data, path_param};
use actix_web::{HttpRequest, HttpResponse, Result, web};
use liboxen::error::OxenError;
use liboxen::repositories;
use liboxen::view::StatusMessage;
use liboxen::view::fork::ForkRequest;
#[utoipa::path(
post,
path = "/api/repos/{namespace}/{repo_name}/fork",
tag = "Fork",
description = "Fork a repository to a new namespace, optionally with a new name.",
params(
("namespace" = String, Path, description = "Namespace of the repository to fork", example = "ox"),
("repo_name" = String, Path, description = "Name of the repository to fork", example = "yolov7-repo"),
),
request_body(
content = ForkRequest,
description = "Fork target details, including the new namespace and optional new repository name.",
),
responses(
(status = 202, description = "Fork initiated successfully", body = StatusMessage),
(status = 409, description = "Repository already exists at destination namespace/name", body = StatusMessage),
(status = 404, description = "Original repository not found")
)
)]
pub async fn fork(
req: HttpRequest,
body: web::Json<ForkRequest>,
) -> Result<HttpResponse, OxenHttpError> {
log::debug!("Forking repository with request: {req:?}");
let app_data = app_data(&req)?;
let namespace = path_param(&req, "namespace")?.to_string();
let repo_name = path_param(&req, "repo_name")?.to_string();
let original_repo = get_repo(app_data, &namespace, &repo_name)?;
original_repo.save()?;
let new_repo_namespace = body.namespace.clone();
let new_repo_name = body.new_repo_name.clone().unwrap_or(repo_name.clone());
let new_repo_path = app_data.path.join(&new_repo_namespace).join(&new_repo_name);
match repositories::fork::start_fork(original_repo.path, new_repo_path.clone()) {
Ok(fork_start_response) => {
log::info!("Successfully forked repository to {:?}", &new_repo_path);
Ok(HttpResponse::Accepted().json(fork_start_response))
}
Err(err) => {
log::error!("Failed to fork repository: {err:?}");
Err(OxenHttpError::from(err))
}
}
}
#[utoipa::path(
get,
path = "/api/repos/{namespace}/{repo_name}/fork/status",
tag = "Fork",
description = "Check the status of an in-progress or completed fork operation.",
params(
("namespace" = String, Path, description = "Namespace of the forked repository", example = "new-user"),
("repo_name" = String, Path, description = "Name of the forked repository", example = "yolov7-repo"),
),
responses(
(status = 200, description = "Fork status returned successfully", body = StatusMessage),
(status = 404, description = "Fork status not found or repository does not exist")
)
)]
pub async fn get_status(req: HttpRequest) -> Result<HttpResponse, OxenHttpError> {
let app_data = app_data(&req)?;
let namespace = path_param(&req, "namespace")?.to_string();
let repo_name = path_param(&req, "repo_name")?.to_string();
log::debug!("Getting fork status for repo: {namespace}/{repo_name}");
let repo_path = app_data.path.join(&namespace).join(&repo_name);
match repositories::fork::get_fork_status(&repo_path) {
Ok(status) => Ok(HttpResponse::Ok().json(status)),
Err(OxenError::ForkStatusNotFound) => {
Ok(HttpResponse::NotFound().json(StatusMessage::error("Fork status not found")))
}
Err(e) => {
log::error!("Failed to get fork status: {e}");
Err(OxenHttpError::from(e))
}
}
}