Skip to content
Snippets Groups Projects
Commit c96e1257 authored by 🙈  jacopo beschi 🙉's avatar 🙈 jacopo beschi 🙉 Committed by Mayra Cabrera
Browse files

Make quick action "commands applied" banner more useful

Extends the quick actions "commands applied" banner to show
the quick action preview text, but with everything in past tense.
parent 8b9ad9c6
No related branches found
No related tags found
No related merge requests found
Showing
with 912 additions and 146 deletions
Loading
Loading
@@ -137,6 +137,10 @@ class Label < ApplicationRecord
where(id: ids)
end
 
def self.on_project_board?(project_id, label_id)
on_project_boards(project_id).where(id: label_id).exists?
end
def open_issues_count(user = nil)
issues_count(user, state: 'opened')
end
Loading
Loading
Loading
Loading
@@ -21,7 +21,7 @@ module Notes
 
if quick_actions_service.supported?(note)
options = { merge_request_diff_head_sha: merge_request_diff_head_sha }
content, update_params = quick_actions_service.execute(note, options)
content, update_params, message = quick_actions_service.execute(note, options)
 
only_commands = content.empty?
 
Loading
Loading
@@ -52,7 +52,7 @@ module Notes
# We must add the error after we call #save because errors are reset
# when #save is called
if only_commands
note.errors.add(:commands_only, 'Commands applied')
note.errors.add(:commands_only, message.presence || _('Commands did not apply'))
end
end
 
Loading
Loading
Loading
Loading
@@ -31,17 +31,19 @@ module QuickActions
end
 
# 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.
# Returns the content without commands, a hash of changes to be applied to a record
# and a string containing the execution_message to show to the user.
def execute(content, quick_action_target, only: nil)
return [content, {}] unless current_user.can?(:use_quick_actions)
return [content, {}, ''] unless current_user.can?(:use_quick_actions)
 
@quick_action_target = quick_action_target
@updates = {}
@execution_message = {}
 
content, commands = extractor.extract_commands(content, only: only)
extract_updates(commands)
 
[content, @updates]
[content, @updates, execution_messages_for(commands)]
end
 
# Takes a text and interprets the commands that are extracted from it.
Loading
Loading
@@ -119,8 +121,12 @@ module QuickActions
labels_params.scan(/"([^"]+)"|([^ ]+)/).flatten.compact
end
 
def find_label_references(labels_param)
find_labels(labels_param).map(&:to_reference)
def find_label_references(labels_param, format = :id)
labels_to_reference(find_labels(labels_param), format)
end
def labels_to_reference(labels, format = :id)
labels.map { |l| l.to_reference(format: format) }
end
 
def find_label_ids(labels_param)
Loading
Loading
@@ -128,11 +134,24 @@ module QuickActions
end
 
def explain_commands(commands)
map_commands(commands, :explain)
end
def execution_messages_for(commands)
map_commands(commands, :execute_message).join(' ')
end
def map_commands(commands, method)
commands.map do |name, arg|
definition = self.class.definition_by_name(name)
next unless definition
 
definition.explain(self, arg)
case method
when :explain
definition.explain(self, arg)
when :execute_message
@execution_message[name.to_sym] || definition.execute_message(self, arg)
end
end.compact
end
 
Loading
Loading
---
title: Make quick action commands applied banner more useful
merge_request: 26672
author: Jacopo Beschi @jacopo-beschi
type: added
Loading
Loading
@@ -9,7 +9,8 @@ and commits that are usually done by clicking buttons or dropdowns in GitLab's U
You can enter these commands while creating a new issue or merge request, or
in comments of issues, epics, merge requests, and commits. Each command should be
on a separate line in order to be properly detected and executed. Once executed,
the commands are removed from the text body and not visible to anyone else.
> From [GitLab 12.1](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests/26672), an alert is displayed when a quick action is successfully applied.
 
## Quick Actions for issues and merge requests
 
Loading
Loading
Loading
Loading
@@ -3,8 +3,8 @@
module Gitlab
module QuickActions
class CommandDefinition
attr_accessor :name, :aliases, :description, :explanation, :params,
:condition_block, :parse_params_block, :action_block, :warning, :types
attr_accessor :name, :aliases, :description, :explanation, :execution_message,
:params, :condition_block, :parse_params_block, :action_block, :warning, :types
 
def initialize(name, attributes = {})
@name = name
Loading
Loading
@@ -13,6 +13,7 @@ module Gitlab
@description = attributes[:description] || ''
@warning = attributes[:warning] || ''
@explanation = attributes[:explanation] || ''
@execution_message = attributes[:execution_message] || ''
@params = attributes[:params] || []
@condition_block = attributes[:condition_block]
@parse_params_block = attributes[:parse_params_block]
Loading
Loading
@@ -48,13 +49,23 @@ module Gitlab
end
 
def execute(context, arg)
return if noop? || !available?(context)
return unless executable?(context)
 
count_commands_executed_in(context)
 
execute_block(action_block, context, arg)
end
 
def execute_message(context, arg)
return unless executable?(context)
if execution_message.respond_to?(:call)
execute_block(execution_message, context, arg)
else
execution_message
end
end
def to_h(context)
desc = description
if desc.respond_to?(:call)
Loading
Loading
@@ -77,6 +88,10 @@ module Gitlab
 
private
 
def executable?(context)
!noop? && available?(context)
end
def count_commands_executed_in(context)
return unless context.respond_to?(:commands_executed_count=)
 
Loading
Loading
Loading
Loading
@@ -16,6 +16,13 @@ module Gitlab
_("Tags this commit to %{tag_name}.") % { tag_name: tag_name }
end
end
execution_message do |tag_name, message|
if message.present?
_("Tagged this commit to %{tag_name} with \"%{message}\".") % { tag_name: tag_name, message: message }
else
_("Tagged this commit to %{tag_name}.") % { tag_name: tag_name }
end
end
params 'v1.2.3 <message>'
parse_params do |tag_name_and_message|
tag_name_and_message.split(' ', 2)
Loading
Loading
Loading
Loading
@@ -66,6 +66,35 @@ module Gitlab
@explanation = block_given? ? block : text
end
 
