feat(api): expand API endpoints for repo, PR, user, workspace management
- Add git operation endpoints: archive, compare branches, diff, tree, repository extras - Add repo endpoints: contributors, delete fork, get branch/commit status/deploy key/invitation/member/release/tag/webhook, topics, release assets, webhook deliveries/retry - Add PR endpoints: review requests, templates - Add user endpoints: block/unblock, follow/unfollow, presence, personal access tokens, account restore - Add workspace endpoints: billing history, approvals, domains, integrations, invitations, members, webhooks, restore - Add internal API, notification API, IM API modules - Update route configuration and OpenAPI spec
This commit is contained in:
+10
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::issues::Issue;
|
||||
use crate::models::base_info::{self, UserBaseInfo};
|
||||
use crate::models::issues::IssueDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::issues::core::CreateIssueParams;
|
||||
use crate::session::Session;
|
||||
@@ -50,7 +51,7 @@ pub struct PathParams {
|
||||
content_type = "application/json"
|
||||
),
|
||||
responses(
|
||||
(status = 201, description = "Issue created successfully. Returns the newly created issue with full metadata.", body = ApiResponse<Issue>),
|
||||
(status = 201, description = "Issue created successfully. Returns the newly created issue with full metadata.", body = ApiResponse<IssueDetail>),
|
||||
(status = 400, description = "Invalid parameters: empty title, invalid repository/label/milestone references", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (requires Member role or higher)", body = ApiErrorResponse),
|
||||
@@ -71,5 +72,11 @@ pub async fn create(
|
||||
.issue
|
||||
.issue_create(&session, &path.workspace_name, params.into_inner())
|
||||
.await?;
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(issue)))
|
||||
let author_id = issue.author_id;
|
||||
let users = base_info::resolve_users(&service.ctx.db, &[author_id]).await?;
|
||||
let author = users
|
||||
.get(&author_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UserBaseInfo::placeholder(author_id));
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(issue.into_detail(author))))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::issues::IssueComment;
|
||||
use crate::models::base_info::{self, UserBaseInfo};
|
||||
use crate::models::issues::IssueCommentDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::issues::comments::CreateCommentParams;
|
||||
use crate::session::Session;
|
||||
@@ -38,7 +39,7 @@ pub struct PathParams {
|
||||
params(PathParams),
|
||||
request_body(content = CreateCommentParams, description = "Comment creation parameters", content_type = "application/json"),
|
||||
responses(
|
||||
(status = 201, description = "Comment created successfully.", body = ApiResponse<IssueComment>),
|
||||
(status = 201, description = "Comment created successfully.", body = ApiResponse<IssueCommentDetail>),
|
||||
(status = 400, description = "Invalid parameters: empty body or issue is locked", body = ApiErrorResponse),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions (issue locked and user lacks write access)", body = ApiErrorResponse),
|
||||
@@ -62,5 +63,11 @@ pub async fn create_comment(
|
||||
params.into_inner(),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(comment)))
|
||||
let author_id = comment.author_id;
|
||||
let users = base_info::resolve_users(&service.ctx.db, &[author_id]).await?;
|
||||
let author = users
|
||||
.get(&author_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UserBaseInfo::placeholder(author_id));
|
||||
Ok(HttpResponse::Created().json(ApiResponse::new(comment.into_detail(author))))
|
||||
}
|
||||
|
||||
+10
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::issues::Issue;
|
||||
use crate::models::base_info::{self, UserBaseInfo};
|
||||
use crate::models::issues::IssueDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -27,7 +28,7 @@ pub struct PathParams {
|
||||
operation_id = "issueGet",
|
||||
params(PathParams),
|
||||
responses(
|
||||
(status = 200, description = "Issue retrieved successfully. Returns complete issue with all metadata.", body = ApiResponse<Issue>),
|
||||
(status = 200, description = "Issue retrieved successfully. Returns complete issue with all metadata.", body = ApiResponse<IssueDetail>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to view this issue", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or issue not found", body = ApiErrorResponse),
|
||||
@@ -46,5 +47,11 @@ pub async fn get(
|
||||
.issue
|
||||
.issue_get(&session, &path.workspace_name, path.number)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(issue)))
|
||||
let author_id = issue.author_id;
|
||||
let users = base_info::resolve_users(&service.ctx.db, &[author_id]).await?;
|
||||
let author = users
|
||||
.get(&author_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UserBaseInfo::placeholder(author_id));
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(issue.into_detail(author))))
|
||||
}
|
||||
|
||||
+16
-3
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::issues::Issue;
|
||||
use crate::models::base_info::{self, UserBaseInfo};
|
||||
use crate::models::issues::IssueDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::service::issues::core::IssueListFilters;
|
||||
use crate::session::Session;
|
||||
@@ -48,7 +49,7 @@ pub struct QueryParams {
|
||||
operation_id = "issueList",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Issues listed successfully. Returns filtered array of issue objects with metadata.", body = ApiResponse<Vec<Issue>>),
|
||||
(status = 200, description = "Issues listed successfully. Returns filtered array of issue objects with metadata.", body = ApiResponse<Vec<IssueDetail>>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace not found", body = ApiErrorResponse),
|
||||
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||
@@ -81,5 +82,17 @@ pub async fn list(
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(issues)))
|
||||
let user_ids: Vec<_> = issues.iter().map(|i| i.author_id).collect();
|
||||
let users = base_info::resolve_users(&service.ctx.db, &user_ids).await?;
|
||||
let details: Vec<IssueDetail> = issues
|
||||
.into_iter()
|
||||
.map(|i| {
|
||||
let author = users
|
||||
.get(&i.author_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UserBaseInfo::placeholder(i.author_id));
|
||||
i.into_detail(author)
|
||||
})
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(details)))
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ use utoipa::IntoParams;
|
||||
|
||||
use crate::api::response::{ApiErrorResponse, ApiResponse};
|
||||
use crate::error::AppError;
|
||||
use crate::models::issues::IssueComment;
|
||||
use crate::models::base_info::{self, UserBaseInfo};
|
||||
use crate::models::issues::IssueCommentDetail;
|
||||
use crate::service::AppService;
|
||||
use crate::session::Session;
|
||||
|
||||
@@ -31,7 +32,7 @@ pub struct QueryParams {
|
||||
operation_id = "issueListComments",
|
||||
params(PathParams, QueryParams),
|
||||
responses(
|
||||
(status = 200, description = "Comments listed successfully.", body = ApiResponse<Vec<IssueComment>>),
|
||||
(status = 200, description = "Comments listed successfully.", body = ApiResponse<Vec<IssueCommentDetail>>),
|
||||
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||
(status = 403, description = "Insufficient permissions to view this issue", body = ApiErrorResponse),
|
||||
(status = 404, description = "Workspace or issue not found", body = ApiErrorResponse),
|
||||
@@ -55,5 +56,17 @@ pub async fn list_comments(
|
||||
query.offset.unwrap_or(0),
|
||||
)
|
||||
.await?;
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(comments)))
|
||||
let user_ids: Vec<_> = comments.iter().map(|c| c.author_id).collect();
|
||||
let users = base_info::resolve_users(&service.ctx.db, &user_ids).await?;
|
||||
let details: Vec<IssueCommentDetail> = comments
|
||||
.into_iter()
|
||||
.map(|c| {
|
||||
let author = users
|
||||
.get(&c.author_id)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| UserBaseInfo::placeholder(c.author_id));
|
||||
c.into_detail(author)
|
||||
})
|
||||
.collect();
|
||||
Ok(HttpResponse::Ok().json(ApiResponse::new(details)))
|
||||
}
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ use actix_web::web;
|
||||
|
||||
pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/issues")
|
||||
web::scope("")
|
||||
// Core
|
||||
.route("", web::get().to(list::list))
|
||||
.route("", web::post().to(create::create))
|
||||
@@ -144,7 +144,7 @@ pub fn configure(cfg: &mut web::ServiceConfig) {
|
||||
|
||||
pub fn configure_repo_level(cfg: &mut web::ServiceConfig) {
|
||||
cfg.service(
|
||||
web::scope("/issues")
|
||||
web::scope("")
|
||||
.route("/labels", web::get().to(list_labels::list_labels))
|
||||
.route("/labels", web::post().to(create_label::create_label))
|
||||
.route(
|
||||
|
||||
Reference in New Issue
Block a user