summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFilipa Lacerda <filipa@gitlab.com>2017-04-01 00:27:34 +0100
committerFilipa Lacerda <filipa@gitlab.com>2017-04-01 00:27:34 +0100
commit8d3ed6e49e1577e4198b7d5e60fd2e70cb3a446d (patch)
tree3d77ec54a8e97a75aee650a2ef4e464065af9c5d
parentd0260cb7f20ba23672d773e5fc011f9c2cf81aaa (diff)
parentb54acba8b732688c59fe2f38510c469dc86ee499 (diff)
downloadgitlab-ce-30264-fix-vue-pagination.tar.gz
Merge branch 'master' into 30264-fix-vue-pagination30264-fix-vue-pagination
* master: Prevent users from disconnecting gitlab account from CAS 30276 Move issue, mr, todos next to profile dropdown in top nav Refactor SearchController#show Updating documentation to include a missing step in the update procedure Fix link to Jira service documentation Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery Fixed a gap underneath the scrolling inner page links Add download attribute to download links
-rw-r--r--app/assets/javascripts/vue_pipelines_index/components/pipelines_artifacts.js1
-rw-r--r--app/assets/stylesheets/framework/nav.scss8
-rw-r--r--app/controllers/profiles/accounts_controller.rb13
-rw-r--r--app/controllers/search_controller.rb40
-rw-r--r--app/helpers/auth_helper.rb4
-rw-r--r--app/models/project_services/jira_service.rb2
-rw-r--r--app/models/user.rb6
-rw-r--r--app/services/search/global_service.rb4
-rw-r--r--app/services/search/project_service.rb4
-rw-r--r--app/services/search/snippet_service.rb4
-rw-r--r--app/services/search_service.rb63
-rw-r--r--app/views/layouts/header/_default.html.haml18
-rw-r--r--app/views/notify/project_was_exported_email.html.haml2
-rw-r--r--app/views/profiles/accounts/show.html.haml8
-rw-r--r--app/views/projects/artifacts/browse.html.haml2
-rw-r--r--app/views/projects/builds/_sidebar.html.haml2
-rw-r--r--app/views/projects/ci/builds/_build.html.haml2
-rw-r--r--app/views/projects/edit.html.haml2
-rw-r--r--changelogs/unreleased/25556-prevent-users-from-disconnecting-gitlab-account-from-cas.yml4
-rw-r--r--changelogs/unreleased/29929-jira-doc.yml4
-rw-r--r--changelogs/unreleased/30276-move-top-badges.yml4
-rw-r--r--changelogs/unreleased/forked-subquery-order.yml4
-rw-r--r--doc/update/8.2-to-8.3.md8
-rw-r--r--spec/controllers/profiles/accounts_controller_spec.rb52
-rw-r--r--spec/helpers/auth_helper_spec.rb14
-rw-r--r--spec/services/search/global_service_spec.rb66
-rw-r--r--spec/services/search_service_spec.rb299
27 files changed, 531 insertions, 109 deletions
diff --git a/app/assets/javascripts/vue_pipelines_index/components/pipelines_artifacts.js b/app/assets/javascripts/vue_pipelines_index/components/pipelines_artifacts.js
index 3555040d60f..f18e2dfadaf 100644
--- a/app/assets/javascripts/vue_pipelines_index/components/pipelines_artifacts.js
+++ b/app/assets/javascripts/vue_pipelines_index/components/pipelines_artifacts.js
@@ -21,6 +21,7 @@ export default {
<li v-for="artifact in artifacts">
<a
rel="nofollow"
+ download
:href="artifact.path">
<i class="fa fa-download" aria-hidden="true"></i>
<span>Download {{artifact.name}} artifacts</span>
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 0e09638a8cc..e6d808717f3 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -146,6 +146,10 @@
display: block;
}
+ &.scrolling-tabs {
+ float: left;
+ }
+
li a {
padding: 16px 15px 11px;
}
@@ -480,6 +484,10 @@
.inner-page-scroll-tabs {
position: relative;
+ .nav-links {
+ padding-bottom: 1px;
+ }
+
.fade-right {
@include fade(left, $white-light);
right: 0;
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index 69959fe3687..7d1aa8d1ce0 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -1,11 +1,22 @@
class Profiles::AccountsController < Profiles::ApplicationController
+ include AuthHelper
+
def show
@user = current_user
end
def unlink
provider = params[:provider]
- current_user.identities.find_by(provider: provider).destroy unless provider.to_s == 'saml'
+ identity = current_user.identities.find_by(provider: provider)
+
+ return render_404 unless identity
+
+ if unlink_allowed?(provider)
+ identity.destroy
+ else
+ flash[:alert] = "You are not allowed to unlink your primary login account"
+ end
+
redirect_to profile_account_path
end
end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 612d69cf557..4a579601785 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -6,45 +6,19 @@ class SearchController < ApplicationController
layout 'search'
def show
- if params[:project_id].present?
- @project = Project.find_by(id: params[:project_id])
- @project = nil unless can?(current_user, :download_code, @project)
- end
+ search_service = SearchService.new(current_user, params)
- if params[:group_id].present?
- @group = Group.find_by(id: params[:group_id])
- @group = nil unless can?(current_user, :read_group, @group)
- end
+ @project = search_service.project
+ @group = search_service.group
return if params[:search].blank?
@search_term = params[:search]
- @scope = params[:scope]
- @show_snippets = params[:snippets].eql? 'true'
-
- @search_results =
- 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])
+ @scope = search_service.scope
+ @show_snippets = search_service.show_snippets?
+ @search_results = search_service.search_results
+ @search_objects = search_service.search_objects
check_single_commit_result
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index 1ee6c1d3afa..101fe579da2 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -76,5 +76,9 @@ module AuthHelper
(current_user.otp_grace_period_started_at + current_application_settings.two_factor_grace_period.hours) < Time.current
end
+ def unlink_allowed?(provider)
+ %w(saml cas3).exclude?(provider.to_s)
+ end
+
extend self
end
diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb
index eef403dba92..3b90fd1c2c7 100644
--- a/app/models/project_services/jira_service.rb
+++ b/app/models/project_services/jira_service.rb
@@ -62,7 +62,7 @@ class JiraService < IssueTrackerService
def help
"You need to configure JIRA before enabling this service. For more details
read the
- [JIRA service documentation](#{help_page_url('project_services/jira')})."
+ [JIRA service documentation](#{help_page_url('user/project/integrations/jira')})."
end
def title
diff --git a/app/models/user.rb b/app/models/user.rb
index cbd741f96ed..95a766f2ede 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -635,8 +635,10 @@ class User < ActiveRecord::Base
end
def fork_of(project)
- links = ForkedProjectLink.where(forked_from_project_id: project, forked_to_project_id: personal_projects)
-
+ links = ForkedProjectLink.where(
+ forked_from_project_id: project,
+ forked_to_project_id: personal_projects.unscope(:order)
+ )
if links.any?
links.first.forked_to_project
else
diff --git a/app/services/search/global_service.rb b/app/services/search/global_service.rb
index 781cd13b44b..a3c655493a5 100644
--- a/app/services/search/global_service.rb
+++ b/app/services/search/global_service.rb
@@ -16,5 +16,9 @@ module Search
Gitlab::SearchResults.new(current_user, projects, params[:search])
end
+
+ def scope
+ @scope ||= %w[issues merge_requests milestones].delete(params[:scope]) { 'projects' }
+ end
end
end
diff --git a/app/services/search/project_service.rb b/app/services/search/project_service.rb
index 4b500914cfb..9a22abae635 100644
--- a/app/services/search/project_service.rb
+++ b/app/services/search/project_service.rb
@@ -12,5 +12,9 @@ module Search
params[:search],
params[:repository_ref])
end
+
+ def scope
+ @scope ||= %w[notes issues merge_requests milestones wiki_blobs commits].delete(params[:scope]) { 'blobs' }
+ end
end
end
diff --git a/app/services/search/snippet_service.rb b/app/services/search/snippet_service.rb
index 0b3e713e220..4f161beea4d 100644
--- a/app/services/search/snippet_service.rb
+++ b/app/services/search/snippet_service.rb
@@ -11,5 +11,9 @@ module Search
Gitlab::SnippetSearchResults.new(snippets, params[:search])
end
+
+ def scope
+ @scope ||= %w[snippet_titles].delete(params[:scope]) { 'snippet_blobs' }
+ end
end
end
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
new file mode 100644
index 00000000000..8d46a8dab3e
--- /dev/null
+++ b/app/services/search_service.rb
@@ -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
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 43abd44d89f..23abf6897d4 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -35,6 +35,15 @@
%li
= link_to admin_root_path, title: 'Admin Area', aria: { label: "Admin Area" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('wrench fw')
+ - if current_user.can_create_project?
+ %li
+ = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = icon('plus fw')
+ - if Gitlab::Sherlock.enabled?
+ %li
+ = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = icon('tachometer fw')
%li
= link_to assigned_issues_dashboard_path, title: 'Issues', aria: { label: "Issues" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
= icon('hashtag fw')
@@ -50,15 +59,6 @@
= icon('check-circle fw')
%span.badge.todos-count
= todos_count_format(todos_pending_count)
- - if current_user.can_create_project?
- %li
- = link_to new_project_path, title: 'New project', aria: { label: "New project" }, data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
- = icon('plus fw')
- - if Gitlab::Sherlock.enabled?
- %li
- = link_to sherlock_transactions_path, title: 'Sherlock Transactions',
- data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
- = icon('tachometer fw')
%li.header-user.dropdown
= link_to current_user, class: "header-user-dropdown-toggle", data: { toggle: "dropdown" } do
= image_tag avatar_icon(current_user, 26), width: 26, height: 26, class: "header-user-avatar"
diff --git a/app/views/notify/project_was_exported_email.html.haml b/app/views/notify/project_was_exported_email.html.haml
index b28fea35ad5..76440926a2b 100644
--- a/app/views/notify/project_was_exported_email.html.haml
+++ b/app/views/notify/project_was_exported_email.html.haml
@@ -2,7 +2,7 @@
Project #{@project.name} was exported successfully.
%p
The project export can be downloaded from:
- = link_to download_export_namespace_project_url(@project.namespace, @project) do
+ = link_to download_export_namespace_project_url(@project.namespace, @project), rel: 'nofollow', download: '', do
= @project.name_with_namespace + " export"
%p
The download link will expire in 24 hours.
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 8a994f6d600..5ce2220c907 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -75,12 +75,12 @@
.provider-btn-image
= provider_image_tag(provider)
- if auth_active?(provider)
- - if provider.to_s == 'saml'
- %a.provider-btn
- Active
- - else
+ - if unlink_allowed?(provider)
= link_to unlink_profile_account_path(provider: provider), method: :delete, class: 'provider-btn' do
Disconnect
+ - else
+ %a.provider-btn
+ Active
- else
= link_to omniauth_authorize_path(:user, provider), method: :post, class: 'provider-btn not-active' do
Connect
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
index edf55d59f28..de8c173f26f 100644
--- a/app/views/projects/artifacts/browse.html.haml
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -3,7 +3,7 @@
.top-block.row-content-block.clearfix
.pull-right
= link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build),
- class: 'btn btn-default download' do
+ rel: 'nofollow', download: '', class: 'btn btn-default download' do
= icon('download')
Download artifacts archive
diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml
index b597c7f7a12..6f45d5b0689 100644
--- a/app/views/projects/builds/_sidebar.html.haml
+++ b/app/views/projects/builds/_sidebar.html.haml
@@ -33,7 +33,7 @@
= link_to keep_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default', method: :post do
Keep
- = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), class: 'btn btn-sm btn-default' do
+ = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build), rel: 'nofollow', download: '', class: 'btn btn-sm btn-default' do
Download
- if @build.artifacts_metadata?
diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml
index 09286a1b3c6..aeed293a724 100644
--- a/app/views/projects/ci/builds/_build.html.haml
+++ b/app/views/projects/ci/builds/_build.html.haml
@@ -94,7 +94,7 @@
%td
.pull-right
- if can?(current_user, :read_build, build) && build.artifacts?
- = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), title: 'Download artifacts', class: 'btn btn-build' do
+ = link_to download_namespace_project_build_artifacts_path(build.project.namespace, build.project, build), rel: 'nofollow', download: '', title: 'Download artifacts', class: 'btn btn-build' do
= icon('download')
- if can?(current_user, :update_build, build)
- if build.active?
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 82e0d0025ec..b78de092a60 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -163,7 +163,7 @@
- if @project.export_project_path
= link_to 'Download export', download_export_namespace_project_path(@project.namespace, @project),
- method: :get, class: "btn btn-default"
+ rel: 'nofollow', download: '', method: :get, class: "btn btn-default"
= link_to 'Generate new export', generate_new_export_namespace_project_path(@project.namespace, @project),
method: :post, class: "btn btn-default"
- else
diff --git a/changelogs/unreleased/25556-prevent-users-from-disconnecting-gitlab-account-from-cas.yml b/changelogs/unreleased/25556-prevent-users-from-disconnecting-gitlab-account-from-cas.yml
new file mode 100644
index 00000000000..17e38ba6243
--- /dev/null
+++ b/changelogs/unreleased/25556-prevent-users-from-disconnecting-gitlab-account-from-cas.yml
@@ -0,0 +1,4 @@
+---
+title: Prevent users from disconnecting GitLab account from CAS
+merge_request: 10282
+author:
diff --git a/changelogs/unreleased/29929-jira-doc.yml b/changelogs/unreleased/29929-jira-doc.yml
new file mode 100644
index 00000000000..f79dcd84634
--- /dev/null
+++ b/changelogs/unreleased/29929-jira-doc.yml
@@ -0,0 +1,4 @@
+---
+title: Fix link to Jira service documentation
+merge_request:
+author:
diff --git a/changelogs/unreleased/30276-move-top-badges.yml b/changelogs/unreleased/30276-move-top-badges.yml
new file mode 100644
index 00000000000..68a8ab359fd
--- /dev/null
+++ b/changelogs/unreleased/30276-move-top-badges.yml
@@ -0,0 +1,4 @@
+---
+title: Move issue, mr, todos next to profile dropdown in top nav
+merge_request:
+author:
diff --git a/changelogs/unreleased/forked-subquery-order.yml b/changelogs/unreleased/forked-subquery-order.yml
new file mode 100644
index 00000000000..06fb8236783
--- /dev/null
+++ b/changelogs/unreleased/forked-subquery-order.yml
@@ -0,0 +1,4 @@
+---
+title: Remove unnecessary ORDER BY clause from `forked_to_project_id` subquery
+merge_request:
+author: mhasbini
diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md
index f28896c2227..4b3c5bf6d64 100644
--- a/doc/update/8.2-to-8.3.md
+++ b/doc/update/8.2-to-8.3.md
@@ -120,6 +120,14 @@ There are new configuration options available for [`gitlab.yml`][yaml]. View the
git diff origin/8-2-stable:config/gitlab.yml.example origin/8-3-stable:config/gitlab.yml.example
```
+#### GitLab default file
+
+The value of the `gitlab_workhorse_options` variable should be updated within the default gitlab file (`/etc/default/gitlab`) according to the following diff:
+
+```sh
+git diff origin/8-2-stable:lib/support/init.d/gitlab.default.example origin/8-3-stable:lib/support/init.d/gitlab.default.example
+```
+
#### Nginx configuration
GitLab 8.3 introduces major changes in the NGINX configuration.
diff --git a/spec/controllers/profiles/accounts_controller_spec.rb b/spec/controllers/profiles/accounts_controller_spec.rb
index 18148acde3e..2f9d18e3a0e 100644
--- a/spec/controllers/profiles/accounts_controller_spec.rb
+++ b/spec/controllers/profiles/accounts_controller_spec.rb
@@ -1,25 +1,47 @@
require 'spec_helper'
describe Profiles::AccountsController do
- let(:user) { create(:omniauth_user, provider: 'saml') }
+ describe 'DELETE unlink' do
+ let(:user) { create(:omniauth_user) }
- before do
- sign_in(user)
- end
+ before do
+ sign_in(user)
+ end
- it 'does not allow to unlink SAML connected account' do
- identity = user.identities.last
- delete :unlink, provider: 'saml'
- updated_user = User.find(user.id)
+ it 'renders 404 if someone tries to unlink a non existent provider' do
+ delete :unlink, provider: 'github'
- expect(response).to have_http_status(302)
- expect(updated_user.identities.size).to eq(1)
- expect(updated_user.identities).to include(identity)
- end
+ expect(response).to have_http_status(404)
+ end
+
+ [:saml, :cas3].each do |provider|
+ describe "#{provider} provider" do
+ let(:user) { create(:omniauth_user, provider: provider.to_s) }
+
+ it "does not allow to unlink connected account" do
+ identity = user.identities.last
+
+ delete :unlink, provider: provider.to_s
+
+ expect(response).to have_http_status(302)
+ expect(user.reload.identities).to include(identity)
+ end
+ end
+ end
+
+ [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider|
+ describe "#{provider} provider" do
+ let(:user) { create(:omniauth_user, provider: provider.to_s) }
+
+ it 'allows to unlink connected account' do
+ identity = user.identities.last
- it 'does allow to delete other linked accounts' do
- user.identities.create(provider: 'twitter', extern_uid: 'twitter_123')
+ delete :unlink, provider: provider.to_s
- expect { delete :unlink, provider: 'twitter' }.to change(Identity.all, :size).by(-1)
+ expect(response).to have_http_status(302)
+ expect(user.reload.identities).not_to include(identity)
+ end
+ end
+ end
end
end
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index cd3281d6f51..a0e1265efff 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -62,4 +62,18 @@ describe AuthHelper do
end
end
end
+
+ describe 'unlink_allowed?' do
+ [:saml, :cas3].each do |provider|
+ it "returns true if the provider is #{provider}" do
+ expect(helper.unlink_allowed?(provider)).to be false
+ end
+ end
+
+ [:twitter, :facebook, :google_oauth2, :gitlab, :github, :bitbucket, :crowd, :auth0].each do |provider|
+ it "returns false if the provider is #{provider}" do
+ expect(helper.unlink_allowed?(provider)).to be true
+ end
+ end
+ end
end
diff --git a/spec/services/search/global_service_spec.rb b/spec/services/search/global_service_spec.rb
new file mode 100644
index 00000000000..2531607acad
--- /dev/null
+++ b/spec/services/search/global_service_spec.rb
@@ -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
diff --git a/spec/services/search_service_spec.rb b/spec/services/search_service_spec.rb
index 6ef5fa008aa..2112f1cf9ea 100644
--- a/spec/services/search_service_spec.rb
+++ b/spec/services/search_service_spec.rb
@@ -1,65 +1,286 @@
require 'spec_helper'
-describe 'Search::GlobalService', services: true do
+describe SearchService, services: true do
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!(: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') }
+ let(:accessible_group) { create(:group, :private) }
+ let(:inaccessible_group) { create(:group, :private) }
+ let!(:group_member) { create(:group_member, group: accessible_group, user: user) }
+
+ 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
- found_project.team << [user, :master]
+ accessible_project.add_master(user)
+ end
+
+ describe '#project' do
+ context 'when the project is accessible' do
+ it 'returns the project' do
+ project = SearchService.new(user, project_id: accessible_project.id).project
+
+ expect(project).to eq accessible_project
+ end
+ end
+
+ context 'when the project is not accessible' do
+ it 'returns nil' do
+ project = SearchService.new(user, project_id: inaccessible_project.id).project
+
+ expect(project).to be_nil
+ end
+ end
+
+ context 'when there is no project_id' do
+ it 'returns nil' do
+ project = SearchService.new(user).project
+
+ expect(project).to be_nil
+ end
+ end
end
- describe '#execute' do
- context 'unauthenticated' do
- it 'returns public projects only' do
- context = Search::GlobalService.new(nil, search: "searchable")
- results = context.execute
- expect(results.objects('projects')).to match_array [public_project]
+ describe '#group' do
+ context 'when the group is accessible' do
+ it 'returns the group' do
+ group = SearchService.new(user, group_id: accessible_group.id).group
+
+ expect(group).to eq accessible_group
end
end
- context 'authenticated' do
- it 'returns public, internal and private projects' do
- context = Search::GlobalService.new(user, search: "searchable")
- results = context.execute
- expect(results.objects('projects')).to match_array [public_project, found_project, internal_project]
+ context 'when the group is not accessible' do
+ it 'returns nil' do
+ group = SearchService.new(user, group_id: inaccessible_group.id).group
+
+ expect(group).to be_nil
end
+ end
+
+ context 'when there is no group_id' do
+ it 'returns nil' do
+ group = SearchService.new(user).group
- it 'returns only public & internal projects' do
- context = Search::GlobalService.new(internal_user, search: "searchable")
- results = context.execute
- expect(results.objects('projects')).to match_array [internal_project, public_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?
- it 'namespace name is searchable' do
- context = Search::GlobalService.new(user, search: found_project.namespace.path)
- results = context.execute
- expect(results.objects('projects')).to match_array [found_project]
+ expect(show_snippets).to be_truthy
end
+ end
- context 'nested group' do
- let!(:nested_group) { create(:group, :nested) }
- let!(:project) { create(:empty_project, namespace: nested_group) }
+ 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
- before { project.add_master(user) }
+ context 'when :snippets is missing' do
+ it 'returns false' do
+ show_snippets = SearchService.new(user).show_snippets?
- it 'returns result from nested group' do
- context = Search::GlobalService.new(user, search: project.path)
- results = context.execute
- expect(results.objects('projects')).to match_array [project]
+ 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
+
+ context 'and disallowed scope' do
+ it 'returns the default scope' do
+ scope = SearchService.new(user, project_id: accessible_project.id, scope: 'projects').scope
- it 'returns result from descendants when search inside group' do
- context = Search::GlobalService.new(user, search: project.path, group_id: nested_group.parent)
- results = context.execute
- expect(results.objects('projects')).to match_array [project]
+ expect(scope).to eq 'blobs'
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