Commit be82f19b authored by Dmitry Gruzd's avatar Dmitry Gruzd Committed by Ash McKenzie

Add confidentiality filter support for search API

This MR adds the ability to filter search API results by the
confidentiality flag.
parent e7655a30
......@@ -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