# Allows to provide a message about quick action execution result, success or failure.
# This message is shown after quick action execution and after saving the note.
#
# Example:
#
# execution_message do |arguments|
# "Added label(s) #{arguments.join(' ')}"
# end
# command :command_key do |arguments|
# # Awesome code block
# end
#
# Note: The execution_message won't be executed unless the condition block returns true.
# execution_message block is executed always after the command block has run,
# for this reason if the condition block doesn't return true after the command block has
# run you need to set the @execution_message variable inside the command block instead as
# shown in the following example.
#
# Example using instance variable:
#
# command :command_key do |arguments|
# # Awesome code block
# @execution_message[:command_key] = 'command_key executed successfully'
# end
#
def execution_message(text = '', &block)
@execution_message = block_given? ? block : text
end
# Allows to define type(s) that must be met in order for the command
# to be returned by `.command_names` & `.command_definitions`.
#
Loading
Loading
@@ -121,10 +150,16 @@ module Gitlab
# comment.
# It accepts aliases and takes a block.
#
# You can also set the @execution_message instance variable, on conflicts with
# execution_message method the instance variable has precedence.
#
# Example:
#
# command :my_command, :alias_for_my_command do |arguments|
# # Awesome code block
# @updates[:my_command] = 'foo'
#
# @execution_message[:my_command] = 'my_command executed successfully'
# end
def command(*command_names, &block)
define_command(CommandDefinition, *command_names, &block)
Loading
Loading
@@ -158,6 +193,7 @@ module Gitlab
description: @description,
warning: @warning,
explanation: @explanation,
execution_message: @execution_message,
params: @params,
condition_block: @condition_block,
parse_params_block: @parse_params_block,
Loading
Loading
@@ -173,6 +209,7 @@ module Gitlab
 
