summaryrefslogtreecommitdiff
path: root/lib/bitbucket_server/representation/comment.rb
blob: 7e55c446e76a2226f94fc0aea6cf43ca1952ec03 (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
# frozen_string_literal: true

module BitbucketServer
  module Representation
    # A general comment with the structure:
    # "comment": {
    #   "author": {
    #               "active": true,
    #               "displayName": "root",
    #               "emailAddress": "stanhu+bitbucket@gitlab.com",
    #               "id": 1,
    #               "links": {
    #                 "self": [
    #                   {
    #                     "href": "http://localhost:7990/users/root"
    #                   }
    #                 ]
    #                },
    #                "name": "root",
    #                "slug": "root",
    #                "type": "NORMAL"
    #               }
    #   }
    # }
    class Comment < Representation::Base
      attr_reader :parent_comment

      CommentNode = Struct.new(:raw_comments, :parent)

      def initialize(raw, parent_comment: nil)
        super(raw)

        @parent_comment = parent_comment
      end

      def id
        raw_comment['id']
      end

      def author_username
        author['username'] ||
          author['slug'] ||
          author['displayName']
      end

      def author_email
        author['emailAddress']
      end

      def note
        raw_comment['text']
      end

      def created_at
        self.class.convert_timestamp(created_date)
      end

      def updated_at
        self.class.convert_timestamp(created_date)
      end

      # Bitbucket Server supports the ability to reply to any comment
      # and created multiple threads. It represents these as a linked list
      # of comments within comments. For example:
      #
      # "comments": [
      #    {
      #       "author" : ...
      #       "comments": [
      #          {
      #             "author": ...
      #
      # Since GitLab only supports a single thread, we flatten all these
      # comments into a single discussion.
      def comments
        @comments ||= flatten_comments
      end

      private

      # In order to provide context for each reply, we need to track
      # the parent of each comment. This method works as follows:
      #
      # 1. Insert the root comment into the workset. The root element is the current note.
      # 2. For each node in the workset:
      #    a. Examine if it has replies to that comment. If it does,
      #       insert that node into the workset.
      #    b. Parse that note into a Comment structure and add it to a flat list.
      def flatten_comments
        comments = raw_comment['comments']
        workset =
          if comments
            [CommentNode.new(comments, self)]
          else
            []
          end

        all_comments = []

        until workset.empty?
          node = workset.pop
          parent = node.parent

          node.raw_comments.each do |comment|
            new_comments = comment.delete('comments')
            current_comment = Comment.new({ 'comment' => comment }, parent_comment: parent)
            all_comments << current_comment
            workset << CommentNode.new(new_comments, current_comment) if new_comments
          end
        end

        all_comments
      end

      def raw_comment
        raw.fetch('comment', {})
      end

      def author
        raw_comment['author']
      end

      def created_date
        raw_comment['createdDate']
      end

      def updated_date
        raw_comment['updatedDate']
      end
    end
  end
end