From 47c2ca117a588a906089c48329fc1d561b07d7af Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sun, 17 Dec 2023 10:40:04 +0100 Subject: [PATCH 01/14] Add functions to search for merge_user in merge request view extend the merge_request_finder by functions to filter by merge_user and add frontend and API code to display the additional filters Changelog: added Signed-off-by: Martin Schurz --- .../add_extra_tokens_for_merge_requests.js | 14 +++++++++++ .../available_dropdown_mappings.js | 6 +++++ .../javascripts/filtered_search/constants.js | 2 ++ .../filtered_search_bar/constants.js | 2 ++ app/finders/concerns/merged_by_filter.rb | 23 +++++++++++++++++++ app/finders/merge_request/metrics_finder.rb | 3 ++- app/finders/merge_requests_finder.rb | 5 ++++ app/finders/merge_requests_finder/params.rb | 10 ++++++++ app/models/merge_request/metrics.rb | 1 + .../issuable/_merged_by_dropdown.html.haml | 9 ++++++++ .../shared/issuable/_search_bar.html.haml | 1 + 11 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 app/finders/concerns/merged_by_filter.rb create mode 100644 app/views/shared/issuable/_merged_by_dropdown.html.haml diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 698302c5209408..4f59e72f5a426b 100644 --- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -1,8 +1,10 @@ import { __ } from '~/locale'; import { TOKEN_TITLE_APPROVED_BY, + TOKEN_TITLE_MERGED_BY, TOKEN_TITLE_REVIEWER, TOKEN_TYPE_APPROVED_BY, + TOKEN_TYPE_MERGED_BY, TOKEN_TYPE_REVIEWER, TOKEN_TYPE_TARGET_BRANCH, TOKEN_TYPE_SOURCE_BRANCH, @@ -24,6 +26,18 @@ export default ( IssuableTokenKeys.tokenKeys.splice(2, 0, reviewerToken); IssuableTokenKeys.tokenKeysWithAlternative.splice(2, 0, reviewerToken); + const mergedByToken = { + formattedKey: TOKEN_TITLE_MERGED_BY, + key: TOKEN_TYPE_MERGED_BY, + type: 'string', + param: 'username', + symbol: '@', + icon: 'merge', + tag: '@merged-by', + }; + IssuableTokenKeys.tokenKeys.splice(2, 0, mergedByToken); + IssuableTokenKeys.tokenKeysWithAlternative.splice(2, 0, mergedByToken); + const draftToken = { token: { formattedKey: __('Draft'), diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js index a1782c549d62fb..2e306c30906844 100644 --- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js +++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js @@ -2,6 +2,7 @@ import { sortMilestonesByDueDate } from '~/milestones/utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; import { TOKEN_TYPE_APPROVED_BY, + TOKEN_TYPE_MERGED_BY, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -86,6 +87,11 @@ export default class AvailableDropdownMappings { gl: DropdownUser, element: this.container.querySelector('#js-dropdown-reviewer'), }, + [TOKEN_TYPE_MERGED_BY]: { + reference: null, + gl: DropdownUser, + element: this.container.querySelector('#js-dropdown-merged-by'), + }, attention: { reference: null, gl: DropdownUser, diff --git a/app/assets/javascripts/filtered_search/constants.js b/app/assets/javascripts/filtered_search/constants.js index b328ae6a8721ca..ddf7d8113cd557 100644 --- a/app/assets/javascripts/filtered_search/constants.js +++ b/app/assets/javascripts/filtered_search/constants.js @@ -1,5 +1,6 @@ import { TOKEN_TYPE_APPROVED_BY, + TOKEN_TYPE_MERGED_BY, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_REVIEWER, @@ -9,6 +10,7 @@ export const USER_TOKEN_TYPES = [ TOKEN_TYPE_AUTHOR, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_APPROVED_BY, + TOKEN_TYPE_MERGED_BY, TOKEN_TYPE_REVIEWER, 'attention', ]; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 952af4eeb5d923..8470b5733264a4 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -62,6 +62,7 @@ export const TOKEN_EMPTY_SEARCH_TERM = { }; export const TOKEN_TITLE_APPROVED_BY = __('Approved-By'); +export const TOKEN_TITLE_MERGED_BY = __('Merged-By'); export const TOKEN_TITLE_ASSIGNEE = s__('SearchToken|Assignee'); export const TOKEN_TITLE_AUTHOR = __('Author'); export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential'); @@ -84,6 +85,7 @@ export const TOKEN_TITLE_CREATED = __('Created date'); export const TOKEN_TITLE_CLOSED = __('Closed date'); export const TOKEN_TYPE_APPROVED_BY = 'approved-by'; +export const TOKEN_TYPE_MERGED_BY = 'merged-by'; export const TOKEN_TYPE_ASSIGNEE = 'assignee'; export const TOKEN_TYPE_AUTHOR = 'author'; export const TOKEN_TYPE_CONFIDENTIAL = 'confidential'; diff --git a/app/finders/concerns/merged_by_filter.rb b/app/finders/concerns/merged_by_filter.rb new file mode 100644 index 00000000000000..32f7755719aa68 --- /dev/null +++ b/app/finders/concerns/merged_by_filter.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module MergedByFilter + private + + def by_merged_by(items) + return items unless params.merged_by_id? || params.merged_by_username? + + mr_metrics_scope = MergeRequest::Metrics + mr_metrics_scope = mr_metrics_scope.merged_by(params.merged_by) + + items.join_metrics.merge(mr_metrics_scope) + end + + def by_negated_merged_by(items) + return items unless not_params.merged_by_id? || not_params.merged_by_username? + + mr_metrics_scope = MergeRequest::Metrics + mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merged_by) + + items.join_metrics.merge(mr_metrics_scope.invert_where) + end +end diff --git a/app/finders/merge_request/metrics_finder.rb b/app/finders/merge_request/metrics_finder.rb index 1a3732bbdf9c90..a41d0845360355 100644 --- a/app/finders/merge_request/metrics_finder.rb +++ b/app/finders/merge_request/metrics_finder.rb @@ -14,7 +14,8 @@ def execute items = init_collection items = by_target_project(items) items = by_merged_after(items) - by_merged_before(items) + items = by_merged_before(items) + by_merged_by(items) end private diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index b7de1c08f86284..a687cac00fbc9d 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -33,6 +33,7 @@ class MergeRequestsFinder < IssuableFinder extend ::Gitlab::Utils::Override include MergedAtFilter + include MergedByFilter def self.scalar_params @scalar_params ||= super + [ @@ -44,6 +45,8 @@ def self.scalar_params :environment, :merged_after, :merged_before, + :merged_by_id, + :merged_by_username, :reviewer_id, :reviewer_username, :source_branch, @@ -70,6 +73,7 @@ def filter_items(_items) items = by_draft(items) items = by_target_branch(items) items = by_merged_at(items) + items = by_merged_by(items) items = by_approvals(items) items = by_deployments(items) items = by_reviewer(items) @@ -82,6 +86,7 @@ def filter_negated_items(items) items = super(items) items = by_negated_reviewer(items) items = by_negated_approved_by(items) + items = by_negated_merged_by(items) items = by_negated_target_branch(items) by_negated_source_branch(items) end diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb index 2c218898dcf60a..f040812ad75ba9 100644 --- a/app/finders/merge_requests_finder/params.rb +++ b/app/finders/merge_requests_finder/params.rb @@ -19,5 +19,15 @@ def reviewer end end end + + def merged_by + strong_memoize(:merged_by) do + if merged_by_id? + User.find_by_id(params[:merged_by_id]) + elsif merged_by_username? + User.find_by_username(params[:merged_by_username]) + end + end + end end end diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index 3c592c0008f38a..3581b6ddb41818 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -13,6 +13,7 @@ class MergeRequest::Metrics < ApplicationRecord scope :merged_after, ->(date) { where(arel_table[:merged_at].gteq(date)) } scope :merged_before, ->(date) { where(arel_table[:merged_at].lteq(date.is_a?(Time) ? date.end_of_day : date)) } + scope :merged_by, ->(user) { where(merged_by_id: user) } scope :with_valid_time_to_merge, -> { where(arel_table[:merged_at].gt(arel_table[:created_at])) } scope :by_target_project, ->(project) { where(target_project_id: project) } diff --git a/app/views/shared/issuable/_merged_by_dropdown.html.haml b/app/views/shared/issuable/_merged_by_dropdown.html.haml new file mode 100644 index 00000000000000..3d44ff3d827e7a --- /dev/null +++ b/app/views/shared/issuable/_merged_by_dropdown.html.haml @@ -0,0 +1,9 @@ +#js-dropdown-merged-by.filtered-search-input-dropdown-menu.dropdown-menu + %ul{ data: { dropdown: true } } + - if current_user + = render 'shared/issuable/user_dropdown_item', + user: current_user + %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } + = render 'shared/issuable/user_dropdown_item', + user: User.new(username: '{{username}}', name: '{{name}}'), + avatar: { lazy: true, url: '{{avatar_url}}' } diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 8db7f7345f4da6..fee66b0cd8c55a 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -93,6 +93,7 @@ avatar: { lazy: true, url: '{{avatar_url}}' } = render_if_exists 'shared/issuable/approver_dropdown' = render_if_exists 'shared/issuable/approved_by_dropdown' + = render_if_exists 'shared/issuable/merged_by_dropdown' #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { value: 'None' } } -- GitLab From 6b76932ae076d4f97143fa074f5f28d4302a1f13 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sun, 17 Dec 2023 10:46:31 +0100 Subject: [PATCH 02/14] Update gettext file Signed-off-by: Martin Schurz --- locale/gitlab.pot | 3 +++ 1 file changed, 3 insertions(+) diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 2d958691959f54..c486a32cd5da8c 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -30402,6 +30402,9 @@ msgstr "" msgid "Merged this merge request." msgstr "" +msgid "Merged-By" +msgstr "" + msgid "Merged: %{merged}" msgstr "" -- GitLab From 4c6f3e8b596056c9974fd323352eb45cfc1020a1 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sun, 17 Dec 2023 12:22:11 +0100 Subject: [PATCH 03/14] Fix rspec tests Signed-off-by: Martin Schurz --- app/finders/merge_request/metrics_finder.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/finders/merge_request/metrics_finder.rb b/app/finders/merge_request/metrics_finder.rb index a41d0845360355..1a3732bbdf9c90 100644 --- a/app/finders/merge_request/metrics_finder.rb +++ b/app/finders/merge_request/metrics_finder.rb @@ -14,8 +14,7 @@ def execute items = init_collection items = by_target_project(items) items = by_merged_after(items) - items = by_merged_before(items) - by_merged_by(items) + by_merged_before(items) end private -- GitLab From 6c5863dd2ba2bfdf02fb0b3172cf58b1dff2ad2c Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sun, 17 Dec 2023 13:41:37 +0100 Subject: [PATCH 04/14] Add documentation for new parameters Signed-off-by: Martin Schurz --- doc/api/merge_requests.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index a852c7c0b961c5..c56356c20c8167 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,6 +64,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | +| `merged_by_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. | +| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_id`. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | -- GitLab From df9e94cf0a34f55b71d9efc1ea010bfdea512830 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sun, 17 Dec 2023 13:52:37 +0100 Subject: [PATCH 05/14] Add tests for new filter functions Signed-off-by: Martin Schurz --- .../add_extra_tokens_for_merge_requests.js | 8 +-- app/models/merge_request/metrics.rb | 2 +- .../add_extra_tokens_for_merge_requests.js | 2 +- spec/finders/merge_requests_finder_spec.rb | 52 +++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 4f59e72f5a426b..5bd5496c40c257 100644 --- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -35,8 +35,8 @@ export default ( icon: 'merge', tag: '@merged-by', }; - IssuableTokenKeys.tokenKeys.splice(2, 0, mergedByToken); - IssuableTokenKeys.tokenKeysWithAlternative.splice(2, 0, mergedByToken); + IssuableTokenKeys.tokenKeys.splice(3, 0, mergedByToken); + IssuableTokenKeys.tokenKeysWithAlternative.splice(3, 0, mergedByToken); const draftToken = { token: { @@ -130,7 +130,7 @@ export default ( }; if (gon.features.mrApprovedFilter) { - IssuableTokenKeys.tokenKeys.splice(3, 0, approvedToken.token); + IssuableTokenKeys.tokenKeys.splice(4, 0, approvedToken.token); IssuableTokenKeys.conditions.push(...approvedToken.conditions); } @@ -179,7 +179,7 @@ export default ( ], }; - const tokenPosition = gon.features.mrApprovedFilter ? 4 : 3; + const tokenPosition = gon.features.mrApprovedFilter ? 5 : 4; IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, approvedBy.token); IssuableTokenKeys.tokenKeysWithAlternative.splice( tokenPosition, diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index 3581b6ddb41818..c291fec9d3f347 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -13,7 +13,7 @@ class MergeRequest::Metrics < ApplicationRecord scope :merged_after, ->(date) { where(arel_table[:merged_at].gteq(date)) } scope :merged_before, ->(date) { where(arel_table[:merged_at].lteq(date.is_a?(Time) ? date.end_of_day : date)) } - scope :merged_by, ->(user) { where(merged_by_id: user) } + scope :merged_by, ->(user) { where(merged_by_id: user).where.not(merged_by_id: nil) } scope :with_valid_time_to_merge, -> { where(arel_table[:merged_at].gt(arel_table[:created_at])) } scope :by_target_project, ->(project) { where(target_project_id: project) } diff --git a/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 615ae6e448b4e1..5f93f38dc9a3fd 100644 --- a/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -49,7 +49,7 @@ const approvers = { export default (IssuableTokenKeys, disableBranchFilter = false) => { addExtraTokensForMergeRequests(IssuableTokenKeys, disableBranchFilter); - const tokenPosition = 3; + const tokenPosition = 4; IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvers.token]); IssuableTokenKeys.tokenKeysWithAlternative.splice( diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 3f9c1baec82711..e37525db801660 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -170,6 +170,58 @@ end end + context 'merged_by filtering' do + before do + merge_request1.metrics.update!(merged_by_id: user.id) + merge_request2.metrics.update!(merged_by_id: user.id) + merge_request3.metrics.update!(merged_by_id: user2.id) + merge_request4.metrics.update!(merged_by_id: user2.id) + end + + subject { described_class.new(user, params).execute } + + context 'by merged_by_id' do + let(:params) { { merged_by_id: user.id } } + let(:expected_mr) { [merge_request1, merge_request2] } + + it { is_expected.to contain_exactly(*expected_mr) } + end + + context 'by NOT merged_by_id' do + let(:params) { { not: { merged_by_id: user.id } } } + let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } + + it { is_expected.to contain_exactly(*expected_mr) } + end + + context 'by merged_by_username' do + let(:params) { { merged_by_username: user.username } } + let(:expected_mr) { [merge_request1, merge_request2] } + + it { is_expected.to contain_exactly(*expected_mr) } + end + + context 'by NOT merged_by_username' do + let(:params) { { not: { merged_by_username: user.username } } } + let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } + + it { is_expected.to contain_exactly(*expected_mr) } + end + + context 'by merged_by with unknown user' do + let(:params) { { merged_by_id: 99999 } } + + it { is_expected.to be_empty } + end + + context 'by NOT merged_by with unknown user' do + let(:params) { { not: { merged_by_id: 99999 } } } + let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } + + it { is_expected.to contain_exactly(*expected_mr) } + end + end + context 'filtering by group' do it 'includes all merge requests when user has access excluding merge requests from projects the user does not have access to' do private_project = allow_gitaly_n_plus_1 { create(:project, :private, group: group) } -- GitLab From 5609a1224a19b73a422b478214886fa27f3006f7 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Wed, 20 Dec 2023 23:40:17 +0100 Subject: [PATCH 06/14] Apply suggestion from tw Signed-off-by: Martin Schurz --- doc/api/merge_requests.md | 4 ++-- doc/user/project/merge_requests/index.md | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index c56356c20c8167..dcd48c3f830f70 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,8 +64,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merged_by_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. | -| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_id`. | +| `merged_by_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 3555d9ffa01bbc..8caca0aadc567f 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -82,6 +82,7 @@ or: > - Filtering by potential approvers was moved to GitLab Premium in 13.9. > - Filtering by `approved-by` moved to GitLab Premium in 13.9. > - Filtering by `source-branch` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134555) in GitLab 16.6. +> - Filtering by `merged-by` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. To filter the list of merge requests: @@ -95,6 +96,7 @@ To filter the list of merge requests: - **Approved-By**, for merge requests already approved by a user. **(PREMIUM ALL)**. - **Approver**, for merge requests that this user is eligible to approve. (For more information, read about [Code owners](../codeowners/index.md)). **(PREMIUM ALL)** + - **Merged-by**, for merge requests merged by this user. - **Reviewer**, for merge requests reviewed by this user. 1. Select or type the operator to use for filtering the attribute. The following operators are available: -- GitLab From 5eaa95f2cccd695cf39686de8ef3c1d204d1ba87 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Thu, 21 Dec 2023 11:26:44 +0100 Subject: [PATCH 07/14] Change query parameter merged_by_id to merged_by_user_id Applying suggestion from code review Signed-off-by: Martin Schurz --- app/finders/concerns/merged_by_filter.rb | 4 +-- app/finders/merge_requests_finder.rb | 2 +- app/finders/merge_requests_finder/params.rb | 4 +-- doc/api/merge_requests.md | 4 +-- spec/finders/merge_requests_finder_spec.rb | 29 +++++++++++++++------ 5 files changed, 28 insertions(+), 15 deletions(-) diff --git a/app/finders/concerns/merged_by_filter.rb b/app/finders/concerns/merged_by_filter.rb index 32f7755719aa68..31868f789568ed 100644 --- a/app/finders/concerns/merged_by_filter.rb +++ b/app/finders/concerns/merged_by_filter.rb @@ -4,7 +4,7 @@ module MergedByFilter private def by_merged_by(items) - return items unless params.merged_by_id? || params.merged_by_username? + return items unless params.merged_by_user_id? || params.merged_by_username? mr_metrics_scope = MergeRequest::Metrics mr_metrics_scope = mr_metrics_scope.merged_by(params.merged_by) @@ -13,7 +13,7 @@ def by_merged_by(items) end def by_negated_merged_by(items) - return items unless not_params.merged_by_id? || not_params.merged_by_username? + return items unless not_params.merged_by_user_id? || not_params.merged_by_username? mr_metrics_scope = MergeRequest::Metrics mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merged_by) diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index a687cac00fbc9d..90307ca575fc89 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -45,7 +45,7 @@ def self.scalar_params :environment, :merged_after, :merged_before, - :merged_by_id, + :merged_by_user_id, :merged_by_username, :reviewer_id, :reviewer_username, diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb index f040812ad75ba9..3f22926702e5e8 100644 --- a/app/finders/merge_requests_finder/params.rb +++ b/app/finders/merge_requests_finder/params.rb @@ -22,8 +22,8 @@ def reviewer def merged_by strong_memoize(:merged_by) do - if merged_by_id? - User.find_by_id(params[:merged_by_id]) + if merged_by_user_id? + User.find_by_id(params[:merged_by_user_id]) elsif merged_by_username? User.find_by_username(params[:merged_by_username]) end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index dcd48c3f830f70..845ebc93390975 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,8 +64,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merged_by_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merged_by_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index e37525db801660..9797610100bff7 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -180,15 +180,15 @@ subject { described_class.new(user, params).execute } - context 'by merged_by_id' do - let(:params) { { merged_by_id: user.id } } + context 'by merged_by_user_id' do + let(:params) { { merged_by_user_id: user.id } } let(:expected_mr) { [merge_request1, merge_request2] } it { is_expected.to contain_exactly(*expected_mr) } end - context 'by NOT merged_by_id' do - let(:params) { { not: { merged_by_id: user.id } } } + context 'by NOT merged_by_user_id' do + let(:params) { { not: { merged_by_user_id: user.id } } } let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } @@ -208,14 +208,27 @@ it { is_expected.to contain_exactly(*expected_mr) } end - context 'by merged_by with unknown user' do - let(:params) { { merged_by_id: 99999 } } + context 'by merged_by with unknown user id' do + let(:params) { { merged_by_user_id: 99999 } } it { is_expected.to be_empty } end - context 'by NOT merged_by with unknown user' do - let(:params) { { not: { merged_by_id: 99999 } } } + context 'by merged_by with unknown user name' do + let(:params) { { merged_by_username: 'does-not-exist' } } + + it { is_expected.to be_empty } + end + + context 'by NOT merged_by with unknown user id' do + let(:params) { { not: { merged_by_user_id: 99999 } } } + let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } + + it { is_expected.to contain_exactly(*expected_mr) } + end + + context 'by NOT merged_by with unknown user name' do + let(:params) { { not: { merged_by_username: 'does-not-exist' } } } let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } -- GitLab From 5603a4a47711ab004b5ab821beb101c0c6014fb0 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Fri, 22 Dec 2023 17:42:05 +0100 Subject: [PATCH 08/14] Rewrite queries to never use NULL Signed-off-by: Martin Schurz --- app/finders/concerns/merged_by_filter.rb | 12 ++++++++++-- app/models/merge_request/metrics.rb | 2 +- spec/finders/merge_requests_finder_spec.rb | 20 ++++++++++++-------- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/app/finders/concerns/merged_by_filter.rb b/app/finders/concerns/merged_by_filter.rb index 31868f789568ed..0ea15ec3f54633 100644 --- a/app/finders/concerns/merged_by_filter.rb +++ b/app/finders/concerns/merged_by_filter.rb @@ -9,7 +9,11 @@ def by_merged_by(items) mr_metrics_scope = MergeRequest::Metrics mr_metrics_scope = mr_metrics_scope.merged_by(params.merged_by) - items.join_metrics.merge(mr_metrics_scope) + if params.merged_by + items.join_metrics.merge(mr_metrics_scope) + else # merged_by user not found + items.none + end end def by_negated_merged_by(items) @@ -18,6 +22,10 @@ def by_negated_merged_by(items) mr_metrics_scope = MergeRequest::Metrics mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merged_by) - items.join_metrics.merge(mr_metrics_scope.invert_where) + if not_params.merged_by + items.join_metrics.merge(mr_metrics_scope.invert_where).or(items.join_metrics.open_and_closed) + else # merged_by user not found + items + end end end diff --git a/app/models/merge_request/metrics.rb b/app/models/merge_request/metrics.rb index c291fec9d3f347..3581b6ddb41818 100644 --- a/app/models/merge_request/metrics.rb +++ b/app/models/merge_request/metrics.rb @@ -13,7 +13,7 @@ class MergeRequest::Metrics < ApplicationRecord scope :merged_after, ->(date) { where(arel_table[:merged_at].gteq(date)) } scope :merged_before, ->(date) { where(arel_table[:merged_at].lteq(date.is_a?(Time) ? date.end_of_day : date)) } - scope :merged_by, ->(user) { where(merged_by_id: user).where.not(merged_by_id: nil) } + scope :merged_by, ->(user) { where(merged_by_id: user) } scope :with_valid_time_to_merge, -> { where(arel_table[:merged_at].gt(arel_table[:created_at])) } scope :by_target_project, ->(project) { where(target_project_id: project) } diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 9797610100bff7..3d662830ae6fc4 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -172,62 +172,66 @@ context 'merged_by filtering' do before do + merge_request1.update!(state_id: MergeRequest.available_states[:merged]) merge_request1.metrics.update!(merged_by_id: user.id) + merge_request2.update!(state_id: MergeRequest.available_states[:merged]) merge_request2.metrics.update!(merged_by_id: user.id) + merge_request3.update!(state_id: MergeRequest.available_states[:merged]) merge_request3.metrics.update!(merged_by_id: user2.id) + merge_request4.update!(state_id: MergeRequest.available_states[:merged]) merge_request4.metrics.update!(merged_by_id: user2.id) end subject { described_class.new(user, params).execute } - context 'by merged_by_user_id' do + describe 'by merged_by_user_id' do let(:params) { { merged_by_user_id: user.id } } let(:expected_mr) { [merge_request1, merge_request2] } it { is_expected.to contain_exactly(*expected_mr) } end - context 'by NOT merged_by_user_id' do + describe 'by NOT merged_by_user_id' do let(:params) { { not: { merged_by_user_id: user.id } } } let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } end - context 'by merged_by_username' do + describe 'by merged_by_username' do let(:params) { { merged_by_username: user.username } } let(:expected_mr) { [merge_request1, merge_request2] } it { is_expected.to contain_exactly(*expected_mr) } end - context 'by NOT merged_by_username' do + describe 'by NOT merged_by_username' do let(:params) { { not: { merged_by_username: user.username } } } let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } end - context 'by merged_by with unknown user id' do + describe 'by merged_by with unknown user id' do let(:params) { { merged_by_user_id: 99999 } } it { is_expected.to be_empty } end - context 'by merged_by with unknown user name' do + describe 'by merged_by with unknown user name' do let(:params) { { merged_by_username: 'does-not-exist' } } it { is_expected.to be_empty } end - context 'by NOT merged_by with unknown user id' do + describe 'by NOT merged_by with unknown user id' do let(:params) { { not: { merged_by_user_id: 99999 } } } let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } end - context 'by NOT merged_by with unknown user name' do + describe 'by NOT merged_by with unknown user name' do let(:params) { { not: { merged_by_username: 'does-not-exist' } } } let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } -- GitLab From df868de589bb3c7a3f71ee7301edeabfa99d5aad Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Tue, 26 Dec 2023 03:36:50 +0100 Subject: [PATCH 09/14] Update documentation for API Signed-off-by: Martin Schurz --- doc/api/merge_requests.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 845ebc93390975..af9491c806bcdf 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -68,7 +68,7 @@ Supported attributes: | `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merged_by_user_id`, `merged_by_username`. | | `order_by` | string | No | Returns requests ordered by `created_at`, `title`, or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8.| | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | @@ -246,9 +246,11 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `iids[]` | integer array | No | Returns the request having the given `iid`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | +| `merged_by_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merged_by_user_id`, `merged_by_username` . | | `order_by` | string | No | Returns requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. | | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | @@ -430,10 +432,12 @@ Supported attributes: | `created_after` | datetime | No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `created_before` | datetime | No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | +| `merged_by_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `non_archived` | boolean | No | Returns merge requests from non archived projects only. Default is `true`. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merged_by_user_id`, `merged_by_username`. | | `order_by` | string | No | Returns merge requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. | | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | -- GitLab From ac551daf17afd6600bf023f0d684c95119791cc9 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Tue, 26 Dec 2023 04:22:09 +0100 Subject: [PATCH 10/14] Add API bindings Signed-off-by: Martin Schurz --- app/finders/concerns/merged_by_filter.rb | 2 + lib/api/helpers/merge_requests_helpers.rb | 5 ++ spec/requests/api/merge_requests_spec.rb | 76 ++++++++++++++++++++++- 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/app/finders/concerns/merged_by_filter.rb b/app/finders/concerns/merged_by_filter.rb index 0ea15ec3f54633..4a06dba706e129 100644 --- a/app/finders/concerns/merged_by_filter.rb +++ b/app/finders/concerns/merged_by_filter.rb @@ -23,6 +23,8 @@ def by_negated_merged_by(items) mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merged_by) if not_params.merged_by + # we return all items not merged by the specified users AND all items that are not merged + # this makes it easier to combine with additional filters items.join_metrics.merge(mr_metrics_scope.invert_where).or(items.join_metrics.open_and_closed) else # merged_by user not found items diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb index 0a0d70520efa6e..b99e311a5efff0 100644 --- a/lib/api/helpers/merge_requests_helpers.rb +++ b/lib/api/helpers/merge_requests_helpers.rb @@ -36,6 +36,11 @@ module MergeRequestsHelpers desc: "#{options[:prefix]}Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone." optional :my_reaction_emoji, type: String, desc: "#{options[:prefix]}Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction." + optional :merged_by_user_id, type: Integer, + desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`." + optional :merged_by_username, type: String, + desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`." + mutually_exclusive :merged_by_user_id, :merged_by_username end params :merge_requests_base_params do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 6000fa29dc4dca..302e0fb1e35850 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -27,7 +27,7 @@ shared_context 'with merge requests' do let_it_be(:milestone1) { create(:milestone, title: '0.9', project: project) } - let_it_be(:merge_request_merged) { create(:merge_request, state: "merged", author: user, assignees: [user], source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds, updated_at: base_time + 1.hour, merge_commit_sha: '9999999999999999999999999999999999999999') } + let_it_be(:merge_request_merged) { create(:merge_request, :with_merged_metrics, state: "merged", author: user, assignees: [user], source_project: project, target_project: project, title: "Merged test", created_at: base_time + 2.seconds, updated_at: base_time + 1.hour, merge_commit_sha: '9999999999999999999999999999999999999999', merged_by: user) } let_it_be(:merge_request) { create(:merge_request, :simple, milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, source_branch: 'markdown', title: "Test", created_at: base_time, updated_at: base_time + 3.hours) } let_it_be(:merge_request_closed) { create(:merge_request, state: "closed", milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, title: "Closed test", created_at: base_time + 1.second, updated_at: base_time) } let_it_be(:merge_request_locked) { create(:merge_request, state: "locked", milestone: milestone1, author: user, assignees: [user], source_project: project, target_project: project, title: "Locked test", created_at: base_time + 1.second, updated_at: base_time + 2.hours) } @@ -669,6 +669,45 @@ end end end + + context 'filter by merge_user' do + context 'with merged_by_user_id' do + let(:params) { { not: { merged_by_user_id: user.id } } } + + it 'returns merge requests that do not have the given merge_user' do + get api(endpoint_path, user), params: params + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to be_an(Array) + expect(json_response.length).to eq(3) + expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request_merged) + end + end + + context 'with merged_by_username' do + let(:params) { { not: { merged_by_username: user.username } } } + + it 'returns merge requests that do not have the given merge_user' do + get api(endpoint_path, user), params: params + + expect(response).to have_gitlab_http_status(:ok) + expect(json_response).to be_an(Array) + expect(json_response.length).to eq(3) + expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request_merged) + end + end + + context 'when both merged_by_user_id and merged_by_username' do + let(:params) { { not: { merged_by_user_id: user.id, merged_by_username: user.username } } } + + it 'returns a 400' do + get api('/merge_requests', user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('not[merged_by_user_id], not[merged_by_username] are mutually exclusive') + end + end + end end context 'source_branch param' do @@ -931,6 +970,41 @@ end end + context 'filter by merge_user' do + let(:params) { { scope: :all } } + + context 'with merged_by_user_id' do + let(:params) { super().merge(merged_by_user_id: user.id) } + + it 'returns merged merge requests for the given user' do + get api('/merge_requests', user), params: params + + expect_response_contain_exactly(merge_request_merged.id) + end + end + + context 'with merged_by_username' do + let(:params) { super().merge(merged_by_username: user.username) } + + it 'returns merged merge requests for the given user' do + get api('/merge_requests', user), params: params + + expect_response_contain_exactly(merge_request_merged.id) + end + end + + context 'with both merged_by_user_id and merged_by_username' do + let(:params) { super().merge(merged_by_user_id: user.id, merged_by_username: user.username) } + + it 'returns a 400' do + get api('/merge_requests', user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('merged_by_user_id, merged_by_username are mutually exclusive') + end + end + end + it 'returns an array of merge requests assigned to the given user' do merge_request3 = create(:merge_request, :simple, author: user, assignees: [user2], source_project: project2, target_project: project2, source_branch: 'other-branch') -- GitLab From e131904057de3df4ccd1b0bbfc2b7a5a22f30642 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Wed, 27 Dec 2023 02:14:30 +0100 Subject: [PATCH 11/14] Rename merged_by to merge_user The name merged_by is deprecated and frontend facing things should use merge_user instead Signed-off-by: Martin Schurz --- .../add_extra_tokens_for_merge_requests.js | 16 ++++----- .../available_dropdown_mappings.js | 6 ++-- .../javascripts/filtered_search/constants.js | 4 +-- .../filtered_search_bar/constants.js | 4 +-- ...rged_by_filter.rb => merge_user_filter.rb} | 22 ++++++------ app/finders/merge_requests_finder.rb | 10 +++--- app/finders/merge_requests_finder/params.rb | 12 +++---- ...ml.haml => _merge_user_dropdown.html.haml} | 2 +- .../shared/issuable/_search_bar.html.haml | 2 +- doc/api/merge_requests.md | 18 +++++----- doc/user/project/merge_requests/index.md | 2 +- lib/api/helpers/merge_requests_helpers.rb | 10 +++--- spec/finders/merge_requests_finder_spec.rb | 34 +++++++++---------- spec/requests/api/merge_requests_spec.rb | 28 +++++++-------- 14 files changed, 85 insertions(+), 85 deletions(-) rename app/finders/concerns/{merged_by_filter.rb => merge_user_filter.rb} (55%) rename app/views/shared/issuable/{_merged_by_dropdown.html.haml => _merge_user_dropdown.html.haml} (83%) diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 5bd5496c40c257..022f614342bd6d 100644 --- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -1,10 +1,10 @@ import { __ } from '~/locale'; import { TOKEN_TITLE_APPROVED_BY, - TOKEN_TITLE_MERGED_BY, + TOKEN_TITLE_MERGE_USER, TOKEN_TITLE_REVIEWER, TOKEN_TYPE_APPROVED_BY, - TOKEN_TYPE_MERGED_BY, + TOKEN_TYPE_MERGE_USER, TOKEN_TYPE_REVIEWER, TOKEN_TYPE_TARGET_BRANCH, TOKEN_TYPE_SOURCE_BRANCH, @@ -26,17 +26,17 @@ export default ( IssuableTokenKeys.tokenKeys.splice(2, 0, reviewerToken); IssuableTokenKeys.tokenKeysWithAlternative.splice(2, 0, reviewerToken); - const mergedByToken = { - formattedKey: TOKEN_TITLE_MERGED_BY, - key: TOKEN_TYPE_MERGED_BY, + const mergeUserToken = { + formattedKey: TOKEN_TITLE_MERGE_USER, + key: TOKEN_TYPE_MERGE_USER, type: 'string', param: 'username', symbol: '@', icon: 'merge', - tag: '@merged-by', + tag: '@merge_user', }; - IssuableTokenKeys.tokenKeys.splice(3, 0, mergedByToken); - IssuableTokenKeys.tokenKeysWithAlternative.splice(3, 0, mergedByToken); + IssuableTokenKeys.tokenKeys.splice(3, 0, mergeUserToken); + IssuableTokenKeys.tokenKeysWithAlternative.splice(3, 0, mergeUserToken); const draftToken = { token: { diff --git a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js index 2e306c30906844..e1a5b269fa70ec 100644 --- a/app/assets/javascripts/filtered_search/available_dropdown_mappings.js +++ b/app/assets/javascripts/filtered_search/available_dropdown_mappings.js @@ -2,7 +2,7 @@ import { sortMilestonesByDueDate } from '~/milestones/utils'; import { mergeUrlParams } from '~/lib/utils/url_utility'; import { TOKEN_TYPE_APPROVED_BY, - TOKEN_TYPE_MERGED_BY, + TOKEN_TYPE_MERGE_USER, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, @@ -87,10 +87,10 @@ export default class AvailableDropdownMappings { gl: DropdownUser, element: this.container.querySelector('#js-dropdown-reviewer'), }, - [TOKEN_TYPE_MERGED_BY]: { + [TOKEN_TYPE_MERGE_USER]: { reference: null, gl: DropdownUser, - element: this.container.querySelector('#js-dropdown-merged-by'), + element: this.container.querySelector('#js-dropdown-merge-user'), }, attention: { reference: null, diff --git a/app/assets/javascripts/filtered_search/constants.js b/app/assets/javascripts/filtered_search/constants.js index ddf7d8113cd557..39fa936895c419 100644 --- a/app/assets/javascripts/filtered_search/constants.js +++ b/app/assets/javascripts/filtered_search/constants.js @@ -1,6 +1,6 @@ import { TOKEN_TYPE_APPROVED_BY, - TOKEN_TYPE_MERGED_BY, + TOKEN_TYPE_MERGE_USER, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_REVIEWER, @@ -10,7 +10,7 @@ export const USER_TOKEN_TYPES = [ TOKEN_TYPE_AUTHOR, TOKEN_TYPE_ASSIGNEE, TOKEN_TYPE_APPROVED_BY, - TOKEN_TYPE_MERGED_BY, + TOKEN_TYPE_MERGE_USER, TOKEN_TYPE_REVIEWER, 'attention', ]; diff --git a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js index 8470b5733264a4..8fc4de1798febe 100644 --- a/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js +++ b/app/assets/javascripts/vue_shared/components/filtered_search_bar/constants.js @@ -62,7 +62,7 @@ export const TOKEN_EMPTY_SEARCH_TERM = { }; export const TOKEN_TITLE_APPROVED_BY = __('Approved-By'); -export const TOKEN_TITLE_MERGED_BY = __('Merged-By'); +export const TOKEN_TITLE_MERGE_USER = __('Merged-By'); export const TOKEN_TITLE_ASSIGNEE = s__('SearchToken|Assignee'); export const TOKEN_TITLE_AUTHOR = __('Author'); export const TOKEN_TITLE_CONFIDENTIAL = __('Confidential'); @@ -85,7 +85,7 @@ export const TOKEN_TITLE_CREATED = __('Created date'); export const TOKEN_TITLE_CLOSED = __('Closed date'); export const TOKEN_TYPE_APPROVED_BY = 'approved-by'; -export const TOKEN_TYPE_MERGED_BY = 'merged-by'; +export const TOKEN_TYPE_MERGE_USER = 'merge-user'; export const TOKEN_TYPE_ASSIGNEE = 'assignee'; export const TOKEN_TYPE_AUTHOR = 'author'; export const TOKEN_TYPE_CONFIDENTIAL = 'confidential'; diff --git a/app/finders/concerns/merged_by_filter.rb b/app/finders/concerns/merge_user_filter.rb similarity index 55% rename from app/finders/concerns/merged_by_filter.rb rename to app/finders/concerns/merge_user_filter.rb index 4a06dba706e129..8cfde199a2842c 100644 --- a/app/finders/concerns/merged_by_filter.rb +++ b/app/finders/concerns/merge_user_filter.rb @@ -1,32 +1,32 @@ # frozen_string_literal: true -module MergedByFilter +module MergeUserFilter private - def by_merged_by(items) - return items unless params.merged_by_user_id? || params.merged_by_username? + def by_merge_user(items) + return items unless params.merge_user_id? || params.merge_user_username? mr_metrics_scope = MergeRequest::Metrics - mr_metrics_scope = mr_metrics_scope.merged_by(params.merged_by) + mr_metrics_scope = mr_metrics_scope.merged_by(params.merge_user) - if params.merged_by + if params.merge_user items.join_metrics.merge(mr_metrics_scope) - else # merged_by user not found + else # merge_user user not found items.none end end - def by_negated_merged_by(items) - return items unless not_params.merged_by_user_id? || not_params.merged_by_username? + def by_negated_merge_user(items) + return items unless not_params.merge_user_id? || not_params.merge_user_username? mr_metrics_scope = MergeRequest::Metrics - mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merged_by) + mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merge_user) - if not_params.merged_by + if not_params.merge_user # we return all items not merged by the specified users AND all items that are not merged # this makes it easier to combine with additional filters items.join_metrics.merge(mr_metrics_scope.invert_where).or(items.join_metrics.open_and_closed) - else # merged_by user not found + else # merge_user user not found items end end diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 90307ca575fc89..6f5b60303a1d84 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -33,7 +33,7 @@ class MergeRequestsFinder < IssuableFinder extend ::Gitlab::Utils::Override include MergedAtFilter - include MergedByFilter + include MergeUserFilter def self.scalar_params @scalar_params ||= super + [ @@ -43,10 +43,10 @@ def self.scalar_params :deployed_before, :draft, :environment, + :merge_user_id, + :merge_user_username, :merged_after, :merged_before, - :merged_by_user_id, - :merged_by_username, :reviewer_id, :reviewer_username, :source_branch, @@ -72,8 +72,8 @@ def filter_items(_items) items = by_source_branch(items) items = by_draft(items) items = by_target_branch(items) + items = by_merge_user(items) items = by_merged_at(items) - items = by_merged_by(items) items = by_approvals(items) items = by_deployments(items) items = by_reviewer(items) @@ -86,7 +86,7 @@ def filter_negated_items(items) items = super(items) items = by_negated_reviewer(items) items = by_negated_approved_by(items) - items = by_negated_merged_by(items) + items = by_negated_merge_user(items) items = by_negated_target_branch(items) by_negated_source_branch(items) end diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb index 3f22926702e5e8..22ebe73abaccd4 100644 --- a/app/finders/merge_requests_finder/params.rb +++ b/app/finders/merge_requests_finder/params.rb @@ -20,12 +20,12 @@ def reviewer end end - def merged_by - strong_memoize(:merged_by) do - if merged_by_user_id? - User.find_by_id(params[:merged_by_user_id]) - elsif merged_by_username? - User.find_by_username(params[:merged_by_username]) + def merge_user + strong_memoize(:merge_user) do + if merge_user_id? + User.find_by_id(params[:merge_user_id]) + elsif merge_user_username? + User.find_by_username(params[:merge_user_username]) end end end diff --git a/app/views/shared/issuable/_merged_by_dropdown.html.haml b/app/views/shared/issuable/_merge_user_dropdown.html.haml similarity index 83% rename from app/views/shared/issuable/_merged_by_dropdown.html.haml rename to app/views/shared/issuable/_merge_user_dropdown.html.haml index 3d44ff3d827e7a..8577eb821e62f7 100644 --- a/app/views/shared/issuable/_merged_by_dropdown.html.haml +++ b/app/views/shared/issuable/_merge_user_dropdown.html.haml @@ -1,4 +1,4 @@ -#js-dropdown-merged-by.filtered-search-input-dropdown-menu.dropdown-menu +#js-dropdown-merge-user.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } - if current_user = render 'shared/issuable/user_dropdown_item', diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index fee66b0cd8c55a..55cd3b76cc4d5a 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -93,7 +93,7 @@ avatar: { lazy: true, url: '{{avatar_url}}' } = render_if_exists 'shared/issuable/approver_dropdown' = render_if_exists 'shared/issuable/approved_by_dropdown' - = render_if_exists 'shared/issuable/merged_by_dropdown' + = render_if_exists 'shared/issuable/merge_user_dropdown' #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { value: 'None' } } diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index af9491c806bcdf..5114430a924b5c 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,11 +64,11 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merged_by_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merged_by_user_id`, `merged_by_username`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merge_user_id`, `merge_user_username`. | | `order_by` | string | No | Returns requests ordered by `created_at`, `title`, or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8.| | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | @@ -246,11 +246,11 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `iids[]` | integer array | No | Returns the request having the given `iid`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merged_by_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merged_by_user_id`, `merged_by_username` . | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merge_user_id`, `merge_user_username` . | | `order_by` | string | No | Returns requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. | | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | @@ -432,12 +432,12 @@ Supported attributes: | `created_after` | datetime | No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `created_before` | datetime | No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merged_by_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merged_by_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `non_archived` | boolean | No | Returns merge requests from non archived projects only. Default is `true`. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merged_by_user_id`, `merged_by_username`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merge_user_id`, `merge_user_username`. | | `order_by` | string | No | Returns merge requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. | | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 8caca0aadc567f..d68aa1660dc26c 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -96,7 +96,7 @@ To filter the list of merge requests: - **Approved-By**, for merge requests already approved by a user. **(PREMIUM ALL)**. - **Approver**, for merge requests that this user is eligible to approve. (For more information, read about [Code owners](../codeowners/index.md)). **(PREMIUM ALL)** - - **Merged-by**, for merge requests merged by this user. + - **Merged-By**, for merge requests merged by this user. - **Reviewer**, for merge requests reviewed by this user. 1. Select or type the operator to use for filtering the attribute. The following operators are available: diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb index b99e311a5efff0..e6e1770280c7f5 100644 --- a/lib/api/helpers/merge_requests_helpers.rb +++ b/lib/api/helpers/merge_requests_helpers.rb @@ -36,11 +36,11 @@ module MergeRequestsHelpers desc: "#{options[:prefix]}Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone." optional :my_reaction_emoji, type: String, desc: "#{options[:prefix]}Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction." - optional :merged_by_user_id, type: Integer, - desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merged_by_username`." - optional :merged_by_username, type: String, - desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merged_by_user_id`." - mutually_exclusive :merged_by_user_id, :merged_by_username + optional :merge_user_id, type: Integer, + desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`." + optional :merge_user_username, type: String, + desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`." + mutually_exclusive :merge_user_id, :merge_user_username end params :merge_requests_base_params do diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 3d662830ae6fc4..180eb70fc5c397 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -170,7 +170,7 @@ end end - context 'merged_by filtering' do + context 'merge_user filtering' do before do merge_request1.update!(state_id: MergeRequest.available_states[:merged]) merge_request1.metrics.update!(merged_by_id: user.id) @@ -184,55 +184,55 @@ subject { described_class.new(user, params).execute } - describe 'by merged_by_user_id' do - let(:params) { { merged_by_user_id: user.id } } + describe 'by merge_user_id' do + let(:params) { { merge_user_id: user.id } } let(:expected_mr) { [merge_request1, merge_request2] } it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by NOT merged_by_user_id' do - let(:params) { { not: { merged_by_user_id: user.id } } } + describe 'by NOT merge_user_id' do + let(:params) { { not: { merge_user_id: user.id } } } let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by merged_by_username' do - let(:params) { { merged_by_username: user.username } } + describe 'by merge_user_username' do + let(:params) { { merge_user_username: user.username } } let(:expected_mr) { [merge_request1, merge_request2] } it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by NOT merged_by_username' do - let(:params) { { not: { merged_by_username: user.username } } } + describe 'by NOT merge_user_username' do + let(:params) { { not: { merge_user_username: user.username } } } let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by merged_by with unknown user id' do - let(:params) { { merged_by_user_id: 99999 } } + describe 'by merge_user_id with unknown user id' do + let(:params) { { merge_user_id: 99999 } } it { is_expected.to be_empty } end - describe 'by merged_by with unknown user name' do - let(:params) { { merged_by_username: 'does-not-exist' } } + describe 'by merge_user_username with unknown user name' do + let(:params) { { merge_user_username: 'does-not-exist' } } it { is_expected.to be_empty } end - describe 'by NOT merged_by with unknown user id' do - let(:params) { { not: { merged_by_user_id: 99999 } } } + describe 'by NOT merge_user_id with unknown user id' do + let(:params) { { not: { merged_user_id: 99999 } } } let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by NOT merged_by with unknown user name' do - let(:params) { { not: { merged_by_username: 'does-not-exist' } } } + describe 'by NOT merge_user_username with unknown user name' do + let(:params) { { not: { merge_user_username: 'does-not-exist' } } } let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } it { is_expected.to contain_exactly(*expected_mr) } diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 302e0fb1e35850..e16efc7f534d54 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -671,8 +671,8 @@ end context 'filter by merge_user' do - context 'with merged_by_user_id' do - let(:params) { { not: { merged_by_user_id: user.id } } } + context 'with merge_user_id' do + let(:params) { { not: { merge_user_id: user.id } } } it 'returns merge requests that do not have the given merge_user' do get api(endpoint_path, user), params: params @@ -684,8 +684,8 @@ end end - context 'with merged_by_username' do - let(:params) { { not: { merged_by_username: user.username } } } + context 'with merge_user_username' do + let(:params) { { not: { merge_user_username: user.username } } } it 'returns merge requests that do not have the given merge_user' do get api(endpoint_path, user), params: params @@ -697,14 +697,14 @@ end end - context 'when both merged_by_user_id and merged_by_username' do - let(:params) { { not: { merged_by_user_id: user.id, merged_by_username: user.username } } } + context 'when both merge_user_id and merge_user_username' do + let(:params) { { not: { merge_user_id: user.id, merge_user_username: user.username } } } it 'returns a 400' do get api('/merge_requests', user), params: params expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to eq('not[merged_by_user_id], not[merged_by_username] are mutually exclusive') + expect(json_response['error']).to eq('not[merge_user_id], not[merge_user_username] are mutually exclusive') end end end @@ -973,8 +973,8 @@ context 'filter by merge_user' do let(:params) { { scope: :all } } - context 'with merged_by_user_id' do - let(:params) { super().merge(merged_by_user_id: user.id) } + context 'with merge_user_id' do + let(:params) { super().merge(merge_user_id: user.id) } it 'returns merged merge requests for the given user' do get api('/merge_requests', user), params: params @@ -983,8 +983,8 @@ end end - context 'with merged_by_username' do - let(:params) { super().merge(merged_by_username: user.username) } + context 'with merge_user_username' do + let(:params) { super().merge(merge_user_username: user.username) } it 'returns merged merge requests for the given user' do get api('/merge_requests', user), params: params @@ -993,14 +993,14 @@ end end - context 'with both merged_by_user_id and merged_by_username' do - let(:params) { super().merge(merged_by_user_id: user.id, merged_by_username: user.username) } + context 'with both merge_user_id and merge_user_username' do + let(:params) { super().merge(merge_user_id: user.id, merge_user_username: user.username) } it 'returns a 400' do get api('/merge_requests', user), params: params expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to eq('merged_by_user_id, merged_by_username are mutually exclusive') + expect(json_response['error']).to eq('merge_user_id, merge_user_username are mutually exclusive') end end end -- GitLab From 5c0db5e0659fd3245cdd6b2ecb456c65c59e3414 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Sat, 6 Jan 2024 15:18:05 +0100 Subject: [PATCH 12/14] Remove option to negate search Signed-off-by: Martin Schurz --- .../add_extra_tokens_for_merge_requests.js | 1 + app/finders/concerns/merge_user_filter.rb | 15 ------- app/finders/merge_requests_finder.rb | 1 - doc/api/merge_requests.md | 6 +-- lib/api/helpers/merge_requests_helpers.rb | 10 ++--- spec/finders/merge_requests_finder_spec.rb | 28 ------------- spec/requests/api/merge_requests_spec.rb | 39 ------------------- 7 files changed, 9 insertions(+), 91 deletions(-) diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 022f614342bd6d..5a40effb7d9573 100644 --- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -34,6 +34,7 @@ export default ( symbol: '@', icon: 'merge', tag: '@merge_user', + hideNotEqual: true, }; IssuableTokenKeys.tokenKeys.splice(3, 0, mergeUserToken); IssuableTokenKeys.tokenKeysWithAlternative.splice(3, 0, mergeUserToken); diff --git a/app/finders/concerns/merge_user_filter.rb b/app/finders/concerns/merge_user_filter.rb index 8cfde199a2842c..160f995c240d1a 100644 --- a/app/finders/concerns/merge_user_filter.rb +++ b/app/finders/concerns/merge_user_filter.rb @@ -15,19 +15,4 @@ def by_merge_user(items) items.none end end - - def by_negated_merge_user(items) - return items unless not_params.merge_user_id? || not_params.merge_user_username? - - mr_metrics_scope = MergeRequest::Metrics - mr_metrics_scope = mr_metrics_scope.merged_by(not_params.merge_user) - - if not_params.merge_user - # we return all items not merged by the specified users AND all items that are not merged - # this makes it easier to combine with additional filters - items.join_metrics.merge(mr_metrics_scope.invert_where).or(items.join_metrics.open_and_closed) - else # merge_user user not found - items - end - end end diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index 6f5b60303a1d84..c19d8b68f605a5 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -86,7 +86,6 @@ def filter_negated_items(items) items = super(items) items = by_negated_reviewer(items) items = by_negated_approved_by(items) - items = by_negated_merge_user(items) items = by_negated_target_branch(items) by_negated_source_branch(items) end diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 5114430a924b5c..2e4ca7df305ba1 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -68,7 +68,7 @@ Supported attributes: | `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merge_user_id`, `merge_user_username`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | | `order_by` | string | No | Returns requests ordered by `created_at`, `title`, or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8.| | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | @@ -250,7 +250,7 @@ Supported attributes: | `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merge_user_id`, `merge_user_username` . | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | | `order_by` | string | No | Returns requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. | | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | @@ -437,7 +437,7 @@ Supported attributes: | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `non_archived` | boolean | No | Returns merge requests from non archived projects only. Default is `true`. | -| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`, `merge_user_id`, `merge_user_username`. | +| `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | | `order_by` | string | No | Returns merge requests ordered by `created_at`, `title` or `updated_at` fields. Default is `created_at`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/331625) in GitLab 14.8. | | `reviewer_id` | integer | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given user `id`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_username`. | | `reviewer_username` | string | No | Returns merge requests which have the user as a [reviewer](../user/project/merge_requests/reviews/index.md) with the given `username`. `None` returns merge requests with no reviewers. `Any` returns merge requests with any reviewer. Mutually exclusive with `reviewer_id`. | diff --git a/lib/api/helpers/merge_requests_helpers.rb b/lib/api/helpers/merge_requests_helpers.rb index e6e1770280c7f5..fbb52e22434373 100644 --- a/lib/api/helpers/merge_requests_helpers.rb +++ b/lib/api/helpers/merge_requests_helpers.rb @@ -36,11 +36,6 @@ module MergeRequestsHelpers desc: "#{options[:prefix]}Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone." optional :my_reaction_emoji, type: String, desc: "#{options[:prefix]}Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction." - optional :merge_user_id, type: Integer, - desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`." - optional :merge_user_username, type: String, - desc: "#{options[:prefix]}Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`." - mutually_exclusive :merge_user_id, :merge_user_username end params :merge_requests_base_params do @@ -113,6 +108,11 @@ module MergeRequestsHelpers optional :approved, type: String, values: %w[yes no], desc: 'Filters merge requests by their `approved` status. `yes` returns only approved merge requests. `no` returns only non-approved merge requests.' + optional :merge_user_id, type: Integer, + desc: "Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`." + optional :merge_user_username, type: String, + desc: "Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`." + mutually_exclusive :merge_user_id, :merge_user_username end params :optional_scope_param do diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index 180eb70fc5c397..af414a6fc53aac 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -191,13 +191,6 @@ it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by NOT merge_user_id' do - let(:params) { { not: { merge_user_id: user.id } } } - let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } - - it { is_expected.to contain_exactly(*expected_mr) } - end - describe 'by merge_user_username' do let(:params) { { merge_user_username: user.username } } let(:expected_mr) { [merge_request1, merge_request2] } @@ -205,13 +198,6 @@ it { is_expected.to contain_exactly(*expected_mr) } end - describe 'by NOT merge_user_username' do - let(:params) { { not: { merge_user_username: user.username } } } - let(:expected_mr) { [merge_request3, merge_request4, merge_request5] } - - it { is_expected.to contain_exactly(*expected_mr) } - end - describe 'by merge_user_id with unknown user id' do let(:params) { { merge_user_id: 99999 } } @@ -223,20 +209,6 @@ it { is_expected.to be_empty } end - - describe 'by NOT merge_user_id with unknown user id' do - let(:params) { { not: { merged_user_id: 99999 } } } - let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } - - it { is_expected.to contain_exactly(*expected_mr) } - end - - describe 'by NOT merge_user_username with unknown user name' do - let(:params) { { not: { merge_user_username: 'does-not-exist' } } } - let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } - - it { is_expected.to contain_exactly(*expected_mr) } - end end context 'filtering by group' do diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index e16efc7f534d54..3094644d7114a7 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -669,45 +669,6 @@ end end end - - context 'filter by merge_user' do - context 'with merge_user_id' do - let(:params) { { not: { merge_user_id: user.id } } } - - it 'returns merge requests that do not have the given merge_user' do - get api(endpoint_path, user), params: params - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_an(Array) - expect(json_response.length).to eq(3) - expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request_merged) - end - end - - context 'with merge_user_username' do - let(:params) { { not: { merge_user_username: user.username } } } - - it 'returns merge requests that do not have the given merge_user' do - get api(endpoint_path, user), params: params - - expect(response).to have_gitlab_http_status(:ok) - expect(json_response).to be_an(Array) - expect(json_response.length).to eq(3) - expect(json_response.map { |mr| mr['id'] }).not_to include(merge_request_merged) - end - end - - context 'when both merge_user_id and merge_user_username' do - let(:params) { { not: { merge_user_id: user.id, merge_user_username: user.username } } } - - it 'returns a 400' do - get api('/merge_requests', user), params: params - - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to eq('not[merge_user_id], not[merge_user_username] are mutually exclusive') - end - end - end end context 'source_branch param' do -- GitLab From 7535381a66bc809e929004571b60804208a760ce Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Wed, 10 Jan 2024 13:13:31 +0100 Subject: [PATCH 13/14] Add feature flag Signed-off-by: Martin Schurz --- .../add_extra_tokens_for_merge_requests.js | 32 ++++---- app/controllers/dashboard_controller.rb | 1 + app/controllers/groups_controller.rb | 1 + .../projects/merge_requests_controller.rb | 1 + app/finders/concerns/merge_user_filter.rb | 1 + .../shared/issuable/_search_bar.html.haml | 3 +- .../development/mr_merge_user_filter.yml | 8 ++ doc/api/merge_requests.md | 12 +-- doc/user/project/merge_requests/index.md | 2 +- .../add_extra_tokens_for_merge_requests.js | 2 +- spec/finders/merge_requests_finder_spec.rb | 58 ++++++++++---- spec/requests/api/merge_requests_spec.rb | 78 +++++++++++++++---- 12 files changed, 142 insertions(+), 57 deletions(-) create mode 100644 config/feature_flags/development/mr_merge_user_filter.yml diff --git a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 5a40effb7d9573..bda2b05fb8fa8f 100644 --- a/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -26,19 +26,6 @@ export default ( IssuableTokenKeys.tokenKeys.splice(2, 0, reviewerToken); IssuableTokenKeys.tokenKeysWithAlternative.splice(2, 0, reviewerToken); - const mergeUserToken = { - formattedKey: TOKEN_TITLE_MERGE_USER, - key: TOKEN_TYPE_MERGE_USER, - type: 'string', - param: 'username', - symbol: '@', - icon: 'merge', - tag: '@merge_user', - hideNotEqual: true, - }; - IssuableTokenKeys.tokenKeys.splice(3, 0, mergeUserToken); - IssuableTokenKeys.tokenKeysWithAlternative.splice(3, 0, mergeUserToken); - const draftToken = { token: { formattedKey: __('Draft'), @@ -131,7 +118,7 @@ export default ( }; if (gon.features.mrApprovedFilter) { - IssuableTokenKeys.tokenKeys.splice(4, 0, approvedToken.token); + IssuableTokenKeys.tokenKeys.splice(3, 0, approvedToken.token); IssuableTokenKeys.conditions.push(...approvedToken.conditions); } @@ -180,7 +167,7 @@ export default ( ], }; - const tokenPosition = gon.features.mrApprovedFilter ? 5 : 4; + const tokenPosition = gon.features.mrApprovedFilter ? 4 : 3; IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, approvedBy.token); IssuableTokenKeys.tokenKeysWithAlternative.splice( tokenPosition, @@ -189,6 +176,21 @@ export default ( ); IssuableTokenKeys.conditions.push(...approvedBy.condition); + const mergeUserToken = { + formattedKey: TOKEN_TITLE_MERGE_USER, + key: TOKEN_TYPE_MERGE_USER, + type: 'string', + param: 'username', + symbol: '@', + icon: 'merge', + tag: '@merge_user', + hideNotEqual: true, + }; + if (gon.features.mrMergeUserFilter) { + IssuableTokenKeys.tokenKeys.splice(3, 0, mergeUserToken); + IssuableTokenKeys.tokenKeysWithAlternative.splice(3, 0, mergeUserToken); + } + if (!disableEnvironmentFilter) { const environmentToken = { formattedKey: __('Environment'), diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb index a0997484c58395..3cdfba61dadf88 100644 --- a/app/controllers/dashboard_controller.rb +++ b/app/controllers/dashboard_controller.rb @@ -19,6 +19,7 @@ class DashboardController < Dashboard::ApplicationController before_action only: :merge_requests do push_frontend_feature_flag(:mr_approved_filter, type: :ops) + push_frontend_feature_flag(:mr_merge_user_filter, type: :development) end respond_to :html diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 5b9b3b7de11949..b343c8d3beffc0 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -45,6 +45,7 @@ class GroupsController < Groups::ApplicationController before_action only: :merge_requests do push_frontend_feature_flag(:mr_approved_filter, type: :ops) + push_frontend_feature_flag(:mr_merge_user_filter, type: :development) end helper_method :captcha_required? diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 0899e30330573c..69bb32d75a7cd4 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -34,6 +34,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo before_action only: :index do push_frontend_feature_flag(:mr_approved_filter, type: :ops) + push_frontend_feature_flag(:mr_merge_user_filter, type: :development) end before_action only: [:show, :diffs] do diff --git a/app/finders/concerns/merge_user_filter.rb b/app/finders/concerns/merge_user_filter.rb index 160f995c240d1a..bd35f12b5c7454 100644 --- a/app/finders/concerns/merge_user_filter.rb +++ b/app/finders/concerns/merge_user_filter.rb @@ -5,6 +5,7 @@ module MergeUserFilter def by_merge_user(items) return items unless params.merge_user_id? || params.merge_user_username? + return items if Feature.disabled?(:mr_merge_user_filter, type: :development) mr_metrics_scope = MergeRequest::Metrics mr_metrics_scope = mr_metrics_scope.merged_by(params.merge_user) diff --git a/app/views/shared/issuable/_search_bar.html.haml b/app/views/shared/issuable/_search_bar.html.haml index 55cd3b76cc4d5a..33975ebbb42fff 100644 --- a/app/views/shared/issuable/_search_bar.html.haml +++ b/app/views/shared/issuable/_search_bar.html.haml @@ -93,7 +93,8 @@ avatar: { lazy: true, url: '{{avatar_url}}' } = render_if_exists 'shared/issuable/approver_dropdown' = render_if_exists 'shared/issuable/approved_by_dropdown' - = render_if_exists 'shared/issuable/merge_user_dropdown' + - if ::Feature.enabled?(:mr_merge_user_filter, type: :development) + = render_if_exists 'shared/issuable/merge_user_dropdown' #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu %ul{ data: { dropdown: true } } %li.filter-dropdown-item{ data: { value: 'None' } } diff --git a/config/feature_flags/development/mr_merge_user_filter.yml b/config/feature_flags/development/mr_merge_user_filter.yml new file mode 100644 index 00000000000000..43b2d96061037f --- /dev/null +++ b/config/feature_flags/development/mr_merge_user_filter.yml @@ -0,0 +1,8 @@ +--- +name: mr_merge_user_filter +introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002 +rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437871 +milestone: '16.7' +type: development +group: group::code review +default_enabled: false diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index 2e4ca7df305ba1..d6af5006bc9a51 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,8 +64,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | @@ -246,8 +246,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `iids[]` | integer array | No | Returns the request having the given `iid`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | @@ -432,8 +432,8 @@ Supported attributes: | `created_after` | datetime | No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `created_before` | datetime | No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | -| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `non_archived` | boolean | No | Returns merge requests from non archived projects only. Default is `true`. | diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index d68aa1660dc26c..466201b393cb0c 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -82,7 +82,7 @@ or: > - Filtering by potential approvers was moved to GitLab Premium in 13.9. > - Filtering by `approved-by` moved to GitLab Premium in 13.9. > - Filtering by `source-branch` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134555) in GitLab 16.6. -> - Filtering by `merged-by` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. +> - Filtering by `merged-by` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. To filter the list of merge requests: diff --git a/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js b/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js index 5f93f38dc9a3fd..b272737b824763 100644 --- a/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js +++ b/ee/app/assets/javascripts/filtered_search/add_extra_tokens_for_merge_requests.js @@ -49,7 +49,7 @@ const approvers = { export default (IssuableTokenKeys, disableBranchFilter = false) => { addExtraTokensForMergeRequests(IssuableTokenKeys, disableBranchFilter); - const tokenPosition = 4; + const tokenPosition = gon.features.mrMergeUserFilter ? 4 : 3; IssuableTokenKeys.tokenKeys.splice(tokenPosition, 0, ...[approvers.token]); IssuableTokenKeys.tokenKeysWithAlternative.splice( diff --git a/spec/finders/merge_requests_finder_spec.rb b/spec/finders/merge_requests_finder_spec.rb index af414a6fc53aac..d85ae92f95413e 100644 --- a/spec/finders/merge_requests_finder_spec.rb +++ b/spec/finders/merge_requests_finder_spec.rb @@ -184,30 +184,56 @@ subject { described_class.new(user, params).execute } - describe 'by merge_user_id' do - let(:params) { { merge_user_id: user.id } } - let(:expected_mr) { [merge_request1, merge_request2] } + context 'when flag `mr_merge_user_filter` is disabled' do + before do + stub_feature_flags(mr_merge_user_filter: false) + end - it { is_expected.to contain_exactly(*expected_mr) } - end + describe 'by merge_user_id' do + let(:params) { { merge_user_id: user.id } } + let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } - describe 'by merge_user_username' do - let(:params) { { merge_user_username: user.username } } - let(:expected_mr) { [merge_request1, merge_request2] } + it { is_expected.to contain_exactly(*expected_mr) } + end - it { is_expected.to contain_exactly(*expected_mr) } + describe 'by merge_user_username' do + let(:params) { { merge_user_username: user.username } } + let(:expected_mr) { [merge_request1, merge_request2, merge_request3, merge_request4, merge_request5] } + + it { is_expected.to contain_exactly(*expected_mr) } + end end - describe 'by merge_user_id with unknown user id' do - let(:params) { { merge_user_id: 99999 } } + context 'when flag `mr_merge_user_filter` is enabled' do + before do + stub_feature_flags(mr_merge_user_filter: true) + end + + describe 'by merge_user_id' do + let(:params) { { merge_user_id: user.id } } + let(:expected_mr) { [merge_request1, merge_request2] } - it { is_expected.to be_empty } - end + it { is_expected.to contain_exactly(*expected_mr) } + end - describe 'by merge_user_username with unknown user name' do - let(:params) { { merge_user_username: 'does-not-exist' } } + describe 'by merge_user_username' do + let(:params) { { merge_user_username: user.username } } + let(:expected_mr) { [merge_request1, merge_request2] } - it { is_expected.to be_empty } + it { is_expected.to contain_exactly(*expected_mr) } + end + + describe 'by merge_user_id with unknown user id' do + let(:params) { { merge_user_id: 99999 } } + + it { is_expected.to be_empty } + end + + describe 'by merge_user_username with unknown user name' do + let(:params) { { merge_user_username: 'does-not-exist' } } + + it { is_expected.to be_empty } + end end end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 3094644d7114a7..19dbfeb03bd13b 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -934,34 +934,78 @@ context 'filter by merge_user' do let(:params) { { scope: :all } } - context 'with merge_user_id' do - let(:params) { super().merge(merge_user_id: user.id) } + context 'when flag `mr_merge_user_filter` is disabled' do + before do + stub_feature_flags(mr_merge_user_filter: false) + end - it 'returns merged merge requests for the given user' do - get api('/merge_requests', user), params: params + context 'with merge_user_id' do + let(:params) { super().merge(merge_user_id: user.id) } - expect_response_contain_exactly(merge_request_merged.id) + it 'returns merged merge requests for the given user' do + get api('/merge_requests', user), params: params + + expect_response_contain_exactly( + merge_request.id, + merge_request_closed.id, + merge_request_merged.id, + merge_request_locked.id, + merge_request2.id + ) + end end - end - context 'with merge_user_username' do - let(:params) { super().merge(merge_user_username: user.username) } + context 'with merge_user_username' do + let(:params) { super().merge(merge_user_username: user.username) } - it 'returns merged merge requests for the given user' do - get api('/merge_requests', user), params: params + it 'returns merged merge requests for the given user' do + get api('/merge_requests', user), params: params - expect_response_contain_exactly(merge_request_merged.id) + expect_response_contain_exactly( + merge_request.id, + merge_request_closed.id, + merge_request_merged.id, + merge_request_locked.id, + merge_request2.id + ) + end end end - context 'with both merge_user_id and merge_user_username' do - let(:params) { super().merge(merge_user_id: user.id, merge_user_username: user.username) } + context 'when flag `mr_merge_user_filter` is enabled' do + before do + stub_feature_flags(mr_merge_user_filter: true) + end - it 'returns a 400' do - get api('/merge_requests', user), params: params + context 'with merge_user_id' do + let(:params) { super().merge(merge_user_id: user.id) } - expect(response).to have_gitlab_http_status(:bad_request) - expect(json_response['error']).to eq('merge_user_id, merge_user_username are mutually exclusive') + it 'returns merged merge requests for the given user' do + get api('/merge_requests', user), params: params + + expect_response_contain_exactly(merge_request_merged.id) + end + end + + context 'with merge_user_username' do + let(:params) { super().merge(merge_user_username: user.username) } + + it 'returns merged merge requests for the given user' do + get api('/merge_requests', user), params: params + + expect_response_contain_exactly(merge_request_merged.id) + end + end + + context 'with both merge_user_id and merge_user_username' do + let(:params) { super().merge(merge_user_id: user.id, merge_user_username: user.username) } + + it 'returns a 400' do + get api('/merge_requests', user), params: params + + expect(response).to have_gitlab_http_status(:bad_request) + expect(json_response['error']).to eq('merge_user_id, merge_user_username are mutually exclusive') + end end end end -- GitLab From f37092a238e6ce4d8bb1cf4ec21d79e8efa4a929 Mon Sep 17 00:00:00 2001 From: Martin Schurz Date: Tue, 16 Jan 2024 08:02:05 +0100 Subject: [PATCH 14/14] Update milestone to current iteration Signed-off-by: Martin Schurz --- .../development/mr_merge_user_filter.yml | 2 +- doc/api/merge_requests.md | 12 ++++++------ doc/user/project/merge_requests/index.md | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/config/feature_flags/development/mr_merge_user_filter.yml b/config/feature_flags/development/mr_merge_user_filter.yml index 43b2d96061037f..147de6b0eaa1a7 100644 --- a/config/feature_flags/development/mr_merge_user_filter.yml +++ b/config/feature_flags/development/mr_merge_user_filter.yml @@ -2,7 +2,7 @@ name: mr_merge_user_filter introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002 rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/437871 -milestone: '16.7' +milestone: '16.9' type: development group: group::code review default_enabled: false diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md index d6af5006bc9a51..62e106885d2f8e 100644 --- a/doc/api/merge_requests.md +++ b/doc/api/merge_requests.md @@ -64,8 +64,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `in` | string | No | Modify the scope of the `search` attribute. `title`, `description`, or a string joining them with comma. Default is `title,description`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | -| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | @@ -246,8 +246,8 @@ Supported attributes: | `environment` | string | No | Returns merge requests deployed to the given environment. | | `iids[]` | integer array | No | Returns the request having the given `iid`. | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | -| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `not` | Hash | No | Returns merge requests that do not match the parameters supplied. Accepts: `labels`, `milestone`, `author_id`, `author_username`, `assignee_id`, `assignee_username`, `reviewer_id`, `reviewer_username`, `my_reaction_emoji`. | @@ -432,8 +432,8 @@ Supported attributes: | `created_after` | datetime | No | Returns merge requests created on or after the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `created_before` | datetime | No | Returns merge requests created on or before the given time. Expected in ISO 8601 format (`2019-03-15T08:00:00Z`). | | `labels` | string | No | Returns merge requests matching a comma-separated list of labels. `None` lists all merge requests with no labels. `Any` lists all merge requests with at least one label. Predefined names are case-insensitive. | -| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | -| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_id` | integer | No | Returns merge requests which have been merged by the user with the given user `id`. Mutually exclusive with `merge_user_username`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. | +| `merge_user_username` | string | No | Returns merge requests which have been merged by the user with the given `username`. Mutually exclusive with `merge_user_id`. [Introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. | | `milestone` | string | No | Returns merge requests for a specific milestone. `None` returns merge requests with no milestone. `Any` returns merge requests that have an assigned milestone. | | `my_reaction_emoji` | string | No | Returns merge requests reacted by the authenticated user by the given `emoji`. `None` returns issues not given a reaction. `Any` returns issues given at least one reaction. | | `non_archived` | boolean | No | Returns merge requests from non archived projects only. Default is `true`. | diff --git a/doc/user/project/merge_requests/index.md b/doc/user/project/merge_requests/index.md index 466201b393cb0c..dece5600b0d2f9 100644 --- a/doc/user/project/merge_requests/index.md +++ b/doc/user/project/merge_requests/index.md @@ -82,7 +82,7 @@ or: > - Filtering by potential approvers was moved to GitLab Premium in 13.9. > - Filtering by `approved-by` moved to GitLab Premium in 13.9. > - Filtering by `source-branch` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/134555) in GitLab 16.6. -> - Filtering by `merged-by` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.8. Available only when the feature flag `mr_merge_user_filter` is enabled. +> - Filtering by `merged-by` [introduced](https://gitlab.com/gitlab-org/gitlab/-/merge_requests/140002) in GitLab 16.9. Available only when the feature flag `mr_merge_user_filter` is enabled. To filter the list of merge requests: -- GitLab