a06673b
use async_graphql::{
ComplexObject, Context, EmptyMutation, EmptySubscription, ID, Object, Schema, SimpleObject,
};
use serde_json::json;
use uuid::Uuid;
use crate::features::RepoService;
pub type RepoSchema = Schema<RootQuery, EmptyMutation, EmptySubscription>;
pub fn build_schema(svc: RepoService) -> RepoSchema {
let root = RootQuery(svc);
Schema::build(root.clone(), EmptyMutation, EmptySubscription)
.data(root)
.finish()
}
#[derive(Clone)]
pub struct RootQuery(pub RepoService);
mod dto {
use async_graphql::SimpleObject;
#[derive(SimpleObject, Clone)]
pub struct Permission {
pub admin: bool,
pub push: bool,
pub pull: bool,
}
#[derive(SimpleObject, Clone)]
pub struct InternalTracker {
pub enable_time_tracker: bool,
pub allow_only_contributors_to_track_time: bool,
pub enable_issue_dependencies: bool,
}
#[derive(SimpleObject, Clone)]
pub struct ExternalTracker {
pub external_tracker_url: String,
pub external_tracker_format: String,
pub external_tracker_style: String,
}
#[derive(SimpleObject, Clone)]
pub struct ExternalWiki {
pub external_wiki_url: String,
}
#[derive(SimpleObject, Clone)]
pub struct ChannelSummary {
pub id: String,
pub name: String,
pub display_name: Option<String>,
}
#[derive(SimpleObject, Clone)]
pub struct SpaceSummary {
pub id: String,
pub name: String,
pub display_name: Option<String>,
}
#[derive(SimpleObject, Clone)]
#[graphql(complex)]
pub struct RepoView {
pub id: String,
pub channel_id: String,
pub name: String,
pub lower_name: String,
pub description: Option<String>,
pub website: Option<String>,
pub default_branch: String,
pub enable_lfs: bool,
pub is_private: bool,
pub is_archived: bool,
pub is_fork: bool,
pub is_template: bool,
pub is_mirror: bool,
pub empty: bool,
pub size: i64,
pub git_size: i64,
pub lfs_size: i64,
pub nums_watch: i32,
pub nums_star: i32,
pub nums_pr: i32,
pub nums_commits: i32,
pub nums_branches: i32,
pub nums_tags: i32,
pub allow_rebase: bool,
pub allow_rebase_explicit: bool,
pub allow_squash_merge: bool,
pub allow_merge_commits: bool,
pub allow_fast_forward_only_merge: bool,
pub allow_manual_merge: bool,
pub autodetect_manual_merge: bool,
pub allow_merge_update: bool,
pub allow_rebase_update: bool,
pub has_code: bool,
pub has_issues: bool,
pub has_wiki: bool,
pub has_pull_requests: bool,
pub has_projects: bool,
pub has_releases: bool,
pub has_packages: bool,
pub has_actions: bool,
pub ignore_whitespace_conflicts: bool,
pub full_name: String,
pub html_url: String,
pub url: String,
pub link: String,
pub ssh_url: String,
pub clone_url: String,
pub original_url: String,
pub stars_count: i32,
pub default_target_branch: Option<String>,
pub archived_at: Option<String>,
pub updated_at: String,
pub mirror_interval: Option<String>,
pub created_at: String,
pub last_pushed_at: Option<String>,
pub storage_limit_size: Option<i64>,
pub avatar_url: Option<String>,
pub primary_language: Option<String>,
pub license_name: Option<String>,
pub object_format: String,
pub topics: Vec<String>,
pub licenses: Vec<String>,
pub channel: Option<ChannelSummary>,
pub space: Option<SpaceSummary>,
}
}
pub use dto::{
ChannelSummary, ExternalTracker, ExternalWiki, InternalTracker, Permission, RepoView,
SpaceSummary,
};
#[Object]
impl RootQuery {
async fn repo(
&self,
ctx: &Context<'_>,
channel_id: ID,
repo_id: ID,
) -> async_graphql::Result<RepoView> {
let _ = ctx;
let cid = parse_uuid(&channel_id)?;
let rid = parse_uuid(&repo_id)?;
let view = self
.0
.get_repo(actor_id(), cid, rid)
.await
.map_err(|e| async_graphql::Error::new(format!("{e:?}")))?;
Ok(to_gql_repo(view))
}
async fn repos(
&self,
_ctx: &Context<'_>,
q: Option<String>,
channel_id: Option<ID>,
limit: Option<i32>,
) -> async_graphql::Result<Vec<RepoView>> {
let q = crate::features::lifecycle::ListRepoQuery {
channel_id: channel_id.map(|x| parse_uuid(&x)).transpose()?,
owner_user_id: None,
is_private: None,
is_archived: None,
q,
limit: limit.map(|x| x as u64),
offset: None,
};
let page = self
.0
.list_repos(actor_id(), q)
.await
.map_err(|e| async_graphql::Error::new(format!("{e:?}")))?;
Ok(page.items.into_iter().map(to_gql_repo).collect())
}
}
#[ComplexObject]
impl RepoView {
async fn metadata(&self) -> String {
let _ = self;
json!({ "source": "lib/repo" }).to_string()
}
}
fn parse_uuid(id: &ID) -> async_graphql::Result<Uuid> {
Uuid::parse_str(id.as_str())
.map_err(|e| async_graphql::Error::new(format!("invalid uuid: {e}")))
}
fn actor_id() -> Uuid {
Uuid::nil()
}
fn to_gql_repo(view: crate::features::lifecycle::RepoView) -> RepoView {
let full_name = format!("channel/{}/{}", view.channel_id, view.name);
let name = view.name.clone();
RepoView {
id: view.id.to_string(),
channel_id: view.channel_id.to_string(),
name,
lower_name: view.lower_name,
description: view.description,
website: view.website.clone(),
default_branch: view.default_branch,
enable_lfs: view.enable_lfs,
is_private: view.is_private,
is_archived: view.is_archived,
is_fork: view.is_fork,
is_template: view.is_template,
is_mirror: view.is_mirror,
empty: false,
size: view.size,
git_size: view.git_size,
lfs_size: view.lfs_size,
nums_watch: view.nums_watch,
nums_star: view.nums_star,
nums_pr: view.nums_pr,
nums_commits: view.nums_commits,
nums_branches: view.nums_branches,
nums_tags: view.nums_tags,
allow_rebase: view.allow_rebase,
allow_rebase_explicit: view.allow_rebase_explicit,
allow_squash_merge: view.allow_squash_merge,
allow_merge_commits: view.allow_merge_commits,
allow_fast_forward_only_merge: false,
allow_manual_merge: false,
autodetect_manual_merge: false,
allow_merge_update: false,
allow_rebase_update: false,
has_code: true,
has_issues: false,
has_wiki: false,
has_pull_requests: false,
has_projects: false,
has_releases: false,
has_packages: false,
has_actions: false,
ignore_whitespace_conflicts: false,
full_name,
html_url: String::new(),
url: String::new(),
link: String::new(),
ssh_url: String::new(),
clone_url: String::new(),
original_url: String::new(),
stars_count: view.nums_star,
default_target_branch: None,
archived_at: view.archived_at,
updated_at: view
.last_pushed_at
.clone()
.unwrap_or_else(|| view.created_at.clone()),
mirror_interval: None,
created_at: view.created_at,
last_pushed_at: view.last_pushed_at,
storage_limit_size: view.storage_limit_size,
avatar_url: view.avatar_url,
primary_language: view.primary_language,
license_name: view.license_name.clone(),
object_format: view.object_format.clone(),
topics: Vec::new(),
licenses: view.license_name.into_iter().collect(),
channel: None,
space: None,
}
}