Commit 7691d182 authored by Pedro Pombeiro's avatar Pedro Pombeiro Committed by Natalia Tepluhina

Add /health_status, /clear_health_status quick actions

parent 8bb0cff3
......@@ -639,6 +639,9 @@ You can then see the issue's status in the issues list and the epic tree.
After an issue is closed, its health status can't be edited and the **Edit** button becomes disabled
until the issue is reopened.
You can also set and clear health statuses using the `/health_status` and `/clear_health_status`
[quick actions](../quick_actions.md#issues-merge-requests-and-epics).
## Publish an issue **(ULTIMATE)**
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/30906) in GitLab 13.1.
......
......@@ -48,7 +48,7 @@ threads. Some quick actions might not be available to all subscription tiers.
<!-- Keep this table sorted alphabetically -->
| Command | Issue | Merge request | Epic | Action |
|:--------------------------------------|:-----------------------|:-----------------------|:-----------------------|:-------|
|:-------------------------------------------------------------------------------------------------|:-----------------------|:-----------------------|:-----------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `/add_contacts email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add one or more [CRM contacts](../crm/index.md) ([introduced in GitLab 14.6](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73413)). |
| `/approve` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Approve the merge request. |
| `/assign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Assign one or more users. |
......@@ -57,8 +57,9 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/assign_reviewer me` or `/reviewer me` or `/request_review me` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Assign yourself as a reviewer. |
| `/award :emoji:` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Toggle emoji award. |
| `/child_epic <epic>` | **{dotted-circle}** No | **{dotted-circle}** No | **{check-circle}** Yes | Add child epic to `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic ([introduced in GitLab 12.0](https://gitlab.com/gitlab-org/gitlab/-/issues/7330)). |
| `/clear_health_status` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear [health status](issues/managing_issues.md#health-status) ([introduced in GitLab 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/213814)). |
| `/clear_weight` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clear weight. |
| `/clone <path/to/project> [--with_notes]`| **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument. |
| `/clone <path/to/project> [--with_notes]` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Clone the issue to given project, or the current one if no arguments are given ([introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/9421) in GitLab 13.7). Copies as much data as possible as long as the target project contains equivalent labels, milestones, and so on. Does not copy comments or system notes unless `--with_notes` is provided as an argument. |
| `/close` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Close. |
| `/confidential` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Make confidential. |
| `/copy_metadata <!merge_request>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another merge request in the project. |
......@@ -70,6 +71,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/duplicate <#issue>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Close this issue and mark as a duplicate of another issue. **(FREE)** Also, mark both as related. |
| `/epic <epic>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add to epic `<epic>`. The `<epic>` value should be in the format of `&epic`, `group&epic`, or a URL to an epic. |
| `/estimate <time>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Set time estimate. For example, `/estimate 1mo 2w 3d 4h 5m`. Learn more about [time tracking](time_tracking.md). |
| `/health_status <value>` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set [health status](issues/managing_issues.md#health-status). Valid options for `<value>` are `on_track`, `needs_attention`, and `at_risk` ([introduced in GitLab 14.7](https://gitlab.com/gitlab-org/gitlab/-/issues/213814)). |
| `/invite_email email1 email2` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Add up to six email participants. This action is behind feature flag `issue_email_participants` and is not yet supported in issue templates. |
| `/iteration *iteration:"iteration name"` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Set iteration. For example, to set the `Late in July` iteration: `/iteration *iteration:"Late in July"` ([introduced in GitLab 13.1](https://gitlab.com/gitlab-org/gitlab/-/issues/196795)). |
| `/label ~label1 ~label2` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add one or more labels. Label names can also start without a tilde (`~`), but mixed syntax is not supported. |
......@@ -106,7 +108,7 @@ threads. Some quick actions might not be available to all subscription tiers.
| `/target_branch <local branch name>` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Set target branch. |
| `/title <new title>` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Change title. |
| `/todo` | **{check-circle}** Yes | **{check-circle}** Yes | **{check-circle}** Yes | Add a to-do item. |
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced in GitLab 14.3](https://gitlab.com/gitlab-org/gitlab/-/issues/8103)|
| `/unapprove` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Unapprove the merge request. ([introduced in GitLab 14.3](https://gitlab.com/gitlab-org/gitlab/-/issues/8103) |
| `/unassign @user1 @user2` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific assignees. |
| `/unassign` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove all assignees. |
| `/unassign_reviewer @user1 @user2` or `/remove_reviewer @user1 @user2` | **{dotted-circle}** No | **{check-circle}** Yes | **{dotted-circle}** No | Remove specific reviewers. |
......
---
key_path: redis_hll_counters.quickactions.i_quickactions_clear_health_status_monthly
name: "count_clear_health_status_quick_actions_monthly"
description: Count of MAU using the `/clear_health_status` quick action
product_section: dev
product_stage: plan
product_group: group::project management
product_category: issue_tracking
value_type: number
status: active
milestone: "14.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77215
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_clear_health_status
performance_indicator_type: []
distribution:
- ee
tier:
- ultimate
---
key_path: redis_hll_counters.quickactions.i_quickactions_health_status_monthly
name: "count_health_status_quick_actions_monthly"
description: Count of MAU using the `/health_status` quick action
product_section: dev
product_stage: plan
product_group: group::project management
product_category: issue_tracking
value_type: number
status: active
milestone: "14.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77215
time_frame: 28d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_health_status
performance_indicator_type: []
distribution:
- ee
tier:
- ultimate
---
key_path: redis_hll_counters.quickactions.i_quickactions_health_status_weekly
name: "count_health_status_quick_actions_weekly"
description: Count of WAU using the `/health_status` quick action
product_section: dev
product_stage: plan
product_group: group::project management
product_category: issue_tracking
value_type: number
status: active
milestone: "14.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77215
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_health_status
performance_indicator_type: []
distribution:
- ee
tier:
- ultimate
---
key_path: redis_hll_counters.quickactions.i_quickactions_clear_health_status_weekly
name: "count_clear_health_status_quick_actions_weekly"
description: Count of WAU using the `/clear_health_status` quick action
product_section: dev
product_stage: plan
product_group: group::project management
product_category: issue_tracking
value_type: number
status: active
milestone: "14.7"
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/77215
time_frame: 7d
data_source: redis_hll
data_category: optional
instrumentation_class: RedisHLLMetric
options:
events:
- i_quickactions_clear_health_status
performance_indicator_type: []
distribution:
- ee
tier:
- ultimate
......@@ -136,6 +136,52 @@ module EE
@execution_message[:publish] = _('Failed to publish issue on status page.')
end
end
desc _('Set health status')
explanation do |health_status|
_("Sets health status to %{health_status}.") % { health_status: health_status } if health_status
end
params "<#{::Issue.health_statuses.keys.join('|')}>"
types Issue
condition do
quick_action_target.supports_health_status? &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
end
parse_params do |health_status|
find_health_status(health_status)
end
command :health_status do |health_status|
if health_status
@updates[:health_status] = health_status
@execution_message[:health_status] = _("Set health status to %{health_status}.") % { health_status: health_status }
end
end
desc _('Clear health status')
explanation _('Clears health status.')
execution_message _('Cleared health status.')
types Issue
condition do
quick_action_target.persisted? &&
quick_action_target.supports_health_status? &&
quick_action_target.health_status &&
current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target)
end
command :clear_health_status do
@updates[:health_status] = nil
end
end
private
def find_health_status(health_status_param)
return unless health_status_param
health_status_param = health_status_param.downcase
health_statuses = ::Issue.health_statuses.keys.map(&:downcase)
health_statuses.include?(health_status_param) && health_status_param
end
end
end
......
......@@ -1043,6 +1043,127 @@ RSpec.describe QuickActions::InterpretService do
end
end
shared_examples 'health_status command' do
it 'populates health_status specified by the /health_status command' do
_, updates = service.execute(content, issuable)
expect(updates).to eq(health_status: health_status)
end
end
shared_examples 'clear_health_status command' do
it 'populates health_status: nil if content contains /clear_health_status' do
issuable.update!(health_status: 'on_track')
_, updates = service.execute(content, issuable)
expect(updates).to eq(health_status: nil)
end
end
context 'issuable health statuses licensed' do
let(:issuable) { issue }
before do
stub_licensed_features(issuable_health_status: true)
end
context 'health_status' do
let(:content) { "/health_status #{health_status}" }
it_behaves_like 'health_status command' do
let(:health_status) { 'on_track' }
end
it_behaves_like 'health_status command' do
let(:health_status) { 'at_risk' }
end
context 'when health_status is invalid' do
it 'does not populate health_status' do
content = "/health_status unknown"
_, updates = service.execute(content, issuable)
expect(updates).to be_empty
end
end
context 'when the user does not have enough permissions' do
before do
allow(current_user).to receive(:can?).with(:use_quick_actions).and_return(true)
allow(current_user).to receive(:can?).with(:admin_issue, issuable).and_return(false)
end
it 'returns an error message' do
content = "/health_status on_track"
_, updates, message = service.execute(content, issuable)
expect(updates).to be_empty
expect(message).to eq('Could not apply health_status command.')
end
end
end
context 'clear_health_status' do
it_behaves_like 'clear_health_status command' do
let(:content) { '/clear_health_status' }
end
context 'when the user does not have enough permissions' do
before do
allow(current_user).to receive(:can?).with(:use_quick_actions).and_return(true)
allow(current_user).to receive(:can?).with(:admin_issue, issuable).and_return(false)
end
it 'returns an error message' do
content = "/clear_health_status"
_, updates, message = service.execute(content, issuable)
expect(updates).to be_empty
expect(message).to eq('Could not apply clear_health_status command.')
end
end
end
end
context 'issuable health_status unlicensed' do
before do
stub_licensed_features(issuable_health_status: false)
end
it 'does not recognise /health_status X' do
_, updates = service.execute('/health_status needs_attention', issue)
expect(updates).to be_empty
end
it 'does not recognise /clear_health_status' do
_, updates = service.execute('/clear_health_status', issue)
expect(updates).to be_empty
end
end
context 'issuable health_status not supported by type' do
let_it_be(:incident) { create(:incident, project: project) }
before do
stub_licensed_features(issuable_health_status: true)
end
it 'does not recognise /health_status X' do
_, updates = service.execute('/health_status on_track', incident)
expect(updates).to be_empty
end
it 'does not recognise /clear_health_status' do
_, updates = service.execute('/clear_health_status', incident)
expect(updates).to be_empty
end
end
shared_examples 'empty command' do
it 'populates {} if content contains an unsupported command' do
_, updates = service.execute(content, issuable)
......@@ -1095,6 +1216,21 @@ RSpec.describe QuickActions::InterpretService do
end
describe '#explain' do
describe 'health_status command' do
let(:content) { '/health_status on_track' }
context 'issuable health statuses licensed' do
before do
stub_licensed_features(issuable_health_status: true)
end
it 'includes the value' do
_, explanations = service.explain(content, issue)
expect(explanations).to eq(['Sets health status to on_track.'])
end
end
end
describe 'unassign command' do
let(:content) { '/unassign' }
let(:issue) { create(:issue, project: project, assignees: [user, user2]) }
......
......@@ -39,6 +39,10 @@
category: quickactions
redis_slot: quickactions
aggregation: weekly
- name: i_quickactions_clear_health_status
category: quickactions
redis_slot: quickactions
aggregation: weekly
- name: i_quickactions_clone
category: quickactions
redis_slot: quickactions
......@@ -263,6 +267,10 @@
category: quickactions
redis_slot: quickactions
aggregation: weekly
- name: i_quickactions_health_status
category: quickactions
redis_slot: quickactions
aggregation: weekly
- name: i_quickactions_wip
category: quickactions
redis_slot: quickactions
......
......@@ -7326,6 +7326,9 @@ msgstr ""
msgid "Clear due date"
msgstr ""
msgid "Clear health status"
msgstr ""
msgid "Clear recent searches"
msgstr ""
......@@ -7344,9 +7347,15 @@ msgstr ""
msgid "Clear weight"
msgstr ""
msgid "Cleared health status."
msgstr ""
msgid "Cleared weight."
msgstr ""
msgid "Clears health status."
msgstr ""
msgid "Clears weight."
msgstr ""
......@@ -32275,6 +32284,12 @@ msgstr ""
msgid "Set due date"
msgstr ""
msgid "Set health status"
msgstr ""
msgid "Set health status to %{health_status}."
msgstr ""
msgid "Set iteration"
msgstr ""
......@@ -32431,6 +32446,9 @@ msgstr ""
msgid "Sets %{epic_ref} as parent epic."
msgstr ""
msgid "Sets health status to %{health_status}."
msgstr ""
msgid "Sets target branch to %{branch_name}."
msgstr ""
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment