Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--app/controllers/explore/snippets_controller.rb2
-rw-r--r--app/controllers/notification_settings_controller.rb4
-rw-r--r--app/controllers/profiles/groups_controller.rb2
-rw-r--r--app/controllers/profiles/notifications_controller.rb11
-rw-r--r--app/finders/snippets_finder.rb21
-rw-r--r--app/models/concerns/stepable.rb8
-rw-r--r--app/models/notification_setting.rb4
-rw-r--r--app/models/snippet.rb2
-rw-r--r--app/models/user.rb17
-rw-r--r--app/services/application_settings/update_service.rb29
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/profiles/notifications/_group_settings.html.haml2
-rw-r--r--changelogs/unreleased/29477-notification-settings-display-all-groups.yml5
-rw-r--r--changelogs/unreleased/30877-optimize-explore-snippets.yml5
-rw-r--r--changelogs/unreleased/34120-design-system-notes-icon-does-not-appear.yml5
-rw-r--r--changelogs/unreleased/return-error-message-when-performance-bar-group-is-not-found.yml5
-rw-r--r--changelogs/unreleased/sort-severity-then-confidence.yml5
-rw-r--r--db/migrate/20191014084150_add_index_on_snippets_project_id_and_visibility_level.rb17
-rw-r--r--db/migrate/20191014132931_remove_index_on_snippets_project_id.rb17
-rw-r--r--db/schema.rb2
-rw-r--r--doc/user/analytics/productivity_analytics.md2
-rw-r--r--doc/user/project/clusters/img/kubernetes_pod_logs_v12_4.png (renamed from doc/user/project/clusters/img/kubernetes_pod_logs.png)bin393690 -> 393690 bytes
-rw-r--r--doc/user/project/clusters/kubernetes_pod_logs.md10
-rw-r--r--lib/gitlab/database_importers/self_monitoring/project/create_service.rb87
-rw-r--r--locale/gitlab.pot6
-rw-r--r--spec/controllers/profiles/notifications_controller_spec.rb32
-rw-r--r--spec/finders/snippets_finder_spec.rb59
-rw-r--r--spec/models/concerns/stepable_spec.rb34
-rw-r--r--spec/models/snippet_spec.rb4
-rw-r--r--spec/models/user_spec.rb74
-rw-r--r--spec/services/application_settings/update_service_spec.rb65
31 files changed, 409 insertions, 129 deletions
diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb
index d4c6aae2ca8..61068df77d1 100644
--- a/app/controllers/explore/snippets_controller.rb
+++ b/app/controllers/explore/snippets_controller.rb
@@ -5,7 +5,7 @@ class Explore::SnippetsController < Explore::ApplicationController
include Gitlab::NoteableMetadata
def index
- @snippets = SnippetsFinder.new(current_user)
+ @snippets = SnippetsFinder.new(current_user, explore: true)
.execute
.page(params[:page])
.inc_author
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index 43c4f4d220e..c97fec0a6ee 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -50,8 +50,6 @@ class NotificationSettingsController < ApplicationController
end
def notification_setting_params_for(source)
- allowed_fields = NotificationSetting.email_events(source).dup
- allowed_fields << :level
- params.require(:notification_setting).permit(allowed_fields)
+ params.require(:notification_setting).permit(NotificationSetting.allowed_fields(source))
end
end
diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb
index c755bcb718a..04b5ee270dc 100644
--- a/app/controllers/profiles/groups_controller.rb
+++ b/app/controllers/profiles/groups_controller.rb
@@ -5,7 +5,7 @@ class Profiles::GroupsController < Profiles::ApplicationController
def update
group = find_routable!(Group, params[:id])
- notification_setting = current_user.notification_settings.find_by(source: group) # rubocop: disable CodeReuse/ActiveRecord
+ notification_setting = current_user.notification_settings_for(group)
if notification_setting.update(update_params)
flash[:notice] = "Notification settings for #{group.name} saved"
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index 617e5bb7cb3..5f44e55f3ef 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -3,9 +3,14 @@
class Profiles::NotificationsController < Profiles::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def show
- @user = current_user
- @group_notifications = current_user.notification_settings.for_groups.order(:id)
- @project_notifications = current_user.notification_settings.for_projects.order(:id)
+ @user = current_user
+ @group_notifications = current_user.notification_settings.for_groups.order(:id)
+ @group_notifications += GroupsFinder.new(
+ current_user,
+ all_available: false,
+ exclude_group_ids: @group_notifications.select(:source_id)
+ ).execute.map { |group| current_user.notification_settings_for(group, inherit: true) }
+ @project_notifications = current_user.notification_settings.for_projects.order(:id)
@global_notification_setting = current_user.global_notification_setting
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 6e2bb1ded43..bd6b6190fb5 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -41,13 +41,14 @@
class SnippetsFinder < UnionFinder
include FinderMethods
- attr_accessor :current_user, :project, :author, :scope
+ attr_accessor :current_user, :project, :author, :scope, :explore
def initialize(current_user = nil, params = {})
@current_user = current_user
@project = params[:project]
@author = params[:author]
@scope = params[:scope].to_s
+ @explore = params[:explore]
if project && author
raise(
@@ -66,13 +67,23 @@ class SnippetsFinder < UnionFinder
private
def init_collection
- if project
+ if explore
+ snippets_for_explore
+ elsif project
snippets_for_a_single_project
else
snippets_for_multiple_projects
end
end
+ # Produces a query that retrieves snippets for the Explore page
+ #
+ # We only show personal snippets here because this page is meant for
+ # discovery, and project snippets are of limited interest here.
+ def snippets_for_explore
+ Snippet.public_to_user(current_user).only_personal_snippets
+ end
+
# Produces a query that retrieves snippets from multiple projects.
#
# The resulting query will, depending on the user's permissions, include the
@@ -86,7 +97,7 @@ class SnippetsFinder < UnionFinder
# Each collection is constructed in isolation, allowing for greater control
# over the resulting SQL query.
def snippets_for_multiple_projects
- queries = [global_snippets]
+ queries = [personal_snippets]
if Ability.allowed?(current_user, :read_cross_project)
queries << snippets_of_visible_projects
@@ -100,8 +111,8 @@ class SnippetsFinder < UnionFinder
Snippet.for_project_with_user(project, current_user)
end
- def global_snippets
- snippets_for_author_or_visible_to_user.only_global_snippets
+ def personal_snippets
+ snippets_for_author_or_visible_to_user.only_personal_snippets
end
# Returns the snippets that the current user (logged in or not) can view.
diff --git a/app/models/concerns/stepable.rb b/app/models/concerns/stepable.rb
index d00a049a004..dea241c5dbe 100644
--- a/app/models/concerns/stepable.rb
+++ b/app/models/concerns/stepable.rb
@@ -11,15 +11,15 @@ module Stepable
initial_result = {}
steps.inject(initial_result) do |previous_result, callback|
- result = method(callback).call
+ result = method(callback).call(previous_result)
- if result[:status] == :error
- result[:failed_step] = callback
+ if result[:status] != :success
+ result[:last_step] = callback
break result
end
- previous_result.merge(result)
+ result
end
end
diff --git a/app/models/notification_setting.rb b/app/models/notification_setting.rb
index 20160da62d2..2b3443f24d7 100644
--- a/app/models/notification_setting.rb
+++ b/app/models/notification_setting.rb
@@ -47,6 +47,10 @@ class NotificationSetting < ApplicationRecord
EMAIL_EVENTS
end
+ def self.allowed_fields(source = nil)
+ NotificationSetting.email_events(source).dup + %i(level notification_email)
+ end
+
def email_events
self.class.email_events(source)
end
diff --git a/app/models/snippet.rb b/app/models/snippet.rb
index b6c0976cd43..4010a3e2167 100644
--- a/app/models/snippet.rb
+++ b/app/models/snippet.rb
@@ -71,7 +71,7 @@ class Snippet < ApplicationRecord
end
end
- def self.only_global_snippets
+ def self.only_personal_snippets
where(project_id: nil)
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 42a5f6cebeb..fe5c13f1c68 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1317,14 +1317,27 @@ class User < ApplicationRecord
notification_group&.notification_email_for(self) || notification_email
end
- def notification_settings_for(source)
+ def notification_settings_for(source, inherit: false)
if notification_settings.loaded?
notification_settings.find do |notification|
notification.source_type == source.class.base_class.name &&
notification.source_id == source.id
end
else
- notification_settings.find_or_initialize_by(source: source)
+ notification_settings.find_or_initialize_by(source: source) do |ns|
+ next unless source.is_a?(Group) && inherit
+
+ # If we're here it means we're trying to create a NotificationSetting for a group that doesn't have one.
+ # Find the closest parent with a notification_setting that's not Global level, or that has an email set.
+ ancestor_ns = source
+ .notification_settings(hierarchy_order: :asc)
+ .where(user: self)
+ .find_by('level != ? OR notification_email IS NOT NULL', NotificationSetting.levels[:global])
+ # Use it to seed the settings
+ ns.assign_attributes(ancestor_ns&.slice(*NotificationSetting.allowed_fields))
+ ns.source = source
+ ns.user = self
+ end
end
end
diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb
index 6400b182715..df9217bea32 100644
--- a/app/services/application_settings/update_service.rb
+++ b/app/services/application_settings/update_service.rb
@@ -20,7 +20,11 @@ module ApplicationSettings
add_to_outbound_local_requests_whitelist(@params.delete(:add_to_outbound_local_requests_whitelist))
if params.key?(:performance_bar_allowed_group_path)
- params[:performance_bar_allowed_group_id] = performance_bar_allowed_group_id
+ group_id = process_performance_bar_allowed_group_id
+
+ return false if application_setting.errors.any?
+
+ params[:performance_bar_allowed_group_id] = group_id
end
if usage_stats_updated? && !params.delete(:skip_usage_stats_user)
@@ -65,12 +69,27 @@ module ApplicationSettings
@application_setting.reset_memoized_terms
end
- def performance_bar_allowed_group_id
- performance_bar_enabled = !params.key?(:performance_bar_enabled) || params.delete(:performance_bar_enabled)
+ def process_performance_bar_allowed_group_id
group_full_path = params.delete(:performance_bar_allowed_group_path)
- return unless Gitlab::Utils.to_boolean(performance_bar_enabled)
+ enable_param_on = Gitlab::Utils.to_boolean(params.delete(:performance_bar_enabled))
+ performance_bar_enabled = enable_param_on.nil? || enable_param_on # Default to true
+
+ return if group_full_path.blank?
+ return if enable_param_on == false # Explicitly disabling
+
+ unless performance_bar_enabled
+ application_setting.errors.add(:performance_bar_allowed_group_id, 'not allowed when performance bar is disabled')
+ return
+ end
+
+ group = Group.find_by_full_path(group_full_path.chomp('/'))
+
+ unless group
+ application_setting.errors.add(:performance_bar_allowed_group_id, 'not found')
+ return
+ end
- Group.find_by_full_path(group_full_path)&.id if group_full_path.present?
+ group.id
end
def bypass_external_auth?
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 0e6c16f0f06..457d05b4a97 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -5,6 +5,8 @@
= auto_discovery_link_tag(:atom, group_url(@group, rss_url_options), title: "#{@group.name} activity")
%div{ class: [("limit-container-width" unless fluid_layout)] }
+ = render_if_exists 'trials/banner', namespace: @group
+
= render 'groups/home_panel'
.groups-listing{ data: { endpoints: { default: group_children_path(@group, format: :json), shared: group_shared_projects_path(@group, format: :json) } } }
diff --git a/app/views/profiles/notifications/_group_settings.html.haml b/app/views/profiles/notifications/_group_settings.html.haml
index 1776d260e19..33b0aa93d84 100644
--- a/app/views/profiles/notifications/_group_settings.html.haml
+++ b/app/views/profiles/notifications/_group_settings.html.haml
@@ -12,5 +12,5 @@
= render 'shared/notifications/button', notification_setting: setting, emails_disabled: emails_disabled
.table-section.section-30
- = form_for @user.notification_settings.find { |ns| ns.source == group }, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications' } do |f|
+ = form_for setting, url: profile_notifications_group_path(group), method: :put, html: { class: 'update-notifications' } do |f|
= f.select :notification_email, @user.all_emails, { include_blank: 'Global notification email' }, class: 'select2 js-group-notification-email'
diff --git a/changelogs/unreleased/29477-notification-settings-display-all-groups.yml b/changelogs/unreleased/29477-notification-settings-display-all-groups.yml
new file mode 100644
index 00000000000..a4cb6fe1643
--- /dev/null
+++ b/changelogs/unreleased/29477-notification-settings-display-all-groups.yml
@@ -0,0 +1,5 @@
+---
+title: Show all groups user belongs to in Notification settings
+merge_request: 17303
+author:
+type: fixed
diff --git a/changelogs/unreleased/30877-optimize-explore-snippets.yml b/changelogs/unreleased/30877-optimize-explore-snippets.yml
new file mode 100644
index 00000000000..7ca52876609
--- /dev/null
+++ b/changelogs/unreleased/30877-optimize-explore-snippets.yml
@@ -0,0 +1,5 @@
+---
+title: Show only personal snippets on explore page
+merge_request: 18092
+author:
+type: performance
diff --git a/changelogs/unreleased/34120-design-system-notes-icon-does-not-appear.yml b/changelogs/unreleased/34120-design-system-notes-icon-does-not-appear.yml
new file mode 100644
index 00000000000..8533067a408
--- /dev/null
+++ b/changelogs/unreleased/34120-design-system-notes-icon-does-not-appear.yml
@@ -0,0 +1,5 @@
+---
+title: Resolve missing design system notes icons
+merge_request: 18693
+author:
+type: fixed
diff --git a/changelogs/unreleased/return-error-message-when-performance-bar-group-is-not-found.yml b/changelogs/unreleased/return-error-message-when-performance-bar-group-is-not-found.yml
new file mode 100644
index 00000000000..1d00597ba7d
--- /dev/null
+++ b/changelogs/unreleased/return-error-message-when-performance-bar-group-is-not-found.yml
@@ -0,0 +1,5 @@
+---
+title: Show error message when setting an invalid group ID for the performance bar
+merge_request:
+author:
+type: fixed
diff --git a/changelogs/unreleased/sort-severity-then-confidence.yml b/changelogs/unreleased/sort-severity-then-confidence.yml
new file mode 100644
index 00000000000..3c7ab63d60b
--- /dev/null
+++ b/changelogs/unreleased/sort-severity-then-confidence.yml
@@ -0,0 +1,5 @@
+---
+title: Sort vulnerabilities by severity then confidence for dashboard and pipeline views
+merge_request: 18675
+author:
+type: changed
diff --git a/db/migrate/20191014084150_add_index_on_snippets_project_id_and_visibility_level.rb b/db/migrate/20191014084150_add_index_on_snippets_project_id_and_visibility_level.rb
new file mode 100644
index 00000000000..a8f40953e5d
--- /dev/null
+++ b/db/migrate/20191014084150_add_index_on_snippets_project_id_and_visibility_level.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class AddIndexOnSnippetsProjectIdAndVisibilityLevel < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ add_concurrent_index :snippets, [:project_id, :visibility_level]
+ end
+
+ def down
+ remove_concurrent_index :snippets, [:project_id, :visibility_level]
+ end
+end
diff --git a/db/migrate/20191014132931_remove_index_on_snippets_project_id.rb b/db/migrate/20191014132931_remove_index_on_snippets_project_id.rb
new file mode 100644
index 00000000000..a1d3ffdb8c8
--- /dev/null
+++ b/db/migrate/20191014132931_remove_index_on_snippets_project_id.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+class RemoveIndexOnSnippetsProjectId < ActiveRecord::Migration[5.2]
+ include Gitlab::Database::MigrationHelpers
+
+ DOWNTIME = false
+
+ disable_ddl_transaction!
+
+ def up
+ remove_concurrent_index :snippets, [:project_id]
+ end
+
+ def down
+ add_concurrent_index :snippets, [:project_id]
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index ef9cd696db5..b39eb408b0a 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -3449,7 +3449,7 @@ ActiveRecord::Schema.define(version: 2019_10_16_220135) do
t.index ["author_id"], name: "index_snippets_on_author_id"
t.index ["content"], name: "index_snippets_on_content_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["file_name"], name: "index_snippets_on_file_name_trigram", opclass: :gin_trgm_ops, using: :gin
- t.index ["project_id"], name: "index_snippets_on_project_id"
+ t.index ["project_id", "visibility_level"], name: "index_snippets_on_project_id_and_visibility_level"
t.index ["title"], name: "index_snippets_on_title_trigram", opclass: :gin_trgm_ops, using: :gin
t.index ["updated_at"], name: "index_snippets_on_updated_at"
t.index ["visibility_level"], name: "index_snippets_on_visibility_level"
diff --git a/doc/user/analytics/productivity_analytics.md b/doc/user/analytics/productivity_analytics.md
index 7313a25e797..09f83dcff4b 100644
--- a/doc/user/analytics/productivity_analytics.md
+++ b/doc/user/analytics/productivity_analytics.md
@@ -1,6 +1,6 @@
# Productivity Analytics **(PREMIUM)**
-> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12079) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.3 (enabled by feature flags `productivity_analytics`, `productivity_analytics_scatterplot_enabled`).
+> [Introduced](https://gitlab.com/gitlab-org/gitlab/issues/12079) in [GitLab Premium](https://about.gitlab.com/pricing/) 12.3 (enabled by default using the feature flags `productivity_analytics`, `productivity_analytics_scatterplot_enabled`).
Track development velocity with Productivity Analytics.
diff --git a/doc/user/project/clusters/img/kubernetes_pod_logs.png b/doc/user/project/clusters/img/kubernetes_pod_logs_v12_4.png
index 73c2ecd182a..73c2ecd182a 100644
--- a/doc/user/project/clusters/img/kubernetes_pod_logs.png
+++ b/doc/user/project/clusters/img/kubernetes_pod_logs_v12_4.png
Binary files differ
diff --git a/doc/user/project/clusters/kubernetes_pod_logs.md b/doc/user/project/clusters/kubernetes_pod_logs.md
index 910ebb8a503..4036eaf0bfb 100644
--- a/doc/user/project/clusters/kubernetes_pod_logs.md
+++ b/doc/user/project/clusters/kubernetes_pod_logs.md
@@ -17,8 +17,14 @@ Everything you need to build, test, deploy, and run your app at scale.
1. On the **Environments** page, you should see the status of the environment's pods with [Deploy Boards](../deploy_boards.md).
1. When mousing over the list of pods, a tooltip will appear with the exact pod name and status.
![Deploy Boards pod list](img/pod_logs_deploy_board.png)
-1. Click on the desired pod to bring up the logs view, which will contain the last 500 lines for that pod. You may switch between pods and environments in this view. Support for pods with multiple containers is coming [in a future release](https://gitlab.com/gitlab-org/gitlab/issues/6502).
- ![Deploy Boards pod list](img/kubernetes_pod_logs.png)
+1. Click on the desired pod to bring up the logs view, which will contain the last 500 lines for that pod.
+ You may switch between the following in this view:
+ - Pods.
+ - [From GitLab 12.4](https://gitlab.com/gitlab-org/gitlab/issues/5769), environments.
+
+ Support for pods with multiple containers is coming [in a future release](https://gitlab.com/gitlab-org/gitlab/issues/6502).
+
+ ![Deploy Boards pod list](img/kubernetes_pod_logs_v12_4.png)
## Requirements
diff --git a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
index 5422a8631a0..dfef158cc1d 100644
--- a/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
+++ b/lib/gitlab/database_importers/self_monitoring/project/create_service.rb
@@ -33,7 +33,7 @@ module Gitlab
if result[:status] == :success
result
- elsif STEPS_ALLOWED_TO_FAIL.include?(result[:failed_step])
+ elsif STEPS_ALLOWED_TO_FAIL.include?(result[:last_step])
success
else
raise StandardError, result[:message]
@@ -42,121 +42,124 @@ module Gitlab
private
- def validate_application_settings
+ def validate_application_settings(_result)
return success if application_settings
log_error('No application_settings found')
error(_('No application_settings found'))
end
- def validate_project_created
- return success unless project_created?
+ def validate_project_created(result)
+ return success(result) unless project_created?
log_error('Project already created')
error(_('Project already created'))
end
- def validate_admins
+ def validate_admins(result)
unless instance_admins.any?
log_error('No active admin user found')
return error(_('No active admin user found'))
end
- success
+ success(result)
end
- def create_group
+ def create_group(result)
if project_created?
log_info(_('Instance administrators group already exists'))
- @group = application_settings.instance_administration_project.owner
- return success(group: @group)
+ result[:group] = application_settings.instance_administration_project.owner
+ return success(result)
end
- @group = ::Groups::CreateService.new(group_owner, create_group_params).execute
+ result[:group] = ::Groups::CreateService.new(group_owner, create_group_params).execute
- if @group.persisted?
- success(group: @group)
+ if result[:group].persisted?
+ success(result)
else
error(_('Could not create group'))
end
end
- def create_project
+ def create_project(result)
if project_created?
log_info('Instance administration project already exists')
- @project = application_settings.instance_administration_project
- return success(project: project)
+ result[:project] = application_settings.instance_administration_project
+ return success(result)
end
- @project = ::Projects::CreateService.new(group_owner, create_project_params).execute
+ result[:project] = ::Projects::CreateService.new(group_owner, create_project_params(result[:group])).execute
- if project.persisted?
- success(project: project)
+ if result[:project].persisted?
+ success(result)
else
- log_error("Could not create instance administration project. Errors: %{errors}" % { errors: project.errors.full_messages })
+ log_error("Could not create instance administration project. Errors: %{errors}" % { errors: result[:project].errors.full_messages })
error(_('Could not create project'))
end
end
- def save_project_id
+ def save_project_id(result)
return success if project_created?
- result = application_settings.update(instance_administration_project_id: @project.id)
+ response = application_settings.update(
+ instance_administration_project_id: result[:project].id
+ )
- if result
- success
+ if response
+ success(result)
else
log_error("Could not save instance administration project ID, errors: %{errors}" % { errors: application_settings.errors.full_messages })
error(_('Could not save project ID'))
end
end
- def add_group_members
- members = @group.add_users(members_to_add, Gitlab::Access::MAINTAINER)
+ def add_group_members(result)
+ group = result[:group]
+ members = group.add_users(members_to_add(group), Gitlab::Access::MAINTAINER)
errors = members.flat_map { |member| member.errors.full_messages }
if errors.any?
log_error('Could not add admins as members to self-monitoring project. Errors: %{errors}' % { errors: errors })
error(_('Could not add admins as members'))
else
- success
+ success(result)
end
end
- def add_to_whitelist
- return success unless prometheus_enabled?
- return success unless prometheus_listen_address.present?
+ def add_to_whitelist(result)
+ return success(result) unless prometheus_enabled?
+ return success(result) unless prometheus_listen_address.present?
uri = parse_url(internal_prometheus_listen_address_uri)
return error(_('Prometheus listen_address in config/gitlab.yml is not a valid URI')) unless uri
application_settings.add_to_outbound_local_requests_whitelist([uri.normalized_host])
- result = application_settings.save
+ response = application_settings.save
- if result
+ if response
# Expire the Gitlab::CurrentSettings cache after updating the whitelist.
# This happens automatically in an after_commit hook, but in migrations,
# the after_commit hook only runs at the end of the migration.
Gitlab::CurrentSettings.expire_current_application_settings
- success
+ success(result)
else
log_error("Could not add prometheus URL to whitelist, errors: %{errors}" % { errors: application_settings.errors.full_messages })
error(_('Could not add prometheus URL to whitelist'))
end
end
- def add_prometheus_manual_configuration
- return success unless prometheus_enabled?
- return success unless prometheus_listen_address.present?
+ def add_prometheus_manual_configuration(result)
+ return success(result) unless prometheus_enabled?
+ return success(result) unless prometheus_listen_address.present?
- service = project.find_or_initialize_service('prometheus')
+ service = result[:project].find_or_initialize_service('prometheus')
unless service.update(prometheus_service_attributes)
log_error('Could not save prometheus manual configuration for self-monitoring project. Errors: %{errors}' % { errors: service.errors.full_messages })
return error(_('Could not save prometheus manual configuration'))
end
- success
+ success(result)
end
def application_settings
@@ -196,11 +199,11 @@ module Gitlab
instance_admins.first
end
- def members_to_add
+ def members_to_add(group)
# Exclude admins who are already members of group because
- # `@group.add_users(users)` returns an error if the users parameter contains
+ # `group.add_users(users)` returns an error if the users parameter contains
# users who are already members of the group.
- instance_admins - @group.members.collect(&:user)
+ instance_admins - group.members.collect(&:user)
end
def create_group_params
@@ -217,13 +220,13 @@ module Gitlab
)
end
- def create_project_params
+ def create_project_params(group)
{
initialize_with_readme: true,
visibility_level: VISIBILITY_LEVEL,
name: PROJECT_NAME,
description: "This project is automatically generated and will be used to help monitor this GitLab instance. [More information](#{docs_path})",
- namespace_id: @group.id
+ namespace_id: group.id
}
end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index b3f4b0f4931..bf24381c031 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -10947,6 +10947,9 @@ msgstr ""
msgid "No data to display"
msgstr ""
+msgid "No deployment platform available"
+msgstr ""
+
msgid "No deployments found"
msgstr ""
@@ -11013,6 +11016,9 @@ msgstr ""
msgid "No parent group"
msgstr ""
+msgid "No pods available"
+msgstr ""
+
msgid "No preview for this file type"
msgstr ""
diff --git a/spec/controllers/profiles/notifications_controller_spec.rb b/spec/controllers/profiles/notifications_controller_spec.rb
index f69847119d4..dbc408bcdd9 100644
--- a/spec/controllers/profiles/notifications_controller_spec.rb
+++ b/spec/controllers/profiles/notifications_controller_spec.rb
@@ -20,6 +20,38 @@ describe Profiles::NotificationsController do
expect(response).to render_template :show
end
+
+ context 'with groups that do not have notification preferences' do
+ set(:group) { create(:group) }
+ set(:subgroup) { create(:group, parent: group) }
+
+ before do
+ group.add_developer(user)
+ end
+
+ it 'still shows up in the list' do
+ sign_in(user)
+
+ get :show
+
+ expect(assigns(:group_notifications).map(&:source_id)).to include(subgroup.id)
+ end
+
+ it 'has an N+1 (but should not)' do
+ sign_in(user)
+
+ control = ActiveRecord::QueryRecorder.new do
+ get :show
+ end
+
+ create_list(:group, 2, parent: group)
+
+ # We currently have an N + 1, switch to `not_to` once fixed
+ expect do
+ get :show
+ end.to exceed_query_limit(control)
+ end
+ end
end
describe 'POST update' do
diff --git a/spec/finders/snippets_finder_spec.rb b/spec/finders/snippets_finder_spec.rb
index e7372189d17..bcb762664f7 100644
--- a/spec/finders/snippets_finder_spec.rb
+++ b/spec/finders/snippets_finder_spec.rb
@@ -17,16 +17,27 @@ describe SnippetsFinder do
end
describe '#execute' do
- set(:user) { create(:user) }
- set(:private_personal_snippet) { create(:personal_snippet, :private, author: user) }
- set(:internal_personal_snippet) { create(:personal_snippet, :internal, author: user) }
- set(:public_personal_snippet) { create(:personal_snippet, :public, author: user) }
+ let_it_be(:user) { create(:user) }
+ let_it_be(:admin) { create(:admin) }
+ let_it_be(:group) { create(:group, :public) }
+ let_it_be(:project) { create(:project, :public, group: group) }
+
+ let_it_be(:private_personal_snippet) { create(:personal_snippet, :private, author: user) }
+ let_it_be(:internal_personal_snippet) { create(:personal_snippet, :internal, author: user) }
+ let_it_be(:public_personal_snippet) { create(:personal_snippet, :public, author: user) }
+
+ let_it_be(:private_project_snippet) { create(:project_snippet, :private, project: project) }
+ let_it_be(:internal_project_snippet) { create(:project_snippet, :internal, project: project) }
+ let_it_be(:public_project_snippet) { create(:project_snippet, :public, project: project) }
context 'filter by scope' do
it "returns all snippets for 'all' scope" do
snippets = described_class.new(user, scope: :all).execute
- expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
+ expect(snippets).to contain_exactly(
+ private_personal_snippet, internal_personal_snippet, public_personal_snippet,
+ internal_project_snippet, public_project_snippet
+ )
end
it "returns all snippets for 'are_private' scope" do
@@ -38,13 +49,13 @@ describe SnippetsFinder do
it "returns all snippets for 'are_internal' scope" do
snippets = described_class.new(user, scope: :are_internal).execute
- expect(snippets).to contain_exactly(internal_personal_snippet)
+ expect(snippets).to contain_exactly(internal_personal_snippet, internal_project_snippet)
end
- it "returns all snippets for 'are_private' scope" do
+ it "returns all snippets for 'are_public' scope" do
snippets = described_class.new(user, scope: :are_public).execute
- expect(snippets).to contain_exactly(public_personal_snippet)
+ expect(snippets).to contain_exactly(public_personal_snippet, public_project_snippet)
end
end
@@ -86,7 +97,6 @@ describe SnippetsFinder do
end
it 'returns all snippets for an admin' do
- admin = create(:user, :admin)
snippets = described_class.new(admin, author: user).execute
expect(snippets).to contain_exactly(private_personal_snippet, internal_personal_snippet, public_personal_snippet)
@@ -94,12 +104,6 @@ describe SnippetsFinder do
end
context 'project snippets' do
- let(:group) { create(:group, :public) }
- let(:project) { create(:project, :public, group: group) }
- let!(:private_project_snippet) { create(:project_snippet, :private, project: project) }
- let!(:internal_project_snippet) { create(:project_snippet, :internal, project: project) }
- let!(:public_project_snippet) { create(:project_snippet, :public, project: project) }
-
it 'returns public personal and project snippets for unauthorized user' do
snippets = described_class.new(nil, project: project).execute
@@ -147,7 +151,6 @@ describe SnippetsFinder do
end
it 'returns all snippets for an admin' do
- admin = create(:user, :admin)
snippets = described_class.new(admin, project: project).execute
expect(snippets).to contain_exactly(private_project_snippet, internal_project_snippet, public_project_snippet)
@@ -174,6 +177,30 @@ describe SnippetsFinder do
end
end
+ context 'explore snippets' do
+ it 'returns only public personal snippets for unauthenticated users' do
+ snippets = described_class.new(nil, explore: true).execute
+
+ expect(snippets).to contain_exactly(public_personal_snippet)
+ end
+
+ it 'also returns internal personal snippets for authenticated users' do
+ snippets = described_class.new(user, explore: true).execute
+
+ expect(snippets).to contain_exactly(
+ internal_personal_snippet, public_personal_snippet
+ )
+ end
+
+ it 'returns all personal snippets for admins' do
+ snippets = described_class.new(admin, explore: true).execute
+
+ expect(snippets).to contain_exactly(
+ private_personal_snippet, internal_personal_snippet, public_personal_snippet
+ )
+ end
+ end
+
context 'when the user cannot read cross project' do
before do
allow(Ability).to receive(:allowed?).and_call_original
diff --git a/spec/models/concerns/stepable_spec.rb b/spec/models/concerns/stepable_spec.rb
index 5685de6a9bf..51356c3eaf6 100644
--- a/spec/models/concerns/stepable_spec.rb
+++ b/spec/models/concerns/stepable_spec.rb
@@ -7,6 +7,8 @@ describe Stepable do
Class.new do
include Stepable
+ attr_writer :return_non_success
+
steps :method1, :method2, :method3
def execute
@@ -15,18 +17,18 @@ describe Stepable do
private
- def method1
+ def method1(_result)
{ status: :success }
end
- def method2
- return { status: :error } unless @pass
+ def method2(result)
+ return { status: :not_a_success } if @return_non_success
- { status: :success, variable1: 'var1' }
+ result.merge({ status: :success, variable1: 'var1', excluded_variable: 'a' })
end
- def method3
- { status: :success, variable2: 'var2' }
+ def method3(result)
+ result.except(:excluded_variable).merge({ status: :success, variable2: 'var2' })
end
end
end
@@ -41,8 +43,8 @@ describe Stepable do
private
- def appended_method1
- { status: :success }
+ def appended_method1(previous_result)
+ previous_result.merge({ status: :success })
end
end
end
@@ -51,21 +53,19 @@ describe Stepable do
described_class.prepend(prepended_module)
end
- it 'stops after the first error' do
+ it 'stops after the first non success status' do
+ subject.return_non_success = true
+
expect(subject).not_to receive(:method3)
expect(subject).not_to receive(:appended_method1)
expect(subject.execute).to eq(
- status: :error,
- failed_step: :method2
+ status: :not_a_success,
+ last_step: :method2
)
end
context 'when all methods return success' do
- before do
- subject.instance_variable_set(:@pass, true)
- end
-
it 'calls all methods in order' do
expect(subject).to receive(:method1).and_call_original.ordered
expect(subject).to receive(:method2).and_call_original.ordered
@@ -82,6 +82,10 @@ describe Stepable do
variable2: 'var2'
)
end
+
+ it 'can modify results of previous steps' do
+ expect(subject.execute).not_to include(excluded_variable: 'a')
+ end
end
context 'with multiple stepable classes' do
diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb
index 15dcccb37d9..f4dcbfbc190 100644
--- a/spec/models/snippet_spec.rb
+++ b/spec/models/snippet_spec.rb
@@ -183,12 +183,12 @@ describe Snippet do
end
end
- describe '.only_global_snippets' do
+ describe '.only_personal_snippets' do
it 'returns snippets not associated with any projects' do
create(:project_snippet)
snippet = create(:snippet)
- snippets = described_class.only_global_snippets
+ snippets = described_class.only_personal_snippets
expect(snippets).to eq([snippet])
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 240c917e7cf..8eb2f9b5bc0 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -3735,6 +3735,80 @@ describe User do
end
end
+ describe '#notification_settings_for' do
+ let(:user) { create(:user) }
+ let(:source) { nil }
+
+ subject { user.notification_settings_for(source) }
+
+ context 'when source is nil' do
+ it 'returns a blank global notification settings object' do
+ expect(subject.source).to eq(nil)
+ expect(subject.notification_email).to eq(nil)
+ expect(subject.level).to eq('global')
+ end
+ end
+
+ context 'when source is a Group' do
+ let(:group) { create(:group) }
+
+ subject { user.notification_settings_for(group, inherit: true) }
+
+ context 'when group has no existing notification settings' do
+ context 'when group has no ancestors' do
+ it 'will be a default Global notification setting' do
+ expect(subject.notification_email).to eq(nil)
+ expect(subject.level).to eq('global')
+ end
+ end
+
+ context 'when group has ancestors' do
+ context 'when an ancestor has a level other than Global' do
+ let(:ancestor) { create(:group) }
+ let(:group) { create(:group, parent: ancestor) }
+
+ before do
+ create(:notification_setting, user: user, source: ancestor, level: 'participating', notification_email: 'ancestor@example.com')
+ end
+
+ it 'has the same level set' do
+ expect(subject.level).to eq('participating')
+ end
+
+ it 'has the same email set' do
+ expect(subject.notification_email).to eq('ancestor@example.com')
+ end
+
+ context 'when inherit is false' do
+ subject { user.notification_settings_for(group) }
+
+ it 'does not inherit settings' do
+ expect(subject.notification_email).to eq(nil)
+ expect(subject.level).to eq('global')
+ end
+ end
+ end
+
+ context 'when an ancestor has a Global level but has an email set' do
+ let(:grand_ancestor) { create(:group) }
+ let(:ancestor) { create(:group, parent: grand_ancestor) }
+ let(:group) { create(:group, parent: ancestor) }
+
+ before do
+ create(:notification_setting, user: user, source: grand_ancestor, level: 'participating', notification_email: 'grand@example.com')
+ create(:notification_setting, user: user, source: ancestor, level: 'global', notification_email: 'ancestor@example.com')
+ end
+
+ it 'has the same email set' do
+ expect(subject.level).to eq('global')
+ expect(subject.notification_email).to eq('ancestor@example.com')
+ end
+ end
+ end
+ end
+ end
+ end
+
describe '#notification_email_for' do
let(:user) { create(:user) }
let(:group) { create(:group) }
diff --git a/spec/services/application_settings/update_service_spec.rb b/spec/services/application_settings/update_service_spec.rb
index ea7ea02cee3..6e1fdb7aad0 100644
--- a/spec/services/application_settings/update_service_spec.rb
+++ b/spec/services/application_settings/update_service_spec.rb
@@ -147,35 +147,44 @@ describe ApplicationSettings::UpdateService do
using RSpec::Parameterized::TableSyntax
where(:params_performance_bar_enabled,
- :params_performance_bar_allowed_group_path,
- :previous_performance_bar_allowed_group_id,
- :expected_performance_bar_allowed_group_id) do
- true | '' | nil | nil
- true | '' | 42_000_000 | nil
- true | nil | nil | nil
- true | nil | 42_000_000 | nil
- true | 'foo' | nil | nil
- true | 'foo' | 42_000_000 | nil
- true | 'group_a' | nil | 42_000_000
- true | 'group_b' | 42_000_000 | 43_000_000
- true | 'group_a' | 42_000_000 | 42_000_000
- false | '' | nil | nil
- false | '' | 42_000_000 | nil
- false | nil | nil | nil
- false | nil | 42_000_000 | nil
- false | 'foo' | nil | nil
- false | 'foo' | 42_000_000 | nil
- false | 'group_a' | nil | nil
- false | 'group_b' | 42_000_000 | nil
- false | 'group_a' | 42_000_000 | nil
+ :params_performance_bar_allowed_group_path,
+ :previous_performance_bar_allowed_group_id,
+ :expected_performance_bar_allowed_group_id,
+ :expected_valid) do
+ true | '' | nil | nil | true
+ true | '' | 42_000_000 | nil | true
+ true | nil | nil | nil | true
+ true | nil | 42_000_000 | nil | true
+ true | 'foo' | nil | nil | false
+ true | 'foo' | 42_000_000 | 42_000_000 | false
+ true | 'group_a' | nil | 42_000_000 | true
+ true | 'group_b' | 42_000_000 | 43_000_000 | true
+ true | 'group_b/' | 42_000_000 | 43_000_000 | true
+ true | 'group_a' | 42_000_000 | 42_000_000 | true
+ false | '' | nil | nil | true
+ false | '' | 42_000_000 | nil | true
+ false | nil | nil | nil | true
+ false | nil | 42_000_000 | nil | true
+ false | 'foo' | nil | nil | true
+ false | 'foo' | 42_000_000 | nil | true
+ false | 'group_a' | nil | nil | true
+ false | 'group_b' | 42_000_000 | nil | true
+ false | 'group_a' | 42_000_000 | nil | true
+ nil | '' | nil | nil | true
+ nil | 'foo' | nil | nil | false
+ nil | 'group_a' | nil | 42_000_000 | true
end
with_them do
let(:params) do
{
- performance_bar_enabled: params_performance_bar_enabled,
performance_bar_allowed_group_path: params_performance_bar_allowed_group_path
- }
+ }.tap do |params_hash|
+ # Treat nil in the table as missing
+ unless params_performance_bar_enabled.nil?
+ params_hash[:performance_bar_enabled] = params_performance_bar_enabled
+ end
+ end
end
before do
@@ -202,6 +211,14 @@ describe ApplicationSettings::UpdateService do
.not_to change(application_settings, :performance_bar_allowed_group_id)
end
end
+
+ it 'adds errors to the model for invalid params' do
+ expect(subject.execute).to eq(expected_valid)
+
+ unless expected_valid
+ expect(application_settings.errors[:performance_bar_allowed_group_id]).to be_present
+ end
+ end
end
context 'when :performance_bar_allowed_group_path is not present' do
@@ -221,7 +238,7 @@ describe ApplicationSettings::UpdateService do
let(:group) { create(:group) }
let(:params) { { performance_bar_allowed_group_path: group.full_path } }
- it 'implicitely defaults to true' do
+ it 'implicitly defaults to true' do
expect { subject.execute }
.to change(application_settings, :performance_bar_allowed_group_id)
.from(nil).to(group.id)