From d16b84e3aabc7e4fbaf1a15d90761a64ca2fca01 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Tue, 12 Dec 2023 16:34:30 +0100 Subject: [PATCH 1/8] Adds convert_to_ticket quick action This quick action allows to convert an existing issue to a Service Desk issue. Changelog: added --- .../issues/convert_to_ticket_service.rb | 92 ++++++++++++++ .../i_quickactions_convert_to_ticket.yml | 24 ++++ .../beta/convert_to_ticket_quick_action.yml | 9 ++ ...l_i_quickactions_convert_to_ticket_28d.yml | 27 +++++ doc/user/project/service_desk/index.md | 1 + .../service_desk/using_service_desk.md | 17 +++ lib/gitlab/quick_actions/issue_actions.rb | 20 ++++ locale/gitlab.pot | 24 ++++ .../issues/convert_to_ticket_service_spec.rb | 112 ++++++++++++++++++ .../quick_actions/interpret_service_spec.rb | 83 +++++++++++++ 10 files changed, 409 insertions(+) create mode 100644 app/services/issues/convert_to_ticket_service.rb create mode 100644 config/events/i_quickactions_convert_to_ticket.yml create mode 100644 config/feature_flags/beta/convert_to_ticket_quick_action.yml create mode 100644 config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml create mode 100644 spec/services/issues/convert_to_ticket_service_spec.rb diff --git a/app/services/issues/convert_to_ticket_service.rb b/app/services/issues/convert_to_ticket_service.rb new file mode 100644 index 00000000000000..7038ff9828a818 --- /dev/null +++ b/app/services/issues/convert_to_ticket_service.rb @@ -0,0 +1,92 @@ +# frozen_string_literal: true + +module Issues + class ConvertToTicketService < ::BaseProjectService + attr_reader :target, :email, :original_author + + def initialize(target:, current_user:, email:) + super(project: target.project, current_user: current_user) + + @target = target + @email = email + @original_author = target.author + end + + def execute + return error_feature_flag unless Feature.enabled?(:convert_to_ticket_quick_action, target.project, type: :beta) + return error_underprivileged unless current_user.can?(:"admin_#{target.to_ability_name}", target) + return error_already_ticket if ticket? + return error_invalid_email unless valid_email? + + update_target + add_note + + ServiceResponse.success(message: success_message) + end + + private + + def update_target + target.update!( + service_desk_reply_to: email, + author: Users::Internal.support_bot, + confidential: true + ) + + # Migrate to IssueEmailParticipants::CreateService + # once :issue_email_participants feature flag has been removed + IssueEmailParticipant.create!(issue: target, email: email) + end + + def add_note + message = s_( + "ServiceDesk|This issue has been converted to a Service Desk ticket. The email address `%{email}`\ + is the new author of this issue. GitLab didn't send a `thank_you` Service Desk email.\ + The original author of this issue was `%{original_author}`." + ) + + ::Notes::CreateService.new( + project, + Users::Internal.support_bot, + noteable: target, + note: format(message, email: email, original_author: original_author.to_reference), + internal: true + ).execute + end + + def ticket? + target.from_service_desk? + end + + def valid_email? + email.present? && IssueEmailParticipant.new(issue: target, email: email).valid? + end + + def error(message) + ServiceResponse.error(message: message) + end + + def error_feature_flag + # Don't translate feature flag error because it's temporary. + error("Feature flag convert_to_ticket_quick_action is not enabled for this project.") + end + + def error_underprivileged + error(_("You don't have permission to manage this issue.")) + end + + def error_already_ticket + error(s_("ServiceDesk|Cannot convert to ticket because it is already a ticket.")) + end + + def error_invalid_email + error( + s_("ServiceDesk|Cannot convert issue to ticket because no email was provided or the format was invalid.") + ) + end + + def success_message + s_('ServiceDesk|Converted issue to Service Desk ticket.') + end + end +end diff --git a/config/events/i_quickactions_convert_to_ticket.yml b/config/events/i_quickactions_convert_to_ticket.yml new file mode 100644 index 00000000000000..26133ee2609feb --- /dev/null +++ b/config/events/i_quickactions_convert_to_ticket.yml @@ -0,0 +1,24 @@ +--- +description: When the quickaction /convert_to_ticket is executed on an issue +category: InternalEventTracking +action: i_quickactions_convert_to_ticket +label_description: +property_description: +value_description: +extra_properties: +identifiers: +- project +- user +- namespace +product_section: seg +product_stage: service_management +product_group: respond +milestone: "16.9" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 +distributions: +- ce +- ee +tiers: +- free +- premium +- ultimate diff --git a/config/feature_flags/beta/convert_to_ticket_quick_action.yml b/config/feature_flags/beta/convert_to_ticket_quick_action.yml new file mode 100644 index 00000000000000..10fa0210884500 --- /dev/null +++ b/config/feature_flags/beta/convert_to_ticket_quick_action.yml @@ -0,0 +1,9 @@ +--- +name: convert_to_ticket_quick_action +feature_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/433376 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139553 +rollout_issue_url: https://gitlab.com/gitlab-com/gl-infra/production/-/issues/17488 +milestone: '16.9' +group: group::respond +type: beta +default_enabled: false diff --git a/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml b/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml new file mode 100644 index 00000000000000..236e00df449dee --- /dev/null +++ b/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml @@ -0,0 +1,27 @@ +--- +key_path: count_total_i_quickactions_convert_to_ticket_28d +description: Number of executions of the /convert_to_ticket quickaction on an issue +product_section: seg +product_stage: service_management +product_group: respond +performance_indicator_type: [] +value_type: number +status: active +milestone: "16.9" +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 +time_frame: 28d +data_source: internal_events +data_category: optional +instrumentation_class: TotalCountMetric +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - i_quickactions_convert_to_ticket +events: + - name: i_quickactions_convert_to_ticket diff --git a/doc/user/project/service_desk/index.md b/doc/user/project/service_desk/index.md index 566d719dac813c..2903991e02f35a 100644 --- a/doc/user/project/service_desk/index.md +++ b/doc/user/project/service_desk/index.md @@ -62,6 +62,7 @@ Meanwhile: - [As an end user (issue creator)](using_service_desk.md#as-an-end-user-issue-creator) - [As a responder to the issue](using_service_desk.md#as-a-responder-to-the-issue) - [Email contents and formatting](using_service_desk.md#email-contents-and-formatting) + - [Convert a regular issue to a Service Desk ticket](using_service_desk.md#convert-a-regular-issue-to-a-service-desk-ticket) - [Privacy considerations](using_service_desk.md#privacy-considerations) ## Troubleshooting Service Desk diff --git a/doc/user/project/service_desk/using_service_desk.md b/doc/user/project/service_desk/using_service_desk.md index 0680d92cda72c7..482074cb845752 100644 --- a/doc/user/project/service_desk/using_service_desk.md +++ b/doc/user/project/service_desk/using_service_desk.md @@ -149,6 +149,23 @@ attachments are sent as part of the email. In other cases, the email contains li In GitLab 15.9 and earlier, uploads to a comment are sent as links in the email. +## Convert a regular issue to a Service Desk ticket + +> - [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/433376) in GitLab 16.9 [with a flag](../../../administration/feature_flags.md) named `convert_to_ticket_quick_action`. Disabled by default. + +FLAG: +On self-managed GitLab, by default this feature is not available. To make it available per group, +an administrator can [enable the feature flag](../../../administration/feature_flags.md) named `convert_to_ticket_quick_action`. +On GitLab.com, this feature is not available. + +Use the quick action `/convert_to_ticket external-issue-author@example.com` to convert any regular issue +into a Service Desk ticket. This assigns the provided email address as the external author of the ticket +and add them to the list of external participants. They will receive Service Desk emails for any public +comment on the ticket and can reply to these emails. Replies will add a new comment on the ticket. + +GitLab doesn't send [the default `thank_you` email](configure.md#customize-emails-sent-to-the-requester). +You can add a public comment on the ticket to let the end user know that the ticket has been created. + ## Privacy considerations > - [Changed](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/108901) the minimum required role to view the creator's and participant's email in GitLab 15.9. diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb index b3f56e8590ac40..a045dc5e838fba 100644 --- a/lib/gitlab/quick_actions/issue_actions.rb +++ b/lib/gitlab/quick_actions/issue_actions.rb @@ -260,6 +260,26 @@ module IssueActions @execution_message[:remove_email] = response.message end + desc { s_('ServiceDesk|Convert issue to Service Desk ticket') } + explanation { s_('ServiceDesk|Converts this issue to a Service Desk ticket.') } + params 'external-issue-author@example.com' + types Issue + condition do + quick_action_target.persisted? && + Feature.enabled?(:convert_to_ticket_quick_action, parent, type: :beta) && + current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target) && + quick_action_target.try(:from_service_desk?) == false + end + command :convert_to_ticket do |email = ""| + response = ::Issues::ConvertToTicketService.new( + target: quick_action_target, + current_user: current_user, + email: email + ).execute + + @execution_message[:convert_to_ticket] = response.message + end + desc { _('Promote issue to incident') } explanation { _('Promotes issue to incident') } execution_message { _('Issue has been promoted to incident') } diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 1d430964c9b3db..77ee4f3a3967e9 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -45628,6 +45628,12 @@ msgstr "" msgid "ServiceDesk|CRAM-MD5" msgstr "" +msgid "ServiceDesk|Cannot convert issue to ticket because no email was provided or the format was invalid." +msgstr "" + +msgid "ServiceDesk|Cannot convert to ticket because it is already a ticket." +msgstr "" + msgid "ServiceDesk|Cannot create custom email" msgstr "" @@ -45646,6 +45652,15 @@ msgstr "" msgid "ServiceDesk|Connect a custom email address your customers can use to create Service Desk issues. Forward all emails from your custom email address to the Service Desk email address of this project. GitLab will send Service Desk emails from the custom address on your behalf using your SMTP credentials. %{linkStart}Learn more about prerequisites and the verification process%{linkEnd}." msgstr "" +msgid "ServiceDesk|Convert issue to Service Desk ticket" +msgstr "" + +msgid "ServiceDesk|Converted issue to Service Desk ticket." +msgstr "" + +msgid "ServiceDesk|Converts this issue to a Service Desk ticket." +msgstr "" + msgid "ServiceDesk|Copy Service Desk email address" msgstr "" @@ -45817,6 +45832,12 @@ msgstr "" msgid "ServiceDesk|This also adds an internal comment that mentions the assignees of the issue." msgstr "" +msgid "" +"ServiceDesk|This issue has been converted to a Service Desk ticket. The email address `%{email}`\\\n" +" is the new author of this issue. GitLab didn't send a `thank_you` Service Desk email.\\\n" +" The original author of this issue was `%{original_author}`." +msgstr "" + msgid "ServiceDesk|This issue has been reopened because it received a new comment from an external participant." msgstr "" @@ -56797,6 +56818,9 @@ msgstr "" msgid "You don't have permission to manage email participants." msgstr "" +msgid "You don't have permission to manage this issue." +msgstr "" + msgid "You don't have permission to view this epic" msgstr "" diff --git a/spec/services/issues/convert_to_ticket_service_spec.rb b/spec/services/issues/convert_to_ticket_service_spec.rb new file mode 100644 index 00000000000000..b688cffef5f964 --- /dev/null +++ b/spec/services/issues/convert_to_ticket_service_spec.rb @@ -0,0 +1,112 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Issues::ConvertToTicketService, feature_category: :service_desk do + shared_examples 'a successful service execution' do + it 'converts issue to Service Desk issue', :aggregate_failures do + original_author = issue.author + + response = service.execute + expect(response).to be_success + expect(response.message).to eq(success_message) + + issue.reset + + expect(issue).to have_attributes( + confidential: true, + author: support_bot, + service_desk_reply_to: 'user@example.com' + ) + + external_participant = issue.issue_email_participants.last + expect(external_participant.email).to eq(email) + + note = issue.notes.last + expect(note.author).to eq(support_bot) + expect(note.note).to include(email) + expect(note.note).to include(original_author.to_reference) + end + end + + shared_examples 'a failed service execution' do + it 'returns error ServiceResponse with message', :aggregate_failures do + response = service.execute + expect(response).to be_error + expect(response.message).to eq(error_message) + end + end + + describe '#execute' do + let_it_be_with_reload(:project) { create(:project) } + let_it_be(:user) { create(:user) } + let_it_be(:support_bot) { Users::Internal.support_bot } + let_it_be_with_reload(:issue) { create(:issue, project: project) } + + let(:email) { nil } + let(:service) { described_class.new(target: issue, current_user: user, email: email) } + + let(:error_feature_flag) { "Feature flag convert_to_ticket_quick_action is not enabled for this project." } + let(:error_underprivileged) { _("You don't have permission to manage this issue.") } + let(:error_already_ticket) { s_("ServiceDesk|Cannot convert to ticket because it is already a ticket.") } + let(:error_invalid_email) do + s_("ServiceDesk|Cannot convert issue to ticket because no email was provided or the format was invalid.") + end + + let(:success_message) { s_('ServiceDesk|Converted issue to Service Desk ticket.') } + + context 'when the user is not a project member' do + let(:error_message) { error_underprivileged } + + it_behaves_like 'a failed service execution' + end + + context 'when user has the reporter role in project' do + before_all do + project.add_developer(user) + end + + context 'without email' do + let(:error_message) { error_invalid_email } + + it_behaves_like 'a failed service execution' + end + + context 'with invalid email' do + let(:email) { 'not-a-valid-email' } + let(:error_message) { error_invalid_email } + + it_behaves_like 'a failed service execution' + end + + context 'with valid email' do + let(:email) { 'user@example.com' } + + it_behaves_like 'a successful service execution' + + context 'when issue is Service Desk issue' do + let(:error_message) { error_already_ticket } + + before do + issue.update!( + author: Users::Internal.support_bot, + service_desk_reply_to: 'user@example.com' + ) + end + + it_behaves_like 'a failed service execution' + end + end + end + + context 'when feature flag convert_to_ticket_quick_action is disabled' do + let(:error_message) { error_feature_flag } + + before do + stub_feature_flags(convert_to_ticket_quick_action: false) + end + + it_behaves_like 'a failed service execution' + end + end +end diff --git a/spec/services/quick_actions/interpret_service_spec.rb b/spec/services/quick_actions/interpret_service_spec.rb index 5bb6aab56fda37..7b0c60447ad3c1 100644 --- a/spec/services/quick_actions/interpret_service_spec.rb +++ b/spec/services/quick_actions/interpret_service_spec.rb @@ -2566,6 +2566,89 @@ end end + describe 'convert_to_ticket command' do + shared_examples 'a failed command execution' do + it 'fails with message' do + _, _, message = convert_to_ticket + + expect(message).to eq(expected_message) + expect(issuable).to have_attributes( + confidential: false, + author_id: original_author.id, + service_desk_reply_to: nil + ) + end + end + + let_it_be_with_reload(:issuable) { issue } + let_it_be(:original_author) { issue.author } + + let(:content) { '/convert_to_ticket' } + let(:expected_message) do + s_("ServiceDesk|Cannot convert issue to ticket because no email was provided or the format was invalid.") + end + + subject(:convert_to_ticket) { service.execute(content, issuable) } + + it 'is part of the available commands' do + expect(service.available_commands(issuable)).to include(a_hash_including(name: :convert_to_ticket)) + end + + it_behaves_like 'a failed command execution' + + context 'when parameter is not an email' do + let(:content) { '/convert_to_ticket no-email-at-all' } + + it_behaves_like 'a failed command execution' + end + + context 'when parameter is an email' do + let(:content) { '/convert_to_ticket user@example.com' } + + it 'converts issue to Service Desk issue' do + _, _, message = convert_to_ticket + + expect(message).to eq(s_('ServiceDesk|Converted issue to Service Desk ticket.')) + expect(issuable).to have_attributes( + confidential: true, + author_id: Users::Internal.support_bot.id, + service_desk_reply_to: 'user@example.com' + ) + end + end + + context 'when issue is Service Desk issue' do + before do + issue.update!( + author: Users::Internal.support_bot, + service_desk_reply_to: 'user@example.com' + ) + end + + it 'is not part of the available commands' do + expect(service.available_commands(issuable)).not_to include(a_hash_including(name: :convert_to_ticket)) + end + end + + context 'with non-persisted issue' do + let(:issuable) { build(:issue) } + + it 'is not part of the available commands' do + expect(service.available_commands(issuable)).not_to include(a_hash_including(name: :convert_to_ticket)) + end + end + + context 'with feature flag convert_to_ticket_quick_action disabled' do + before do + stub_feature_flags(convert_to_ticket_quick_action: false) + end + + it 'is not part of the available commands' do + expect(service.available_commands(issuable)).not_to include(a_hash_including(name: :convert_to_ticket)) + end + end + end + context 'severity command' do let_it_be_with_reload(:issuable) { create(:incident, project: project) } -- GitLab From 52da2e251eb9f0ed50309458b1a95e89235eeab5 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Thu, 1 Feb 2024 16:36:40 +0100 Subject: [PATCH 2/8] Applies backend review suggestion --- app/services/issues/convert_to_ticket_service.rb | 11 ++++++----- locale/gitlab.pot | 5 +---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/services/issues/convert_to_ticket_service.rb b/app/services/issues/convert_to_ticket_service.rb index 7038ff9828a818..51e042aaa6fb12 100644 --- a/app/services/issues/convert_to_ticket_service.rb +++ b/app/services/issues/convert_to_ticket_service.rb @@ -1,11 +1,11 @@ # frozen_string_literal: true module Issues - class ConvertToTicketService < ::BaseProjectService + class ConvertToTicketService < ::BaseContainerService attr_reader :target, :email, :original_author def initialize(target:, current_user:, email:) - super(project: target.project, current_user: current_user) + super(container: target.project, current_user: current_user) @target = target @email = email @@ -40,9 +40,10 @@ def update_target def add_note message = s_( - "ServiceDesk|This issue has been converted to a Service Desk ticket. The email address `%{email}`\ - is the new author of this issue. GitLab didn't send a `thank_you` Service Desk email.\ - The original author of this issue was `%{original_author}`." + "ServiceDesk|This issue has been converted to a Service Desk ticket. " \ + "The email address `%{email}` is the new author of this issue. " \ + "GitLab didn't send a `thank_you` Service Desk email. " \ + "The original author of this issue was `%{original_author}`." ) ::Notes::CreateService.new( diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 77ee4f3a3967e9..5219076ee2fbb1 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -45832,10 +45832,7 @@ msgstr "" msgid "ServiceDesk|This also adds an internal comment that mentions the assignees of the issue." msgstr "" -msgid "" -"ServiceDesk|This issue has been converted to a Service Desk ticket. The email address `%{email}`\\\n" -" is the new author of this issue. GitLab didn't send a `thank_you` Service Desk email.\\\n" -" The original author of this issue was `%{original_author}`." +msgid "ServiceDesk|This issue has been converted to a Service Desk ticket. The email address `%{email}` is the new author of this issue. GitLab didn't send a `thank_you` Service Desk email. The original author of this issue was `%{original_author}`." msgstr "" msgid "ServiceDesk|This issue has been reopened because it received a new comment from an external participant." -- GitLab From f7f0a9dbe774d73075e4c15f97ff75e620fe6f1f Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Thu, 1 Feb 2024 17:09:55 +0100 Subject: [PATCH 3/8] Updated events definition --- ...uickactions_convert_to_ticket_monthly.yml} | 9 +++---- ..._quickactions_convert_to_ticket_weekly.yml | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 5 deletions(-) rename config/metrics/counts_28d/{count_total_i_quickactions_convert_to_ticket_28d.yml => count_total_i_quickactions_convert_to_ticket_monthly.yml} (60%) create mode 100644 config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml diff --git a/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml b/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml similarity index 60% rename from config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml rename to config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml index 236e00df449dee..7a7ca75f1c2087 100644 --- a/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_28d.yml +++ b/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml @@ -1,6 +1,6 @@ --- -key_path: count_total_i_quickactions_convert_to_ticket_28d -description: Number of executions of the /convert_to_ticket quickaction on an issue +key_path: counts.count_total_i_quickactions_convert_to_ticket_monthly +description: Monthly count of executions of the /convert_to_ticket quickaction on an issue product_section: seg product_stage: service_management product_group: respond @@ -12,7 +12,6 @@ introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 time_frame: 28d data_source: internal_events data_category: optional -instrumentation_class: TotalCountMetric distribution: - ce - ee @@ -22,6 +21,6 @@ tier: - ultimate options: events: - - i_quickactions_convert_to_ticket + - i_quickactions_convert_to_ticket events: - - name: i_quickactions_convert_to_ticket +- name: i_quickactions_convert_to_ticket diff --git a/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml b/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml new file mode 100644 index 00000000000000..a5a3318abfe9fc --- /dev/null +++ b/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_i_quickactions_convert_to_ticket_weekly +description: Weekly count of executions of the /convert_to_ticket quickaction on an issue +product_section: seg +product_stage: service_management +product_group: respond +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 +time_frame: 7d +data_source: internal_events +data_category: optional +distribution: +- ce +- ee +tier: +- free +- premium +- ultimate +options: + events: + - i_quickactions_convert_to_ticket +events: +- name: i_quickactions_convert_to_ticket -- GitLab From ebebc5400f1220d03cb9e733ba3457302a5b946e Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Mon, 5 Feb 2024 11:40:14 +0100 Subject: [PATCH 4/8] Use internal events for quick action --- .../issues/convert_to_ticket_service.rb | 2 +- .../i_quickactions_convert_to_ticket.yml | 8 +------ .../quick_action_activity_unique_counter.rb | 13 +++++++++++- ...ick_action_activity_unique_counter_spec.rb | 21 ++++++++++++++++--- 4 files changed, 32 insertions(+), 12 deletions(-) diff --git a/app/services/issues/convert_to_ticket_service.rb b/app/services/issues/convert_to_ticket_service.rb index 51e042aaa6fb12..2fadad89ca45c4 100644 --- a/app/services/issues/convert_to_ticket_service.rb +++ b/app/services/issues/convert_to_ticket_service.rb @@ -5,7 +5,7 @@ class ConvertToTicketService < ::BaseContainerService attr_reader :target, :email, :original_author def initialize(target:, current_user:, email:) - super(container: target.project, current_user: current_user) + super(container: target.resource_parent, current_user: current_user) @target = target @email = email diff --git a/config/events/i_quickactions_convert_to_ticket.yml b/config/events/i_quickactions_convert_to_ticket.yml index 26133ee2609feb..abaa1b986b91bd 100644 --- a/config/events/i_quickactions_convert_to_ticket.yml +++ b/config/events/i_quickactions_convert_to_ticket.yml @@ -2,18 +2,12 @@ description: When the quickaction /convert_to_ticket is executed on an issue category: InternalEventTracking action: i_quickactions_convert_to_ticket -label_description: -property_description: -value_description: -extra_properties: identifiers: -- project - user -- namespace product_section: seg product_stage: service_management product_group: respond -milestone: "16.9" +milestone: '16.9' introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 distributions: - ce diff --git a/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb b/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb index 6e4c5d4e845739..aa5ba1cf34fb2b 100644 --- a/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb +++ b/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter.rb @@ -4,6 +4,10 @@ module Gitlab module UsageDataCounters module QuickActionActivityUniqueCounter class << self + # List of events that use the current internal events implementation. + # Only add internal events for new quick actions. + INTERNAL_EVENTS = %w[convert_to_ticket].freeze + # Tracks the quick action with name `name`. # `args` is expected to be a single string, will be split internally when necessary. def track_unique_action(name, args:, user:) @@ -12,7 +16,14 @@ def track_unique_action(name, args:, user:) args ||= '' name = prepare_name(name, args) - Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:"i_quickactions_#{name}", values: user.id) + if INTERNAL_EVENTS.include?(name) + Gitlab::InternalEvents.track_event("i_quickactions_#{name}", user: user) + else + # Legacy event implementation. Migrate existing events to internal events. + # See implementation of `convert_to_ticket` quickaction and + # https://docs.gitlab.com/ee/development/internal_analytics/internal_event_instrumentation/migration.html#backend-1 + Gitlab::UsageDataCounters::HLLRedisCounter.track_event(:"i_quickactions_#{name}", values: user.id) + end end private diff --git a/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb index 903ae64cf33f2b..3d754871025193 100644 --- a/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb @@ -21,6 +21,13 @@ end end + shared_examples_for 'a tracked quick action internal event' do + it 'tracks the internal event' do + expect(Gitlab::InternalEvents).to receive(:track_event).with(action, user: user).once + subject + end + end + subject { described_class.track_unique_action(quickaction_name, args: args, user: user) } describe '.track_unique_action' do @@ -189,10 +196,10 @@ end end - context 'tracking invite_email' do + context 'when tracking invite_email', feature_category: :service_desk do let(:quickaction_name) { 'invite_email' } - context 'single email' do + context 'with single email' do let(:args) { 'someone@gitlab.com' } it_behaves_like 'a tracked quick action unique event' do @@ -200,7 +207,7 @@ end end - context 'multiple emails' do + context 'with multiple emails' do let(:args) { 'someone@gitlab.com another@gitlab.com' } it_behaves_like 'a tracked quick action unique event' do @@ -208,4 +215,12 @@ end end end + + context 'when tracking convert_to_ticket', feature_category: :service_desk do + let(:quickaction_name) { 'convert_to_ticket' } + + it_behaves_like 'a tracked quick action internal event' do + let(:action) { 'i_quickactions_convert_to_ticket' } + end + end end -- GitLab From 051c4a02ebba3bc35e7a1a1d8e5eff7408413302 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Mon, 5 Feb 2024 18:32:41 +0100 Subject: [PATCH 5/8] Applies database reviewer suggestions --- app/services/issues/convert_to_ticket_service.rb | 5 +++-- config/events/i_quickactions_convert_to_ticket.yml | 2 +- .../count_total_i_quickactions_convert_to_ticket_monthly.yml | 2 +- .../count_total_i_quickactions_convert_to_ticket_weekly.yml | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/app/services/issues/convert_to_ticket_service.rb b/app/services/issues/convert_to_ticket_service.rb index 2fadad89ca45c4..7f3ea0b54319fc 100644 --- a/app/services/issues/convert_to_ticket_service.rb +++ b/app/services/issues/convert_to_ticket_service.rb @@ -2,8 +2,6 @@ module Issues class ConvertToTicketService < ::BaseContainerService - attr_reader :target, :email, :original_author - def initialize(target:, current_user:, email:) super(container: target.resource_parent, current_user: current_user) @@ -26,6 +24,8 @@ def execute private + attr_reader :target, :email, :original_author + def update_target target.update!( service_desk_reply_to: email, @@ -35,6 +35,7 @@ def update_target # Migrate to IssueEmailParticipants::CreateService # once :issue_email_participants feature flag has been removed + # https://gitlab.com/gitlab-org/gitlab/-/issues/440456 IssueEmailParticipant.create!(issue: target, email: email) end diff --git a/config/events/i_quickactions_convert_to_ticket.yml b/config/events/i_quickactions_convert_to_ticket.yml index abaa1b986b91bd..57e0cfda4943fe 100644 --- a/config/events/i_quickactions_convert_to_ticket.yml +++ b/config/events/i_quickactions_convert_to_ticket.yml @@ -8,7 +8,7 @@ product_section: seg product_stage: service_management product_group: respond milestone: '16.9' -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139553 distributions: - ce - ee diff --git a/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml b/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml index 7a7ca75f1c2087..7933f1f6a3509e 100644 --- a/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml +++ b/config/metrics/counts_28d/count_total_i_quickactions_convert_to_ticket_monthly.yml @@ -8,7 +8,7 @@ performance_indicator_type: [] value_type: number status: active milestone: "16.9" -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139553 time_frame: 28d data_source: internal_events data_category: optional diff --git a/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml b/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml index a5a3318abfe9fc..144cbe4c56e02a 100644 --- a/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml +++ b/config/metrics/counts_7d/count_total_i_quickactions_convert_to_ticket_weekly.yml @@ -8,7 +8,7 @@ performance_indicator_type: [] value_type: number status: active milestone: '16.9' -introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/121544 +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/139553 time_frame: 7d data_source: internal_events data_category: optional -- GitLab From 39ac12df415f97d28196771d7b01496a8225d8a7 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Tue, 6 Feb 2024 12:08:03 +0100 Subject: [PATCH 6/8] Applies technical review suggestions --- doc/user/project/quick_actions.md | 1 + doc/user/project/service_desk/using_service_desk.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/user/project/quick_actions.md b/doc/user/project/quick_actions.md index 4f5ed444ae3bc6..1eef20423a82a3 100644 --- a/doc/user/project/quick_actions.md +++ b/doc/user/project/quick_actions.md @@ -67,6 +67,7 @@ To auto-format this table, use the VS Code Markdown Table formatter: `https://do | `/clone [--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. Copies as much data as possible as long as the target project contains equivalent objects like labels, milestones, or epics. 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 | **{check-circle}** Yes | Mark issue or epic as confidential. Support for epics [introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213741) in GitLab 15.6. | +| `/convert_to_ticket ` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | [Convert an issue into a Service Desk ticket](service_desk/using_service_desk.md#convert-a-regular-issue-to-a-service-desk-ticket). [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/433376) in GitLab 16.9 [with a flag](../../administration/feature_flags.md) named `convert_to_ticket_quick_action`. Disabled by default. | | `/copy_metadata ` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another merge request in the project. | | `/copy_metadata <#issue>` | **{check-circle}** Yes | **{check-circle}** Yes | **{dotted-circle}** No | Copy labels and milestone from another issue in the project. | | `/create_merge_request ` | **{check-circle}** Yes | **{dotted-circle}** No | **{dotted-circle}** No | Create a new merge request starting from the current issue. | diff --git a/doc/user/project/service_desk/using_service_desk.md b/doc/user/project/service_desk/using_service_desk.md index 482074cb845752..44e6c2e7c1f417 100644 --- a/doc/user/project/service_desk/using_service_desk.md +++ b/doc/user/project/service_desk/using_service_desk.md @@ -160,8 +160,8 @@ On GitLab.com, this feature is not available. Use the quick action `/convert_to_ticket external-issue-author@example.com` to convert any regular issue into a Service Desk ticket. This assigns the provided email address as the external author of the ticket -and add them to the list of external participants. They will receive Service Desk emails for any public -comment on the ticket and can reply to these emails. Replies will add a new comment on the ticket. +and add them to the list of external participants. They receive Service Desk emails for any public +comment on the ticket and can reply to these emails. Replies add a new comment on the ticket. GitLab doesn't send [the default `thank_you` email](configure.md#customize-emails-sent-to-the-requester). You can add a public comment on the ticket to let the end user know that the ticket has been created. -- GitLab From effc47e00785dc267ff0719f125e173bebb965fa Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Tue, 6 Feb 2024 13:47:27 +0100 Subject: [PATCH 7/8] Adds internal event tracking spec --- .../quick_action_activity_unique_counter_spec.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb b/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb index 3d754871025193..2c21774f7fbf97 100644 --- a/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb +++ b/spec/lib/gitlab/usage_data_counters/quick_action_activity_unique_counter_spec.rb @@ -22,6 +22,10 @@ end shared_examples_for 'a tracked quick action internal event' do + it_behaves_like 'internal event tracking' do + let(:event) { action } + end + it 'tracks the internal event' do expect(Gitlab::InternalEvents).to receive(:track_event).with(action, user: user).once subject -- GitLab From 2398711f870f47c269d3d27e6dd4d23b8dbfc9b1 Mon Sep 17 00:00:00 2001 From: Marc Saleiko Date: Wed, 7 Feb 2024 20:10:30 +0100 Subject: [PATCH 8/8] Applies backend review suggestions --- lib/gitlab/quick_actions/issue_actions.rb | 3 ++- spec/services/issues/convert_to_ticket_service_spec.rb | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/gitlab/quick_actions/issue_actions.rb b/lib/gitlab/quick_actions/issue_actions.rb index a045dc5e838fba..59a6eeb153e558 100644 --- a/lib/gitlab/quick_actions/issue_actions.rb +++ b/lib/gitlab/quick_actions/issue_actions.rb @@ -268,7 +268,8 @@ module IssueActions quick_action_target.persisted? && Feature.enabled?(:convert_to_ticket_quick_action, parent, type: :beta) && current_user.can?(:"admin_#{quick_action_target.to_ability_name}", quick_action_target) && - quick_action_target.try(:from_service_desk?) == false + quick_action_target.respond_to?(:from_service_desk?) && + !quick_action_target.from_service_desk? end command :convert_to_ticket do |email = ""| response = ::Issues::ConvertToTicketService.new( diff --git a/spec/services/issues/convert_to_ticket_service_spec.rb b/spec/services/issues/convert_to_ticket_service_spec.rb index b688cffef5f964..c9f6a30f7602e2 100644 --- a/spec/services/issues/convert_to_ticket_service_spec.rb +++ b/spec/services/issues/convert_to_ticket_service_spec.rb @@ -63,7 +63,7 @@ context 'when user has the reporter role in project' do before_all do - project.add_developer(user) + project.add_reporter(user) end context 'without email' do -- GitLab