654c4dd922
This replaces API::Notes::NOTEABLE_TYPES and API::Discussions::NOTEABLE_TYPES with class methods defined in a separate helper modules. This allows EE to extend the list of noteable types, without having to modify the constant in-place. We can't define these methods directly in the API classes, as they would be used before we're able to extend them in EE.
237 lines
9.9 KiB
Ruby
237 lines
9.9 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
module API
|
|
class Discussions < Grape::API
|
|
include PaginationParams
|
|
helpers ::API::Helpers::NotesHelpers
|
|
|
|
before { authenticate! }
|
|
|
|
Helpers::DiscussionsHelpers.noteable_types.each do |noteable_type|
|
|
parent_type = noteable_type.parent_class.to_s.underscore
|
|
noteables_str = noteable_type.to_s.underscore.pluralize
|
|
noteables_path = noteable_type == Commit ? "repository/#{noteables_str}" : noteables_str
|
|
|
|
params do
|
|
requires :id, type: String, desc: "The ID of a #{parent_type}"
|
|
end
|
|
resource parent_type.pluralize.to_sym, requirements: API::NAMESPACE_OR_PROJECT_REQUIREMENTS do
|
|
desc "Get a list of #{noteable_type.to_s.downcase} discussions" do
|
|
success Entities::Discussion
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
use :pagination
|
|
end
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
get ":id/#{noteables_path}/:noteable_id/discussions" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
|
|
notes = noteable.notes
|
|
.inc_relations_for_view
|
|
.includes(:noteable)
|
|
.fresh
|
|
|
|
notes = notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
|
|
discussions = Kaminari.paginate_array(Discussion.build_collection(notes, noteable))
|
|
|
|
present paginate(discussions), with: Entities::Discussion
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
|
|
desc "Get a single #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Discussion
|
|
end
|
|
params do
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
end
|
|
get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
notes = readable_discussion_notes(noteable, params[:discussion_id])
|
|
|
|
if notes.empty?
|
|
break not_found!("Discussion")
|
|
end
|
|
|
|
discussion = Discussion.build(notes, noteable)
|
|
|
|
present discussion, with: Entities::Discussion
|
|
end
|
|
|
|
desc "Create a new #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Discussion
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
requires :body, type: String, desc: 'The content of a note'
|
|
optional :created_at, type: String, desc: 'The creation date of the note'
|
|
optional :position, type: Hash do
|
|
requires :base_sha, type: String, desc: 'Base commit SHA in the source branch'
|
|
requires :start_sha, type: String, desc: 'SHA referencing commit in target branch'
|
|
requires :head_sha, type: String, desc: 'SHA referencing HEAD of this merge request'
|
|
requires :position_type, type: String, desc: 'Type of the position reference', values: %w(text image)
|
|
optional :new_path, type: String, desc: 'File path after change'
|
|
optional :new_line, type: Integer, desc: 'Line number after change'
|
|
optional :old_path, type: String, desc: 'File path before change'
|
|
optional :old_line, type: Integer, desc: 'Line number before change'
|
|
optional :width, type: Integer, desc: 'Width of the image'
|
|
optional :height, type: Integer, desc: 'Height of the image'
|
|
optional :x, type: Integer, desc: 'X coordinate in the image'
|
|
optional :y, type: Integer, desc: 'Y coordinate in the image'
|
|
end
|
|
end
|
|
post ":id/#{noteables_path}/:noteable_id/discussions" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
type = params[:position] ? 'DiffNote' : 'DiscussionNote'
|
|
id_key = noteable.is_a?(Commit) ? :commit_id : :noteable_id
|
|
|
|
opts = {
|
|
note: params[:body],
|
|
created_at: params[:created_at],
|
|
type: type,
|
|
noteable_type: noteables_str.classify,
|
|
position: params[:position],
|
|
id_key => noteable.id
|
|
}
|
|
|
|
note = create_note(noteable, opts)
|
|
|
|
if note.valid?
|
|
present note.discussion, with: Entities::Discussion
|
|
else
|
|
bad_request!("Note #{note.errors.messages}")
|
|
end
|
|
end
|
|
|
|
desc "Get comments in a single #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Discussion
|
|
end
|
|
params do
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
end
|
|
get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
notes = readable_discussion_notes(noteable, params[:discussion_id])
|
|
|
|
if notes.empty?
|
|
break not_found!("Notes")
|
|
end
|
|
|
|
present notes, with: Entities::Note
|
|
end
|
|
|
|
desc "Add a comment to a #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Note
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :body, type: String, desc: 'The content of a note'
|
|
optional :created_at, type: String, desc: 'The creation date of the note'
|
|
end
|
|
post ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
notes = readable_discussion_notes(noteable, params[:discussion_id])
|
|
|
|
break not_found!("Discussion") if notes.empty?
|
|
break bad_request!("Discussion is an individual note.") unless notes.first.part_of_discussion?
|
|
|
|
opts = {
|
|
note: params[:body],
|
|
type: 'DiscussionNote',
|
|
in_reply_to_discussion_id: params[:discussion_id],
|
|
created_at: params[:created_at]
|
|
}
|
|
note = create_note(noteable, opts)
|
|
|
|
if note.valid?
|
|
present note, with: Entities::Note
|
|
else
|
|
bad_request!("Note #{note.errors.messages}")
|
|
end
|
|
end
|
|
|
|
desc "Get a comment in a #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Note
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :note_id, type: Integer, desc: 'The ID of a note'
|
|
end
|
|
get ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
|
|
get_note(noteable, params[:note_id])
|
|
end
|
|
|
|
desc "Edit a comment in a #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Note
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :note_id, type: Integer, desc: 'The ID of a note'
|
|
optional :body, type: String, desc: 'The content of a note'
|
|
optional :resolved, type: Boolean, desc: 'Mark note resolved/unresolved'
|
|
exactly_one_of :body, :resolved
|
|
end
|
|
put ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
|
|
if params[:resolved].nil?
|
|
update_note(noteable, params[:note_id])
|
|
else
|
|
resolve_note(noteable, params[:note_id], params[:resolved])
|
|
end
|
|
end
|
|
|
|
desc "Delete a comment in a #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Note
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :note_id, type: Integer, desc: 'The ID of a note'
|
|
end
|
|
delete ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
|
|
delete_note(noteable, params[:note_id])
|
|
end
|
|
|
|
if Noteable::RESOLVABLE_TYPES.include?(noteable_type.to_s)
|
|
desc "Resolve/unresolve an existing #{noteable_type.to_s.downcase} discussion" do
|
|
success Entities::Discussion
|
|
end
|
|
params do
|
|
requires :noteable_id, types: [Integer, String], desc: 'The ID of the noteable'
|
|
requires :discussion_id, type: String, desc: 'The ID of a discussion'
|
|
requires :resolved, type: Boolean, desc: 'Mark discussion resolved/unresolved'
|
|
end
|
|
put ":id/#{noteables_path}/:noteable_id/discussions/:discussion_id" do
|
|
noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])
|
|
|
|
resolve_discussion(noteable, params[:discussion_id], params[:resolved])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
helpers do
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
|
def readable_discussion_notes(noteable, discussion_id)
|
|
notes = noteable.notes
|
|
.where(discussion_id: discussion_id)
|
|
.inc_relations_for_view
|
|
.includes(:noteable)
|
|
.fresh
|
|
|
|
notes.reject { |n| n.cross_reference_not_visible_for?(current_user) }
|
|
end
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
|
end
|
|
end
|
|
end
|