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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-04-25 06:09:02 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-04-25 06:09:02 +0300
commit26891eec2cea8ca5e75a96a50d8785da6042cc5a (patch)
treeb732a7a69a0a4cf44afa24bf9d6eddfa5c3805fd
parent15e5a05bcd3525dd6c046dca2682b04532ba9bd1 (diff)
Add latest changes from gitlab-org/gitlab@master
-rw-r--r--.gitlab/ci/rules.gitlab-ci.yml43
-rw-r--r--.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml13
-rw-r--r--.rubocop_todo/rspec/missing_feature_category.yml1
-rw-r--r--app/assets/javascripts/profile/preferences/components/profile_preferences.vue4
-rw-r--r--app/controllers/concerns/product_analytics_tracking.rb20
-rw-r--r--app/controllers/concerns/redis_tracking.rb49
-rw-r--r--app/controllers/concerns/snippets_actions.rb4
-rw-r--r--app/controllers/concerns/wiki_actions.rb4
-rw-r--r--app/controllers/profiles/preferences_controller.rb4
-rw-r--r--app/controllers/projects/pipelines_controller.rb12
-rw-r--r--app/controllers/search_controller.rb2
-rw-r--r--app/controllers/users_controller.rb7
-rw-r--r--app/models/user.rb61
-rw-r--r--app/services/users/update_service.rb29
-rw-r--r--app/views/profiles/preferences/show.html.haml16
-rw-r--r--app/views/users/show.html.haml2
-rw-r--r--config/feature_flags/development/disable_follow_users.yml8
-rw-r--r--db/docs/batched_background_migrations/populate_vulnerability_dismissal_fields.yml6
-rw-r--r--db/migrate/20230328165313_add_disabled_following_to_user_preferences.rb9
-rw-r--r--db/post_migrate/20230410123709_validate_fk_projects_creator_id.rb15
-rw-r--r--db/post_migrate/20230411011959_add_temp_index_to_null_dismissed_info_vulnerabilities.rb18
-rw-r--r--db/post_migrate/20230412104514_add_index_to_group_group_links.rb16
-rw-r--r--db/post_migrate/20230412185837_queue_populate_vulnerability_dismissal_fields.rb29
-rw-r--r--db/schema_migrations/202303281653131
-rw-r--r--db/schema_migrations/202304101237091
-rw-r--r--db/schema_migrations/202304110119591
-rw-r--r--db/schema_migrations/202304121045141
-rw-r--r--db/schema_migrations/202304121858371
-rw-r--r--db/structure.sql7
-rw-r--r--lib/api/entities/user.rb17
-rw-r--r--lib/api/users.rb5
-rw-r--r--lib/gitlab/background_migration/populate_vulnerability_dismissal_fields.rb90
-rw-r--r--locale/gitlab.pot12
-rw-r--r--rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb33
-rw-r--r--spec/controllers/concerns/redis_tracking_spec.rb135
-rw-r--r--spec/controllers/profiles/preferences_controller_spec.rb28
-rw-r--r--spec/frontend/fixtures/jobs.rb7
-rw-r--r--spec/lib/api/entities/user_spec.rb52
-rw-r--r--spec/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields_spec.rb114
-rw-r--r--spec/migrations/20230412185837_queue_populate_vulnerability_dismissal_fields_spec.rb37
-rw-r--r--spec/models/user_spec.rb81
-rw-r--r--spec/requests/api/users_spec.rb14
-rw-r--r--spec/requests/users_controller_spec.rb29
-rw-r--r--spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb17
-rw-r--r--spec/services/users/update_service_spec.rb43
45 files changed, 790 insertions, 308 deletions
diff --git a/.gitlab/ci/rules.gitlab-ci.yml b/.gitlab/ci/rules.gitlab-ci.yml
index 321993799e4..5ba0bc20a75 100644
--- a/.gitlab/ci/rules.gitlab-ci.yml
+++ b/.gitlab/ci/rules.gitlab-ci.yml
@@ -821,10 +821,23 @@
when: never
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-merge-request-labels-run-review-app
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *ci-build-images-patterns
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *code-qa-patterns
+ # Rules to support .qa:rules:package-and-test-ee
+ - <<: *if-merge-request
+ changes: *dependency-patterns
+ - <<: *if-merge-request-labels-run-all-e2e
+ - <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e
+ changes: *feature-flag-development-config-patterns
+ - <<: *if-merge-request
+ changes: *feature-flag-development-config-patterns
+ - <<: *if-merge-request
+ changes: *nodejs-patterns
+ - <<: *if-merge-request
+ changes: *ci-qa-patterns
+ - <<: *if-force-ci
.build-images:rules:build-qa-image:
rules:
@@ -843,7 +856,6 @@
- <<: *if-dot-com-gitlab-org-schedule
variables:
ARCH: amd64,arm64
- - <<: *if-force-ci
- <<: *if-ruby2-branch
.build-images:rules:build-qa-image-as-if-foss:
@@ -872,7 +884,6 @@
- <<: *if-merge-request-targeting-stable-branch
- <<: *if-ruby2-branch
- <<: *if-merge-request-labels-run-review-app
- - <<: *if-merge-request-labels-run-all-e2e
- <<: *if-auto-deploy-branches
- !reference [".releases:rules:canonical-dot-com-gitlab-stable-branch-only-setup-test-env-patterns", rules]
- <<: *if-default-refs
@@ -1275,6 +1286,8 @@
##########
.notify:rules:create-issues-for-failing-tests:
rules:
+ - <<: *if-not-canonical-namespace
+ when: never
# Don't report child pipeline failures
- if: '$CI_PIPELINE_SOURCE == "parent_pipeline"'
when: never
@@ -1331,14 +1344,32 @@
when: never
- <<: *if-merge-request-targeting-stable-branch
allow_failure: true
- - <<: *if-dot-com-gitlab-org-and-security-merge-request
+ - <<: *if-merge-request
changes: *code-backstage-qa-patterns
allow_failure: true
- <<: *if-dot-com-gitlab-org-schedule
allow_failure: true
+ - <<: *if-ruby2-branch
+ # Rules to support .qa:rules:package-and-test-ee
+ - <<: *if-merge-request
+ changes: *dependency-patterns
+ allow_failure: true
+ - <<: *if-merge-request-labels-run-all-e2e
+ allow_failure: true
+ - <<: *if-dot-com-gitlab-org-and-security-merge-request-manual-ff-package-and-e2e
+ changes: *feature-flag-development-config-patterns
+ allow_failure: true
+ - <<: *if-merge-request
+ changes: *feature-flag-development-config-patterns
+ allow_failure: true
+ - <<: *if-merge-request
+ changes: *nodejs-patterns
+ allow_failure: true
+ - <<: *if-merge-request
+ changes: *ci-qa-patterns
+ allow_failure: true
- <<: *if-force-ci
allow_failure: true
- - <<: *if-ruby2-branch
.qa:rules:package-and-test-common:
rules:
diff --git a/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml b/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml
deleted file mode 100644
index f5433dfd320..00000000000
--- a/.rubocop_todo/gitlab/deprecate_track_redis_hll_event.yml
+++ /dev/null
@@ -1,13 +0,0 @@
----
-Gitlab/DeprecateTrackRedisHLLEvent:
- Exclude:
- - 'app/controllers/concerns/snippets_actions.rb'
- - 'app/controllers/concerns/wiki_actions.rb'
- - 'app/controllers/projects/blob_controller.rb'
- - 'app/controllers/projects/pipelines_controller.rb'
- - 'ee/app/controllers/admin/audit_logs_controller.rb'
- - 'ee/app/controllers/admin/credentials_controller.rb'
- - 'ee/app/controllers/groups/analytics/ci_cd_analytics_controller.rb'
- - 'ee/app/controllers/groups/audit_events_controller.rb'
- - 'ee/app/controllers/groups/epic_boards_controller.rb'
- - 'spec/controllers/concerns/redis_tracking_spec.rb'
diff --git a/.rubocop_todo/rspec/missing_feature_category.yml b/.rubocop_todo/rspec/missing_feature_category.yml
index 04cf1ec1a07..2941f2999d3 100644
--- a/.rubocop_todo/rspec/missing_feature_category.yml
+++ b/.rubocop_todo/rspec/missing_feature_category.yml
@@ -5494,7 +5494,6 @@ RSpec/MissingFeatureCategory:
- 'spec/rubocop/cop/gitlab/change_timezone_spec.rb'
- 'spec/rubocop/cop/gitlab/const_get_inherit_false_spec.rb'
- 'spec/rubocop/cop/gitlab/delegate_predicate_methods_spec.rb'
- - 'spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb'
- 'spec/rubocop/cop/gitlab/event_store_subscriber_spec.rb'
- 'spec/rubocop/cop/gitlab/except_spec.rb'
- 'spec/rubocop/cop/gitlab/feature_available_usage_spec.rb'
diff --git a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue
index d0d947ddd6e..164ec46cdb9 100644
--- a/app/assets/javascripts/profile/preferences/components/profile_preferences.vue
+++ b/app/assets/javascripts/profile/preferences/components/profile_preferences.vue
@@ -131,6 +131,10 @@ export default {
:message-url="view.message_url"
:config="$options.integrationViewConfigs[view.name]"
/>
+ </div>
+
+ <div class="col-lg-4"></div>
+ <div class="col-lg-8">
<hr />
</div>
<div class="col-sm-12 js-hide-when-nothing-matches-search">
diff --git a/app/controllers/concerns/product_analytics_tracking.rb b/app/controllers/concerns/product_analytics_tracking.rb
index fc33770b4d8..5424354b92c 100644
--- a/app/controllers/concerns/product_analytics_tracking.rb
+++ b/app/controllers/concerns/product_analytics_tracking.rb
@@ -2,7 +2,6 @@
module ProductAnalyticsTracking
include Gitlab::Tracking::Helpers
- include RedisTracking
extend ActiveSupport::Concern
class_methods do
@@ -39,4 +38,23 @@ module ProductAnalyticsTracking
**optional_arguments
)
end
+
+ def track_unique_redis_hll_event(event_name, &block)
+ custom_id = block ? yield(self) : nil
+
+ unique_id = custom_id || visitor_id
+
+ return unless unique_id
+
+ Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id)
+ end
+
+ def visitor_id
+ return cookies[:visitor_id] if cookies[:visitor_id].present?
+ return unless current_user
+
+ uuid = SecureRandom.uuid
+ cookies[:visitor_id] = { value: uuid, expires: 24.months }
+ uuid
+ end
end
diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb
deleted file mode 100644
index 445e72b8266..00000000000
--- a/app/controllers/concerns/redis_tracking.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-# frozen_string_literal: true
-
-# Example:
-#
-# # In controller include module
-# # Track event for index action
-#
-# include RedisTracking
-#
-# track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score'
-#
-# You can also pass custom conditions using `if:`, using the same format as with Rails callbacks.
-# You can also pass an optional block that calculates and returns a custom id to track.
-module RedisTracking
- include Gitlab::Tracking::Helpers
- extend ActiveSupport::Concern
-
- class_methods do
- def track_redis_hll_event(*controller_actions, name:, if: nil, &block)
- custom_conditions = Array.wrap(binding.local_variable_get('if'))
- conditions = [:trackable_html_request?, *custom_conditions]
-
- after_action only: controller_actions, if: conditions do
- track_unique_redis_hll_event(name, &block)
- end
- end
- end
-
- private
-
- def track_unique_redis_hll_event(event_name, &block)
- custom_id = block ? yield(self) : nil
-
- unique_id = custom_id || visitor_id
-
- return unless unique_id
-
- Gitlab::UsageDataCounters::HLLRedisCounter.track_event(event_name, values: unique_id)
- end
-
- def visitor_id
- return cookies[:visitor_id] if cookies[:visitor_id].present?
- return unless current_user
-
- uuid = SecureRandom.uuid
- cookies[:visitor_id] = { value: uuid, expires: 24.months }
- uuid
- end
-end
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index 1bb81a46e50..62c5aee16e4 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -9,13 +9,13 @@ module SnippetsActions
include Gitlab::NoteableMetadata
include Snippets::SendBlob
include SnippetsSort
- include RedisTracking
+ include ProductAnalyticsTracking
included do
skip_before_action :verify_authenticity_token,
if: -> { action_name == 'show' && js_request? }
- track_redis_hll_event :show, name: 'i_snippets_show'
+ track_event :show, name: 'i_snippets_show'
respond_to :html
end
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index a007abacacc..265cf2a7698 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -5,7 +5,7 @@ module WikiActions
include PreviewMarkdown
include SendsBlob
include Gitlab::Utils::StrongMemoize
- include RedisTracking
+ include ProductAnalyticsTracking
extend ActiveSupport::Concern
RESCUE_GIT_TIMEOUTS_IN = %w[show edit history diff pages].freeze
@@ -46,7 +46,7 @@ module WikiActions
end
end
- track_redis_hll_event :show, name: 'wiki_action'
+ track_event :show, name: 'wiki_action'
helper_method :view_file_button, :diff_file_html_data
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index 7786bad4251..b7e8432c039 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -37,7 +37,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
end
def preferences_param_names
- [
+ preferences_param_names = [
:color_scheme_id,
:diffs_deletion_color,
:diffs_addition_color,
@@ -60,6 +60,8 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:use_legacy_web_ide,
:use_new_navigation
]
+ preferences_param_names << :enabled_following if ::Feature.enabled?(:disable_follow_users, user)
+ preferences_param_names
end
end
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index a8107a46b4f..30f582f90a5 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -2,7 +2,7 @@
class Projects::PipelinesController < Projects::ApplicationController
include ::Gitlab::Utils::StrongMemoize
- include RedisTracking
+ include ProductAnalyticsTracking
include ProductAnalyticsTracking
include ProjectStatsRefreshConflictsGuard
@@ -34,11 +34,11 @@ class Projects::PipelinesController < Projects::ApplicationController
label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly',
destinations: %i[redis_hll snowplow]
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_pipelines', if: -> { should_track_ci_cd_pipelines? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', if: -> { should_track_ci_cd_deployment_frequency? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_lead_time', if: -> { should_track_ci_cd_lead_time? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_time_to_restore_service', if: -> { should_track_ci_cd_time_to_restore_service? }
- track_redis_hll_event :charts, name: 'p_analytics_ci_cd_change_failure_rate', if: -> { should_track_ci_cd_change_failure_rate? }
+ track_event :charts, name: 'p_analytics_ci_cd_pipelines', conditions: -> { should_track_ci_cd_pipelines? }
+ track_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', conditions: -> { should_track_ci_cd_deployment_frequency? }
+ track_event :charts, name: 'p_analytics_ci_cd_lead_time', conditions: -> { should_track_ci_cd_lead_time? }
+ track_event :charts, name: 'p_analytics_ci_cd_time_to_restore_service', conditions: -> { should_track_ci_cd_time_to_restore_service? }
+ track_event :charts, name: 'p_analytics_ci_cd_change_failure_rate', conditions: -> { should_track_ci_cd_change_failure_rate? }
wrap_parameters Ci::Pipeline
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index 688c56e56e0..a3c6499bc54 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -3,7 +3,7 @@
class SearchController < ApplicationController
include ControllerWithCrossProjectAccessCheck
include SearchHelper
- include RedisTracking
+ include ProductAnalyticsTracking
include ProductAnalyticsTracking
include SearchRateLimitable
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index e4354eaa452..02b29d42313 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -191,7 +191,12 @@ class UsersController < ApplicationController
def follow
followee = current_user.follow(user)
- flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any?
+ if followee
+ flash[:alert] = followee.errors.full_messages.join(', ') if followee&.errors&.any?
+ else
+ flash[:alert] = s_('Action not allowed.')
+ end
+
redirect_path = referer_path(request) || @user
redirect_to redirect_path
diff --git a/app/models/user.rb b/app/models/user.rb
index 0aa509e58d7..cb1fbd97a00 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -349,29 +349,30 @@ class User < ApplicationRecord
# User's role
enum role: { software_developer: 0, development_team_lead: 1, devops_engineer: 2, systems_administrator: 3, security_analyst: 4, data_analyst: 5, product_manager: 6, product_designer: 7, other: 8 }, _suffix: true
- delegate :notes_filter_for,
- :set_notes_filter,
- :first_day_of_week, :first_day_of_week=,
- :timezone, :timezone=,
- :time_display_relative, :time_display_relative=,
- :time_format_in_24h, :time_format_in_24h=,
- :show_whitespace_in_diffs, :show_whitespace_in_diffs=,
- :view_diffs_file_by_file, :view_diffs_file_by_file=,
- :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=,
- :tab_width, :tab_width=,
- :sourcegraph_enabled, :sourcegraph_enabled=,
- :gitpod_enabled, :gitpod_enabled=,
- :setup_for_company, :setup_for_company=,
- :render_whitespace_in_code, :render_whitespace_in_code=,
- :markdown_surround_selection, :markdown_surround_selection=,
- :markdown_automatic_lists, :markdown_automatic_lists=,
- :diffs_deletion_color, :diffs_deletion_color=,
- :diffs_addition_color, :diffs_addition_color=,
- :use_legacy_web_ide, :use_legacy_web_ide=,
- :use_new_navigation, :use_new_navigation=,
- :pinned_nav_items, :pinned_nav_items=,
- :achievements_enabled, :achievements_enabled=,
- to: :user_preference
+ delegate :notes_filter_for,
+ :set_notes_filter,
+ :first_day_of_week, :first_day_of_week=,
+ :timezone, :timezone=,
+ :time_display_relative, :time_display_relative=,
+ :time_format_in_24h, :time_format_in_24h=,
+ :show_whitespace_in_diffs, :show_whitespace_in_diffs=,
+ :view_diffs_file_by_file, :view_diffs_file_by_file=,
+ :pass_user_identities_to_ci_jwt, :pass_user_identities_to_ci_jwt=,
+ :tab_width, :tab_width=,
+ :sourcegraph_enabled, :sourcegraph_enabled=,
+ :gitpod_enabled, :gitpod_enabled=,
+ :setup_for_company, :setup_for_company=,
+ :render_whitespace_in_code, :render_whitespace_in_code=,
+ :markdown_surround_selection, :markdown_surround_selection=,
+ :markdown_automatic_lists, :markdown_automatic_lists=,
+ :diffs_deletion_color, :diffs_deletion_color=,
+ :diffs_addition_color, :diffs_addition_color=,
+ :use_legacy_web_ide, :use_legacy_web_ide=,
+ :use_new_navigation, :use_new_navigation=,
+ :pinned_nav_items, :pinned_nav_items=,
+ :achievements_enabled, :achievements_enabled=,
+ :enabled_following, :enabled_following=,
+ to: :user_preference
delegate :path, to: :namespace, allow_nil: true, prefix: true
delegate :job_title, :job_title=, to: :user_detail, allow_nil: true
@@ -1694,7 +1695,7 @@ class User < ApplicationRecord
end
def follow(user)
- return false if self.id == user.id
+ return false unless following_users_allowed?(user)
begin
followee = Users::UserFollowUser.create(follower_id: self.id, followee_id: user.id)
@@ -1713,6 +1714,18 @@ class User < ApplicationRecord
end
end
+ def following_users_allowed?(user)
+ return false if self.id == user.id
+
+ following_users_enabled? && user.following_users_enabled?
+ end
+
+ def following_users_enabled?
+ return true unless ::Feature.enabled?(:disable_follow_users, self)
+
+ enabled_following
+ end
+
def forkable_namespaces
strong_memoize(:forkable_namespaces) do
personal_namespace = Namespace.where(id: namespace_id)
diff --git a/app/services/users/update_service.rb b/app/services/users/update_service.rb
index 96018db5974..36c41c03303 100644
--- a/app/services/users/update_service.rb
+++ b/app/services/users/update_service.rb
@@ -6,6 +6,7 @@ module Users
attr_reader :user, :identity_params
ATTRS_REQUIRING_PASSWORD_CHECK = %w[email].freeze
+ BATCH_SIZE = 100
def initialize(current_user, params = {})
@current_user = current_user
@@ -34,7 +35,7 @@ module Users
reset_unconfirmed_email
if @user.save(validate: validate) && update_status
- notify_success(user_exists)
+ after_update(user_exists)
else
messages = @user.errors.full_messages + Array(@user.status&.errors&.full_messages)
error(messages.uniq.join('. '))
@@ -80,8 +81,6 @@ module Users
def notify_success(user_exists)
notify_new_user(@user, nil) unless user_exists
-
- success
end
def discard_read_only_attributes
@@ -118,6 +117,30 @@ module Users
def provider_params
identity_params.slice(*provider_attributes)
end
+
+ def after_update(user_exists)
+ notify_success(user_exists)
+ remove_followers_and_followee! if ::Feature.enabled?(:disable_follow_users, user)
+
+ success
+ end
+
+ def remove_followers_and_followee!
+ return false unless user.user_preference.enabled_following_previously_changed?(from: true, to: false)
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ loop do
+ inner_query = Users::UserFollowUser
+ .where(follower_id: user.id).or(Users::UserFollowUser.where(followee_id: user.id))
+ .select(:follower_id, :followee_id)
+ .limit(BATCH_SIZE)
+
+ deleted_records = Users::UserFollowUser.where('(follower_id, followee_id) IN (?)', inner_query).delete_all
+
+ break if deleted_records == 0
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
end
end
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index c16469bbf79..8fb66cb3cd9 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -170,5 +170,21 @@
- if Feature.enabled?(:user_time_settings)
.form-group
= f.gitlab_ui_checkbox_component :time_format_in_24h, s_('Preferences|Display time in 24-hour format')
+ - if Feature.enabled?(:disable_follow_users, @user)
+ .row.js-preferences-form.js-search-settings-section
+ .col-sm-12
+ %hr
+ .col-lg-4.profile-settings-sidebar#disabled_following
+ %h4.gl-mt-0
+ = s_('Preferences|Disable follow users feature')
+ %p
+ = s_('Preferences|Turns off the ability to follow or be followed by other users.')
+ = succeed '.' do
+ = link_to _('Learn more'), help_page_path('user/profile/index', anchor: 'follow-users'), target: '_blank', rel: 'noopener noreferrer'
+ .col-lg-8
+ .form-group
+ = f.gitlab_ui_checkbox_component :enabled_following,
+ s_('Preferences|Disable follow users')
+
#js-profile-preferences-app{ data: data_attributes }
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 70dccc4821b..e26b4de7b20 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -36,7 +36,7 @@
= render Pajamas::ButtonComponent.new(href: [:admin, @user],
icon: 'user',
button_options: { class: 'gl-flex-grow-1 gl-mx-1 has-tooltip', title: s_('UserProfile|View user in admin area'), data: {toggle: 'tooltip', placement: 'bottom', container: 'body'}})
- - if current_user && current_user.id != @user.id
+ - if current_user && current_user.following_users_allowed?(@user)
- if current_user.following?(@user)
= form_tag user_unfollow_path(@user, :json), class: link_classes + 'gl-display-inline-block' do
= render Pajamas::ButtonComponent.new(type: :submit, button_options: { class: 'gl-w-full', data: { track_action: 'click_button', track_label: 'unfollow_from_profile' } }) do
diff --git a/config/feature_flags/development/disable_follow_users.yml b/config/feature_flags/development/disable_follow_users.yml
new file mode 100644
index 00000000000..9788ca520fd
--- /dev/null
+++ b/config/feature_flags/development/disable_follow_users.yml
@@ -0,0 +1,8 @@
+---
+name: disable_follow_users
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/116023
+rollout_issue_url:
+milestone: '16.0'
+type: development
+group: group::authentication and authorization
+default_enabled: false
diff --git a/db/docs/batched_background_migrations/populate_vulnerability_dismissal_fields.yml b/db/docs/batched_background_migrations/populate_vulnerability_dismissal_fields.yml
new file mode 100644
index 00000000000..b51a6ab37d0
--- /dev/null
+++ b/db/docs/batched_background_migrations/populate_vulnerability_dismissal_fields.yml
@@ -0,0 +1,6 @@
+---
+migration_job_name: PopulateVulnerabilityDismissalFields
+description: This populates missing dismissal info for vulnerabilities.
+feature_category: vulnerability_management
+introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/issues/405032
+milestone: 15.11
diff --git a/db/migrate/20230328165313_add_disabled_following_to_user_preferences.rb b/db/migrate/20230328165313_add_disabled_following_to_user_preferences.rb
new file mode 100644
index 00000000000..0841829c957
--- /dev/null
+++ b/db/migrate/20230328165313_add_disabled_following_to_user_preferences.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class AddDisabledFollowingToUserPreferences < Gitlab::Database::Migration[2.1]
+ enable_lock_retries!
+
+ def change
+ add_column :user_preferences, :enabled_following, :boolean, default: true, null: false
+ end
+end
diff --git a/db/post_migrate/20230410123709_validate_fk_projects_creator_id.rb b/db/post_migrate/20230410123709_validate_fk_projects_creator_id.rb
new file mode 100644
index 00000000000..7f522cd92ef
--- /dev/null
+++ b/db/post_migrate/20230410123709_validate_fk_projects_creator_id.rb
@@ -0,0 +1,15 @@
+# frozen_string_literal: true
+
+class ValidateFkProjectsCreatorId < Gitlab::Database::Migration[2.1]
+ TABLE_NAME = :projects
+ COLUMN_NAME = :creator_id
+ FK_NAME = :fk_03ec10b0d3
+
+ def up
+ validate_foreign_key TABLE_NAME, COLUMN_NAME, name: FK_NAME
+ end
+
+ def down
+ # no-op
+ end
+end
diff --git a/db/post_migrate/20230411011959_add_temp_index_to_null_dismissed_info_vulnerabilities.rb b/db/post_migrate/20230411011959_add_temp_index_to_null_dismissed_info_vulnerabilities.rb
new file mode 100644
index 00000000000..fe62260cb8f
--- /dev/null
+++ b/db/post_migrate/20230411011959_add_temp_index_to_null_dismissed_info_vulnerabilities.rb
@@ -0,0 +1,18 @@
+# frozen_string_literal: true
+
+class AddTempIndexToNullDismissedInfoVulnerabilities < Gitlab::Database::Migration[2.1]
+ INDEX_NAME = 'tmp_index_vulnerability_dismissal_info'
+
+ disable_ddl_transaction!
+
+ def up
+ # Temporary index to be removed in https://gitlab.com/gitlab-org/gitlab/-/issues/406653
+ add_concurrent_index :vulnerabilities, :id,
+ where: "state = 2 AND (dismissed_at IS NULL OR dismissed_by_id IS NULL)",
+ name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name :vulnerabilities, INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20230412104514_add_index_to_group_group_links.rb b/db/post_migrate/20230412104514_add_index_to_group_group_links.rb
new file mode 100644
index 00000000000..655bff43b3c
--- /dev/null
+++ b/db/post_migrate/20230412104514_add_index_to_group_group_links.rb
@@ -0,0 +1,16 @@
+# frozen_string_literal: true
+
+class AddIndexToGroupGroupLinks < Gitlab::Database::Migration[2.1]
+ disable_ddl_transaction!
+
+ INDEX_NAME = 'index_group_group_links_on_shared_with_group_and_group_access'
+ TABLE_NAME = :group_group_links
+
+ def up
+ add_concurrent_index TABLE_NAME, [:shared_with_group_id, :group_access], name: INDEX_NAME
+ end
+
+ def down
+ remove_concurrent_index_by_name TABLE_NAME, name: INDEX_NAME
+ end
+end
diff --git a/db/post_migrate/20230412185837_queue_populate_vulnerability_dismissal_fields.rb b/db/post_migrate/20230412185837_queue_populate_vulnerability_dismissal_fields.rb
new file mode 100644
index 00000000000..d0924e8fdf8
--- /dev/null
+++ b/db/post_migrate/20230412185837_queue_populate_vulnerability_dismissal_fields.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class QueuePopulateVulnerabilityDismissalFields < Gitlab::Database::Migration[2.1]
+ MIGRATION = "PopulateVulnerabilityDismissalFields"
+ DELAY_INTERVAL = 2.minutes
+ BATCH_SIZE = 1_000
+ MAX_BATCH_SIZE = 10_000
+ SUB_BATCH_SIZE = 200
+
+ disable_ddl_transaction!
+
+ restrict_gitlab_migration gitlab_schema: :gitlab_main
+
+ def up
+ queue_batched_background_migration(
+ MIGRATION,
+ :vulnerabilities,
+ :id,
+ job_interval: DELAY_INTERVAL,
+ batch_size: BATCH_SIZE,
+ max_batch_size: MAX_BATCH_SIZE,
+ sub_batch_size: SUB_BATCH_SIZE
+ )
+ end
+
+ def down
+ delete_batched_background_migration(MIGRATION, :vulnerabilities, :id, [])
+ end
+end
diff --git a/db/schema_migrations/20230328165313 b/db/schema_migrations/20230328165313
new file mode 100644
index 00000000000..a9ddacf5e48
--- /dev/null
+++ b/db/schema_migrations/20230328165313
@@ -0,0 +1 @@
+ea52a177f82cc872b1f38490ff17bf166a885a4df7cf2c6c99dc7b1cccd14d33 \ No newline at end of file
diff --git a/db/schema_migrations/20230410123709 b/db/schema_migrations/20230410123709
new file mode 100644
index 00000000000..2cf17b397dd
--- /dev/null
+++ b/db/schema_migrations/20230410123709
@@ -0,0 +1 @@
+d1e5678ea93b5b8f5d76b0332e79934badbaa84546754b66916bb2816cfaf307 \ No newline at end of file
diff --git a/db/schema_migrations/20230411011959 b/db/schema_migrations/20230411011959
new file mode 100644
index 00000000000..830774fc029
--- /dev/null
+++ b/db/schema_migrations/20230411011959
@@ -0,0 +1 @@
+31e8f1f8d65ea821efb158bd59657776de54ecbd5e10d3a64c5afe37706c5388 \ No newline at end of file
diff --git a/db/schema_migrations/20230412104514 b/db/schema_migrations/20230412104514
new file mode 100644
index 00000000000..50c4a36e790
--- /dev/null
+++ b/db/schema_migrations/20230412104514
@@ -0,0 +1 @@
+5a1245d37e10d03320a3cd8afda34226e54c6f6641c3abedfcb1333ea6ed69a0 \ No newline at end of file
diff --git a/db/schema_migrations/20230412185837 b/db/schema_migrations/20230412185837
new file mode 100644
index 00000000000..d4c425e68fa
--- /dev/null
+++ b/db/schema_migrations/20230412185837
@@ -0,0 +1 @@
+f9c342816a6c656b1c13b8e9d0a771c1ee6a9847c03a76577c662f9cf238ad03 \ No newline at end of file
diff --git a/db/structure.sql b/db/structure.sql
index 1ba0ff9b13a..c366c1e3bb6 100644
--- a/db/structure.sql
+++ b/db/structure.sql
@@ -23487,6 +23487,7 @@ CREATE TABLE user_preferences (
achievements_enabled boolean DEFAULT true NOT NULL,
pinned_nav_items jsonb DEFAULT '{}'::jsonb NOT NULL,
pass_user_identities_to_ci_jwt boolean DEFAULT false NOT NULL,
+ enabled_following boolean DEFAULT true NOT NULL,
CONSTRAINT check_89bf269f41 CHECK ((char_length(diffs_deletion_color) <= 7)),
CONSTRAINT check_d07ccd35f7 CHECK ((char_length(diffs_addition_color) <= 7))
);
@@ -30780,6 +30781,8 @@ CREATE UNIQUE INDEX index_group_deploy_tokens_on_group_and_deploy_token_ids ON g
CREATE UNIQUE INDEX index_group_group_links_on_shared_group_and_shared_with_group ON group_group_links USING btree (shared_group_id, shared_with_group_id);
+CREATE INDEX index_group_group_links_on_shared_with_group_and_group_access ON group_group_links USING btree (shared_with_group_id, group_access);
+
CREATE INDEX index_group_group_links_on_shared_with_group_and_shared_group ON group_group_links USING btree (shared_with_group_id, shared_group_id);
CREATE INDEX index_group_import_states_on_group_id ON group_import_states USING btree (group_id);
@@ -33010,6 +33013,8 @@ CREATE INDEX tmp_index_on_vulnerabilities_non_dismissed ON vulnerabilities USING
CREATE INDEX tmp_index_project_statistics_cont_registry_size ON project_statistics USING btree (project_id) WHERE (container_registry_size = 0);
+CREATE INDEX tmp_index_vulnerability_dismissal_info ON vulnerabilities USING btree (id) WHERE ((state = 2) AND ((dismissed_at IS NULL) OR (dismissed_by_id IS NULL)));
+
CREATE INDEX tmp_index_vulnerability_overlong_title_html ON vulnerabilities USING btree (id) WHERE (length(title_html) > 800);
CREATE UNIQUE INDEX uniq_pkgs_deb_grp_architectures_on_distribution_id_and_name ON packages_debian_group_architectures USING btree (distribution_id, name);
@@ -34470,7 +34475,7 @@ ALTER TABLE ONLY design_management_designs_versions
ADD CONSTRAINT fk_03c671965c FOREIGN KEY (design_id) REFERENCES design_management_designs(id) ON DELETE CASCADE;
ALTER TABLE ONLY projects
- ADD CONSTRAINT fk_03ec10b0d3 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL NOT VALID;
+ ADD CONSTRAINT fk_03ec10b0d3 FOREIGN KEY (creator_id) REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE ONLY analytics_dashboards_pointers
ADD CONSTRAINT fk_05d96922bd FOREIGN KEY (target_project_id) REFERENCES projects(id) ON DELETE CASCADE;
diff --git a/lib/api/entities/user.rb b/lib/api/entities/user.rb
index 884f0f75d7a..689996be7fc 100644
--- a/lib/api/entities/user.rb
+++ b/lib/api/entities/user.rb
@@ -5,6 +5,7 @@ module API
class User < UserBasic
include UsersHelper
include TimeZoneHelper
+ include Gitlab::Utils::StrongMemoize
expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) }
expose :bio, :location, :public_email, :skype, :linkedin, :twitter, :discord, :website_url, :organization, :job_title, :pronouns
@@ -12,18 +13,28 @@ module API
expose :work_information do |user|
work_information(user)
end
- expose :followers, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } do |user|
+ expose :followers, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) && following_users_allowed(opts[:current_user], user) } do |user|
user.followers.size
end
- expose :following, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } do |user|
+ expose :following, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) && following_users_allowed(opts[:current_user], user) } do |user|
user.followees.size
end
- expose :is_followed, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) && opts[:current_user] } do |user, opts|
+ expose :is_followed, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) && opts[:current_user] && following_users_allowed(opts[:current_user], user) } do |user, opts|
user.followed_by?(opts[:current_user])
end
expose :local_time do |user|
local_time(user.timezone)
end
+
+ def following_users_allowed(current_user, user)
+ strong_memoize(:following_users_allowed) do
+ if current_user
+ current_user.following_users_allowed?(user)
+ else
+ true
+ end
+ end
+ end
end
end
end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 48e13127712..505fcb2b38e 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -195,12 +195,13 @@ module API
not_found!('User') unless user
followee = current_user.follow(user)
+
+ not_modified! unless followee
+
if followee&.errors&.any?
render_api_error!(followee.errors.full_messages.join(', '), 400)
elsif followee&.persisted?
present user, with: Entities::UserBasic
- else
- not_modified!
end
end
diff --git a/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields.rb b/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields.rb
new file mode 100644
index 00000000000..ee0f73cc3de
--- /dev/null
+++ b/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields.rb
@@ -0,0 +1,90 @@
+# frozen_string_literal: true
+
+module Gitlab
+ module BackgroundMigration
+ # Populates missing dismissal information for vulnerabilities.
+ class PopulateVulnerabilityDismissalFields < BatchedMigrationJob
+ feature_category :vulnerability_management
+ scope_to ->(relation) { relation.where('state = 2 AND (dismissed_at IS NULL OR dismissed_by_id IS NULL)') }
+ operation_name :populate_vulnerability_dismissal_fields
+
+ # rubocop:disable Style/Documentation
+ class Vulnerability < ApplicationRecord
+ self.table_name = 'vulnerabilities'
+
+ has_one :finding, class_name: 'Finding'
+
+ def copy_dismissal_information
+ return unless finding&.dismissal_feedback
+
+ update_columns(
+ dismissed_at: finding.dismissal_feedback.created_at,
+ dismissed_by_id: finding.dismissal_feedback.author_id
+ )
+ end
+ end
+
+ class Finding < ApplicationRecord
+ self.table_name = 'vulnerability_occurrences'
+
+ validates :details, json_schema: { filename: "filename" }
+
+ def dismissal_feedback
+ Feedback.dismissal.where(finding_uuid: uuid).first
+ end
+ end
+
+ class Feedback < ApplicationRecord
+ DISMISSAL_TYPE = 0 # dismissal
+
+ self.table_name = 'vulnerability_feedback'
+
+ scope :dismissal, -> { where(feedback_type: DISMISSAL_TYPE) }
+ end
+ # rubocop:enable Style/Documentation
+
+ def perform
+ each_sub_batch do |sub_batch|
+ vulnerability_ids = sub_batch.pluck(:id)
+ Vulnerability.includes(:finding).where(id: vulnerability_ids).each do |vulnerability|
+ populate_for(vulnerability)
+ end
+
+ log_info(vulnerability_ids)
+ end
+ end
+
+ private
+
+ def populate_for(vulnerability)
+ log_warning(vulnerability) unless vulnerability.copy_dismissal_information
+ rescue StandardError => error
+ log_error(error, vulnerability)
+ end
+
+ def log_info(vulnerability_ids)
+ ::Gitlab::BackgroundMigration::Logger.info(
+ migrator: self.class.name,
+ message: 'Dismissal information has been copied',
+ count: vulnerability_ids.length
+ )
+ end
+
+ def log_warning(vulnerability)
+ ::Gitlab::BackgroundMigration::Logger.warn(
+ migrator: self.class.name,
+ message: 'Could not update vulnerability!',
+ vulnerability_id: vulnerability.id
+ )
+ end
+
+ def log_error(error, vulnerability)
+ ::Gitlab::BackgroundMigration::Logger.error(
+ migrator: self.class.name,
+ message: error.message,
+ vulnerability_id: vulnerability.id
+ )
+ end
+ end
+ end
+end
diff --git a/locale/gitlab.pot b/locale/gitlab.pot
index c7c0ee8a54d..3d747645075 100644
--- a/locale/gitlab.pot
+++ b/locale/gitlab.pot
@@ -2226,6 +2226,9 @@ msgstr ""
msgid "Action"
msgstr ""
+msgid "Action not allowed."
+msgstr ""
+
msgid "Action to take when receiving an alert. %{docsLink}"
msgstr ""
@@ -33326,6 +33329,12 @@ msgstr ""
msgid "Preferences|Diff colors"
msgstr ""
+msgid "Preferences|Disable follow users"
+msgstr ""
+
+msgid "Preferences|Disable follow users feature"
+msgstr ""
+
msgid "Preferences|Display time in 24-hour format"
msgstr ""
@@ -33404,6 +33413,9 @@ msgstr ""
msgid "Preferences|Time preferences"
msgstr ""
+msgid "Preferences|Turns off the ability to follow or be followed by other users."
+msgstr ""
+
msgid "Preferences|Use relative times"
msgstr ""
diff --git a/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb b/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb
deleted file mode 100644
index 58411357eeb..00000000000
--- a/rubocop/cop/gitlab/deprecate_track_redis_hll_event.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-# frozen_string_literal: true
-
-require 'rack/utils'
-
-module RuboCop
- module Cop
- module Gitlab
- # This cop prevents from using deprecated `track_redis_hll_event` method.
- #
- # @example
- #
- # # bad
- # track_redis_hll_event :show, name: 'p_analytics_valuestream'
- #
- # # good
- # track_event :show, name: 'g_analytics_valuestream', destinations: [:redis_hll]
- class DeprecateTrackRedisHLLEvent < RuboCop::Cop::Base
- MSG = '`track_redis_hll_event` is deprecated. Use `track_event` helper instead. ' \
- 'See https://docs.gitlab.com/ee/development/service_ping/implement.html#add-new-events'
-
- def_node_matcher :track_redis_hll_event_used?, <<~PATTERN
- (send _ :track_redis_hll_event ...)
- PATTERN
-
- def on_send(node)
- return unless track_redis_hll_event_used?(node)
-
- add_offense(node.loc.selector)
- end
- end
- end
- end
-end
diff --git a/spec/controllers/concerns/redis_tracking_spec.rb b/spec/controllers/concerns/redis_tracking_spec.rb
deleted file mode 100644
index 0ad8fa79e5e..00000000000
--- a/spec/controllers/concerns/redis_tracking_spec.rb
+++ /dev/null
@@ -1,135 +0,0 @@
-# frozen_string_literal: true
-
-require "spec_helper"
-
-RSpec.describe RedisTracking do
- include TrackingHelpers
-
- let(:user) { create(:user) }
-
- controller(ApplicationController) do
- include RedisTracking
-
- skip_before_action :authenticate_user!, only: :show
- track_redis_hll_event(:index, :show,
- name: 'g_compliance_approval_rules',
- if: [:custom_condition_one?, :custom_condition_two?]) { |controller| controller.get_custom_id }
-
- def index
- render html: 'index'
- end
-
- def new
- render html: 'new'
- end
-
- def show
- render html: 'show'
- end
-
- def get_custom_id
- 'some_custom_id'
- end
-
- private
-
- def custom_condition_one?
- true
- end
-
- def custom_condition_two?
- true
- end
- end
-
- def expect_tracking
- expect(Gitlab::UsageDataCounters::HLLRedisCounter).to receive(:track_event)
- .with('g_compliance_approval_rules', values: instance_of(String))
- end
-
- def expect_no_tracking
- expect(Gitlab::UsageDataCounters::HLLRedisCounter).not_to receive(:track_event)
- end
-
- context 'when user is logged in' do
- before do
- sign_in(user)
- end
-
- it 'tracks the event' do
- expect_tracking
-
- get :index
- end
-
- it 'tracks the event if DNT is not enabled' do
- stub_do_not_track('0')
-
- expect_tracking
-
- get :index
- end
-
- it 'does not track the event if DNT is enabled' do
- stub_do_not_track('1')
-
- expect_no_tracking
-
- get :index
- end
-
- it 'does not track the event if the format is not HTML' do
- expect_no_tracking
-
- get :index, format: :json
- end
-
- it 'does not track the event if a custom condition returns false' do
- expect(controller).to receive(:custom_condition_two?).and_return(false)
-
- expect_no_tracking
-
- get :index
- end
-
- it 'does not track the event for untracked actions' do
- expect_no_tracking
-
- get :new
- end
- end
-
- context 'when user is not logged in' do
- let(:visitor_id) { SecureRandom.uuid }
-
- it 'tracks the event when there is a visitor id' do
- cookies[:visitor_id] = { value: visitor_id, expires: 24.months }
-
- expect_tracking
-
- get :show, params: { id: 1 }
- end
- end
-
- context 'when user is not logged in and there is no visitor_id' do
- it 'does not track the event' do
- expect_no_tracking
-
- get :index
- end
-
- it 'tracks the event when there is custom id' do
- expect_tracking
-
- get :show, params: { id: 1 }
- end
-
- it 'does not track the event when there is no custom id' do
- expect(controller).to receive(:get_custom_id).and_return(nil)
-
- expect_no_tracking
-
- get :show, params: { id: 2 }
- end
- end
-end
diff --git a/spec/controllers/profiles/preferences_controller_spec.rb b/spec/controllers/profiles/preferences_controller_spec.rb
index e2a216bb462..0554ab3184f 100644
--- a/spec/controllers/profiles/preferences_controller_spec.rb
+++ b/spec/controllers/profiles/preferences_controller_spec.rb
@@ -109,5 +109,33 @@ RSpec.describe Profiles::PreferencesController do
expect(response.parsed_body['type']).to eq('alert')
end
end
+
+ context 'on disable_follow_users feature flag' do
+ context 'with feature flag disabled' do
+ before do
+ stub_feature_flags(disable_follow_users: false)
+ end
+
+ it 'does not update enabled_following preference of user' do
+ prefs = { enabled_following: false }
+
+ go params: prefs
+ user.reload
+
+ expect(user.enabled_following).to eq(true)
+ end
+ end
+
+ context 'with feature flag enabled' do
+ it 'does not update enabled_following preference of user' do
+ prefs = { enabled_following: false }
+
+ go params: prefs
+ user.reload
+
+ expect(user.enabled_following).to eq(false)
+ end
+ end
+ end
end
end
diff --git a/spec/frontend/fixtures/jobs.rb b/spec/frontend/fixtures/jobs.rb
index 043d6a9db29..15bbec9b9f2 100644
--- a/spec/frontend/fixtures/jobs.rb
+++ b/spec/frontend/fixtures/jobs.rb
@@ -50,6 +50,7 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do
shared_examples 'graphql queries' do |path, jobs_query|
let_it_be(:variables) { {} }
+ let_it_be(:success_path) { '' }
let_it_be(:query) do
get_graphql_query_as_string("#{path}/#{jobs_query}")
@@ -57,9 +58,10 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do
fixtures_path = 'graphql/jobs/'
- it "#{fixtures_path}#{jobs_query}.json" do
+ it "#{fixtures_path}#{jobs_query}.json", :aggregate_failures do
post_graphql(query, current_user: user, variables: variables)
+ expect(graphql_data.dig(*success_path)).not_to be_nil
expect_graphql_errors_to_be_empty
end
@@ -87,15 +89,18 @@ RSpec.describe 'Jobs (JavaScript fixtures)' do
it_behaves_like 'graphql queries', 'jobs/components/table/graphql/queries', 'get_jobs.query.graphql' do
let(:variables) { { fullPath: 'frontend-fixtures/builds-project' } }
+ let(:success_path) { %w[project jobs] }
end
it_behaves_like 'graphql queries', 'pages/admin/jobs/components/table/graphql/queries', 'get_all_jobs.query.graphql' do
let(:user) { create(:admin) }
+ let(:success_path) { 'jobs' }
end
it_behaves_like 'graphql queries', 'pages/admin/jobs/components/table/graphql/queries', 'get_cancelable_jobs_count.query.graphql' do
let(:variables) { { statuses: %w[PENDING RUNNING] } }
let(:user) { create(:admin) }
+ let(:success_path) { %w[cancelable count] }
end
end
diff --git a/spec/lib/api/entities/user_spec.rb b/spec/lib/api/entities/user_spec.rb
index 3094fc748c9..6475dcd7618 100644
--- a/spec/lib/api/entities/user_spec.rb
+++ b/spec/lib/api/entities/user_spec.rb
@@ -30,20 +30,62 @@ RSpec.describe API::Entities::User do
end
%i(followers following is_followed).each do |relationship|
- context 'when current user cannot read user profile' do
- let(:can_read_user_profile) { false }
-
+ shared_examples 'does not expose relationship' do
it "does not expose #{relationship}" do
expect(subject).not_to include(relationship)
end
end
+ shared_examples 'exposes relationship' do
+ it "exposes #{relationship}" do
+ expect(subject).to include(relationship)
+ end
+ end
+
+ context 'when current user cannot read user profile' do
+ let(:can_read_user_profile) { false }
+
+ it_behaves_like 'does not expose relationship'
+ end
+
context 'when current user can read user profile' do
let(:can_read_user_profile) { true }
- it "exposes #{relationship}" do
- expect(subject).to include(relationship)
+ it_behaves_like 'exposes relationship'
+ end
+
+ context 'when current user can read user profile and disable_follow_users is switched off' do
+ let(:can_read_user_profile) { true }
+
+ before do
+ stub_feature_flags(disable_follow_users: false)
+ user.enabled_following = false
+ user.save!
+ end
+
+ it_behaves_like 'exposes relationship'
+ end
+
+ context 'when current user can read user profile, disable_follow_users is switched on and user disabled it for themself' do
+ let(:can_read_user_profile) { true }
+
+ before do
+ user.enabled_following = false
+ user.save!
+ end
+
+ it_behaves_like 'does not expose relationship'
+ end
+
+ context 'when current user can read user profile, disable_follow_users is switched on and current user disabled it for themself' do
+ let(:can_read_user_profile) { true }
+
+ before do
+ current_user.enabled_following = false
+ current_user.save!
end
+
+ it_behaves_like 'does not expose relationship'
end
end
end
diff --git a/spec/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields_spec.rb b/spec/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields_spec.rb
new file mode 100644
index 00000000000..50380247c9f
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/populate_vulnerability_dismissal_fields_spec.rb
@@ -0,0 +1,114 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::BackgroundMigration::PopulateVulnerabilityDismissalFields, schema: 20230412185837, feature_category: :vulnerability_management do # rubocop:disable Layout/LineLength
+ let(:users) { table(:users) }
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:vulnerabilities) { table(:vulnerabilities) }
+ let(:findings) { table(:vulnerability_occurrences) }
+ let(:scanners) { table(:vulnerability_scanners) }
+ let(:identifiers) { table(:vulnerability_identifiers) }
+ let(:feedback) { table(:vulnerability_feedback) }
+
+ let(:user) { users.create!(name: 'test', email: 'test@example.com', projects_limit: 5) }
+ let(:namespace) { namespaces.create!(name: 'gitlab', path: 'gitlab-org') }
+ let(:project) { projects.create!(namespace_id: namespace.id, name: 'foo', project_namespace_id: namespace.id) }
+ let(:vulnerability_1) do
+ vulnerabilities.create!(title: 'title', state: 2, severity: 0,
+ confidence: 5, report_type: 2, project_id: project.id, author_id: user.id
+ )
+ end
+
+ let(:vulnerability_2) do
+ vulnerabilities.create!(title: 'title', state: 2, severity: 0,
+ confidence: 5, report_type: 2, project_id: project.id, author_id: user.id
+ )
+ end
+
+ let(:scanner) { scanners.create!(project_id: project.id, external_id: 'foo', name: 'bar') }
+ let(:identifier) do
+ identifiers.create!(project_id: project.id, fingerprint: 'foo',
+ external_type: 'bar', external_id: 'zoo', name: 'identifier'
+ )
+ end
+
+ let(:uuid) { SecureRandom.uuid }
+
+ before do
+ feedback.create!(feedback_type: 0,
+ category: 'sast',
+ project_fingerprint: '418291a26024a1445b23fe64de9380cdcdfd1fa8',
+ project_id: project.id,
+ author_id: user.id,
+ created_at: Time.current,
+ finding_uuid: uuid
+ )
+
+ findings.create!(name: 'Finding',
+ report_type: 'sast',
+ project_fingerprint: '418291a26024a1445b23fe64de9380cdcdfd1f98',
+ location_fingerprint: 'bar',
+ severity: 1,
+ confidence: 1,
+ metadata_version: 1,
+ raw_metadata: '',
+ details: {},
+ uuid: uuid,
+ project_id: project.id,
+ vulnerability_id: vulnerability_1.id,
+ scanner_id: scanner.id,
+ primary_identifier_id: identifier.id
+ )
+
+ allow(::Gitlab::BackgroundMigration::Logger).to receive_messages(info: true, warn: true, error: true)
+ end
+
+ subject do
+ described_class.new(
+ start_id: vulnerability_1.id,
+ end_id: vulnerability_2.id,
+ batch_table: :vulnerabilities,
+ batch_column: :id,
+ sub_batch_size: 200,
+ pause_ms: 2.minutes,
+ connection: ApplicationRecord.connection
+ )
+ end
+
+ describe '#perform' do
+ it 'updates the missing dismissal information of the vulnerability' do
+ expect { subject.perform }.to change { vulnerability_1.reload.dismissed_at }
+ .from(nil)
+ .and change { vulnerability_1.reload.dismissed_by_id }.from(nil).to(user.id)
+ end
+
+ it 'writes log messages', :aggregate_failures do
+ subject.perform
+
+ expect(::Gitlab::BackgroundMigration::Logger).to have_received(:info).with(migrator: described_class.name,
+ message: 'Dismissal information has been copied',
+ count: 2
+ )
+ expect(::Gitlab::BackgroundMigration::Logger).to have_received(:warn).with(migrator: described_class.name,
+ message: 'Could not update vulnerability!',
+ vulnerability_id: vulnerability_2.id
+ )
+ end
+
+ context 'when logger throws exception StandardError' do
+ before do
+ allow(::Gitlab::BackgroundMigration::Logger).to receive(:warn).and_raise(StandardError)
+ end
+
+ it 'logs StandardError' do
+ expect(::Gitlab::BackgroundMigration::Logger).to receive(:error).with({
+ migrator: described_class.name, message: StandardError.to_s, vulnerability_id: vulnerability_2.id
+ })
+
+ subject.perform
+ end
+ end
+ end
+end
diff --git a/spec/migrations/20230412185837_queue_populate_vulnerability_dismissal_fields_spec.rb b/spec/migrations/20230412185837_queue_populate_vulnerability_dismissal_fields_spec.rb
new file mode 100644
index 00000000000..d39936abb90
--- /dev/null
+++ b/spec/migrations/20230412185837_queue_populate_vulnerability_dismissal_fields_spec.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+require_migration!
+
+RSpec.describe QueuePopulateVulnerabilityDismissalFields, feature_category: :vulnerability_management do
+ let!(:batched_migration) { described_class::MIGRATION }
+
+ describe '#up' do
+ it 'schedules a new batched migration' do
+ reversible_migration do |migration|
+ migration.before -> {
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ }
+
+ migration.after -> {
+ expect(batched_migration).to have_scheduled_batched_migration(
+ table_name: :vulnerabilities,
+ column_name: :id,
+ interval: described_class::DELAY_INTERVAL,
+ batch_size: described_class::BATCH_SIZE,
+ sub_batch_size: described_class::SUB_BATCH_SIZE
+ )
+ }
+ end
+ end
+ end
+
+ describe '#down' do
+ it 'deletes all batched migration records' do
+ migrate!
+ schema_migrate_down!
+
+ expect(batched_migration).not_to have_scheduled_batched_migration
+ end
+ end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index c05b7274317..21cea5fde14 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -3840,6 +3840,54 @@ RSpec.describe User, feature_category: :user_profile do
expect(user.following?(followee)).to be_falsey
end
+
+ it 'does not follow if user disabled following' do
+ user = create(:user)
+ user.enabled_following = false
+
+ followee = create(:user)
+
+ expect(user.follow(followee)).to eq(false)
+
+ expect(user.following?(followee)).to be_falsey
+ end
+
+ it 'does not follow if followee user disabled following' do
+ user = create(:user)
+
+ followee = create(:user)
+ followee.enabled_following = false
+
+ expect(user.follow(followee)).to eq(false)
+
+ expect(user.following?(followee)).to be_falsey
+ end
+
+ context 'when disable_follow_users feature flag is off' do
+ before do
+ stub_feature_flags(disable_follow_users: false)
+ end
+
+ it 'follows user even if user disabled following' do
+ user = create(:user)
+ user.enabled_following = false
+
+ followee = create(:user)
+
+ expect(user.follow(followee)).to be_truthy
+ expect(user.following?(followee)).to be_truthy
+ end
+
+ it 'follows user even if followee user disabled following' do
+ user = create(:user)
+
+ followee = create(:user)
+ followee.enabled_following = false
+
+ expect(user.follow(followee)).to be_truthy
+ expect(user.following?(followee)).to be_truthy
+ end
+ end
end
describe '#unfollow' do
@@ -3882,6 +3930,39 @@ RSpec.describe User, feature_category: :user_profile do
end
end
+ describe '#following_users_allowed?' do
+ using RSpec::Parameterized::TableSyntax
+
+ let_it_be(:user) { create(:user) }
+ let_it_be(:followee) { create(:user) }
+
+ where(:user_enabled_following, :followee_enabled_following, :feature_flag_status, :result) do
+ true | true | false | true
+ true | false | false | true
+ true | true | true | true
+ true | false | true | false
+ false | true | false | true
+ false | true | true | false
+ false | false | false | true
+ false | false | true | false
+ end
+
+ with_them do
+ before do
+ user.enabled_following = user_enabled_following
+ followee.enabled_following = followee_enabled_following
+ followee.save!
+ stub_feature_flags(disable_follow_users: feature_flag_status)
+ end
+
+ it { expect(user.following_users_allowed?(followee)).to eq result }
+ end
+
+ it 'is false when user and followee is the same user' do
+ expect(user.following_users_allowed?(user)).to eq(false)
+ end
+ end
+
describe '#notification_email_or_default' do
let(:email) { 'gonzo@muppets.com' }
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 79e2755f34c..3ecd012be12 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -1009,6 +1009,20 @@ RSpec.describe API::Users, :aggregate_failures, feature_category: :user_profile
expect(response).to have_gitlab_http_status(:not_modified)
end
end
+
+ context 'on a user with disabled following' do
+ before do
+ user.enabled_following = false
+ user.save!
+ end
+
+ it 'does not change following' do
+ post api("/users/#{followee.id}/follow", user)
+
+ expect(user.followees).to be_empty
+ expect(response).to have_gitlab_http_status(:not_modified)
+ end
+ end
end
describe 'POST /users/:id/unfollow' do
diff --git a/spec/requests/users_controller_spec.rb b/spec/requests/users_controller_spec.rb
index 14e4fea0548..3367414b987 100644
--- a/spec/requests/users_controller_spec.rb
+++ b/spec/requests/users_controller_spec.rb
@@ -914,6 +914,35 @@ RSpec.describe UsersController, feature_category: :user_management do
expect(user).not_to be_following(public_user)
end
end
+
+ context 'when user or followee disabled following' do
+ before do
+ sign_in(user)
+ end
+
+ it 'alerts and not follow if user disabled following' do
+ user.enabled_following = false
+
+ post user_follow_url(username: public_user.username)
+ expect(response).to be_redirect
+
+ expected_message = format(_('Action not allowed.'))
+ expect(flash[:alert]).to eq(expected_message)
+ expect(user).not_to be_following(public_user)
+ end
+
+ it 'alerts and not follow if followee disabled following' do
+ public_user.enabled_following = false
+ public_user.save!
+
+ post user_follow_url(username: public_user.username)
+ expect(response).to be_redirect
+
+ expected_message = format(_('Action not allowed.'))
+ expect(flash[:alert]).to eq(expected_message)
+ expect(user).not_to be_following(public_user)
+ end
+ end
end
context 'token authentication' do
diff --git a/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb b/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb
deleted file mode 100644
index eed30e11a98..00000000000
--- a/spec/rubocop/cop/gitlab/deprecate_track_redis_hll_event_spec.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-# frozen_string_literal: true
-
-require 'rubocop_spec_helper'
-require_relative '../../../../rubocop/cop/gitlab/deprecate_track_redis_hll_event'
-
-RSpec.describe RuboCop::Cop::Gitlab::DeprecateTrackRedisHLLEvent do
- it 'does not flag the use of track_event' do
- expect_no_offenses('track_event :show, name: "p_analytics_insights"')
- end
-
- it 'flags the use of track_redis_hll_event' do
- expect_offense(<<~SOURCE)
- track_redis_hll_event :show, name: 'p_analytics_valuestream'
- ^^^^^^^^^^^^^^^^^^^^^ `track_redis_hll_event` is deprecated[...]
- SOURCE
- end
-end
diff --git a/spec/services/users/update_service_spec.rb b/spec/services/users/update_service_spec.rb
index 1716685566c..d2a26425f52 100644
--- a/spec/services/users/update_service_spec.rb
+++ b/spec/services/users/update_service_spec.rb
@@ -185,6 +185,49 @@ RSpec.describe Users::UpdateService, feature_category: :user_profile do
end.not_to raise_error
end
+ describe 'updates the enabled_following' do
+ let(:user) { create(:user) }
+
+ before do
+ 3.times do
+ user.follow(create(:user))
+ create(:user).follow(user)
+ end
+ user.reload
+ end
+
+ it 'removes followers and followees' do
+ expect do
+ update_user(user, enabled_following: false)
+ end.to change { user.followed_users.count }.from(3).to(0)
+ .and change { user.following_users.count }.from(3).to(0)
+ expect(user.enabled_following).to eq(false)
+ end
+
+ it 'does not remove followers/followees if feature flag is off' do
+ stub_feature_flags(disable_follow_users: false)
+
+ expect do
+ update_user(user, enabled_following: false)
+ end.to not_change { user.followed_users.count }
+ .and not_change { user.following_users.count }
+ end
+
+ context 'when there is more followers/followees then batch limit' do
+ before do
+ stub_env('BATCH_SIZE', 1)
+ end
+
+ it 'removes followers and followees' do
+ expect do
+ update_user(user, enabled_following: false)
+ end.to change { user.followed_users.count }.from(3).to(0)
+ .and change { user.following_users.count }.from(3).to(0)
+ expect(user.enabled_following).to eq(false)
+ end
+ end
+ end
+
def update_user(user, opts)
described_class.new(user, opts.merge(user: user)).execute
end