summaryrefslogtreecommitdiff
path: root/lib/api/discussions.rb
blob: 6abd575b6adafd825d7866df9b0c88f9fff468aa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
module API
  class Discussions < Grape::API
    include PaginationParams
    helpers ::API::Helpers::NotesHelpers

    before { authenticate! }

    NOTEABLE_TYPES = [Issue, Snippet].freeze

    NOTEABLE_TYPES.each do |noteable_type|
      parent_type = noteable_type.parent_class.to_s.underscore
      noteables_str = noteable_type.to_s.underscore.pluralize

      params do
        requires :id, type: String, desc: "The ID of a #{parent_type}"
      end
      resource parent_type.pluralize.to_sym, requirements: API::PROJECT_ENDPOINT_REQUIREMENTS do
        desc "Get a list of #{noteable_type.to_s.downcase} discussions" do
          success Entities::Discussion
        end
        params do
          requires :noteable_id, type: Integer, desc: 'The ID of the noteable'
          use :pagination
        end
        get ":id/#{noteables_str}/:noteable_id/discussions" do
          noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])

          return not_found!("Discussions") unless can?(current_user, noteable_read_ability_name(noteable), noteable)

          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

        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, type: Integer, desc: 'The ID of the noteable'
        end
        get ":id/#{noteables_str}/: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? || !can?(current_user, noteable_read_ability_name(noteable), noteable)
            return 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, type: Integer, 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'
        end
        post ":id/#{noteables_str}/:noteable_id/discussions" do
          noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])

          opts = {
            note: params[:body],
            created_at: params[:created_at],
            type: 'DiscussionNote',
            noteable_type: noteables_str.classify,
            noteable_id: 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, type: Integer, desc: 'The ID of the noteable'
        end
        get ":id/#{noteables_str}/: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? || !can?(current_user, noteable_read_ability_name(noteable), noteable)
            return 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, type: Integer, 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_str}/: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])

          return not_found!("Discussion") if notes.empty?
          return 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, type: Integer, 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_str}/: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, type: Integer, 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'
          requires :body, type: String, desc: 'The content of a note'
        end
        put ":id/#{noteables_str}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
          noteable = find_noteable(parent_type, noteables_str, params[:noteable_id])

          update_note(noteable, params[:note_id])
        end

        desc "Delete a comment in a #{noteable_type.to_s.downcase} discussion" do
          success Entities::Note
        end
        params do
          requires :noteable_id, type: Integer, 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_str}/: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
      end
    end

    helpers do
      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
    end
  end
end