Commit f7bb2b1d authored by Sarah Yasonik's avatar Sarah Yasonik Committed by Tiger Watson

Add ability to search for an escalation policy by name

This adds a name argument to the escalationPolicies field
on projects, which allows a caller to provide a string
to search through escalation policies. Utility is limited
in the moment, as only one escalation policy is available
on each project, but that will change in future.

This change is needed to easily take advantage of shared
sidebar components in the UI.

Changelog: added
EE: true
parent b9f94fae
...@@ -13297,7 +13297,6 @@ Represents vulnerability finding of a security report on the pipeline. ...@@ -13297,7 +13297,6 @@ Represents vulnerability finding of a security report on the pipeline.
| <a id="projecthttpurltorepo"></a>`httpUrlToRepo` | [`String`](#string) | URL to connect to the project via HTTPS. | | <a id="projecthttpurltorepo"></a>`httpUrlToRepo` | [`String`](#string) | URL to connect to the project via HTTPS. |
| <a id="projectid"></a>`id` | [`ID!`](#id) | ID of the project. | | <a id="projectid"></a>`id` | [`ID!`](#id) | ID of the project. |
| <a id="projectimportstatus"></a>`importStatus` | [`String`](#string) | Status of import background job of the project. | | <a id="projectimportstatus"></a>`importStatus` | [`String`](#string) | Status of import background job of the project. |
| <a id="projectincidentmanagementescalationpolicies"></a>`incidentManagementEscalationPolicies` | [`EscalationPolicyTypeConnection`](#escalationpolicytypeconnection) | Incident Management escalation policies of the project. (see [Connections](#connections)) |
| <a id="projectissuesenabled"></a>`issuesEnabled` | [`Boolean`](#boolean) | Indicates if Issues are enabled for the current user. | | <a id="projectissuesenabled"></a>`issuesEnabled` | [`Boolean`](#boolean) | Indicates if Issues are enabled for the current user. |
| <a id="projectjiraimportstatus"></a>`jiraImportStatus` | [`String`](#string) | Status of Jira import background job of the project. | | <a id="projectjiraimportstatus"></a>`jiraImportStatus` | [`String`](#string) | Status of Jira import background job of the project. |
| <a id="projectjiraimports"></a>`jiraImports` | [`JiraImportConnection`](#jiraimportconnection) | Jira imports into the project. (see [Connections](#connections)) | | <a id="projectjiraimports"></a>`jiraImports` | [`JiraImportConnection`](#jiraimportconnection) | Jira imports into the project. (see [Connections](#connections)) |
...@@ -13606,6 +13605,22 @@ four standard [pagination arguments](#connection-pagination-arguments): ...@@ -13606,6 +13605,22 @@ four standard [pagination arguments](#connection-pagination-arguments):
| <a id="projectenvironmentssearch"></a>`search` | [`String`](#string) | Search query for environment name. | | <a id="projectenvironmentssearch"></a>`search` | [`String`](#string) | Search query for environment name. |
| <a id="projectenvironmentsstates"></a>`states` | [`[String!]`](#string) | States of environments that should be included in result. | | <a id="projectenvironmentsstates"></a>`states` | [`[String!]`](#string) | States of environments that should be included in result. |
##### `Project.incidentManagementEscalationPolicies`
Incident Management escalation policies of the project.
Returns [`EscalationPolicyTypeConnection`](#escalationpolicytypeconnection).
This field returns a [connection](#connections). It accepts the
four standard [pagination arguments](#connection-pagination-arguments):
`before: String`, `after: String`, `first: Int`, `last: Int`.
###### Arguments
| Name | Type | Description |
| ---- | ---- | ----------- |
| <a id="projectincidentmanagementescalationpoliciesname"></a>`name` | [`String`](#string) | Fuzzy search by escalation policy name. |
##### `Project.incidentManagementEscalationPolicy` ##### `Project.incidentManagementEscalationPolicy`
Incident Management escalation policy of the project. Incident Management escalation policy of the project.
...@@ -13617,6 +13632,7 @@ Returns [`EscalationPolicyType`](#escalationpolicytype). ...@@ -13617,6 +13632,7 @@ Returns [`EscalationPolicyType`](#escalationpolicytype).
| Name | Type | Description | | Name | Type | Description |
| ---- | ---- | ----------- | | ---- | ---- | ----------- |
| <a id="projectincidentmanagementescalationpolicyid"></a>`id` | [`IncidentManagementEscalationPolicyID!`](#incidentmanagementescalationpolicyid) | ID of the escalation policy. | | <a id="projectincidentmanagementescalationpolicyid"></a>`id` | [`IncidentManagementEscalationPolicyID!`](#incidentmanagementescalationpolicyid) | ID of the escalation policy. |
| <a id="projectincidentmanagementescalationpolicyname"></a>`name` | [`String`](#string) | Fuzzy search by escalation policy name. |
##### `Project.incidentManagementOncallSchedules` ##### `Project.incidentManagementOncallSchedules`
...@@ -12,6 +12,7 @@ module IncidentManagement ...@@ -12,6 +12,7 @@ module IncidentManagement
return IncidentManagement::EscalationPolicy.none unless allowed? return IncidentManagement::EscalationPolicy.none unless allowed?
collection = project.incident_management_escalation_policies collection = project.incident_management_escalation_policies
collection = by_name_search(collection)
by_id(collection) by_id(collection)
end end
...@@ -28,5 +29,11 @@ module IncidentManagement ...@@ -28,5 +29,11 @@ module IncidentManagement
collection.id_in(params[:id]) collection.id_in(params[:id])
end end
def by_name_search(collection)
return collection unless params[:name_search].present?
collection.search_by_name(params[:name_search])
end
end end
end end
...@@ -9,6 +9,11 @@ module Resolvers ...@@ -9,6 +9,11 @@ module Resolvers
type Types::IncidentManagement::EscalationPolicyType.connection_type, null: true type Types::IncidentManagement::EscalationPolicyType.connection_type, null: true
argument :name,
GraphQL::Types::String,
required: false,
description: 'Fuzzy search by escalation policy name.'
when_single do when_single do
argument :id, argument :id,
::Types::GlobalIDType[::IncidentManagement::EscalationPolicy], ::Types::GlobalIDType[::IncidentManagement::EscalationPolicy],
...@@ -17,10 +22,16 @@ module Resolvers ...@@ -17,10 +22,16 @@ module Resolvers
prepare: ->(id, ctx) { id.model_id } prepare: ->(id, ctx) { id.model_id }
end end
def resolve_with_lookahead(**args) def resolve_with_lookahead(name: nil, **args)
context[:execution_time] = Time.current context[:execution_time] = Time.current
apply_lookahead(::IncidentManagement::EscalationPoliciesFinder.new(current_user, project, args).execute) apply_lookahead(
::IncidentManagement::EscalationPoliciesFinder.new(
current_user,
project,
{ name_search: name, **args }
).execute
)
end end
private private
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module IncidentManagement module IncidentManagement
class EscalationPolicy < ApplicationRecord class EscalationPolicy < ApplicationRecord
include Gitlab::SQL::Pattern
self.table_name = 'incident_management_escalation_policies' self.table_name = 'incident_management_escalation_policies'
belongs_to :project belongs_to :project
...@@ -13,6 +15,7 @@ module IncidentManagement ...@@ -13,6 +15,7 @@ module IncidentManagement
validates :description, length: { maximum: 160 } validates :description, length: { maximum: 160 }
scope :for_project, -> (project) { where(project: project) } scope :for_project, -> (project) { where(project: project) }
scope :search_by_name, -> (query) { fuzzy_search(query, [:name]) }
accepts_nested_attributes_for :rules accepts_nested_attributes_for :rules
......
...@@ -5,7 +5,7 @@ require 'spec_helper' ...@@ -5,7 +5,7 @@ require 'spec_helper'
RSpec.describe IncidentManagement::EscalationPoliciesFinder do RSpec.describe IncidentManagement::EscalationPoliciesFinder do
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be_with_refind(:project) { create(:project) } let_it_be_with_refind(:project) { create(:project) }
let_it_be(:escalation_policy) { create(:incident_management_escalation_policy, project: project) } let_it_be(:escalation_policy) { create(:incident_management_escalation_policy, project: project, name: 'unique identifier') }
let_it_be(:escalation_policy_from_another_project) { create(:incident_management_escalation_policy) } let_it_be(:escalation_policy_from_another_project) { create(:incident_management_escalation_policy) }
let(:params) { {} } let(:params) { {} }
...@@ -32,6 +32,18 @@ RSpec.describe IncidentManagement::EscalationPoliciesFinder do ...@@ -32,6 +32,18 @@ RSpec.describe IncidentManagement::EscalationPoliciesFinder do
it { is_expected.to contain_exactly(escalation_policy) } it { is_expected.to contain_exactly(escalation_policy) }
end end
context 'when search_name is given' do
let(:params) { { name_search: 'ique iden' } }
it { is_expected.to contain_exactly(escalation_policy) }
context 'when the name does not match' do
let(:params) { { name_search: 'not a matching search' } }
it { is_expected.to eq(IncidentManagement::EscalationPolicy.none) }
end
end
end end
context 'when user has no permissions' do context 'when user has no permissions' do
......
...@@ -7,7 +7,8 @@ RSpec.describe Resolvers::IncidentManagement::EscalationPoliciesResolver do ...@@ -7,7 +7,8 @@ RSpec.describe Resolvers::IncidentManagement::EscalationPoliciesResolver do
let_it_be(:current_user) { create(:user) } let_it_be(:current_user) { create(:user) }
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:policy) { create(:incident_management_escalation_policy, project: project) } let_it_be(:policy) { create(:incident_management_escalation_policy, project: project, name: 'Target policy') }
let_it_be(:other_policy) { create(:incident_management_escalation_policy) }
let(:args) { {} } let(:args) { {} }
let(:resolver) { described_class } let(:resolver) { described_class }
...@@ -29,6 +30,16 @@ RSpec.describe Resolvers::IncidentManagement::EscalationPoliciesResolver do ...@@ -29,6 +30,16 @@ RSpec.describe Resolvers::IncidentManagement::EscalationPoliciesResolver do
expect(resolved_policies.first).to have_attributes(id: policy.id) expect(resolved_policies.first).to have_attributes(id: policy.id)
end end
context 'with name param provided' do
let(:args) { { name: 'target' } }
it 'returns escalation policies matching the name search' do
expect(resolved_policies.length).to eq(1)
expect(resolved_policies.first).to be_a(::IncidentManagement::EscalationPolicy)
expect(resolved_policies.first).to have_attributes(id: policy.id)
end
end
context 'when resolving a single item' do context 'when resolving a single item' do
let(:resolver) { described_class.single } let(:resolver) { described_class.single }
......
...@@ -38,12 +38,18 @@ RSpec.describe IncidentManagement::EscalationPolicy do ...@@ -38,12 +38,18 @@ RSpec.describe IncidentManagement::EscalationPolicy do
describe 'scopes' do describe 'scopes' do
let_it_be(:project) { create(:project) } let_it_be(:project) { create(:project) }
let_it_be(:policy) { create(:incident_management_escalation_policy, project: project) } let_it_be(:policy) { create(:incident_management_escalation_policy, project: project) }
let_it_be(:other_policy) { create(:incident_management_escalation_policy) } let_it_be(:other_policy) { create(:incident_management_escalation_policy, name: 'Other policy') }
describe '.for_project' do describe '.for_project' do
subject { described_class.for_project(project) } subject { described_class.for_project(project) }
it { is_expected.to contain_exactly(policy) } it { is_expected.to contain_exactly(policy) }
end end
describe '.search_by_name' do
subject { described_class.search_by_name('other') }
it { is_expected.to contain_exactly(other_policy) }
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