diff --git a/Gemfile b/Gemfile index dd8885df7..c5f4f2ab6 100644 --- a/Gemfile +++ b/Gemfile @@ -135,4 +135,4 @@ gem 'doorkeeper' gem 'doorkeeper-jwt' -gem 'gitea-client', '~> 0.10.4' \ No newline at end of file +gem 'gitea-client', '~> 0.10.5' \ No newline at end of file diff --git a/app/controllers/api/v1/base_controller.rb b/app/controllers/api/v1/base_controller.rb index a66bfc528..c765906b0 100644 --- a/app/controllers/api/v1/base_controller.rb +++ b/app/controllers/api/v1/base_controller.rb @@ -2,6 +2,7 @@ class Api::V1::BaseController < ApplicationController include Api::ProjectHelper include Api::UserHelper + include Api::PullHelper # before_action :doorkeeper_authorize! # skip_before_action :user_setup @@ -30,18 +31,18 @@ class Api::V1::BaseController < ApplicationController # 具有对仓库的管理权限 def require_manager_above @project = load_project - return render_forbidden unless current_user.admin? && @project.manager?(current_user) + return render_forbidden if !current_user.admin? && !@project.manager?(current_user) end # 具有对仓库的操作权限 def require_operate_above @project = load_project - return render_forbidden unless current_user.admin? && @project.operator?(current_user) + return render_forbidden if !current_user.admin? && !@project.operator?(current_user) end # 具有对仓库的访问权限 def require_public_and_member_above @project = load_project - return render_forbidden unless @project.is_public || (current_user.admin? && @project.member?(current_user)) + return render_forbidden if !@project.is_public && !current_user.admin? && !@project.member?(current_user) end end \ No newline at end of file diff --git a/app/controllers/api/v1/projects/pulls/base_controller.rb b/app/controllers/api/v1/projects/pulls/base_controller.rb index 0d5ae08b5..acb5e2a03 100644 --- a/app/controllers/api/v1/projects/pulls/base_controller.rb +++ b/app/controllers/api/v1/projects/pulls/base_controller.rb @@ -2,19 +2,4 @@ class Api::V1::Projects::Pulls::BaseController < Api::V1::BaseController before_action :require_public_and_member_above before_action :load_pull_request - def load_pull_request - pull_request_id = params[:pull_id] || params[:id] - @pull_request = @project.pull_requests.where(gitea_number: pull_request_id).where.not(id: pull_request_id).take || PullRequest.find_by_id(pull_request_id) - @issue = @pull_request&.issue - if @pull_request - logger.info "###########pull_request founded" - @pull_request - else - logger.info "###########pull_request not found" - @pull_request = nil - render_not_found and return - end - - @pull_request - end end \ No newline at end of file diff --git a/app/controllers/api/v1/projects/pulls/comments_controller.rb b/app/controllers/api/v1/projects/pulls/comments_controller.rb new file mode 100644 index 000000000..8622c3190 --- /dev/null +++ b/app/controllers/api/v1/projects/pulls/comments_controller.rb @@ -0,0 +1,2 @@ +class Api::V1::Projects::Pulls::CommentsController < Api::V1::Projects::Pulls::BaseController +end \ No newline at end of file diff --git a/app/controllers/api/v1/projects/pulls/reviews_controller.rb b/app/controllers/api/v1/projects/pulls/reviews_controller.rb new file mode 100644 index 000000000..523137a86 --- /dev/null +++ b/app/controllers/api/v1/projects/pulls/reviews_controller.rb @@ -0,0 +1,22 @@ +class Api::V1::Projects::Pulls::ReviewsController < Api::V1::Projects::Pulls::BaseController + + def index + @reviews = @pull_request.reviews + @reviews = kaminari_paginate(@reviews) + end + + before_action :require_reviewer, only: [:create] + + def create + @journal, @review = Api::V1::Projects::Pulls::Reviews::CreateService.call(@project, @pull_request, review_params, current_user) + end + + private + def require_reviewer + return render_forbidden('您没有审查权限,请联系项目管理员') if !current_user.admin? && !@pull_request.reviewers.exists?(current_user.id) + end + + def review_params + params.require(:review).permit(:content, :commit_id, :status) + end +end \ No newline at end of file diff --git a/app/controllers/api/v1/projects/pulls_controller.rb b/app/controllers/api/v1/projects/pulls_controller.rb new file mode 100644 index 000000000..a930df760 --- /dev/null +++ b/app/controllers/api/v1/projects/pulls_controller.rb @@ -0,0 +1,20 @@ +class Api::V1::Projects::PullsController < Api::V1::BaseController + before_action :require_public_and_member_above + + def index + @pulls = Api::V1::Projects::Pulls::ListService.call(@project, query_params) + @pulls = kaminari_paginate(@pulls) + end + + before_action :load_pull_request, only: [:show] + + def show + @result_object = Api::V1::Projects::Pulls::GetService.call(@project, @pull_request, current_user&.gitea_token) + @last_review = @pull_request.reviews.take + end + + private + def query_params + params.permit(:status, :keyword, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :sort_by, :sort_direction) + end +end \ No newline at end of file diff --git a/app/controllers/concerns/api/pull_helper.rb b/app/controllers/concerns/api/pull_helper.rb new file mode 100644 index 000000000..e186cd6f2 --- /dev/null +++ b/app/controllers/concerns/api/pull_helper.rb @@ -0,0 +1,19 @@ +module Api::PullHelper + extend ActiveSupport::Concern + + def load_pull_request + pull_request_id = params[:pull_id] || params[:id] + @pull_request = @project.pull_requests.where(gitea_number: pull_request_id).where.not(id: pull_request_id).take || PullRequest.find_by_id(pull_request_id) + @issue = @pull_request&.issue + if @pull_request + logger.info "###########pull_request founded" + @pull_request + else + logger.info "###########pull_request not found" + @pull_request = nil + render_not_found and return + end + + @pull_request + end +end \ No newline at end of file diff --git a/app/controllers/pull_requests_controller.rb b/app/controllers/pull_requests_controller.rb index 52da4ac48..8bbfdc230 100644 --- a/app/controllers/pull_requests_controller.rb +++ b/app/controllers/pull_requests_controller.rb @@ -176,7 +176,7 @@ class PullRequestsController < ApplicationController @issue_assign_to = @issue.get_assign_user @gitea_pull = Gitea::PullRequest::GetService.call(@owner.login, @repository.identifier, @pull_request.gitea_number, current_user&.gitea_token) - @last_review = @pull_request.issue.reviews.take + @last_review = @pull_request.reviews.take end def pr_merge diff --git a/app/models/issue.rb b/app/models/issue.rb index fa8c43ea9..2a3b95958 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -68,7 +68,6 @@ class Issue < ApplicationRecord has_many :issue_tags, through: :issue_tags_relates has_many :issue_times, dependent: :destroy has_many :issue_depends, dependent: :destroy - has_many :reviews, dependent: :destroy scope :issue_includes, ->{includes(:user)} scope :issue_many_includes, ->{includes(journals: :user)} scope :issue_issue, ->{where(issue_classify: [nil,"issue"])} diff --git a/app/models/journal.rb b/app/models/journal.rb index 297c7a148..19fdce1c9 100644 --- a/app/models/journal.rb +++ b/app/models/journal.rb @@ -25,7 +25,8 @@ class Journal < ApplicationRecord belongs_to :user - belongs_to :issue, foreign_key: :journalized_id, :touch => true + belongs_to :issue, foreign_key: :journalized_id, :touch => true, optional: true + belongs_to :journalized, polymorphic: true has_many :journal_details, :dependent => :delete_all has_many :attachments, as: :container, dependent: :destroy diff --git a/app/models/pull_request.rb b/app/models/pull_request.rb index e36af832b..b3ab1e03b 100644 --- a/app/models/pull_request.rb +++ b/app/models/pull_request.rb @@ -32,12 +32,17 @@ class PullRequest < ApplicationRecord belongs_to :issue belongs_to :user belongs_to :project, counter_cache: true, touch: true - # belongs_to :fork_project, foreign_key: :fork_project_id + belongs_to :fork_project, class_name: 'Project', foreign_key: :fork_project_id, optional: true has_many :pull_request_assigns, foreign_key: :pull_request_id has_many :pull_request_tags, foreign_key: :pull_request_id has_many :project_trends, as: :trend, dependent: :destroy has_many :attachments, as: :container, dependent: :destroy has_one :gitea_pull, foreign_key: :id, primary_key: :gitea_number, class_name: 'Gitea::Pull' + has_many :journals, :as => :journalized, :dependent => :destroy + has_many :journal_details, through: :journals + has_many :reviews, dependent: :destroy + has_many :pull_requests_reviewers, dependent: :destroy + has_many :reviewers, through: :pull_requests_reviewers scope :merged_and_closed, ->{where.not(status: 0)} scope :opening, -> {where(status: 0)} diff --git a/app/models/pull_requests_reviewer.rb b/app/models/pull_requests_reviewer.rb new file mode 100644 index 000000000..31502b2a9 --- /dev/null +++ b/app/models/pull_requests_reviewer.rb @@ -0,0 +1,21 @@ +# == Schema Information +# +# Table name: pull_requests_reviewers +# +# id :integer not null, primary key +# pull_request_id :integer +# reviewer_id :integer +# created_at :datetime not null +# updated_at :datetime not null +# +# Indexes +# +# index_pull_requests_reviewers_on_pull_request_id (pull_request_id) +# index_pull_requests_reviewers_on_reviewer_id (reviewer_id) +# + +class PullRequestsReviewer < ApplicationRecord + + belongs_to :pull_request + belongs_to :reviewers, class_name: 'User', foreign_key: :reviewer_id +end diff --git a/app/models/review.rb b/app/models/review.rb index f2d3e678d..efb508139 100644 --- a/app/models/review.rb +++ b/app/models/review.rb @@ -2,24 +2,24 @@ # # Table name: reviews # -# id :integer not null, primary key -# issue_id :integer -# reviewer_id :integer -# content :text(65535) -# commit_id :string(255) -# status :integer default("0") -# created_at :datetime not null -# updated_at :datetime not null +# id :integer not null, primary key +# reviewer_id :integer +# content :text(65535) +# commit_id :string(255) +# status :integer default("0") +# created_at :datetime not null +# updated_at :datetime not null +# pull_request_id :integer # # Indexes # -# index_reviews_on_issue_id (issue_id) -# index_reviews_on_reviewer_id (reviewer_id) +# index_reviews_on_pull_request_id (pull_request_id) +# index_reviews_on_reviewer_id (reviewer_id) # class Review < ApplicationRecord - belongs_to :issue + belongs_to :pull_request belongs_to :reviewer, class_name: 'User', foreign_key: :reviewer_id has_one :journal, dependent: :destroy diff --git a/app/services/api/v1/projects/pulls/get_service.rb b/app/services/api/v1/projects/pulls/get_service.rb new file mode 100644 index 000000000..3c1bba99b --- /dev/null +++ b/app/services/api/v1/projects/pulls/get_service.rb @@ -0,0 +1,32 @@ +class Api::V1::Projects::Pulls::GetService < ApplicationService + + attr_reader :project, :pull_request, :owner, :repo, :index, :token + attr_accessor :gitea_data + + def initialize(project, pull_request, token = nil) + @project = project + @pull_request = pull_request + @owner = project&.owner.login + @repo = project&.identifier + @index = pull_request.gitea_number + @token = token + end + + def call + load_gitea_data + + gitea_data + end + + private + def request_params + { + access_token: token + } + end + + def load_gitea_data + @gitea_data = $gitea_client.get_repos_pulls_by_owner_repo_index(owner, repo, index, {query: request_params}) + # raise Error, '获取合并请求失败!' unless @gitea_data.is_a?(Hash) + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/pulls/list_service.rb b/app/services/api/v1/projects/pulls/list_service.rb new file mode 100644 index 000000000..6b18da2ce --- /dev/null +++ b/app/services/api/v1/projects/pulls/list_service.rb @@ -0,0 +1,46 @@ +class Api::V1::Projects::Pulls::ListService < ApplicationService + include ActiveModel::Model + + attr_reader :project, :keyword, :status, :priority_id, :issue_tag_id, :version_id, :reviewer_id, :sort_by, :sort_direction + attr_accessor :queried_pull_requests + + validates :status, inclusion: {in: [0, 1, 2], message: "请输入正确的Status"}, allow_nil: true + validates :sort_by, inclusion: {in: PullRequest.column_names, message: '请输入正确的SortBy'} + validates :sort_direction, inclusion: {in: %w(asc desc), message: '请输入正确的SortDirection'} + + def initialize(project, params={}) + @project = project + @keyword = params[:keyword] + @status = params[:status].to_i + @priority_id = params[:priority_id] + @issue_tag_id = params[:issue_tag_id] + @version_id = params[:version_id] + @reviewer_id = params[:reviewer_id] + @sort_by = params[:sort_by] || 'created_at' + @sort_direction = params[:sort_direction] || 'desc' + end + + def call + raise Error, errors.full_messages.join(",") unless valid? + pull_request_query_data + + queried_pull_requests + end + + private + def pull_request_query_data + pull_requests = @project.pull_requests + + pull_requests = pull_requests.where(status: status) if status.present? + pull_requests = pull_requests.where(issues: {priority_id: priority_id}) if priority_id.present? + pull_requests = pull_requests.where(issue_tags: {id: issue_tag_id}) if issue_tag_id.present? + pull_requests = pull_requests.where(issues: {fixed_version_id: version_id}) if version_id.present? + pull_requests = pull_requests.where(users: {id: reviewer_id}) if reviewer_id.present? + + q = pull_requests.ransack(title_or_body_cont: keyword) + scope = q.result.includes(:fork_project, :journals, :reviews, :reviewers, issue: [:journals, :priority, :version, :issue_tags]) + scope = scope.order("pull_requests.#{sort_by} #{sort_direction}") + + @queried_pull_requests = scope + end +end \ No newline at end of file diff --git a/app/services/api/v1/projects/pulls/reviews/create_service.rb b/app/services/api/v1/projects/pulls/reviews/create_service.rb index 13ec9f7e7..bef944870 100644 --- a/app/services/api/v1/projects/pulls/reviews/create_service.rb +++ b/app/services/api/v1/projects/pulls/reviews/create_service.rb @@ -24,17 +24,17 @@ class Api::V1::Projects::Pulls::Reviews::CreateService < ApplicationService end return @journal, @review - rescue - raise Error, '服务器错误,请联系系统管理员!' + # rescue + # raise Error, '服务器错误,请联系系统管理员!' end private def create_review - @review = issue.reviews.create!(status: status, content: content, commit_id: commit_id, reviewer_id: @current_user.id) + @review = pull_request.reviews.create!(status: status, content: content, commit_id: commit_id, reviewer_id: @current_user.id) end def create_journal - @journal = issue.journals.create!(notes: content, user_id: @current_user.id, review_id: @review.id) + @journal = pull_request.journals.create!(notes: content, user_id: @current_user.id, review_id: @review.id) end end \ No newline at end of file diff --git a/app/views/api/v1/projects/issues/_simple_detail.json.jbuilder b/app/views/api/v1/projects/issues/_simple_detail.json.jbuilder new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/api/v1/projects/pulls/_simple_detail.json.jbuilder b/app/views/api/v1/projects/pulls/_simple_detail.json.jbuilder new file mode 100644 index 000000000..e8c668c59 --- /dev/null +++ b/app/views/api/v1/projects/pulls/_simple_detail.json.jbuilder @@ -0,0 +1,25 @@ +json.(pull, :id, :head, :base, :is_original) +json.index pull.gitea_number +json.status pull.status == 1 ? "merged" : (pull.status == 2 ? "closed" : "open") + +fork_project = pull&.fork_project +if fork_project.present? + json.fork_project do + json.(fork_project, :id, :identifier) + json.login fork_project&.owner&.login + end +end + +issue = pull&.issue +json.issue do + json.id issue&.id + json.author do + json.partial! '/api/v1/users/simple_user', user: issue&.user + end + json.priority issue&.priority.try(:name) + json.version issue&.version.try(:name) + json.comments_count issue.journals.count + json.issue_tags issue.get_issue_tags +end + +json.comments_count pull.journals.count \ No newline at end of file diff --git a/app/views/api/v1/projects/pulls/index.json.jbuilder b/app/views/api/v1/projects/pulls/index.json.jbuilder new file mode 100644 index 000000000..412ea13d7 --- /dev/null +++ b/app/views/api/v1/projects/pulls/index.json.jbuilder @@ -0,0 +1,4 @@ +json.total_count @pulls.total_count +json.pulls @pulls.each do |pull| + json.partial! 'api/v1/projects/pulls/simple_detail', pull: pull +end \ No newline at end of file diff --git a/app/views/api/v1/projects/pulls/reviews/create.json.jbuilder b/app/views/api/v1/projects/pulls/reviews/create.json.jbuilder new file mode 100644 index 000000000..2b9f0efe1 --- /dev/null +++ b/app/views/api/v1/projects/pulls/reviews/create.json.jbuilder @@ -0,0 +1,8 @@ +json.reviewer do + json.partial! "api/v1/users/simple_user", user: @review.reviewer +end +json.pull_request do + json.partial! "api/v1/projects/pulls/simple_detail", pull: @review.pull_request +end +json.(@review, :id, :commit_id, :content, :status) +json.created_at format_time(@review.created_at) \ No newline at end of file diff --git a/app/views/api/v1/projects/pulls/show.json.jbuilder b/app/views/api/v1/projects/pulls/show.json.jbuilder new file mode 100644 index 000000000..fce27e17f --- /dev/null +++ b/app/views/api/v1/projects/pulls/show.json.jbuilder @@ -0,0 +1,29 @@ +json.partial! "api/v1/projects/pulls/simple_detail", pull: @pull_request +json.merge_base @result_object['merge_base'] +json.base_commit_sha @result_object['base']['sha'] +json.head_commit_sha @result_object['head']['sha'] +json.commit_num @result_object['commit_num'] +json.changed_files @result_object['changed_files'] +json.is_locked @result_object['is_locked'] +json.mergeable @result_object['mergeable'] # 是否能合并 +json.merged @result_object['merged'] +json.merged_at @result_object['merged_at'].nil? ? '' : render_unix_time( @result_object['merged_at']) +json.merge_commit_sha @result_object['merge_commit_sha'] +json.merge_by do + if @result_object['merged_by'] + json.partial! 'api/v1/users/commit_user', locals: { user: render_cache_commit_author(@result_object['merged_by']), name: @result_object['merged_by']['login'] } + else + json.nil! + end +end +json.last_review do + if @last_review.present? + json.(@last_review, :id, :commit_id, :content, :status) + json.created_at format_time(@last_review.created_at) + json.reviewer do + json.partial! "api/v1/users/simple_user", user: @last_review.reviewer + end + else + json.nil! + end +end \ No newline at end of file diff --git a/config/routes/api.rb b/config/routes/api.rb index 8340097ee..0dfe6112f 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -25,6 +25,7 @@ defaults format: :json do get :diff end end + resources :reviews, only: [:index, :create] end resources :versions diff --git a/db/migrate/20220725020217_create_pull_requests_reviewers.rb b/db/migrate/20220725020217_create_pull_requests_reviewers.rb new file mode 100644 index 000000000..be5c561cf --- /dev/null +++ b/db/migrate/20220725020217_create_pull_requests_reviewers.rb @@ -0,0 +1,9 @@ +class CreatePullRequestsReviewers < ActiveRecord::Migration[5.2] + def change + create_table :pull_requests_reviewers do |t| + t.belongs_to :pull_request, index: true + t.belongs_to :reviewer, class_name: User, index:true + t.timestamps + end + end +end diff --git a/db/migrate/20220725020443_change_reviews_reference_to_pull_requests.rb b/db/migrate/20220725020443_change_reviews_reference_to_pull_requests.rb new file mode 100644 index 000000000..e03943915 --- /dev/null +++ b/db/migrate/20220725020443_change_reviews_reference_to_pull_requests.rb @@ -0,0 +1,6 @@ +class ChangeReviewsReferenceToPullRequests < ActiveRecord::Migration[5.2] + def change + remove_reference :reviews, :issue + add_reference :reviews, :pull_request + end +end diff --git a/spec/models/pull_requests_reviewer_spec.rb b/spec/models/pull_requests_reviewer_spec.rb new file mode 100644 index 000000000..f3dfd9b95 --- /dev/null +++ b/spec/models/pull_requests_reviewer_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe PullRequestsReviewer, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end