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