GitLab steht aufgrund eines wichtigen Updates heute, zwischen 18:00 und 19:00 Uhr, nicht zur Verfügung.

interpret_service.rb 8.28 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 = {}

Douwe Maan's avatar
Douwe Maan committed
13
      opts = {
14
        issuable:     issuable,
Douwe Maan's avatar
Douwe Maan committed
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|
Douwe Maan's avatar
Douwe Maan committed
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

Douwe Maan's avatar
Douwe Maan committed
28
      [content, @updates]
29 30 31 32
    end

    private

Douwe Maan's avatar
Douwe Maan committed
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

Rémy Coutable's avatar
Rémy Coutable committed
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
Rémy Coutable's avatar
Rémy Coutable committed
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 120
      available_labels = LabelsFinder.new(current_user, project_id: project.id).execute

121
      current_user.can?(:"admin_#{issuable.to_ability_name}", project) &&
122
        available_labels.any?
123
    end
Douwe Maan's avatar
Douwe Maan committed
124
    command :label do |labels_param|
125 126
      label_ids = find_label_ids(labels_param)

barthc's avatar
barthc committed
127 128 129 130 131 132
      if label_ids.any?
        @updates[:add_label_ids] ||= []
        @updates[:add_label_ids] += label_ids

        @updates[:add_label_ids].uniq!
      end
133 134
    end

Douwe Maan's avatar
Douwe Maan committed
135
    desc 'Remove all or specific label(s)'
136
    params '~label1 ~"label 2"'
137
    condition do
138 139 140
      issuable.persisted? &&
        issuable.labels.any? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
141
    end
Douwe Maan's avatar
Douwe Maan committed
142 143 144
    command :unlabel do |labels_param = nil|
      if labels_param.present?
        label_ids = find_label_ids(labels_param)
145

barthc's avatar
barthc committed
146 147 148 149 150 151
        if label_ids.any?
          @updates[:remove_label_ids] ||= []
          @updates[:remove_label_ids] += label_ids

          @updates[:remove_label_ids].uniq!
        end
Douwe Maan's avatar
Douwe Maan committed
152 153 154
      else
        @updates[:label_ids] = []
      end
155 156
    end

Douwe Maan's avatar
Douwe Maan committed
157 158
    desc 'Replace all label(s)'
    params '~label1 ~"label 2"'
159
    condition do
160 161 162
      issuable.persisted? &&
        issuable.labels.any? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
163
    end
Douwe Maan's avatar
Douwe Maan committed
164 165 166
    command :relabel do |labels_param|
      label_ids = find_label_ids(labels_param)

barthc's avatar
barthc committed
167 168 169 170 171 172
      if label_ids.any?
        @updates[:label_ids] ||= []
        @updates[:label_ids] += label_ids

        @updates[:label_ids].uniq!
      end
173 174 175
    end

    desc 'Add a todo'
176
    condition do
177 178
      issuable.persisted? &&
        !TodoService.new.todo_exist?(issuable, current_user)
179
    end
180
    command :todo do
181
      @updates[:todo_event] = 'add'
182 183 184
    end

    desc 'Mark todo as done'
185
    condition do
186 187
      issuable.persisted? &&
        TodoService.new.todo_exist?(issuable, current_user)
188
    end
189 190 191 192 193
    command :done do
      @updates[:todo_event] = 'done'
    end

    desc 'Subscribe'
194
    condition do
195
      issuable.persisted? &&
196
        !issuable.subscribed?(current_user, project)
197
    end
198 199 200 201 202
    command :subscribe do
      @updates[:subscription_event] = 'subscribe'
    end

    desc 'Unsubscribe'
203
    condition do
204
      issuable.persisted? &&
205
        issuable.subscribed?(current_user, project)
206
    end
207 208 209 210
    command :unsubscribe do
      @updates[:subscription_event] = 'unsubscribe'
    end

211
    desc 'Set due date'
Douwe Maan's avatar
Douwe Maan committed
212
    params '<in 2 days | this Friday | December 31st>'
213
    condition do
214
      issuable.respond_to?(:due_date) &&
215
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
216
    end
Douwe Maan's avatar
Douwe Maan committed
217
    command :due do |due_date_param|
218
      due_date = Chronic.parse(due_date_param).try(:to_date)
219 220 221 222 223

      @updates[:due_date] = due_date if due_date
    end

    desc 'Remove due date'
224
    condition do
225 226 227
      issuable.persisted? &&
        issuable.respond_to?(:due_date) &&
        issuable.due_date? &&
228
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
229
    end
Douwe Maan's avatar
Douwe Maan committed
230
    command :remove_due_date do
231 232 233
      @updates[:due_date] = nil
    end

Thomas Balthazar's avatar
Thomas Balthazar committed
234 235 236 237 238 239 240 241 242 243 244 245
    desc do
      "Toggle the Work In Progress status"
    end
    condition do
      issuable.persisted? &&
        issuable.respond_to?(:work_in_progress?) &&
        current_user.can?(:"update_#{issuable.to_ability_name}", issuable)
    end
    command :wip do
      @updates[:wip_event] = issuable.work_in_progress? ? 'unwip' : 'wip'
    end

246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
    desc 'Set time estimate'
    params '<1w 3d 2h 14m>'
    condition do
      current_user.can?(:"admin_#{issuable.to_ability_name}", project)
    end
    command :estimate do |raw_duration|
      time_estimate = Gitlab::TimeTrackingFormatter.parse(raw_duration)

      if time_estimate
        @updates[:time_estimate] = time_estimate
      end
    end

    desc 'Add or substract spent time'
    params '<1h 30m | -1h 30m>'
    condition do
      current_user.can?(:"admin_#{issuable.to_ability_name}", issuable)
    end
    command :spend do |raw_duration|
      time_spent = Gitlab::TimeTrackingFormatter.parse(raw_duration)

      if time_spent
268
        @updates[:spend_time] = { duration: time_spent, user: current_user }
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
      end
    end

    desc 'Remove time estimate'
    condition do
      issuable.persisted? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
    end
    command :remove_estimate do
      @updates[:time_estimate] = 0
    end

    desc 'Remove spent time'
    condition do
      issuable.persisted? &&
        current_user.can?(:"admin_#{issuable.to_ability_name}", project)
    end
    command :remove_time_spent do
287
      @updates[:spend_time] = { duration: :reset, user: current_user }
288 289
    end

290 291 292
    # This is a dummy command, so that it appears in the autocomplete commands
    desc 'CC'
    params '@user'
293
    command :cc
294

295
    def find_label_ids(labels_param)
296
      label_ids_by_reference = extract_references(labels_param, :label).map(&:id)
297
      labels_ids_by_name = LabelsFinder.new(current_user, project_id: project.id, name: labels_param.split).execute.select(:id)
298 299

      label_ids_by_reference | labels_ids_by_name
300 301
    end

302
    def extract_references(arg, type)
303
      ext = Gitlab::ReferenceExtractor.new(project, current_user)
304
      ext.analyze(arg, author: current_user)
305 306 307 308 309

      ext.references(type)
    end
  end
end