Commit 1ba856f5 authored by Ash McKenzie's avatar Ash McKenzie

Merge branch '238311-add-api-issues-confidentiality-filter' into 'master'

Search API - filter issues by confidentiality

See merge request gitlab-org/gitlab!44451
parents ca29a313 be82f19b
......@@ -100,7 +100,7 @@ module SearchHelper
end
def search_service
@search_service ||= ::SearchService.new(current_user, params)
@search_service ||= ::SearchService.new(current_user, params.merge(confidential: Gitlab::Utils.to_boolean(params[:confidential])))
end
private
......
......@@ -24,6 +24,7 @@ GET /search
| `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query |
| `state` | string | no | Filter by state. Issues and merge requests are supported; it is ignored for other scopes. |
| `confidential` | boolean | no | Filter by confidentiality. Issues scope is supported; it is ignored for other scopes. This parameter is behind a [feature flag (`search_filter_by_confidential`)](../administration/feature_flags.md). |
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, users.
......@@ -433,6 +434,7 @@ GET /groups/:id/search
| `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query |
| `state` | string | no | Filter by state. Issues and merge requests are supported; it is ignored for other scopes. |
| `confidential` | boolean | no | Filter by confidentiality. Issues scope is supported; it is ignored for other scopes. This parameter is behind a [feature flag (`search_filter_by_confidential`)](../administration/feature_flags.md). |
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, users.
......@@ -812,6 +814,7 @@ GET /projects/:id/search
| `search` | string | yes | The search query |
| `ref` | string | no | The name of a repository branch or tag to search on. The project's default branch is used by default. This is only applicable for scopes: commits, blobs, and wiki_blobs. |
| `state` | string | no | Filter by state. Issues and merge requests are supported; it is ignored for other scopes. |
| `confidential` | boolean | no | Filter by confidentiality. Issues scope is supported; it is ignored for other scopes. This parameter is behind a [feature flag (`search_filter_by_confidential`)](../administration/feature_flags.md). |
Search the expression within the specified scope. Currently these scopes are supported: issues, merge_requests, milestones, notes, wiki_blobs, commits, blobs, users.
......
......@@ -36,10 +36,9 @@ module Elastic
def confidentiality_filter(query_hash, options)
current_user = options[:current_user]
project_ids = options[:project_ids]
confidential_filter = options[:confidential]
if Feature.enabled?(:search_filter_by_confidential) && confidential_filter.present? && %w(yes no).include?(confidential_filter)
query_hash[:query][:bool][:filter] << { term: { confidential: confidential_filter == 'yes' } }
if [true, false].include?(options[:confidential]) && Feature.enabled?(:search_filter_by_confidential)
query_hash[:query][:bool][:filter] << { term: { confidential: options[:confidential] } }
end
return query_hash if current_user&.can_read_all_resources?
......
......@@ -33,6 +33,7 @@ module API
scope: params[:scope],
search: params[:search],
state: params[:state],
confidential: params[:confidential],
snippets: snippets?,
page: params[:page],
per_page: params[:per_page]
......@@ -75,6 +76,7 @@ module API
desc: 'The scope of the search',
values: Helpers::SearchHelpers.global_search_scopes
optional :state, type: String, desc: 'Filter results by state', values: Helpers::SearchHelpers.search_states
optional :confidential, type: Boolean, desc: 'Filter results by confidentiality'
use :pagination
end
get do
......@@ -96,6 +98,7 @@ module API
desc: 'The scope of the search',
values: Helpers::SearchHelpers.group_search_scopes
optional :state, type: String, desc: 'Filter results by state', values: Helpers::SearchHelpers.search_states
optional :confidential, type: Boolean, desc: 'Filter results by confidentiality'
use :pagination
end
get ':id/(-/)search' do
......@@ -118,6 +121,7 @@ module API
values: Helpers::SearchHelpers.project_search_scopes
optional :ref, type: String, desc: 'The name of a repository branch or tag. If not given, the default branch is used'
optional :state, type: String, desc: 'Filter results by state', values: Helpers::SearchHelpers.search_states
optional :confidential, type: Boolean, desc: 'Filter results by confidentiality'
use :pagination
end
get ':id/(-/)search' do
......
......@@ -219,8 +219,8 @@ module Gitlab
params[:state] = filters[:state] if filters.key?(:state)
if Feature.enabled?(:search_filter_by_confidential) && filters.key?(:confidential) && %w(yes no).include?(filters[:confidential])
params[:confidential] = filters[:confidential] == 'yes'
if [true, false].include?(filters[:confidential]) && Feature.enabled?(:search_filter_by_confidential)
params[:confidential] = filters[:confidential]
end
end
end
......
......@@ -448,4 +448,33 @@ RSpec.describe SearchHelper do
end
end
end
describe '#search_service' do
using RSpec::Parameterized::TableSyntax
subject { search_service }
before do
allow(self).to receive(:current_user).and_return(:the_current_user)
end
where(:confidential, :expected) do
'0' | false
'1' | true
'yes' | true
'no' | false
true | true
false | false
end
let(:params) {{ confidential: confidential }}
with_them do
it 'transforms confidentiality param' do
expect(::SearchService).to receive(:new).with(:the_current_user, { confidential: expected })
subject
end
end
end
end
......@@ -58,6 +58,17 @@ RSpec.describe API::Search do
end
end
shared_examples 'filter by confidentiality' do |scope:, search:|
it 'respects confidentiality filtering' do
get api(endpoint, user), params: { scope: scope, search: search, confidential: confidential.to_s }
documents = Gitlab::Json.parse(response.body)
expect(documents.count).to eq(1)
expect(documents.first['confidential']).to eq(confidential)
end
end
describe 'GET /search' do
let(:endpoint) { '/search' }
......@@ -137,6 +148,26 @@ RSpec.describe API::Search do
include_examples 'filter by state', scope: :issues, search: 'awesome'
end
end
context 'filter by confidentiality' do
before do
stub_feature_flags(search_filter_by_confidential: true)
create(:issue, project: project, author: user, title: 'awesome non-confidential issue')
create(:issue, :confidential, project: project, author: user, title: 'awesome confidential issue')
end
context 'confidential: true' do
let(:confidential) { true }
include_examples 'filter by confidentiality', scope: :issues, search: 'awesome'
end
context 'confidential: false' do
let(:confidential) { false }
include_examples 'filter by confidentiality', scope: :issues, search: 'awesome'
end
end
end
context 'for merge_requests scope' do
......
......@@ -24,7 +24,7 @@ RSpec.shared_examples 'search results filtered by confidential' do
end
context 'confidential filter' do
let(:filters) { { confidential: 'yes' } }
let(:filters) { { confidential: true } }
context 'when Feature search_filter_by_confidential enabled' do
it 'returns only confidential results', :aggregate_failures do
......@@ -46,7 +46,7 @@ RSpec.shared_examples 'search results filtered by confidential' do
end
context 'not confidential filter' do
let(:filters) { { confidential: 'no' } }
let(:filters) { { confidential: false } }
context 'when Feature search_filter_by_confidential enabled' do
it 'returns not confidential results', :aggregate_failures do
......@@ -66,26 +66,4 @@ RSpec.shared_examples 'search results filtered by confidential' do
end
end
end
context 'unsupported filter' do
let(:filters) { { confidential: 'goodbye' } }
context 'when Feature search_filter_by_confidential enabled' do
it 'returns confidential and not confidential results', :aggregate_failures do
expect(results.objects('issues')).to include confidential_result
expect(results.objects('issues')).to include opened_result
end
end
context 'when Feature search_filter_by_confidential not enabled' do
before do
stub_feature_flags(search_filter_by_confidential: false)
end
it 'returns confidential and not confidential results', :aggregate_failures do
expect(results.objects('issues')).to include confidential_result
expect(results.objects('issues')).to include opened_result
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