diff --git a/app/controllers/projects/merge_requests/creations_controller.rb b/app/controllers/projects/merge_requests/creations_controller.rb index 33a93ed99fb64d2e8726e437d8cd6f0ace7fa9bd..8039ebc5d92e607c4ef7d7fd4df0b14b7905a906 100644 --- a/app/controllers/projects/merge_requests/creations_controller.rb +++ b/app/controllers/projects/merge_requests/creations_controller.rb @@ -4,6 +4,7 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap include DiffForPath include DiffHelper include RendersCommits + include ProductAnalyticsTracking skip_before_action :merge_request before_action :authorize_create_merge_request_from! @@ -25,6 +26,12 @@ class Projects::MergeRequests::CreationsController < Projects::MergeRequests::Ap :branch_to ] + track_internal_event :new, name: 'create_mr_web_ide', conditions: -> { webide_source? } + + track_internal_event :new, + name: 'visit_after_push_link_or_create_mr_banner', + conditions: -> { after_push_link? } + def new define_new_vars end @@ -160,10 +167,27 @@ def selected_target_project end # rubocop: enable CodeReuse/ActiveRecord + def webide_source? + params[:nav_source] == 'webide' + end + + def after_push_link? + # Link in the console after you push changes: + # .../-/merge_requests/new?merge_request%5Bsource_branch%5D=branch-name + request.query_parameters.keys == ['merge_request'] && + request.query_parameters['merge_request'].keys == ['source_branch'] + end + def incr_count_webide_merge_request - return if params[:nav_source] != 'webide' + webide_source? && Gitlab::UsageDataCounters::WebIdeCounter.increment_merge_requests_count + end + + def tracking_project_source + @project + end - Gitlab::UsageDataCounters::WebIdeCounter.increment_merge_requests_count + def tracking_namespace_source + @project.namespace end end diff --git a/app/helpers/projects/issues_helper.rb b/app/helpers/projects/issues_helper.rb new file mode 100644 index 0000000000000000000000000000000000000000..7e30936715390b7479545df091f5d9b1656e8167 --- /dev/null +++ b/app/helpers/projects/issues_helper.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Projects + module IssuesHelper + def create_mr_tracking_data(can_create_mr, can_create_confidential_mr) + if can_create_confidential_mr + { event_tracking: 'click_create_confidential_mr_issues_list' } + elsif can_create_mr + { event_tracking: 'click_create_mr_issues_list' } + else + {} + end + end + end +end diff --git a/app/views/projects/issues/_new_branch.html.haml b/app/views/projects/issues/_new_branch.html.haml index 2f2f67ce3cdfcd85290a3e4a80704aac6f94db88..f41cedb1c498f076fb2cc855e310646a27be889f 100644 --- a/app/views/projects/issues/_new_branch.html.haml +++ b/app/views/projects/issues/_new_branch.html.haml @@ -8,6 +8,7 @@ - can_create_path = can_create_branch_project_issue_path(@project, @issue) - create_branch_path = project_branches_path(@project, branch_name: @issue.to_branch_name, ref: @project.default_branch, issue_iid: @issue.iid, format: :json) - refs_path = refs_namespace_project_path(@project.namespace, @project, search: '') + - tracking_data = create_mr_tracking_data(can_create_merge_request, can_create_confidential_merge_request?) .create-mr-dropdown-wrap.d-inline-block.full-width-mobile.js-create-mr{ data: { project_path: @project.full_path, project_id: @project.id, can_create_path: can_create_path, create_mr_path: create_mr_path(from: @issue.to_branch_name, source_project: @project, to: @project.default_branch, mr_params: { issue_iid: @issue.iid }), create_branch_path: create_branch_path, refs_path: refs_path, is_confidential: can_create_confidential_merge_request?.to_s } } .btn-group.unavailable @@ -18,7 +19,8 @@ .btn-group.available.hidden - = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'js-create-merge-request', data: { action: data_action } }) do + = render Pajamas::ButtonComponent.new(variant: :confirm, + button_options: { class: 'js-create-merge-request', data: { action: data_action, **tracking_data } }) do = gl_loading_icon(inline: true , css_class: 'js-create-mr-spinner js-spinner gl-display-none') = value @@ -56,7 +58,8 @@ %input#source-name.js-ref.ref.form-control.gl-form-input{ type: 'text', placeholder: "#{@project.default_branch}", value: "#{@project.default_branch}", data: { value: "#{@project.default_branch}" } } %span.js-ref-message.form-text.gl-font-sm - = render Pajamas::ButtonComponent.new(variant: :confirm, button_options: { class: 'js-create-target', data: { action: 'create-mr' } }) do + = render Pajamas::ButtonComponent.new(variant: :confirm, + button_options: { class: 'js-create-target', data: { action: 'create-mr', **tracking_data } }) do = create_mr_text - if can_create_confidential_merge_request? diff --git a/app/views/projects/merge_requests/_nav_btns.html.haml b/app/views/projects/merge_requests/_nav_btns.html.haml index 4607aebf121778daefa36feae548d22893a82939..e289a762ce62c2c514639f8b73680f8cf98b0d90 100644 --- a/app/views/projects/merge_requests/_nav_btns.html.haml +++ b/app/views/projects/merge_requests/_nav_btns.html.haml @@ -5,7 +5,8 @@ = render Pajamas::ButtonComponent.new(type: :submit, button_options: { class: 'gl-mr-3 js-bulk-update-toggle' }) do = _("Bulk edit") - if merge_project && can?(@current_user, :create_merge_request_in, @project) - = render Pajamas::ButtonComponent.new(href: new_merge_request_path, variant: :confirm) do + = render Pajamas::ButtonComponent.new(href: new_merge_request_path, variant: :confirm, + button_options: { data: { event_tracking: 'click_new_merge_request_list' } }) do = _("New merge request") .dropdown.gl-dropdown diff --git a/app/views/shared/empty_states/_merge_requests.html.haml b/app/views/shared/empty_states/_merge_requests.html.haml index 5a96b51be61e90665b649591c2bdeb8296d9aa72..fabcf9fcc263e79006c2cbe9f7381f0669d90496 100644 --- a/app/views/shared/empty_states/_merge_requests.html.haml +++ b/app/views/shared/empty_states/_merge_requests.html.haml @@ -4,6 +4,7 @@ - is_opened_state = params[:state] == 'opened' - is_closed_state = params[:state] == 'closed' - can_create_merge_request = merge_request_source_project_for_project(@project) +- tracking_data = { event_tracking: 'click_new_merge_request_empty_list' } .row.empty-state.merge-requests{ data: { testid: 'issuable-empty-state' } } .col-12 @@ -18,7 +19,8 @@ = _("To widen your search, change or remove filters above") .text-center - if can_create_merge_request - = link_button_to _("New merge request"), button_path || project_new_merge_request_path(@project), title: _("New merge request"), variant: :confirm + = link_button_to _("New merge request"), button_path || project_new_merge_request_path(@project), + title: _("New merge request"), variant: :confirm, data: tracking_data - elsif is_opened_state && opened_merged_count == 0 && closed_merged_count > 0 %h4.text-center = _("There are no open merge requests") @@ -26,7 +28,8 @@ = _("To keep this project going, create a new merge request") .text-center - if can_create_merge_request - = link_button_to _("New merge request"), button_path || project_new_merge_request_path(@project), title: _("New merge request"), variant: :confirm + = link_button_to _("New merge request"), button_path || project_new_merge_request_path(@project), + title: _("New merge request"), variant: :confirm, data: tracking_data - elsif is_closed_state && opened_merged_count > 0 && closed_merged_count == 0 %h4.text-center = _("There are no closed merge requests") @@ -37,4 +40,8 @@ = _("Interested parties can even contribute by pushing commits if they want to.") - if button_path .text-center - = link_button_to _('New merge request'), button_path, title: _('New merge request'), id: 'new_merge_request_link', data: { testid: "new-merge-request-button" }, variant: :confirm + = link_button_to _('New merge request'), button_path, + title: _('New merge request'), + id: 'new_merge_request_link', + variant: :confirm, + data: { testid: "new-merge-request-button", **tracking_data } diff --git a/config/events/click_create_confidential_mr_issues_list.yml b/config/events/click_create_confidential_mr_issues_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..e31d1dbaf52dbadf46ffa84001af285422d76b05 --- /dev/null +++ b/config/events/click_create_confidential_mr_issues_list.yml @@ -0,0 +1,20 @@ +--- +description: User clicks Create confidential merge request on issues list +category: InternalEventTracking +action: click_create_confidential_mr_issues_list +identifiers: + - project + - namespace + - user +product_section: growth +product_stage: growth +product_group: acquisition +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/events/click_create_mr_issues_list.yml b/config/events/click_create_mr_issues_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..f9d2c3fb5924324ca31b6011a504dcfe00843e6e --- /dev/null +++ b/config/events/click_create_mr_issues_list.yml @@ -0,0 +1,20 @@ +--- +description: User clicks Create merge request on issues list +category: InternalEventTracking +action: click_create_mr_issues_list +identifiers: + - project + - namespace + - user +product_section: growth +product_stage: growth +product_group: acquisition +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/events/click_new_merge_request_empty_list.yml b/config/events/click_new_merge_request_empty_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..6250fb363c5d691926574a0e0260319d5619b91a --- /dev/null +++ b/config/events/click_new_merge_request_empty_list.yml @@ -0,0 +1,20 @@ +--- +description: User clicks New merge request on empty MR list +category: InternalEventTracking +action: click_new_merge_request_empty_list +identifiers: + - project + - namespace + - user +product_section: growth +product_stage: growth +product_group: acquisition +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/events/click_new_merge_request_list.yml b/config/events/click_new_merge_request_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..6ce5ce65230eb8098728db5eb466584dcc34f73d --- /dev/null +++ b/config/events/click_new_merge_request_list.yml @@ -0,0 +1,20 @@ +--- +description: User clicks New merge request on MR list +category: InternalEventTracking +action: click_new_merge_request_list +identifiers: + - project + - namespace + - user +product_section: growth +product_stage: growth +product_group: acquisition +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/events/create_mr_web_ide.yml b/config/events/create_mr_web_ide.yml new file mode 100644 index 0000000000000000000000000000000000000000..c4071946d42b25e672a46268d0b5d979058fef09 --- /dev/null +++ b/config/events/create_mr_web_ide.yml @@ -0,0 +1,20 @@ +--- +description: Create new MR from WebIDE +category: InternalEventTracking +action: create_mr_web_ide +identifiers: + - project + - namespace + - user +product_section: growth +product_stage: growth +product_group: acquisition +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/events/visit_after_push_link_or_create_mr_banner.yml b/config/events/visit_after_push_link_or_create_mr_banner.yml new file mode 100644 index 0000000000000000000000000000000000000000..f4fbafd51410ac1fd0d7218d686d4ba6b2a2694d --- /dev/null +++ b/config/events/visit_after_push_link_or_create_mr_banner.yml @@ -0,0 +1,20 @@ +--- +description: 'Visit new MR link after push or create new MR from banner' +category: InternalEventTracking +action: visit_after_push_link_or_create_mr_banner +identifiers: + - project + - namespace + - user +product_section: growth +product_stage: growth +product_group: acquisition +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +distributions: + - ce + - ee +tiers: + - free + - premium + - ultimate diff --git a/config/metrics/counts_all/count_total_click_create_confidential_mr_issues_list.yml b/config/metrics/counts_all/count_total_click_create_confidential_mr_issues_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..6631e00d517cd9019a4c398aa1f3c4425c122c7a --- /dev/null +++ b/config/metrics/counts_all/count_total_click_create_confidential_mr_issues_list.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_click_create_confidential_mr_issues_list +description: Total count of clicks Create confidential merge request on issues list +product_section: growth +product_stage: growth +product_group: acquisition +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +time_frame: all +data_source: internal_events +data_category: optional +distribution: + - ce + - ee +tier: + - free + - premium + - ultimate +options: + events: + - click_create_confidential_mr_issues_list +events: + - name: click_create_confidential_mr_issues_list diff --git a/config/metrics/counts_all/count_total_click_create_mr_issues_list.yml b/config/metrics/counts_all/count_total_click_create_mr_issues_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..51b6fb77562b02fa71b7d06e8ef1c8f288f2244f --- /dev/null +++ b/config/metrics/counts_all/count_total_click_create_mr_issues_list.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_click_create_mr_issues_list +description: Total count of clicks Create merge request on issues list +product_section: growth +product_stage: growth +product_group: acquisition +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +time_frame: all +data_source: internal_events +data_category: optional +distribution: + - ce + - ee +tier: + - free + - premium + - ultimate +options: + events: + - click_create_mr_issues_list +events: + - name: click_create_mr_issues_list diff --git a/config/metrics/counts_all/count_total_click_new_merge_request_empty_list.yml b/config/metrics/counts_all/count_total_click_new_merge_request_empty_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..2a05baeb6526549da6ef927553e4aac55374ba4d --- /dev/null +++ b/config/metrics/counts_all/count_total_click_new_merge_request_empty_list.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_click_new_merge_request_empty_list +description: Total count of clicks New merge request on empty list +product_section: growth +product_stage: growth +product_group: acquisition +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +time_frame: all +data_source: internal_events +data_category: optional +distribution: + - ce + - ee +tier: + - free + - premium + - ultimate +options: + events: + - click_new_merge_request_empty_list +events: + - name: click_new_merge_request_empty_list diff --git a/config/metrics/counts_all/count_total_click_new_merge_request_list.yml b/config/metrics/counts_all/count_total_click_new_merge_request_list.yml new file mode 100644 index 0000000000000000000000000000000000000000..19d183b862e55a275d9b82dd245cca2ce05b0834 --- /dev/null +++ b/config/metrics/counts_all/count_total_click_new_merge_request_list.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_click_new_merge_request_list +description: Total count of clicks New merge request on list +product_section: growth +product_stage: growth +product_group: acquisition +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +time_frame: all +data_source: internal_events +data_category: optional +distribution: + - ce + - ee +tier: + - free + - premium + - ultimate +options: + events: + - click_new_merge_request_list +events: + - name: click_new_merge_request_list diff --git a/config/metrics/counts_all/count_total_create_mr_web_ide.yml b/config/metrics/counts_all/count_total_create_mr_web_ide.yml new file mode 100644 index 0000000000000000000000000000000000000000..f7e296e20ec7d8beedf75a68079206c7085ed3bb --- /dev/null +++ b/config/metrics/counts_all/count_total_create_mr_web_ide.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_create_mr_web_ide +description: Total count of created MRs from WebIDE +product_section: growth +product_stage: growth +product_group: acquisition +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +time_frame: all +data_source: internal_events +data_category: optional +distribution: + - ce + - ee +tier: + - free + - premium + - ultimate +options: + events: + - create_mr_web_ide +events: + - name: create_mr_web_ide diff --git a/config/metrics/counts_all/count_total_visit_after_push_link_or_create_mr_banner.yml b/config/metrics/counts_all/count_total_visit_after_push_link_or_create_mr_banner.yml new file mode 100644 index 0000000000000000000000000000000000000000..b75d84ff4e1290a5e602e18bb4a6d14bc28c0cf8 --- /dev/null +++ b/config/metrics/counts_all/count_total_visit_after_push_link_or_create_mr_banner.yml @@ -0,0 +1,26 @@ +--- +key_path: counts.count_total_visit_after_push_link_or_create_mr_banner +description: Total count of visited after push links or created MRs from banner +product_section: growth +product_stage: growth +product_group: acquisition +performance_indicator_type: [] +value_type: number +status: active +milestone: '16.9' +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/143036 +time_frame: all +data_source: internal_events +data_category: optional +distribution: + - ce + - ee +tier: + - free + - premium + - ultimate +options: + events: + - visit_after_push_link_or_create_mr_banner +events: + - name: visit_after_push_link_or_create_mr_banner diff --git a/spec/controllers/projects/merge_requests/creations_controller_spec.rb b/spec/controllers/projects/merge_requests/creations_controller_spec.rb index 5cf9d7c3fa02d5e68810714b244dc69f7464f706..e0d1cc12429c2933b1b8691bd87a2a3c10517dde 100644 --- a/spec/controllers/projects/merge_requests/creations_controller_spec.rb +++ b/spec/controllers/projects/merge_requests/creations_controller_spec.rb @@ -6,15 +6,18 @@ let(:project) { create(:project, :repository) } let(:user) { project.first_owner } let(:fork_project) { create(:forked_project_with_submodules) } + + let(:base_params) do + { project_id: fork_project, namespace_id: fork_project.namespace.to_param } + end + let(:get_diff_params) do - { - namespace_id: fork_project.namespace.to_param, - project_id: fork_project, + base_params.merge( merge_request: { source_branch: 'remove-submodule', target_branch: 'master' } - } + ) end before do @@ -36,14 +39,12 @@ render_views let(:large_diff_params) do - { - namespace_id: fork_project.namespace.to_param, - project_id: fork_project, + base_params.merge( merge_request: { source_branch: 'master', target_branch: 'fix' } - } + ) end describe 'with artificial limits' do @@ -78,6 +79,30 @@ expect(response).to have_gitlab_http_status(:ok) end end + + context 'with tracking' do + let(:base_params) do + { project_id: project, namespace_id: project.namespace.to_param } + end + + context 'when webide source' do + it_behaves_like 'internal event tracking' do + let(:event) { 'create_mr_web_ide' } + + subject { get :new, params: base_params.merge(nav_source: 'webide') } + end + end + + context 'when after push link' do + it_behaves_like 'internal event tracking' do + let(:event) { 'visit_after_push_link_or_create_mr_banner' } + + subject do + get :new, params: base_params.merge(merge_request: { source_branch: 'feature' }) + end + end + end + end end describe 'GET diffs' do @@ -184,12 +209,7 @@ def diff_for_path(extra_params = {}) expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true } expect(Ability).to receive(:allowed?).with(user, :create_merge_request_in, project) { true }.at_least(:once) - get :branch_to, params: { - namespace_id: fork_project.namespace, - project_id: fork_project, - target_project_id: project.id, - ref: 'master' - } + get :branch_to, params: base_params.merge(target_project_id: project.id, ref: 'master') expect(assigns(:commit)).not_to be_nil expect(response).to have_gitlab_http_status(:ok) @@ -199,12 +219,7 @@ def diff_for_path(extra_params = {}) expect(Ability).to receive(:allowed?).with(user, :read_project, project) { true } expect(Ability).to receive(:allowed?).with(user, :create_merge_request_in, project) { false }.at_least(:once) - get :branch_to, params: { - namespace_id: fork_project.namespace, - project_id: fork_project, - target_project_id: project.id, - ref: 'master' - } + get :branch_to, params: base_params.merge(target_project_id: project.id, ref: 'master') expect(assigns(:commit)).to be_nil expect(response).to have_gitlab_http_status(:ok) @@ -214,12 +229,7 @@ def diff_for_path(extra_params = {}) expect(Ability).to receive(:allowed?).with(user, :read_project, project) { false } expect(Ability).to receive(:allowed?).with(user, :create_merge_request_in, project) { true }.at_least(:once) - get :branch_to, params: { - namespace_id: fork_project.namespace, - project_id: fork_project, - target_project_id: project.id, - ref: 'master' - } + get :branch_to, params: base_params.merge(target_project_id: project.id, ref: 'master') expect(assigns(:commit)).to be_nil expect(response).to have_gitlab_http_status(:ok) @@ -244,12 +254,7 @@ def diff_for_path(extra_params = {}) context 'project is a fork' do it 'calls to project defaults to selects a correct target project' do - get :branch_to, - params: { - namespace_id: fork_project.namespace, - project_id: fork_project, - ref: 'master' - } + get :branch_to, params: base_params.merge(ref: 'master') expect(assigns(:target_project)).to eq(project) expect(response).to have_gitlab_http_status(:ok) @@ -260,15 +265,13 @@ def diff_for_path(extra_params = {}) describe 'POST create' do let(:params) do - { - namespace_id: fork_project.namespace.to_param, - project_id: fork_project, + base_params.merge( merge_request: { title: 'Test merge request', source_branch: 'remove-submodule', target_branch: 'master' } - } + ) end it 'creates merge request' do diff --git a/spec/helpers/projects/issues_helper_spec.rb b/spec/helpers/projects/issues_helper_spec.rb new file mode 100644 index 0000000000000000000000000000000000000000..f971cc58ef1c4ea827df46ed74326744a5992de1 --- /dev/null +++ b/spec/helpers/projects/issues_helper_spec.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Projects::IssuesHelper, feature_category: :team_planning do + describe '#create_mr_tracking_data' do + using RSpec::Parameterized::TableSyntax + + where(:can_create_mr, :can_create_confidential_mr, :tracking_data) do + true | true | { event_tracking: 'click_create_confidential_mr_issues_list' } + true | false | { event_tracking: 'click_create_mr_issues_list' } + false | false | {} + end + + with_them do + it do + expect(create_mr_tracking_data(can_create_mr, can_create_confidential_mr)).to eq(tracking_data) + end + end + end +end