Commit 827650ec authored by Peter Leitzen's avatar Peter Leitzen

Merge branch '232798-alrt-graphql' into 'master'

Alert Management assignee filter

See merge request gitlab-org/gitlab!44911
parents c9ab107a e39b91d0
...@@ -19,8 +19,10 @@ module AlertManagement ...@@ -19,8 +19,10 @@ module AlertManagement
collection = project.alert_management_alerts collection = project.alert_management_alerts
collection = by_status(collection) collection = by_status(collection)
collection = by_search(collection)
collection = by_iid(collection) collection = by_iid(collection)
collection = by_assignee(collection)
collection = by_search(collection)
sort(collection) sort(collection)
end end
...@@ -48,6 +50,10 @@ module AlertManagement ...@@ -48,6 +50,10 @@ module AlertManagement
params[:sort] ? collection.sort_by_attribute(params[:sort]) : collection params[:sort] ? collection.sort_by_attribute(params[:sort]) : collection
end end
def by_assignee(collection)
params[:assignee_username].present? ? collection.for_assignee_username(params[:assignee_username]) : collection
end
def authorized? def authorized?
Ability.allowed?(current_user, :read_alert_management_alert, project) Ability.allowed?(current_user, :read_alert_management_alert, project)
end end
......
...@@ -22,6 +22,10 @@ module Resolvers ...@@ -22,6 +22,10 @@ module Resolvers
description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.', description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
required: false required: false
argument :assignee_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of a user assigned to the issue'
type Types::AlertManagement::AlertType, null: true type Types::AlertManagement::AlertType, null: true
def resolve_with_lookahead(**args) def resolve_with_lookahead(**args)
......
...@@ -9,6 +9,10 @@ module Resolvers ...@@ -9,6 +9,10 @@ module Resolvers
description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.', description: 'Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.',
required: false required: false
argument :assignee_username, GraphQL::STRING_TYPE,
required: false,
description: 'Username of a user assigned to the issue'
def resolve(**args) def resolve(**args)
::Gitlab::AlertManagement::AlertStatusCounts.new(context[:current_user], object, args) ::Gitlab::AlertManagement::AlertStatusCounts.new(context[:current_user], object, args)
end end
......
...@@ -113,6 +113,7 @@ module AlertManagement ...@@ -113,6 +113,7 @@ module AlertManagement
scope :for_status, -> (status) { with_status(status) } scope :for_status, -> (status) { with_status(status) }
scope :for_fingerprint, -> (project, fingerprint) { where(project: project, fingerprint: fingerprint) } scope :for_fingerprint, -> (project, fingerprint) { where(project: project, fingerprint: fingerprint) }
scope :for_environment, -> (environment) { where(environment: environment) } scope :for_environment, -> (environment) { where(environment: environment) }
scope :for_assignee_username, -> (assignee_username) { joins(:assignees).merge(User.by_username(assignee_username)) }
scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) } scope :search, -> (query) { fuzzy_search(query, [:title, :description, :monitoring_tool, :service]) }
scope :open, -> { with_status(open_statuses) } scope :open, -> { with_status(open_statuses) }
scope :not_resolved, -> { without_status(:resolved) } scope :not_resolved, -> { without_status(:resolved) }
......
---
title: Make alerts searchable by assignee username in GraphQL API
merge_request: 44911
author:
type: added
...@@ -13002,6 +13002,11 @@ type Project { ...@@ -13002,6 +13002,11 @@ type Project {
A single Alert Management alert of the project A single Alert Management alert of the project
""" """
alertManagementAlert( alertManagementAlert(
"""
Username of a user assigned to the issue
"""
assigneeUsername: String
""" """
IID of the alert. For example, "1" IID of the alert. For example, "1"
""" """
...@@ -13027,6 +13032,11 @@ type Project { ...@@ -13027,6 +13032,11 @@ type Project {
Counts of alerts by status for the project Counts of alerts by status for the project
""" """
alertManagementAlertStatusCounts( alertManagementAlertStatusCounts(
"""
Username of a user assigned to the issue
"""
assigneeUsername: String
""" """
Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool. Search criteria for filtering alerts. This will search on title, description, service, monitoring_tool.
""" """
...@@ -13042,6 +13052,11 @@ type Project { ...@@ -13042,6 +13052,11 @@ type Project {
""" """
after: String after: String
"""
Username of a user assigned to the issue
"""
assigneeUsername: String
""" """
Returns the elements in the list that come before the specified cursor. Returns the elements in the list that come before the specified cursor.
""" """
......
...@@ -38430,6 +38430,16 @@ ...@@ -38430,6 +38430,16 @@
"ofType": null "ofType": null
}, },
"defaultValue": null "defaultValue": null
},
{
"name": "assigneeUsername",
"description": "Username of a user assigned to the issue",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
} }
], ],
"type": { "type": {
...@@ -38453,6 +38463,16 @@ ...@@ -38453,6 +38463,16 @@
"ofType": null "ofType": null
}, },
"defaultValue": null "defaultValue": null
},
{
"name": "assigneeUsername",
"description": "Username of a user assigned to the issue",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
} }
], ],
"type": { "type": {
...@@ -38515,6 +38535,16 @@ ...@@ -38515,6 +38535,16 @@
}, },
"defaultValue": null "defaultValue": null
}, },
{
"name": "assigneeUsername",
"description": "Username of a user assigned to the issue",
"type": {
"kind": "SCALAR",
"name": "String",
"ofType": null
},
"defaultValue": null
},
{ {
"name": "after", "name": "after",
"description": "Returns the elements in the list that come after the specified cursor.", "description": "Returns the elements in the list that come after the specified cursor.",
...@@ -191,59 +191,78 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do ...@@ -191,59 +191,78 @@ RSpec.describe AlertManagement::AlertsFinder, '#execute' do
end end
end end
end end
end
context 'search query given' do context 'search query given' do
let_it_be(:alert) do let_it_be(:alert) do
create(:alert_management_alert, create(:alert_management_alert,
:with_fingerprint, :with_fingerprint,
title: 'Title', project: project,
description: 'Desc', title: 'Title',
service: 'Service', description: 'Desc',
monitoring_tool: 'Monitor' service: 'Service',
) monitoring_tool: 'Monitor'
end )
end
before do context 'searching title' do
alert.project.add_developer(current_user) let(:params) { { search: alert.title } }
end
subject { described_class.new(current_user, alert.project, params).execute } it { is_expected.to match_array([alert]) }
end
context 'searching title' do context 'searching description' do
let(:params) { { search: alert.title } } let(:params) { { search: alert.description } }
it { is_expected.to match_array([alert]) } it { is_expected.to match_array([alert]) }
end end
context 'searching description' do context 'searching service' do
let(:params) { { search: alert.description } } let(:params) { { search: alert.service } }
it { is_expected.to match_array([alert]) } it { is_expected.to match_array([alert]) }
end end
context 'searching service' do context 'searching monitoring tool' do
let(:params) { { search: alert.service } } let(:params) { { search: alert.monitoring_tool } }
it { is_expected.to match_array([alert]) } it { is_expected.to match_array([alert]) }
end end
context 'searching monitoring tool' do context 'searching something else' do
let(:params) { { search: alert.monitoring_tool } } let(:params) { { search: alert.fingerprint } }
it { is_expected.to match_array([alert]) } it { is_expected.to be_empty }
end end
context 'searching something else' do context 'empty search' do
let(:params) { { search: alert.fingerprint } } let(:params) { { search: ' ' } }
it { is_expected.to be_empty } it { is_expected.not_to include(alert) }
end
end end
context 'empty search' do context 'assignee username given' do
let(:params) { { search: ' ' } } let_it_be(:assignee) { create(:user) }
let_it_be(:alert) { create(:alert_management_alert, project: project, assignees: [assignee]) }
let(:params) { { assignee_username: username } }
context 'with valid assignee_username' do
let(:username) { assignee.username }
it { is_expected.to match_array([alert]) }
end
context 'with invalid assignee_username' do
let(:username) { 'unknown username' }
it { is_expected.to be_empty }
end
context 'with empty assignee_username' do
let(:username) { ' ' }
it { is_expected.to match_array([alert]) } it { is_expected.not_to include(alert) }
end
end end
end end
end end
......
...@@ -230,6 +230,35 @@ RSpec.describe AlertManagement::Alert do ...@@ -230,6 +230,35 @@ RSpec.describe AlertManagement::Alert do
it { is_expected.to match_array(env_alert) } it { is_expected.to match_array(env_alert) }
end end
describe '.for_assignee_username' do
let_it_be(:alert) { triggered_alert }
let_it_be(:assignee) { create(:user) }
subject { AlertManagement::Alert.for_assignee_username(assignee_username) }
before_all do
alert.update!(assignees: [assignee])
end
context 'when matching assignee_username' do
let(:assignee_username) { assignee.username }
it { is_expected.to contain_exactly(alert) }
end
context 'when unknown assignee_username' do
let(:assignee_username) { 'unknown username' }
it { is_expected.to be_empty }
end
context 'with empty assignee_username' do
let(:assignee_username) { ' ' }
it { is_expected.to be_empty }
end
end
describe '.order_severity_with_open_prometheus_alert' do describe '.order_severity_with_open_prometheus_alert' do
subject { described_class.where(project: alert_project).order_severity_with_open_prometheus_alert } subject { described_class.where(project: alert_project).order_severity_with_open_prometheus_alert }
......
...@@ -139,6 +139,19 @@ RSpec.describe 'getting Alert Management Alerts' do ...@@ -139,6 +139,19 @@ RSpec.describe 'getting Alert Management Alerts' do
it { expect(alerts.size).to eq(0) } it { expect(alerts.size).to eq(0) }
end end
end end
context 'assignee_username' do
let(:alert) { triggered_alert }
let(:assignee) { alert.assignees.first! }
let(:params) { { assignee_username: assignee.username } }
it_behaves_like 'a working graphql query'
specify do
expect(alerts.size).to eq(1)
expect(first_alert['iid']).to eq(alert.iid.to_s)
end
end
end end
end end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment