Commit e65b8416 authored by Terri Chu's avatar Terri Chu Committed by Mark Chao

Backend support for search issue filtering by confidential

Pass confidential filter through to search when backed
by PostgreSQL or Elasticsearch.
parent 759f6a3f
# frozen_string_literal: true # frozen_string_literal: true
module SearchHelper module SearchHelper
SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets, :state].freeze SEARCH_PERMITTED_PARAMS = [:search, :scope, :project_id, :group_id, :repository_ref, :snippets, :state, :confidential].freeze
def search_autocomplete_opts(term) def search_autocomplete_opts(term)
return unless current_user return unless current_user
......
...@@ -14,7 +14,7 @@ module Search ...@@ -14,7 +14,7 @@ module Search
Gitlab::SearchResults.new(current_user, Gitlab::SearchResults.new(current_user,
params[:search], params[:search],
projects, projects,
filters: { state: params[:state] }) filters: { state: params[:state], confidential: params[:confidential] })
end end
def projects def projects
......
...@@ -16,7 +16,7 @@ module Search ...@@ -16,7 +16,7 @@ module Search
params[:search], params[:search],
projects, projects,
group: group, group: group,
filters: { state: params[:state] } filters: { state: params[:state], confidential: params[:confidential] }
) )
end end
......
...@@ -13,7 +13,8 @@ module Search ...@@ -13,7 +13,8 @@ module Search
params[:search], params[:search],
project: project, project: project,
repository_ref: params[:repository_ref], repository_ref: params[:repository_ref],
filters: { state: params[:state] }) filters: { confidential: params[:confidential], state: params[:state] }
)
end end
def scope def scope
......
...@@ -16,7 +16,7 @@ module EE ...@@ -16,7 +16,7 @@ module EE
params[:search], params[:search],
elastic_projects, elastic_projects,
public_and_internal_projects: elastic_global, public_and_internal_projects: elastic_global,
filters: { state: params[:state] } filters: { confidential: params[:confidential], state: params[:state] }
) )
end end
......
...@@ -30,7 +30,7 @@ module EE ...@@ -30,7 +30,7 @@ module EE
elastic_projects, elastic_projects,
group: group, group: group,
public_and_internal_projects: elastic_global, public_and_internal_projects: elastic_global,
filters: { state: params[:state] } filters: { confidential: params[:confidential], state: params[:state] }
) )
end end
end end
......
...@@ -15,7 +15,7 @@ module EE ...@@ -15,7 +15,7 @@ module EE
params[:search], params[:search],
project: project, project: project,
repository_ref: repository_ref, repository_ref: repository_ref,
filters: { state: params[:state] } filters: { confidential: params[:confidential], state: params[:state] }
) )
end end
......
...@@ -33,6 +33,11 @@ module Elastic ...@@ -33,6 +33,11 @@ module Elastic
def confidentiality_filter(query_hash, options) def confidentiality_filter(query_hash, options)
current_user = options[:current_user] current_user = options[:current_user]
project_ids = options[:project_ids] 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' } }
end
return query_hash if current_user&.can_read_all_resources? return query_hash if current_user&.can_read_all_resources?
......
...@@ -197,8 +197,7 @@ module Gitlab ...@@ -197,8 +197,7 @@ module Gitlab
def issues def issues
strong_memoize(:issues) do strong_memoize(:issues) do
options = base_options options = base_options.merge(filters.slice(:confidential, :state))
options[:state] = filters[:state] if filters.key?(:state)
Issue.elastic_search(query, options: options) Issue.elastic_search(query, options: options)
end end
......
...@@ -19,15 +19,19 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults, :elastic do ...@@ -19,15 +19,19 @@ RSpec.describe Gitlab::Elastic::GroupSearchResults, :elastic do
let!(:project) { create(:project, :public, group: group) } let!(:project) { create(:project, :public, group: group) }
let!(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') } let!(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') }
let!(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') } let!(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') }
let!(:confidential_result) { create(:issue, :confidential, project: project, title: 'foo confidential') }
let(:query) { 'foo' } let(:query) { 'foo' }
let(:scope) { 'issues' } let(:scope) { 'issues' }
include_examples 'search results filtered by state' do before do
before do project.add_developer(user)
ensure_elasticsearch_index!
end ensure_elasticsearch_index!
end end
include_examples 'search results filtered by state'
include_examples 'search results filtered by confidential'
end end
context 'merge_requests search', :sidekiq_inline do context 'merge_requests search', :sidekiq_inline do
......
...@@ -70,32 +70,35 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do ...@@ -70,32 +70,35 @@ RSpec.describe Gitlab::Elastic::ProjectSearchResults, :elastic do
end end
context 'filtering' do context 'filtering' do
let!(:project) { create(:project, :public) }
let(:query) { 'foo' }
context 'issues' do context 'issues' do
let!(:project) { create(:project, :public) }
let!(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') } let!(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') }
let!(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') } let!(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') }
let(:query) { 'foo' } let!(:confidential_result) { create(:issue, :confidential, project: project, title: 'foo confidential') }
let(:scope) { 'issues' } let(:scope) { 'issues' }
include_examples 'search results filtered by state' do before do
before do project.add_developer(user)
ensure_elasticsearch_index!
end ensure_elasticsearch_index!
end end
include_examples 'search results filtered by state'
include_examples 'search results filtered by confidential'
end end
context 'merge_requests' do context 'merge_requests' do
let!(:project) { create(:project, :public) }
let!(:opened_result) { create(:merge_request, :opened, source_project: project, title: 'foo opened') } let!(:opened_result) { create(:merge_request, :opened, source_project: project, title: 'foo opened') }
let!(:closed_result) { create(:merge_request, :closed, source_project: project, title: 'foo closed') } let!(:closed_result) { create(:merge_request, :closed, source_project: project, title: 'foo closed') }
let(:query) { 'foo' }
let(:scope) { 'merge_requests' } let(:scope) { 'merge_requests' }
include_examples 'search results filtered by state' do before do
before do ensure_elasticsearch_index!
ensure_elasticsearch_index!
end
end end
include_examples 'search results filtered by state'
end end
end end
end end
......
...@@ -171,15 +171,19 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need ...@@ -171,15 +171,19 @@ RSpec.describe Gitlab::Elastic::SearchResults, :elastic, :sidekiq_might_not_need
let!(:project) { create(:project, :public) } let!(:project) { create(:project, :public) }
let!(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') } let!(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') }
let!(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') } let!(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') }
let!(:confidential_result) { create(:issue, :confidential, project: project, title: 'foo confidential') }
let(:scope) { 'issues' } let(:scope) { 'issues' }
let(:results) { described_class.new(user, 'foo', [project], filters: filters) } let(:results) { described_class.new(user, 'foo', [project.id], filters: filters) }
include_examples 'search results filtered by state' do before do
before do project.add_developer(user)
ensure_elasticsearch_index!
end ensure_elasticsearch_index!
end end
include_examples 'search results filtered by state'
include_examples 'search results filtered by confidential'
end end
end end
......
...@@ -193,6 +193,10 @@ module Gitlab ...@@ -193,6 +193,10 @@ module Gitlab
end end
params[:state] = filters[:state] if filters.key?(:state) 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'
end
end end
end end
......
...@@ -17,10 +17,17 @@ RSpec.describe Gitlab::GroupSearchResults do ...@@ -17,10 +17,17 @@ RSpec.describe Gitlab::GroupSearchResults do
describe 'issues search' do describe 'issues search' do
let_it_be(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') } let_it_be(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') }
let_it_be(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') } let_it_be(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') }
let_it_be(:confidential_result) { create(:issue, :confidential, project: project, title: 'foo confidential') }
let(:query) { 'foo' } let(:query) { 'foo' }
let(:scope) { 'issues' } let(:scope) { 'issues' }
before do
project.add_developer(user)
end
include_examples 'search results filtered by state' include_examples 'search results filtered by state'
include_examples 'search results filtered by confidential'
end end
describe 'merge_requests search' do describe 'merge_requests search' do
......
...@@ -265,9 +265,15 @@ RSpec.describe Gitlab::ProjectSearchResults do ...@@ -265,9 +265,15 @@ RSpec.describe Gitlab::ProjectSearchResults do
let_it_be(:project) { create(:project, :public) } let_it_be(:project) { create(:project, :public) }
let_it_be(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') } let_it_be(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') }
let_it_be(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') } let_it_be(:opened_result) { create(:issue, :opened, project: project, title: 'foo opened') }
let_it_be(:confidential_result) { create(:issue, :confidential, project: project, title: 'foo confidential') }
let(:query) { 'foo' } let(:query) { 'foo' }
before do
project.add_developer(user)
end
include_examples 'search results filtered by state' include_examples 'search results filtered by state'
include_examples 'search results filtered by confidential'
end end
end end
......
...@@ -181,8 +181,10 @@ RSpec.describe Gitlab::SearchResults do ...@@ -181,8 +181,10 @@ RSpec.describe Gitlab::SearchResults do
let_it_be(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') } let_it_be(:closed_result) { create(:issue, :closed, project: project, title: 'foo closed') }
let_it_be(:opened_result) { create(:issue, :opened, project: project, title: 'foo open') } let_it_be(:opened_result) { create(:issue, :opened, project: project, title: 'foo open') }
let_it_be(:confidential_result) { create(:issue, :confidential, project: project, title: 'foo confidential') }
include_examples 'search results filtered by state' include_examples 'search results filtered by state'
include_examples 'search results filtered by confidential'
end end
end end
......
# frozen_string_literal: true
RSpec.shared_examples 'search results filtered by confidential' do
context 'filter not provided (all behavior)' do
let(:filters) { {} }
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
context 'confidential filter' do
let(:filters) { { confidential: 'yes' } }
context 'when Feature search_filter_by_confidential enabled' do
it 'returns only confidential results', :aggregate_failures do
expect(results.objects('issues')).to include confidential_result
expect(results.objects('issues')).not_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
context 'not confidential filter' do
let(:filters) { { confidential: 'no' } }
context 'when Feature search_filter_by_confidential enabled' do
it 'returns not confidential results', :aggregate_failures do
expect(results.objects('issues')).not_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
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