26af08e
use models::repo::{repo_relation_topic, repo_topic, repos};
use sea_orm::{
ActiveModelTrait, ColumnTrait, EntityTrait, QueryFilter, QueryOrder, QuerySelect, Set,
};
use serde::{Deserialize, Serialize};
use time::OffsetDateTime;
use utoipa::ToSchema;
use uuid::Uuid;
use crate::context::facade::GitContext;
use crate::service::GitService;
use crate::share::errors::GitError;
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct TopicDto {
pub id: Uuid,
pub name: String,
pub created_at: OffsetDateTime,
pub updated_at: OffsetDateTime,
}
impl From<repo_topic::Model> for TopicDto {
fn from(m: repo_topic::Model) -> Self {
Self {
id: m.id,
name: m.name,
created_at: m.created_at,
updated_at: m.updated_at,
}
}
}
#[derive(Debug, Clone, Deserialize, ToSchema)]
pub struct CreateTopicCmd {
pub name: String,
}
#[derive(Debug, Clone, Serialize, ToSchema)]
pub struct TopicRepoDto {
pub repo_id: Uuid,
pub repo_name: String,
pub space_name: String,
pub is_private: bool,
}
impl GitService {
pub async fn create_topic(&self, name: String) -> Result<TopicDto, GitError> {
let trimmed = name.trim().to_string();
if trimmed.is_empty() {
return Err(GitError::InvalidArgument(
"topic name must not be empty".to_string(),
));
}
let now = OffsetDateTime::now_utc();
let existing = repo_topic::Entity::find()
.filter(repo_topic::Column::Name.eq(&trimmed))
.one(self.db_read())
.await?;
if let Some(row) = existing {
return Ok(row.into());
}
let am = repo_topic::ActiveModel {
id: Set(Uuid::now_v7()),
name: Set(trimmed),
created_at: Set(now),
updated_at: Set(now),
};
let saved = am.insert(self.db_write()).await?;
Ok(saved.into())
}
pub async fn get_topic(&self, name: &str) -> Result<TopicDto, GitError> {
let row = repo_topic::Entity::find()
.filter(repo_topic::Column::Name.eq(name))
.one(self.db_read())
.await?
.ok_or(GitError::NotFound)?;
Ok(row.into())
}
pub async fn list_topics(&self, limit: u64, offset: u64) -> Result<Vec<TopicDto>, GitError> {
let rows = repo_topic::Entity::find()
.order_by_asc(repo_topic::Column::Name)
.limit(limit)
.offset(offset)
.all(self.db_read())
.await?;
Ok(rows.into_iter().map(Into::into).collect())
}
pub async fn add_topic_to_repo(
&self,
ctx: &GitContext,
topic_name: &str,
) -> Result<TopicDto, GitError> {
let now = OffsetDateTime::now_utc();
let topic = match repo_topic::Entity::find()
.filter(repo_topic::Column::Name.eq(topic_name))
.one(self.db_read())
.await?
{
Some(t) => t,
None => {
let am = repo_topic::ActiveModel {
id: Set(Uuid::now_v7()),
name: Set(topic_name.to_string()),
created_at: Set(now),
updated_at: Set(now),
};
am.insert(self.db_write()).await?
}
};
let existing = repo_relation_topic::Entity::find()
.filter(repo_relation_topic::Column::RepoId.eq(ctx.model.id))
.filter(repo_relation_topic::Column::TopicId.eq(topic.id))
.one(self.db_read())
.await?;
if existing.is_none() {
let am = repo_relation_topic::ActiveModel {
id: Set(Uuid::now_v7()),
repo_id: Set(ctx.model.id),
topic_id: Set(topic.id),
created_at: Set(now),
};
am.insert(self.db_write()).await?;
}
Ok(topic.into())
}
pub async fn remove_topic_from_repo(
&self,
ctx: &GitContext,
topic_name: &str,
) -> Result<(), GitError> {
let topic = repo_topic::Entity::find()
.filter(repo_topic::Column::Name.eq(topic_name))
.one(self.db_read())
.await?
.ok_or(GitError::NotFound)?;
repo_relation_topic::Entity::delete_many()
.filter(repo_relation_topic::Column::RepoId.eq(ctx.model.id))
.filter(repo_relation_topic::Column::TopicId.eq(topic.id))
.exec(self.db_write())
.await?;
Ok(())
}
pub async fn list_repo_topics(&self, ctx: &GitContext) -> Result<Vec<TopicDto>, GitError> {
let relations = repo_relation_topic::Entity::find()
.filter(repo_relation_topic::Column::RepoId.eq(ctx.model.id))
.all(self.db_read())
.await?;
if relations.is_empty() {
return Ok(Vec::new());
}
let topic_ids: Vec<Uuid> = relations.iter().map(|r| r.topic_id).collect();
let topics = repo_topic::Entity::find()
.filter(repo_topic::Column::Id.is_in(topic_ids))
.all(self.db_read())
.await?;
Ok(topics.into_iter().map(Into::into).collect())
}
pub async fn list_repos_by_topic(
&self,
topic_name: &str,
limit: u64,
offset: u64,
) -> Result<Vec<TopicRepoDto>, GitError> {
let topic = repo_topic::Entity::find()
.filter(repo_topic::Column::Name.eq(topic_name))
.one(self.db_read())
.await?
.ok_or(GitError::NotFound)?;
let relations = repo_relation_topic::Entity::find()
.filter(repo_relation_topic::Column::TopicId.eq(topic.id))
.limit(limit)
.offset(offset)
.all(self.db_read())
.await?;
if relations.is_empty() {
return Ok(Vec::new());
}
let repo_ids: Vec<Uuid> = relations.iter().map(|r| r.repo_id).collect();
let rows = repos::Entity::find()
.filter(repos::Column::Id.is_in(repo_ids))
.all(self.db_read())
.await?;
Ok(rows
.into_iter()
.map(|r| TopicRepoDto {
repo_id: r.id,
repo_name: r.name,
space_name: String::new(),
is_private: r.is_private,
})
.collect())
}
}