Commit 09da07e6 authored by Jan Provaznik's avatar Jan Provaznik Committed by Sean McGivern

move users method to public section

this is for EE to be able to call this (elastic search)
parent 7380e48c
...@@ -29,6 +29,7 @@ class SearchController < ApplicationController ...@@ -29,6 +29,7 @@ class SearchController < ApplicationController
@search_objects = search_service.search_objects @search_objects = search_service.search_objects
render_commits if @scope == 'commits' render_commits if @scope == 'commits'
eager_load_user_status if @scope == 'users'
check_single_commit_result check_single_commit_result
end end
...@@ -54,6 +55,12 @@ class SearchController < ApplicationController ...@@ -54,6 +55,12 @@ class SearchController < ApplicationController
@search_objects = prepare_commits_for_rendering(@search_objects) @search_objects = prepare_commits_for_rendering(@search_objects)
end end
def eager_load_user_status
return if Feature.disabled?(:users_search, default_enabled: true)
@search_objects = @search_objects.eager_load(:status) # rubocop:disable CodeReuse/ActiveRecord
end
def check_single_commit_result def check_single_commit_result
if @search_results.single_commit_result? if @search_results.single_commit_result?
only_commit = @search_results.objects('commits').first only_commit = @search_results.objects('commits').first
......
...@@ -366,7 +366,8 @@ module ProjectsHelper ...@@ -366,7 +366,8 @@ module ProjectsHelper
blobs: :download_code, blobs: :download_code,
commits: :download_code, commits: :download_code,
merge_requests: :read_merge_request, merge_requests: :read_merge_request,
notes: [:read_merge_request, :download_code, :read_issue, :read_project_snippet] notes: [:read_merge_request, :download_code, :read_issue, :read_project_snippet],
members: :read_project_member
) )
end end
......
...@@ -201,6 +201,16 @@ module SearchHelper ...@@ -201,6 +201,16 @@ module SearchHelper
def limited_count(count, limit = 1000) def limited_count(count, limit = 1000)
count > limit ? "#{limit}+" : count count > limit ? "#{limit}+" : count
end end
def search_tabs?(tab)
return false if Feature.disabled?(:users_search, default_enabled: true)
if @project
project_search_tabs?(:members)
else
can?(current_user, :read_users_list)
end
end
end end
SearchHelper.prepend(EE::SearchHelper) SearchHelper.prepend(EE::SearchHelper)
...@@ -23,7 +23,8 @@ module Search ...@@ -23,7 +23,8 @@ module Search
def allowed_scopes def allowed_scopes
strong_memoize(:allowed_scopes) do strong_memoize(:allowed_scopes) do
%w[issues merge_requests milestones] allowed_scopes = %w[issues merge_requests milestones]
allowed_scopes << 'users' if Feature.enabled?(:users_search, default_enabled: true)
end end
end end
......
...@@ -11,6 +11,12 @@ module Search ...@@ -11,6 +11,12 @@ module Search
@group = group @group = group
end end
def execute
Gitlab::GroupSearchResults.new(
current_user, projects, group, params[:search], default_project_filter: default_project_filter
)
end
def projects def projects
return Project.none unless group return Project.none unless group
return @projects if defined? @projects return @projects if defined? @projects
......
...@@ -16,7 +16,12 @@ module Search ...@@ -16,7 +16,12 @@ module Search
end end
def scope def scope
@scope ||= %w[notes issues merge_requests milestones wiki_blobs commits].delete(params[:scope]) { 'blobs' } @scope ||= begin
allowed_scopes = %w[notes issues merge_requests milestones wiki_blobs commits]
allowed_scopes << 'users' if Feature.enabled?(:users_search, default_enabled: true)
allowed_scopes.delete(params[:scope]) { 'blobs' }
end
end end
end end
end end
......
- users = capture_haml do
- if search_tabs?(:members)
%li{ class: active_when(@scope == 'users') }
= link_to search_filter_path(scope: 'users') do
Users
%span.badge.badge-pill
= limited_count(@search_results.limited_users_count)
.scrolling-tabs-container.inner-page-scroll-tabs.is-smaller .scrolling-tabs-container.inner-page-scroll-tabs.is-smaller
.fade-left= icon('angle-left') .fade-left= icon('angle-left')
.fade-right= icon('angle-right') .fade-right= icon('angle-right')
...@@ -45,6 +53,7 @@ ...@@ -45,6 +53,7 @@
= _("Commits") = _("Commits")
%span.badge.badge-pill %span.badge.badge-pill
= @search_results.commits_count = @search_results.commits_count
= users
- elsif @show_snippets - elsif @show_snippets
%li{ class: active_when(@scope == 'snippet_blobs') } %li{ class: active_when(@scope == 'snippet_blobs') }
...@@ -94,3 +103,4 @@ ...@@ -94,3 +103,4 @@
= _("Wiki") = _("Wiki")
%span.badge.badge-pill %span.badge.badge-pill
= limited_count(@search_results.wiki_blobs_count) = limited_count(@search_results.wiki_blobs_count)
= users
%ul.content-list
%li
.avatar-cell.d-none.d-sm-block
= user_avatar(user: user, user_name: user.name, css_class: 'd-none d-sm-inline avatar s40')
.user-info
= link_to user_path(user), class: 'd-none d-sm-inline' do
.item-title
= user.name
= user_status(user)
.cgray= user.to_reference
---
title: Add users search results to global search
merge_request: 21197
author: Alexis Reigel
type: added
...@@ -17,7 +17,7 @@ GET /search ...@@ -17,7 +17,7 @@ GET /search
| `scope` | string | yes | The scope to search in | | `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query | | `search` | string | yes | The search query |
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs. Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, snippet_titles, snippet_blobs, users.
If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md).
...@@ -255,7 +255,7 @@ Example response: ...@@ -255,7 +255,7 @@ Example response:
### Scope: snippet_blobs ### Scope: snippet_blobs
```bash ```bash
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=snippet_blobs&search=test curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=snippet_blos&search=test
``` ```
Example response: Example response:
...@@ -375,6 +375,27 @@ Example response: ...@@ -375,6 +375,27 @@ Example response:
] ]
``` ```
### Scope: users
```bash
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/search?scope=users&search=doe
```
Example response:
```json
[
{
"id": 1,
"name": "John Doe1",
"username": "user1",
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
"web_url": "http://localhost/user1"
}
]
```
## Group Search API ## Group Search API
Search within the specified group. Search within the specified group.
...@@ -391,7 +412,7 @@ GET /groups/:id/search ...@@ -391,7 +412,7 @@ GET /groups/:id/search
| `scope` | string | yes | The scope to search in | | `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query | | `search` | string | yes | The search query |
Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones. Search the expression within the specified scope. Currently these scopes are supported: projects, issues, merge_requests, milestones, users.
If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md). If Elasticsearch is enabled additional scopes available are blobs, wiki_blobs and commits. Find more about [the feature](../integration/elasticsearch.md).
...@@ -687,6 +708,27 @@ Example response: ...@@ -687,6 +708,27 @@ Example response:
] ]
``` ```
### Scope: users
```bash
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/groups/3/search?scope=users&search=doe
```
Example response:
```json
[
{
"id": 1,
"name": "John Doe1",
"username": "user1",
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
"web_url": "http://localhost/user1"
}
]
```
## Project Search API ## Project Search API
Search within the specified project. Search within the specified project.
...@@ -703,7 +745,7 @@ GET /projects/:id/search ...@@ -703,7 +745,7 @@ GET /projects/:id/search
| `scope` | string | yes | The scope to search in | | `scope` | string | yes | The scope to search in |
| `search` | string | yes | The search query | | `search` | string | yes | The search query |
Search the expression within the specified scope. Currently these scopes are supported: issues, merge_requests, milestones, notes, wiki_blobs, commits, blobs. Search the expression within the specified scope. Currently these scopes are supported: issues, merge_requests, milestones, notes, wiki_blobs, commits, blobs, users.
The response depends on the requested scope. The response depends on the requested scope.
...@@ -1016,4 +1058,25 @@ Example response: ...@@ -1016,4 +1058,25 @@ Example response:
] ]
``` ```
### Scope: users
```bash
curl --request GET --header "PRIVATE-TOKEN: <your_access_token>" https://gitlab.example.com/api/v4/projects/6/search?scope=users&search=doe
```
Example response:
```json
[
{
"id": 1,
"name": "John Doe1",
"username": "user1",
"state": "active",
"avatar_url": "http://www.gravatar.com/avatar/c922747a93b40d1ea88262bf1aebee62?s=80&d=identicon",
"web_url": "http://localhost/user1"
}
]
```
[ce-41763]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41763 [ce-41763]: https://gitlab.com/gitlab-org/gitlab-ce/issues/41763
...@@ -9,7 +9,9 @@ module EE ...@@ -9,7 +9,9 @@ module EE
override :execute override :execute
def execute def execute
if ::Gitlab::CurrentSettings.elasticsearch_search? if ::Gitlab::CurrentSettings.elasticsearch_search?
::Gitlab::Elastic::SearchResults.new(current_user, params[:search], elastic_projects, elastic_global) ::Gitlab::Elastic::SearchResults.new(current_user, params[:search],
elastic_projects, projects,
elastic_global)
else else
super super
end end
......
...@@ -3,6 +3,8 @@ ...@@ -3,6 +3,8 @@
module EE module EE
module Search module Search
module GroupService module GroupService
extend ::Gitlab::Utils::Override
def elastic_projects def elastic_projects
@elastic_projects ||= projects.pluck(:id) # rubocop:disable CodeReuse/ActiveRecord @elastic_projects ||= projects.pluck(:id) # rubocop:disable CodeReuse/ActiveRecord
end end
...@@ -10,6 +12,15 @@ module EE ...@@ -10,6 +12,15 @@ module EE
def elastic_global def elastic_global
false false
end end
override :execute
def execute
return super unless ::Gitlab::CurrentSettings.elasticsearch_search?
::Gitlab::Elastic::GroupSearchResults.new(
current_user, elastic_projects, projects, group, params[:search],
elastic_global, default_project_filter: default_project_filter)
end
end end
end end
end end
# frozen_string_literal: true
module Gitlab
module Elastic
# Always prefer to use the full class namespace when specifying a
# superclass inside a module, because autoloading can occur in a
# different order between execution environments.
class GroupSearchResults < Gitlab::Elastic::SearchResults
delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results
attr_reader :group, :default_project_filter
def initialize(current_user, limit_project_ids, limit_projects, group, query, public_and_internal_projects, default_project_filter: false, per_page: 20)
super(current_user, query, limit_project_ids, limit_projects, public_and_internal_projects)
@default_project_filter = default_project_filter
@group = group
end
def objects(scope, page = nil)
case scope
when 'users'
users.page(page).per(per_page)
else
super
end
end
def generic_search_results
@generic_search_results ||= Gitlab::GroupSearchResults.new(current_user, limit_projects, group, query, default_project_filter: default_project_filter)
end
end
end
end
...@@ -8,6 +8,9 @@ module Gitlab ...@@ -8,6 +8,9 @@ module Gitlab
class ProjectSearchResults < Gitlab::Elastic::SearchResults class ProjectSearchResults < Gitlab::Elastic::SearchResults
attr_reader :project, :repository_ref attr_reader :project, :repository_ref
delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results
def initialize(current_user, query, project_id, repository_ref = nil) def initialize(current_user, query, project_id, repository_ref = nil)
@current_user = current_user @current_user = current_user
@project = Project.find(project_id) @project = Project.find(project_id)
...@@ -26,11 +29,17 @@ module Gitlab ...@@ -26,11 +29,17 @@ module Gitlab
wiki_blobs.page(page).per(per_page) wiki_blobs.page(page).per(per_page)
when 'commits' when 'commits'
commits(page: page, per_page: per_page) commits(page: page, per_page: per_page)
when 'users'
users.page(page).per(per_page)
else else
super super
end end
end end
def generic_search_results
@generic_search_results ||= Gitlab::ProjectSearchResults.new(current_user, project, query, repository_ref)
end
def blobs_count def blobs_count
@blobs_count ||= blobs.total_count @blobs_count ||= blobs.total_count
end end
......
...@@ -7,11 +7,15 @@ module Gitlab ...@@ -7,11 +7,15 @@ module Gitlab
# Limit search results by passed project ids # Limit search results by passed project ids
# It allows us to search only for projects user has access to # It allows us to search only for projects user has access to
attr_reader :limit_project_ids attr_reader :limit_project_ids, :limit_projects
def initialize(current_user, query, limit_project_ids, public_and_internal_projects = true) delegate :users, to: :generic_search_results
delegate :limited_users_count, to: :generic_search_results
def initialize(current_user, query, limit_project_ids, limit_projects = nil, public_and_internal_projects = true)
@current_user = current_user @current_user = current_user
@limit_project_ids = limit_project_ids @limit_project_ids = limit_project_ids
@limit_projects = limit_projects
@query = query @query = query
@public_and_internal_projects = public_and_internal_projects @public_and_internal_projects = public_and_internal_projects
end end
...@@ -32,11 +36,17 @@ module Gitlab ...@@ -32,11 +36,17 @@ module Gitlab
wiki_blobs.page(page).per(per_page) wiki_blobs.page(page).per(per_page)
when 'commits' when 'commits'
commits(page: page, per_page: per_page) commits(page: page, per_page: per_page)
when 'users'
users.page(page).per(per_page)
else else
Kaminari.paginate_array([]) Kaminari.paginate_array([])
end end
end end
def generic_search_results
@generic_search_results ||= Gitlab::SearchResults.new(current_user, limit_projects, query)
end
def projects_count def projects_count
@projects_count ||= projects.total_count @projects_count ||= projects.total_count
end end
......
require 'spec_helper'
describe Gitlab::Elastic::GroupSearchResults do
set(:user) { create(:user) }
set(:group) { create(:group) }
set(:guest) { create(:user).tap { |u| group.add_user(u, Gitlab::Access::GUEST) } }
before do
stub_ee_application_setting(elasticsearch_search: true, elasticsearch_indexing: true)
end
context 'user search' do
subject(:results) { described_class.new(user, nil, nil, group, guest.username, nil) }
before do
expect(Gitlab::GroupSearchResults).to receive(:new).and_call_original
end
it { expect(results.objects('users')).to eq([guest]) }
it { expect(results.limited_users_count).to eq(1) }
end
end
...@@ -224,4 +224,15 @@ describe Gitlab::Elastic::ProjectSearchResults do ...@@ -224,4 +224,15 @@ describe Gitlab::Elastic::ProjectSearchResults do
expect(results.issues_count).to eq 3 expect(results.issues_count).to eq 3
end end
end end
context 'user search' do
subject(:results) { described_class.new(user, project.owner.username, project.id) }
before do
expect(Gitlab::ProjectSearchResults).to receive(:new).and_call_original
end
it { expect(results.objects('users')).to eq([project.owner]) }
it { expect(results.limited_users_count).to eq(1) }
end
end end
...@@ -5,17 +5,17 @@ module API ...@@ -5,17 +5,17 @@ module API
module SearchHelpers module SearchHelpers
def self.global_search_scopes def self.global_search_scopes
# This is a separate method so that EE can redefine it. # This is a separate method so that EE can redefine it.
%w(projects issues merge_requests milestones snippet_titles snippet_blobs) %w(projects issues merge_requests milestones snippet_titles snippet_blobs users)
end end
def self.group_search_scopes def self.group_search_scopes
# This is a separate method so that EE can redefine it. # This is a separate method so that EE can redefine it.
%w(projects issues merge_requests milestones) %w(projects issues merge_requests milestones users)
end end
def self.project_search_scopes def self.project_search_scopes
# This is a separate method so that EE can redefine it. # This is a separate method so that EE can redefine it.
%w(issues merge_requests milestones notes wiki_blobs commits blobs) %w(issues merge_requests milestones notes wiki_blobs commits blobs users)
end end
end end
end end
......
...@@ -17,7 +17,8 @@ module API ...@@ -17,7 +17,8 @@ module API
blobs: Entities::Blob, blobs: Entities::Blob,
wiki_blobs: Entities::Blob, wiki_blobs: Entities::Blob,
snippet_titles: Entities::Snippet, snippet_titles: Entities::Snippet,
snippet_blobs: Entities::Snippet snippet_blobs: Entities::Snippet,
users: Entities::UserBasic
}.freeze }.freeze
def search(additional_params = {}) def search(additional_params = {})
...@@ -51,6 +52,12 @@ module API ...@@ -51,6 +52,12 @@ module API
# Defining this method here as a noop allows us to easily extend it in # Defining this method here as a noop allows us to easily extend it in
# EE, without having to modify this file directly. # EE, without having to modify this file directly.
end end
def check_users_search_allowed!
if params[:scope].to_sym == :users && Feature.disabled?(:users_search, default_enabled: true)
render_api_error!({ error: _("Scope not supported with disabled 'users_search' feature!") }, 400)
end
end
end end
resource :search do resource :search do
...@@ -67,6 +74,7 @@ module API ...@@ -67,6 +74,7 @@ module API
end end
get do get do
verify_search_scope! verify_search_scope!
check_users_search_allowed!
present search, with: entity present search, with: entity
end end
...@@ -87,6 +95,7 @@ module API ...@@ -87,6 +95,7 @@ module API
end end
get ':id/(-/)search' do get ':id/(-/)search' do
verify_search_scope! verify_search_scope!
check_users_search_allowed!
present search(group_id: user_group.id), with: entity present search(group_id: user_group.id), with: entity
end end
...@@ -106,6 +115,8 @@ module API ...@@ -106,6 +115,8 @@ module API
use :pagination use :pagination
end end
get ':id/(-/)search' do get ':id/(-/)search' do
check_users_search_allowed!
present search(project_id: user_project.id), with: entity present search(project_id: user_project.id), with: entity
end end
end end
......
# frozen_string_literal: true
module Gitlab
class GroupSearchResults < SearchResults
def initialize(current_user, limit_projects, group, query, default_project_filter: false, per_page: 20)
super(current_user, limit_projects, query, default_project_filter: default_project_filter, per_page: per_page)
@group = group
end
# rubocop:disable CodeReuse/ActiveRecord
def users
# 1: get all groups the current user has access to
groups = GroupsFinder.new(current_user).execute.joins(:users)
# 2: Get the group's whole hierarchy
group_users = @group.direct_and_indirect_users
# 3: get all users the current user has access to (->
# `SearchResults#users`), which also applies the query.
users = super
# 4: filter for users that belong to the previously selected groups
users
.where(id: group_users.select('id'))
.where(id: groups.select('members.user_id'))
end
# rubocop:enable CodeReuse/ActiveRecord
end
end
...@@ -22,11 +22,17 @@ module Gitlab ...@@ -22,11 +22,17 @@ module Gitlab
paginated_blobs(wiki_blobs, page) paginated_blobs(wiki_blobs, page)
when 'commits' when 'commits'
Kaminari.paginate_array(commits).page(page).per(per_page) Kaminari.paginate_array(commits).page(page).per(per_page)
when 'users'
users.page(page).per(per_page)
else else
super(scope, page, false) super(scope, page, false)
end end
end end
def users
super.where(id: @project.team.members) # rubocop:disable CodeReuse/ActiveRecord
end
def blobs_count def blobs_count
@blobs_count ||= blobs.count @blobs_count ||= blobs.count
end end
......
...@@ -32,6 +32,8 @@ module Gitlab ...@@ -32,6 +32,8 @@ module Gitlab
merge_requests.page(page).per(per_page) merge_requests.page(page).per(per_page)
when 'milestones' when 'milestones'
milestones.page(page).per(per_page) milestones.page(page).per(per_page)
when 'users'
users.page(page).per(per_page)
else else
Kaminari.paginate_array([]).page(page).per(per_page) Kaminari.paginate_array([]).page(page).per(per_page)
end end
...@@ -71,6 +73,12 @@ module Gitlab ...@@ -71,6 +73,12 @@ module Gitlab
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
# rubocop:disable CodeReuse/ActiveRecord
def limited_users_count
@limited_users_count ||= users.limit(count_limit).count
end
# rubocop:enable CodeReuse/ActiveRecord
def single_commit_result? def single_commit_result?
false false
end end
...@@ -79,6 +87,12 @@ module Gitlab ...@@ -79,6 +87,12 @@ module Gitlab
1001 1001
end end
def users
return User.none unless Ability.allowed?(current_user, :read_users_list)
UsersFinder.new(current_user, search: query).execute
end
private private
def projects def projects
......
...@@ -8816,6 +8816,9 @@ msgstr "" ...@@ -8816,6 +8816,9 @@ msgstr ""
msgid "Scope" msgid "Scope"
msgstr "" msgstr ""
msgid "Scope not supported with disabled 'users_search' feature!"
msgstr ""
msgid "Scoped issue boards" msgid "Scoped issue boards"
msgstr "" msgstr ""
......
require 'spec_helper'
describe 'User searches for users' do
context 'when on the dashboard' do
it 'finds the user' do
create(:user, username: 'gob_bluth', name: 'Gob Bluth')
sign_in(create(:user))
visit dashboard_projects_path
fill_in 'search', with: 'gob'
click_button 'Go'
expect(page).to have_content('Users 1')
click_on('Users 1')
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
end
end
context 'when on the project page' do
it 'finds the user belonging to the project' do
project = create(:project)
user1 = create(:user, username: 'gob_bluth', name: 'Gob Bluth')
create(:project_member, :developer, user: user1, project: project)
user2 = create(:user, username: 'michael_bluth', name: 'Michael Bluth')
create(:project_member, :developer, user: user2, project: project)
create(:user, username: 'gob_2018', name: 'George Oscar Bluth')
sign_in(user1)
visit projects_path(project)
fill_in 'search', with: 'gob'
click_button 'Go'
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
expect(page).not_to have_content('Michael Bluth')
expect(page).not_to have_content('@michael_bluth')
expect(page).not_to have_content('George Oscar Bluth')
expect(page).not_to have_content('@gob_2018')
end
end
context 'when on the group page' do
it 'finds the user belonging to the group' do
group = create(:group)
user1 = create(:user, username: 'gob_bluth', name: 'Gob Bluth')
create(:group_member, :developer, user: user1, group: group)
user2 = create(:user, username: 'michael_bluth', name: 'Michael Bluth')
create(:group_member, :developer, user: user2, group: group)
create(:user, username: 'gob_2018', name: 'George Oscar Bluth')
sign_in(user1)
visit group_path(group)
fill_in 'search', with: 'gob'
click_button 'Go'
expect(page).to have_content('Gob Bluth')
expect(page).to have_content('@gob_bluth')
expect(page).not_to have_content('Michael Bluth')
expect(page).not_to have_content('@michael_bluth')
expect(page).not_to have_content('George Oscar Bluth')
expect(page).not_to have_content('@gob_2018')
end
end
end
require 'spec_helper'
describe Gitlab::GroupSearchResults do
let(:user) { create(:user) }
describe 'user search' do
let(:group) { create(:group) }
it 'returns the users belonging to the group matching the search query' do
user1 = create(:user, username: 'gob_bluth')
create(:group_member, :developer, user: user1, group: group)
user2 = create(:user, username: 'michael_bluth')
create(:group_member, :developer, user: user2, group: group)
create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users')
expect(result).to eq [user1]
end
it 'returns the user belonging to the subgroup matching the search query', :nested_groups do
user1 = create(:user, username: 'gob_bluth')
subgroup = create(:group, parent: group)
create(:group_member, :developer, user: user1, group: subgroup)
create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users')
expect(result).to eq [user1]
end
it 'returns the user belonging to the parent group matching the search query', :nested_groups do
user1 = create(:user, username: 'gob_bluth')
parent_group = create(:group, children: [group])
create(:group_member, :developer, user: user1, group: parent_group)
create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users')
expect(result).to eq [user1]
end
it 'does not return the user belonging to the private subgroup', :nested_groups do
user1 = create(:user, username: 'gob_bluth')
subgroup = create(:group, :private, parent: group)
create(:group_member, :developer, user: user1, group: subgroup)
create(:user, username: 'gob_2018')
result = described_class.new(user, anything, group, 'gob').objects('users')
expect(result).to eq []
end
it 'does not return the user belonging to an unrelated group' do
user = create(:user, username: 'gob_bluth')
unrelated_group = create(:group)
create(:group_member, :developer, user: user, group: unrelated_group)
result = described_class.new(user, anything, group, 'gob').objects('users')
expect(result).to eq []
end
end
end
...@@ -412,4 +412,36 @@ describe Gitlab::ProjectSearchResults do ...@@ -412,4 +412,36 @@ describe Gitlab::ProjectSearchResults do
end end
end end
end end
describe 'user search' do
it 'returns the user belonging to the project matching the search query' do
project = create(:project)
user1 = create(:user, username: 'gob_bluth')
create(:project_member, :developer, user: user1, project: project)
user2 = create(:user, username: 'michael_bluth')
create(:project_member, :developer, user: user2, project: project)
create(:user, username: 'gob_2018')
result = described_class.new(user, project, 'gob').objects('users')
expect(result).to eq [user1]
end
it 'returns the user belonging to the group matching the search query' do
group = create(:group)
project = create(:project, namespace: group)
user1 = create(:user, username: 'gob_bluth')
create(:group_member, :developer, user: user1, group: group)
create(:user, username: 'gob_2018')
result = described_class.new(user, project, 'gob').objects('users')
expect(result).to eq [user1]
end
end
end end
...@@ -121,6 +121,22 @@ describe Gitlab::SearchResults do ...@@ -121,6 +121,22 @@ describe Gitlab::SearchResults do
results.objects('issues') results.objects('issues')
end end
end end
describe '#users' do
it 'does not call the UsersFinder when the current_user is not allowed to read users list' do
allow(Ability).to receive(:allowed?).and_return(false)
expect(UsersFinder).not_to receive(:new).with(user, search: 'foo').and_call_original
results.objects('users')
end
it 'calls the UsersFinder' do
expect(UsersFinder).to receive(:new).with(user, search: 'foo').and_call_original
results.objects('users')
end
end
end end
it 'does not list issues on private projects' do it 'does not list issues on private projects' do
......
...@@ -77,6 +77,28 @@ describe API::Search do ...@@ -77,6 +77,28 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones' it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end end
context 'for users scope' do
before do
create(:user, name: 'billy')
get api('/search', user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
get api('/search', user), params: { scope: 'users', search: 'billy' }
end
it 'returns 400 error' do
expect(response).to have_gitlab_http_status(400)
end
end
end
context 'for snippet_titles scope' do context 'for snippet_titles scope' do
before do before do
create(:snippet, :public, title: 'awesome snippet', content: 'snippet content') create(:snippet, :public, title: 'awesome snippet', content: 'snippet content')
...@@ -192,6 +214,40 @@ describe API::Search do ...@@ -192,6 +214,40 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones' it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end end
context 'for users scope' do
before do
user = create(:user, name: 'billy')
create(:group_member, :developer, user: user, group: group)
get api("/groups/#{group.id}/search", user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
get api("/groups/#{group.id}/search", user), params: { scope: 'users', search: 'billy' }
end
it 'returns 400 error' do
expect(response).to have_gitlab_http_status(400)
end
end
end
context 'for users scope with group path as id' do
before do
user1 = create(:user, name: 'billy')
create(:group_member, :developer, user: user1, group: group)
get api("/groups/#{CGI.escape(group.full_path)}/search", user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
end
end end
end end
...@@ -269,6 +325,29 @@ describe API::Search do ...@@ -269,6 +325,29 @@ describe API::Search do
it_behaves_like 'response is correct', schema: 'public_api/v4/milestones' it_behaves_like 'response is correct', schema: 'public_api/v4/milestones'
end end
context 'for users scope' do
before do
user1 = create(:user, name: 'billy')
create(:project_member, :developer, user: user1, project: project)
get api("/projects/#{project.id}/search", user), params: { scope: 'users', search: 'billy' }
end
it_behaves_like 'response is correct', schema: 'public_api/v4/user/basics'
context 'when users search feature is disabled' do
before do
allow(Feature).to receive(:disabled?).with(:users_search, default_enabled: true).and_return(true)
get api("/projects/#{project.id}/search", user), params: { scope: 'users', search: 'billy' }
end
it 'returns 400 error' do
expect(response).to have_gitlab_http_status(400)
end
end
end
context 'for notes scope' do context 'for notes scope' do
before do before do
create(:note_on_merge_request, project: project, note: 'awesome note') create(:note_on_merge_request, project: project, note: 'awesome note')
......
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