@description = nil
@explanation = nil
@execution_message = nil
@params = nil
@condition_block = nil
@warning = nil
Loading
Loading
Loading
Loading
@@ -12,10 +12,16 @@ module Gitlab
included do
# Issue, MergeRequest, Epic: quick actions definitions
desc do
"Close this #{quick_action_target.to_ability_name.humanize(capitalize: false)}"
_('Close this %{quick_action_target}') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
explanation do
"Closes this #{quick_action_target.to_ability_name.humanize(capitalize: false)}."
_('Closes this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
execution_message do
_('Closed this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
types Issuable
condition do
Loading
Loading
@@ -28,10 +34,16 @@ module Gitlab
end
 
desc do
"Reopen this #{quick_action_target.to_ability_name.humanize(capitalize: false)}"
_('Reopen this %{quick_action_target}') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
explanation do
"Reopens this #{quick_action_target.to_ability_name.humanize(capitalize: false)}."
_('Reopens this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
execution_message do
_('Reopened this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
types Issuable
condition do
Loading
Loading
@@ -45,7 +57,10 @@ module Gitlab
 
desc _('Change title')
explanation do |title_param|
_("Changes the title to \"%{title_param}\".") % { title_param: title_param }
_('Changes the title to "%{title_param}".') % { title_param: title_param }
end
execution_message do |title_param|
_('Changed the title to "%{title_param}".') % { title_param: title_param }
end
params '<New title>'
types Issuable
Loading
Loading
@@ -61,7 +76,10 @@ module Gitlab
explanation do |labels_param|
labels = find_label_references(labels_param)
 
"Adds #{labels.join(' ')} #{'label'.pluralize(labels.count)}." if labels.any?
if labels.any?
_("Adds %{labels} %{label_text}.") %
{ labels: labels.join(' '), label_text: 'label'.pluralize(labels.count) }
end
end
params '~label1 ~"label 2"'
types Issuable
Loading
Loading
@@ -71,21 +89,15 @@ module Gitlab
find_labels.any?
end
command :label do |labels_param|
label_ids = find_label_ids(labels_param)
if label_ids.any?
@updates[:add_label_ids] ||= []
@updates[:add_label_ids] += label_ids
@updates[:add_label_ids].uniq!
end
run_label_command(labels: find_labels(labels_param), command: :label, updates_key: :add_label_ids)
end
 
desc _('Remove all or specific label(s)')
explanation do |labels_param = nil|
if labels_param.present?
labels = find_label_references(labels_param)
"Removes #{labels.join(' ')} #{'label'.pluralize(labels.count)}." if labels.any?
label_references = labels_param.present? ? find_label_references(labels_param) : []
if label_references.any?
_("Removes %{label_references} %{label_text}.") %
{ label_references: label_references.join(' '), label_text: 'label'.pluralize(label_references.count) }
else
_('Removes all labels.')
end
Loading
Loading
@@ -99,7 +111,9 @@ module Gitlab
end
command :unlabel do |labels_param = nil|
if labels_param.present?
label_ids = find_label_ids(labels_param)
labels = find_labels(labels_param)
label_ids = labels.map(&:id)
label_references = labels_to_reference(labels, :name)
 
if label_ids.any?
@updates[:remove_label_ids] ||= []
Loading
Loading
@@ -109,7 +123,10 @@ module Gitlab
end
else
@updates[:label_ids] = []
label_references = []
end
@execution_message[:unlabel] = remove_label_message(label_references)
end
 
desc _('Replace all label(s)')
Loading
Loading
@@ -125,18 +142,12 @@ module Gitlab
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", parent)
end
command :relabel do |labels_param|
label_ids = find_label_ids(labels_param)
if label_ids.any?
@updates[:label_ids] ||= []
@updates[:label_ids] += label_ids
@updates[:label_ids].uniq!
end
run_label_command(labels: find_labels(labels_param), command: :relabel, updates_key: :label_ids)
end
 
desc _('Add a todo')
explanation _('Adds a todo.')
execution_message _('Added a todo.')
types Issuable
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -148,6 +159,7 @@ module Gitlab
 
desc _('Mark to do as done')
explanation _('Marks to do as done.')
execution_message _('Marked to do as done.')
types Issuable
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -159,7 +171,12 @@ module Gitlab
 
desc _('Subscribe')
explanation do
"Subscribes to this #{quick_action_target.to_ability_name.humanize(capitalize: false)}."
_('Subscribes to this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
execution_message do
_('Subscribed to this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
types Issuable
condition do
Loading
Loading
@@ -172,7 +189,12 @@ module Gitlab
 
desc _('Unsubscribe')
explanation do
"Unsubscribes from this #{quick_action_target.to_ability_name.humanize(capitalize: false)}."
_('Unsubscribes from this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
execution_message do
_('Unsubscribed from this %{quick_action_target}.') %
{ quick_action_target: quick_action_target.to_ability_name.humanize(capitalize: false) }
end
types Issuable
condition do
Loading
Loading
@@ -187,6 +209,9 @@ module Gitlab
explanation do |name|
_("Toggles :%{name}: emoji award.") % { name: name } if name
end
execution_message do |name|
_("Toggled :%{name}: emoji award.") % { name: name } if name
end
params ':emoji:'
types Issuable
condition do
Loading
Loading
@@ -215,6 +240,41 @@ module Gitlab
substitution :tableflip do |comment|
"#{comment} #{TABLEFLIP}"
end
private
def run_label_command(labels:, command:, updates_key:)
return if labels.empty?
@updates[updates_key] ||= []
@updates[updates_key] += labels.map(&:id)
@updates[updates_key].uniq!
label_references = labels_to_reference(labels, :name)
@execution_message[command] = case command
when :relabel
_('Replaced all labels with %{label_references} %{label_text}.') %
{
label_references: label_references.join(' '),
label_text: 'label'.pluralize(label_references.count)
}
when :label
_('Added %{label_references} %{label_text}.') %
{
label_references: label_references.join(' '),
label_text: 'label'.pluralize(labels.count)
}
end
end
def remove_label_message(label_references)
if label_references.any?
_("Removed %{label_references} %{label_text}.") %
{ label_references: label_references.join(' '), label_text: 'label'.pluralize(label_references.count) }
else
_('Removed all labels.')
end
end
end
end
end
Loading
Loading
Loading
Loading
@@ -12,6 +12,9 @@ module Gitlab
explanation do |due_date|
_("Sets the due date to %{due_date}.") % { due_date: due_date.strftime('%b %-d, %Y') } if due_date
end
execution_message do |due_date|
_("Set the due date to %{due_date}.") % { due_date: due_date.strftime('%b %-d, %Y') } if due_date
end
params '<in 2 days | this Friday | December 31st>'
types Issue
condition do
Loading
Loading
@@ -27,6 +30,7 @@ module Gitlab
 
desc _('Remove due date')
explanation _('Removes the due date.')
execution_message _('Removed the due date.')
types Issue
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -49,22 +53,27 @@ module Gitlab
current_user.can?(:"update_#{quick_action_target.to_ability_name}", quick_action_target) &&
quick_action_target.project.boards.count == 1
end
# rubocop: disable CodeReuse/ActiveRecord
command :board_move do |target_list_name|
label_ids = find_label_ids(target_list_name)
labels = find_labels(target_list_name)
label_ids = labels.map(&:id)
 
if label_ids.size == 1
label_id = label_ids.first
 
# Ensure this label corresponds to a list on the board
next unless Label.on_project_boards(quick_action_target.project_id).where(id: label_id).exists?
next unless Label.on_project_board?(quick_action_target.project_id, label_id)
 
@updates[:remove_label_ids] =
quick_action_target.labels.on_project_boards(quick_action_target.project_id).where.not(id: label_id).pluck(:id)
quick_action_target.labels.on_project_boards(quick_action_target.project_id).where.not(id: label_id).pluck(:id) # rubocop: disable CodeReuse/ActiveRecord
@updates[:add_label_ids] = [label_id]
message = _("Moved issue to %{label} column in the board.") % { label: labels_to_reference(labels).first }
else
message = _('Move this issue failed because you need to specify only one label.')
end
@execution_message[:board_move] = message
end
# rubocop: enable CodeReuse/ActiveRecord
 
desc _('Mark this issue as a duplicate of another issue')
explanation do |duplicate_reference|
Loading
Loading
@@ -81,7 +90,13 @@ module Gitlab
 
if canonical_issue.present?
@updates[:canonical_issue_id] = canonical_issue.id
message = _("Marked this issue as a duplicate of %{duplicate_param}.") % { duplicate_param: duplicate_param }
else
message = _('Mark as duplicate failed because referenced issue was not found')
end
@execution_message[:duplicate] = message
end
 
desc _('Move this issue to another project.')
Loading
Loading
@@ -99,13 +114,22 @@ module Gitlab
 
if target_project.present?
@updates[:target_project] = target_project
message = _("Moved this issue to %{path_to_project}.") % { path_to_project: target_project_path }
else
message = _("Move this issue failed because target project doesn't exists")
end
@execution_message[:move] = message
end
 
desc _('Make issue confidential.')
explanation do
_('Makes this issue confidential')
end
execution_message do
_('Made this issue confidential')
end
types Issue
condition do
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
Loading
Loading
@@ -119,7 +143,14 @@ module Gitlab
if branch_name
_("Creates branch '%{branch_name}' and a merge request to resolve this issue") % { branch_name: branch_name }
else
"Creates a branch and a merge request to resolve this issue"
_('Creates a branch and a merge request to resolve this issue')
end
end
execution_message do |branch_name = nil|
if branch_name
_("Created branch '%{branch_name}' and a merge request to resolve this issue") % { branch_name: branch_name }
else
_('Created a branch and a merge request to resolve this issue')
end
end
params "<branch name>"
Loading
Loading
Loading
Loading
@@ -9,12 +9,9 @@ module Gitlab
included do
# Issue, MergeRequest: quick actions definitions
desc _('Assign')
# rubocop: disable CodeReuse/ActiveRecord
explanation do |users|
users = quick_action_target.allows_multiple_assignees? ? users : users.take(1)
"Assigns #{users.map(&:to_reference).to_sentence}."
_('Assigns %{assignee_users_sentence}.') % { assignee_users_sentence: assignee_users_sentence(users) }
end
# rubocop: enable CodeReuse/ActiveRecord
params do
quick_action_target.allows_multiple_assignees? ? '@user1 @user2' : '@user'
end
Loading
Loading
@@ -26,7 +23,10 @@ module Gitlab
extract_users(assignee_param)
end
command :assign do |users|
next if users.empty?
if users.empty?
@execution_message[:assign] = _("Assign command failed because no user was found")
next
end
 
if quick_action_target.allows_multiple_assignees?
@updates[:assignee_ids] ||= quick_action_target.assignees.map(&:id)
Loading
Loading
@@ -34,6 +34,8 @@ module Gitlab
else
@updates[:assignee_ids] = [users.first.id]
end
@execution_message[:assign] = _('Assigned %{assignee_users_sentence}.') % { assignee_users_sentence: assignee_users_sentence(users) }
end
 
desc do
Loading
Loading
@@ -44,9 +46,14 @@ module Gitlab
end
end
explanation do |users = nil|
assignees = quick_action_target.assignees
assignees &= users if users.present? && quick_action_target.allows_multiple_assignees?
"Removes #{'assignee'.pluralize(assignees.size)} #{assignees.map(&:to_reference).to_sentence}."
assignees = assignees_for_removal(users)
_("Removes %{assignee_text} %{assignee_references}.") %
{ assignee_text: 'assignee'.pluralize(assignees.size), assignee_references: assignees.map(&:to_reference).to_sentence }
end
execution_message do |users = nil|
assignees = assignees_for_removal(users)
_("Removed %{assignee_text} %{assignee_references}.") %
{ assignee_text: 'assignee'.pluralize(assignees.size), assignee_references: assignees.map(&:to_reference).to_sentence }
end
params do
quick_action_target.allows_multiple_assignees? ? '@user1 @user2' : ''
Loading
Loading
@@ -74,6 +81,9 @@ module Gitlab
explanation do |milestone|
_("Sets the milestone to %{milestone_reference}.") % { milestone_reference: milestone.to_reference } if milestone
end
execution_message do |milestone|
_("Set the milestone to %{milestone_reference}.") % { milestone_reference: milestone.to_reference } if milestone
end
params '%"milestone"'
types Issue, MergeRequest
condition do
Loading
Loading
@@ -92,6 +102,9 @@ module Gitlab
explanation do
_("Removes %{milestone_reference} milestone.") % { milestone_reference: quick_action_target.milestone.to_reference(format: :name) }
end
execution_message do
_("Removed %{milestone_reference} milestone.") % { milestone_reference: quick_action_target.milestone.to_reference(format: :name) }
end
types Issue, MergeRequest
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -116,17 +129,22 @@ module Gitlab
extract_references(issuable_param, :merge_request).first
end
command :copy_metadata do |source_issuable|
if source_issuable.present? && source_issuable.project.id == quick_action_target.project.id
if can_copy_metadata?(source_issuable)
@updates[:add_label_ids] = source_issuable.labels.map(&:id)
@updates[:milestone_id] = source_issuable.milestone.id if source_issuable.milestone
@execution_message[:copy_metadata] = _("Copied labels and milestone from %{source_issuable_reference}.") % { source_issuable_reference: source_issuable.to_reference }
end
end
 
desc _('Set time estimate')
explanation do |time_estimate|
time_estimate = Gitlab::TimeTrackingFormatter.output(time_estimate)
_("Sets time estimate to %{time_estimate}.") % { time_estimate: time_estimate } if time_estimate
formatted_time_estimate = format_time_estimate(time_estimate)
_("Sets time estimate to %{time_estimate}.") % { time_estimate: formatted_time_estimate } if formatted_time_estimate
end
execution_message do |time_estimate|
formatted_time_estimate = format_time_estimate(time_estimate)
_("Set time estimate to %{time_estimate}.") % { time_estimate: formatted_time_estimate } if formatted_time_estimate
end
params '<1w 3d 2h 14m>'
types Issue, MergeRequest
Loading
Loading
@@ -144,18 +162,12 @@ module Gitlab
 
desc _('Add or subtract spent time')
explanation do |time_spent, time_spent_date|
if time_spent
if time_spent > 0
verb = _('Adds')
value = time_spent
else
verb = _('Subtracts')
value = -time_spent
end
_("%{verb} %{time_spent_value} spent time.") % { verb: verb, time_spent_value: Gitlab::TimeTrackingFormatter.output(value) }
end
spend_time_message(time_spent, time_spent_date, false)
end
execution_message do |time_spent, time_spent_date|
spend_time_message(time_spent, time_spent_date, true)
end
params '<time(1h30m | -1h30m)> <date(YYYY-MM-DD)>'
types Issue, MergeRequest
condition do
Loading
Loading
@@ -176,6 +188,7 @@ module Gitlab
 
desc _('Remove time estimate')
explanation _('Removes time estimate.')
execution_message _('Removed time estimate.')
types Issue, MergeRequest
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -187,6 +200,7 @@ module Gitlab
 
desc _('Remove spent time')
explanation _('Removes spent time.')
execution_message _('Removed spent time.')
condition do
quick_action_target.persisted? &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", project)
Loading
Loading
@@ -198,6 +212,7 @@ module Gitlab
 
desc _("Lock the discussion")
explanation _("Locks the discussion")
execution_message _("Locked the discussion")
types Issue, MergeRequest
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -210,6 +225,7 @@ module Gitlab
 
desc _("Unlock the discussion")
explanation _("Unlocks the discussion")
execution_message _("Unlocked the discussion")
types Issue, MergeRequest
condition do
quick_action_target.persisted? &&
Loading
Loading
@@ -219,6 +235,47 @@ module Gitlab
command :unlock do
@updates[:discussion_locked] = false
end
private
def assignee_users_sentence(users)
if quick_action_target.allows_multiple_assignees?
users
else
[users.first]
end.map(&:to_reference).to_sentence
end
def assignees_for_removal(users)
assignees = quick_action_target.assignees
if users.present? && quick_action_target.allows_multiple_assignees?
assignees & users
else
assignees
end
end
def can_copy_metadata?(source_issuable)
source_issuable.present? && source_issuable.project_id == quick_action_target.project_id
end
def format_time_estimate(time_estimate)
Gitlab::TimeTrackingFormatter.output(time_estimate)
end
def spend_time_message(time_spent, time_spent_date, paste_tense)
return unless time_spent
if time_spent > 0
verb = paste_tense ? _('Added') : _('Adds')
value = time_spent
else
verb = paste_tense ? _('Subtracted') : _('Subtracts')
value = -time_spent
end
_("%{verb} %{time_spent_value} spent time.") % { verb: verb, time_spent_value: format_time_estimate(value) }
end
end
end
end
Loading
Loading
Loading
Loading
@@ -8,8 +8,9 @@ module Gitlab
 
included do
# MergeRequest only quick actions definitions
desc 'Merge (when the pipeline succeeds)'
explanation 'Merges this merge request when the pipeline succeeds.'
desc _('Merge (when the pipeline succeeds)')
explanation _('Merges this merge request when the pipeline succeeds.')
execution_message _('Scheduled to merge this merge request when the pipeline succeeds.')
types MergeRequest
condition do
last_diff_sha = params && params[:merge_request_diff_head_sha]
Loading
Loading
@@ -22,10 +23,22 @@ module Gitlab
 
desc 'Toggle the Work In Progress status'
explanation do
verb = quick_action_target.work_in_progress? ? 'Unmarks' : 'Marks'
noun = quick_action_target.to_ability_name.humanize(capitalize: false)
"#{verb} this #{noun} as Work In Progress."
if quick_action_target.work_in_progress?
_("Unmarks this %{noun} as Work In Progress.")
else
_("Marks this %{noun} as Work In Progress.")
end % { noun: noun }
end
execution_message do
noun = quick_action_target.to_ability_name.humanize(capitalize: false)
if quick_action_target.work_in_progress?
_("Unmarked this %{noun} as Work In Progress.")
else
_("Marked this %{noun} as Work In Progress.")
end % { noun: noun }
end
types MergeRequest
condition do
quick_action_target.respond_to?(:work_in_progress?) &&
Loading
Loading
@@ -36,9 +49,12 @@ module Gitlab
@updates[:wip_event] = quick_action_target.work_in_progress? ? 'unwip' : 'wip'
end
 
desc 'Set target branch'
desc _('Set target branch')
explanation do |branch_name|
"Sets target branch to #{branch_name}."
_('Sets target branch to %{branch_name}.') % { branch_name: branch_name }
end
execution_message do |branch_name|
_('Set target branch to %{branch_name}.') % { branch_name: branch_name }
end
params '<Local branch name>'
types MergeRequest
Loading
Loading
Loading
Loading
@@ -735,6 +735,15 @@ msgstr ""
msgid "AddMember|Too many users specified (limit is %{user_limit})"
msgstr ""
 
msgid "Added"
msgstr ""
msgid "Added %{label_references} %{label_text}."
msgstr ""
msgid "Added a todo."
msgstr ""
msgid "Added at"
msgstr ""
 
Loading
Loading
@@ -744,6 +753,9 @@ msgstr ""
msgid "Adds"
msgstr ""
 
msgid "Adds %{labels} %{label_text}."
msgstr ""
msgid "Adds a todo."
msgstr ""
 
Loading
Loading
@@ -1332,6 +1344,9 @@ msgstr ""
msgid "Assign"
msgstr ""
 
msgid "Assign command failed because no user was found"
msgstr ""
msgid "Assign custom color like #FF0000"
msgstr ""
 
Loading
Loading
@@ -1353,6 +1368,9 @@ msgstr ""
msgid "Assign yourself to this issue"
msgstr ""
 
msgid "Assigned %{assignee_users_sentence}."
msgstr ""
msgid "Assigned Issues"
msgstr ""
 
Loading
Loading
@@ -1370,6 +1388,9 @@ msgstr[1] ""
msgid "Assignee(s)"
msgstr ""
 
msgid "Assigns %{assignee_users_sentence}."
msgstr ""
msgid "Attach a file"
msgstr ""
 
Loading
Loading
@@ -2005,6 +2026,9 @@ msgstr ""
msgid "ChangeTypeAction|This will create a new commit in order to revert the existing changes."
msgstr ""
 
msgid "Changed the title to \"%{title_param}\"."
msgstr ""
msgid "Changes"
msgstr ""
 
Loading
Loading
@@ -2341,9 +2365,18 @@ msgstr ""
msgid "Close sidebar"
msgstr ""
 
msgid "Close this %{quick_action_target}"
msgstr ""
msgid "Closed"
msgstr ""
 
msgid "Closed this %{quick_action_target}."
msgstr ""
msgid "Closes this %{quick_action_target}."
msgstr ""
msgid "ClusterIntegration| %{custom_domain_start}More information%{custom_domain_end}."
msgstr ""
 
Loading
Loading
@@ -2887,6 +2920,9 @@ msgstr ""
msgid "Commands applied"
msgstr ""
 
msgid "Commands did not apply"
msgstr ""
msgid "Comment"
msgstr ""
 
Loading
Loading
@@ -3186,6 +3222,9 @@ msgstr ""
msgid "Copied"
msgstr ""
 
msgid "Copied labels and milestone from %{source_issuable_reference}."
msgstr ""
msgid "Copy %{http_label} clone URL"
msgstr ""
 
Loading
Loading
@@ -3396,6 +3435,12 @@ msgstr ""
msgid "Created At"
msgstr ""
 
msgid "Created a branch and a merge request to resolve this issue"
msgstr ""
msgid "Created branch '%{branch_name}' and a merge request to resolve this issue"
msgstr ""
msgid "Created by me"
msgstr ""
 
Loading
Loading
@@ -3405,6 +3450,9 @@ msgstr ""
msgid "Created on:"
msgstr ""
 
msgid "Creates a branch and a merge request to resolve this issue"
msgstr ""
msgid "Creates branch '%{branch_name}' and a merge request to resolve this issue"
msgstr ""
 
Loading
Loading
@@ -6337,6 +6385,9 @@ msgstr ""
msgid "Locked by %{fileLockUserName}"
msgstr ""
 
msgid "Locked the discussion"
msgstr ""
msgid "Locked to current projects"
msgstr ""
 
Loading
Loading
@@ -6355,6 +6406,9 @@ msgstr ""
msgid "MRDiff|Show full file"
msgstr ""
 
msgid "Made this issue confidential"
msgstr ""
msgid "Make and review changes in the browser with the Web IDE"
msgstr ""
 
Loading
Loading
@@ -6436,6 +6490,9 @@ msgstr ""
msgid "Mark as done"
msgstr ""
 
msgid "Mark as duplicate failed because referenced issue was not found"
msgstr ""
msgid "Mark as resolved"
msgstr ""
 
Loading
Loading
@@ -6454,6 +6511,18 @@ msgstr ""
msgid "Markdown enabled"
msgstr ""
 
msgid "Marked this %{noun} as Work In Progress."
msgstr ""
msgid "Marked this issue as a duplicate of %{duplicate_param}."
msgstr ""
msgid "Marked to do as done."
msgstr ""
msgid "Marks this %{noun} as Work In Progress."
msgstr ""
msgid "Marks this issue as a duplicate of %{duplicate_reference}."
msgstr ""
 
Loading
Loading
@@ -6499,6 +6568,9 @@ msgstr ""
msgid "Merge"
msgstr ""
 
msgid "Merge (when the pipeline succeeds)"
msgstr ""
msgid "Merge Request"
msgstr ""
 
Loading
Loading
@@ -6622,6 +6694,9 @@ msgstr ""
msgid "Merged branches are being deleted. This can take some time depending on the number of branches. Please refresh the page to see changes."
msgstr ""
 
msgid "Merges this merge request when the pipeline succeeds."
msgstr ""
msgid "Messages"
msgstr ""
 
Loading
Loading
@@ -6805,6 +6880,12 @@ msgstr ""
msgid "Move issue from one column of the board to another"
msgstr ""
 
msgid "Move this issue failed because target project doesn't exists"
msgstr ""
msgid "Move this issue failed because you need to specify only one label."
msgstr ""
msgid "Move this issue to another project."
msgstr ""
 
Loading
Loading
@@ -6814,6 +6895,12 @@ msgstr ""
msgid "MoveIssue|Cannot move issue to project it originates from!"
msgstr ""
 
msgid "Moved issue to %{label} column in the board."
msgstr ""
msgid "Moved this issue to %{path_to_project}."
msgstr ""
msgid "Moves issue to %{label} column in the board."
msgstr ""
 
Loading
Loading
@@ -8986,12 +9073,39 @@ msgstr ""
msgid "Remove time estimate"
msgstr ""
 
msgid "Removed %{assignee_text} %{assignee_references}."
msgstr ""
msgid "Removed %{label_references} %{label_text}."
msgstr ""
msgid "Removed %{milestone_reference} milestone."
msgstr ""
msgid "Removed all labels."
msgstr ""
msgid "Removed group can not be restored!"
msgstr ""
 
msgid "Removed projects cannot be restored!"
msgstr ""
 
msgid "Removed spent time."
msgstr ""
msgid "Removed the due date."
msgstr ""
msgid "Removed time estimate."
msgstr ""
msgid "Removes %{assignee_text} %{assignee_references}."
msgstr ""
msgid "Removes %{label_references} %{label_text}."
msgstr ""
msgid "Removes %{milestone_reference} milestone."
msgstr ""
 
Loading
Loading
@@ -9025,12 +9139,24 @@ msgstr ""
msgid "Reopen milestone"
msgstr ""
 
msgid "Reopen this %{quick_action_target}"
msgstr ""
msgid "Reopened this %{quick_action_target}."
msgstr ""
msgid "Reopens this %{quick_action_target}."
msgstr ""
msgid "Replace"
msgstr ""
 
msgid "Replace all label(s)"
msgstr ""
 
msgid "Replaced all labels with %{label_references} %{label_text}."
msgstr ""
msgid "Reply by email"
msgstr ""
 
Loading
Loading
@@ -9369,6 +9495,9 @@ msgstr ""
msgid "Scheduled"
msgstr ""
 
msgid "Scheduled to merge this merge request when the pipeline succeeds."
msgstr ""
msgid "Schedules"
msgstr ""
 
Loading
Loading
@@ -9696,18 +9825,33 @@ msgstr ""
msgid "Set requirements for a user to sign-in. Enable mandatory two-factor authentication."
msgstr ""
 
msgid "Set target branch"
msgstr ""
msgid "Set target branch to %{branch_name}."
msgstr ""
msgid "Set the default expiration time for each job's artifacts. 0 for unlimited. The default unit is in seconds, but you can define an alternative. For example: <code>4 mins 2 sec</code>, <code>2h42min</code>."
msgstr ""
 
msgid "Set the due date to %{due_date}."
msgstr ""
msgid "Set the duration for which the jobs will be considered as old and expired. Once that time passes, the jobs will be archived and no longer able to be retried. Make it empty to never expire jobs. It has to be no less than 1 day, for example: <code>15 days</code>, <code>1 month</code>, <code>2 years</code>."
msgstr ""
 
msgid "Set the maximum file size for each job's artifacts"
msgstr ""
 
msgid "Set the milestone to %{milestone_reference}."
msgstr ""
msgid "Set time estimate"
msgstr ""
 
msgid "Set time estimate to %{time_estimate}."
msgstr ""
msgid "Set up CI/CD"
msgstr ""
 
Loading
Loading
@@ -9753,6 +9897,9 @@ msgstr ""
msgid "SetStatusModal|What's your status?"
msgstr ""
 
msgid "Sets target branch to %{branch_name}."
msgstr ""
msgid "Sets the due date to %{due_date}."
msgstr ""
 
Loading
Loading
@@ -10334,9 +10481,18 @@ msgstr ""
msgid "Subscribed"
msgstr ""
 
msgid "Subscribed to this %{quick_action_target}."
msgstr ""
msgid "Subscribes to this %{quick_action_target}."
msgstr ""
msgid "Subscription"
msgstr ""
 
msgid "Subtracted"
msgstr ""
msgid "Subtracts"
msgstr ""
 
Loading
Loading
@@ -10484,6 +10640,12 @@ msgstr ""
msgid "Tag this commit."
msgstr ""
 
msgid "Tagged this commit to %{tag_name} with \"%{message}\"."
msgstr ""
msgid "Tagged this commit to %{tag_name}."
msgstr ""
msgid "Tags"
msgstr ""
 
Loading
Loading
@@ -11543,6 +11705,9 @@ msgstr ""
msgid "ToggleButton|Toggle Status: ON"
msgstr ""
 
msgid "Toggled :%{name}: emoji award."
msgstr ""
msgid "Toggles :%{name}: emoji award."
msgstr ""
 
Loading
Loading
@@ -11750,9 +11915,18 @@ msgstr ""
msgid "Unlocked"
msgstr ""
 
msgid "Unlocked the discussion"
msgstr ""
msgid "Unlocks the discussion"
msgstr ""
 
msgid "Unmarked this %{noun} as Work In Progress."
msgstr ""
msgid "Unmarks this %{noun} as Work In Progress."
msgstr ""
msgid "Unresolve discussion"
msgstr ""
 
Loading
Loading
@@ -11795,6 +11969,12 @@ msgstr ""
msgid "Unsubscribe from %{type}"
msgstr ""
 
msgid "Unsubscribed from this %{quick_action_target}."
msgstr ""
msgid "Unsubscribes from this %{quick_action_target}."
msgstr ""
msgid "Until"
msgstr ""
 
Loading
Loading
Loading
Loading
@@ -219,6 +219,52 @@ describe Gitlab::QuickActions::CommandDefinition do
end
end
 
describe "#execute_message" do
context "when the command is a noop" do
it 'returns nil' do
expect(subject.execute_message({}, nil)).to be_nil
end
end
context "when the command is not a noop" do
before do
subject.action_block = proc { self.run = true }
end
context "when the command is not available" do
before do
subject.condition_block = proc { false }
end
it 'returns nil' do
expect(subject.execute_message({}, nil)).to be_nil
end
end
context "when the command is available" do
context 'when the execution_message is a static string' do
before do
subject.execution_message = 'Assigned jacopo'
end
it 'returns this static string' do
expect(subject.execute_message({}, nil)).to eq('Assigned jacopo')
end
end
context 'when the explanation is dynamic' do
before do
subject.execution_message = proc { |arg| "Assigned #{arg}" }
end
it 'invokes the proc' do
expect(subject.execute_message({}, 'Jacopo')).to eq('Assigned Jacopo')
end
end
end
end
end
describe '#explain' do
context 'when the command is not available' do
before do
Loading
Loading
Loading
Loading
@@ -20,6 +20,9 @@ describe Gitlab::QuickActions::Dsl do
desc do
"A dynamic description for #{noteable.upcase}"
end
execution_message do |arg|
"A dynamic execution message for #{noteable.upcase} passing #{arg}"
end
params 'The first argument', 'The second argument'
command :dynamic_description do |args|
args.split
Loading
Loading
@@ -30,6 +33,7 @@ describe Gitlab::QuickActions::Dsl do
explanation do |arg|
"Action does something with #{arg}"
end
execution_message 'Command applied correctly'
condition do
project == 'foo'
end
Loading
Loading
@@ -67,6 +71,7 @@ describe Gitlab::QuickActions::Dsl do
expect(no_args_def.aliases).to eq([:none])
expect(no_args_def.description).to eq('A command with no args')
expect(no_args_def.explanation).to eq('')
expect(no_args_def.execution_message).to eq('')
expect(no_args_def.params).to eq([])
expect(no_args_def.condition_block).to be_nil
expect(no_args_def.types).to eq([])
Loading
Loading
@@ -78,6 +83,8 @@ describe Gitlab::QuickActions::Dsl do
expect(explanation_with_aliases_def.aliases).to eq([:once, :first])
expect(explanation_with_aliases_def.description).to eq('')
expect(explanation_with_aliases_def.explanation).to eq('Static explanation')
expect(explanation_with_aliases_def.execution_message).to eq('')
expect(no_args_def.params).to eq([])
expect(explanation_with_aliases_def.params).to eq(['The first argument'])
expect(explanation_with_aliases_def.condition_block).to be_nil
expect(explanation_with_aliases_def.types).to eq([])
Loading
Loading
@@ -88,7 +95,7 @@ describe Gitlab::QuickActions::Dsl do
expect(dynamic_description_def.name).to eq(:dynamic_description)
expect(dynamic_description_def.aliases).to eq([])
expect(dynamic_description_def.to_h(OpenStruct.new(noteable: 'issue'))[:description]).to eq('A dynamic description for ISSUE')
expect(dynamic_description_def.explanation).to eq('')
expect(dynamic_description_def.execute_message(OpenStruct.new(noteable: 'issue'), 'arg')).to eq('A dynamic execution message for ISSUE passing arg')
expect(dynamic_description_def.params).to eq(['The first argument', 'The second argument'])
expect(dynamic_description_def.condition_block).to be_nil
expect(dynamic_description_def.types).to eq([])
Loading
Loading
@@ -100,6 +107,7 @@ describe Gitlab::QuickActions::Dsl do
expect(cc_def.aliases).to eq([])
expect(cc_def.description).to eq('')
expect(cc_def.explanation).to eq('')
expect(cc_def.execution_message).to eq('')
expect(cc_def.params).to eq([])
expect(cc_def.condition_block).to be_nil
expect(cc_def.types).to eq([])
Loading
Loading
@@ -111,6 +119,7 @@ describe Gitlab::QuickActions::Dsl do
expect(cond_action_def.aliases).to eq([])
expect(cond_action_def.description).to eq('')
expect(cond_action_def.explanation).to be_a_kind_of(Proc)
expect(cond_action_def.execution_message).to eq('Command applied correctly')
expect(cond_action_def.params).to eq([])
expect(cond_action_def.condition_block).to be_a_kind_of(Proc)
expect(cond_action_def.types).to eq([])
Loading
Loading
@@ -122,6 +131,7 @@ describe Gitlab::QuickActions::Dsl do
expect(with_params_parsing_def.aliases).to eq([])
expect(with_params_parsing_def.description).to eq('')
expect(with_params_parsing_def.explanation).to eq('')
expect(with_params_parsing_def.execution_message).to eq('')
expect(with_params_parsing_def.params).to eq([])
expect(with_params_parsing_def.condition_block).to be_nil
expect(with_params_parsing_def.types).to eq([])
Loading
Loading
@@ -133,6 +143,7 @@ describe Gitlab::QuickActions::Dsl do
expect(substitution_def.aliases).to eq([])
expect(substitution_def.description).to eq('')
expect(substitution_def.explanation).to eq('')
expect(substitution_def.execution_message).to eq('')
expect(substitution_def.params).to eq(['<Comment>'])
expect(substitution_def.condition_block).to be_nil
expect(substitution_def.types).to eq([])
Loading
Loading
@@ -144,6 +155,7 @@ describe Gitlab::QuickActions::Dsl do
expect(has_types.aliases).to eq([])
expect(has_types.description).to eq('A command with types')
expect(has_types.explanation).to eq('')
expect(has_types.execution_message).to eq('')
expect(has_types.params).to eq([])
expect(has_types.condition_block).to be_nil
expect(has_types.types).to eq([Issue, Commit])
Loading
Loading
This diff is collapsed.
Loading
Loading
@@ -5,7 +5,7 @@ shared_examples 'tag quick action' do
it 'tags this commit' do
add_note("/tag #{tag_name} #{tag_message}")
 
expect(page).to have_content 'Commands applied'
expect(page).to have_content %{Tagged this commit to #{tag_name} with "#{tag_message}".}
expect(page).to have_content "tagged commit #{truncated_commit_sha}"
expect(page).to have_content tag_name
 
Loading
Loading
Loading
Loading
@@ -68,7 +68,7 @@ shared_examples 'close quick action' do |issuable_type|
it "does not close the #{issuable_type}" do
add_note('/close')
 
expect(page).not_to have_content 'Commands applied'
expect(page).not_to have_content "Closed this #{issuable.to_ability_name.humanize(capitalize: false)}."
expect(issuable).to be_open
end
end
Loading
Loading
Loading
Loading
@@ -2,8 +2,14 @@
 
shared_examples 'create_merge_request quick action' do
context 'create a merge request starting from an issue' do
def expect_mr_quickaction(success)
expect(page).to have_content 'Commands applied'
def expect_mr_quickaction(success, branch_name = nil)
command_message = if branch_name
"Created branch '#{branch_name}' and a merge request to resolve this issue"
else
"Created a branch and a merge request to resolve this issue"
end
expect(page).to have_content command_message
 
if success
expect(page).to have_content 'created merge request'
Loading
Loading
@@ -13,19 +19,21 @@ shared_examples 'create_merge_request quick action' do
end
 
it "doesn't create a merge request when the branch name is invalid" do
add_note("/create_merge_request invalid branch name")
branch_name = 'invalid branch name'
add_note("/create_merge_request #{branch_name}")
 
wait_for_requests
 
expect_mr_quickaction(false)
expect_mr_quickaction(false, branch_name)
end
 
it "doesn't create a merge request when a branch with that name already exists" do
add_note("/create_merge_request feature")
branch_name = 'feature'
add_note("/create_merge_request #{branch_name}")
 
wait_for_requests
 
expect_mr_quickaction(false)
expect_mr_quickaction(false, branch_name)
end
 
it 'creates a new merge request using issue iid and title as branch name when the branch name is empty' do
Loading
Loading
@@ -46,7 +54,7 @@ shared_examples 'create_merge_request quick action' do
branch_name = '1-feature'
add_note("/create_merge_request #{branch_name}")
 
expect_mr_quickaction(true)
expect_mr_quickaction(true, branch_name)
 
created_mr = project.merge_requests.last
expect(created_mr.source_branch).to eq(branch_name)
Loading
Loading
Loading
Loading
@@ -9,7 +9,6 @@ shared_examples 'duplicate quick action' do
add_note("/duplicate ##{original_issue.to_reference}")
 
expect(page).not_to have_content "/duplicate #{original_issue.to_reference}"
expect(page).to have_content 'Commands applied'
expect(page).to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
 
expect(issue.reload).to be_closed
Loading
Loading
@@ -28,7 +27,6 @@ shared_examples 'duplicate quick action' do
it 'does not create a note, and does not mark the issue as a duplicate' do
add_note("/duplicate ##{original_issue.to_reference}")
 
expect(page).not_to have_content 'Commands applied'
expect(page).not_to have_content "marked this issue as a duplicate of #{original_issue.to_reference}"
 
expect(issue.reload).to be_open
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment