Refactor SearchController#show
This commit is contained in:
parent
53b21c1e8e
commit
b0ab0e4eff
7 changed files with 408 additions and 72 deletions
|
@ -6,45 +6,19 @@ class SearchController < ApplicationController
|
||||||
layout 'search'
|
layout 'search'
|
||||||
|
|
||||||
def show
|
def show
|
||||||
if params[:project_id].present?
|
search_service = SearchService.new(current_user, params)
|
||||||
@project = Project.find_by(id: params[:project_id])
|
|
||||||
@project = nil unless can?(current_user, :download_code, @project)
|
|
||||||
end
|
|
||||||
|
|
||||||
if params[:group_id].present?
|
@project = search_service.project
|
||||||
@group = Group.find_by(id: params[:group_id])
|
@group = search_service.group
|
||||||
@group = nil unless can?(current_user, :read_group, @group)
|
|
||||||
end
|
|
||||||
|
|
||||||
return if params[:search].blank?
|
return if params[:search].blank?
|
||||||
|
|
||||||
@search_term = params[:search]
|
@search_term = params[:search]
|
||||||
|
|
||||||
@scope = params[:scope]
|
@scope = search_service.scope
|
||||||
@show_snippets = params[:snippets].eql? 'true'
|
@show_snippets = search_service.show_snippets?
|
||||||
|
@search_results = search_service.search_results
|
||||||
@search_results =
|
@search_objects = search_service.search_objects
|
||||||
if @project
|
|
||||||
unless %w(blobs notes issues merge_requests milestones wiki_blobs
|
|
||||||
commits).include?(@scope)
|
|
||||||
@scope = 'blobs'
|
|
||||||
end
|
|
||||||
|
|
||||||
Search::ProjectService.new(@project, current_user, params).execute
|
|
||||||
elsif @show_snippets
|
|
||||||
unless %w(snippet_blobs snippet_titles).include?(@scope)
|
|
||||||
@scope = 'snippet_blobs'
|
|
||||||
end
|
|
||||||
|
|
||||||
Search::SnippetService.new(current_user, params).execute
|
|
||||||
else
|
|
||||||
unless %w(projects issues merge_requests milestones).include?(@scope)
|
|
||||||
@scope = 'projects'
|
|
||||||
end
|
|
||||||
Search::GlobalService.new(current_user, params).execute
|
|
||||||
end
|
|
||||||
|
|
||||||
@search_objects = @search_results.objects(@scope, params[:page])
|
|
||||||
|
|
||||||
check_single_commit_result
|
check_single_commit_result
|
||||||
end
|
end
|
||||||
|
|
|
@ -16,5 +16,9 @@ module Search
|
||||||
|
|
||||||
Gitlab::SearchResults.new(current_user, projects, params[:search])
|
Gitlab::SearchResults.new(current_user, projects, params[:search])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
@scope ||= %w[issues merge_requests milestones].delete(params[:scope]) { 'projects' }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,5 +12,9 @@ module Search
|
||||||
params[:search],
|
params[:search],
|
||||||
params[:repository_ref])
|
params[:repository_ref])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
@scope ||= %w[notes issues merge_requests milestones wiki_blobs commits].delete(params[:scope]) { 'blobs' }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,5 +11,9 @@ module Search
|
||||||
|
|
||||||
Gitlab::SnippetSearchResults.new(snippets, params[:search])
|
Gitlab::SnippetSearchResults.new(snippets, params[:search])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def scope
|
||||||
|
@scope ||= %w[snippet_titles].delete(params[:scope]) { 'snippet_blobs' }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
63
app/services/search_service.rb
Normal file
63
app/services/search_service.rb
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
class SearchService
|
||||||
|
include Gitlab::Allowable
|
||||||
|
|
||||||
|
def initialize(current_user, params = {})
|
||||||
|
@current_user = current_user
|
||||||
|
@params = params.dup
|
||||||
|
end
|
||||||
|
|
||||||
|
def project
|
||||||
|
return @project if defined?(@project)
|
||||||
|
|
||||||
|
@project =
|
||||||
|
if params[:project_id].present?
|
||||||
|
the_project = Project.find_by(id: params[:project_id])
|
||||||
|
can?(current_user, :download_code, the_project) ? the_project : nil
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def group
|
||||||
|
return @group if defined?(@group)
|
||||||
|
|
||||||
|
@group =
|
||||||
|
if params[:group_id].present?
|
||||||
|
the_group = Group.find_by(id: params[:group_id])
|
||||||
|
can?(current_user, :read_group, the_group) ? the_group : nil
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def show_snippets?
|
||||||
|
return @show_snippets if defined?(@show_snippets)
|
||||||
|
|
||||||
|
@show_snippets = params[:snippets] == 'true'
|
||||||
|
end
|
||||||
|
|
||||||
|
delegate :scope, to: :search_service
|
||||||
|
|
||||||
|
def search_results
|
||||||
|
@search_results ||= search_service.execute
|
||||||
|
end
|
||||||
|
|
||||||
|
def search_objects
|
||||||
|
@search_objects ||= search_results.objects(scope, params[:page])
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def search_service
|
||||||
|
@search_service ||=
|
||||||
|
if project
|
||||||
|
Search::ProjectService.new(project, current_user, params)
|
||||||
|
elsif show_snippets?
|
||||||
|
Search::SnippetService.new(current_user, params)
|
||||||
|
else
|
||||||
|
Search::GlobalService.new(current_user, params)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :current_user, :params
|
||||||
|
end
|
66
spec/services/search/global_service_spec.rb
Normal file
66
spec/services/search/global_service_spec.rb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
describe Search::GlobalService, services: true do
|
||||||
|
let(:user) { create(:user) }
|
||||||
|
let(:internal_user) { create(:user) }
|
||||||
|
|
||||||
|
let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') }
|
||||||
|
let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') }
|
||||||
|
let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') }
|
||||||
|
let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') }
|
||||||
|
|
||||||
|
before do
|
||||||
|
found_project.add_master(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#execute' do
|
||||||
|
context 'unauthenticated' do
|
||||||
|
it 'returns public projects only' do
|
||||||
|
results = Search::GlobalService.new(nil, search: "searchable").execute
|
||||||
|
|
||||||
|
expect(results.objects('projects')).to match_array [public_project]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'authenticated' do
|
||||||
|
it 'returns public, internal and private projects' do
|
||||||
|
results = Search::GlobalService.new(user, search: "searchable").execute
|
||||||
|
|
||||||
|
expect(results.objects('projects')).to match_array [public_project, found_project, internal_project]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns only public & internal projects' do
|
||||||
|
results = Search::GlobalService.new(internal_user, search: "searchable").execute
|
||||||
|
|
||||||
|
expect(results.objects('projects')).to match_array [internal_project, public_project]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'namespace name is searchable' do
|
||||||
|
results = Search::GlobalService.new(user, search: found_project.namespace.path).execute
|
||||||
|
|
||||||
|
expect(results.objects('projects')).to match_array [found_project]
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'nested group' do
|
||||||
|
let!(:nested_group) { create(:group, :nested) }
|
||||||
|
let!(:project) { create(:empty_project, namespace: nested_group) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
project.add_master(user)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns result from nested group' do
|
||||||
|
results = Search::GlobalService.new(user, search: project.path).execute
|
||||||
|
|
||||||
|
expect(results.objects('projects')).to match_array [project]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns result from descendants when search inside group' do
|
||||||
|
results = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent).execute
|
||||||
|
|
||||||
|
expect(results.objects('projects')).to match_array [project]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,65 +1,286 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
describe 'Search::GlobalService', services: true do
|
describe SearchService, services: true do
|
||||||
let(:user) { create(:user) }
|
let(:user) { create(:user) }
|
||||||
let(:public_user) { create(:user) }
|
|
||||||
let(:internal_user) { create(:user) }
|
|
||||||
|
|
||||||
let!(:found_project) { create(:empty_project, :private, name: 'searchable_project') }
|
let(:accessible_group) { create(:group, :private) }
|
||||||
let!(:unfound_project) { create(:empty_project, :private, name: 'unfound_project') }
|
let(:inaccessible_group) { create(:group, :private) }
|
||||||
let!(:internal_project) { create(:empty_project, :internal, name: 'searchable_internal_project') }
|
let!(:group_member) { create(:group_member, group: accessible_group, user: user) }
|
||||||
let!(:public_project) { create(:empty_project, :public, name: 'searchable_public_project') }
|
|
||||||
|
let!(:accessible_project) { create(:empty_project, :private, name: 'accessible_project') }
|
||||||
|
let!(:inaccessible_project) { create(:empty_project, :private, name: 'inaccessible_project') }
|
||||||
|
let(:note) { create(:note_on_issue, project: accessible_project) }
|
||||||
|
|
||||||
|
let(:snippet) { create(:snippet, author: user) }
|
||||||
|
let(:group_project) { create(:empty_project, group: accessible_group, name: 'group_project') }
|
||||||
|
let(:public_project) { create(:empty_project, :public, name: 'public_project') }
|
||||||
|
|
||||||
before do
|
before do
|
||||||
found_project.team << [user, :master]
|
accessible_project.add_master(user)
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#execute' do
|
describe '#project' do
|
||||||
context 'unauthenticated' do
|
context 'when the project is accessible' do
|
||||||
it 'returns public projects only' do
|
it 'returns the project' do
|
||||||
context = Search::GlobalService.new(nil, search: "searchable")
|
project = SearchService.new(user, project_id: accessible_project.id).project
|
||||||
results = context.execute
|
|
||||||
expect(results.objects('projects')).to match_array [public_project]
|
expect(project).to eq accessible_project
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'authenticated' do
|
context 'when the project is not accessible' do
|
||||||
it 'returns public, internal and private projects' do
|
it 'returns nil' do
|
||||||
context = Search::GlobalService.new(user, search: "searchable")
|
project = SearchService.new(user, project_id: inaccessible_project.id).project
|
||||||
results = context.execute
|
|
||||||
expect(results.objects('projects')).to match_array [public_project, found_project, internal_project]
|
expect(project).to be_nil
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns only public & internal projects' do
|
context 'when there is no project_id' do
|
||||||
context = Search::GlobalService.new(internal_user, search: "searchable")
|
it 'returns nil' do
|
||||||
results = context.execute
|
project = SearchService.new(user).project
|
||||||
expect(results.objects('projects')).to match_array [internal_project, public_project]
|
|
||||||
|
expect(project).to be_nil
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'namespace name is searchable' do
|
describe '#group' do
|
||||||
context = Search::GlobalService.new(user, search: found_project.namespace.path)
|
context 'when the group is accessible' do
|
||||||
results = context.execute
|
it 'returns the group' do
|
||||||
expect(results.objects('projects')).to match_array [found_project]
|
group = SearchService.new(user, group_id: accessible_group.id).group
|
||||||
|
|
||||||
|
expect(group).to eq accessible_group
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context 'nested group' do
|
context 'when the group is not accessible' do
|
||||||
let!(:nested_group) { create(:group, :nested) }
|
it 'returns nil' do
|
||||||
let!(:project) { create(:empty_project, namespace: nested_group) }
|
group = SearchService.new(user, group_id: inaccessible_group.id).group
|
||||||
|
|
||||||
before { project.add_master(user) }
|
expect(group).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns result from nested group' do
|
context 'when there is no group_id' do
|
||||||
context = Search::GlobalService.new(user, search: project.path)
|
it 'returns nil' do
|
||||||
results = context.execute
|
group = SearchService.new(user).group
|
||||||
expect(results.objects('projects')).to match_array [project]
|
|
||||||
|
expect(group).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#show_snippets?' do
|
||||||
|
context 'when :snippets is \'true\'' do
|
||||||
|
it 'returns true' do
|
||||||
|
show_snippets = SearchService.new(user, snippets: 'true').show_snippets?
|
||||||
|
|
||||||
|
expect(show_snippets).to be_truthy
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when :snippets is not \'true\'' do
|
||||||
|
it 'returns false' do
|
||||||
|
show_snippets = SearchService.new(user, snippets: 'tru').show_snippets?
|
||||||
|
|
||||||
|
expect(show_snippets).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when :snippets is missing' do
|
||||||
|
it 'returns false' do
|
||||||
|
show_snippets = SearchService.new(user).show_snippets?
|
||||||
|
|
||||||
|
expect(show_snippets).to be_falsey
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#scope' do
|
||||||
|
context 'with accessible project_id' do
|
||||||
|
context 'and allowed scope' do
|
||||||
|
it 'returns the specified scope' do
|
||||||
|
scope = SearchService.new(user, project_id: accessible_project.id, scope: 'notes').scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'notes'
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'returns result from descendants when search inside group' do
|
context 'and disallowed scope' do
|
||||||
context = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent)
|
it 'returns the default scope' do
|
||||||
results = context.execute
|
scope = SearchService.new(user, project_id: accessible_project.id, scope: 'projects').scope
|
||||||
expect(results.objects('projects')).to match_array [project]
|
|
||||||
|
expect(scope).to eq 'blobs'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'and no scope' do
|
||||||
|
it 'returns the default scope' do
|
||||||
|
scope = SearchService.new(user, project_id: accessible_project.id).scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'blobs'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with \'true\' snippets' do
|
||||||
|
context 'and allowed scope' do
|
||||||
|
it 'returns the specified scope' do
|
||||||
|
scope = SearchService.new(user, snippets: 'true', scope: 'snippet_titles').scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'snippet_titles'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and disallowed scope' do
|
||||||
|
it 'returns the default scope' do
|
||||||
|
scope = SearchService.new(user, snippets: 'true', scope: 'projects').scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'snippet_blobs'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and no scope' do
|
||||||
|
it 'returns the default scope' do
|
||||||
|
scope = SearchService.new(user, snippets: 'true').scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'snippet_blobs'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with no project_id, no snippets' do
|
||||||
|
context 'and allowed scope' do
|
||||||
|
it 'returns the specified scope' do
|
||||||
|
scope = SearchService.new(user, scope: 'issues').scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'issues'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and disallowed scope' do
|
||||||
|
it 'returns the default scope' do
|
||||||
|
scope = SearchService.new(user, scope: 'blobs').scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'projects'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'and no scope' do
|
||||||
|
it 'returns the default scope' do
|
||||||
|
scope = SearchService.new(user).scope
|
||||||
|
|
||||||
|
expect(scope).to eq 'projects'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#search_results' do
|
||||||
|
context 'with accessible project_id' do
|
||||||
|
it 'returns an instance of Gitlab::ProjectSearchResults' do
|
||||||
|
search_results = SearchService.new(
|
||||||
|
user,
|
||||||
|
project_id: accessible_project.id,
|
||||||
|
scope: 'notes',
|
||||||
|
search: note.note).search_results
|
||||||
|
|
||||||
|
expect(search_results).to be_a Gitlab::ProjectSearchResults
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with accessible project_id and \'true\' snippets' do
|
||||||
|
it 'returns an instance of Gitlab::ProjectSearchResults' do
|
||||||
|
search_results = SearchService.new(
|
||||||
|
user,
|
||||||
|
project_id: accessible_project.id,
|
||||||
|
snippets: 'true',
|
||||||
|
scope: 'notes',
|
||||||
|
search: note.note).search_results
|
||||||
|
|
||||||
|
expect(search_results).to be_a Gitlab::ProjectSearchResults
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with \'true\' snippets' do
|
||||||
|
it 'returns an instance of Gitlab::SnippetSearchResults' do
|
||||||
|
search_results = SearchService.new(
|
||||||
|
user,
|
||||||
|
snippets: 'true',
|
||||||
|
search: snippet.content).search_results
|
||||||
|
|
||||||
|
expect(search_results).to be_a Gitlab::SnippetSearchResults
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with no project_id and no snippets' do
|
||||||
|
it 'returns an instance of Gitlab::SearchResults' do
|
||||||
|
search_results = SearchService.new(
|
||||||
|
user,
|
||||||
|
search: public_project.name).search_results
|
||||||
|
|
||||||
|
expect(search_results).to be_a Gitlab::SearchResults
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#search_objects' do
|
||||||
|
context 'with accessible project_id' do
|
||||||
|
it 'returns objects in the project' do
|
||||||
|
search_objects = SearchService.new(
|
||||||
|
user,
|
||||||
|
project_id: accessible_project.id,
|
||||||
|
scope: 'notes',
|
||||||
|
search: note.note).search_objects
|
||||||
|
|
||||||
|
expect(search_objects.first).to eq note
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with accessible project_id and \'true\' snippets' do
|
||||||
|
it 'returns objects in the project' do
|
||||||
|
search_objects = SearchService.new(
|
||||||
|
user,
|
||||||
|
project_id: accessible_project.id,
|
||||||
|
snippets: 'true',
|
||||||
|
scope: 'notes',
|
||||||
|
search: note.note).search_objects
|
||||||
|
|
||||||
|
expect(search_objects.first).to eq note
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with \'true\' snippets' do
|
||||||
|
it 'returns objects in snippets' do
|
||||||
|
search_objects = SearchService.new(
|
||||||
|
user,
|
||||||
|
snippets: 'true',
|
||||||
|
search: snippet.content).search_objects
|
||||||
|
|
||||||
|
expect(search_objects.first).to eq snippet
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with accessible group_id' do
|
||||||
|
it 'returns objects in the group' do
|
||||||
|
search_objects = SearchService.new(
|
||||||
|
user,
|
||||||
|
group_id: accessible_group.id,
|
||||||
|
search: group_project.name).search_objects
|
||||||
|
|
||||||
|
expect(search_objects.first).to eq group_project
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with no project_id, group_id or snippets' do
|
||||||
|
it 'returns objects in global' do
|
||||||
|
search_objects = SearchService.new(
|
||||||
|
user,
|
||||||
|
search: public_project.name).search_objects
|
||||||
|
|
||||||
|
expect(search_objects.first).to eq public_project
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue