3cab86b
use axum::extract::{Path, Query, State};
use axum::Json;
use export::rest::{AppResponseData, NoData};
use serde::{Deserialize, Serialize};
use session::Session;
use utoipa::IntoParams;
use crate::errors::{ApiError, GitApiError};
use crate::handler::{RepoPath, require_writer, resolve_reader};
use crate::service::GitService;
use crate::service::topics::{CreateTopicCmd, TopicDto, TopicRepoDto};
#[derive(Debug, Clone, Deserialize, IntoParams)]
pub struct TopicNamePath {
pub name: String,
}
#[derive(Debug, Clone, Deserialize, IntoParams)]
pub struct TopicListQuery {
pub limit: Option<u64>,
pub offset: Option<u64>,
}
#[derive(Debug, Clone, Deserialize, IntoParams, utoipa::ToSchema)]
pub struct RepoTopicPath {
pub space_name: String,
pub repo_name: String,
pub topic_name: String,
}
#[derive(Debug, Clone, Serialize)]
pub struct TopicOkResp {
pub topic: TopicDto,
}
#[utoipa::path(
post,
path = "/topics",
tag = "topics",
request_body = CreateTopicCmd,
responses(
(status = 200, description = "create topic", body = AppResponseData<TopicDto>)
)
)]
pub async fn create_topic(
State(service): State<GitService>,
Json(cmd): Json<CreateTopicCmd>,
) -> Result<AppResponseData<TopicDto>, ApiError> {
let out = service.create_topic(cmd.name).await.map_err(GitApiError::from)?;
Ok(AppResponseData::ok(out))
}
#[utoipa::path(
get,
path = "/topics",
tag = "topics",
params(TopicListQuery),
responses(
(status = 200, description = "list topics", body = AppResponseData<Vec<TopicDto>>)
)
)]
pub async fn list_topics(
State(service): State<GitService>,
Query(q): Query<TopicListQuery>,
) -> Result<AppResponseData<Vec<TopicDto>>, ApiError> {
let limit = q.limit.unwrap_or(50).clamp(1, 100);
let offset = q.offset.unwrap_or(0);
let out = service.list_topics(limit, offset).await.map_err(GitApiError::from)?;
Ok(AppResponseData::ok(out))
}
#[utoipa::path(
get,
path = "/topics/{name}",
tag = "topics",
params(TopicNamePath),
responses(
(status = 200, description = "get topic", body = AppResponseData<TopicDto>)
)
)]
pub async fn get_topic(
State(service): State<GitService>,
Path(p): Path<TopicNamePath>,
) -> Result<AppResponseData<TopicDto>, ApiError> {
let out = service.get_topic(&p.name).await.map_err(GitApiError::from)?;
Ok(AppResponseData::ok(out))
}
#[utoipa::path(
get,
path = "/topics/{name}/repos",
tag = "topics",
params(TopicNamePath, TopicListQuery),
responses(
(status = 200, description = "repos using topic", body = AppResponseData<Vec<TopicRepoDto>>)
)
)]
pub async fn list_repos_by_topic(
State(service): State<GitService>,
Path(p): Path<TopicNamePath>,
Query(q): Query<TopicListQuery>,
) -> Result<AppResponseData<Vec<TopicRepoDto>>, ApiError> {
let limit = q.limit.unwrap_or(50).clamp(1, 100);
let offset = q.offset.unwrap_or(0);
let out = service.list_repos_by_topic(&p.name, limit, offset).await.map_err(GitApiError::from)?;
Ok(AppResponseData::ok(out))
}
#[utoipa::path(
put,
path = "/repos/{space_name}/{repo_name}/topics/{topic_name}",
tag = "repos",
params(RepoTopicPath),
responses(
(status = 200, description = "add topic to repo", body = AppResponseData<TopicDto>)
)
)]
pub async fn add_topic_to_repo(
State(service): State<GitService>,
session: Session,
Path(p): Path<RepoTopicPath>,
) -> Result<AppResponseData<TopicDto>, ApiError> {
let (ctx, _uid) = require_writer(&service, &session, p.space_name.clone(), p.repo_name.clone()).await?;
let out = service.add_topic_to_repo(&ctx, &p.topic_name).await.map_err(GitApiError::from)?;
Ok(AppResponseData::ok(out))
}
#[utoipa::path(
delete,
path = "/repos/{space_name}/{repo_name}/topics/{topic_name}",
tag = "repos",
params(RepoTopicPath),
responses(
(status = 200, description = "remove topic from repo", body = AppResponseData<NoData>)
)
)]
pub async fn remove_topic_from_repo(
State(service): State<GitService>,
session: Session,
Path(p): Path<RepoTopicPath>,
) -> Result<AppResponseData<NoData>, ApiError> {
let (ctx, _uid) = require_writer(&service, &session, p.space_name.clone(), p.repo_name.clone()).await?;
service.remove_topic_from_repo(&ctx, &p.topic_name).await.map_err(GitApiError::from)?;
Ok(AppResponseData::empty())
}
#[utoipa::path(
get,
path = "/repos/{space_name}/{repo_name}/topics",
tag = "repos",
params(RepoPath),
responses(
(status = 200, description = "list repo topics", body = AppResponseData<Vec<TopicDto>>)
)
)]
pub async fn list_repo_topics(
State(service): State<GitService>,
session: Session,
Path(path): Path<RepoPath>,
) -> Result<AppResponseData<Vec<TopicDto>>, ApiError> {
let (ctx, _uid) = resolve_reader(&service, &session, path.space_name.clone(), path.repo_name.clone()).await?;
let out = service.list_repo_topics(&ctx).await.map_err(GitApiError::from)?;
Ok(AppResponseData::ok(out))
}