summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.rubocop.yml2
-rw-r--r--CHANGELOG4
-rw-r--r--Gemfile3
-rw-r--r--Gemfile.lock3
-rw-r--r--app/controllers/admin/application_settings_controller.rb15
-rw-r--r--app/controllers/admin/health_check_controller.rb5
-rw-r--r--app/controllers/health_check_controller.rb22
-rw-r--r--app/helpers/application_settings_helper.rb14
-rw-r--r--app/helpers/auth_helper.rb10
-rw-r--r--app/models/application_setting.rb18
-rw-r--r--app/models/ci/build.rb2
-rw-r--r--app/models/concerns/subscribable.rb6
-rw-r--r--app/models/merge_request.rb2
-rw-r--r--app/views/admin/application_settings/_form.html.haml7
-rw-r--r--app/views/admin/builds/index.html.haml2
-rw-r--r--app/views/admin/health_check/show.html.haml49
-rw-r--r--app/views/devise/sessions/new.html.haml2
-rw-r--r--app/views/devise/shared/_omniauth_box.html.haml2
-rw-r--r--app/views/layouts/nav/_admin.html.haml5
-rw-r--r--app/views/projects/builds/index.html.haml2
-rw-r--r--config/boot.rb2
-rw-r--r--config/initializers/carrierwave.rb2
-rw-r--r--config/initializers/health_check.rb3
-rw-r--r--config/routes.rb5
-rw-r--r--db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb5
-rw-r--r--db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb5
-rw-r--r--db/schema.rb4
-rw-r--r--doc/api/labels.md70
-rw-r--r--doc/integration/img/enabled-oauth-sign-in-sources.pngbin0 -> 49081 bytes
-rw-r--r--doc/integration/omniauth.md15
-rw-r--r--lib/api/api.rb1
-rw-r--r--lib/api/entities.rb4
-rw-r--r--lib/api/helpers.rb11
-rw-r--r--lib/api/issues.rb39
-rw-r--r--lib/api/labels.rb6
-rw-r--r--lib/api/merge_requests.rb36
-rw-r--r--lib/api/subscriptions.rb60
-rw-r--r--lib/gitlab/backend/shell.rb2
-rw-r--r--lib/gitlab/redis.rb2
-rw-r--r--spec/config/mail_room_spec.rb2
-rw-r--r--spec/controllers/health_check_controller_spec.rb105
-rw-r--r--spec/features/admin/admin_builds_spec.rb1
-rw-r--r--spec/features/admin/admin_health_check_spec.rb55
-rw-r--r--spec/features/builds_spec.rb1
-rw-r--r--spec/helpers/auth_helper_spec.rb47
-rw-r--r--spec/helpers/events_helper_spec.rb95
-rw-r--r--spec/models/application_setting_spec.rb9
-rw-r--r--spec/models/concerns/subscribable_spec.rb10
-rw-r--r--spec/requests/api/issues_spec.rb12
-rw-r--r--spec/requests/api/labels_spec.rb82
-rw-r--r--spec/routing/admin_routing_spec.rb7
-rw-r--r--spec/routing/routing_spec.rb10
-rw-r--r--spec/services/projects/create_service_spec.rb4
-rw-r--r--spec/services/projects/destroy_service_spec.rb8
54 files changed, 750 insertions, 145 deletions
diff --git a/.rubocop.yml b/.rubocop.yml
index ccceea45963..3fb2f2df592 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -770,7 +770,7 @@ Lint/DefEndAlignment:
# Check for deprecated class method calls.
Lint/DeprecatedClassMethods:
- Enabled: false
+ Enabled: true
# Check for duplicate method definitions.
Lint/DuplicateMethods:
diff --git a/CHANGELOG b/CHANGELOG
index 777b211daab..588a4b4eef6 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -14,6 +14,7 @@ v 8.8.0 (unreleased)
- Reduce delay in destroying a project from 1-minute to immediately
- Make build status canceled if any of the jobs was canceled and none failed
- Upgrade Sidekiq to 4.1.2
+ - Added /health_check endpoint for checking service status
- Sanitize repo paths in new project error message
- Bump mail_room to 0.7.0 to fix stuck IDLE connections
- Remove future dates from contribution calendar graph.
@@ -46,11 +47,14 @@ v 8.8.0 (unreleased)
- Fix adding a todo for private group members (Ahmad Sherif)
- Bump ace-rails-ap gem version from 2.0.1 to 4.0.2 which upgrades Ace Editor from 1.1.2 to 1.2.3
- Total method execution timings are no longer tracked
+ - Allow Admins to remove the Login with buttons for OAuth services and still be able to import !4034. (Andrei Gliga)
+ - Add API endpoints for un/subscribing from/to a label. !4051 (Ahmad Sherif)
v 8.7.5
- Fix relative links in wiki pages. !4050
- Fix always showing build notification message when switching between merge requests !4086
- Fix an issue when filtering merge requests with more than one label. !3886
+ - Fix short note for the default scope on build page (Takuya Noguchi)
v 8.7.4
- Links for Redmine issue references are generated correctly again !4048 (Benedikt Huss)
diff --git a/Gemfile b/Gemfile
index 2854bf8a57b..96d9decb261 100644
--- a/Gemfile
+++ b/Gemfile
@@ -332,3 +332,6 @@ gem 'oauth2', '~> 1.0.0'
# Soft deletion
gem "paranoia", "~> 2.0"
+
+# Health check
+gem 'health_check', '~> 1.5.1'
diff --git a/Gemfile.lock b/Gemfile.lock
index bc47533e5bb..ce1da92e517 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -402,6 +402,8 @@ GEM
html2haml (>= 1.0.1)
railties (>= 4.0.1)
hashie (3.4.3)
+ health_check (1.5.1)
+ rails (>= 2.3.0)
highline (1.7.8)
hipchat (1.5.2)
httparty
@@ -945,6 +947,7 @@ DEPENDENCIES
grape (~> 0.13.0)
grape-entity (~> 0.4.2)
haml-rails (~> 0.9.0)
+ health_check (~> 1.5.1)
hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0)
httparty (~> 0.13.3)
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index ec22548ddeb..8c973f0e4a8 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -19,6 +19,12 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
redirect_to admin_runners_path
end
+ def reset_health_check_token
+ @application_setting.reset_health_check_access_token!
+ flash[:notice] = 'New health check access token has been generated!'
+ redirect_to :back
+ end
+
def clear_repository_check_states
RepositoryCheck::ClearWorker.perform_async
@@ -53,6 +59,12 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
end
+ enabled_oauth_sign_in_sources = params[:application_setting].delete(:enabled_oauth_sign_in_sources)
+
+ params[:application_setting][:disabled_oauth_sign_in_sources] =
+ AuthHelper.button_based_providers.map(&:to_s) -
+ Array(enabled_oauth_sign_in_sources)
+
params.require(:application_setting).permit(
:default_projects_limit,
:default_branch_protection,
@@ -95,7 +107,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:repository_checks_enabled,
:metrics_packet_size,
restricted_visibility_levels: [],
- import_sources: []
+ import_sources: [],
+ disabled_oauth_sign_in_sources: []
)
end
end
diff --git a/app/controllers/admin/health_check_controller.rb b/app/controllers/admin/health_check_controller.rb
new file mode 100644
index 00000000000..241c7be0ea1
--- /dev/null
+++ b/app/controllers/admin/health_check_controller.rb
@@ -0,0 +1,5 @@
+class Admin::HealthCheckController < Admin::ApplicationController
+ def show
+ @errors = HealthCheck::Utils.process_checks('standard')
+ end
+end
diff --git a/app/controllers/health_check_controller.rb b/app/controllers/health_check_controller.rb
new file mode 100644
index 00000000000..037da7d2bce
--- /dev/null
+++ b/app/controllers/health_check_controller.rb
@@ -0,0 +1,22 @@
+class HealthCheckController < HealthCheck::HealthCheckController
+ before_action :validate_health_check_access!
+
+ private
+
+ def validate_health_check_access!
+ render_404 unless token_valid?
+ end
+
+ def token_valid?
+ token = params[:token].presence || request.headers['TOKEN']
+ token.present? &&
+ ActiveSupport::SecurityUtils.variable_size_secure_compare(
+ token,
+ current_application_settings.health_check_access_token
+ )
+ end
+
+ def render_404
+ render file: Rails.root.join('public', '404'), layout: false, status: '404'
+ end
+end
diff --git a/app/helpers/application_settings_helper.rb b/app/helpers/application_settings_helper.rb
index 914b0ef6042..03080d25931 100644
--- a/app/helpers/application_settings_helper.rb
+++ b/app/helpers/application_settings_helper.rb
@@ -60,4 +60,18 @@ module ApplicationSettingsHelper
end
end
end
+
+ def oauth_providers_checkboxes
+ button_based_providers.map do |source|
+ disabled = current_application_settings.disabled_oauth_sign_in_sources.include?(source.to_s)
+ css_class = 'btn'
+ css_class << ' active' unless disabled
+ checkbox_name = 'application_setting[enabled_oauth_sign_in_sources][]'
+
+ label_tag(checkbox_name, class: css_class) do
+ check_box_tag(checkbox_name, source, !disabled,
+ autocomplete: 'off') + Gitlab::OAuth::Provider.label_for(source)
+ end
+ end
+ end
end
diff --git a/app/helpers/auth_helper.rb b/app/helpers/auth_helper.rb
index b4f80fd9b3e..b05fa0a14d6 100644
--- a/app/helpers/auth_helper.rb
+++ b/app/helpers/auth_helper.rb
@@ -38,6 +38,16 @@ module AuthHelper
auth_providers.reject { |provider| form_based_provider?(provider) }
end
+ def enabled_button_based_providers
+ disabled_providers = current_application_settings.disabled_oauth_sign_in_sources || []
+
+ button_based_providers.map(&:to_s) - disabled_providers
+ end
+
+ def button_based_providers_enabled?
+ enabled_button_based_providers.any?
+ end
+
def provider_image_tag(provider, size = 64)
label = label_for_provider(provider)
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 7039db2d41e..1a10768655f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -1,11 +1,13 @@
class ApplicationSetting < ActiveRecord::Base
include TokenAuthenticatable
add_authentication_token_field :runners_registration_token
+ add_authentication_token_field :health_check_access_token
CACHE_KEY = 'application_setting.last'
serialize :restricted_visibility_levels
serialize :import_sources
+ serialize :disabled_oauth_sign_in_sources
serialize :restricted_signup_domains, Array
attr_accessor :restricted_signup_domains_raw
@@ -69,7 +71,18 @@ class ApplicationSetting < ActiveRecord::Base
end
end
+ validates_each :disabled_oauth_sign_in_sources do |record, attr, value|
+ unless value.nil?
+ value.each do |source|
+ unless Devise.omniauth_providers.include?(source.to_sym)
+ record.errors.add(attr, "'#{source}' is not an OAuth sign-in source")
+ end
+ end
+ end
+ end
+
before_save :ensure_runners_registration_token
+ before_save :ensure_health_check_access_token
after_commit do
Rails.cache.write(CACHE_KEY, self)
@@ -107,6 +120,7 @@ class ApplicationSetting < ActiveRecord::Base
recaptcha_enabled: false,
akismet_enabled: false,
repository_checks_enabled: true,
+ disabled_oauth_sign_in_sources: []
)
end
@@ -133,4 +147,8 @@ class ApplicationSetting < ActiveRecord::Base
def runners_registration_token
ensure_runners_registration_token!
end
+
+ def health_check_access_token
+ ensure_health_check_access_token!
+ end
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 4a3f92ad34a..92327bdb08d 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -205,7 +205,7 @@ module Ci
end
def recreate_trace_dir
- unless Dir.exists?(dir_to_trace)
+ unless Dir.exist?(dir_to_trace)
FileUtils.mkdir_p(dir_to_trace)
end
end
diff --git a/app/models/concerns/subscribable.rb b/app/models/concerns/subscribable.rb
index d5a881b2445..083257f1005 100644
--- a/app/models/concerns/subscribable.rb
+++ b/app/models/concerns/subscribable.rb
@@ -36,6 +36,12 @@ module Subscribable
update(subscribed: !subscribed?(user))
end
+ def subscribe(user)
+ subscriptions.
+ find_or_initialize_by(user_id: user.id).
+ update(subscribed: true)
+ end
+
def unsubscribe(user)
subscriptions.
find_or_initialize_by(user_id: user.id).
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 5460935366c..5c5e6007aa0 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -514,7 +514,7 @@ class MergeRequest < ActiveRecord::Base
end
def ref_is_fetched?
- File.exists?(File.join(project.repository.path_to_repo, ref_path))
+ File.exist?(File.join(project.repository.path_to_repo, ref_path))
end
def ensure_ref_fetched
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index e0d8d16a954..f7c799c968f 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -109,6 +109,13 @@
= f.label :signin_enabled do
= f.check_box :signin_enabled
Sign-in enabled
+ - if omniauth_enabled? && button_based_providers.any?
+ .form-group
+ = f.label :enabled_oauth_sign_in_sources, 'Enabled OAuth Sign-In sources', class: 'control-label col-sm-2'
+ .col-sm-10
+ .btn-group{ data: { toggle: 'buttons' } }
+ - oauth_providers_checkboxes.each do |source|
+ = source
.form-group
= f.label :two_factor_authentication, 'Two-factor authentication', class: 'control-label col-sm-2'
.col-sm-10
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index 804d7851bdb..ed24757087b 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -20,7 +20,7 @@
= link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
.row-content-block.second-block
- #{(@scope || 'running').capitalize} builds
+ #{(@scope || 'all').capitalize} builds
%ul.content-list
- if @builds.blank?
diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml
new file mode 100644
index 00000000000..c2313986a7f
--- /dev/null
+++ b/app/views/admin/health_check/show.html.haml
@@ -0,0 +1,49 @@
+- page_title "Health Check"
+
+%h3.page-title
+ Health Check
+.bs-callout.clearfix
+ .pull-left
+ %p
+ Access token is
+ %code#health-check-token= current_application_settings.health_check_access_token
+ = button_to reset_health_check_token_admin_application_settings_path,
+ method: :put, class: 'btn btn-default',
+ data: { confirm: 'Are you sure you want to reset the health check token?' } do
+ = icon('refresh')
+ Reset health check access token
+%p.light
+ Health information can be retrieved as plain text, JSON, or XML using:
+ %ul
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, format: :json)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, format: :xml)
+
+%p.light
+ You can also ask for the status of specific services:
+ %ul
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :cache)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :database)
+ %li
+ %code= health_check_url(token: current_application_settings.health_check_access_token, checks: :migrations)
+
+%hr
+.panel.panel-default
+ .panel-heading
+ Current Status:
+ - if @errors.blank?
+ = icon('circle', class: 'cgreen')
+ Healthy
+ - else
+ = icon('warning', class: 'cred')
+ Unhealthy
+ .panel-body
+ - if @errors.blank?
+ No Health Problems Detected
+ - else
+ = @errors
diff --git a/app/views/devise/sessions/new.html.haml b/app/views/devise/sessions/new.html.haml
index d65fa60025c..28194506acc 100644
--- a/app/views/devise/sessions/new.html.haml
+++ b/app/views/devise/sessions/new.html.haml
@@ -4,7 +4,7 @@
= render 'devise/shared/signin_box'
-# Omniauth fits between signin/ldap signin and signup and does not have a surrounding box
- - if omniauth_enabled? && devise_mapping.omniauthable?
+ - if omniauth_enabled? && devise_mapping.omniauthable? && button_based_providers_enabled?
.clearfix.prepend-top-20
= render 'devise/shared/omniauth_box'
diff --git a/app/views/devise/shared/_omniauth_box.html.haml b/app/views/devise/shared/_omniauth_box.html.haml
index ecf680e7b23..de18bc2d844 100644
--- a/app/views/devise/shared/_omniauth_box.html.haml
+++ b/app/views/devise/shared/_omniauth_box.html.haml
@@ -1,7 +1,7 @@
%p
%span.light
Sign in with &nbsp;
- - providers = button_based_providers
+ - providers = enabled_button_based_providers
- providers.each do |provider|
%span.light
- has_icon = provider_has_icon?(provider)
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index 280a1b93729..f292730fe45 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -41,6 +41,11 @@
= icon('file-text fw')
%span
Logs
+ = nav_link(controller: :health_check) do
+ = link_to admin_health_check_path, title: 'Health Check' do
+ = icon('medkit fw')
+ %span
+ Health Check
= nav_link(controller: :broadcast_messages) do
= link_to admin_broadcast_messages_path, title: 'Messages' do
= icon('bullhorn fw')
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 2e8015d119b..98f4a9416e5 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -36,7 +36,7 @@
%span CI Lint
.row-content-block
- #{(@scope || 'running').capitalize} builds from this project
+ #{(@scope || 'all').capitalize} builds from this project
%ul.content-list
- if @builds.blank?
diff --git a/config/boot.rb b/config/boot.rb
index 4489e58688c..f2830ae3166 100644
--- a/config/boot.rb
+++ b/config/boot.rb
@@ -3,4 +3,4 @@ require 'rubygems'
# Set up gems listed in the Gemfile.
ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__)
-require 'bundler/setup' if File.exists?(ENV['BUNDLE_GEMFILE'])
+require 'bundler/setup' if File.exist?(ENV['BUNDLE_GEMFILE'])
diff --git a/config/initializers/carrierwave.rb b/config/initializers/carrierwave.rb
index df28d30d750..0d278717d6e 100644
--- a/config/initializers/carrierwave.rb
+++ b/config/initializers/carrierwave.rb
@@ -2,7 +2,7 @@ CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/
aws_file = Rails.root.join('config', 'aws.yml')
-if File.exists?(aws_file)
+if File.exist?(aws_file)
AWS_CONFIG = YAML.load(File.read(aws_file))[Rails.env]
CarrierWave.configure do |config|
diff --git a/config/initializers/health_check.rb b/config/initializers/health_check.rb
new file mode 100644
index 00000000000..79e2d23ab2e
--- /dev/null
+++ b/config/initializers/health_check.rb
@@ -0,0 +1,3 @@
+HealthCheck.setup do |config|
+ config.standard_checks = ['database', 'migrations', 'cache']
+end
diff --git a/config/routes.rb b/config/routes.rb
index dd946305181..ec876eba345 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -73,6 +73,9 @@ Rails.application.routes.draw do
mount Sidekiq::Web, at: '/admin/sidekiq', as: :sidekiq
end
+ # Health check
+ get 'health_check(/:checks)' => 'health_check#index', as: :health_check
+
# Enable Grack support
mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
@@ -254,6 +257,7 @@ Rails.application.routes.draw do
end
resource :logs, only: [:show]
+ resource :health_check, controller: 'health_check', only: [:show]
resource :background_jobs, controller: 'background_jobs', only: [:show]
resources :namespaces, path: '/projects', constraints: { id: /[a-zA-Z.0-9_\-]+/ }, only: [] do
@@ -285,6 +289,7 @@ Rails.application.routes.draw do
resource :application_settings, only: [:show, :update] do
resources :services
put :reset_runners_token
+ put :reset_health_check_token
put :clear_repository_check_states
end
diff --git a/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
new file mode 100644
index 00000000000..facd33875ba
--- /dev/null
+++ b/db/migrate/20160504091942_add_disabled_oauth_sign_in_sources_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddDisabledOauthSignInSourcesToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :disabled_oauth_sign_in_sources, :text
+ end
+end
diff --git a/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
new file mode 100644
index 00000000000..9d729fec189
--- /dev/null
+++ b/db/migrate/20160509201028_add_health_check_access_token_to_application_settings.rb
@@ -0,0 +1,5 @@
+class AddHealthCheckAccessTokenToApplicationSettings < ActiveRecord::Migration
+ def change
+ add_column :application_settings, :health_check_access_token, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 71d953afe30..9b5aa640cb0 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20160508194200) do
+ActiveRecord::Schema.define(version: 20160509201028) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@@ -80,6 +80,8 @@ ActiveRecord::Schema.define(version: 20160508194200) do
t.boolean "repository_checks_enabled", default: false
t.text "shared_runners_text"
t.integer "metrics_packet_size", default: 1
+ t.text "disabled_oauth_sign_in_sources"
+ t.string "health_check_access_token"
end
create_table "audit_events", force: :cascade do |t|
diff --git a/doc/api/labels.md b/doc/api/labels.md
index 3730c07c5a7..b857d81768e 100644
--- a/doc/api/labels.md
+++ b/doc/api/labels.md
@@ -165,3 +165,73 @@ Example response:
"description": "Documentation"
}
```
+
+## Subscribe to a label
+
+Subscribes the authenticated user to a label to receive notifications. If the
+operation is successful, status code `201` together with the updated label is
+returned. If the user is already subscribed to the label, the status code `304`
+is returned. If the project or label is not found, status code `404` is
+returned.
+
+```
+POST /projects/:id/labels/:label_id/subscription
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ----------------- | -------- | ------------------------------------ |
+| `id` | integer | yes | The ID of a project |
+| `label_id` | integer or string | yes | The ID or title of a project's label |
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscription
+```
+
+Example response:
+
+```json
+{
+ "name": "Docs",
+ "color": "#cc0033",
+ "description": "",
+ "open_issues_count": 0,
+ "closed_issues_count": 0,
+ "open_merge_requests_count": 0,
+ "subscribed": true
+}
+```
+
+## Unsubscribe from a label
+
+Unsubscribes the authenticated user from a label to not receive notifications
+from it. If the operation is successful, status code `200` together with the
+updated label is returned. If the user is not subscribed to the label, the
+status code `304` is returned. If the project or label is not found, status code
+`404` is returned.
+
+```
+DELETE /projects/:id/labels/:label_id/subscription
+```
+
+| Attribute | Type | Required | Description |
+| ---------- | ----------------- | -------- | ------------------------------------ |
+| `id` | integer | yes | The ID of a project |
+| `label_id` | integer or string | yes | The ID or title of a project's label |
+
+```bash
+curl -X DELETE -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/5/labels/1/subscription
+```
+
+Example response:
+
+```json
+{
+ "name": "Docs",
+ "color": "#cc0033",
+ "description": "",
+ "open_issues_count": 0,
+ "closed_issues_count": 0,
+ "open_merge_requests_count": 0,
+ "subscribed": false
+}
+```
diff --git a/doc/integration/img/enabled-oauth-sign-in-sources.png b/doc/integration/img/enabled-oauth-sign-in-sources.png
new file mode 100644
index 00000000000..95f8bbdcd24
--- /dev/null
+++ b/doc/integration/img/enabled-oauth-sign-in-sources.png
Binary files differ
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index cab329c0dec..820f40f81a9 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -11,6 +11,7 @@ of the configured mechanisms.
- [Supported Providers](#supported-providers)
- [Enable OmniAuth for an Existing User](#enable-omniauth-for-an-existing-user)
- [OmniAuth configuration sample when using Omnibus GitLab](https://gitlab.com/gitlab-org/omnibus-gitlab/tree/master#omniauth-google-twitter-github-login)
+- [Enable or disable Sign In with an OmniAuth provider without disabling import sources](#enable-or-disable-sign-in-with-an-omniauth-provider-without-disabling-import-sources)
## Supported Providers
@@ -191,3 +192,17 @@ experience [in the public Wiki](https://github.com/gitlabhq/gitlab-public-wiki/w
While we can't officially support every possible authentication mechanism out there,
we'd like to at least help those with specific needs.
+
+## Enable or disable Sign In with an OmniAuth provider without disabling import sources
+
+>**Note:**
+This setting was introduced with version 8.8 of GitLab.
+
+Administrators are able to enable or disable Sign In via some OmniAuth providers.
+
+>**Note:**
+By default Sign In is enabled via all the OAuth Providers that have been configured in `config/gitlab.yml`.
+
+In order to enable/disable an OmniAuth provider, go to Admin Area -> Settings -> Sign-in Restrictions section -> Enabled OAuth Sign-In sources and select the providers you want to enable or disable.
+
+![Enabled OAuth Sign-In sources](img/enabled-oauth-sign-in-sources.png)
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 5fd9c30cb42..360fb41a721 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -57,5 +57,6 @@ module API
mount ::API::Variables
mount ::API::Runners
mount ::API::Licenses
+ mount ::API::Subscriptions
end
end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 2870a6a40ef..406f5ea9139 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -307,6 +307,10 @@ module API
class Label < Grape::Entity
expose :name, :color, :description
expose :open_issues_count, :closed_issues_count, :open_merge_requests_count
+
+ expose :subscribed do |label, options|
+ label.subscribed?(options[:current_user])
+ end
end
class Compare < Grape::Entity
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index 40c967453fb..5e638dbe16a 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -95,6 +95,17 @@ module API
end
end
+ def find_project_label(id)
+ label = user_project.labels.find_by_id(id) || user_project.labels.find_by_title(id)
+ label || not_found!('Label')
+ end
+
+ def find_project_issue(id)
+ issue = user_project.issues.find(id)
+ not_found! unless can?(current_user, :read_issue, issue)
+ issue
+ end
+
def paginate(relation)
relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
add_pagination_headers(data)
diff --git a/lib/api/issues.rb b/lib/api/issues.rb
index 40928749481..f59a4d6c012 100644
--- a/lib/api/issues.rb
+++ b/lib/api/issues.rb
@@ -103,8 +103,7 @@ module API
# Example Request:
# GET /projects/:id/issues/:issue_id
get ":id/issues/:issue_id" do
- @issue = user_project.issues.find(params[:issue_id])
- not_found! unless can?(current_user, :read_issue, @issue)
+ @issue = find_project_issue(params[:issue_id])
present @issue, with: Entities::Issue, current_user: current_user
end
@@ -234,42 +233,6 @@ module API
authorize!(:destroy_issue, issue)
issue.destroy
end
-
- # Subscribes to a project issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # Example Request:
- # POST /projects/:id/issues/:issue_id/subscription
- post ':id/issues/:issue_id/subscription' do
- issue = user_project.issues.find(params[:issue_id])
-
- if issue.subscribed?(current_user)
- not_modified!
- else
- issue.toggle_subscription(current_user)
- present issue, with: Entities::Issue, current_user: current_user
- end
- end
-
- # Unsubscribes from a project issue
- #
- # Parameters:
- # id (required) - The ID of a project
- # issue_id (required) - The ID of a project issue
- # Example Request:
- # DELETE /projects/:id/issues/:issue_id/subscription
- delete ':id/issues/:issue_id/subscription' do
- issue = user_project.issues.find(params[:issue_id])
-
- if issue.subscribed?(current_user)
- issue.unsubscribe(current_user)
- present issue, with: Entities::Issue, current_user: current_user
- else
- not_modified!
- end
- end
end
end
end
diff --git a/lib/api/labels.rb b/lib/api/labels.rb
index 4af6bef0fa7..c806829d69e 100644
--- a/lib/api/labels.rb
+++ b/lib/api/labels.rb
@@ -11,7 +11,7 @@ module API
# Example Request:
# GET /projects/:id/labels
get ':id/labels' do
- present user_project.labels, with: Entities::Label
+ present user_project.labels, with: Entities::Label, current_user: current_user
end
# Creates a new label
@@ -36,7 +36,7 @@ module API
label = user_project.labels.create(attrs)
if label.valid?
- present label, with: Entities::Label
+ present label, with: Entities::Label, current_user: current_user
else
render_validation_error!(label)
end
@@ -90,7 +90,7 @@ module API
attrs[:name] = attrs.delete(:new_name) if attrs.key?(:new_name)
if label.update(attrs)
- present label, with: Entities::Label
+ present label, with: Entities::Label, current_user: current_user
else
render_validation_error!(label)
end
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 7e78609ecb9..4e7de8867b4 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -327,42 +327,6 @@ module API
issues = ::Kaminari.paginate_array(merge_request.closes_issues(current_user))
present paginate(issues), with: Entities::Issue, current_user: current_user
end
-
- # Subscribes to a merge request
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of a merge request
- # Example Request:
- # POST /projects/:id/issues/:merge_request_id/subscription
- post "#{path}/subscription" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
-
- if merge_request.subscribed?(current_user)
- not_modified!
- else
- merge_request.toggle_subscription(current_user)
- present merge_request, with: Entities::MergeRequest, current_user: current_user
- end
- end
-
- # Unsubscribes from a merge request
- #
- # Parameters:
- # id (required) - The ID of a project
- # merge_request_id (required) - The ID of a merge request
- # Example Request:
- # DELETE /projects/:id/merge_requests/:merge_request_id/subscription
- delete "#{path}/subscription" do
- merge_request = user_project.merge_requests.find(params[:merge_request_id])
-
- if merge_request.subscribed?(current_user)
- merge_request.unsubscribe(current_user)
- present merge_request, with: Entities::MergeRequest, current_user: current_user
- else
- not_modified!
- end
- end
end
end
end
diff --git a/lib/api/subscriptions.rb b/lib/api/subscriptions.rb
new file mode 100644
index 00000000000..c49e2a21b82
--- /dev/null
+++ b/lib/api/subscriptions.rb
@@ -0,0 +1,60 @@
+module API
+ class Subscriptions < Grape::API
+ before { authenticate! }
+
+ subscribable_types = {
+ 'merge_request' => proc { |id| user_project.merge_requests.find(id) },
+ 'merge_requests' => proc { |id| user_project.merge_requests.find(id) },
+ 'issues' => proc { |id| find_project_issue(id) },
+ 'labels' => proc { |id| find_project_label(id) },
+ }
+
+ resource :projects do
+ subscribable_types.each do |type, finder|
+ type_singularized = type.singularize
+ type_id_str = :"#{type_singularized}_id"
+ entity_class = Entities.const_get(type_singularized.camelcase)
+
+ # Subscribe to a resource
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # subscribable_id (required) - The ID of a resource
+ # Example Request:
+ # POST /projects/:id/labels/:subscribable_id/subscription
+ # POST /projects/:id/issues/:subscribable_id/subscription
+ # POST /projects/:id/merge_requests/:subscribable_id/subscription
+ post ":id/#{type}/:#{type_id_str}/subscription" do
+ resource = instance_exec(params[type_id_str], &finder)
+
+ if resource.subscribed?(current_user)
+ not_modified!
+ else
+ resource.subscribe(current_user)
+ present resource, with: entity_class, current_user: current_user
+ end
+ end
+
+ # Unsubscribe from a resource
+ #
+ # Parameters:
+ # id (required) - The ID of a project
+ # subscribable_id (required) - The ID of a resource
+ # Example Request:
+ # DELETE /projects/:id/labels/:subscribable_id/subscription
+ # DELETE /projects/:id/issues/:subscribable_id/subscription
+ # DELETE /projects/:id/merge_requests/:subscribable_id/subscription
+ delete ":id/#{type}/:#{type_id_str}/subscription" do
+ resource = instance_exec(params[type_id_str], &finder)
+
+ if !resource.subscribed?(current_user)
+ not_modified!
+ else
+ resource.unsubscribe(current_user)
+ present resource, with: entity_class, current_user: current_user
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 132f9cd1966..3e3986d6382 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -180,7 +180,7 @@ module Gitlab
# exists?('gitlab/cookies.git')
#
def exists?(dir_name)
- File.exists?(full_path(dir_name))
+ File.exist?(full_path(dir_name))
end
protected
diff --git a/lib/gitlab/redis.rb b/lib/gitlab/redis.rb
index 5c352c96de5..f7db7a6391c 100644
--- a/lib/gitlab/redis.rb
+++ b/lib/gitlab/redis.rb
@@ -42,7 +42,7 @@ module Gitlab
config_file = File.expand_path('../../../config/resque.yml', __FILE__)
@url = "redis://localhost:6379"
- if File.exists?(config_file)
+ if File.exist?(config_file)
@url =YAML.load_file(config_file)[rails_env]
end
end
diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb
index 462afb24f08..6fad7e2b9e7 100644
--- a/spec/config/mail_room_spec.rb
+++ b/spec/config/mail_room_spec.rb
@@ -43,7 +43,7 @@ describe "mail_room.yml" do
redis_config_file = Rails.root.join('config', 'resque.yml')
redis_url =
- if File.exists?(redis_config_file)
+ if File.exist?(redis_config_file)
YAML.load_file(redis_config_file)[Rails.env]
else
"redis://localhost:6379"
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
new file mode 100644
index 00000000000..0d8a68bb51a
--- /dev/null
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe HealthCheckController do
+ let(:token) { current_application_settings.health_check_access_token }
+ let(:json_response) { JSON.parse(response.body) }
+ let(:xml_response) { Hash.from_xml(response.body)['hash'] }
+
+ describe 'GET #index' do
+ context 'when services are up but NO access token' do
+ it 'returns a not found page' do
+ get :index
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when services are up and an access token is provided' do
+ it 'supports passing the token in the header' do
+ request.headers['TOKEN'] = token
+ get :index
+ expect(response).to be_success
+ expect(response.content_type).to eq 'text/plain'
+ end
+
+ it 'supports successful plaintest response' do
+ get :index, token: token
+ expect(response).to be_success
+ expect(response.content_type).to eq 'text/plain'
+ end
+
+ it 'supports successful json response' do
+ get :index, token: token, format: :json
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be true
+ end
+
+ it 'supports successful xml response' do
+ get :index, token: token, format: :xml
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/xml'
+ expect(xml_response['healthy']).to be true
+ end
+
+ it 'supports successful responses for specific checks' do
+ get :index, token: token, checks: 'email', format: :json
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be true
+ end
+ end
+
+ context 'when a service is down but NO access token' do
+ it 'returns a not found page' do
+ get :index
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when a service is down and an access token is provided' do
+ before do
+ allow(HealthCheck::Utils).to receive(:process_checks).with('standard').and_return('The server is on fire')
+ allow(HealthCheck::Utils).to receive(:process_checks).with('email').and_return('Email is on fire')
+ end
+
+ it 'supports passing the token in the header' do
+ request.headers['TOKEN'] = token
+ get :index
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to include('The server is on fire')
+ end
+
+ it 'supports failure plaintest response' do
+ get :index, token: token
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to include('The server is on fire')
+ end
+
+ it 'supports failure json response' do
+ get :index, token: token, format: :json
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be false
+ expect(json_response['message']).to include('The server is on fire')
+ end
+
+ it 'supports failure xml response' do
+ get :index, token: token, format: :xml
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/xml'
+ expect(xml_response['healthy']).to be false
+ expect(xml_response['message']).to include('The server is on fire')
+ end
+
+ it 'supports failure responses for specific checks' do
+ get :index, token: token, checks: 'email', format: :json
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be false
+ expect(json_response['message']).to include('Email is on fire')
+ end
+ end
+ end
+end
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 2e9851fb442..7bbe20fec43 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -19,6 +19,7 @@ describe 'Admin Builds' do
visit admin_builds_path
expect(page).to have_selector('.nav-links li.active', text: 'All')
+ expect(page).to have_selector('.row-content-block', text: 'All builds')
expect(page.all('.build-link').size).to eq(4)
expect(page).to have_link 'Cancel all'
end
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
new file mode 100644
index 00000000000..dec2dedf2b5
--- /dev/null
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+feature "Admin Health Check", feature: true do
+ include WaitForAjax
+
+ before do
+ login_as :admin
+ end
+
+ describe '#show' do
+ before do
+ visit admin_health_check_path
+ end
+
+ it { page.has_text? 'Health Check' }
+ it { page.has_text? 'Health information can be retrieved' }
+
+ it 'has a health check access token' do
+ token = current_application_settings.health_check_access_token
+ expect(page).to have_content("Access token is #{token}")
+ expect(page).to have_selector('#health-check-token', text: token)
+ end
+
+ describe 'reload access token', js: true do
+ it 'changes the access token' do
+ orig_token = current_application_settings.health_check_access_token
+ click_button 'Reset health check access token'
+ wait_for_ajax
+ expect(find('#health-check-token').text).not_to eq orig_token
+ end
+ end
+ end
+
+ context 'when services are up' do
+ before do
+ visit admin_health_check_path
+ end
+
+ it 'shows healthy status' do
+ expect(page).to have_content('Current Status: Healthy')
+ end
+ end
+
+ context 'when a service is down' do
+ before do
+ allow(HealthCheck::Utils).to receive(:process_checks).and_return('The server is on fire')
+ visit admin_health_check_path
+ end
+
+ it 'shows unhealthy status' do
+ expect(page).to have_content('Current Status: Unhealthy')
+ expect(page).to have_content('The server is on fire')
+ end
+ end
+end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 090a941958f..f83a78308e3 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -43,6 +43,7 @@ describe "Builds" do
end
it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
+ it { expect(page).to have_selector('.row-content-block', text: 'All builds from this project') }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
it { expect(page).to have_content @build.name }
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index e47a54fdac5..16fbb5dcecb 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
describe AuthHelper do
describe "button_based_providers" do
- it 'returns all enabled providers' do
+ it 'returns all enabled providers from devise' do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
expect(helper.button_based_providers).to include(*[:twitter, :github])
end
@@ -17,4 +17,49 @@ describe AuthHelper do
expect(helper.button_based_providers).to eq([])
end
end
+
+ describe 'enabled_button_based_providers' do
+ before do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ end
+
+ context 'all providers are enabled to sign in' do
+ it 'returns all the enabled providers from settings' do
+ expect(helper.enabled_button_based_providers).to include('twitter', 'github')
+ end
+ end
+
+ context 'GitHub OAuth sign in is disabled from application setting' do
+ it "doesn't return github as provider" do
+ stub_application_setting(
+ disabled_oauth_sign_in_sources: ['github']
+ )
+
+ expect(helper.enabled_button_based_providers).to include('twitter')
+ expect(helper.enabled_button_based_providers).to_not include('github')
+ end
+ end
+ end
+
+ describe 'button_based_providers_enabled?' do
+ before do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ end
+
+ context 'button based providers enabled' do
+ it 'returns true' do
+ expect(helper.button_based_providers_enabled?).to be true
+ end
+ end
+
+ context 'all the button based providers are disabled via application_setting' do
+ it 'returns false' do
+ stub_application_setting(
+ disabled_oauth_sign_in_sources: ['github', 'twitter']
+ )
+
+ expect(helper.button_based_providers_enabled?).to be false
+ end
+ end
+ end
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index e68a5ec29ab..c0d2be98e85 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -1,64 +1,65 @@
require 'spec_helper'
describe EventsHelper do
- include ApplicationHelper
- include GitlabMarkdownHelper
+ describe '#event_note' do
+ before do
+ allow(helper).to receive(:current_user).and_return(double)
+ end
- let(:current_user) { create(:user, email: "current@email.com") }
+ it 'should display one line of plain text without alteration' do
+ input = 'A short, plain note'
+ expect(helper.event_note(input)).to match(input)
+ expect(helper.event_note(input)).not_to match(/\.\.\.\z/)
+ end
- it 'should display one line of plain text without alteration' do
- input = 'A short, plain note'
- expect(event_note(input)).to match(input)
- expect(event_note(input)).not_to match(/\.\.\.\z/)
- end
+ it 'should display inline code' do
+ input = 'A note with `inline code`'
+ expected = 'A note with <code>inline code</code>'
- it 'should display inline code' do
- input = 'A note with `inline code`'
- expected = 'A note with <code>inline code</code>'
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should truncate a note with multiple paragraphs' do
+ input = "Paragraph 1\n\nParagraph 2"
+ expected = 'Paragraph 1...'
- it 'should truncate a note with multiple paragraphs' do
- input = "Paragraph 1\n\nParagraph 2"
- expected = 'Paragraph 1...'
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should display the first line of a code block' do
+ input = "```\nCode block\nwith two lines\n```"
+ expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
- it 'should display the first line of a code block' do
- input = "```\nCode block\nwith two lines\n```"
- expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should truncate a single long line of text' do
+ text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
+ input = text * 4
+ expected = (text * 2).sub(/.{3}/, '...')
- it 'should truncate a single long line of text' do
- text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
- input = "#{text}#{text}#{text}#{text}" # 200 chars
- expected = "#{text}#{text}".sub(/.{3}/, '...')
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
-
- it 'should preserve a link href when link text is truncated' do
- text = 'The quick brown fox jumped over the lazy dog' # 44 chars
- input = "#{text}#{text}#{text} " # 133 chars
- link_url = 'http://example.com/foo/bar/baz' # 30 chars
- input << link_url
- expected_link_text = 'http://example...</a>'
+ it 'should preserve a link href when link text is truncated' do
+ text = 'The quick brown fox jumped over the lazy dog' # 44 chars
+ input = "#{text}#{text}#{text} " # 133 chars
+ link_url = 'http://example.com/foo/bar/baz' # 30 chars
+ input << link_url
+ expected_link_text = 'http://example...</a>'
- expect(event_note(input)).to match(link_url)
- expect(event_note(input)).to match(expected_link_text)
- end
+ expect(helper.event_note(input)).to match(link_url)
+ expect(helper.event_note(input)).to match(expected_link_text)
+ end
- it 'should preserve code color scheme' do
- input = "```ruby\ndef test\n 'hello world'\nend\n```"
- expected = '<pre class="code highlight js-syntax-highlight ruby">' \
- "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
- " <span class=\"s1\">\'hello world\'</span>\n" \
- "<span class=\"k\">end</span>" \
- '</code></pre>'
- expect(event_note(input)).to eq(expected)
+ it 'should preserve code color scheme' do
+ input = "```ruby\ndef test\n 'hello world'\nend\n```"
+ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
+ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
+ " <span class=\"s1\">\'hello world\'</span>\n" \
+ "<span class=\"k\">end</span>" \
+ '</code></pre>'
+ expect(helper.event_note(input)).to eq(expected)
+ end
end
end
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 1ce22feed5c..d84f3e998f5 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -20,6 +20,15 @@ describe ApplicationSetting, models: true do
it { is_expected.to allow_value(https).for(:after_sign_out_path) }
it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
+ describe 'disabled_oauth_sign_in_sources validations' do
+ before do
+ allow(Devise).to receive(:omniauth_providers).and_return([:github])
+ end
+
+ it { is_expected.to allow_value(['github']).for(:disabled_oauth_sign_in_sources) }
+ it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) }
+ end
+
it { is_expected.to validate_presence_of(:max_attachment_size) }
it do
diff --git a/spec/models/concerns/subscribable_spec.rb b/spec/models/concerns/subscribable_spec.rb
index e31fdb0bffb..b7fc5a92497 100644
--- a/spec/models/concerns/subscribable_spec.rb
+++ b/spec/models/concerns/subscribable_spec.rb
@@ -44,6 +44,16 @@ describe Subscribable, 'Subscribable' do
end
end
+ describe '#subscribe' do
+ it 'subscribes the given user' do
+ expect(resource.subscribed?(user)).to be_falsey
+
+ resource.subscribe(user)
+
+ expect(resource.subscribed?(user)).to be_truthy
+ end
+ end
+
describe '#unsubscribe' do
it 'unsubscribes the given current user' do
resource.subscriptions.create(user: user, subscribed: true)
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 9dd43f4fab3..37ab9cc8cfe 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -623,6 +623,12 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
+
+ it 'returns 404 if the issue is confidential' do
+ post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
+
+ expect(response.status).to eq(404)
+ end
end
describe 'DELETE :id/issues/:issue_id/subscription' do
@@ -644,5 +650,11 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
+
+ it 'returns 404 if the issue is confidential' do
+ delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
+
+ expect(response.status).to eq(404)
+ end
end
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 6943ff9d26c..b2c7f8d9acb 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -190,4 +190,86 @@ describe API::API, api: true do
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
end
+
+ describe "POST /projects/:id/labels/:label_id/subscription" do
+ context "when label_id is a label title" do
+ it "should subscribe to the label" do
+ post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_truthy
+ end
+ end
+
+ context "when label_id is a label ID" do
+ it "should subscribe to the label" do
+ post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_truthy
+ end
+ end
+
+ context "when user is already subscribed to label" do
+ before { label1.subscribe(user) }
+
+ it "should return 304" do
+ post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+ end
+
+ context "when label ID is not found" do
+ it "should a return 404 error" do
+ post api("/projects/#{project.id}/labels/1234/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
+ describe "DELETE /projects/:id/labels/:label_id/subscription" do
+ before { label1.subscribe(user) }
+
+ context "when label_id is a label title" do
+ it "should unsubscribe from the label" do
+ delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_falsey
+ end
+ end
+
+ context "when label_id is a label ID" do
+ it "should unsubscribe from the label" do
+ delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_falsey
+ end
+ end
+
+ context "when user is already unsubscribed from label" do
+ before { label1.unsubscribe(user) }
+
+ it "should return 304" do
+ delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+ end
+
+ context "when label ID is not found" do
+ it "should a return 404 error" do
+ delete api("/projects/#{project.id}/labels/1234/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
end
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index cd16a8e6322..b5ed8584c8a 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -118,3 +118,10 @@ describe Admin::DashboardController, "routing" do
expect(get("/admin")).to route_to('admin/dashboard#index')
end
end
+
+# admin_health_check GET /admin/health_check(.:format) admin/health_check#show
+describe Admin::HealthCheckController, "routing" do
+ it "to #show" do
+ expect(get("/admin/health_check")).to route_to('admin/health_check#show')
+ end
+end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 8530a2f31d5..de13c0db5d1 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -276,3 +276,13 @@ describe "Groups", "routing" do
expect(get('/1')).to route_to('namespaces#show', id: '1')
end
end
+
+describe HealthCheckController, 'routing' do
+ it 'to #index' do
+ expect(get('/health_check')).to route_to('health_check#index')
+ end
+
+ it 'also supports passing checks in the url' do
+ expect(get('/health_check/email')).to route_to('health_check#index', checks: 'email')
+ end
+end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e43903dbd3c..fd114359467 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -64,7 +64,7 @@ describe Projects::CreateService, services: true do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { expect(File.exists?(@path)).to be_truthy }
+ it { expect(File.exist?(@path)).to be_truthy }
end
context 'wiki_enabled false does not create wiki repository directory' do
@@ -74,7 +74,7 @@ describe Projects::CreateService, services: true do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { expect(File.exists?(@path)).to be_falsey }
+ it { expect(File.exist?(@path)).to be_falsey }
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 1ec27077717..a5cb6f382e4 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -13,8 +13,8 @@ describe Projects::DestroyService, services: true do
end
it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exists?(path)).to be_falsey }
- it { expect(Dir.exists?(remove_path)).to be_falsey }
+ it { expect(Dir.exist?(path)).to be_falsey }
+ it { expect(Dir.exist?(remove_path)).to be_falsey }
end
context 'Sidekiq fake' do
@@ -24,8 +24,8 @@ describe Projects::DestroyService, services: true do
end
it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exists?(path)).to be_falsey }
- it { expect(Dir.exists?(remove_path)).to be_truthy }
+ it { expect(Dir.exist?(path)).to be_falsey }
+ it { expect(Dir.exist?(remove_path)).to be_truthy }
end
def destroy_project(project, user, params)