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 a587f119f90..f03419fbe3d 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 @@ -206,7 +206,7 @@ export default { icon: 'user', token: UserToken, dataType: 'user', - operators: OPERATORS_IS, + operators: OPERATORS_IS_NOT, fullPath: this.fullPath, isProject: true, recentSuggestionsStorageKey: `${this.fullPath}-merge-requests-recent-tokens-reviewer`, diff --git a/app/graphql/resolvers/merge_requests_resolver.rb b/app/graphql/resolvers/merge_requests_resolver.rb index b4e46fdb6c0..73c2dcd7d4a 100644 --- a/app/graphql/resolvers/merge_requests_resolver.rb +++ b/app/graphql/resolvers/merge_requests_resolver.rb @@ -135,6 +135,9 @@ module Resolvers argument :milestone_title, GraphQL::Types::String, required: false, description: 'Title of the milestone.' + argument :reviewer_username, GraphQL::Types::String, + required: false, + description: 'Username of the reviewer.' end validates mutually_exclusive: [:assignee_username, :assignee_wildcard_id] diff --git a/app/models/cloud_connector/service_access_token.rb b/app/models/cloud_connector/service_access_token.rb index e026b10ec0c..eeaf946b774 100644 --- a/app/models/cloud_connector/service_access_token.rb +++ b/app/models/cloud_connector/service_access_token.rb @@ -15,5 +15,9 @@ module CloudConnector encode_iv: false validates :token, :expires_at, presence: true + + def expired? + expires_at.past? + end end end diff --git a/data/whats_new/20230220001_15_08.yml b/data/whats_new/20230220001_15_08.yml index 6e62b3955f0..1faea891983 100644 --- a/data/whats_new/20230220001_15_08.yml +++ b/data/whats_new/20230220001_15_08.yml @@ -66,7 +66,7 @@ self-managed: true gitlab-com: false available_in: [Ultimate] - documentation_link: https://docs.gitlab.com/ee/ci/runners/configure_runners.html#view-statistics-for-runner-performance + documentation_link: https://docs.gitlab.com/ee/ci/runners/runners_scope.html#view-statistics-for-runner-performance image_url: https://about.gitlab.com/images/15_8/admin-runners-estimated-queue-wait-time.png published_at: 2023-01-22 release: 15.8 diff --git a/doc/api/graphql/reference/index.md b/doc/api/graphql/reference/index.md index 74356de3090..722c6b4e241 100644 --- a/doc/api/graphql/reference/index.md +++ b/doc/api/graphql/reference/index.md @@ -39726,6 +39726,7 @@ Defines which user roles, users, or groups can merge into a protected branch. | `assigneeUsernames` | [`[String!]`](#string) | Usernames of the assignee to exclude. | | `labels` | [`[String!]`](#string) | Array of label names. All resolved merge requests will not have these labels. | | `milestoneTitle` | [`String`](#string) | Title of the milestone. | +| `reviewerUsername` | [`String`](#string) | Username of the reviewer. | ### `MonthSelectionInput` 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 d5136875893..ea3780bf6f3 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 @@ -208,11 +208,18 @@ describe('Merge requests list app', () => { type: 'assignee', value: { data: ['root'], operator: OPERATOR_IS }, }, + { + type: 'reviewer', + value: { data: 'root', operator: OPERATOR_IS }, + }, ]); await nextTick(); expect(router.push).toHaveBeenCalledWith({ - query: expect.objectContaining({ 'assignee_username[]': ['root'] }), + query: expect.objectContaining({ + 'assignee_username[]': ['root'], + reviewer_username: 'root', + }), }); }); @@ -224,19 +231,23 @@ describe('Merge requests list app', () => { type: 'assignee', value: { data: ['root'], operator: OPERATOR_NOT }, }, + { + type: 'reviewer', + value: { data: 'root', operator: OPERATOR_NOT }, + }, ]); await nextTick(); expect(getQueryResponseMock).toHaveBeenCalledWith( expect.objectContaining({ - not: { assigneeUsernames: ['root'] }, + not: { assigneeUsernames: ['root'], reviewerUsername: 'root' }, }), ); expect(getCountsQueryResponseMock).toHaveBeenCalledWith( expect.objectContaining({ - not: { assigneeUsernames: ['root'] }, + not: { assigneeUsernames: ['root'], reviewerUsername: 'root' }, }), ); }); @@ -249,12 +260,19 @@ describe('Merge requests list app', () => { type: 'assignee', value: { data: ['root'], operator: OPERATOR_NOT }, }, + { + type: 'reviewer', + value: { data: 'root', operator: OPERATOR_NOT }, + }, ]); await nextTick(); expect(router.push).toHaveBeenCalledWith({ - query: expect.objectContaining({ 'not[assignee_username][]': ['root'] }), + query: expect.objectContaining({ + 'not[assignee_username][]': ['root'], + 'not[reviewer_username]': 'root', + }), }); }); }); diff --git a/spec/graphql/resolvers/merge_requests_resolver_spec.rb b/spec/graphql/resolvers/merge_requests_resolver_spec.rb index af24c7db14f..bd7a68da5ef 100644 --- a/spec/graphql/resolvers/merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/merge_requests_resolver_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Resolvers::MergeRequestsResolver, feature_category: :code_review_ let_it_be(:other_user) { create(:user) } let_it_be(:common_attrs) { { author: current_user, source_project: project, target_project: project } } let_it_be(:merge_request_1) { create(:merge_request, :simple, reviewers: create_list(:user, 2), **common_attrs) } - let_it_be(:merge_request_2) { create(:merge_request, :rebased, **common_attrs) } + let_it_be(:merge_request_2) { create(:merge_request, :rebased, reviewers: [current_user], **common_attrs) } let_it_be(:merge_request_3) { create(:merge_request, :unique_branches, assignees: [current_user], **common_attrs) } let_it_be(:merge_request_4) { create(:merge_request, :unique_branches, :locked, **common_attrs) } let_it_be(:merge_request_5) { create(:merge_request, :simple, :locked, **common_attrs) } @@ -402,6 +402,14 @@ RSpec.describe Resolvers::MergeRequestsResolver, feature_category: :code_review_ expect(result).to contain_exactly(merge_request_1, merge_request_2, merge_request_4, merge_request_5, merge_request_6, merge_request_with_milestone) end end + + context 'with reviewer' do + it do + result = resolve_mr(project, not: { reviewer_username: current_user.username }) + + expect(result).to contain_exactly(merge_request_1, merge_request_3, merge_request_4, merge_request_5, merge_request_6, merge_request_with_milestone) + end + end end describe 'sorting' do diff --git a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb index ac50a1b2793..914b452acea 100644 --- a/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb +++ b/spec/graphql/resolvers/project_merge_requests_resolver_spec.rb @@ -156,6 +156,14 @@ RSpec.describe Resolvers::ProjectMergeRequestsResolver do expect(result).to contain_exactly(merge_request2) end end + + context 'with negated reviewer username' do + it do + result = resolve_mr(project, not: { reviewer_username: reviewer.username }) + + expect(result).to contain_exactly(merge_request2) + end + end end def resolve_mr(project, resolver: described_class, user: current_user, **args) diff --git a/spec/models/cloud_connector/service_access_token_spec.rb b/spec/models/cloud_connector/service_access_token_spec.rb index 4239ec486a5..462a20d858f 100644 --- a/spec/models/cloud_connector/service_access_token_spec.rb +++ b/spec/models/cloud_connector/service_access_token_spec.rb @@ -3,19 +3,16 @@ require 'spec_helper' RSpec.describe CloudConnector::ServiceAccessToken, type: :model, feature_category: :cloud_connector do - describe '.expired', :freeze_time do - let_it_be(:expired_token) { create(:service_access_token, :expired) } - let_it_be(:active_token) { create(:service_access_token, :active) } + let_it_be(:expired_token) { create(:service_access_token, :expired) } + let_it_be(:active_token) { create(:service_access_token, :active) } + describe '.expired', :freeze_time do it 'selects all expired tokens' do expect(described_class.expired).to match_array([expired_token]) end end describe '.active', :freeze_time do - let_it_be(:expired_token) { create(:service_access_token, :expired) } - let_it_be(:active_token) { create(:service_access_token, :active) } - it 'selects all active tokens' do expect(described_class.active).to match_array([active_token]) end @@ -42,4 +39,14 @@ RSpec.describe CloudConnector::ServiceAccessToken, type: :model, feature_categor it { is_expected.to validate_presence_of(:expires_at) } end end + + describe '#expired?' do + it 'returns false for active token' do + expect(active_token).not_to be_expired + end + + it 'returns true for expired token' do + expect(expired_token).to be_expired + end + end end