feat(api): add pull request and wiki API endpoints with OpenAPI generator
- Add gen_openapi binary for generating OpenAPI specification - Implement comprehensive pull request API endpoints including core operations - Add pull request reviews, check runs, labels, assignees, and events APIs - Include pull request status and merge strategy management endpoints - Add wiki page CRUD operations with revision history and comparison - Update OpenAPI documentation with Pull Requests and Wiki tags - Modify workspace find function visibility for external access - Integrate new API modules into main OpenAPI router configuration
This commit is contained in:
@@ -11,6 +11,10 @@ path = "lib.rs"
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "appks"
|
name = "appks"
|
||||||
path = "main.rs"
|
path = "main.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "gen_openapi"
|
||||||
|
path = "gen_openapi.rs"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
sqlx = { version = "0.9.0", features = ["postgres","runtime-tokio","chrono","uuid","json"] }
|
sqlx = { version = "0.9.0", features = ["postgres","runtime-tokio","chrono","uuid","json"] }
|
||||||
tokio = { version = "1.52.3", features = ["full"] }
|
tokio = { version = "1.52.3", features = ["full"] }
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
pub mod auth;
|
pub mod auth;
|
||||||
pub mod issue;
|
pub mod issue;
|
||||||
pub mod openapi;
|
pub mod openapi;
|
||||||
|
pub mod pr;
|
||||||
pub mod repo;
|
pub mod repo;
|
||||||
pub mod response;
|
pub mod response;
|
||||||
pub mod routes;
|
pub mod routes;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
pub mod wiki;
|
||||||
pub mod workspace;
|
pub mod workspace;
|
||||||
|
|||||||
+157
-14
@@ -7,6 +7,8 @@ use crate::api::auth::register::RegisterResponse;
|
|||||||
use crate::api::issue::lock::LockIssueParams;
|
use crate::api::issue::lock::LockIssueParams;
|
||||||
use crate::api::issue::subscribers::MuteIssueParams;
|
use crate::api::issue::subscribers::MuteIssueParams;
|
||||||
use crate::api::issue::transfer::TransferIssueParams;
|
use crate::api::issue::transfer::TransferIssueParams;
|
||||||
|
use crate::api::pr::lock::LockPrParams;
|
||||||
|
use crate::api::pr::subscriptions::MutePrParams;
|
||||||
use crate::api::repo::accept_invitation::AcceptInvitationParams;
|
use crate::api::repo::accept_invitation::AcceptInvitationParams;
|
||||||
use crate::api::repo::set_branch_protection::SetBranchProtectionParams;
|
use crate::api::repo::set_branch_protection::SetBranchProtectionParams;
|
||||||
use crate::api::repo::transfer_owner::TransferOwnerParams;
|
use crate::api::repo::transfer_owner::TransferOwnerParams;
|
||||||
@@ -18,6 +20,11 @@ use crate::models::issues::{
|
|||||||
Issue, IssueAssignee, IssueComment, IssueEvent, IssueLabel, IssueLabelRelation, IssueMilestone,
|
Issue, IssueAssignee, IssueComment, IssueEvent, IssueLabel, IssueLabelRelation, IssueMilestone,
|
||||||
IssuePrRelation, IssueReaction, IssueRepoRelation, IssueSubscriber, IssueTemplate,
|
IssuePrRelation, IssueReaction, IssueRepoRelation, IssueSubscriber, IssueTemplate,
|
||||||
};
|
};
|
||||||
|
use crate::models::prs::{
|
||||||
|
PrAssignee, PrCheckRun, PrCommit, PrEvent, PrFile, PrLabel, PrLabelRelation, PrMergeStrategy,
|
||||||
|
PrReaction, PrReview, PrReviewComment, PrStatus, PrSubscription, PullRequest,
|
||||||
|
};
|
||||||
|
use crate::models::wiki::{WikiPage, WikiPageRevision};
|
||||||
use crate::models::repos::{
|
use crate::models::repos::{
|
||||||
BranchProtectionRule, Repo, RepoBranch, RepoCommitComment, RepoCommitStatus, RepoDeployKey,
|
BranchProtectionRule, Repo, RepoBranch, RepoCommitComment, RepoCommitStatus, RepoDeployKey,
|
||||||
RepoFork, RepoInvitation, RepoMember, RepoRelease, RepoStar, RepoStats, RepoTag, RepoWatch,
|
RepoFork, RepoInvitation, RepoMember, RepoRelease, RepoStar, RepoStats, RepoTag, RepoWatch,
|
||||||
@@ -52,6 +59,17 @@ use crate::service::issues::pr_relations::LinkPrParams;
|
|||||||
use crate::service::issues::reactions::CreateIssueReactionParams;
|
use crate::service::issues::reactions::CreateIssueReactionParams;
|
||||||
use crate::service::issues::repo_relations::LinkRepoParams;
|
use crate::service::issues::repo_relations::LinkRepoParams;
|
||||||
use crate::service::issues::templates::{CreateTemplateParams, UpdateTemplateParams};
|
use crate::service::issues::templates::{CreateTemplateParams, UpdateTemplateParams};
|
||||||
|
use crate::service::pr::check_runs::{CreateCheckRunParams, UpdateCheckRunParams};
|
||||||
|
use crate::service::pr::core::{CreatePrParams, MergePrParams, PrListFilters, UpdatePrParams};
|
||||||
|
use crate::service::pr::labels::{CreatePrLabelParams, UpdatePrLabelParams};
|
||||||
|
use crate::service::pr::merge_strategy::UpdateMergeStrategyParams;
|
||||||
|
use crate::service::pr::reactions::CreateReactionParams;
|
||||||
|
use crate::service::pr::reviews::{
|
||||||
|
AddReplyParams, CreateReviewParams, DismissReviewParams, ReviewCommentParams,
|
||||||
|
SubmitReviewParams,
|
||||||
|
};
|
||||||
|
use crate::api::wiki::compare_revisions::WikiCompareResult;
|
||||||
|
use crate::service::wiki::core::{CreateWikiPageParams, UpdateWikiPageParams};
|
||||||
use crate::service::repo::branches::CreateBranchParams;
|
use crate::service::repo::branches::CreateBranchParams;
|
||||||
use crate::service::repo::commit_status::{CreateCommitCommentParams, CreateCommitStatusParams};
|
use crate::service::repo::commit_status::{CreateCommitCommentParams, CreateCommitStatusParams};
|
||||||
use crate::service::repo::core::{CreateRepoParams, UpdateRepoParams};
|
use crate::service::repo::core::{CreateRepoParams, UpdateRepoParams};
|
||||||
@@ -92,14 +110,16 @@ use crate::service::workspace::webhooks::{CreateWebhookParams, UpdateWebhookPara
|
|||||||
info(
|
info(
|
||||||
title = "AppKS API",
|
title = "AppKS API",
|
||||||
version = "0.1.0",
|
version = "0.1.0",
|
||||||
description = "AppKS collaborative development platform HTTP API. Auth endpoints use server-side sessions backed by Redis and a signed/encrypted session cookie. Sensitive password fields are RSA-OAEP-SHA256 encrypted per session before transmission."
|
description = "AppKS collaborative development platform HTTP API."
|
||||||
),
|
),
|
||||||
tags(
|
tags(
|
||||||
(name = "Auth", description = "Authentication, registration, session and email security endpoints."),
|
(name = "Auth", description = "Authentication, registration, session and email security endpoints."),
|
||||||
(name = "User", description = "User account management, profile, appearance, notification settings, SSH/GPG keys, sessions, devices, OAuth accounts, security logs, and personal access tokens."),
|
(name = "User", description = "User account management, profile, appearance, notification settings, SSH/GPG keys, sessions, devices, OAuth accounts, security logs, and personal access tokens."),
|
||||||
(name = "Workspaces", description = "Workspace CRUD, archiving, ownership transfer, and avatar management."),
|
(name = "Workspaces", description = "Workspace CRUD, archiving, ownership transfer, and avatar management."),
|
||||||
(name = "Repos", description = "Repository management including branches, tags, releases, forks, stars, watches, members, invitations, deploy keys, webhooks, protection rules, commit statuses, and statistics."),
|
(name = "Repos", description = "Repository management including branches, tags, releases, forks, stars, watches, members, invitations, deploy keys, webhooks, protection rules, commit statuses, and statistics."),
|
||||||
(name = "Issues", description = "Issue tracking, comments, labels, milestones, assignees, events, reactions, subscribers, templates, and cross-references with repos and pull requests."),
|
(name = "Issues", description = "Issue tracking, comments, labels, milestones, assignees, events, reactions, subscribers, templates, and cross-references."),
|
||||||
|
(name = "Pull Requests", description = "Pull request lifecycle including reviews, check runs, merge strategies, labels, assignees, events, reactions, and subscriptions."),
|
||||||
|
(name = "Wiki", description = "Wiki page management including CRUD operations, revision history, version comparison, and page reversion."),
|
||||||
),
|
),
|
||||||
paths(
|
paths(
|
||||||
// Auth
|
// Auth
|
||||||
@@ -146,7 +166,7 @@ use crate::service::workspace::webhooks::{CreateWebhookParams, UpdateWebhookPara
|
|||||||
crate::api::user::list_security_logs::list_security_logs,
|
crate::api::user::list_security_logs::list_security_logs,
|
||||||
crate::api::user::list_personal_access_tokens::list_tokens,
|
crate::api::user::list_personal_access_tokens::list_tokens,
|
||||||
crate::api::user::revoke_personal_access_token::revoke_token,
|
crate::api::user::revoke_personal_access_token::revoke_token,
|
||||||
// Issues - Core
|
// Issues
|
||||||
crate::api::issue::list::list,
|
crate::api::issue::list::list,
|
||||||
crate::api::issue::get::get,
|
crate::api::issue::get::get,
|
||||||
crate::api::issue::create::create,
|
crate::api::issue::create::create,
|
||||||
@@ -156,53 +176,107 @@ use crate::service::workspace::webhooks::{CreateWebhookParams, UpdateWebhookPara
|
|||||||
crate::api::issue::delete::delete,
|
crate::api::issue::delete::delete,
|
||||||
crate::api::issue::lock::lock,
|
crate::api::issue::lock::lock,
|
||||||
crate::api::issue::transfer::transfer,
|
crate::api::issue::transfer::transfer,
|
||||||
// Issues - Comments
|
|
||||||
crate::api::issue::list_comments::list_comments,
|
crate::api::issue::list_comments::list_comments,
|
||||||
crate::api::issue::create_comment::create_comment,
|
crate::api::issue::create_comment::create_comment,
|
||||||
crate::api::issue::update_comment::update_comment,
|
crate::api::issue::update_comment::update_comment,
|
||||||
crate::api::issue::delete_comment::delete_comment,
|
crate::api::issue::delete_comment::delete_comment,
|
||||||
// Issues - Labels (repo-level)
|
|
||||||
crate::api::issue::list_labels::list_labels,
|
crate::api::issue::list_labels::list_labels,
|
||||||
crate::api::issue::create_label::create_label,
|
crate::api::issue::create_label::create_label,
|
||||||
crate::api::issue::update_label::update_label,
|
crate::api::issue::update_label::update_label,
|
||||||
crate::api::issue::delete_label::delete_label,
|
crate::api::issue::delete_label::delete_label,
|
||||||
// Issues - Label relations (issue-level)
|
|
||||||
crate::api::issue::list_issue_labels::list_issue_labels,
|
crate::api::issue::list_issue_labels::list_issue_labels,
|
||||||
crate::api::issue::assign_label::assign_label,
|
crate::api::issue::assign_label::assign_label,
|
||||||
crate::api::issue::unassign_label::unassign_label,
|
crate::api::issue::unassign_label::unassign_label,
|
||||||
// Issues - Milestones (repo-level)
|
|
||||||
crate::api::issue::list_milestones::list_milestones,
|
crate::api::issue::list_milestones::list_milestones,
|
||||||
crate::api::issue::create_milestone::create_milestone,
|
crate::api::issue::create_milestone::create_milestone,
|
||||||
crate::api::issue::update_milestone::update_milestone,
|
crate::api::issue::update_milestone::update_milestone,
|
||||||
crate::api::issue::delete_milestone::delete_milestone,
|
crate::api::issue::delete_milestone::delete_milestone,
|
||||||
// Issues - Assignees
|
|
||||||
crate::api::issue::list_assignees::list_assignees,
|
crate::api::issue::list_assignees::list_assignees,
|
||||||
crate::api::issue::assign_issue::assign_issue,
|
crate::api::issue::assign_issue::assign_issue,
|
||||||
crate::api::issue::unassign_issue::unassign_issue,
|
crate::api::issue::unassign_issue::unassign_issue,
|
||||||
// Issues - Events
|
|
||||||
crate::api::issue::list_events::list_events,
|
crate::api::issue::list_events::list_events,
|
||||||
// Issues - Reactions
|
|
||||||
crate::api::issue::reactions::list_reactions,
|
crate::api::issue::reactions::list_reactions,
|
||||||
crate::api::issue::reactions::add_reaction,
|
crate::api::issue::reactions::add_reaction,
|
||||||
crate::api::issue::reactions::remove_reaction,
|
crate::api::issue::reactions::remove_reaction,
|
||||||
// Issues - Subscribers
|
|
||||||
crate::api::issue::subscribers::list_subscribers,
|
crate::api::issue::subscribers::list_subscribers,
|
||||||
crate::api::issue::subscribers::subscribe,
|
crate::api::issue::subscribers::subscribe,
|
||||||
crate::api::issue::subscribers::unsubscribe,
|
crate::api::issue::subscribers::unsubscribe,
|
||||||
crate::api::issue::subscribers::mute,
|
crate::api::issue::subscribers::mute,
|
||||||
// Issues - Templates (repo-level)
|
|
||||||
crate::api::issue::templates::list_templates,
|
crate::api::issue::templates::list_templates,
|
||||||
crate::api::issue::templates::create_template,
|
crate::api::issue::templates::create_template,
|
||||||
crate::api::issue::templates::update_template,
|
crate::api::issue::templates::update_template,
|
||||||
crate::api::issue::templates::delete_template,
|
crate::api::issue::templates::delete_template,
|
||||||
// Issues - Repo relations
|
|
||||||
crate::api::issue::repo_relations::list_repo_relations,
|
crate::api::issue::repo_relations::list_repo_relations,
|
||||||
crate::api::issue::repo_relations::link_repo,
|
crate::api::issue::repo_relations::link_repo,
|
||||||
crate::api::issue::repo_relations::unlink_repo,
|
crate::api::issue::repo_relations::unlink_repo,
|
||||||
// Issues - PR relations
|
|
||||||
crate::api::issue::pr_relations::list_pr_relations,
|
crate::api::issue::pr_relations::list_pr_relations,
|
||||||
crate::api::issue::pr_relations::link_pr,
|
crate::api::issue::pr_relations::link_pr,
|
||||||
crate::api::issue::pr_relations::unlink_pr,
|
crate::api::issue::pr_relations::unlink_pr,
|
||||||
|
// Pull Requests - Core
|
||||||
|
crate::api::pr::list::list,
|
||||||
|
crate::api::pr::get::get,
|
||||||
|
crate::api::pr::create::create,
|
||||||
|
crate::api::pr::update::update,
|
||||||
|
crate::api::pr::mark_ready::mark_ready,
|
||||||
|
crate::api::pr::close::close,
|
||||||
|
crate::api::pr::reopen::reopen,
|
||||||
|
crate::api::pr::delete::delete,
|
||||||
|
crate::api::pr::lock::lock,
|
||||||
|
crate::api::pr::merge::merge,
|
||||||
|
// Pull Requests - Commits & Files
|
||||||
|
crate::api::pr::list_commits::list_commits,
|
||||||
|
crate::api::pr::list_files::list_files,
|
||||||
|
// Pull Requests - Status & Merge Strategy
|
||||||
|
crate::api::pr::get_status::get_status,
|
||||||
|
crate::api::pr::merge_strategy::get_merge_strategy,
|
||||||
|
crate::api::pr::merge_strategy::update_merge_strategy,
|
||||||
|
// Pull Requests - Labels
|
||||||
|
crate::api::pr::labels::list_labels,
|
||||||
|
crate::api::pr::labels::create_label,
|
||||||
|
crate::api::pr::labels::update_label,
|
||||||
|
crate::api::pr::labels::delete_label,
|
||||||
|
crate::api::pr::labels::list_label_relations,
|
||||||
|
crate::api::pr::labels::assign_label,
|
||||||
|
crate::api::pr::labels::unassign_label,
|
||||||
|
// Pull Requests - Assignees
|
||||||
|
crate::api::pr::assignees::list_assignees,
|
||||||
|
crate::api::pr::assignees::assign_user,
|
||||||
|
crate::api::pr::assignees::unassign_user,
|
||||||
|
// Pull Requests - Reviews
|
||||||
|
crate::api::pr::reviews::list_reviews,
|
||||||
|
crate::api::pr::reviews::create_review,
|
||||||
|
crate::api::pr::reviews::submit_review,
|
||||||
|
crate::api::pr::reviews::dismiss_review,
|
||||||
|
crate::api::pr::reviews::list_review_comments,
|
||||||
|
crate::api::pr::reviews::add_review_reply,
|
||||||
|
crate::api::pr::reviews::update_review_comment,
|
||||||
|
crate::api::pr::reviews::delete_review_comment,
|
||||||
|
// Pull Requests - Check Runs
|
||||||
|
crate::api::pr::check_runs::list_check_runs,
|
||||||
|
crate::api::pr::check_runs::create_check_run,
|
||||||
|
crate::api::pr::check_runs::update_check_run,
|
||||||
|
crate::api::pr::check_runs::delete_check_run,
|
||||||
|
// Pull Requests - Events
|
||||||
|
crate::api::pr::events::list_events,
|
||||||
|
// Pull Requests - Reactions
|
||||||
|
crate::api::pr::reactions::list_reactions,
|
||||||
|
crate::api::pr::reactions::add_reaction,
|
||||||
|
crate::api::pr::reactions::remove_reaction,
|
||||||
|
// Pull Requests - Subscriptions
|
||||||
|
crate::api::pr::subscriptions::list_subscriptions,
|
||||||
|
crate::api::pr::subscriptions::subscribe,
|
||||||
|
crate::api::pr::subscriptions::unsubscribe,
|
||||||
|
crate::api::pr::subscriptions::mute,
|
||||||
|
// Wiki
|
||||||
|
crate::api::wiki::list_pages::list_pages,
|
||||||
|
crate::api::wiki::get_page::get_page,
|
||||||
|
crate::api::wiki::create_page::create_page,
|
||||||
|
crate::api::wiki::update_page::update_page,
|
||||||
|
crate::api::wiki::delete_page::delete_page,
|
||||||
|
crate::api::wiki::revert_page::revert_page,
|
||||||
|
crate::api::wiki::list_revisions::list_revisions,
|
||||||
|
crate::api::wiki::get_revision::get_revision,
|
||||||
|
crate::api::wiki::compare_revisions::compare_revisions,
|
||||||
// Workspaces
|
// Workspaces
|
||||||
crate::api::workspace::list::handle,
|
crate::api::workspace::list::handle,
|
||||||
crate::api::workspace::get::handle,
|
crate::api::workspace::get::handle,
|
||||||
@@ -434,6 +508,75 @@ use crate::service::workspace::webhooks::{CreateWebhookParams, UpdateWebhookPara
|
|||||||
ApiResponse<Vec<IssuePrRelation>>,
|
ApiResponse<Vec<IssuePrRelation>>,
|
||||||
IssuePrRelation,
|
IssuePrRelation,
|
||||||
LinkPrParams,
|
LinkPrParams,
|
||||||
|
// Pull Requests
|
||||||
|
ApiResponse<PullRequest>,
|
||||||
|
ApiResponse<Vec<PullRequest>>,
|
||||||
|
PullRequest,
|
||||||
|
CreatePrParams,
|
||||||
|
UpdatePrParams,
|
||||||
|
PrListFilters,
|
||||||
|
MergePrParams,
|
||||||
|
LockPrParams,
|
||||||
|
ApiResponse<PrCommit>,
|
||||||
|
ApiResponse<Vec<PrCommit>>,
|
||||||
|
PrCommit,
|
||||||
|
ApiResponse<PrFile>,
|
||||||
|
ApiResponse<Vec<PrFile>>,
|
||||||
|
PrFile,
|
||||||
|
ApiResponse<PrStatus>,
|
||||||
|
PrStatus,
|
||||||
|
ApiResponse<PrMergeStrategy>,
|
||||||
|
PrMergeStrategy,
|
||||||
|
UpdateMergeStrategyParams,
|
||||||
|
ApiResponse<PrLabel>,
|
||||||
|
ApiResponse<Vec<PrLabel>>,
|
||||||
|
PrLabel,
|
||||||
|
CreatePrLabelParams,
|
||||||
|
UpdatePrLabelParams,
|
||||||
|
ApiResponse<PrLabelRelation>,
|
||||||
|
ApiResponse<Vec<PrLabelRelation>>,
|
||||||
|
PrLabelRelation,
|
||||||
|
ApiResponse<PrAssignee>,
|
||||||
|
ApiResponse<Vec<PrAssignee>>,
|
||||||
|
PrAssignee,
|
||||||
|
ApiResponse<PrReview>,
|
||||||
|
ApiResponse<Vec<PrReview>>,
|
||||||
|
PrReview,
|
||||||
|
CreateReviewParams,
|
||||||
|
ReviewCommentParams,
|
||||||
|
SubmitReviewParams,
|
||||||
|
DismissReviewParams,
|
||||||
|
AddReplyParams,
|
||||||
|
ApiResponse<PrReviewComment>,
|
||||||
|
ApiResponse<Vec<PrReviewComment>>,
|
||||||
|
PrReviewComment,
|
||||||
|
ApiResponse<PrCheckRun>,
|
||||||
|
ApiResponse<Vec<PrCheckRun>>,
|
||||||
|
PrCheckRun,
|
||||||
|
CreateCheckRunParams,
|
||||||
|
UpdateCheckRunParams,
|
||||||
|
ApiResponse<PrEvent>,
|
||||||
|
ApiResponse<Vec<PrEvent>>,
|
||||||
|
PrEvent,
|
||||||
|
ApiResponse<PrReaction>,
|
||||||
|
ApiResponse<Vec<PrReaction>>,
|
||||||
|
PrReaction,
|
||||||
|
CreateReactionParams,
|
||||||
|
ApiResponse<PrSubscription>,
|
||||||
|
ApiResponse<Vec<PrSubscription>>,
|
||||||
|
PrSubscription,
|
||||||
|
MutePrParams,
|
||||||
|
// Wiki
|
||||||
|
ApiResponse<WikiPage>,
|
||||||
|
ApiResponse<Vec<WikiPage>>,
|
||||||
|
WikiPage,
|
||||||
|
CreateWikiPageParams,
|
||||||
|
UpdateWikiPageParams,
|
||||||
|
ApiResponse<WikiPageRevision>,
|
||||||
|
ApiResponse<Vec<WikiPageRevision>>,
|
||||||
|
ApiResponse<WikiCompareResult>,
|
||||||
|
WikiCompareResult,
|
||||||
|
WikiPageRevision,
|
||||||
// Workspaces
|
// Workspaces
|
||||||
ApiResponse<Workspace>,
|
ApiResponse<Workspace>,
|
||||||
ApiResponse<Vec<Workspace>>,
|
ApiResponse<Vec<Workspace>>,
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Close a pull request
|
||||||
|
///
|
||||||
|
/// Closes an open PR without merging.
|
||||||
|
/// Requires write access to the PR.
|
||||||
|
///
|
||||||
|
/// Returns the closed PR.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/close",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prClose",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR closed successfully.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 400, description = "PR is not open", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn close(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_close(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::pr::core::CreatePrParams;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a pull request
|
||||||
|
///
|
||||||
|
/// Creates a new pull request proposing changes from a source branch to a target branch.
|
||||||
|
/// Requires at least Member role in the repository.
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - title: PR title (required)
|
||||||
|
/// - body: PR description in markdown (optional)
|
||||||
|
/// - source_repo_id: Source repository ID (supports cross-repo PRs from forks)
|
||||||
|
/// - source_branch: Source branch name (must exist)
|
||||||
|
/// - target_branch: Target branch name (must exist in the repo)
|
||||||
|
/// - head_commit_sha: Head commit SHA from the source branch
|
||||||
|
/// - base_commit_sha: Base commit SHA for diff calculation (optional)
|
||||||
|
/// - draft: Whether this is a draft PR (optional, defaults to false)
|
||||||
|
///
|
||||||
|
/// Effects:
|
||||||
|
/// - PR is created with auto-incrementing number
|
||||||
|
/// - PR status tracking is initialized
|
||||||
|
/// - Author is automatically subscribed
|
||||||
|
/// - Repository stats are updated
|
||||||
|
///
|
||||||
|
/// Returns the created PR with full metadata.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prCreate",
|
||||||
|
params(PathParams),
|
||||||
|
request_body(
|
||||||
|
content = CreatePrParams,
|
||||||
|
description = "PR creation parameters",
|
||||||
|
content_type = "application/json"
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 201, description = "PR created successfully. Returns the newly created PR with full metadata.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 400, description = "Invalid parameters: empty title, non-existent branch/commit, or invalid fork relationship", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions (requires Member role or higher)", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "Repository or workspace not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn create(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
params: web::Json<CreatePrParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_create(&session, &path.workspace_name, &path.repo_name, params.into_inner())
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Created().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Delete a pull request
|
||||||
|
///
|
||||||
|
/// Soft-deletes a PR. Requires Admin role in the repository.
|
||||||
|
///
|
||||||
|
/// Returns success message on completion.
|
||||||
|
#[utoipa::path(
|
||||||
|
delete,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prDelete",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR deleted successfully.", body = ApiResponse<String>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions (requires Admin role)", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn delete(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
service
|
||||||
|
.pr
|
||||||
|
.pr_delete(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new("PR deleted".to_string())))
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a pull request by number
|
||||||
|
///
|
||||||
|
/// Returns detailed information about a specific pull request.
|
||||||
|
/// Requires read access to the repository.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prGet",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR retrieved successfully. Returns complete PR with all metadata.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions to access this repository", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "Repository, workspace, or PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn get(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_get(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PrStatus;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get PR status summary
|
||||||
|
///
|
||||||
|
/// Returns the current status of a PR including checks state, mergeability,
|
||||||
|
/// approval count, and file change statistics.
|
||||||
|
/// Requires read access to the repository.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/status",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prGetStatus",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR status retrieved successfully.", body = ApiResponse<PrStatus>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR or status not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(("session_cookie" = []))
|
||||||
|
)]
|
||||||
|
pub async fn get_status(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let status = service
|
||||||
|
.pr
|
||||||
|
.pr_status(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(status)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::pr::core::PrListFilters;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct QueryParams {
|
||||||
|
/// Filter by PR state ("open", "closed", "merged")
|
||||||
|
pub state: Option<String>,
|
||||||
|
/// Filter by author user ID
|
||||||
|
pub author_id: Option<uuid::Uuid>,
|
||||||
|
/// Filter by draft status
|
||||||
|
pub draft: Option<bool>,
|
||||||
|
/// Maximum number of PRs to return (default: 50, max: 100)
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
/// Number of PRs to skip for pagination (default: 0)
|
||||||
|
pub offset: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List pull requests in a repository
|
||||||
|
///
|
||||||
|
/// Returns a paginated list of pull requests, sorted by number (newest first).
|
||||||
|
/// Supports filtering by state, author, and draft status.
|
||||||
|
/// Requires read access to the repository.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prList",
|
||||||
|
params(PathParams, QueryParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PRs listed successfully. Returns filtered array of PR objects.", body = ApiResponse<Vec<PullRequest>>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions to access this repository", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "Repository or workspace not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn list(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
query: web::Query<QueryParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let filters = PrListFilters {
|
||||||
|
state: query.state.clone(),
|
||||||
|
author_id: query.author_id,
|
||||||
|
draft: query.draft,
|
||||||
|
};
|
||||||
|
let prs = service
|
||||||
|
.pr
|
||||||
|
.pr_list(
|
||||||
|
&session,
|
||||||
|
&path.workspace_name,
|
||||||
|
&path.repo_name,
|
||||||
|
filters,
|
||||||
|
query.limit.unwrap_or(50),
|
||||||
|
query.offset.unwrap_or(0),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(prs)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PrCommit;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct QueryParams {
|
||||||
|
/// Maximum number of commits to return (default: 50, max: 100)
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
/// Number of commits to skip for pagination (default: 0)
|
||||||
|
pub offset: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List commits in a pull request
|
||||||
|
///
|
||||||
|
/// Returns a paginated list of all commits included in the PR, sorted by position.
|
||||||
|
/// Requires read access to the repository.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/commits",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prListCommits",
|
||||||
|
params(PathParams, QueryParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Commits listed successfully.", body = ApiResponse<Vec<PrCommit>>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(("session_cookie" = []))
|
||||||
|
)]
|
||||||
|
pub async fn list_commits(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
query: web::Query<QueryParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let commits = service
|
||||||
|
.pr
|
||||||
|
.pr_commits(&session, &path.workspace_name, &path.repo_name, path.number, query.limit.unwrap_or(50), query.offset.unwrap_or(0))
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(commits)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PrFile;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct QueryParams {
|
||||||
|
/// Maximum number of files to return (default: 50, max: 100)
|
||||||
|
pub limit: Option<i64>,
|
||||||
|
/// Number of files to skip for pagination (default: 0)
|
||||||
|
pub offset: Option<i64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List changed files in a pull request
|
||||||
|
///
|
||||||
|
/// Returns a paginated list of all files changed in the PR, sorted by path.
|
||||||
|
/// Includes additions, deletions, and patch diffs for each file.
|
||||||
|
/// Requires read access to the repository.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/files",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prListFiles",
|
||||||
|
params(PathParams, QueryParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Files listed successfully.", body = ApiResponse<Vec<PrFile>>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(("session_cookie" = []))
|
||||||
|
)]
|
||||||
|
pub async fn list_files(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
query: web::Query<QueryParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let files = service
|
||||||
|
.pr
|
||||||
|
.pr_files(&session, &path.workspace_name, &path.repo_name, path.number, query.limit.unwrap_or(50), query.offset.unwrap_or(0))
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(files)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, utoipa::ToSchema)]
|
||||||
|
pub struct LockPrParams {
|
||||||
|
/// Whether to lock (true) or unlock (false) the PR conversation
|
||||||
|
pub locked: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lock or unlock a pull request conversation
|
||||||
|
///
|
||||||
|
/// When locked, only repository maintainers and admins can comment on the PR.
|
||||||
|
/// Requires write access to the PR.
|
||||||
|
///
|
||||||
|
/// Returns the updated PR.
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/lock",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prLock",
|
||||||
|
params(PathParams),
|
||||||
|
request_body(
|
||||||
|
content = LockPrParams,
|
||||||
|
description = "Lock/unlock parameters",
|
||||||
|
content_type = "application/json"
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR lock status updated.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn lock(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
params: web::Json<LockPrParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_lock(&session, &path.workspace_name, &path.repo_name, path.number, params.locked)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mark a draft PR as ready for review
|
||||||
|
///
|
||||||
|
/// Converts a draft pull request to a ready-for-review state.
|
||||||
|
/// Requires write access to the PR.
|
||||||
|
///
|
||||||
|
/// Effects:
|
||||||
|
/// - Draft flag is set to false
|
||||||
|
/// - A "DraftReady" event is logged
|
||||||
|
/// - Reviewers are notified that the PR is ready
|
||||||
|
///
|
||||||
|
/// Returns the updated PR.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/ready",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prMarkReady",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR marked as ready for review.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 400, description = "PR is already ready for review", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn mark_ready(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_mark_ready(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::pr::core::MergePrParams;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Merge a pull request
|
||||||
|
///
|
||||||
|
/// Merges the source branch into the target branch.
|
||||||
|
/// Requires at least Maintainer role in the repository.
|
||||||
|
///
|
||||||
|
/// Branch protection rules are enforced:
|
||||||
|
/// - Required approvals count (self-approval not allowed)
|
||||||
|
/// - Required status checks must pass
|
||||||
|
/// - Admins can bypass protection rules
|
||||||
|
///
|
||||||
|
/// Parameters:
|
||||||
|
/// - strategy: Merge strategy ("merge", "squash", "rebase", default: "merge")
|
||||||
|
/// - squash_title: Custom title for squash merge (optional)
|
||||||
|
/// - squash_message: Custom message for squash merge (optional)
|
||||||
|
/// - delete_source_branch: Delete source branch after merge (optional, only same-repo)
|
||||||
|
///
|
||||||
|
/// Returns the merged PR with merge commit SHA.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/merge",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prMerge",
|
||||||
|
params(PathParams),
|
||||||
|
request_body(
|
||||||
|
content = MergePrParams,
|
||||||
|
description = "Merge parameters",
|
||||||
|
content_type = "application/json"
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR merged successfully. Returns the merged PR with merge commit SHA.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 400, description = "Cannot merge: PR not open, is draft, or branch protection requirements not met", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions (requires Maintainer role or higher)", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error or git merge failure", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn merge(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
params: web::Json<MergePrParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_merge(&session, &path.workspace_name, &path.repo_name, path.number, params.into_inner())
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PrMergeStrategy;
|
||||||
|
use crate::service::pr::merge_strategy::UpdateMergeStrategyParams;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get PR merge strategy
|
||||||
|
///
|
||||||
|
/// Returns the current merge strategy settings for a PR.
|
||||||
|
/// Requires read access to the repository.
|
||||||
|
#[utoipa::path(
|
||||||
|
get,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/merge-strategy",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prGetMergeStrategy",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Merge strategy retrieved successfully.", body = ApiResponse<PrMergeStrategy>),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR or merge strategy not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(("session_cookie" = []))
|
||||||
|
)]
|
||||||
|
pub async fn get_merge_strategy(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let strategy = service
|
||||||
|
.pr
|
||||||
|
.pr_merge_strategy(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(strategy)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update PR merge strategy
|
||||||
|
///
|
||||||
|
/// Updates the merge strategy settings for a PR.
|
||||||
|
/// Requires write access to the PR.
|
||||||
|
///
|
||||||
|
/// Updatable fields:
|
||||||
|
/// - strategy: Merge strategy type ("merge", "squash", "rebase")
|
||||||
|
/// - auto_merge: Enable auto-merge when all checks pass
|
||||||
|
/// - squash_title: Custom title for squash merge
|
||||||
|
/// - squash_message: Custom message for squash merge
|
||||||
|
/// - delete_source_branch: Delete source branch after merge
|
||||||
|
/// - merge_when_checks_pass: Auto-merge when checks pass
|
||||||
|
///
|
||||||
|
/// All fields are optional; only provided fields are updated.
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/merge-strategy",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prUpdateMergeStrategy",
|
||||||
|
params(PathParams),
|
||||||
|
request_body(
|
||||||
|
content = UpdateMergeStrategyParams,
|
||||||
|
description = "Merge strategy update parameters (all fields optional)",
|
||||||
|
content_type = "application/json"
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "Merge strategy updated successfully.", body = ApiResponse<PrMergeStrategy>),
|
||||||
|
(status = 400, description = "Invalid parameters: unsupported strategy", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(("session_cookie" = []))
|
||||||
|
)]
|
||||||
|
pub async fn update_merge_strategy(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
params: web::Json<UpdateMergeStrategyParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let strategy = service
|
||||||
|
.pr
|
||||||
|
.pr_update_merge_strategy(&session, &path.workspace_name, &path.repo_name, path.number, params.into_inner())
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(strategy)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reopen a pull request
|
||||||
|
///
|
||||||
|
/// Reopens a closed (but not merged) PR.
|
||||||
|
/// Requires write access to the PR.
|
||||||
|
///
|
||||||
|
/// Returns the reopened PR.
|
||||||
|
#[utoipa::path(
|
||||||
|
post,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}/reopen",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prReopen",
|
||||||
|
params(PathParams),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR reopened successfully.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 400, description = "PR is not closed or already merged", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn reopen(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_reopen(&session, &path.workspace_name, &path.repo_name, path.number)
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use actix_web::{web, HttpResponse};
|
||||||
|
use serde::Deserialize;
|
||||||
|
use utoipa::IntoParams;
|
||||||
|
|
||||||
|
use crate::api::response::{ApiResponse, ApiErrorResponse};
|
||||||
|
use crate::error::AppError;
|
||||||
|
use crate::models::prs::PullRequest;
|
||||||
|
use crate::service::pr::core::UpdatePrParams;
|
||||||
|
use crate::service::AppService;
|
||||||
|
use crate::session::Session;
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, IntoParams)]
|
||||||
|
pub struct PathParams {
|
||||||
|
/// Workspace name (unique identifier)
|
||||||
|
pub workspace_name: String,
|
||||||
|
/// Repository name (unique within the workspace)
|
||||||
|
pub repo_name: String,
|
||||||
|
/// PR number (unique within the repository)
|
||||||
|
pub number: i64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a pull request
|
||||||
|
///
|
||||||
|
/// Updates an existing pull request's metadata such as title, body, target branch, and draft status.
|
||||||
|
/// Requires write access to the PR (author or repository member).
|
||||||
|
///
|
||||||
|
/// All fields are optional; only provided fields are updated.
|
||||||
|
/// Returns the updated PR with full metadata.
|
||||||
|
#[utoipa::path(
|
||||||
|
put,
|
||||||
|
path = "/api/v1/workspaces/{workspace_name}/repos/{repo_name}/prs/{number}",
|
||||||
|
tag = "Pull Requests",
|
||||||
|
operation_id = "prUpdate",
|
||||||
|
params(PathParams),
|
||||||
|
request_body(
|
||||||
|
content = UpdatePrParams,
|
||||||
|
description = "PR update parameters (all fields optional)",
|
||||||
|
content_type = "application/json"
|
||||||
|
),
|
||||||
|
responses(
|
||||||
|
(status = 200, description = "PR updated successfully. Returns the updated PR with full metadata.", body = ApiResponse<PullRequest>),
|
||||||
|
(status = 400, description = "Invalid parameters", body = ApiErrorResponse),
|
||||||
|
(status = 401, description = "Authentication required or session expired", body = ApiErrorResponse),
|
||||||
|
(status = 403, description = "Insufficient permissions to edit this PR", body = ApiErrorResponse),
|
||||||
|
(status = 404, description = "Repository, workspace, or PR not found", body = ApiErrorResponse),
|
||||||
|
(status = 500, description = "Internal server error", body = ApiErrorResponse),
|
||||||
|
),
|
||||||
|
security(
|
||||||
|
("session_cookie" = [])
|
||||||
|
)
|
||||||
|
)]
|
||||||
|
pub async fn update(
|
||||||
|
service: web::Data<AppService>,
|
||||||
|
session: Session,
|
||||||
|
path: web::Path<PathParams>,
|
||||||
|
params: web::Json<UpdatePrParams>,
|
||||||
|
) -> Result<HttpResponse, AppError> {
|
||||||
|
let pr = service
|
||||||
|
.pr
|
||||||
|
.pr_update(&session, &path.workspace_name, &path.repo_name, path.number, params.into_inner())
|
||||||
|
.await?;
|
||||||
|
Ok(HttpResponse::Ok().json(ApiResponse::new(pr)))
|
||||||
|
}
|
||||||
+8
-1
@@ -3,8 +3,10 @@ use actix_web::web::scope;
|
|||||||
|
|
||||||
use crate::api::auth;
|
use crate::api::auth;
|
||||||
use crate::api::issue;
|
use crate::api::issue;
|
||||||
|
use crate::api::pr;
|
||||||
use crate::api::repo;
|
use crate::api::repo;
|
||||||
use crate::api::user;
|
use crate::api::user;
|
||||||
|
use crate::api::wiki;
|
||||||
use crate::api::workspace;
|
use crate::api::workspace;
|
||||||
|
|
||||||
pub fn init_routes(cfg: &mut web::ServiceConfig) {
|
pub fn init_routes(cfg: &mut web::ServiceConfig) {
|
||||||
@@ -17,7 +19,12 @@ pub fn init_routes(cfg: &mut web::ServiceConfig) {
|
|||||||
.service(
|
.service(
|
||||||
scope("/workspaces/{workspace_name}")
|
scope("/workspaces/{workspace_name}")
|
||||||
.configure(issue::configure)
|
.configure(issue::configure)
|
||||||
.service(scope("/repos/{repo_name}").configure(issue::configure_repo_level)),
|
.service(
|
||||||
|
scope("/repos/{repo_name}")
|
||||||
|
.configure(issue::configure_repo_level)
|
||||||
|
.configure(pr::configure)
|
||||||
|
.configure(wiki::configure),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
use utoipa::OpenApi;
|
||||||
|
use appks::api::openapi::OpenApiDoc;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let json = OpenApiDoc::openapi()
|
||||||
|
.to_pretty_json();
|
||||||
|
if let Ok(json) = json {
|
||||||
|
if let Err(e) = std::fs::write("openapi.json", json) {
|
||||||
|
eprintln!("{}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrAssignee {
|
pub struct PrAssignee {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrCheckRun {
|
pub struct PrCheckRun {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrCommit {
|
pub struct PrCommit {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrEvent {
|
pub struct PrEvent {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrFile {
|
pub struct PrFile {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrLabelRelation {
|
pub struct PrLabelRelation {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrLabel {
|
pub struct PrLabel {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub repo_id: Uuid,
|
pub repo_id: Uuid,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrMergeStrategy {
|
pub struct PrMergeStrategy {
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
pub strategy: MergeStrategyKind,
|
pub strategy: MergeStrategyKind,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrReaction {
|
pub struct PrReaction {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrReview {
|
pub struct PrReview {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrReviewComment {
|
pub struct PrReviewComment {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub review_id: Uuid,
|
pub review_id: Uuid,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrStatus {
|
pub struct PrStatus {
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
pub head_commit_sha: String,
|
pub head_commit_sha: String,
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PrSubscription {
|
pub struct PrSubscription {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub pull_request_id: Uuid,
|
pub pull_request_id: Uuid,
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use chrono::{DateTime, Utc};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, sqlx::FromRow, utoipa::ToSchema)]
|
||||||
pub struct PullRequest {
|
pub struct PullRequest {
|
||||||
pub id: Uuid,
|
pub id: Uuid,
|
||||||
pub repo_id: Uuid,
|
pub repo_id: Uuid,
|
||||||
|
|||||||
@@ -500,7 +500,7 @@ impl WorkspaceService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn find_workspace_by_id(
|
pub async fn find_workspace_by_id(
|
||||||
&self,
|
&self,
|
||||||
workspace_id: Uuid,
|
workspace_id: Uuid,
|
||||||
) -> Result<Workspace, AppError> {
|
) -> Result<Workspace, AppError> {
|
||||||
|
|||||||
Reference in New Issue
Block a user