interpret_service.rb 6.3 KB
Newer Older
1 2 3 4
module SlashCommands
  class InterpretService < BaseService
    include Gitlab::SlashCommands::Dsl

5
    attr_reader :issuable
6

7 8
    # Takes a text and interprets the commands that are extracted from it.
    # Returns the content without commands, and hash of changes to be applied to a record.
9 10
    def execute(content, issuable)
      @issuable = issuable
11 12
      @updates = {}

13
      opts = {
14
        issuable:     issuable,
15 16 17 18 19 20
        current_user: current_user,
        project:      project
      }

      content, commands = extractor.extract_commands(content, opts)

21
      commands.each do |name, arg|
22 23 24
        definition = self.class.command_definitions_by_name[name.to_sym]
        next unless definition

25
        definition.execute(self, opts, arg)
26 27
      end

28
      [content, @updates]
29 30 31 32
    end

    private

33 34
    def extractor
      Gitlab::SlashCommands::Extractor.new(self.class.command_definitions)
35 36
    end

37
    desc do
38
      "Close this #{issuable.to_ability_name.humanize(capitalize: false)}"
39 40
    end
    condition do
41 42 43
      issuable.persisted? &&
        issuable.open? &&
        current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
44
    end
45 46 47 48
    command :close do
      @updates[:state_event] = 'close'
    end

49
    desc do
50
      "Reopen this #{issuable.to_ability_name.humanize(capitalize: false)}"
51 52
    end
    condition do
53 54 55
      issuable.persisted? &&
        issuable.closed? &&
        current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
56
    end
Douwe Maan's avatar
Douwe Maan committed
57
    command :reopen do
58 59 60
      @updates[:state_event] = 'reopen'
    end

61 62
    desc 'Change title'
    params '<New title>'
63
    condition do
64 65
      issuable.persisted? &&
        current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
66
    end
67 68 69 70
    command :title do |title_param|
      @updates[:title] = title_param
    end

71
    desc 'Assign'
72
    params '@user'
73
    condition do
74
      current_user.can?(:"admin_#{issuable.to_ability_name}", project)
75
    end
76
    command :assign do |assignee_param|
77
      user = extract_references(assignee_param, :user).first
78
      user ||= User.find_by(username: assignee_param)
79

80
      @updates[:assignee_id] = user.id if user
81 82 83
    end

    desc 'Remove assignee'
84
    condition do
85 86 87
      issuable.persisted? &&
        issuable.assignee_id? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
88
    end
Douwe Maan's avatar
Douwe Maan committed
89
    command :unassign do
90 91 92
      @updates[:assignee_id] = nil
    end

93
    desc 'Set milestone'
94
    params '%"milestone"'
95
    condition do
96
      current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
97
        project.milestones.active.any?
98
    end
99 100
    command :milestone do |milestone_param|
      milestone = extract_references(milestone_param, :milestone).first
101
      milestone ||= project.milestones.find_by(title: milestone_param.strip)
102

103
      @updates[:milestone_id] = milestone.id if milestone
104 105 106
    end

    desc 'Remove milestone'
107
    condition do
108 109 110
      issuable.persisted? &&
        issuable.milestone_id? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
111
    end
Douwe Maan's avatar
Douwe Maan committed
112
    command :remove_milestone do
113 114 115 116 117
      @updates[:milestone_id] = nil
    end

    desc 'Add label(s)'
    params '~label1 ~"label 2"'
118
    condition do
119
      current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
120
        project.labels.any?
121
    end
Douwe Maan's avatar
Douwe Maan committed
122
    command :label do |labels_param|
123 124
      label_ids = find_label_ids(labels_param)

125
      @updates[:add_label_ids] = label_ids unless label_ids.empty?
126 127
    end

Douwe Maan's avatar
Douwe Maan committed
128
    desc 'Remove all or specific label(s)'
129
    params '~label1 ~"label 2"'
130
    condition do
131 132 133
      issuable.persisted? &&
        issuable.labels.any? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
134
    end
Douwe Maan's avatar
Douwe Maan committed
135 136 137
    command :unlabel do |labels_param = nil|
      if labels_param.present?
        label_ids = find_label_ids(labels_param)
138

Douwe Maan's avatar
Douwe Maan committed
139 140 141 142
        @updates[:remove_label_ids] = label_ids unless label_ids.empty?
      else
        @updates[:label_ids] = []
      end
143 144
    end

Douwe Maan's avatar
Douwe Maan committed
145 146
    desc 'Replace all label(s)'
    params '~label1 ~"label 2"'
147
    condition do
148 149 150
      issuable.persisted? &&
        issuable.labels.any? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
151
    end
Douwe Maan's avatar
Douwe Maan committed
152 153 154 155
    command :relabel do |labels_param|
      label_ids = find_label_ids(labels_param)

      @updates[:label_ids] = label_ids unless label_ids.empty?
156 157 158
    end

    desc 'Add a todo'
159
    condition do
160 161
      issuable.persisted? &&
        !TodoService.new.todo_exist?(issuable, current_user)
162
    end
163
    command :todo do
164
      @updates[:todo_event] = 'add'
165 166 167
    end

    desc 'Mark todo as done'
168
    condition do
169 170
      issuable.persisted? &&
        TodoService.new.todo_exist?(issuable, current_user)
171
    end
172 173 174 175 176
    command :done do
      @updates[:todo_event] = 'done'
    end

    desc 'Subscribe'
177
    condition do
178 179
      issuable.persisted? &&
        !issuable.subscribed?(current_user)
180
    end
181 182 183 184 185
    command :subscribe do
      @updates[:subscription_event] = 'subscribe'
    end

    desc 'Unsubscribe'
186
    condition do
187 188
      issuable.persisted? &&
        issuable.subscribed?(current_user)
189
    end
190 191 192 193
    command :unsubscribe do
      @updates[:subscription_event] = 'unsubscribe'
    end

194
    desc 'Set due date'
Douwe Maan's avatar
Douwe Maan committed
195
    params '<in 2 days | this Friday | December 31st>'
196
    condition do
197 198
      issuable.respond_to?(:due_date) &&
        current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
199
    end
Douwe Maan's avatar
Douwe Maan committed
200
    command :due do |due_date_param|
201
      due_date = Chronic.parse(due_date_param).try(:to_date)
202 203 204 205 206

      @updates[:due_date] = due_date if due_date
    end

    desc 'Remove due date'
207
    condition do
208 209 210 211
      issuable.persisted? &&
        issuable.respond_to?(:due_date) &&
        issuable.due_date? &&
        current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
212
    end
Douwe Maan's avatar
Douwe Maan committed
213
    command :remove_due_date do
214 215 216
      @updates[:due_date] = nil
    end

217 218 219
    # This is a dummy command, so that it appears in the autocomplete commands
    desc 'CC'
    params '@user'
220
    command :cc
221

222
    def find_label_ids(labels_param)
223 224 225 226
      label_ids_by_reference = extract_references(labels_param, :label).map(&:id)
      labels_ids_by_name = @project.labels.where(name: labels_param.split).select(:id)

      label_ids_by_reference | labels_ids_by_name
227 228
    end

229
    def extract_references(arg, type)
230
      ext = Gitlab::ReferenceExtractor.new(project, current_user)
231
      ext.analyze(arg, author: current_user)
232 233 234 235 236

      ext.references(type)
    end
  end
end