From f1cd655f6305c75172684bfd9ec1e965dc3a0946 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Wed, 22 May 2024 19:01:49 -0600 Subject: [PATCH 1/9] Add (single) assignee filter to Vue MR list app --- app/assets/javascripts/issues/list/constants.js | 14 ++++++++++++++ .../list/components/merge_requests_list_app.vue | 16 ++++++++++++++++ .../queries/get_merge_requests.query.graphql | 2 ++ .../components/filtered_search_bar/constants.js | 1 + 4 files changed, 33 insertions(+) diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index 2a1b0e4275639e..7235a397c948a8 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -12,6 +12,7 @@ import { OPERATOR_AFTER, OPERATOR_BEFORE, TOKEN_TYPE_ASSIGNEE, + TOKEN_TYPE_MR_ASSIGNEE, TOKEN_TYPE_AUTHOR, TOKEN_TYPE_CONFIDENTIAL, TOKEN_TYPE_CONTACT, @@ -214,6 +215,19 @@ export const filtersMap = { }, }, }, + [TOKEN_TYPE_MR_ASSIGNEE]: { + [API_PARAM]: { + [NORMAL_FILTER]: 'assigneeUsername', + }, + [URL_PARAM]: { + [OPERATOR_IS]: { + [NORMAL_FILTER]: 'assignee_username', + }, + [OPERATOR_NOT]: { + [NORMAL_FILTER]: 'not[assignee_username]', + }, + }, + }, [TOKEN_TYPE_ASSIGNEE]: { [API_PARAM]: { [NORMAL_FILTER]: 'assigneeUsernames', diff --git a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue index 039c9689e30336..8811d6c2bd6e52 100644 --- a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue +++ b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue @@ -15,6 +15,7 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro import { DEFAULT_PAGE_SIZE, mergeRequestListTabs } from '~/vue_shared/issuable/list/constants'; import { OPERATORS_IS, + OPERATORS_IS_NOT, TOKEN_TITLE_AUTHOR, TOKEN_TYPE_AUTHOR, TOKEN_TITLE_DRAFT, @@ -23,6 +24,8 @@ import { TOKEN_TYPE_TARGET_BRANCH, TOKEN_TITLE_SOURCE_BRANCH, TOKEN_TYPE_SOURCE_BRANCH, + TOKEN_TITLE_ASSIGNEE, + TOKEN_TYPE_MR_ASSIGNEE, } from '~/vue_shared/components/filtered_search_bar/constants'; import { convertToApiParams, @@ -171,6 +174,19 @@ export default { } return [ + { + type: TOKEN_TYPE_MR_ASSIGNEE, + title: TOKEN_TITLE_ASSIGNEE, + icon: 'user', + token: UserToken, + dataType: 'user', + operators: OPERATORS_IS_NOT, + fullPath: this.fullPath, + isProject: this.isProject, + recentSuggestionsStorageKey: `${this.fullPath}-merge-requests-recent-tokens-assignee`, + preloadedUsers, + multiSelect: false, + }, { type: TOKEN_TYPE_AUTHOR, title: TOKEN_TITLE_AUTHOR, diff --git a/app/assets/javascripts/merge_requests/list/queries/get_merge_requests.query.graphql b/app/assets/javascripts/merge_requests/list/queries/get_merge_requests.query.graphql index b7811523b0b624..bd472856ad157e 100644 --- a/app/assets/javascripts/merge_requests/list/queries/get_merge_requests.query.graphql +++ b/app/assets/javascripts/merge_requests/list/queries/get_merge_requests.query.graphql @@ -7,6 +7,7 @@ query getMergeRequests( $fullPath: ID! $sort: MergeRequestSort $state: MergeRequestState + $assigneeUsername: String $authorUsername: String $draft: Boolean $sourceBranches: [String!] @@ -21,6 +22,7 @@ query getMergeRequests( mergeRequests( sort: $sort state: $state + assigneeUsername: $assigneeUsername authorUsername: $authorUsername draft: $draft sourceBranches: $sourceBranches 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 aeebe312e9d08a..32e9c424b87620 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 @@ -90,6 +90,7 @@ export const TOKEN_TITLE_CLOSED = __('Closed date'); export const TOKEN_TYPE_APPROVED_BY = 'approved-by'; export const TOKEN_TYPE_MERGE_USER = 'merge-user'; export const TOKEN_TYPE_ASSIGNEE = 'assignee'; +export const TOKEN_TYPE_MR_ASSIGNEE = 'mr-assignee'; export const TOKEN_TYPE_AUTHOR = 'author'; export const TOKEN_TYPE_CONFIDENTIAL = 'confidential'; export const TOKEN_TYPE_CONTACT = 'contact'; -- GitLab From 3796ee90e76f3e8e91c74a61181cb056f2ff32f5 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Fri, 24 May 2024 22:12:48 -0600 Subject: [PATCH 2/9] Set isProject to true for merge requests (it's always in a project) --- .../merge_requests/list/components/merge_requests_list_app.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue index 8811d6c2bd6e52..81e7e0c82e1e2c 100644 --- a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue +++ b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue @@ -182,7 +182,7 @@ export default { dataType: 'user', operators: OPERATORS_IS_NOT, fullPath: this.fullPath, - isProject: this.isProject, + isProject: true, recentSuggestionsStorageKey: `${this.fullPath}-merge-requests-recent-tokens-assignee`, preloadedUsers, multiSelect: false, -- GitLab From 7f15055a526c219f5ee9c5247b0bef4d1b4b76c1 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Wed, 29 May 2024 10:33:44 -0600 Subject: [PATCH 3/9] Only allow a single instance of the assignee filter We only have backend support for a single string value --- .../merge_requests/list/components/merge_requests_list_app.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue index 81e7e0c82e1e2c..f09f29aa69ad3d 100644 --- a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue +++ b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue @@ -186,6 +186,7 @@ export default { recentSuggestionsStorageKey: `${this.fullPath}-merge-requests-recent-tokens-assignee`, preloadedUsers, multiSelect: false, + unique: true, }, { type: TOKEN_TYPE_AUTHOR, -- GitLab From d35f86eaa35715819f9b3894da51bbe447e4564e Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Wed, 29 May 2024 10:35:50 -0600 Subject: [PATCH 4/9] Switch assignee to the IS operator only IS_NOT is not working, and it doesn't seem our backend can resolve it --- app/assets/javascripts/issues/list/constants.js | 3 --- .../merge_requests/list/components/merge_requests_list_app.vue | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index 7235a397c948a8..4eec137f13129f 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -223,9 +223,6 @@ export const filtersMap = { [OPERATOR_IS]: { [NORMAL_FILTER]: 'assignee_username', }, - [OPERATOR_NOT]: { - [NORMAL_FILTER]: 'not[assignee_username]', - }, }, }, [TOKEN_TYPE_ASSIGNEE]: { diff --git a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue index f09f29aa69ad3d..0221c9be83ae39 100644 --- a/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue +++ b/app/assets/javascripts/merge_requests/list/components/merge_requests_list_app.vue @@ -15,7 +15,6 @@ import IssuableList from '~/vue_shared/issuable/list/components/issuable_list_ro import { DEFAULT_PAGE_SIZE, mergeRequestListTabs } from '~/vue_shared/issuable/list/constants'; import { OPERATORS_IS, - OPERATORS_IS_NOT, TOKEN_TITLE_AUTHOR, TOKEN_TYPE_AUTHOR, TOKEN_TITLE_DRAFT, @@ -180,7 +179,7 @@ export default { icon: 'user', token: UserToken, dataType: 'user', - operators: OPERATORS_IS_NOT, + operators: OPERATORS_IS, fullPath: this.fullPath, isProject: true, recentSuggestionsStorageKey: `${this.fullPath}-merge-requests-recent-tokens-assignee`, -- GitLab From ad1ab3838ac888a659f2e109598ae037b042e987 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Thu, 30 May 2024 12:17:55 -0600 Subject: [PATCH 5/9] Extract pre-displayed search tokens to object from string --- .../list/components/merge_requests_list_app_spec.js | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js b/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js index 1e1239d4b28be9..445a54e3e47717 100644 --- a/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js +++ b/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js @@ -116,8 +116,17 @@ describe('Merge requests list app', () => { }); describe('when all tokens are available', () => { + const urlParams = { + draft: 'yes', + 'target_branches[]': 'branch-a', + 'source_branches[]': 'branch-b', + }; + const paramString = Object.entries(urlParams) + .map(([k, v]) => `${k}=${v}`) + .join('&'); + beforeEach(async () => { - setWindowLocation('?draft=yes&target_branches[]=branch-a&source_branches[]=branch-b'); + setWindowLocation(`?${paramString}`); window.gon = { current_user_id: mockCurrentUser.id, current_user_fullname: mockCurrentUser.name, -- GitLab From 1ff72259a3c1a600a4057e33592bb8abcf40a474 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Thu, 30 May 2024 12:18:36 -0600 Subject: [PATCH 6/9] Test that the MR assignee is displayed properly --- .../list/components/merge_requests_list_app_spec.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js b/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js index 445a54e3e47717..6478760c83a5b0 100644 --- a/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js +++ b/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js @@ -12,6 +12,7 @@ import { TOKEN_TYPE_DRAFT, TOKEN_TYPE_SOURCE_BRANCH, TOKEN_TYPE_TARGET_BRANCH, + TOKEN_TYPE_MR_ASSIGNEE, } from '~/vue_shared/components/filtered_search_bar/constants'; import { mergeRequestListTabs } from '~/vue_shared/issuable/list/constants'; import { getSortOptions } from '~/issues/list/utils'; @@ -107,6 +108,7 @@ describe('Merge requests list app', () => { it('does not have preloaded users when gon.current_user_id does not exist', () => { expect(findIssuableList().props('searchTokens')).toMatchObject([ + { type: TOKEN_TYPE_MR_ASSIGNEE }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers: [] }, { type: TOKEN_TYPE_DRAFT }, { type: TOKEN_TYPE_TARGET_BRANCH }, @@ -117,6 +119,7 @@ describe('Merge requests list app', () => { describe('when all tokens are available', () => { const urlParams = { + assignee_username: 'bob', draft: 'yes', 'target_branches[]': 'branch-a', 'source_branches[]': 'branch-b', @@ -150,6 +153,7 @@ describe('Merge requests list app', () => { ]; expect(findIssuableList().props('searchTokens')).toMatchObject([ + { type: TOKEN_TYPE_MR_ASSIGNEE }, { type: TOKEN_TYPE_AUTHOR, preloadedUsers }, { type: TOKEN_TYPE_DRAFT }, { type: TOKEN_TYPE_TARGET_BRANCH }, @@ -159,6 +163,7 @@ describe('Merge requests list app', () => { it('pre-displays tokens that are in the url search parameters', () => { expect(findIssuableList().props('initialFilterValue')).toMatchObject([ + { type: TOKEN_TYPE_MR_ASSIGNEE }, { type: TOKEN_TYPE_DRAFT }, { type: TOKEN_TYPE_TARGET_BRANCH }, { type: TOKEN_TYPE_SOURCE_BRANCH }, -- GitLab From 01785abc748273d4073544da2d351002ba55ec73 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Thu, 30 May 2024 12:52:41 -0600 Subject: [PATCH 7/9] Change the URL parameter to deconflict issues and MRs --- app/assets/javascripts/issues/list/constants.js | 2 +- .../list/components/merge_requests_list_app_spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index 4eec137f13129f..e25a320ab84e2d 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -221,7 +221,7 @@ export const filtersMap = { }, [URL_PARAM]: { [OPERATOR_IS]: { - [NORMAL_FILTER]: 'assignee_username', + [NORMAL_FILTER]: 'mr_assignee_username', }, }, }, diff --git a/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js b/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js index 6478760c83a5b0..7fa8c062671de8 100644 --- a/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js +++ b/spec/frontend/merge_requests/list/components/merge_requests_list_app_spec.js @@ -119,7 +119,7 @@ describe('Merge requests list app', () => { describe('when all tokens are available', () => { const urlParams = { - assignee_username: 'bob', + mr_assignee_username: 'bob', draft: 'yes', 'target_branches[]': 'branch-a', 'source_branches[]': 'branch-b', -- GitLab From 2fb25866c3f0adb474a6b73b024156cd371a87fe Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Thu, 30 May 2024 13:22:01 -0600 Subject: [PATCH 8/9] Handle assignee wildcard filters (any, none) in the search bar --- app/assets/javascripts/issues/list/constants.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index e25a320ab84e2d..cc24e421c14929 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -218,10 +218,14 @@ export const filtersMap = { [TOKEN_TYPE_MR_ASSIGNEE]: { [API_PARAM]: { [NORMAL_FILTER]: 'assigneeUsername', + [SPECIAL_FILTER]: 'assigneeWildcardId', + [ALTERNATIVE_FILTER]: 'assigneeId', }, [URL_PARAM]: { [OPERATOR_IS]: { [NORMAL_FILTER]: 'mr_assignee_username', + [SPECIAL_FILTER]: 'mr_assignee_id', + [ALTERNATIVE_FILTER]: 'mr_assignee_username', }, }, }, -- GitLab From a83d635eafb846530849a5f1eb5be16b9898d9c4 Mon Sep 17 00:00:00 2001 From: Thomas Randolph Date: Fri, 31 May 2024 10:15:05 -0600 Subject: [PATCH 9/9] Implement NOT and OR url params in case the BE supports them --- app/assets/javascripts/issues/list/constants.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/assets/javascripts/issues/list/constants.js b/app/assets/javascripts/issues/list/constants.js index cc24e421c14929..a7779dc878b390 100644 --- a/app/assets/javascripts/issues/list/constants.js +++ b/app/assets/javascripts/issues/list/constants.js @@ -227,6 +227,12 @@ export const filtersMap = { [SPECIAL_FILTER]: 'mr_assignee_id', [ALTERNATIVE_FILTER]: 'mr_assignee_username', }, + [OPERATOR_NOT]: { + [NORMAL_FILTER]: 'not[mr_assignee_username]', + }, + [OPERATOR_OR]: { + [NORMAL_FILTER]: 'or[mr_assignee_username]', + }, }, }, [TOKEN_TYPE_ASSIGNEE]: { -- GitLab