oxen-server 0.53.0

Oxen server is a fast data version control backend, supporting local disk and S3. Self host your repositories on your own storage, or use the hosted platform on Oxen.ai. Stores, syncs, and serves versioned datasets, model checkpoints, game assets, studio media, and any large data. Use the oxen CLI to push and pull from the oxen server.
use crate::errors::OxenHttpError;
use crate::helpers::get_repo_async;
use crate::params::{app_data, parse_resource_async, path_param};

use actix_web::{HttpRequest, HttpResponse, Result};

use liboxen::view::{ParseResourceResponse, StatusMessage};

use log;
use utoipa;

/// Get the latest revision of a resource
#[utoipa::path(
    get,
    path = "/api/repos/{namespace}/{repo_name}/revisions/{resource}",
    tag = "Revisions",
    description = "Get the latest commit information for the given resource (<branch>/<path>)",
    params(
        ("namespace" = String, Path, description = "Namespace of the repository"),
        ("repo_name" = String, Path, description = "Name of the repository"),
        ("resource" = String, Path, description = "Path of the resource (in <branch>/<path> format)"),
    ),
    responses(
        (status = 200, description = "Directory structure resolved", body = ParseResourceResponse),
        (status = 404, description = "Directory or repository not found"),
        (status = 400, description = "Invalid resource string")
    )
)]
pub async fn get(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();
    let repository = get_repo_async(app_data, &namespace, &repo_name).await?;

    let resource = parse_resource_async(&req, &repository).await?;
    let response = ParseResourceResponse {
        status: StatusMessage::resource_found(),
        resource: resource.into(),
    };

    log::debug!("Response: {response:?}");
    Ok(HttpResponse::Ok().json(response))
}

#[cfg(test)]
mod tests {
    use crate::test;

    use actix_web::http::{self};

    use actix_web::body::to_bytes;

    use liboxen::error::OxenError;
    use liboxen::repositories;

    use crate::controllers;

    #[actix_web::test]
    async fn test_get() -> Result<(), OxenError> {
        let sync_dir = test::get_sync_dir()?;
        let namespace = "Testing-Namespace";
        let repo_name = "Testing-Repo";
        let resource_str = "main/to/resource";

        let repo = test::create_local_repo(&sync_dir, namespace, repo_name)?;
        let path = liboxen::test::add_txt_file_to_dir(&repo.path, resource_str)?;
        repositories::add(&repo, path).await?;
        repositories::commit(&repo, "first commit")?;

        let uri = format!(
            "/oxen/{namespace}/{repo_name}/branches/resolve_resource_attributes/{resource_str}"
        );

        let req = test::repo_request_with_param(
            &sync_dir,
            &uri,
            namespace,
            repo_name,
            "resource",
            resource_str,
        );

        let resp = controllers::revisions::get(req).await.unwrap();
        assert_eq!(resp.status(), http::StatusCode::OK);

        let body = to_bytes(resp.into_body()).await.unwrap();
        let text = std::str::from_utf8(&body).unwrap();
        let parse_resp: liboxen::view::ParseResourceResponse =
            serde_json::from_str(text).map_err(OxenError::from)?;

        assert_eq!(parse_resp.resource.branch.unwrap().name, "main");
        // fix windows tests
        let path = parse_resp.resource.path.to_string_lossy();
        let path = path.replace('\\', "/");
        assert_eq!(path, "to/resource");

        // cleanup
        test::cleanup_repo_and_sync_dir(repo, &sync_dir)?;

        Ok(())
    }
}