summaryrefslogtreecommitdiff
path: root/app/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/abuse_reports_controller.rb2
-rw-r--r--app/controllers/admin/abuse_reports_controller.rb2
-rw-r--r--app/controllers/admin/appearances_controller.rb2
-rw-r--r--app/controllers/admin/application_settings_controller.rb21
-rw-r--r--app/controllers/admin/applications_controller.rb2
-rw-r--r--app/controllers/admin/background_jobs_controller.rb1
-rw-r--r--app/controllers/admin/broadcast_messages_controller.rb2
-rw-r--r--app/controllers/admin/ci/variables_controller.rb2
-rw-r--r--app/controllers/admin/cohorts_controller.rb2
-rw-r--r--app/controllers/admin/dashboard_controller.rb2
-rw-r--r--app/controllers/admin/deploy_keys_controller.rb2
-rw-r--r--app/controllers/admin/dev_ops_report_controller.rb2
-rw-r--r--app/controllers/admin/gitaly_servers_controller.rb2
-rw-r--r--app/controllers/admin/groups_controller.rb2
-rw-r--r--app/controllers/admin/health_check_controller.rb2
-rw-r--r--app/controllers/admin/hook_logs_controller.rb2
-rw-r--r--app/controllers/admin/hooks_controller.rb4
-rw-r--r--app/controllers/admin/identities_controller.rb2
-rw-r--r--app/controllers/admin/impersonation_tokens_controller.rb2
-rw-r--r--app/controllers/admin/impersonations_controller.rb2
-rw-r--r--app/controllers/admin/instance_review_controller.rb39
-rw-r--r--app/controllers/admin/instance_statistics_controller.rb2
-rw-r--r--app/controllers/admin/integrations_controller.rb5
-rw-r--r--app/controllers/admin/jobs_controller.rb2
-rw-r--r--app/controllers/admin/keys_controller.rb2
-rw-r--r--app/controllers/admin/labels_controller.rb2
-rw-r--r--app/controllers/admin/plan_limits_controller.rb2
-rw-r--r--app/controllers/admin/projects_controller.rb3
-rw-r--r--app/controllers/admin/requests_profiles_controller.rb2
-rw-r--r--app/controllers/admin/runner_projects_controller.rb2
-rw-r--r--app/controllers/admin/runners_controller.rb10
-rw-r--r--app/controllers/admin/serverless/domains_controller.rb2
-rw-r--r--app/controllers/admin/services_controller.rb2
-rw-r--r--app/controllers/admin/sessions_controller.rb9
-rw-r--r--app/controllers/admin/spam_logs_controller.rb2
-rw-r--r--app/controllers/admin/system_info_controller.rb2
-rw-r--r--app/controllers/admin/users_controller.rb23
-rw-r--r--app/controllers/application_controller.rb12
-rw-r--r--app/controllers/autocomplete_controller.rb6
-rw-r--r--app/controllers/boards/issues_controller.rb2
-rw-r--r--app/controllers/boards/lists_controller.rb4
-rw-r--r--app/controllers/clusters/base_controller.rb2
-rw-r--r--app/controllers/clusters/clusters_controller.rb29
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor.rb21
-rw-r--r--app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb (renamed from app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb)19
-rw-r--r--app/controllers/concerns/boards_actions.rb4
-rw-r--r--app/controllers/concerns/controller_with_feature_category.rb29
-rw-r--r--app/controllers/concerns/controller_with_feature_category/config.rb38
-rw-r--r--app/controllers/concerns/hooks_execution.rb15
-rw-r--r--app/controllers/concerns/integrations_actions.rb2
-rw-r--r--app/controllers/concerns/issuable_collections.rb10
-rw-r--r--app/controllers/concerns/issuable_collections_action.rb5
-rw-r--r--app/controllers/concerns/membership_actions.rb18
-rw-r--r--app/controllers/concerns/milestone_actions.rb14
-rw-r--r--app/controllers/concerns/multiple_boards_actions.rb6
-rw-r--r--app/controllers/concerns/redis_tracking.rb14
-rw-r--r--app/controllers/concerns/runner_setup_scripts.rb25
-rw-r--r--app/controllers/concerns/show_inherited_labels_checker.rb11
-rw-r--r--app/controllers/concerns/snippets_actions.rb40
-rw-r--r--app/controllers/concerns/wiki_actions.rb8
-rw-r--r--app/controllers/confirmations_controller.rb2
-rw-r--r--app/controllers/dashboard/groups_controller.rb2
-rw-r--r--app/controllers/dashboard/labels_controller.rb6
-rw-r--r--app/controllers/dashboard/milestones_controller.rb2
-rw-r--r--app/controllers/dashboard/projects_controller.rb2
-rw-r--r--app/controllers/dashboard/snippets_controller.rb2
-rw-r--r--app/controllers/dashboard/todos_controller.rb2
-rw-r--r--app/controllers/dashboard_controller.rb4
-rw-r--r--app/controllers/explore/groups_controller.rb2
-rw-r--r--app/controllers/explore/projects_controller.rb2
-rw-r--r--app/controllers/explore/snippets_controller.rb2
-rw-r--r--app/controllers/google_api/authorizations_controller.rb2
-rw-r--r--app/controllers/graphql_controller.rb16
-rw-r--r--app/controllers/groups/avatars_controller.rb2
-rw-r--r--app/controllers/groups/boards_controller.rb2
-rw-r--r--app/controllers/groups/children_controller.rb2
-rw-r--r--app/controllers/groups/deploy_tokens_controller.rb2
-rw-r--r--app/controllers/groups/group_links_controller.rb11
-rw-r--r--app/controllers/groups/group_members_controller.rb6
-rw-r--r--app/controllers/groups/imports_controller.rb2
-rw-r--r--app/controllers/groups/labels_controller.rb30
-rw-r--r--app/controllers/groups/milestones_controller.rb4
-rw-r--r--app/controllers/groups/packages_controller.rb2
-rw-r--r--app/controllers/groups/registry/repositories_controller.rb6
-rw-r--r--app/controllers/groups/releases_controller.rb2
-rw-r--r--app/controllers/groups/runners_controller.rb2
-rw-r--r--app/controllers/groups/settings/ci_cd_controller.rb8
-rw-r--r--app/controllers/groups/settings/integrations_controller.rb2
-rw-r--r--app/controllers/groups/settings/repository_controller.rb2
-rw-r--r--app/controllers/groups/shared_projects_controller.rb2
-rw-r--r--app/controllers/groups/uploads_controller.rb2
-rw-r--r--app/controllers/groups/variables_controller.rb2
-rw-r--r--app/controllers/groups_controller.rb26
-rw-r--r--app/controllers/help_controller.rb57
-rw-r--r--app/controllers/ide_controller.rb2
-rw-r--r--app/controllers/import/available_namespaces_controller.rb2
-rw-r--r--app/controllers/import/base_controller.rb1
-rw-r--r--app/controllers/import/bulk_imports_controller.rb109
-rw-r--r--app/controllers/import/fogbugz_controller.rb2
-rw-r--r--app/controllers/import/github_controller.rb2
-rw-r--r--app/controllers/import/gitlab_groups_controller.rb2
-rw-r--r--app/controllers/import/manifest_controller.rb11
-rw-r--r--app/controllers/invites_controller.rb59
-rw-r--r--app/controllers/jira_connect/application_controller.rb2
-rw-r--r--app/controllers/jira_connect/events_controller.rb4
-rw-r--r--app/controllers/jira_connect/users_controller.rb11
-rw-r--r--app/controllers/jwt_controller.rb2
-rw-r--r--app/controllers/notification_settings_controller.rb2
-rw-r--r--app/controllers/oauth/jira/authorizations_controller.rb2
-rw-r--r--app/controllers/omniauth_callbacks_controller.rb5
-rw-r--r--app/controllers/passwords_controller.rb2
-rw-r--r--app/controllers/profiles/accounts_controller.rb2
-rw-r--r--app/controllers/profiles/active_sessions_controller.rb2
-rw-r--r--app/controllers/profiles/avatars_controller.rb2
-rw-r--r--app/controllers/profiles/chat_names_controller.rb2
-rw-r--r--app/controllers/profiles/emails_controller.rb2
-rw-r--r--app/controllers/profiles/gpg_keys_controller.rb2
-rw-r--r--app/controllers/profiles/groups_controller.rb2
-rw-r--r--app/controllers/profiles/keys_controller.rb2
-rw-r--r--app/controllers/profiles/notifications_controller.rb2
-rw-r--r--app/controllers/profiles/passwords_controller.rb2
-rw-r--r--app/controllers/profiles/personal_access_tokens_controller.rb2
-rw-r--r--app/controllers/profiles/preferences_controller.rb2
-rw-r--r--app/controllers/profiles/two_factor_auths_controller.rb7
-rw-r--r--app/controllers/profiles/u2f_registrations_controller.rb2
-rw-r--r--app/controllers/profiles/webauthn_registrations_controller.rb2
-rw-r--r--app/controllers/profiles_controller.rb5
-rw-r--r--app/controllers/projects/alert_management_controller.rb3
-rw-r--r--app/controllers/projects/alerting/notifications_controller.rb2
-rw-r--r--app/controllers/projects/artifacts_controller.rb2
-rw-r--r--app/controllers/projects/autocomplete_sources_controller.rb5
-rw-r--r--app/controllers/projects/avatars_controller.rb2
-rw-r--r--app/controllers/projects/badges_controller.rb2
-rw-r--r--app/controllers/projects/blame_controller.rb2
-rw-r--r--app/controllers/projects/blob_controller.rb5
-rw-r--r--app/controllers/projects/boards_controller.rb2
-rw-r--r--app/controllers/projects/branches_controller.rb2
-rw-r--r--app/controllers/projects/build_artifacts_controller.rb2
-rw-r--r--app/controllers/projects/builds_controller.rb2
-rw-r--r--app/controllers/projects/ci/daily_build_group_report_results_controller.rb9
-rw-r--r--app/controllers/projects/ci/lints_controller.rb2
-rw-r--r--app/controllers/projects/commit_controller.rb2
-rw-r--r--app/controllers/projects/commits_controller.rb2
-rw-r--r--app/controllers/projects/compare_controller.rb2
-rw-r--r--app/controllers/projects/confluences_controller.rb2
-rw-r--r--app/controllers/projects/cycle_analytics/events_controller.rb2
-rw-r--r--app/controllers/projects/cycle_analytics_controller.rb2
-rw-r--r--app/controllers/projects/deploy_keys_controller.rb2
-rw-r--r--app/controllers/projects/deploy_tokens_controller.rb2
-rw-r--r--app/controllers/projects/deployments_controller.rb2
-rw-r--r--app/controllers/projects/design_management/designs_controller.rb2
-rw-r--r--app/controllers/projects/discussions_controller.rb2
-rw-r--r--app/controllers/projects/environments/prometheus_api_controller.rb2
-rw-r--r--app/controllers/projects/environments/sample_metrics_controller.rb2
-rw-r--r--app/controllers/projects/environments_controller.rb2
-rw-r--r--app/controllers/projects/error_tracking/base_controller.rb2
-rw-r--r--app/controllers/projects/error_tracking/projects_controller.rb2
-rw-r--r--app/controllers/projects/feature_flags_clients_controller.rb29
-rw-r--r--app/controllers/projects/feature_flags_controller.rb174
-rw-r--r--app/controllers/projects/feature_flags_user_lists_controller.rb23
-rw-r--r--app/controllers/projects/find_file_controller.rb2
-rw-r--r--app/controllers/projects/forks_controller.rb2
-rw-r--r--app/controllers/projects/grafana_api_controller.rb2
-rw-r--r--app/controllers/projects/graphs_controller.rb3
-rw-r--r--app/controllers/projects/group_links_controller.rb15
-rw-r--r--app/controllers/projects/hook_logs_controller.rb2
-rw-r--r--app/controllers/projects/hooks_controller.rb4
-rw-r--r--app/controllers/projects/import/jira_controller.rb2
-rw-r--r--app/controllers/projects/imports_controller.rb2
-rw-r--r--app/controllers/projects/incident_management/pager_duty_incidents_controller.rb2
-rw-r--r--app/controllers/projects/incidents_controller.rb39
-rw-r--r--app/controllers/projects/issue_links_controller.rb2
-rw-r--r--app/controllers/projects/issues_controller.rb24
-rw-r--r--app/controllers/projects/jobs_controller.rb55
-rw-r--r--app/controllers/projects/labels_controller.rb5
-rw-r--r--app/controllers/projects/logs_controller.rb2
-rw-r--r--app/controllers/projects/mattermosts_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests/application_controller.rb3
-rw-r--r--app/controllers/projects/merge_requests/diffs_controller.rb3
-rw-r--r--app/controllers/projects/merge_requests/drafts_controller.rb2
-rw-r--r--app/controllers/projects/merge_requests_controller.rb29
-rw-r--r--app/controllers/projects/metrics/dashboards/builder_controller.rb2
-rw-r--r--app/controllers/projects/metrics_dashboard_controller.rb2
-rw-r--r--app/controllers/projects/milestones_controller.rb6
-rw-r--r--app/controllers/projects/mirrors_controller.rb2
-rw-r--r--app/controllers/projects/network_controller.rb2
-rw-r--r--app/controllers/projects/notes_controller.rb2
-rw-r--r--app/controllers/projects/packages/package_files_controller.rb2
-rw-r--r--app/controllers/projects/packages/packages_controller.rb9
-rw-r--r--app/controllers/projects/pages_controller.rb2
-rw-r--r--app/controllers/projects/pages_domains_controller.rb2
-rw-r--r--app/controllers/projects/performance_monitoring/dashboards_controller.rb2
-rw-r--r--app/controllers/projects/pipeline_schedules_controller.rb2
-rw-r--r--app/controllers/projects/pipelines/application_controller.rb2
-rw-r--r--app/controllers/projects/pipelines_controller.rb18
-rw-r--r--app/controllers/projects/pipelines_settings_controller.rb2
-rw-r--r--app/controllers/projects/product_analytics_controller.rb2
-rw-r--r--app/controllers/projects/project_members_controller.rb6
-rw-r--r--app/controllers/projects/prometheus/alerts_controller.rb2
-rw-r--r--app/controllers/projects/prometheus/metrics_controller.rb2
-rw-r--r--app/controllers/projects/protected_refs_controller.rb4
-rw-r--r--app/controllers/projects/raw_controller.rb2
-rw-r--r--app/controllers/projects/refs_controller.rb2
-rw-r--r--app/controllers/projects/registry/application_controller.rb2
-rw-r--r--app/controllers/projects/registry/repositories_controller.rb6
-rw-r--r--app/controllers/projects/registry/tags_controller.rb9
-rw-r--r--app/controllers/projects/releases/evidences_controller.rb2
-rw-r--r--app/controllers/projects/releases_controller.rb14
-rw-r--r--app/controllers/projects/repositories_controller.rb2
-rw-r--r--app/controllers/projects/runner_projects_controller.rb2
-rw-r--r--app/controllers/projects/runners_controller.rb6
-rw-r--r--app/controllers/projects/serverless/functions_controller.rb2
-rw-r--r--app/controllers/projects/service_desk_controller.rb2
-rw-r--r--app/controllers/projects/services_controller.rb4
-rw-r--r--app/controllers/projects/settings/access_tokens_controller.rb2
-rw-r--r--app/controllers/projects/settings/ci_cd_controller.rb25
-rw-r--r--app/controllers/projects/settings/integrations_controller.rb2
-rw-r--r--app/controllers/projects/settings/operations_controller.rb37
-rw-r--r--app/controllers/projects/settings/repository_controller.rb5
-rw-r--r--app/controllers/projects/snippets/application_controller.rb2
-rw-r--r--app/controllers/projects/snippets_controller.rb25
-rw-r--r--app/controllers/projects/starrers_controller.rb2
-rw-r--r--app/controllers/projects/static_site_editor_controller.rb24
-rw-r--r--app/controllers/projects/tags/releases_controller.rb2
-rw-r--r--app/controllers/projects/tags_controller.rb26
-rw-r--r--app/controllers/projects/templates_controller.rb2
-rw-r--r--app/controllers/projects/todos_controller.rb2
-rw-r--r--app/controllers/projects/tracings_controller.rb28
-rw-r--r--app/controllers/projects/tree_controller.rb2
-rw-r--r--app/controllers/projects/triggers_controller.rb2
-rw-r--r--app/controllers/projects/uploads_controller.rb2
-rw-r--r--app/controllers/projects/usage_ping_controller.rb2
-rw-r--r--app/controllers/projects/variables_controller.rb2
-rw-r--r--app/controllers/projects/web_ide_schemas_controller.rb2
-rw-r--r--app/controllers/projects/web_ide_terminals_controller.rb2
-rw-r--r--app/controllers/projects/wikis_controller.rb2
-rw-r--r--app/controllers/projects_controller.rb23
-rw-r--r--app/controllers/registrations/experience_levels_controller.rb10
-rw-r--r--app/controllers/registrations_controller.rb52
-rw-r--r--app/controllers/repositories/git_http_client_controller.rb2
-rw-r--r--app/controllers/runner_setup_controller.rb9
-rw-r--r--app/controllers/search_controller.rb8
-rw-r--r--app/controllers/sent_notifications_controller.rb2
-rw-r--r--app/controllers/sessions_controller.rb9
-rw-r--r--app/controllers/snippets/application_controller.rb2
-rw-r--r--app/controllers/snippets/notes_controller.rb2
-rw-r--r--app/controllers/snippets_controller.rb27
-rw-r--r--app/controllers/uploads_controller.rb2
-rw-r--r--app/controllers/user_callouts_controller.rb2
-rw-r--r--app/controllers/users/terms_controller.rb2
-rw-r--r--app/controllers/users_controller.rb4
-rw-r--r--app/controllers/whats_new_controller.rb25
252 files changed, 1574 insertions, 422 deletions
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 7d8016f763d..5e613c47fc5 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -3,6 +3,8 @@
class AbuseReportsController < ApplicationController
before_action :set_user, only: [:new]
+ feature_category :users
+
def new
@abuse_report = AbuseReport.new
@abuse_report.user_id = @user.id
diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb
index 31d825c235b..6f80ed3c172 100644
--- a/app/controllers/admin/abuse_reports_controller.rb
+++ b/app/controllers/admin/abuse_reports_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::AbuseReportsController < Admin::ApplicationController
+ feature_category :users
+
def index
@abuse_reports = AbuseReportsFinder.new(params).execute
end
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index 8405f2a5cf8..c2614a158b7 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -3,6 +3,8 @@
class Admin::AppearancesController < Admin::ApplicationController
before_action :set_appearance, except: :create
+ feature_category :navigation
+
def show
end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 73f71f7ad55..786ba73a96f 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -2,6 +2,7 @@
class Admin::ApplicationSettingsController < Admin::ApplicationController
include InternalRedirect
+ include ServicesHelper
# NOTE: Use @application_setting in this controller when you need to access
# application_settings after it has been modified. This is because the
@@ -16,6 +17,24 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
push_frontend_feature_flag(:ci_instance_variables_ui, default_enabled: true)
end
+ feature_category :not_owned, [
+ :general, :reporting, :metrics_and_profiling, :network,
+ :preferences, :update, :reset_health_check_token
+ ]
+
+ feature_category :metrics, [
+ :create_self_monitoring_project,
+ :status_create_self_monitoring_project,
+ :delete_self_monitoring_project,
+ :status_delete_self_monitoring_project
+ ]
+
+ feature_category :source_code_management, [:repository, :clear_repository_check_states]
+ feature_category :continuous_integration, [:ci_cd, :reset_registration_token]
+ feature_category :collection, [:usage_data]
+ feature_category :integrations, [:integrations]
+ feature_category :pages, [:lets_encrypt_terms_of_service]
+
VALID_SETTING_PANELS = %w(general repository
ci_cd reporting metrics_and_profiling
network preferences).freeze
@@ -32,6 +51,8 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
end
def integrations
+ return not_found unless instance_level_integrations?
+
@integrations = Service.find_or_initialize_all(Service.for_instance).sort_by(&:title)
end
diff --git a/app/controllers/admin/applications_controller.rb b/app/controllers/admin/applications_controller.rb
index c017ecee054..449aa90b0e6 100644
--- a/app/controllers/admin/applications_controller.rb
+++ b/app/controllers/admin/applications_controller.rb
@@ -6,6 +6,8 @@ class Admin::ApplicationsController < Admin::ApplicationController
before_action :set_application, only: [:show, :edit, :update, :destroy]
before_action :load_scopes, only: [:new, :create, :edit, :update]
+ feature_category :authentication_and_authorization
+
def index
applications = ApplicationsFinder.new.execute
@applications = Kaminari.paginate_array(applications).page(params[:page])
diff --git a/app/controllers/admin/background_jobs_controller.rb b/app/controllers/admin/background_jobs_controller.rb
index fc877142418..d4b906d5e33 100644
--- a/app/controllers/admin/background_jobs_controller.rb
+++ b/app/controllers/admin/background_jobs_controller.rb
@@ -1,4 +1,5 @@
# frozen_string_literal: true
class Admin::BackgroundJobsController < Admin::ApplicationController
+ feature_category :not_owned
end
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index 3233c765941..4660b0bfbb0 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -5,6 +5,8 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
before_action :finder, only: [:edit, :update, :destroy]
+ feature_category :navigation
+
# rubocop: disable CodeReuse/ActiveRecord
def index
@broadcast_messages = BroadcastMessage.order(ends_at: :desc).page(params[:page])
diff --git a/app/controllers/admin/ci/variables_controller.rb b/app/controllers/admin/ci/variables_controller.rb
index ca9b393550d..f30ee37fa58 100644
--- a/app/controllers/admin/ci/variables_controller.rb
+++ b/app/controllers/admin/ci/variables_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::Ci::VariablesController < Admin::ApplicationController
+ feature_category :continuous_integration
+
def show
respond_to do |format|
format.json { render_instance_variables }
diff --git a/app/controllers/admin/cohorts_controller.rb b/app/controllers/admin/cohorts_controller.rb
index e3df98b7917..d5cd9c55422 100644
--- a/app/controllers/admin/cohorts_controller.rb
+++ b/app/controllers/admin/cohorts_controller.rb
@@ -5,6 +5,8 @@ class Admin::CohortsController < Admin::ApplicationController
track_unique_visits :index, target_id: 'i_analytics_cohorts'
+ feature_category :instance_statistics
+
def index
if Gitlab::CurrentSettings.usage_ping_enabled
cohorts_results = Rails.cache.fetch('cohorts', expires_in: 1.day) do
diff --git a/app/controllers/admin/dashboard_controller.rb b/app/controllers/admin/dashboard_controller.rb
index b7b535e70df..7d981d67840 100644
--- a/app/controllers/admin/dashboard_controller.rb
+++ b/app/controllers/admin/dashboard_controller.rb
@@ -6,6 +6,8 @@ class Admin::DashboardController < Admin::ApplicationController
COUNTED_ITEMS = [Project, User, Group].freeze
+ feature_category :not_owned
+
# rubocop: disable CodeReuse/ActiveRecord
def index
@counts = Gitlab::Database::Count.approximate_counts(COUNTED_ITEMS)
diff --git a/app/controllers/admin/deploy_keys_controller.rb b/app/controllers/admin/deploy_keys_controller.rb
index 180f7d4c803..ed63e65d4df 100644
--- a/app/controllers/admin/deploy_keys_controller.rb
+++ b/app/controllers/admin/deploy_keys_controller.rb
@@ -4,6 +4,8 @@ class Admin::DeployKeysController < Admin::ApplicationController
before_action :deploy_keys, only: [:index]
before_action :deploy_key, only: [:destroy, :edit, :update]
+ feature_category :continuous_delivery
+
def index
end
diff --git a/app/controllers/admin/dev_ops_report_controller.rb b/app/controllers/admin/dev_ops_report_controller.rb
index bed0d51c331..59b2200fb59 100644
--- a/app/controllers/admin/dev_ops_report_controller.rb
+++ b/app/controllers/admin/dev_ops_report_controller.rb
@@ -5,6 +5,8 @@ class Admin::DevOpsReportController < Admin::ApplicationController
track_unique_visits :show, target_id: 'i_analytics_dev_ops_score'
+ feature_category :devops_reports
+
# rubocop: disable CodeReuse/ActiveRecord
def show
@metric = DevOpsReport::Metric.order(:created_at).last&.present
diff --git a/app/controllers/admin/gitaly_servers_controller.rb b/app/controllers/admin/gitaly_servers_controller.rb
index 0a5566bfe70..827791c8a4a 100644
--- a/app/controllers/admin/gitaly_servers_controller.rb
+++ b/app/controllers/admin/gitaly_servers_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::GitalyServersController < Admin::ApplicationController
+ feature_category :gitaly
+
def index
@gitaly_servers = Gitaly::Server.all
end
diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb
index 6414792dd43..032e449f995 100644
--- a/app/controllers/admin/groups_controller.rb
+++ b/app/controllers/admin/groups_controller.rb
@@ -5,6 +5,8 @@ class Admin::GroupsController < Admin::ApplicationController
before_action :group, only: [:edit, :update, :destroy, :project_update, :members_update]
+ feature_category :subgroups
+
def index
@groups = groups.sort_by_attribute(@sort = params[:sort])
@groups = @groups.search(params[:name]) if params[:name].present?
diff --git a/app/controllers/admin/health_check_controller.rb b/app/controllers/admin/health_check_controller.rb
index 7668c799cba..e013b5fbd72 100644
--- a/app/controllers/admin/health_check_controller.rb
+++ b/app/controllers/admin/health_check_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::HealthCheckController < Admin::ApplicationController
+ feature_category :not_owned
+
def show
@errors = HealthCheck::Utils.process_checks(checks)
end
diff --git a/app/controllers/admin/hook_logs_controller.rb b/app/controllers/admin/hook_logs_controller.rb
index 8301b3aa880..444ad17f86d 100644
--- a/app/controllers/admin/hook_logs_controller.rb
+++ b/app/controllers/admin/hook_logs_controller.rb
@@ -8,6 +8,8 @@ class Admin::HookLogsController < Admin::ApplicationController
respond_to :html
+ feature_category :integrations
+
def show
end
diff --git a/app/controllers/admin/hooks_controller.rb b/app/controllers/admin/hooks_controller.rb
index 51b0f45c5be..ca24f671b9d 100644
--- a/app/controllers/admin/hooks_controller.rb
+++ b/app/controllers/admin/hooks_controller.rb
@@ -5,6 +5,8 @@ class Admin::HooksController < Admin::ApplicationController
before_action :hook_logs, only: :edit
+ feature_category :integrations
+
def index
@hooks = SystemHook.all
@hook = SystemHook.new
@@ -34,7 +36,7 @@ class Admin::HooksController < Admin::ApplicationController
end
def destroy
- hook.destroy
+ destroy_hook(hook)
redirect_to admin_hooks_path, status: :found
end
diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb
index 327538f1e93..dcec50e882d 100644
--- a/app/controllers/admin/identities_controller.rb
+++ b/app/controllers/admin/identities_controller.rb
@@ -4,6 +4,8 @@ class Admin::IdentitiesController < Admin::ApplicationController
before_action :user
before_action :identity, except: [:index, :new, :create]
+ feature_category :authentication_and_authorization
+
def new
@identity = Identity.new
end
diff --git a/app/controllers/admin/impersonation_tokens_controller.rb b/app/controllers/admin/impersonation_tokens_controller.rb
index c35619a944e..c3166d5dd82 100644
--- a/app/controllers/admin/impersonation_tokens_controller.rb
+++ b/app/controllers/admin/impersonation_tokens_controller.rb
@@ -3,6 +3,8 @@
class Admin::ImpersonationTokensController < Admin::ApplicationController
before_action :user
+ feature_category :authentication_and_authorization
+
def index
set_index_vars
end
diff --git a/app/controllers/admin/impersonations_controller.rb b/app/controllers/admin/impersonations_controller.rb
index 65fe22bd8f4..6c45b03455e 100644
--- a/app/controllers/admin/impersonations_controller.rb
+++ b/app/controllers/admin/impersonations_controller.rb
@@ -4,6 +4,8 @@ class Admin::ImpersonationsController < Admin::ApplicationController
skip_before_action :authenticate_admin!
before_action :authenticate_impersonator!
+ feature_category :authentication_and_authorization
+
def destroy
original_user = stop_impersonation
redirect_to admin_user_path(original_user), status: :found
diff --git a/app/controllers/admin/instance_review_controller.rb b/app/controllers/admin/instance_review_controller.rb
new file mode 100644
index 00000000000..db304c82dd6
--- /dev/null
+++ b/app/controllers/admin/instance_review_controller.rb
@@ -0,0 +1,39 @@
+# frozen_string_literal: true
+class Admin::InstanceReviewController < Admin::ApplicationController
+ feature_category :instance_statistics
+
+ def index
+ redirect_to("#{::Gitlab::SubscriptionPortal::SUBSCRIPTIONS_URL}/instance_review?#{instance_review_params}")
+ end
+
+ def instance_review_params
+ result = {
+ instance_review: {
+ email: current_user.email,
+ last_name: current_user.name,
+ version: ::Gitlab::VERSION
+ }
+ }
+
+ if Gitlab::CurrentSettings.usage_ping_enabled?
+ data = ::Gitlab::UsageData.data
+ counts = data[:counts]
+
+ result[:instance_review].merge!(
+ users_count: data[:active_user_count],
+ projects_count: counts[:projects],
+ groups_count: counts[:groups],
+ issues_count: counts[:issues],
+ merge_requests_count: counts[:merge_requests],
+ internal_pipelines_count: counts[:ci_internal_pipelines],
+ external_pipelines_count: counts[:ci_external_pipelines],
+ labels_count: counts[:labels],
+ milestones_count: counts[:milestones],
+ snippets_count: counts[:snippets],
+ notes_count: counts[:notes]
+ )
+ end
+
+ result.to_query
+ end
+end
diff --git a/app/controllers/admin/instance_statistics_controller.rb b/app/controllers/admin/instance_statistics_controller.rb
index 3aee26b97a2..dfbd704cb0c 100644
--- a/app/controllers/admin/instance_statistics_controller.rb
+++ b/app/controllers/admin/instance_statistics_controller.rb
@@ -7,6 +7,8 @@ class Admin::InstanceStatisticsController < Admin::ApplicationController
track_unique_visits :index, target_id: 'i_analytics_instance_statistics'
+ feature_category :instance_statistics
+
def index
end
diff --git a/app/controllers/admin/integrations_controller.rb b/app/controllers/admin/integrations_controller.rb
index 1e2a99f7078..9a1d5a11f7f 100644
--- a/app/controllers/admin/integrations_controller.rb
+++ b/app/controllers/admin/integrations_controller.rb
@@ -2,6 +2,9 @@
class Admin::IntegrationsController < Admin::ApplicationController
include IntegrationsActions
+ include ServicesHelper
+
+ feature_category :integrations
private
@@ -10,7 +13,7 @@ class Admin::IntegrationsController < Admin::ApplicationController
end
def integrations_enabled?
- true
+ instance_level_integrations?
end
def scoped_edit_integration_path(integration)
diff --git a/app/controllers/admin/jobs_controller.rb b/app/controllers/admin/jobs_controller.rb
index 7b50a45a9cd..b800ca79d6b 100644
--- a/app/controllers/admin/jobs_controller.rb
+++ b/app/controllers/admin/jobs_controller.rb
@@ -3,6 +3,8 @@
class Admin::JobsController < Admin::ApplicationController
BUILDS_PER_PAGE = 30
+ feature_category :continuous_integration
+
def index
# We need all builds for tabs counters
@all_builds = Ci::JobsFinder.new(current_user: current_user).execute
diff --git a/app/controllers/admin/keys_controller.rb b/app/controllers/admin/keys_controller.rb
index 58ea19d1210..03383604e30 100644
--- a/app/controllers/admin/keys_controller.rb
+++ b/app/controllers/admin/keys_controller.rb
@@ -3,6 +3,8 @@
class Admin::KeysController < Admin::ApplicationController
before_action :user, only: [:show, :destroy]
+ feature_category :authentication_and_authorization
+
def show
@key = user.keys.find(params[:id])
diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb
index 6cb206c1686..be63bf4c7ce 100644
--- a/app/controllers/admin/labels_controller.rb
+++ b/app/controllers/admin/labels_controller.rb
@@ -3,6 +3,8 @@
class Admin::LabelsController < Admin::ApplicationController
before_action :set_label, only: [:show, :edit, :update, :destroy]
+ feature_category :issue_tracking
+
def index
@labels = Label.templates.page(params[:page])
end
diff --git a/app/controllers/admin/plan_limits_controller.rb b/app/controllers/admin/plan_limits_controller.rb
index 2620db8aec5..0a5cdc06d61 100644
--- a/app/controllers/admin/plan_limits_controller.rb
+++ b/app/controllers/admin/plan_limits_controller.rb
@@ -5,6 +5,8 @@ class Admin::PlanLimitsController < Admin::ApplicationController
before_action :set_plan_limits
+ feature_category :not_owned
+
def create
redirect_path = referer_path(request) || general_admin_application_settings_path
diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb
index 9fe1f22c342..c4564478462 100644
--- a/app/controllers/admin/projects_controller.rb
+++ b/app/controllers/admin/projects_controller.rb
@@ -6,6 +6,9 @@ class Admin::ProjectsController < Admin::ApplicationController
before_action :project, only: [:show, :transfer, :repository_check, :destroy]
before_action :group, only: [:show, :transfer]
+ feature_category :projects, [:index, :show, :transfer, :destroy]
+ feature_category :source_code_management, [:repository_check]
+
def index
params[:sort] ||= 'latest_activity_desc'
@sort = params[:sort]
diff --git a/app/controllers/admin/requests_profiles_controller.rb b/app/controllers/admin/requests_profiles_controller.rb
index 24383455064..fbbe8c24637 100644
--- a/app/controllers/admin/requests_profiles_controller.rb
+++ b/app/controllers/admin/requests_profiles_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::RequestsProfilesController < Admin::ApplicationController
+ feature_category :not_owned
+
def index
@profile_token = Gitlab::RequestProfiler.profile_token
@profiles = Gitlab::RequestProfiler.all.group_by(&:request_path)
diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb
index 774ce04d079..7761ffaac84 100644
--- a/app/controllers/admin/runner_projects_controller.rb
+++ b/app/controllers/admin/runner_projects_controller.rb
@@ -3,6 +3,8 @@
class Admin::RunnerProjectsController < Admin::ApplicationController
before_action :project, only: [:create]
+ feature_category :continuous_integration
+
def create
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
diff --git a/app/controllers/admin/runners_controller.rb b/app/controllers/admin/runners_controller.rb
index 7a377a33d41..576b148fbff 100644
--- a/app/controllers/admin/runners_controller.rb
+++ b/app/controllers/admin/runners_controller.rb
@@ -1,7 +1,11 @@
# frozen_string_literal: true
class Admin::RunnersController < Admin::ApplicationController
- before_action :runner, except: [:index, :tag_list]
+ include RunnerSetupScripts
+
+ before_action :runner, except: [:index, :tag_list, :runner_setup_scripts]
+
+ feature_category :continuous_integration
def index
finder = Ci::RunnersFinder.new(current_user: current_user, params: params)
@@ -53,6 +57,10 @@ class Admin::RunnersController < Admin::ApplicationController
render json: ActsAsTaggableOn::TagSerializer.new.represent(tags)
end
+ def runner_setup_scripts
+ private_runner_setup_scripts
+ end
+
private
def runner
diff --git a/app/controllers/admin/serverless/domains_controller.rb b/app/controllers/admin/serverless/domains_controller.rb
index 1d4f10e033f..49cd9f7a36d 100644
--- a/app/controllers/admin/serverless/domains_controller.rb
+++ b/app/controllers/admin/serverless/domains_controller.rb
@@ -4,6 +4,8 @@ class Admin::Serverless::DomainsController < Admin::ApplicationController
before_action :check_feature_flag
before_action :domain, only: [:update, :verify, :destroy]
+ feature_category :serverless
+
def index
@domain = PagesDomain.instance_serverless.first_or_initialize
end
diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb
index 1f4250639c4..379e74bb249 100644
--- a/app/controllers/admin/services_controller.rb
+++ b/app/controllers/admin/services_controller.rb
@@ -6,6 +6,8 @@ class Admin::ServicesController < Admin::ApplicationController
before_action :service, only: [:edit, :update]
before_action :whitelist_query_limiting, only: [:index]
+ feature_category :integrations
+
def index
@services = Service.find_or_create_templates.sort_by(&:title)
@existing_instance_types = Service.for_instance.pluck(:type) # rubocop: disable CodeReuse/ActiveRecord
diff --git a/app/controllers/admin/sessions_controller.rb b/app/controllers/admin/sessions_controller.rb
index 0c0bbaf4d93..9c378f4c883 100644
--- a/app/controllers/admin/sessions_controller.rb
+++ b/app/controllers/admin/sessions_controller.rb
@@ -1,12 +1,14 @@
# frozen_string_literal: true
class Admin::SessionsController < ApplicationController
- include Authenticates2FAForAdminMode
+ include AuthenticatesWithTwoFactorForAdminMode
include InternalRedirect
include RendersLdapServers
before_action :user_is_admin!
+ feature_category :authentication_and_authorization
+
def new
if current_user_mode.admin_mode?
redirect_to redirect_path, notice: _('Admin mode already enabled')
@@ -65,7 +67,10 @@ class Admin::SessionsController < ApplicationController
end
def valid_otp_attempt?(user)
- valid_otp_attempt = user.validate_and_consume_otp!(user_params[:otp_attempt])
+ otp_validation_result =
+ ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
+ valid_otp_attempt = otp_validation_result[:status] == :success
+
return valid_otp_attempt if Gitlab::Database.read_only?
valid_otp_attempt || user.invalidate_otp_backup_code!(user_params[:otp_attempt])
diff --git a/app/controllers/admin/spam_logs_controller.rb b/app/controllers/admin/spam_logs_controller.rb
index 689e502a221..67d991c8b03 100644
--- a/app/controllers/admin/spam_logs_controller.rb
+++ b/app/controllers/admin/spam_logs_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::SpamLogsController < Admin::ApplicationController
+ feature_category :not_owned
+
# rubocop: disable CodeReuse/ActiveRecord
def index
@spam_logs = SpamLog.order(id: :desc).page(params[:page])
diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb
index 657aa177ecf..f14305528a3 100644
--- a/app/controllers/admin/system_info_controller.rb
+++ b/app/controllers/admin/system_info_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Admin::SystemInfoController < Admin::ApplicationController
+ feature_category :not_owned
+
EXCLUDED_MOUNT_OPTIONS = %w[
nobrowse
read-only
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index e19b09e1324..bd7b69384b2 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -6,10 +6,14 @@ class Admin::UsersController < Admin::ApplicationController
before_action :user, except: [:index, :new, :create]
before_action :check_impersonation_availability, only: :impersonate
before_action :ensure_destroy_prerequisites_met, only: [:destroy]
+ before_action :check_admin_approval_feature_available!, only: [:approve]
+
+ feature_category :users
def index
@users = User.filter_items(params[:filter]).order_name_asc
@users = @users.search_with_secondary_emails(params[:search_query]) if params[:search_query].present?
+ @users = @users.includes(:authorized_projects) # rubocop: disable CodeReuse/ActiveRecord
@users = @users.sort_by_attribute(@sort = params[:sort])
@users = @users.page(params[:page])
end
@@ -59,6 +63,16 @@ class Admin::UsersController < Admin::ApplicationController
end
end
+ def approve
+ result = Users::ApproveService.new(current_user).execute(user)
+
+ if result[:status] == :success
+ redirect_back_or_admin_user(notice: _("Successfully approved"))
+ else
+ redirect_back_or_admin_user(alert: result[:message])
+ end
+ end
+
def activate
return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user must be unblocked to be activated")) if user.blocked?
@@ -69,6 +83,7 @@ class Admin::UsersController < Admin::ApplicationController
def deactivate
return redirect_back_or_admin_user(notice: _("Error occurred. A blocked user cannot be deactivated")) if user.blocked?
return redirect_back_or_admin_user(notice: _("Successfully deactivated")) if user.deactivated?
+ return redirect_back_or_admin_user(notice: _("Internal users cannot be deactivated")) if user.internal?
return redirect_back_or_admin_user(notice: _("The user you are trying to deactivate has been active in the past %{minimum_inactive_days} days and cannot be deactivated") % { minimum_inactive_days: ::User::MINIMUM_INACTIVE_DAYS }) unless user.can_be_deactivated?
user.deactivate
@@ -78,7 +93,7 @@ class Admin::UsersController < Admin::ApplicationController
def block
result = Users::BlockService.new(current_user).execute(user)
- if result[:status] = :success
+ if result[:status] == :success
redirect_back_or_admin_user(notice: _("Successfully blocked"))
else
redirect_back_or_admin_user(alert: _("Error occurred. User was not blocked"))
@@ -168,7 +183,7 @@ class Admin::UsersController < Admin::ApplicationController
# restore username to keep form action url.
user.username = params[:id]
format.html { render "edit" }
- format.json { render json: [result[:message]], status: result[:status] }
+ format.json { render json: [result[:message]], status: :internal_server_error }
end
end
end
@@ -283,6 +298,10 @@ class Admin::UsersController < Admin::ApplicationController
def log_impersonation_event
Gitlab::AppLogger.info(_("User %{current_user_username} has started impersonating %{username}") % { current_user_username: current_user.username, username: user.username })
end
+
+ def check_admin_approval_feature_available!
+ access_denied! unless Feature.enabled?(:admin_approval_for_new_user_signups, default_enabled: true)
+ end
end
Admin::UsersController.prepend_if_ee('EE::Admin::UsersController')
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 5f05337e59e..05f496c3b99 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -271,6 +271,7 @@ class ApplicationController < ActionController::Base
headers['X-XSS-Protection'] = '1; mode=block'
headers['X-UA-Compatible'] = 'IE=edge'
headers['X-Content-Type-Options'] = 'nosniff'
+ headers[Gitlab::Metrics::RequestsRackMiddleware::FEATURE_CATEGORY_HEADER] = feature_category
end
def default_cache_headers
@@ -465,7 +466,8 @@ class ApplicationController < ActionController::Base
user: -> { auth_user if strong_memoized?(:auth_user) },
project: -> { @project if @project&.persisted? },
namespace: -> { @group if @group&.persisted? },
- caller_id: full_action_name) do
+ caller_id: caller_id,
+ feature_category: feature_category) do
yield
ensure
@current_context = Labkit::Context.current.to_h
@@ -484,7 +486,7 @@ class ApplicationController < ActionController::Base
def set_page_title_header
# Per https://tools.ietf.org/html/rfc5987, headers need to be ISO-8859-1, not UTF-8
- response.headers['Page-Title'] = URI.escape(page_title('GitLab'))
+ response.headers['Page-Title'] = Addressable::URI.encode_component(page_title('GitLab'))
end
def set_current_admin(&block)
@@ -547,10 +549,14 @@ class ApplicationController < ActionController::Base
end
end
- def full_action_name
+ def caller_id
"#{self.class.name}##{action_name}"
end
+ def feature_category
+ self.class.feature_category_for_action(action_name).to_s
+ end
+
def required_signup_info
return unless current_user
return unless current_user.role_required?
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 99fa17e202a..ac4ee14c6a9 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -3,6 +3,12 @@
class AutocompleteController < ApplicationController
skip_before_action :authenticate_user!, only: [:users, :award_emojis, :merge_request_target_branches]
+ feature_category :users, [:users, :user]
+ feature_category :projects, [:projects]
+ feature_category :issue_tracking, [:award_emojis]
+ feature_category :code_review, [:merge_request_target_branches]
+ feature_category :continuous_delivery, [:deploy_keys_with_owners]
+
def users
group = Autocomplete::GroupFinder
.new(current_user, project, params)
diff --git a/app/controllers/boards/issues_controller.rb b/app/controllers/boards/issues_controller.rb
index a18c80b996e..f5a9b9b61db 100644
--- a/app/controllers/boards/issues_controller.rb
+++ b/app/controllers/boards/issues_controller.rb
@@ -21,6 +21,8 @@ module Boards
before_action :validate_id_list, only: [:bulk_move]
before_action :can_move_issues?, only: [:bulk_move]
+ feature_category :boards
+
def index
list_service = Boards::Issues::ListService.new(board_parent, current_user, filter_params)
issues = issues_from(list_service)
diff --git a/app/controllers/boards/lists_controller.rb b/app/controllers/boards/lists_controller.rb
index 0b8469e8290..aecd287370f 100644
--- a/app/controllers/boards/lists_controller.rb
+++ b/app/controllers/boards/lists_controller.rb
@@ -8,6 +8,8 @@ module Boards
before_action :authorize_read_list, only: [:index]
skip_before_action :authenticate_user!, only: [:index]
+ feature_category :boards
+
def index
lists = Boards::Lists::ListService.new(board.resource_parent, current_user).execute(board)
@@ -42,7 +44,7 @@ module Boards
list = board.lists.destroyable.find(params[:id])
service = Boards::Lists::DestroyService.new(board_parent, current_user)
- if service.execute(list)
+ if service.execute(list).success?
head :ok
else
head :unprocessable_entity
diff --git a/app/controllers/clusters/base_controller.rb b/app/controllers/clusters/base_controller.rb
index 188805c6106..b1ffdf00b87 100644
--- a/app/controllers/clusters/base_controller.rb
+++ b/app/controllers/clusters/base_controller.rb
@@ -8,6 +8,8 @@ class Clusters::BaseController < ApplicationController
helper_method :clusterable
+ feature_category :kubernetes_management
+
private
def cluster
diff --git a/app/controllers/clusters/clusters_controller.rb b/app/controllers/clusters/clusters_controller.rb
index 7006c23321c..52719e90e04 100644
--- a/app/controllers/clusters/clusters_controller.rb
+++ b/app/controllers/clusters/clusters_controller.rb
@@ -180,13 +180,20 @@ class Clusters::ClustersController < Clusters::BaseController
params.permit(:cleanup)
end
+ def base_permitted_cluster_params
+ [
+ :enabled,
+ :environment_scope,
+ :managed,
+ :namespace_per_environment
+ ]
+ end
+
def update_params
if cluster.provided_by_user?
params.require(:cluster).permit(
- :enabled,
+ *base_permitted_cluster_params,
:name,
- :environment_scope,
- :managed,
:base_domain,
:management_project_id,
platform_kubernetes_attributes: [
@@ -198,9 +205,7 @@ class Clusters::ClustersController < Clusters::BaseController
)
else
params.require(:cluster).permit(
- :enabled,
- :environment_scope,
- :managed,
+ *base_permitted_cluster_params,
:base_domain,
:management_project_id,
platform_kubernetes_attributes: [
@@ -212,10 +217,8 @@ class Clusters::ClustersController < Clusters::BaseController
def create_gcp_cluster_params
params.require(:cluster).permit(
- :enabled,
+ *base_permitted_cluster_params,
:name,
- :environment_scope,
- :managed,
provider_gcp_attributes: [
:gcp_project_id,
:zone,
@@ -232,10 +235,8 @@ class Clusters::ClustersController < Clusters::BaseController
def create_aws_cluster_params
params.require(:cluster).permit(
- :enabled,
+ *base_permitted_cluster_params,
:name,
- :environment_scope,
- :managed,
provider_aws_attributes: [
:kubernetes_version,
:key_name,
@@ -255,10 +256,8 @@ class Clusters::ClustersController < Clusters::BaseController
def create_user_cluster_params
params.require(:cluster).permit(
- :enabled,
+ *base_permitted_cluster_params,
:name,
- :environment_scope,
- :managed,
platform_kubernetes_attributes: [
:namespace,
:api_url,
diff --git a/app/controllers/concerns/authenticates_with_two_factor.rb b/app/controllers/concerns/authenticates_with_two_factor.rb
index 9ff97f398f5..5c74d79951f 100644
--- a/app/controllers/concerns/authenticates_with_two_factor.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor.rb
@@ -89,10 +89,7 @@ module AuthenticatesWithTwoFactor
user.save!
sign_in(user, message: :two_factor_authenticated, event: :authentication)
else
- user.increment_failed_attempts!
- Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=OTP")
- flash.now[:alert] = _('Invalid two-factor code.')
- prompt_for_two_factor(user)
+ handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.'))
end
end
@@ -101,7 +98,7 @@ module AuthenticatesWithTwoFactor
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
handle_two_factor_success(user)
else
- handle_two_factor_failure(user, 'U2F')
+ handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.'))
end
end
@@ -109,7 +106,7 @@ module AuthenticatesWithTwoFactor
if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute
handle_two_factor_success(user)
else
- handle_two_factor_failure(user, 'WebAuthn')
+ handle_two_factor_failure(user, 'WebAuthn', _('Authentication via WebAuthn device failed.'))
end
end
@@ -152,13 +149,19 @@ module AuthenticatesWithTwoFactor
sign_in(user, message: :two_factor_authenticated, event: :authentication)
end
- def handle_two_factor_failure(user, method)
+ def handle_two_factor_failure(user, method, message)
user.increment_failed_attempts!
+ log_failed_two_factor(user, method, request.remote_ip)
+
Gitlab::AppLogger.info("Failed Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}")
- flash.now[:alert] = _('Authentication via %{method} device failed.') % { method: method }
+ flash.now[:alert] = message
prompt_for_two_factor(user)
end
+ def log_failed_two_factor(user, method, ip_address)
+ # overridden in EE
+ end
+
def handle_changed_user(user)
clear_two_factor_attempt!
@@ -173,3 +176,5 @@ module AuthenticatesWithTwoFactor
Digest::SHA256.hexdigest(user.encrypted_password) != session[:user_password_hash]
end
end
+
+AuthenticatesWithTwoFactor.prepend_if_ee('EE::AuthenticatesWithTwoFactor')
diff --git a/app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb
index 03783cd75a3..a8155f1e639 100644
--- a/app/controllers/admin/concerns/authenticates_2fa_for_admin_mode.rb
+++ b/app/controllers/concerns/authenticates_with_two_factor_for_admin_mode.rb
@@ -1,6 +1,6 @@
# frozen_string_literal: true
-module Authenticates2FAForAdminMode
+module AuthenticatesWithTwoFactorForAdminMode
extend ActiveSupport::Concern
included do
@@ -52,11 +52,7 @@ module Authenticates2FAForAdminMode
# The admin user has successfully passed 2fa, enable admin mode ignoring password
enable_admin_mode
else
- user.increment_failed_attempts!
- Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=OTP")
- flash.now[:alert] = _('Invalid two-factor code.')
-
- admin_mode_prompt_for_two_factor(user)
+ admin_handle_two_factor_failure(user, 'OTP', _('Invalid two-factor code.'))
end
end
@@ -64,7 +60,7 @@ module Authenticates2FAForAdminMode
if U2fRegistration.authenticate(user, u2f_app_id, user_params[:device_response], session[:challenge])
admin_handle_two_factor_success
else
- admin_handle_two_factor_failure(user, 'U2F')
+ admin_handle_two_factor_failure(user, 'U2F', _('Authentication via U2F device failed.'))
end
end
@@ -72,7 +68,7 @@ module Authenticates2FAForAdminMode
if Webauthn::AuthenticateService.new(user, user_params[:device_response], session[:challenge]).execute
admin_handle_two_factor_success
else
- admin_handle_two_factor_failure(user, 'WebAuthn')
+ admin_handle_two_factor_failure(user, 'WebAuthn', _('Authentication via WebAuthn device failed.'))
end
end
@@ -100,11 +96,12 @@ module Authenticates2FAForAdminMode
enable_admin_mode
end
- def admin_handle_two_factor_failure(user, method)
+ def admin_handle_two_factor_failure(user, method, message)
user.increment_failed_attempts!
- Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}")
- flash.now[:alert] = _('Authentication via %{method} device failed.') % { method: method }
+ log_failed_two_factor(user, method, request.remote_ip)
+ Gitlab::AppLogger.info("Failed Admin Mode Login: user=#{user.username} ip=#{request.remote_ip} method=#{method}")
+ flash.now[:alert] = message
admin_mode_prompt_for_two_factor(user)
end
end
diff --git a/app/controllers/concerns/boards_actions.rb b/app/controllers/concerns/boards_actions.rb
index 9d40b9e8c88..b382e338a78 100644
--- a/app/controllers/concerns/boards_actions.rb
+++ b/app/controllers/concerns/boards_actions.rb
@@ -9,7 +9,7 @@ module BoardsActions
before_action :boards, only: :index
before_action :board, only: :show
- before_action :push_wip_limits, only: [:index, :show]
+ before_action :push_licensed_features, only: [:index, :show]
before_action do
push_frontend_feature_flag(:not_issuable_queries, parent, default_enabled: true)
end
@@ -29,7 +29,7 @@ module BoardsActions
private
# Noop on FOSS
- def push_wip_limits
+ def push_licensed_features
end
def boards
diff --git a/app/controllers/concerns/controller_with_feature_category.rb b/app/controllers/concerns/controller_with_feature_category.rb
index f8985cf0950..c1ff9ef2e69 100644
--- a/app/controllers/concerns/controller_with_feature_category.rb
+++ b/app/controllers/concerns/controller_with_feature_category.rb
@@ -5,35 +5,38 @@ module ControllerWithFeatureCategory
include Gitlab::ClassAttributes
class_methods do
- def feature_category(category, config = {})
- validate_config!(config)
+ def feature_category(category, actions = [])
+ feature_category_configuration[category] ||= []
+ feature_category_configuration[category] += actions.map(&:to_s)
- category_config = Config.new(category, config[:only], config[:except], config[:if], config[:unless])
- # Add the config to the beginning. That way, the last defined one takes precedence.
- feature_category_configuration.unshift(category_config)
+ validate_config!(feature_category_configuration)
end
def feature_category_for_action(action)
- category_config = feature_category_configuration.find { |config| config.matches?(action) }
+ category_config = feature_category_configuration.find do |_, actions|
+ actions.empty? || actions.include?(action)
+ end
- category_config&.category || superclass_feature_category_for_action(action)
+ category_config&.first || superclass_feature_category_for_action(action)
end
private
def validate_config!(config)
- invalid_keys = config.keys - [:only, :except, :if, :unless]
- if invalid_keys.any?
- raise ArgumentError, "unknown arguments: #{invalid_keys} "
+ empty = config.find { |_, actions| actions.empty? }
+ duplicate_actions = config.values.flatten.group_by(&:itself).select { |_, v| v.count > 1 }.keys
+
+ if config.length > 1 && empty
+ raise ArgumentError, "#{empty.first} is defined for all actions, but other categories are set"
end
- if config.key?(:only) && config.key?(:except)
- raise ArgumentError, "cannot configure both `only` and `except`"
+ if duplicate_actions.any?
+ raise ArgumentError, "Actions have multiple feature categories: #{duplicate_actions.join(', ')}"
end
end
def feature_category_configuration
- class_attributes[:feature_category_config] ||= []
+ class_attributes[:feature_category_config] ||= {}
end
def superclass_feature_category_for_action(action)
diff --git a/app/controllers/concerns/controller_with_feature_category/config.rb b/app/controllers/concerns/controller_with_feature_category/config.rb
deleted file mode 100644
index 624691ee4f6..00000000000
--- a/app/controllers/concerns/controller_with_feature_category/config.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-# frozen_string_literal: true
-
-module ControllerWithFeatureCategory
- class Config
- attr_reader :category
-
- def initialize(category, only, except, if_proc, unless_proc)
- @category = category.to_sym
- @only, @except = only&.map(&:to_s), except&.map(&:to_s)
- @if_proc, @unless_proc = if_proc, unless_proc
- end
-
- def matches?(action)
- included?(action) && !excluded?(action) &&
- if_proc?(action) && !unless_proc?(action)
- end
-
- private
-
- attr_reader :only, :except, :if_proc, :unless_proc
-
- def if_proc?(action)
- if_proc.nil? || if_proc.call(action)
- end
-
- def unless_proc?(action)
- unless_proc.present? && unless_proc.call(action)
- end
-
- def included?(action)
- only.nil? || only.include?(action)
- end
-
- def excluded?(action)
- except.present? && except.include?(action)
- end
- end
-end
diff --git a/app/controllers/concerns/hooks_execution.rb b/app/controllers/concerns/hooks_execution.rb
index ad1f8341109..87d215f50e7 100644
--- a/app/controllers/concerns/hooks_execution.rb
+++ b/app/controllers/concerns/hooks_execution.rb
@@ -5,6 +5,21 @@ module HooksExecution
private
+ def destroy_hook(hook)
+ result = WebHooks::DestroyService.new(current_user).execute(hook)
+
+ if result[:status] == :success
+ flash[:notice] =
+ if result[:async]
+ _("%{hook_type} was scheduled for deletion") % { hook_type: hook.model_name.human }
+ else
+ _("%{hook_type} was deleted") % { hook_type: hook.model_name.human }
+ end
+ else
+ flash[:alert] = result[:message]
+ end
+ end
+
def set_hook_execution_notice(result)
http_status = result[:http_status]
message = result[:message]
diff --git a/app/controllers/concerns/integrations_actions.rb b/app/controllers/concerns/integrations_actions.rb
index 6060dc729af..39f63bbaaec 100644
--- a/app/controllers/concerns/integrations_actions.rb
+++ b/app/controllers/concerns/integrations_actions.rb
@@ -20,7 +20,7 @@ module IntegrationsActions
respond_to do |format|
format.html do
if saved
- PropagateIntegrationWorker.perform_async(integration.id, false)
+ PropagateIntegrationWorker.perform_async(integration.id)
redirect_to scoped_edit_integration_path(integration), notice: success_message
else
render 'shared/integrations/edit'
diff --git a/app/controllers/concerns/issuable_collections.rb b/app/controllers/concerns/issuable_collections.rb
index 89ba2175b60..0d7af57328a 100644
--- a/app/controllers/concerns/issuable_collections.rb
+++ b/app/controllers/concerns/issuable_collections.rb
@@ -41,10 +41,13 @@ module IssuableCollections
end
def set_pagination
+ row_count = finder.row_count
+
@issuables = @issuables.page(params[:page])
@issuables = per_page_for_relative_position if params[:sort] == 'relative_position'
+ @issuables = @issuables.without_count if row_count == -1
@issuable_meta_data = Gitlab::IssuableMetadata.new(current_user, @issuables).data
- @total_pages = issuable_page_count(@issuables)
+ @total_pages = page_count_for_relation(@issuables, row_count)
end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
@@ -58,14 +61,11 @@ module IssuableCollections
end
# rubocop: enable CodeReuse/ActiveRecord
- def issuable_page_count(relation)
- page_count_for_relation(relation, finder.row_count)
- end
-
def page_count_for_relation(relation, row_count)
limit = relation.limit_value.to_f
return 1 if limit == 0
+ return (params[:page] || 1).to_i + 1 if row_count == -1
(row_count.to_f / limit).ceil
end
diff --git a/app/controllers/concerns/issuable_collections_action.rb b/app/controllers/concerns/issuable_collections_action.rb
index e3ac117660b..7ed66027da3 100644
--- a/app/controllers/concerns/issuable_collections_action.rb
+++ b/app/controllers/concerns/issuable_collections_action.rb
@@ -59,6 +59,9 @@ module IssuableCollectionsAction
end
def finder_options
- super.merge(non_archived: true)
+ super.merge(
+ non_archived: true,
+ issue_types: Issue::TYPES_FOR_LIST
+ )
end
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index 8c7f156f7f8..816a93f14c6 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -22,10 +22,14 @@ module MembershipActions
.new(current_user, update_params)
.execute(member)
- member = present_members([member]).first
-
- respond_to do |format|
- format.js { render 'shared/members/update', locals: { member: member } }
+ if member.expires?
+ render json: {
+ expires_in: helpers.distance_of_time_in_words_to_now(member.expires_at),
+ expires_soon: member.expires_soon?,
+ expires_at_formatted: member.expires_at.to_time.in_time_zone.to_s(:medium)
+ }
+ else
+ render json: {}
end
end
@@ -101,7 +105,7 @@ module MembershipActions
# rubocop: enable CodeReuse/ActiveRecord
def resend_invite
- member = membershipable.members.find(params[:id])
+ member = membershipable_members.find(params[:id])
if member.invite?
member.resend_invite
@@ -118,6 +122,10 @@ module MembershipActions
raise NotImplementedError
end
+ def membershipable_members
+ raise NotImplementedError
+ end
+
def root_params_key
case membershipable
when Namespace
diff --git a/app/controllers/concerns/milestone_actions.rb b/app/controllers/concerns/milestone_actions.rb
index 29138e7b014..6470c75dfbd 100644
--- a/app/controllers/concerns/milestone_actions.rb
+++ b/app/controllers/concerns/milestone_actions.rb
@@ -3,13 +3,25 @@
module MilestoneActions
extend ActiveSupport::Concern
+ def issues
+ respond_to do |format|
+ format.html { redirect_to milestone_redirect_path }
+ format.json do
+ render json: tabs_json("shared/milestones/_issues_tab", {
+ issues: @milestone.sorted_issues(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name])
+ })
+ end
+ end
+ end
+
def merge_requests
respond_to do |format|
format.html { redirect_to milestone_redirect_path }
format.json do
render json: tabs_json("shared/milestones/_merge_requests_tab", {
merge_requests: @milestone.sorted_merge_requests(current_user), # rubocop:disable Gitlab/ModuleWithInstanceVariables
- show_project_name: true
+ show_project_name: Gitlab::Utils.to_boolean(params[:show_project_name])
})
end
end
diff --git a/app/controllers/concerns/multiple_boards_actions.rb b/app/controllers/concerns/multiple_boards_actions.rb
index 95a6800f55c..370b8c72bfe 100644
--- a/app/controllers/concerns/multiple_boards_actions.rb
+++ b/app/controllers/concerns/multiple_boards_actions.rb
@@ -21,11 +21,13 @@ module MultipleBoardsActions
end
def create
- board = Boards::CreateService.new(parent, current_user, board_params).execute
+ response = Boards::CreateService.new(parent, current_user, board_params).execute
respond_to do |format|
format.json do
- if board.persisted?
+ board = response.payload
+
+ if response.success?
extra_json = { board_path: board_path(board) }
render json: serialize_as_json(board).merge(extra_json)
else
diff --git a/app/controllers/concerns/redis_tracking.rb b/app/controllers/concerns/redis_tracking.rb
index fa5eef981d1..d81bd10d5bb 100644
--- a/app/controllers/concerns/redis_tracking.rb
+++ b/app/controllers/concerns/redis_tracking.rb
@@ -11,12 +11,17 @@
#
# if the feature flag is enabled by default you should use
# track_redis_hll_event :index, :show, name: 'i_analytics_dev_ops_score', feature: :my_feature, feature_default_enabled: true
+#
+# You can also pass custom conditions using `if:`, using the same format as with Rails callbacks.
module RedisTracking
extend ActiveSupport::Concern
class_methods do
- def track_redis_hll_event(*controller_actions, name:, feature:, feature_default_enabled: false)
- after_action only: controller_actions, if: -> { request.format.html? && request.headers['DNT'] != '1' } do
+ def track_redis_hll_event(*controller_actions, name:, feature:, feature_default_enabled: false, if: nil)
+ custom_conditions = Array.wrap(binding.local_variable_get('if'))
+ conditions = [:trackable_request?, *custom_conditions]
+
+ after_action only: controller_actions, if: conditions do
track_unique_redis_hll_event(name, feature, feature_default_enabled)
end
end
@@ -26,12 +31,15 @@ module RedisTracking
def track_unique_redis_hll_event(event_name, feature, feature_default_enabled)
return unless metric_feature_enabled?(feature, feature_default_enabled)
- return unless Gitlab::CurrentSettings.usage_ping_enabled?
return unless visitor_id
Gitlab::UsageDataCounters::HLLRedisCounter.track_event(visitor_id, event_name)
end
+ def trackable_request?
+ request.format.html? && request.headers['DNT'] != '1'
+ end
+
def metric_feature_enabled?(feature, default_enabled)
Feature.enabled?(feature, default_enabled: default_enabled)
end
diff --git a/app/controllers/concerns/runner_setup_scripts.rb b/app/controllers/concerns/runner_setup_scripts.rb
new file mode 100644
index 00000000000..c0e657a32d1
--- /dev/null
+++ b/app/controllers/concerns/runner_setup_scripts.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+module RunnerSetupScripts
+ extend ActiveSupport::Concern
+
+ private
+
+ def private_runner_setup_scripts(**kwargs)
+ instructions = Gitlab::Ci::RunnerInstructions.new(current_user: current_user, os: script_params[:os], arch: script_params[:arch], **kwargs)
+ output = {
+ install: instructions.install_script,
+ register: instructions.register_command
+ }
+
+ if instructions.errors.any?
+ render json: { errors: instructions.errors }, status: :bad_request
+ else
+ render json: output
+ end
+ end
+
+ def script_params
+ params.permit(:os, :arch)
+ end
+end
diff --git a/app/controllers/concerns/show_inherited_labels_checker.rb b/app/controllers/concerns/show_inherited_labels_checker.rb
new file mode 100644
index 00000000000..9847226f599
--- /dev/null
+++ b/app/controllers/concerns/show_inherited_labels_checker.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+module ShowInheritedLabelsChecker
+ extend ActiveSupport::Concern
+
+ private
+
+ def show_inherited_labels?(include_ancestor_groups)
+ Feature.enabled?(:show_inherited_labels, @project || @group, default_enabled: true) || include_ancestor_groups # rubocop:disable Gitlab/ModuleWithInstanceVariables
+ end
+end
diff --git a/app/controllers/concerns/snippets_actions.rb b/app/controllers/concerns/snippets_actions.rb
index 4548595d968..e4c3df6ccc3 100644
--- a/app/controllers/concerns/snippets_actions.rb
+++ b/app/controllers/concerns/snippets_actions.rb
@@ -17,13 +17,7 @@ module SnippetsActions
respond_to :html
end
- def edit
- # We need to load some info from the existing blob
- snippet.content = blob.data
- snippet.file_name = blob.path
-
- render 'edit'
- end
+ def edit; end
# This endpoint is being replaced by Snippets::BlobController#raw
# Support for old raw links will be maintainted via this action but
@@ -55,7 +49,6 @@ module SnippetsActions
def show
respond_to do |format|
format.html do
- conditionally_expand_blob(blob)
@note = Note.new(noteable: @snippet, project: @snippet.project)
@noteable = @snippet
@@ -80,29 +73,6 @@ module SnippetsActions
end
end
end
-
- def update
- update_params = snippet_params.merge(spammable_params)
-
- service_response = Snippets::UpdateService.new(@snippet.project, current_user, update_params).execute(@snippet)
- @snippet = service_response.payload[:snippet]
-
- handle_repository_error(:edit)
- end
-
- def destroy
- service_response = Snippets::DestroyService.new(current_user, @snippet).execute
-
- if service_response.success?
- redirect_to gitlab_dashboard_snippets_path(@snippet), status: :found
- elsif service_response.http_status == 403
- access_denied!
- else
- redirect_to gitlab_snippet_path(@snippet),
- status: :found,
- alert: service_response.message
- end
- end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private
@@ -124,12 +94,4 @@ module SnippetsActions
def convert_line_endings(content)
params[:line_ending] == 'raw' ? content : content.gsub(/\r\n/, "\n")
end
-
- def handle_repository_error(action)
- errors = Array(snippet.errors.delete(:repository))
-
- flash.now[:alert] = errors.first if errors.present?
-
- recaptcha_check_with_fallback(errors.empty?) { render action }
- end
end
diff --git a/app/controllers/concerns/wiki_actions.rb b/app/controllers/concerns/wiki_actions.rb
index 5a5b634da40..aed109309e3 100644
--- a/app/controllers/concerns/wiki_actions.rb
+++ b/app/controllers/concerns/wiki_actions.rb
@@ -5,6 +5,7 @@ module WikiActions
include PreviewMarkdown
include SendsBlob
include Gitlab::Utils::StrongMemoize
+ include RedisTracking
extend ActiveSupport::Concern
included do
@@ -31,6 +32,11 @@ module WikiActions
end
end
+ # NOTE: We want to include wiki page views in the same counter as the other
+ # Event-based wiki actions tracked through TrackUniqueEvents, so we use the same event name.
+ track_redis_hll_event :show, name: Gitlab::UsageDataCounters::TrackUniqueEvents::WIKI_ACTION.to_s,
+ feature: :track_unique_wiki_page_views, feature_default_enabled: true
+
helper_method :view_file_button, :diff_file_html_data
end
@@ -44,7 +50,7 @@ module WikiActions
wiki.list_pages(sort: params[:sort], direction: params[:direction])
).page(params[:page])
- @wiki_entries = WikiPage.group_by_directory(@wiki_pages)
+ @wiki_entries = WikiDirectory.group_pages(@wiki_pages)
render 'shared/wikis/pages'
end
diff --git a/app/controllers/confirmations_controller.rb b/app/controllers/confirmations_controller.rb
index a27c4027380..c42c9827eaf 100644
--- a/app/controllers/confirmations_controller.rb
+++ b/app/controllers/confirmations_controller.rb
@@ -3,6 +3,8 @@
class ConfirmationsController < Devise::ConfirmationsController
include AcceptsPendingInvitations
+ feature_category :users
+
def almost_there
flash[:notice] = nil
render layout: "devise_empty"
diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb
index f82cde8e10a..23ffcd50369 100644
--- a/app/controllers/dashboard/groups_controller.rb
+++ b/app/controllers/dashboard/groups_controller.rb
@@ -5,6 +5,8 @@ class Dashboard::GroupsController < Dashboard::ApplicationController
skip_cross_project_access_check :index
+ feature_category :subgroups
+
def index
groups = GroupsFinder.new(current_user, all_available: false).execute
render_group_tree(groups)
diff --git a/app/controllers/dashboard/labels_controller.rb b/app/controllers/dashboard/labels_controller.rb
index 89d87c2d5c8..b661efa12c0 100644
--- a/app/controllers/dashboard/labels_controller.rb
+++ b/app/controllers/dashboard/labels_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Dashboard::LabelsController < Dashboard::ApplicationController
+ feature_category :issue_tracking
+
def index
respond_to do |format|
format.json { render json: LabelSerializer.new.represent_appearance(labels) }
@@ -9,8 +11,8 @@ class Dashboard::LabelsController < Dashboard::ApplicationController
def labels
finder_params = { project_ids: projects.select(:id) }
- labels = LabelsFinder.new(current_user, finder_params).execute
- GlobalLabel.build_collection(labels)
+ LabelsFinder.new(current_user, finder_params).execute
+ .select('DISTINCT ON (labels.title) labels.*')
end
end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 14f9a026688..e17b16c26a2 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -4,6 +4,8 @@ class Dashboard::MilestonesController < Dashboard::ApplicationController
before_action :projects
before_action :groups, only: :index
+ feature_category :issue_tracking
+
def index
respond_to do |format|
format.html do
diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb
index 2bd6fd85381..f7a74f40e4b 100644
--- a/app/controllers/dashboard/projects_controller.rb
+++ b/app/controllers/dashboard/projects_controller.rb
@@ -14,6 +14,8 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController
before_action :projects, only: [:index]
skip_cross_project_access_check :index, :starred
+ feature_category :projects
+
def index
respond_to do |format|
format.html do
diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb
index a8ca3dbd0e7..6fe3d878639 100644
--- a/app/controllers/dashboard/snippets_controller.rb
+++ b/app/controllers/dashboard/snippets_controller.rb
@@ -7,6 +7,8 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController
skip_cross_project_access_check :index
+ feature_category :snippets
+
def index
@snippet_counts = Snippets::CountService
.new(current_user, author: current_user)
diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb
index 4fc2f7b0571..0ae326b5d94 100644
--- a/app/controllers/dashboard/todos_controller.rb
+++ b/app/controllers/dashboard/todos_controller.rb
@@ -9,6 +9,8 @@ class Dashboard::TodosController < Dashboard::ApplicationController
before_action :authorize_read_group!, only: :index
before_action :find_todos, only: [:index, :destroy_all]
+ feature_category :issue_tracking
+
def index
@sort = params[:sort]
@todos = @todos.page(params[:page])
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 07cc31fb7d3..a88cf64d842 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -15,6 +15,10 @@ class DashboardController < Dashboard::ApplicationController
respond_to :html
+ feature_category :audit_events, [:activity]
+ feature_category :issue_tracking, [:issues, :issues_calendar]
+ feature_category :code_review, [:merge_requests]
+
def activity
respond_to do |format|
format.html
diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb
index 67db797b80a..aa4196b1c18 100644
--- a/app/controllers/explore/groups_controller.rb
+++ b/app/controllers/explore/groups_controller.rb
@@ -3,6 +3,8 @@
class Explore::GroupsController < Explore::ApplicationController
include GroupTree
+ feature_category :subgroups
+
def index
render_group_tree GroupsFinder.new(current_user).execute
end
diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb
index b3fa089a712..42795e418a4 100644
--- a/app/controllers/explore/projects_controller.rb
+++ b/app/controllers/explore/projects_controller.rb
@@ -18,6 +18,8 @@ class Explore::ProjectsController < Explore::ApplicationController
rescue_from PageOutOfBoundsError, with: :page_out_of_bounds
+ feature_category :projects
+
def index
@projects = load_projects
diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb
index 3a56a48e578..91ab18f2f55 100644
--- a/app/controllers/explore/snippets_controller.rb
+++ b/app/controllers/explore/snippets_controller.rb
@@ -3,6 +3,8 @@
class Explore::SnippetsController < Explore::ApplicationController
include Gitlab::NoteableMetadata
+ feature_category :snippets
+
def index
@snippets = SnippetsFinder.new(current_user, explore: true)
.execute
diff --git a/app/controllers/google_api/authorizations_controller.rb b/app/controllers/google_api/authorizations_controller.rb
index 5723ccc14a7..76a1c43dfa3 100644
--- a/app/controllers/google_api/authorizations_controller.rb
+++ b/app/controllers/google_api/authorizations_controller.rb
@@ -6,6 +6,8 @@ module GoogleApi
before_action :validate_session_key!
+ feature_category :kubernetes_management
+
def callback
token, expires_at = GoogleApi::CloudPlatform::Client
.new(nil, callback_google_api_auth_url)
diff --git a/app/controllers/graphql_controller.rb b/app/controllers/graphql_controller.rb
index 123102bf793..b5deed70380 100644
--- a/app/controllers/graphql_controller.rb
+++ b/app/controllers/graphql_controller.rb
@@ -26,6 +26,8 @@ class GraphqlController < ApplicationController
# callback execution order here
around_action :sessionless_bypass_admin_mode!, if: :sessionless_user?
+ feature_category :not_owned
+
def execute
result = multiplex? ? execute_multiplex : execute_query
@@ -46,6 +48,10 @@ class GraphqlController < ApplicationController
render_error(exception.message, status: :unprocessable_entity)
end
+ rescue_from ::GraphQL::CoercionError do |exception|
+ render_error(exception.message, status: :unprocessable_entity)
+ end
+
private
def set_user_last_activity
@@ -81,7 +87,7 @@ class GraphqlController < ApplicationController
end
def context
- @context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user? }
+ @context ||= { current_user: current_user, is_sessionless_user: !!sessionless_user?, request: request }
end
def build_variables(variable_info)
@@ -113,6 +119,12 @@ class GraphqlController < ApplicationController
# Merging to :metadata will ensure these are logged as top level keys
payload[:metadata] ||= {}
- payload[:metadata].merge!(graphql: { operation_name: params[:operationName] })
+ payload[:metadata].merge!(graphql: logs)
+ end
+
+ def logs
+ RequestStore.store[:graphql_logs].to_h
+ .except(:duration_s, :query_string)
+ .merge(operation_name: params[:operationName])
end
end
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 8e4dc2bb6e9..1f13be449a9 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -5,6 +5,8 @@ class Groups::AvatarsController < Groups::ApplicationController
skip_cross_project_access_check :destroy
+ feature_category :subgroups
+
def destroy
@group.remove_avatar!
@group.save
diff --git a/app/controllers/groups/boards_controller.rb b/app/controllers/groups/boards_controller.rb
index ea7e83a2caf..b971c5783a8 100644
--- a/app/controllers/groups/boards_controller.rb
+++ b/app/controllers/groups/boards_controller.rb
@@ -12,6 +12,8 @@ class Groups::BoardsController < Groups::ApplicationController
push_frontend_feature_flag(:boards_with_swimlanes, group, default_enabled: false)
end
+ feature_category :boards
+
private
def assign_endpoint_vars
diff --git a/app/controllers/groups/children_controller.rb b/app/controllers/groups/children_controller.rb
index 236a19a8dc4..718914dea35 100644
--- a/app/controllers/groups/children_controller.rb
+++ b/app/controllers/groups/children_controller.rb
@@ -5,6 +5,8 @@ module Groups
before_action :group
skip_cross_project_access_check :index
+ feature_category :subgroups
+
def index
parent = if params[:parent_id].present?
GroupFinder.new(current_user).execute(id: params[:parent_id])
diff --git a/app/controllers/groups/deploy_tokens_controller.rb b/app/controllers/groups/deploy_tokens_controller.rb
index de951f2cb9f..79152bf2695 100644
--- a/app/controllers/groups/deploy_tokens_controller.rb
+++ b/app/controllers/groups/deploy_tokens_controller.rb
@@ -3,6 +3,8 @@
class Groups::DeployTokensController < Groups::ApplicationController
before_action :authorize_destroy_deploy_token!
+ feature_category :continuous_delivery
+
def revoke
@token = @group.deploy_tokens.find(params[:id])
@token.revoke!
diff --git a/app/controllers/groups/group_links_controller.rb b/app/controllers/groups/group_links_controller.rb
index c395b93f4e7..3b775af9722 100644
--- a/app/controllers/groups/group_links_controller.rb
+++ b/app/controllers/groups/group_links_controller.rb
@@ -4,6 +4,8 @@ class Groups::GroupLinksController < Groups::ApplicationController
before_action :authorize_admin_group!
before_action :group_link, only: [:update, :destroy]
+ feature_category :subgroups
+
def create
shared_with_group = Group.find(params[:shared_with_group_id]) if params[:shared_with_group_id].present?
@@ -24,6 +26,15 @@ class Groups::GroupLinksController < Groups::ApplicationController
def update
Groups::GroupLinks::UpdateService.new(@group_link).execute(group_link_params)
+
+ if @group_link.expires?
+ render json: {
+ expires_in: helpers.distance_of_time_in_words_to_now(@group_link.expires_at),
+ expires_soon: @group_link.expires_soon?
+ }
+ else
+ render json: {}
+ end
end
def destroy
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 63311ab983b..5df7ff0632a 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -19,6 +19,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
:approve_access_request, :leave, :resend_invite,
:override
+ feature_category :authentication_and_authorization
+
def index
@sort = params[:sort].presence || sort_value_name
@@ -69,6 +71,10 @@ class Groups::GroupMembersController < Groups::ApplicationController
def filter_params
params.permit(:two_factor, :search).merge(sort: @sort)
end
+
+ def membershipable_members
+ group.members
+ end
end
Groups::GroupMembersController.prepend_if_ee('EE::Groups::GroupMembersController')
diff --git a/app/controllers/groups/imports_controller.rb b/app/controllers/groups/imports_controller.rb
index b611685f9bc..7cf39e378db 100644
--- a/app/controllers/groups/imports_controller.rb
+++ b/app/controllers/groups/imports_controller.rb
@@ -3,6 +3,8 @@
class Groups::ImportsController < Groups::ApplicationController
include ContinueParams
+ feature_category :importers
+
def show
if @group.import_state.nil? || @group.import_state.finished?
if continue_params[:to]
diff --git a/app/controllers/groups/labels_controller.rb b/app/controllers/groups/labels_controller.rb
index 1034ca6cd7b..34856f8d84e 100644
--- a/app/controllers/groups/labels_controller.rb
+++ b/app/controllers/groups/labels_controller.rb
@@ -2,6 +2,7 @@
class Groups::LabelsController < Groups::ApplicationController
include ToggleSubscriptionAction
+ include ShowInheritedLabelsChecker
before_action :label, only: [:edit, :update, :destroy]
before_action :authorize_admin_labels!, only: [:new, :create, :edit, :update, :destroy]
@@ -9,11 +10,14 @@ class Groups::LabelsController < Groups::ApplicationController
respond_to :html
+ feature_category :issue_tracking
+
def index
respond_to do |format|
format.html do
- @labels = GroupLabelsFinder
- .new(current_user, @group, params.merge(sort: sort)).execute
+ # at group level we do not want to list project labels,
+ # we only want `only_group_labels = false` when pulling labels for label filter dropdowns, fetched through json
+ @labels = available_labels(params.merge(only_group_labels: true)).page(params[:page])
end
format.json do
render json: LabelSerializer.new.represent_appearance(available_labels)
@@ -60,13 +64,7 @@ class Groups::LabelsController < Groups::ApplicationController
def destroy
@label.destroy
-
- respond_to do |format|
- format.html do
- redirect_to group_labels_path(@group), status: :found, notice: "#{@label.name} deleted permanently"
- end
- format.js
- end
+ redirect_to group_labels_path(@group), status: :found, notice: "#{@label.name} deleted permanently"
end
protected
@@ -80,7 +78,7 @@ class Groups::LabelsController < Groups::ApplicationController
end
def label
- @label ||= @group.labels.find(params[:id])
+ @label ||= available_labels(params.merge(only_group_labels: true)).find(params[:id])
end
alias_method :subscribable_resource, :label
@@ -108,15 +106,17 @@ class Groups::LabelsController < Groups::ApplicationController
session[:previous_labels_path] = URI(request.referer || '').path
end
- def available_labels
+ def available_labels(options = params)
@available_labels ||=
LabelsFinder.new(
current_user,
group_id: @group.id,
- only_group_labels: params[:only_group_labels],
- include_ancestor_groups: params[:include_ancestor_groups],
- include_descendant_groups: params[:include_descendant_groups],
- search: params[:search]).execute
+ only_group_labels: options[:only_group_labels],
+ include_ancestor_groups: show_inherited_labels?(params[:include_ancestor_groups]),
+ sort: sort,
+ subscribed: options[:subscribed],
+ include_descendant_groups: options[:include_descendant_groups],
+ search: options[:search]).execute
end
def sort
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index df3fb6b67c2..173a24ceb74 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -3,12 +3,14 @@
class Groups::MilestonesController < Groups::ApplicationController
include MilestoneActions
- before_action :milestone, only: [:edit, :show, :update, :merge_requests, :participants, :labels, :destroy]
+ before_action :milestone, only: [:edit, :show, :update, :issues, :merge_requests, :participants, :labels, :destroy]
before_action :authorize_admin_milestones!, only: [:edit, :new, :create, :update, :destroy]
before_action do
push_frontend_feature_flag(:burnup_charts, @group)
end
+ feature_category :issue_tracking
+
def index
respond_to do |format|
format.html do
diff --git a/app/controllers/groups/packages_controller.rb b/app/controllers/groups/packages_controller.rb
index 600acc72e67..47f1816cc4c 100644
--- a/app/controllers/groups/packages_controller.rb
+++ b/app/controllers/groups/packages_controller.rb
@@ -4,6 +4,8 @@ module Groups
class PackagesController < Groups::ApplicationController
before_action :verify_packages_enabled!
+ feature_category :package_registry
+
private
def verify_packages_enabled!
diff --git a/app/controllers/groups/registry/repositories_controller.rb b/app/controllers/groups/registry/repositories_controller.rb
index 14651e0794a..d914e0bffc6 100644
--- a/app/controllers/groups/registry/repositories_controller.rb
+++ b/app/controllers/groups/registry/repositories_controller.rb
@@ -2,9 +2,13 @@
module Groups
module Registry
class RepositoriesController < Groups::ApplicationController
+ include PackagesHelper
+
before_action :verify_container_registry_enabled!
before_action :authorize_read_container_image!
+ feature_category :package_registry
+
def index
respond_to do |format|
format.html
@@ -13,7 +17,7 @@ module Groups
.execute
.with_api_entity_associations
- track_event(:list_repositories)
+ track_package_event(:list_repositories, :container)
serializer = ContainerRepositoriesSerializer
.new(current_user: current_user)
diff --git a/app/controllers/groups/releases_controller.rb b/app/controllers/groups/releases_controller.rb
index 500c57a6f3e..6a42f30b847 100644
--- a/app/controllers/groups/releases_controller.rb
+++ b/app/controllers/groups/releases_controller.rb
@@ -2,6 +2,8 @@
module Groups
class ReleasesController < Groups::ApplicationController
+ feature_category :release_evidence
+
def index
respond_to do |format|
format.json do
diff --git a/app/controllers/groups/runners_controller.rb b/app/controllers/groups/runners_controller.rb
index edebffe2912..dbfd31ebcad 100644
--- a/app/controllers/groups/runners_controller.rb
+++ b/app/controllers/groups/runners_controller.rb
@@ -7,6 +7,8 @@ class Groups::RunnersController < Groups::ApplicationController
before_action :runner, only: [:edit, :update, :destroy, :pause, :resume, :show]
+ feature_category :continuous_integration
+
def show
render 'shared/runners/show'
end
diff --git a/app/controllers/groups/settings/ci_cd_controller.rb b/app/controllers/groups/settings/ci_cd_controller.rb
index bf3a38ce57b..0c72c8a037b 100644
--- a/app/controllers/groups/settings/ci_cd_controller.rb
+++ b/app/controllers/groups/settings/ci_cd_controller.rb
@@ -3,6 +3,8 @@
module Groups
module Settings
class CiCdController < Groups::ApplicationController
+ include RunnerSetupScripts
+
skip_cross_project_access_check :show
before_action :authorize_admin_group!
before_action :authorize_update_max_artifacts_size!, only: [:update]
@@ -11,6 +13,8 @@ module Groups
end
before_action :define_variables, only: [:show]
+ feature_category :continuous_integration
+
NUMBER_OF_RUNNERS_PER_PAGE = 4
def show
@@ -49,6 +53,10 @@ module Groups
redirect_to group_settings_ci_cd_path
end
+ def runner_setup_scripts
+ private_runner_setup_scripts(group: group)
+ end
+
private
def define_variables
diff --git a/app/controllers/groups/settings/integrations_controller.rb b/app/controllers/groups/settings/integrations_controller.rb
index e8551a7f270..b089cfdf341 100644
--- a/app/controllers/groups/settings/integrations_controller.rb
+++ b/app/controllers/groups/settings/integrations_controller.rb
@@ -7,6 +7,8 @@ module Groups
before_action :authorize_admin_group!
+ feature_category :integrations
+
def index
@integrations = Service.find_or_initialize_all(Service.for_group(group)).sort_by(&:title)
end
diff --git a/app/controllers/groups/settings/repository_controller.rb b/app/controllers/groups/settings/repository_controller.rb
index e2fbdc39692..ccc1fa12458 100644
--- a/app/controllers/groups/settings/repository_controller.rb
+++ b/app/controllers/groups/settings/repository_controller.rb
@@ -10,6 +10,8 @@ module Groups
push_frontend_feature_flag(:ajax_new_deploy_token, @group)
end
+ feature_category :continuous_delivery
+
def create_deploy_token
result = Groups::DeployTokens::CreateService.new(@group, current_user, deploy_token_params).execute
@new_deploy_token = result[:deploy_token]
diff --git a/app/controllers/groups/shared_projects_controller.rb b/app/controllers/groups/shared_projects_controller.rb
index 30b7bfc70ae..90ec64d4768 100644
--- a/app/controllers/groups/shared_projects_controller.rb
+++ b/app/controllers/groups/shared_projects_controller.rb
@@ -6,6 +6,8 @@ module Groups
before_action :group
skip_cross_project_access_check :index
+ feature_category :subgroups
+
def index
shared_projects = GroupProjectsFinder.new(
group: group,
diff --git a/app/controllers/groups/uploads_controller.rb b/app/controllers/groups/uploads_controller.rb
index 3ae7e36c740..49249f87d31 100644
--- a/app/controllers/groups/uploads_controller.rb
+++ b/app/controllers/groups/uploads_controller.rb
@@ -9,6 +9,8 @@ class Groups::UploadsController < Groups::ApplicationController
before_action :authorize_upload_file!, only: [:create, :authorize]
before_action :verify_workhorse_api!, only: [:authorize]
+ feature_category :subgroups
+
private
def upload_model_class
diff --git a/app/controllers/groups/variables_controller.rb b/app/controllers/groups/variables_controller.rb
index fb639f6e472..51670325ce3 100644
--- a/app/controllers/groups/variables_controller.rb
+++ b/app/controllers/groups/variables_controller.rb
@@ -6,6 +6,8 @@ module Groups
skip_cross_project_access_check :show, :update
+ feature_category :continuous_integration
+
def show
respond_to do |format|
format.json do
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 2d6f5d0377a..6f8dc75f6bd 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -30,6 +30,7 @@ class GroupsController < Groups::ApplicationController
before_action do
push_frontend_feature_flag(:vue_issuables_list, @group)
+ push_frontend_feature_flag(:deployment_filters)
end
before_action do
@@ -46,6 +47,17 @@ class GroupsController < Groups::ApplicationController
layout :determine_layout
+ feature_category :subgroups, [
+ :index, :new, :create, :show, :edit, :update,
+ :destroy, :details, :transfer
+ ]
+
+ feature_category :audit_events, [:activity]
+ feature_category :issue_tracking, [:issues, :issues_calendar, :preview_markdown]
+ feature_category :code_review, [:merge_requests, :unfoldered_environment_names]
+ feature_category :projects, [:projects]
+ feature_category :importers, [:export, :download_export]
+
def index
redirect_to(current_user ? dashboard_groups_path : explore_groups_path)
end
@@ -168,6 +180,16 @@ class GroupsController < Groups::ApplicationController
end
end
+ def unfoldered_environment_names
+ return render_404 unless Feature.enabled?(:deployment_filters)
+
+ respond_to do |format|
+ format.json do
+ render json: EnvironmentNamesFinder.new(@group, current_user).execute
+ end
+ end
+ end
+
protected
def render_show_html
@@ -230,7 +252,9 @@ class GroupsController < Groups::ApplicationController
:two_factor_grace_period,
:project_creation_level,
:subgroup_creation_level,
- :default_branch_protection
+ :default_branch_protection,
+ :default_branch_name,
+ :allow_mfa_for_subgroups
]
end
diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb
index a1bbcf34f69..5a5200452de 100644
--- a/app/controllers/help_controller.rb
+++ b/app/controllers/help_controller.rb
@@ -2,6 +2,7 @@
class HelpController < ApplicationController
skip_before_action :authenticate_user!, unless: :public_visibility_restricted?
+ feature_category :not_owned
layout 'help'
@@ -26,17 +27,10 @@ class HelpController < ApplicationController
respond_to do |format|
format.any(:markdown, :md, :html) do
- # Note: We are purposefully NOT using `Rails.root.join` because of https://gitlab.com/gitlab-org/gitlab/-/issues/216028.
- path = File.join(Rails.root, 'doc', "#{@path}.md")
-
- if File.exist?(path)
- # Remove YAML frontmatter so that it doesn't look weird
- @markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '')
-
- render 'show.html.haml'
+ if redirect_to_documentation_website?
+ redirect_to documentation_url
else
- # Force template to Haml
- render 'errors/not_found.html.haml', layout: 'errors', status: :not_found
+ render_documentation
end
end
@@ -75,4 +69,47 @@ class HelpController < ApplicationController
params
end
+
+ def redirect_to_documentation_website?
+ return false unless Feature.enabled?(:help_page_documentation_redirect)
+ return false unless Gitlab::UrlSanitizer.valid_web?(documentation_url)
+
+ true
+ end
+
+ def documentation_url
+ return unless documentation_base_url
+
+ @documentation_url ||= Gitlab::Utils.append_path(documentation_base_url, documentation_file_path)
+ end
+
+ def documentation_base_url
+ @documentation_base_url ||= Gitlab::CurrentSettings.current_application_settings.help_page_documentation_base_url.presence
+ end
+
+ def documentation_file_path
+ @documentation_file_path ||= [version_segment, 'ee', "#{@path}.html"].compact.join('/')
+ end
+
+ def version_segment
+ return if Gitlab.pre_release?
+
+ version = Gitlab.version_info
+ [version.major, version.minor].join('.')
+ end
+
+ def render_documentation
+ # Note: We are purposefully NOT using `Rails.root.join` because of https://gitlab.com/gitlab-org/gitlab/-/issues/216028.
+ path = File.join(Rails.root, 'doc', "#{@path}.md")
+
+ if File.exist?(path)
+ # Remove YAML frontmatter so that it doesn't look weird
+ @markdown = File.read(path).gsub(YAML_FRONT_MATTER_REGEXP, '')
+
+ render 'show.html.haml'
+ else
+ # Force template to Haml
+ render 'errors/not_found.html.haml', layout: 'errors', status: :not_found
+ end
+ end
end
diff --git a/app/controllers/ide_controller.rb b/app/controllers/ide_controller.rb
index 2c17f5b5542..8c0414ad5da 100644
--- a/app/controllers/ide_controller.rb
+++ b/app/controllers/ide_controller.rb
@@ -11,6 +11,8 @@ class IdeController < ApplicationController
push_frontend_feature_flag(:schema_linting)
end
+ feature_category :web_ide
+
def index
Gitlab::UsageDataCounters::WebIdeCounter.increment_views_count
end
diff --git a/app/controllers/import/available_namespaces_controller.rb b/app/controllers/import/available_namespaces_controller.rb
index 7983b4f20b5..c6211b33d28 100644
--- a/app/controllers/import/available_namespaces_controller.rb
+++ b/app/controllers/import/available_namespaces_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Import::AvailableNamespacesController < ApplicationController
+ feature_category :importers
+
def index
render json: NamespaceSerializer.new.represent(current_user.manageable_groups_with_routes)
end
diff --git a/app/controllers/import/base_controller.rb b/app/controllers/import/base_controller.rb
index 8a7a4c92b37..151ba46e629 100644
--- a/app/controllers/import/base_controller.rb
+++ b/app/controllers/import/base_controller.rb
@@ -4,6 +4,7 @@ class Import::BaseController < ApplicationController
include ActionView::Helpers::SanitizeHelper
before_action :import_rate_limit, only: [:create]
+ feature_category :importers
def status
respond_to do |format|
diff --git a/app/controllers/import/bulk_imports_controller.rb b/app/controllers/import/bulk_imports_controller.rb
new file mode 100644
index 00000000000..cb2922c2d47
--- /dev/null
+++ b/app/controllers/import/bulk_imports_controller.rb
@@ -0,0 +1,109 @@
+# frozen_string_literal: true
+
+class Import::BulkImportsController < ApplicationController
+ before_action :ensure_group_import_enabled
+ before_action :verify_blocked_uri, only: :status
+
+ feature_category :importers
+
+ rescue_from Gitlab::BulkImport::Client::ConnectionError, with: :bulk_import_connection_error
+
+ def configure
+ session[access_token_key] = params[access_token_key]&.strip
+ session[url_key] = params[url_key]
+
+ redirect_to status_import_bulk_import_url
+ end
+
+ def status
+ respond_to do |format|
+ format.json do
+ render json: { importable_data: serialized_importable_data }
+ end
+
+ format.html
+ end
+ end
+
+ private
+
+ def serialized_importable_data
+ serializer.represent(importable_data, {}, Import::BulkImportEntity)
+ end
+
+ def serializer
+ @serializer ||= BaseSerializer.new(current_user: current_user)
+ end
+
+ def importable_data
+ client.get('groups', top_level_only: true)
+ end
+
+ def client
+ @client ||= Gitlab::BulkImport::Client.new(
+ uri: session[url_key],
+ token: session[access_token_key]
+ )
+ end
+
+ def import_params
+ params.permit(access_token_key, url_key)
+ end
+
+ def ensure_group_import_enabled
+ render_404 unless Feature.enabled?(:bulk_import)
+ end
+
+ def access_token_key
+ :bulk_import_gitlab_access_token
+ end
+
+ def url_key
+ :bulk_import_gitlab_url
+ end
+
+ def verify_blocked_uri
+ Gitlab::UrlBlocker.validate!(
+ session[url_key],
+ **{
+ allow_localhost: allow_local_requests?,
+ allow_local_network: allow_local_requests?,
+ schemes: %w(http https)
+ }
+ )
+ rescue Gitlab::UrlBlocker::BlockedUrlError => e
+ clear_session_data
+
+ redirect_to new_group_path, alert: _('Specified URL cannot be used: "%{reason}"') % { reason: e.message }
+ end
+
+ def allow_local_requests?
+ Gitlab::CurrentSettings.allow_local_requests_from_web_hooks_and_services?
+ end
+
+ def bulk_import_connection_error(error)
+ clear_session_data
+
+ error_message = _("Unable to connect to server: %{error}") % { error: error }
+ flash[:alert] = error_message
+
+ respond_to do |format|
+ format.json do
+ render json: {
+ error: {
+ message: error_message,
+ redirect: new_group_path
+ }
+ }, status: :unprocessable_entity
+ end
+ format.html do
+ redirect_to new_group_path
+ end
+ end
+ end
+
+ def clear_session_data
+ session[url_key] = nil
+ session[access_token_key] = nil
+ end
+end
diff --git a/app/controllers/import/fogbugz_controller.rb b/app/controllers/import/fogbugz_controller.rb
index a34bc9c953f..bcbf5938e11 100644
--- a/app/controllers/import/fogbugz_controller.rb
+++ b/app/controllers/import/fogbugz_controller.rb
@@ -136,7 +136,7 @@ class Import::FogbugzController < Import::BaseController
def verify_blocked_uri
Gitlab::UrlBlocker.validate!(
params[:uri],
- {
+ **{
allow_localhost: allow_local_requests?,
allow_local_network: allow_local_requests?,
schemes: %w(http https)
diff --git a/app/controllers/import/github_controller.rb b/app/controllers/import/github_controller.rb
index 29fe34f0734..a1adc6e062a 100644
--- a/app/controllers/import/github_controller.rb
+++ b/app/controllers/import/github_controller.rb
@@ -108,7 +108,7 @@ class Import::GithubController < Import::BaseController
@client ||= if Feature.enabled?(:remove_legacy_github_client)
Gitlab::GithubImport::Client.new(session[access_token_key])
else
- Gitlab::LegacyGithubImport::Client.new(session[access_token_key], client_options)
+ Gitlab::LegacyGithubImport::Client.new(session[access_token_key], **client_options)
end
end
diff --git a/app/controllers/import/gitlab_groups_controller.rb b/app/controllers/import/gitlab_groups_controller.rb
index 330af68385e..d8118477a80 100644
--- a/app/controllers/import/gitlab_groups_controller.rb
+++ b/app/controllers/import/gitlab_groups_controller.rb
@@ -6,6 +6,8 @@ class Import::GitlabGroupsController < ApplicationController
before_action :ensure_group_import_enabled
before_action :import_rate_limit, only: %i[create]
+ feature_category :importers
+
def create
unless file_is_valid?(group_params[:file])
return redirect_back_or_default(options: { alert: s_('GroupImport|Unable to process group import file') })
diff --git a/app/controllers/import/manifest_controller.rb b/app/controllers/import/manifest_controller.rb
index 9c47e6d4b0b..fdca6da95c5 100644
--- a/app/controllers/import/manifest_controller.rb
+++ b/app/controllers/import/manifest_controller.rb
@@ -26,8 +26,7 @@ class Import::ManifestController < Import::BaseController
manifest = Gitlab::ManifestImport::Manifest.new(params[:manifest].tempfile)
if manifest.valid?
- session[:manifest_import_repositories] = manifest.projects
- session[:manifest_import_group_id] = group.id
+ manifest_import_metadata.save(manifest.projects, group.id)
redirect_to status_import_manifest_path
else
@@ -96,12 +95,16 @@ class Import::ManifestController < Import::BaseController
# rubocop: disable CodeReuse/ActiveRecord
def group
- @group ||= Group.find_by(id: session[:manifest_import_group_id])
+ @group ||= Group.find_by(id: manifest_import_metadata.group_id)
end
# rubocop: enable CodeReuse/ActiveRecord
+ def manifest_import_metadata
+ @manifest_import_status ||= Gitlab::ManifestImport::Metadata.new(current_user, fallback: session)
+ end
+
def repositories
- @repositories ||= session[:manifest_import_repositories]
+ @repositories ||= manifest_import_metadata.repositories
end
def find_jobs
diff --git a/app/controllers/invites_controller.rb b/app/controllers/invites_controller.rb
index aa9c7d01ba3..c7b8486d1c9 100644
--- a/app/controllers/invites_controller.rb
+++ b/app/controllers/invites_controller.rb
@@ -4,6 +4,7 @@ class InvitesController < ApplicationController
include Gitlab::Utils::StrongMemoize
before_action :member
+ before_action :ensure_member_exists
before_action :invite_details
skip_before_action :authenticate_user!, only: :decline
@@ -11,14 +12,17 @@ class InvitesController < ApplicationController
respond_to :html
+ feature_category :authentication_and_authorization
+
def show
- track_experiment('opened')
+ track_new_user_invite_experiment('opened')
accept if skip_invitation_prompt?
end
def accept
if member.accept_invite!(current_user)
- track_experiment('accepted')
+ track_new_user_invite_experiment('accepted')
+ track_invitation_reminders_experiment('accepted')
redirect_to invite_details[:path], notice: _("You have been granted %{member_human_access} access to %{title} %{name}.") %
{ member_human_access: member.human_access, title: invite_details[:title], name: invite_details[:name] }
else
@@ -28,6 +32,8 @@ class InvitesController < ApplicationController
def decline
if member.decline_invite!
+ return render layout: 'devise_experimental_onboarding_issues' if !current_user && member.invite_to_unknown_user? && member.created_by
+
path =
if current_user
dashboard_projects_path
@@ -59,14 +65,16 @@ class InvitesController < ApplicationController
end
def member
- return @member if defined?(@member)
-
- @token = params[:id]
- @member = Member.find_by_invite_token(@token)
+ strong_memoize(:member) do
+ @token = params[:id]
+ Member.find_by_invite_token(@token)
+ end
+ end
- return render_404 unless @member
+ def ensure_member_exists
+ return if member
- @member
+ render_404
end
def authenticate_user!
@@ -76,10 +84,7 @@ class InvitesController < ApplicationController
notice << "or create an account" if Gitlab::CurrentSettings.allow_signup?
notice = notice.join(' ') + "."
- # this is temporary finder instead of using member method due to render_404 possibility
- # will be resolved via https://gitlab.com/gitlab-org/gitlab/-/issues/245325
- initial_member = Member.find_by_invite_token(params[:id])
- redirect_params = initial_member ? { invite_email: initial_member.invite_email } : {}
+ redirect_params = member ? { invite_email: member.invite_email } : {}
store_location_for :user, request.fullpath
@@ -87,31 +92,43 @@ class InvitesController < ApplicationController
end
def invite_details
- @invite_details ||= case @member.source
+ @invite_details ||= case member.source
when Project
{
- name: @member.source.full_name,
- url: project_url(@member.source),
+ name: member.source.full_name,
+ url: project_url(member.source),
title: _("project"),
- path: project_path(@member.source)
+ path: project_path(member.source)
}
when Group
{
- name: @member.source.name,
- url: group_url(@member.source),
+ name: member.source.name,
+ url: group_url(member.source),
title: _("group"),
- path: group_path(@member.source)
+ path: group_path(member.source)
}
end
end
- def track_experiment(action)
+ def track_new_user_invite_experiment(action)
return unless params[:new_user_invite]
property = params[:new_user_invite] == 'experiment' ? 'experiment_group' : 'control_group'
+ track_experiment(:invite_email, action, property)
+ end
+
+ def track_invitation_reminders_experiment(action)
+ return unless Gitlab::Experimentation.enabled?(:invitation_reminders)
+
+ property = Gitlab::Experimentation.enabled_for_attribute?(:invitation_reminders, member.invite_email) ? 'experimental_group' : 'control_group'
+
+ track_experiment(:invitation_reminders, action, property)
+ end
+
+ def track_experiment(experiment_key, action, property)
Gitlab::Tracking.event(
- Gitlab::Experimentation::EXPERIMENTS[:invite_email][:tracking_category],
+ Gitlab::Experimentation.experiment(experiment_key).tracking_category,
action,
property: property,
label: Digest::MD5.hexdigest(member.to_global_id.to_s)
diff --git a/app/controllers/jira_connect/application_controller.rb b/app/controllers/jira_connect/application_controller.rb
index a84f25998a6..9c311f92b69 100644
--- a/app/controllers/jira_connect/application_controller.rb
+++ b/app/controllers/jira_connect/application_controller.rb
@@ -7,6 +7,8 @@ class JiraConnect::ApplicationController < ApplicationController
skip_before_action :verify_authenticity_token
before_action :verify_atlassian_jwt!
+ feature_category :integrations
+
attr_reader :current_jira_installation
private
diff --git a/app/controllers/jira_connect/events_controller.rb b/app/controllers/jira_connect/events_controller.rb
index 8f79c82d847..d833491b8f7 100644
--- a/app/controllers/jira_connect/events_controller.rb
+++ b/app/controllers/jira_connect/events_controller.rb
@@ -1,10 +1,14 @@
# frozen_string_literal: true
class JiraConnect::EventsController < JiraConnect::ApplicationController
+ # See https://developer.atlassian.com/cloud/jira/software/app-descriptor/#lifecycle
+
skip_before_action :verify_atlassian_jwt!, only: :installed
before_action :verify_qsh_claim!, only: :uninstalled
def installed
+ return head :ok if atlassian_jwt_valid?
+
installation = JiraConnectInstallation.new(install_params)
if installation.save
diff --git a/app/controllers/jira_connect/users_controller.rb b/app/controllers/jira_connect/users_controller.rb
new file mode 100644
index 00000000000..571d9f87779
--- /dev/null
+++ b/app/controllers/jira_connect/users_controller.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class JiraConnect::UsersController < ApplicationController
+ feature_category :integrations
+
+ layout 'devise_experimental_onboarding_issues'
+
+ def show
+ @jira_app_link = params.delete(:return_to)
+ end
+end
diff --git a/app/controllers/jwt_controller.rb b/app/controllers/jwt_controller.rb
index 3e7755046cd..5199bb25c8c 100644
--- a/app/controllers/jwt_controller.rb
+++ b/app/controllers/jwt_controller.rb
@@ -8,6 +8,8 @@ class JwtController < ApplicationController
# Add this before other actions, since we want to have the user or project
prepend_before_action :auth_user, :authenticate_project_or_user
+ feature_category :authentication_and_authorization
+
SERVICES = {
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService
}.freeze
diff --git a/app/controllers/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb
index e5d4a4bb073..7d8c035c852 100644
--- a/app/controllers/notification_settings_controller.rb
+++ b/app/controllers/notification_settings_controller.rb
@@ -3,6 +3,8 @@
class NotificationSettingsController < ApplicationController
before_action :authenticate_user!
+ feature_category :users
+
def create
return render_404 unless can_read?(resource)
diff --git a/app/controllers/oauth/jira/authorizations_controller.rb b/app/controllers/oauth/jira/authorizations_controller.rb
index f552b0dc10c..f23149c8544 100644
--- a/app/controllers/oauth/jira/authorizations_controller.rb
+++ b/app/controllers/oauth/jira/authorizations_controller.rb
@@ -8,6 +8,8 @@ class Oauth::Jira::AuthorizationsController < ApplicationController
skip_before_action :authenticate_user!
skip_before_action :verify_authenticity_token
+ feature_category :integrations
+
# 1. Rewire Jira OAuth initial request to our stablished OAuth authorization URL.
def new
session[:redirect_uri] = params['redirect_uri']
diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb
index b798d6680bc..c9791703413 100644
--- a/app/controllers/omniauth_callbacks_controller.rb
+++ b/app/controllers/omniauth_callbacks_controller.rb
@@ -1,8 +1,7 @@
# frozen_string_literal: true
class OmniauthCallbacksController < Devise::OmniauthCallbacksController
- include AuthenticatesWithTwoFactor
- include Authenticates2FAForAdminMode
+ include AuthenticatesWithTwoFactorForAdminMode
include Devise::Controllers::Rememberable
include AuthHelper
include InitializesCurrentUserMode
@@ -12,6 +11,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
protect_from_forgery except: [:kerberos, :saml, :cas3, :failure], with: :exception, prepend: true
+ feature_category :authentication_and_authorization
+
def handle_omniauth
omniauth_flow(Gitlab::Auth::OAuth)
end
diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb
index c27226c3f3f..bc6975f8953 100644
--- a/app/controllers/passwords_controller.rb
+++ b/app/controllers/passwords_controller.rb
@@ -7,6 +7,8 @@ class PasswordsController < Devise::PasswordsController
before_action :check_password_authentication_available, only: [:create]
before_action :throttle_reset, only: [:create]
+ feature_category :authentication_and_authorization
+
# rubocop: disable CodeReuse/ActiveRecord
def edit
super
diff --git a/app/controllers/profiles/accounts_controller.rb b/app/controllers/profiles/accounts_controller.rb
index b19285e98bb..d8419be9f23 100644
--- a/app/controllers/profiles/accounts_controller.rb
+++ b/app/controllers/profiles/accounts_controller.rb
@@ -3,6 +3,8 @@
class Profiles::AccountsController < Profiles::ApplicationController
include AuthHelper
+ feature_category :users
+
def show
render(locals: show_view_variables)
end
diff --git a/app/controllers/profiles/active_sessions_controller.rb b/app/controllers/profiles/active_sessions_controller.rb
index e4cd5d65e1a..1233c906406 100644
--- a/app/controllers/profiles/active_sessions_controller.rb
+++ b/app/controllers/profiles/active_sessions_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Profiles::ActiveSessionsController < Profiles::ApplicationController
+ feature_category :users
+
def index
@sessions = ActiveSession.list(current_user).reject(&:is_impersonated)
end
diff --git a/app/controllers/profiles/avatars_controller.rb b/app/controllers/profiles/avatars_controller.rb
index 3378a09628c..d9e4b9a149d 100644
--- a/app/controllers/profiles/avatars_controller.rb
+++ b/app/controllers/profiles/avatars_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Profiles::AvatarsController < Profiles::ApplicationController
+ feature_category :users
+
def destroy
@user = current_user
diff --git a/app/controllers/profiles/chat_names_controller.rb b/app/controllers/profiles/chat_names_controller.rb
index 80b8279e91e..8cfec247b7a 100644
--- a/app/controllers/profiles/chat_names_controller.rb
+++ b/app/controllers/profiles/chat_names_controller.rb
@@ -4,6 +4,8 @@ class Profiles::ChatNamesController < Profiles::ApplicationController
before_action :chat_name_token, only: [:new]
before_action :chat_name_params, only: [:new, :create, :deny]
+ feature_category :users
+
def index
@chat_names = current_user.chat_names
end
diff --git a/app/controllers/profiles/emails_controller.rb b/app/controllers/profiles/emails_controller.rb
index da553e34ef6..6e5b18cb885 100644
--- a/app/controllers/profiles/emails_controller.rb
+++ b/app/controllers/profiles/emails_controller.rb
@@ -5,6 +5,8 @@ class Profiles::EmailsController < Profiles::ApplicationController
before_action -> { rate_limit!(:profile_add_new_email) }, only: [:create]
before_action -> { rate_limit!(:profile_resend_email_confirmation) }, only: [:resend_confirmation_instructions]
+ feature_category :users
+
def index
@primary_email = current_user.email
@emails = current_user.emails.order_id_desc
diff --git a/app/controllers/profiles/gpg_keys_controller.rb b/app/controllers/profiles/gpg_keys_controller.rb
index 8c34a66c374..7f04927f517 100644
--- a/app/controllers/profiles/gpg_keys_controller.rb
+++ b/app/controllers/profiles/gpg_keys_controller.rb
@@ -3,6 +3,8 @@
class Profiles::GpgKeysController < Profiles::ApplicationController
before_action :set_gpg_key, only: [:destroy, :revoke]
+ feature_category :users
+
def index
@gpg_keys = current_user.gpg_keys.with_subkeys
@gpg_key = GpgKey.new
diff --git a/app/controllers/profiles/groups_controller.rb b/app/controllers/profiles/groups_controller.rb
index 04b5ee270dc..e76ee0a6cea 100644
--- a/app/controllers/profiles/groups_controller.rb
+++ b/app/controllers/profiles/groups_controller.rb
@@ -3,6 +3,8 @@
class Profiles::GroupsController < Profiles::ApplicationController
include RoutableActions
+ feature_category :users
+
def update
group = find_routable!(Group, params[:id])
notification_setting = current_user.notification_settings_for(group)
diff --git a/app/controllers/profiles/keys_controller.rb b/app/controllers/profiles/keys_controller.rb
index 965493955ac..1e6340f285e 100644
--- a/app/controllers/profiles/keys_controller.rb
+++ b/app/controllers/profiles/keys_controller.rb
@@ -3,6 +3,8 @@
class Profiles::KeysController < Profiles::ApplicationController
skip_before_action :authenticate_user!, only: [:get_keys]
+ feature_category :users
+
def index
@keys = current_user.keys.order_id_desc
@key = Key.new
diff --git a/app/controllers/profiles/notifications_controller.rb b/app/controllers/profiles/notifications_controller.rb
index bc51830c119..a3e7638cdbc 100644
--- a/app/controllers/profiles/notifications_controller.rb
+++ b/app/controllers/profiles/notifications_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Profiles::NotificationsController < Profiles::ApplicationController
+ feature_category :users
+
# rubocop: disable CodeReuse/ActiveRecord
def show
@user = current_user
diff --git a/app/controllers/profiles/passwords_controller.rb b/app/controllers/profiles/passwords_controller.rb
index fccbc29f598..85e901eb3eb 100644
--- a/app/controllers/profiles/passwords_controller.rb
+++ b/app/controllers/profiles/passwords_controller.rb
@@ -9,6 +9,8 @@ class Profiles::PasswordsController < Profiles::ApplicationController
layout :determine_layout
+ feature_category :authentication_and_authorization
+
def new
end
diff --git a/app/controllers/profiles/personal_access_tokens_controller.rb b/app/controllers/profiles/personal_access_tokens_controller.rb
index 21adc032940..b005347c43a 100644
--- a/app/controllers/profiles/personal_access_tokens_controller.rb
+++ b/app/controllers/profiles/personal_access_tokens_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Profiles::PersonalAccessTokensController < Profiles::ApplicationController
+ feature_category :authentication_and_authorization
+
def index
set_index_vars
@personal_access_token = finder.build
diff --git a/app/controllers/profiles/preferences_controller.rb b/app/controllers/profiles/preferences_controller.rb
index ea4d3e861be..4d88491e9a8 100644
--- a/app/controllers/profiles/preferences_controller.rb
+++ b/app/controllers/profiles/preferences_controller.rb
@@ -3,6 +3,8 @@
class Profiles::PreferencesController < Profiles::ApplicationController
before_action :user
+ feature_category :users
+
def show
end
diff --git a/app/controllers/profiles/two_factor_auths_controller.rb b/app/controllers/profiles/two_factor_auths_controller.rb
index 5de6d84fdd9..e2f8baa8226 100644
--- a/app/controllers/profiles/two_factor_auths_controller.rb
+++ b/app/controllers/profiles/two_factor_auths_controller.rb
@@ -6,6 +6,8 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
push_frontend_feature_flag(:webauthn)
end
+ feature_category :authentication_and_authorization
+
def show
unless current_user.two_factor_enabled?
current_user.otp_secret = User.generate_otp_secret(32)
@@ -45,7 +47,10 @@ class Profiles::TwoFactorAuthsController < Profiles::ApplicationController
end
def create
- if current_user.validate_and_consume_otp!(params[:pin_code])
+ otp_validation_result =
+ ::Users::ValidateOtpService.new(current_user).execute(params[:pin_code])
+
+ if otp_validation_result[:status] == :success
ActiveSession.destroy_all_but_current(current_user, session)
Users::UpdateService.new(current_user, user: current_user, otp_required_for_login: true).execute! do |user|
diff --git a/app/controllers/profiles/u2f_registrations_controller.rb b/app/controllers/profiles/u2f_registrations_controller.rb
index 84ce4a56e64..32ca303e722 100644
--- a/app/controllers/profiles/u2f_registrations_controller.rb
+++ b/app/controllers/profiles/u2f_registrations_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Profiles::U2fRegistrationsController < Profiles::ApplicationController
+ feature_category :authentication_and_authorization
+
def destroy
u2f_registration = current_user.u2f_registrations.find(params[:id])
u2f_registration.destroy
diff --git a/app/controllers/profiles/webauthn_registrations_controller.rb b/app/controllers/profiles/webauthn_registrations_controller.rb
index 81b1dd6f710..a4a6d84f1ae 100644
--- a/app/controllers/profiles/webauthn_registrations_controller.rb
+++ b/app/controllers/profiles/webauthn_registrations_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Profiles::WebauthnRegistrationsController < Profiles::ApplicationController
+ feature_category :authentication_and_authorization
+
def destroy
webauthn_registration = current_user.webauthn_registrations.find(params[:id])
webauthn_registration.destroy
diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb
index 248d5755d92..c85c83688a4 100644
--- a/app/controllers/profiles_controller.rb
+++ b/app/controllers/profiles_controller.rb
@@ -2,6 +2,7 @@
class ProfilesController < Profiles::ApplicationController
include ActionView::Helpers::SanitizeHelper
+ include Gitlab::Tracking
before_action :user
before_action :authorize_change_username!, only: :update_username
@@ -10,6 +11,8 @@ class ProfilesController < Profiles::ApplicationController
push_frontend_feature_flag(:webauthn)
end
+ feature_category :users
+
def show
end
@@ -63,6 +66,8 @@ class ProfilesController < Profiles::ApplicationController
@events = AuditEvent.where(entity_type: "User", entity_id: current_user.id)
.order("created_at DESC")
.page(params[:page])
+
+ Gitlab::Tracking.event(self.class.name, 'search_audit_event')
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/controllers/projects/alert_management_controller.rb b/app/controllers/projects/alert_management_controller.rb
index 054dc8e6a35..0d0ef9b05cb 100644
--- a/app/controllers/projects/alert_management_controller.rb
+++ b/app/controllers/projects/alert_management_controller.rb
@@ -3,10 +3,13 @@
class Projects::AlertManagementController < Projects::ApplicationController
before_action :authorize_read_alert_management_alert!
+ feature_category :alert_management
+
def index
end
def details
@alert_id = params[:id]
+ push_frontend_feature_flag(:expose_environment_path_in_alert_details, @project)
end
end
diff --git a/app/controllers/projects/alerting/notifications_controller.rb b/app/controllers/projects/alerting/notifications_controller.rb
index fef8235628d..2241ded2db8 100644
--- a/app/controllers/projects/alerting/notifications_controller.rb
+++ b/app/controllers/projects/alerting/notifications_controller.rb
@@ -10,6 +10,8 @@ module Projects
prepend_before_action :repository, :project_without_auth
+ feature_category :alert_management
+
def create
token = extract_alert_manager_token(request)
result = notify_service.execute(token)
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index 652687932fd..f6a92b07295 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -15,6 +15,8 @@ class Projects::ArtifactsController < Projects::ApplicationController
MAX_PER_PAGE = 20
+ feature_category :continuous_integration
+
def index
# Loading artifacts is very expensive in projects with a lot of artifacts.
# This feature flag prevents a DOS attack vector.
diff --git a/app/controllers/projects/autocomplete_sources_controller.rb b/app/controllers/projects/autocomplete_sources_controller.rb
index 605d70d440b..e9c533daa80 100644
--- a/app/controllers/projects/autocomplete_sources_controller.rb
+++ b/app/controllers/projects/autocomplete_sources_controller.rb
@@ -3,6 +3,11 @@
class Projects::AutocompleteSourcesController < Projects::ApplicationController
before_action :authorize_read_milestone!, only: :milestones
+ feature_category :issue_tracking, [:issues, :labels, :milestones, :commands]
+ feature_category :code_review, [:merge_requests]
+ feature_category :users, [:members]
+ feature_category :snippets, [:snippets]
+
def members
render json: ::Projects::ParticipantsService.new(@project, current_user).execute(target)
end
diff --git a/app/controllers/projects/avatars_controller.rb b/app/controllers/projects/avatars_controller.rb
index 6e6bf09a32a..f228206032d 100644
--- a/app/controllers/projects/avatars_controller.rb
+++ b/app/controllers/projects/avatars_controller.rb
@@ -5,6 +5,8 @@ class Projects::AvatarsController < Projects::ApplicationController
before_action :authorize_admin_project!, only: [:destroy]
+ feature_category :projects
+
def show
@blob = @repository.blob_at_branch(@repository.root_ref, @project.avatar_in_git)
diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb
index eb47fec2b7e..855965ca6e1 100644
--- a/app/controllers/projects/badges_controller.rb
+++ b/app/controllers/projects/badges_controller.rb
@@ -6,6 +6,8 @@ class Projects::BadgesController < Projects::ApplicationController
before_action :no_cache_headers, only: [:pipeline, :coverage]
before_action :authorize_read_build!, only: [:pipeline, :coverage]
+ feature_category :continuous_integration
+
def pipeline
pipeline_status = Gitlab::Badge::Pipeline::Status
.new(project, params[:ref], opts: {
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb
index 374b4921dbc..d5de0d38152 100644
--- a/app/controllers/projects/blame_controller.rb
+++ b/app/controllers/projects/blame_controller.rb
@@ -9,6 +9,8 @@ class Projects::BlameController < Projects::ApplicationController
before_action :assign_ref_vars
before_action :authorize_download_code!
+ feature_category :source_code_management
+
def show
@blob = @repository.blob_at(@commit.id, @path)
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 1568d9966dd..c6251d27b05 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -33,13 +33,14 @@ class Projects::BlobController < Projects::ApplicationController
before_action :set_last_commit_sha, only: [:edit, :update]
before_action only: :show do
- push_frontend_feature_flag(:code_navigation, @project, default_enabled: true)
- push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline)
+ push_frontend_experiment(:suggest_pipeline)
push_frontend_feature_flag(:gitlab_ci_yml_preview, @project, default_enabled: false)
end
track_redis_hll_event :create, :update, name: 'g_edit_by_sfe', feature: :track_editor_edit_actions, feature_default_enabled: true
+ feature_category :source_code_management
+
def new
commit unless @repository.empty?
end
diff --git a/app/controllers/projects/boards_controller.rb b/app/controllers/projects/boards_controller.rb
index 5ed35094476..193352ffa70 100644
--- a/app/controllers/projects/boards_controller.rb
+++ b/app/controllers/projects/boards_controller.rb
@@ -12,6 +12,8 @@ class Projects::BoardsController < Projects::ApplicationController
push_frontend_feature_flag(:boards_with_swimlanes, project, default_enabled: false)
end
+ feature_category :boards
+
private
def assign_endpoint_vars
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 7cfb4a508da..9124728ee25 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -13,6 +13,8 @@ class Projects::BranchesController < Projects::ApplicationController
before_action :redirect_for_legacy_index_sort_or_search, only: [:index]
before_action :limit_diverging_commit_counts!, only: [:diverging_commit_counts]
+ feature_category :source_code_management
+
def index
respond_to do |format|
format.html do
diff --git a/app/controllers/projects/build_artifacts_controller.rb b/app/controllers/projects/build_artifacts_controller.rb
index 99f4524eec5..148080a71f4 100644
--- a/app/controllers/projects/build_artifacts_controller.rb
+++ b/app/controllers/projects/build_artifacts_controller.rb
@@ -8,6 +8,8 @@ class Projects::BuildArtifactsController < Projects::ApplicationController
before_action :extract_ref_name_and_path
before_action :validate_artifacts!, except: [:download]
+ feature_category :continuous_integration
+
def download
redirect_to download_project_job_artifacts_path(project, job, params: request.query_parameters)
end
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 6b3d70cb720..c5f6ed1c105 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -3,6 +3,8 @@
class Projects::BuildsController < Projects::ApplicationController
before_action :authorize_read_build!
+ feature_category :continuous_integration
+
def index
redirect_to project_jobs_path(project)
end
diff --git a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
index b36c5f1aea6..d05ab1b4977 100644
--- a/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
+++ b/app/controllers/projects/ci/daily_build_group_report_results_controller.rb
@@ -6,10 +6,11 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
MAX_ITEMS = 1000
REPORT_WINDOW = 90.days
- before_action :validate_feature_flag!
before_action :authorize_read_build_report_results!
before_action :validate_param_type!
+ feature_category :continuous_integration
+
def index
respond_to do |format|
format.csv { send_data(render_csv(report_results), type: 'text/csv; charset=utf-8') }
@@ -19,10 +20,6 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
private
- def validate_feature_flag!
- render_404 unless Feature.enabled?(:ci_download_daily_code_coverage, project, default_enabled: true)
- end
-
def validate_param_type!
respond_422 unless allowed_param_types.include?(param_type)
end
@@ -43,7 +40,7 @@ class Projects::Ci::DailyBuildGroupReportResultsController < Projects::Applicati
end
def report_results
- Ci::DailyBuildGroupReportResultsFinder.new(finder_params).execute
+ Ci::DailyBuildGroupReportResultsFinder.new(**finder_params).execute
end
def finder_params
diff --git a/app/controllers/projects/ci/lints_controller.rb b/app/controllers/projects/ci/lints_controller.rb
index 813a0a9ddd5..7e900fc6051 100644
--- a/app/controllers/projects/ci/lints_controller.rb
+++ b/app/controllers/projects/ci/lints_controller.rb
@@ -6,6 +6,8 @@ class Projects::Ci::LintsController < Projects::ApplicationController
push_frontend_feature_flag(:ci_lint_vue, project)
end
+ feature_category :pipeline_authoring
+
def show
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index b0c6f3cc6a1..2e48f2f0e45 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -21,6 +21,8 @@ class Projects::CommitController < Projects::ApplicationController
BRANCH_SEARCH_LIMIT = 1000
+ feature_category :source_code_management
+
def show
apply_diff_view_cookie!
diff --git a/app/controllers/projects/commits_controller.rb b/app/controllers/projects/commits_controller.rb
index b161e44660e..d267ab732f9 100644
--- a/app/controllers/projects/commits_controller.rb
+++ b/app/controllers/projects/commits_controller.rb
@@ -15,6 +15,8 @@ class Projects::CommitsController < Projects::ApplicationController
before_action :validate_ref!, except: :commits_root
before_action :set_commits, except: :commits_root
+ feature_category :source_code_management
+
def commits_root
redirect_to project_commits_path(@project, @project.default_branch)
end
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 943277afe95..6be0b465402 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -19,6 +19,8 @@ class Projects::CompareController < Projects::ApplicationController
# Validation
before_action :validate_refs!
+ feature_category :source_code_management
+
def index
end
diff --git a/app/controllers/projects/confluences_controller.rb b/app/controllers/projects/confluences_controller.rb
index d563b34a362..fccbdf0bf91 100644
--- a/app/controllers/projects/confluences_controller.rb
+++ b/app/controllers/projects/confluences_controller.rb
@@ -3,6 +3,8 @@
class Projects::ConfluencesController < Projects::ApplicationController
before_action :ensure_confluence
+ feature_category :integrations
+
def show
end
diff --git a/app/controllers/projects/cycle_analytics/events_controller.rb b/app/controllers/projects/cycle_analytics/events_controller.rb
index c69bf029c73..3a5dd23047c 100644
--- a/app/controllers/projects/cycle_analytics/events_controller.rb
+++ b/app/controllers/projects/cycle_analytics/events_controller.rb
@@ -11,6 +11,8 @@ module Projects
before_action :authorize_read_issue!, only: [:issue, :production]
before_action :authorize_read_merge_request!, only: [:code, :review]
+ feature_category :planning_analytics
+
def issue
render_events(cycle_analytics[:issue].events)
end
diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb
index ef97bc795f9..1ddc9d567e0 100644
--- a/app/controllers/projects/cycle_analytics_controller.rb
+++ b/app/controllers/projects/cycle_analytics_controller.rb
@@ -12,6 +12,8 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController
track_unique_visits :show, target_id: 'p_analytics_valuestream'
+ feature_category :planning_analytics
+
def show
@cycle_analytics = ::CycleAnalytics::ProjectLevel.new(@project, options: options(cycle_analytics_project_params))
diff --git a/app/controllers/projects/deploy_keys_controller.rb b/app/controllers/projects/deploy_keys_controller.rb
index 4f4adaea56e..ce25f86d692 100644
--- a/app/controllers/projects/deploy_keys_controller.rb
+++ b/app/controllers/projects/deploy_keys_controller.rb
@@ -10,6 +10,8 @@ class Projects::DeployKeysController < Projects::ApplicationController
layout 'project_settings'
+ feature_category :continuous_delivery
+
def index
respond_to do |format|
format.html { redirect_to_repository }
diff --git a/app/controllers/projects/deploy_tokens_controller.rb b/app/controllers/projects/deploy_tokens_controller.rb
index 830b1f4fe4a..3c890bbafdf 100644
--- a/app/controllers/projects/deploy_tokens_controller.rb
+++ b/app/controllers/projects/deploy_tokens_controller.rb
@@ -3,6 +3,8 @@
class Projects::DeployTokensController < Projects::ApplicationController
before_action :authorize_admin_project!
+ feature_category :continuous_delivery
+
def revoke
@token = @project.deploy_tokens.find(params[:id])
@token.revoke!
diff --git a/app/controllers/projects/deployments_controller.rb b/app/controllers/projects/deployments_controller.rb
index 1344cf775e4..231684427fb 100644
--- a/app/controllers/projects/deployments_controller.rb
+++ b/app/controllers/projects/deployments_controller.rb
@@ -3,6 +3,8 @@
class Projects::DeploymentsController < Projects::ApplicationController
before_action :authorize_read_deployment!
+ feature_category :continuous_delivery
+
# rubocop: disable CodeReuse/ActiveRecord
def index
deployments = environment.deployments.reorder(created_at: :desc)
diff --git a/app/controllers/projects/design_management/designs_controller.rb b/app/controllers/projects/design_management/designs_controller.rb
index fec09fa9515..550d8578396 100644
--- a/app/controllers/projects/design_management/designs_controller.rb
+++ b/app/controllers/projects/design_management/designs_controller.rb
@@ -3,6 +3,8 @@
class Projects::DesignManagement::DesignsController < Projects::ApplicationController
before_action :authorize_read_design!
+ feature_category :design_management
+
private
def authorize_read_design!
diff --git a/app/controllers/projects/discussions_controller.rb b/app/controllers/projects/discussions_controller.rb
index 06231607f73..b9ab1076999 100644
--- a/app/controllers/projects/discussions_controller.rb
+++ b/app/controllers/projects/discussions_controller.rb
@@ -9,6 +9,8 @@ class Projects::DiscussionsController < Projects::ApplicationController
before_action :discussion, only: [:resolve, :unresolve]
before_action :authorize_resolve_discussion!, only: [:resolve, :unresolve]
+ feature_category :issue_tracking
+
def resolve
Discussions::ResolveService.new(project, current_user, one_or_more_discussions: discussion).execute
diff --git a/app/controllers/projects/environments/prometheus_api_controller.rb b/app/controllers/projects/environments/prometheus_api_controller.rb
index f0bb5360f84..97810d7d439 100644
--- a/app/controllers/projects/environments/prometheus_api_controller.rb
+++ b/app/controllers/projects/environments/prometheus_api_controller.rb
@@ -5,6 +5,8 @@ class Projects::Environments::PrometheusApiController < Projects::ApplicationCon
before_action :proxyable
+ feature_category :metrics
+
private
def proxyable
diff --git a/app/controllers/projects/environments/sample_metrics_controller.rb b/app/controllers/projects/environments/sample_metrics_controller.rb
index 9176c7cbd56..3df20810cb3 100644
--- a/app/controllers/projects/environments/sample_metrics_controller.rb
+++ b/app/controllers/projects/environments/sample_metrics_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Projects::Environments::SampleMetricsController < Projects::ApplicationController
+ feature_category :metrics
+
def query
result = Metrics::SampleMetricsService.new(params[:identifier], range_start: params[:start], range_end: params[:end]).query
diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb
index 71195fdb892..c37abf82fe9 100644
--- a/app/controllers/projects/environments_controller.rb
+++ b/app/controllers/projects/environments_controller.rb
@@ -25,6 +25,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController
before_action :expire_etag_cache, only: [:index], unless: -> { request.format.json? }
after_action :expire_etag_cache, only: [:cancel_auto_stop]
+ feature_category :continuous_delivery
+
def index
@environments = project.environments
.with_state(params[:scope] || :available)
diff --git a/app/controllers/projects/error_tracking/base_controller.rb b/app/controllers/projects/error_tracking/base_controller.rb
index 6efc6d00702..ffbe487d8a1 100644
--- a/app/controllers/projects/error_tracking/base_controller.rb
+++ b/app/controllers/projects/error_tracking/base_controller.rb
@@ -3,6 +3,8 @@
class Projects::ErrorTracking::BaseController < Projects::ApplicationController
POLLING_INTERVAL = 1_000
+ feature_category :error_tracking
+
def set_polling_interval
Gitlab::PollingInterval.set_header(response, interval: POLLING_INTERVAL)
end
diff --git a/app/controllers/projects/error_tracking/projects_controller.rb b/app/controllers/projects/error_tracking/projects_controller.rb
index 75a2c976d8b..d59cbc25d25 100644
--- a/app/controllers/projects/error_tracking/projects_controller.rb
+++ b/app/controllers/projects/error_tracking/projects_controller.rb
@@ -7,6 +7,8 @@ module Projects
before_action :authorize_read_sentry_issue!
+ feature_category :error_tracking
+
def index
service = ::ErrorTracking::ListProjectsService.new(
project,
diff --git a/app/controllers/projects/feature_flags_clients_controller.rb b/app/controllers/projects/feature_flags_clients_controller.rb
new file mode 100644
index 00000000000..9a1f8932a27
--- /dev/null
+++ b/app/controllers/projects/feature_flags_clients_controller.rb
@@ -0,0 +1,29 @@
+# frozen_string_literal: true
+
+class Projects::FeatureFlagsClientsController < Projects::ApplicationController
+ before_action :authorize_admin_feature_flags_client!
+ before_action :feature_flags_client
+
+ feature_category :feature_flags
+
+ def reset_token
+ feature_flags_client.reset_token!
+
+ respond_to do |format|
+ format.json do
+ render json: feature_flags_client_token_json, status: :ok
+ end
+ end
+ end
+
+ private
+
+ def feature_flags_client
+ project.operations_feature_flags_client || not_found
+ end
+
+ def feature_flags_client_token_json
+ FeatureFlagsClientSerializer.new
+ .represent_token(feature_flags_client)
+ end
+end
diff --git a/app/controllers/projects/feature_flags_controller.rb b/app/controllers/projects/feature_flags_controller.rb
new file mode 100644
index 00000000000..e9d450a6ce3
--- /dev/null
+++ b/app/controllers/projects/feature_flags_controller.rb
@@ -0,0 +1,174 @@
+# frozen_string_literal: true
+
+class Projects::FeatureFlagsController < Projects::ApplicationController
+ respond_to :html
+
+ before_action :authorize_read_feature_flag!
+ before_action :authorize_create_feature_flag!, only: [:new, :create]
+ before_action :authorize_update_feature_flag!, only: [:edit, :update]
+ before_action :authorize_destroy_feature_flag!, only: [:destroy]
+
+ before_action :feature_flag, only: [:edit, :update, :destroy]
+
+ before_action :ensure_legacy_flags_writable!, only: [:update]
+
+ before_action do
+ push_frontend_feature_flag(:feature_flag_permissions)
+ push_frontend_feature_flag(:feature_flags_new_version, project, default_enabled: true)
+ push_frontend_feature_flag(:feature_flags_legacy_read_only, project, default_enabled: true)
+ push_frontend_feature_flag(:feature_flags_legacy_read_only_override, project)
+ end
+
+ feature_category :feature_flags
+
+ def index
+ @feature_flags = FeatureFlagsFinder
+ .new(project, current_user, scope: params[:scope])
+ .execute
+ .page(params[:page])
+ .per(30)
+
+ respond_to do |format|
+ format.html
+ format.json do
+ Gitlab::PollingInterval.set_header(response, interval: 10_000)
+
+ render json: { feature_flags: feature_flags_json }.merge(summary_json)
+ end
+ end
+ end
+
+ def new
+ end
+
+ def show
+ respond_to do |format|
+ format.json do
+ Gitlab::PollingInterval.set_header(response, interval: 10_000)
+
+ render_success_json(feature_flag)
+ end
+ end
+ end
+
+ def create
+ result = FeatureFlags::CreateService.new(project, current_user, create_params).execute
+
+ if result[:status] == :success
+ respond_to do |format|
+ format.json { render_success_json(result[:feature_flag]) }
+ end
+ else
+ respond_to do |format|
+ format.json { render_error_json(result[:message]) }
+ end
+ end
+ end
+
+ def edit
+ end
+
+ def update
+ result = FeatureFlags::UpdateService.new(project, current_user, update_params).execute(feature_flag)
+
+ if result[:status] == :success
+ respond_to do |format|
+ format.json { render_success_json(result[:feature_flag]) }
+ end
+ else
+ respond_to do |format|
+ format.json { render_error_json(result[:message]) }
+ end
+ end
+ end
+
+ def destroy
+ result = FeatureFlags::DestroyService.new(project, current_user).execute(feature_flag)
+
+ if result[:status] == :success
+ respond_to do |format|
+ format.html { redirect_to_index(notice: _('Feature flag was successfully removed.')) }
+ format.json { render_success_json(feature_flag) }
+ end
+ else
+ respond_to do |format|
+ format.html { redirect_to_index(alert: _('Feature flag was not removed.')) }
+ format.json { render_error_json(result[:message]) }
+ end
+ end
+ end
+
+ protected
+
+ def feature_flag
+ @feature_flag ||= @noteable = if new_version_feature_flags_enabled?
+ project.operations_feature_flags.find_by_iid!(params[:iid])
+ else
+ project.operations_feature_flags.legacy_flag.find_by_iid!(params[:iid])
+ end
+ end
+
+ def new_version_feature_flags_enabled?
+ ::Feature.enabled?(:feature_flags_new_version, project, default_enabled: true)
+ end
+
+ def ensure_legacy_flags_writable!
+ if ::Feature.enabled?(:feature_flags_legacy_read_only, project, default_enabled: true) &&
+ ::Feature.disabled?(:feature_flags_legacy_read_only_override, project) &&
+ feature_flag.legacy_flag?
+ render_error_json(['Legacy feature flags are read-only'])
+ end
+ end
+
+ def create_params
+ params.require(:operations_feature_flag)
+ .permit(:name, :description, :active, :version,
+ scopes_attributes: [:environment_scope, :active,
+ strategies: [:name, parameters: [:groupId, :percentage, :userIds]]],
+ strategies_attributes: [:name, :user_list_id,
+ parameters: [:groupId, :percentage, :userIds, :rollout, :stickiness],
+ scopes_attributes: [:environment_scope]])
+ end
+
+ def update_params
+ params.require(:operations_feature_flag)
+ .permit(:name, :description, :active,
+ scopes_attributes: [:id, :environment_scope, :active, :_destroy,
+ strategies: [:name, parameters: [:groupId, :percentage, :userIds]]],
+ strategies_attributes: [:id, :name, :user_list_id, :_destroy,
+ parameters: [:groupId, :percentage, :userIds, :rollout, :stickiness],
+ scopes_attributes: [:id, :environment_scope, :_destroy]])
+ end
+
+ def feature_flag_json(feature_flag)
+ FeatureFlagSerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(feature_flag)
+ end
+
+ def feature_flags_json
+ FeatureFlagSerializer
+ .new(project: @project, current_user: @current_user)
+ .with_pagination(request, response)
+ .represent(@feature_flags)
+ end
+
+ def summary_json
+ FeatureFlagSummarySerializer
+ .new(project: @project, current_user: @current_user)
+ .represent(@project)
+ end
+
+ def redirect_to_index(**args)
+ redirect_to project_feature_flags_path(@project), status: :found, **args
+ end
+
+ def render_success_json(feature_flag)
+ render json: feature_flag_json(feature_flag), status: :ok
+ end
+
+ def render_error_json(messages)
+ render json: { message: messages },
+ status: :bad_request
+ end
+end
diff --git a/app/controllers/projects/feature_flags_user_lists_controller.rb b/app/controllers/projects/feature_flags_user_lists_controller.rb
new file mode 100644
index 00000000000..7be3254e966
--- /dev/null
+++ b/app/controllers/projects/feature_flags_user_lists_controller.rb
@@ -0,0 +1,23 @@
+# frozen_string_literal: true
+
+class Projects::FeatureFlagsUserListsController < Projects::ApplicationController
+ before_action :authorize_admin_feature_flags_user_lists!
+ before_action :user_list, only: [:edit, :show]
+
+ feature_category :feature_flags
+
+ def new
+ end
+
+ def edit
+ end
+
+ def show
+ end
+
+ private
+
+ def user_list
+ @user_list = project.operations_feature_flags_user_lists.find_by_iid!(params[:iid])
+ end
+end
diff --git a/app/controllers/projects/find_file_controller.rb b/app/controllers/projects/find_file_controller.rb
index c026e9ff332..89e72d98a33 100644
--- a/app/controllers/projects/find_file_controller.rb
+++ b/app/controllers/projects/find_file_controller.rb
@@ -10,6 +10,8 @@ class Projects::FindFileController < Projects::ApplicationController
before_action :assign_ref_vars
before_action :authorize_download_code!
+ feature_category :source_code_management
+
def show
return render_404 unless @repository.commit(@ref)
diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb
index c6b6b825bb7..1c2930f6e9b 100644
--- a/app/controllers/projects/forks_controller.rb
+++ b/app/controllers/projects/forks_controller.rb
@@ -14,6 +14,8 @@ class Projects::ForksController < Projects::ApplicationController
before_action :authorize_fork_project!, only: [:new, :create]
before_action :authorize_fork_namespace!, only: [:create]
+ feature_category :source_code_management
+
def index
@total_forks_count = project.forks.size
@public_forks_count = project.forks.public_only.size
diff --git a/app/controllers/projects/grafana_api_controller.rb b/app/controllers/projects/grafana_api_controller.rb
index c9870f1be2b..9c5d6c8ebc3 100644
--- a/app/controllers/projects/grafana_api_controller.rb
+++ b/app/controllers/projects/grafana_api_controller.rb
@@ -4,6 +4,8 @@ class Projects::GrafanaApiController < Projects::ApplicationController
include RenderServiceResults
include MetricsDashboard
+ feature_category :metrics
+
def proxy
result = ::Grafana::ProxyService.new(
project,
diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb
index 9b889f9e837..2b030793c58 100644
--- a/app/controllers/projects/graphs_controller.rb
+++ b/app/controllers/projects/graphs_controller.rb
@@ -11,6 +11,8 @@ class Projects::GraphsController < Projects::ApplicationController
track_unique_visits :charts, target_id: 'p_analytics_repo'
+ feature_category :source_code_management
+
def show
respond_to do |format|
format.html
@@ -57,7 +59,6 @@ class Projects::GraphsController < Projects::ApplicationController
end
def get_daily_coverage_options
- return unless Feature.enabled?(:ci_download_daily_code_coverage, @project, default_enabled: true)
return unless can?(current_user, :read_build_report_results, project)
date_today = Date.current
diff --git a/app/controllers/projects/group_links_controller.rb b/app/controllers/projects/group_links_controller.rb
index a30c455a7e4..c6c90ffaba2 100644
--- a/app/controllers/projects/group_links_controller.rb
+++ b/app/controllers/projects/group_links_controller.rb
@@ -5,6 +5,8 @@ class Projects::GroupLinksController < Projects::ApplicationController
before_action :authorize_admin_project!
before_action :authorize_admin_project_member!, only: [:update]
+ feature_category :subgroups
+
def create
group = Group.find(params[:link_group_id]) if params[:link_group_id].present?
@@ -21,8 +23,17 @@ class Projects::GroupLinksController < Projects::ApplicationController
end
def update
- @group_link = @project.project_group_links.find(params[:id])
- Projects::GroupLinks::UpdateService.new(@group_link).execute(group_link_params)
+ group_link = @project.project_group_links.find(params[:id])
+ Projects::GroupLinks::UpdateService.new(group_link).execute(group_link_params)
+
+ if group_link.expires?
+ render json: {
+ expires_in: helpers.distance_of_time_in_words_to_now(group_link.expires_at),
+ expires_soon: group_link.expires_soon?
+ }
+ else
+ render json: {}
+ end
end
def destroy
diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb
index ed7e7b68acb..99ebe3335c0 100644
--- a/app/controllers/projects/hook_logs_controller.rb
+++ b/app/controllers/projects/hook_logs_controller.rb
@@ -12,6 +12,8 @@ class Projects::HookLogsController < Projects::ApplicationController
layout 'project_settings'
+ feature_category :integrations
+
def show
end
diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb
index 2f4dc1ddc3a..8dabf3e640b 100644
--- a/app/controllers/projects/hooks_controller.rb
+++ b/app/controllers/projects/hooks_controller.rb
@@ -12,6 +12,8 @@ class Projects::HooksController < Projects::ApplicationController
layout "project_settings"
+ feature_category :integrations
+
def index
@hooks = @project.hooks
@hook = ProjectHook.new
@@ -50,7 +52,7 @@ class Projects::HooksController < Projects::ApplicationController
end
def destroy
- hook.destroy
+ destroy_hook(hook)
redirect_to action: :index, status: :found
end
diff --git a/app/controllers/projects/import/jira_controller.rb b/app/controllers/projects/import/jira_controller.rb
index 976ac7df976..8418a607659 100644
--- a/app/controllers/projects/import/jira_controller.rb
+++ b/app/controllers/projects/import/jira_controller.rb
@@ -7,6 +7,8 @@ module Projects
before_action :authorize_read_project!
before_action :validate_jira_import_settings!
+ feature_category :integrations
+
def show
end
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 9bed12fd151..6cdd1c0bc8c 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -11,6 +11,8 @@ class Projects::ImportsController < Projects::ApplicationController
before_action :redirect_if_progress, except: :show
before_action :redirect_if_no_import, only: :show
+ feature_category :importers
+
def new
end
diff --git a/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb b/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb
index dac1640dd08..f1264ca4a45 100644
--- a/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb
+++ b/app/controllers/projects/incident_management/pager_duty_incidents_controller.rb
@@ -10,6 +10,8 @@ module Projects
prepend_before_action :project_without_auth
+ feature_category :incident_management
+
def create
result = webhook_processor.execute(params[:token])
diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb
index 12cc4dde1f4..3395e75666e 100644
--- a/app/controllers/projects/incidents_controller.rb
+++ b/app/controllers/projects/incidents_controller.rb
@@ -1,8 +1,45 @@
# frozen_string_literal: true
class Projects::IncidentsController < Projects::ApplicationController
- before_action :authorize_read_incidents!
+ include IssuableActions
+ include Gitlab::Utils::StrongMemoize
+
+ before_action :authorize_read_issue!
+ before_action :load_incident, only: [:show]
+
+ feature_category :incident_management
def index
end
+
+ private
+
+ def incident
+ strong_memoize(:incident) do
+ incident_finder
+ .execute
+ .inc_relations_for_view
+ .iid_in(params[:id])
+ .without_order
+ .first
+ end
+ end
+
+ def load_incident
+ @issue = incident # needed by rendered view
+ return render_404 unless can?(current_user, :read_issue, incident)
+
+ @noteable = incident
+ @note = incident.project.notes.new(noteable: issuable)
+ end
+
+ alias_method :issuable, :incident
+
+ def incident_finder
+ IssuesFinder.new(current_user, project_id: @project.id, issue_types: :incident)
+ end
+
+ def serializer
+ IssueSerializer.new(current_user: current_user, project: incident.project)
+ end
end
diff --git a/app/controllers/projects/issue_links_controller.rb b/app/controllers/projects/issue_links_controller.rb
index 2f7489373ed..35f3e00fae7 100644
--- a/app/controllers/projects/issue_links_controller.rb
+++ b/app/controllers/projects/issue_links_controller.rb
@@ -7,6 +7,8 @@ module Projects
before_action :authorize_admin_issue_link!, only: [:create, :destroy]
before_action :authorize_issue_link_association!, only: :destroy
+ feature_category :issue_tracking
+
private
def authorize_admin_issue_link!
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 7f0d23b79ce..9a8965dbeb6 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -44,8 +44,6 @@ class Projects::IssuesController < Projects::ApplicationController
push_frontend_feature_flag(:vue_issuable_sidebar, project.group)
push_frontend_feature_flag(:tribute_autocomplete, @project)
push_frontend_feature_flag(:vue_issuables_list, project)
- push_frontend_feature_flag(:design_management_todo_button, project, default_enabled: true)
- push_frontend_feature_flag(:vue_sidebar_labels, @project)
end
before_action only: :show do
@@ -53,10 +51,13 @@ class Projects::IssuesController < Projects::ApplicationController
real_time_enabled = Gitlab::ActionCable::Config.in_app? || Feature.enabled?(real_time_feature_flag, @project)
gon.push({ features: { real_time_feature_flag.to_s.camelize(:lower) => real_time_enabled } }, true)
+
+ record_experiment_user(:invite_members_version_a)
+ record_experiment_user(:invite_members_version_b)
end
before_action only: :index do
- push_frontend_feature_flag(:scoped_labels, @project)
+ push_frontend_feature_flag(:scoped_labels, @project, type: :licensed)
end
around_action :allow_gitaly_ref_name_caching, only: [:discussions]
@@ -65,6 +66,17 @@ class Projects::IssuesController < Projects::ApplicationController
alias_method :designs, :show
+ feature_category :issue_tracking, [
+ :index, :calendar, :show, :new, :create, :edit, :update,
+ :destroy, :move, :reorder, :designs, :toggle_subscription,
+ :discussions, :bulk_update, :realtime_changes,
+ :toggle_award_emoji, :mark_as_spam, :related_branches,
+ :can_create_branch, :create_merge_request
+ ]
+
+ feature_category :service_desk, [:service_desk]
+ feature_category :importers, [:import_csv, :export_csv]
+
def index
@issues = @issuables
@@ -204,7 +216,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def export_csv
- ExportCsvWorker.perform_async(current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker
+ IssuableExportCsvWorker.perform_async(:issue, current_user.id, project.id, finder_options.to_h) # rubocop:disable CodeReuse/Worker
index_path = project_issues_path(project)
message = _('Your CSV export has started. It will be emailed to %{email} when complete.') % { email: current_user.notification_email }
@@ -239,7 +251,7 @@ class Projects::IssuesController < Projects::ApplicationController
return @issue if defined?(@issue)
# The Sortable default scope causes performance issues when used with find_by
- @issuable = @noteable = @issue ||= @project.issues.includes(author: :status).where(iid: params[:id]).reorder(nil).take!
+ @issuable = @noteable = @issue ||= @project.issues.inc_relations_for_view.iid_in(params[:id]).without_order.take!
@note = @project.notes.new(noteable: @issuable)
return render_404 unless can?(current_user, :read_issue, @issue)
@@ -313,7 +325,7 @@ class Projects::IssuesController < Projects::ApplicationController
end
def store_uri
- if request.get? && !request.xhr?
+ if request.get? && request.format.html?
store_location_for :user, request.fullpath
end
end
diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb
index 3f7f8da3478..3ceb60a6aef 100644
--- a/app/controllers/projects/jobs_controller.rb
+++ b/app/controllers/projects/jobs_controller.rb
@@ -4,7 +4,8 @@ class Projects::JobsController < Projects::ApplicationController
include SendFileUpload
include ContinueParams
- before_action :build, except: [:index]
+ before_action :find_job_as_build, except: [:index, :play]
+ before_action :find_job_as_processable, only: [:play]
before_action :authorize_read_build!
before_action :authorize_update_build!,
except: [:index, :show, :status, :raw, :trace, :erase]
@@ -16,6 +17,8 @@ class Projects::JobsController < Projects::ApplicationController
layout 'project'
+ feature_category :continuous_integration
+
def index
# We need all builds for tabs counters
@all_builds = Ci::JobsFinder.new(current_user: current_user, project: @project).execute
@@ -28,11 +31,6 @@ class Projects::JobsController < Projects::ApplicationController
# rubocop: disable CodeReuse/ActiveRecord
def show
- @pipeline = @build.pipeline
- @builds = @pipeline.builds
- .order('id DESC')
- .present(current_user: current_user)
-
respond_to do |format|
format.html
format.json do
@@ -47,10 +45,10 @@ class Projects::JobsController < Projects::ApplicationController
# rubocop: enable CodeReuse/ActiveRecord
def trace
- build.trace.read do |stream|
+ @build.trace.read do |stream|
respond_to do |format|
format.json do
- build.trace.being_watched!
+ @build.trace.being_watched!
build_trace = Ci::BuildTrace.new(
build: @build,
@@ -75,8 +73,13 @@ class Projects::JobsController < Projects::ApplicationController
def play
return respond_422 unless @build.playable?
- build = @build.play(current_user, play_params[:job_variables_attributes])
- redirect_to build_path(build)
+ job = @build.play(current_user, play_params[:job_variables_attributes])
+
+ if job.is_a?(Ci::Bridge)
+ redirect_to pipeline_path(job.pipeline)
+ else
+ redirect_to build_path(job)
+ end
end
def cancel
@@ -120,7 +123,7 @@ class Projects::JobsController < Projects::ApplicationController
send_params: raw_send_params,
redirect_params: raw_redirect_params)
else
- build.trace.read do |stream|
+ @build.trace.read do |stream|
if stream.file?
workhorse_set_content_type!
send_file stream.path, type: 'text/plain; charset=utf-8', disposition: 'inline'
@@ -152,19 +155,19 @@ class Projects::JobsController < Projects::ApplicationController
private
def authorize_update_build!
- return access_denied! unless can?(current_user, :update_build, build)
+ return access_denied! unless can?(current_user, :update_build, @build)
end
def authorize_erase_build!
- return access_denied! unless can?(current_user, :erase_build, build)
+ return access_denied! unless can?(current_user, :erase_build, @build)
end
def authorize_use_build_terminal!
- return access_denied! unless can?(current_user, :create_build_terminal, build)
+ return access_denied! unless can?(current_user, :create_build_terminal, @build)
end
def authorize_create_proxy_build!
- return access_denied! unless can?(current_user, :create_build_service_proxy, build)
+ return access_denied! unless can?(current_user, :create_build_service_proxy, @build)
end
def verify_api_request!
@@ -189,14 +192,22 @@ class Projects::JobsController < Projects::ApplicationController
end
def trace_artifact_file
- @trace_artifact_file ||= build.job_artifacts_trace&.file
+ @trace_artifact_file ||= @build.job_artifacts_trace&.file
end
- def build
- @build ||= project.builds.find(params[:id])
+ def find_job_as_build
+ @build = project.builds.find(params[:id])
.present(current_user: current_user)
end
+ def find_job_as_processable
+ if ::Gitlab::Ci::Features.manual_bridges_enabled?(project)
+ @build = project.processables.find(params[:id])
+ else
+ find_job_as_build
+ end
+ end
+
def build_path(build)
project_job_path(build.project, build)
end
@@ -211,10 +222,10 @@ class Projects::JobsController < Projects::ApplicationController
end
def build_service_specification
- build.service_specification(service: params['service'],
- port: params['port'],
- path: params['path'],
- subprotocols: proxy_subprotocol)
+ @build.service_specification(service: params['service'],
+ port: params['port'],
+ path: params['path'],
+ subprotocols: proxy_subprotocol)
end
def proxy_subprotocol
diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb
index b7aeab8f5ff..ba8e6b90971 100644
--- a/app/controllers/projects/labels_controller.rb
+++ b/app/controllers/projects/labels_controller.rb
@@ -2,6 +2,7 @@
class Projects::LabelsController < Projects::ApplicationController
include ToggleSubscriptionAction
+ include ShowInheritedLabelsChecker
before_action :check_issuables_available!
before_action :label, only: [:edit, :update, :destroy, :promote]
@@ -14,6 +15,8 @@ class Projects::LabelsController < Projects::ApplicationController
respond_to :js, :html
+ feature_category :issue_tracking
+
def index
@prioritized_labels = @available_labels.prioritized(@project)
@labels = @available_labels.unprioritized(@project).page(params[:page])
@@ -161,7 +164,7 @@ class Projects::LabelsController < Projects::ApplicationController
@available_labels ||=
LabelsFinder.new(current_user,
project_id: @project.id,
- include_ancestor_groups: params[:include_ancestor_groups],
+ include_ancestor_groups: show_inherited_labels?(params[:include_ancestor_groups]),
search: params[:search],
subscribed: params[:subscribed],
sort: sort).execute
diff --git a/app/controllers/projects/logs_controller.rb b/app/controllers/projects/logs_controller.rb
index b9027b3a2cb..b9aa9bfc947 100644
--- a/app/controllers/projects/logs_controller.rb
+++ b/app/controllers/projects/logs_controller.rb
@@ -7,6 +7,8 @@ module Projects
before_action :authorize_read_pod_logs!
before_action :ensure_deployments, only: %i(k8s elasticsearch)
+ feature_category :logging
+
def index
if environment || cluster
render :index
diff --git a/app/controllers/projects/mattermosts_controller.rb b/app/controllers/projects/mattermosts_controller.rb
index cfaeddf711a..63a36732d87 100644
--- a/app/controllers/projects/mattermosts_controller.rb
+++ b/app/controllers/projects/mattermosts_controller.rb
@@ -10,6 +10,8 @@ class Projects::MattermostsController < Projects::ApplicationController
before_action :service
before_action :teams, only: [:new]
+ feature_category :integrations
+
def new
end
diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb
index 921da788ad2..9cac9f37eb7 100644
--- a/app/controllers/projects/merge_requests/application_controller.rb
+++ b/app/controllers/projects/merge_requests/application_controller.rb
@@ -5,6 +5,8 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
before_action :merge_request
before_action :authorize_read_merge_request!
+ feature_category :code_review
+
private
def merge_request
@@ -35,6 +37,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont
:source_branch,
:source_project_id,
:state_event,
+ :wip_event,
:squash,
:target_branch,
:target_project_id,
diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb
index 8aacfdce094..07c38431f0f 100644
--- a/app/controllers/projects/merge_requests/diffs_controller.rb
+++ b/app/controllers/projects/merge_requests/diffs_controller.rb
@@ -64,7 +64,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
render: ->(partial, locals) { view_to_html_string(partial, locals) }
}
- options = additional_attributes.merge(diff_view: Feature.enabled?(:unified_diff_lines, @merge_request.project) ? "inline" : diff_view)
+ options = additional_attributes.merge(diff_view: Feature.enabled?(:unified_diff_lines, @merge_request.project, default_enabled: true) ? "inline" : diff_view)
if @merge_request.project.context_commits_enabled?
options[:context_commits] = @merge_request.recent_context_commits
@@ -173,7 +173,6 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic
end
def update_diff_discussion_positions!
- return unless Feature.enabled?(:merge_ref_head_comments, @merge_request.target_project, default_enabled: true)
return unless Feature.enabled?(:merge_red_head_comments_position_on_demand, @merge_request.target_project, default_enabled: true)
return if @merge_request.has_any_diff_note_positions?
diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb
index f4846b1aa81..ca3f36cafe1 100644
--- a/app/controllers/projects/merge_requests/drafts_controller.rb
+++ b/app/controllers/projects/merge_requests/drafts_controller.rb
@@ -45,7 +45,7 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli
if result[:status] == :success
head :ok
else
- render json: { message: result[:message] }, status: result[:status]
+ render json: { message: result[:message] }, status: :internal_server_error
end
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 92785540172..91a041bb35b 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -27,11 +27,8 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
before_action :authenticate_user!, only: [:assign_related_issues]
before_action :check_user_can_push_to_source_branch!, only: [:rebase]
before_action only: [:show] do
- push_frontend_feature_flag(:deploy_from_footer, @project, default_enabled: true)
- push_frontend_feature_flag(:suggest_pipeline) if experiment_enabled?(:suggest_pipeline)
- push_frontend_feature_flag(:code_navigation, @project, default_enabled: true)
+ push_frontend_experiment(:suggest_pipeline)
push_frontend_feature_flag(:widget_visibility_polling, @project, default_enabled: true)
- push_frontend_feature_flag(:merge_ref_head_comments, @project, default_enabled: true)
push_frontend_feature_flag(:mr_commit_neighbor_nav, @project, default_enabled: true)
push_frontend_feature_flag(:multiline_comments, @project, default_enabled: true)
push_frontend_feature_flag(:file_identifier_hash)
@@ -39,25 +36,35 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo
push_frontend_feature_flag(:approvals_commented_by, @project, default_enabled: true)
push_frontend_feature_flag(:hide_jump_to_next_unresolved_in_threads, default_enabled: true)
push_frontend_feature_flag(:merge_request_widget_graphql, @project)
- push_frontend_feature_flag(:unified_diff_lines, @project)
+ push_frontend_feature_flag(:unified_diff_lines, @project, default_enabled: true)
push_frontend_feature_flag(:highlight_current_diff_row, @project)
push_frontend_feature_flag(:default_merge_ref_for_diffs, @project)
+ push_frontend_feature_flag(:core_security_mr_widget, @project, default_enabled: true)
+
+ record_experiment_user(:invite_members_version_a)
+ record_experiment_user(:invite_members_version_b)
end
before_action do
push_frontend_feature_flag(:vue_issuable_sidebar, @project.group)
+ push_frontend_feature_flag(:deployment_filters)
end
around_action :allow_gitaly_ref_name_caching, only: [:index, :show, :discussions]
after_action :log_merge_request_show, only: [:show]
- feature_category :source_code_management,
- unless: -> (action) { action.ends_with?("_reports") }
- feature_category :code_testing,
- only: [:test_reports, :coverage_reports, :terraform_reports]
- feature_category :accessibility_testing,
- only: [:accessibility_reports]
+ feature_category :code_review, [
+ :assign_related_issues, :bulk_update, :cancel_auto_merge,
+ :ci_environments_status, :commit_change_content, :commits,
+ :context_commits, :destroy, :diff_for_path, :discussions,
+ :edit, :exposed_artifacts, :index, :merge,
+ :pipeline_status, :pipelines, :rebase, :remove_wip, :show,
+ :toggle_award_emoji, :toggle_subscription, :update
+ ]
+
+ feature_category :code_testing, [:test_reports, :coverage_reports, :terraform_reports]
+ feature_category :accessibility_testing, [:accessibility_reports]
def index
@merge_requests = @issuables
diff --git a/app/controllers/projects/metrics/dashboards/builder_controller.rb b/app/controllers/projects/metrics/dashboards/builder_controller.rb
index 2ab574d7d10..96ca6d89111 100644
--- a/app/controllers/projects/metrics/dashboards/builder_controller.rb
+++ b/app/controllers/projects/metrics/dashboards/builder_controller.rb
@@ -6,6 +6,8 @@ module Projects
class BuilderController < Projects::ApplicationController
before_action :authorize_metrics_dashboard!
+ feature_category :metrics
+
def panel_preview
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/metrics_dashboard_controller.rb b/app/controllers/projects/metrics_dashboard_controller.rb
index bc0a701b9fd..3f10749602e 100644
--- a/app/controllers/projects/metrics_dashboard_controller.rb
+++ b/app/controllers/projects/metrics_dashboard_controller.rb
@@ -14,6 +14,8 @@ module Projects
push_frontend_feature_flag(:disable_metric_dashboard_refresh_rate)
end
+ feature_category :metrics
+
def show
if environment
render 'projects/environments/metrics'
diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb
index 16d63cc184f..e6c4af00b29 100644
--- a/app/controllers/projects/milestones_controller.rb
+++ b/app/controllers/projects/milestones_controller.rb
@@ -5,7 +5,7 @@ class Projects::MilestonesController < Projects::ApplicationController
include MilestoneActions
before_action :check_issuables_available!
- before_action :milestone, only: [:edit, :update, :destroy, :show, :merge_requests, :participants, :labels, :promote]
+ before_action :milestone, only: [:edit, :update, :destroy, :show, :issues, :merge_requests, :participants, :labels, :promote]
before_action do
push_frontend_feature_flag(:burnup_charts, @project)
end
@@ -14,13 +14,15 @@ class Projects::MilestonesController < Projects::ApplicationController
before_action :authorize_read_milestone!
# Allow admin milestone
- before_action :authorize_admin_milestone!, except: [:index, :show, :merge_requests, :participants, :labels]
+ before_action :authorize_admin_milestone!, except: [:index, :show, :issues, :merge_requests, :participants, :labels]
# Allow to promote milestone
before_action :authorize_promote_milestone!, only: :promote
respond_to :html
+ feature_category :issue_tracking
+
def index
@sort = params[:sort] || 'due_date_asc'
@milestones = milestones.sort_by_attribute(@sort)
diff --git a/app/controllers/projects/mirrors_controller.rb b/app/controllers/projects/mirrors_controller.rb
index 518e6a92afa..01abb72fc86 100644
--- a/app/controllers/projects/mirrors_controller.rb
+++ b/app/controllers/projects/mirrors_controller.rb
@@ -10,6 +10,8 @@ class Projects::MirrorsController < Projects::ApplicationController
layout "project_settings"
+ feature_category :source_code_management
+
def show
redirect_to_repository_settings(project, anchor: 'js-push-remote-settings')
end
diff --git a/app/controllers/projects/network_controller.rb b/app/controllers/projects/network_controller.rb
index 47788438da9..89b679fc033 100644
--- a/app/controllers/projects/network_controller.rb
+++ b/app/controllers/projects/network_controller.rb
@@ -11,6 +11,8 @@ class Projects::NetworkController < Projects::ApplicationController
before_action :assign_options
before_action :assign_commit
+ feature_category :source_code_management
+
def show
@url = project_network_path(@project, @ref, @options.merge(format: :json))
@commit_url = project_commit_path(@project, 'ae45ca32').gsub("ae45ca32", "%s")
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index c0b8c9e550d..e50e293a103 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -11,6 +11,8 @@ class Projects::NotesController < Projects::ApplicationController
before_action :authorize_create_note!, only: [:create]
before_action :authorize_resolve_note!, only: [:resolve, :unresolve]
+ feature_category :issue_tracking
+
def delete_attachment
note.remove_attachment!
note.update_attribute(:attachment, nil)
diff --git a/app/controllers/projects/packages/package_files_controller.rb b/app/controllers/projects/packages/package_files_controller.rb
index dd6d875cd1e..32aadb4fcf4 100644
--- a/app/controllers/projects/packages/package_files_controller.rb
+++ b/app/controllers/projects/packages/package_files_controller.rb
@@ -6,6 +6,8 @@ module Projects
include PackagesAccess
include SendFileUpload
+ feature_category :package_registry
+
def download
package_file = project.package_files.find(params[:id])
diff --git a/app/controllers/projects/packages/packages_controller.rb b/app/controllers/projects/packages/packages_controller.rb
index fc4ef7a01dc..15dc11f5df8 100644
--- a/app/controllers/projects/packages/packages_controller.rb
+++ b/app/controllers/projects/packages/packages_controller.rb
@@ -5,20 +5,13 @@ module Projects
class PackagesController < Projects::ApplicationController
include PackagesAccess
- before_action :authorize_destroy_package!, only: [:destroy]
+ feature_category :package_registry
def show
@package = project.packages.find(params[:id])
@package_files = @package.package_files.recent
@maven_metadatum = @package.maven_metadatum
end
-
- def destroy
- @package = project.packages.find(params[:id])
- @package.destroy
-
- redirect_to project_packages_path(@project), status: :found, notice: _('Package was removed')
- end
end
end
end
diff --git a/app/controllers/projects/pages_controller.rb b/app/controllers/projects/pages_controller.rb
index 9ad6bf4095a..0aac517e3e3 100644
--- a/app/controllers/projects/pages_controller.rb
+++ b/app/controllers/projects/pages_controller.rb
@@ -8,6 +8,8 @@ class Projects::PagesController < Projects::ApplicationController
before_action :authorize_update_pages!, except: [:show, :destroy]
before_action :authorize_remove_pages!, only: [:destroy]
+ feature_category :pages
+
# rubocop: disable CodeReuse/ActiveRecord
def show
@domains = @project.pages_domains.order(:domain).present(current_user: current_user)
diff --git a/app/controllers/projects/pages_domains_controller.rb b/app/controllers/projects/pages_domains_controller.rb
index cccf8fe4358..a6b22a28b17 100644
--- a/app/controllers/projects/pages_domains_controller.rb
+++ b/app/controllers/projects/pages_domains_controller.rb
@@ -9,6 +9,8 @@ class Projects::PagesDomainsController < Projects::ApplicationController
helper_method :domain_presenter
+ feature_category :pages
+
def show
end
diff --git a/app/controllers/projects/performance_monitoring/dashboards_controller.rb b/app/controllers/projects/performance_monitoring/dashboards_controller.rb
index ec5a33f5dd6..51a07c1b7a5 100644
--- a/app/controllers/projects/performance_monitoring/dashboards_controller.rb
+++ b/app/controllers/projects/performance_monitoring/dashboards_controller.rb
@@ -12,6 +12,8 @@ module Projects
respond_error(http_status: :bad_request, message: _('Request parameter %{param} is missing.') % { param: exception.param })
end
+ feature_category :metrics
+
def create
result = ::Metrics::Dashboard::CloneDashboardService.new(project, current_user, dashboard_params).execute
diff --git a/app/controllers/projects/pipeline_schedules_controller.rb b/app/controllers/projects/pipeline_schedules_controller.rb
index e7e8a900060..5655d3b4c0d 100644
--- a/app/controllers/projects/pipeline_schedules_controller.rb
+++ b/app/controllers/projects/pipeline_schedules_controller.rb
@@ -10,6 +10,8 @@ class Projects::PipelineSchedulesController < Projects::ApplicationController
before_action :authorize_update_pipeline_schedule!, except: [:index, :new, :create, :play]
before_action :authorize_admin_pipeline_schedule!, only: [:destroy]
+ feature_category :continuous_integration
+
# rubocop: disable CodeReuse/ActiveRecord
def index
@scope = params[:scope]
diff --git a/app/controllers/projects/pipelines/application_controller.rb b/app/controllers/projects/pipelines/application_controller.rb
index 92887750813..c147d697888 100644
--- a/app/controllers/projects/pipelines/application_controller.rb
+++ b/app/controllers/projects/pipelines/application_controller.rb
@@ -10,6 +10,8 @@ module Projects
before_action :pipeline
before_action :authorize_read_pipeline!
+ feature_category :continuous_integration
+
private
def pipeline
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index c1734d2cd8a..953dce4d63c 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -5,17 +5,19 @@ class Projects::PipelinesController < Projects::ApplicationController
include Analytics::UniqueVisitsHelper
before_action :whitelist_query_limiting, only: [:create, :retry]
- before_action :pipeline, except: [:index, :new, :create, :charts]
+ before_action :pipeline, except: [:index, :new, :create, :charts, :config_variables]
before_action :set_pipeline_path, only: [:show]
before_action :authorize_read_pipeline!
before_action :authorize_read_build!, only: [:index]
- before_action :authorize_create_pipeline!, only: [:new, :create]
+ before_action :authorize_create_pipeline!, only: [:new, :create, :config_variables]
before_action :authorize_update_pipeline!, only: [:retry, :cancel]
before_action do
push_frontend_feature_flag(:filter_pipelines_search, project, default_enabled: true)
push_frontend_feature_flag(:dag_pipeline_tab, project, default_enabled: true)
push_frontend_feature_flag(:pipelines_security_report_summary, project)
- push_frontend_feature_flag(:new_pipeline_form)
+ push_frontend_feature_flag(:new_pipeline_form, project)
+ push_frontend_feature_flag(:graphql_pipeline_header, project, type: :development, default_enabled: false)
+ push_frontend_feature_flag(:new_pipeline_form_prefilled_vars, project, type: :development)
end
before_action :ensure_pipeline, only: [:show]
@@ -30,6 +32,8 @@ class Projects::PipelinesController < Projects::ApplicationController
POLLING_INTERVAL = 10_000
+ feature_category :continuous_integration
+
def index
@pipelines = Ci::PipelinesFinder
.new(project, current_user, index_params)
@@ -206,6 +210,14 @@ class Projects::PipelinesController < Projects::ApplicationController
end
end
+ def config_variables
+ respond_to do |format|
+ format.json do
+ render json: Ci::ListConfigVariablesService.new(@project).execute(params[:sha])
+ end
+ end
+ end
+
private
def serialize_pipelines
diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb
index 7f988110977..6e08a889520 100644
--- a/app/controllers/projects/pipelines_settings_controller.rb
+++ b/app/controllers/projects/pipelines_settings_controller.rb
@@ -3,6 +3,8 @@
class Projects::PipelinesSettingsController < Projects::ApplicationController
before_action :authorize_admin_pipeline!
+ feature_category :continuous_integration
+
def show
redirect_to project_settings_ci_cd_path(@project, params: params.to_unsafe_h)
end
diff --git a/app/controllers/projects/product_analytics_controller.rb b/app/controllers/projects/product_analytics_controller.rb
index c019dc191d6..5db7585d8e0 100644
--- a/app/controllers/projects/product_analytics_controller.rb
+++ b/app/controllers/projects/product_analytics_controller.rb
@@ -5,6 +5,8 @@ class Projects::ProductAnalyticsController < Projects::ApplicationController
before_action :authorize_read_product_analytics!
before_action :tracker_variables, only: [:setup, :test]
+ feature_category :product_analytics
+
def index
@events = product_analytics_events.order_by_time.page(params[:page])
end
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 3e52248f292..631f627838b 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -8,6 +8,8 @@ class Projects::ProjectMembersController < Projects::ApplicationController
# Authorize
before_action :authorize_admin_project_member!, except: [:index, :leave, :request_access]
+ feature_category :authentication_and_authorization
+
def index
@sort = params[:sort].presence || sort_value_name
@@ -55,6 +57,10 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def filter_params
params.permit(:search).merge(sort: @sort)
end
+
+ def membershipable_members
+ project.members
+ end
end
Projects::ProjectMembersController.prepend_if_ee('EE::Projects::ProjectMembersController')
diff --git a/app/controllers/projects/prometheus/alerts_controller.rb b/app/controllers/projects/prometheus/alerts_controller.rb
index c6ae65f7832..2892542e63c 100644
--- a/app/controllers/projects/prometheus/alerts_controller.rb
+++ b/app/controllers/projects/prometheus/alerts_controller.rb
@@ -16,6 +16,8 @@ module Projects
before_action :authorize_read_prometheus_alerts!, except: [:notify]
before_action :alert, only: [:update, :show, :destroy, :metrics_dashboard]
+ feature_category :alert_management
+
def index
render json: serialize_as_json(alerts)
end
diff --git a/app/controllers/projects/prometheus/metrics_controller.rb b/app/controllers/projects/prometheus/metrics_controller.rb
index 0340cb5beb0..d70d29a341f 100644
--- a/app/controllers/projects/prometheus/metrics_controller.rb
+++ b/app/controllers/projects/prometheus/metrics_controller.rb
@@ -6,6 +6,8 @@ module Projects
before_action :authorize_admin_project!
before_action :require_prometheus_metrics!
+ feature_category :metrics
+
def active_common
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/protected_refs_controller.rb b/app/controllers/projects/protected_refs_controller.rb
index 060403a9cd9..4cba1a75330 100644
--- a/app/controllers/projects/protected_refs_controller.rb
+++ b/app/controllers/projects/protected_refs_controller.rb
@@ -10,6 +10,8 @@ class Projects::ProtectedRefsController < Projects::ApplicationController
layout "project_settings"
+ feature_category :source_code_management
+
def index
redirect_to_repository_settings(@project)
end
@@ -62,7 +64,7 @@ class Projects::ProtectedRefsController < Projects::ApplicationController
end
def access_level_attributes
- %i[access_level id _destroy]
+ %i[access_level id _destroy deploy_key_id]
end
end
diff --git a/app/controllers/projects/raw_controller.rb b/app/controllers/projects/raw_controller.rb
index 29f1e4bfd44..a9490c106d4 100644
--- a/app/controllers/projects/raw_controller.rb
+++ b/app/controllers/projects/raw_controller.rb
@@ -15,6 +15,8 @@ class Projects::RawController < Projects::ApplicationController
before_action :no_cache_headers, only: [:show]
before_action :redirect_to_external_storage, only: :show, if: :static_objects_external_storage_enabled?
+ feature_category :source_code_management
+
def show
@blob = @repository.blob_at(@commit.id, @path)
diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb
index db770d3e438..4d23c853334 100644
--- a/app/controllers/projects/refs_controller.rb
+++ b/app/controllers/projects/refs_controller.rb
@@ -11,6 +11,8 @@ class Projects::RefsController < Projects::ApplicationController
before_action :assign_ref_vars
before_action :authorize_download_code!
+ feature_category :source_code_management
+
def switch
respond_to do |format|
format.html do
diff --git a/app/controllers/projects/registry/application_controller.rb b/app/controllers/projects/registry/application_controller.rb
index 2f891d78c91..e7bf8c8e757 100644
--- a/app/controllers/projects/registry/application_controller.rb
+++ b/app/controllers/projects/registry/application_controller.rb
@@ -8,6 +8,8 @@ module Projects
before_action :verify_registry_enabled!
before_action :authorize_read_container_image!
+ feature_category :container_registry
+
private
def verify_registry_enabled!
diff --git a/app/controllers/projects/registry/repositories_controller.rb b/app/controllers/projects/registry/repositories_controller.rb
index 19d0cb9acdc..28a86ecc9f0 100644
--- a/app/controllers/projects/registry/repositories_controller.rb
+++ b/app/controllers/projects/registry/repositories_controller.rb
@@ -3,6 +3,8 @@
module Projects
module Registry
class RepositoriesController < ::Projects::Registry::ApplicationController
+ include PackagesHelper
+
before_action :authorize_update_container_image!, only: [:destroy]
before_action :ensure_root_container_repository!, only: [:index]
@@ -13,7 +15,7 @@ module Projects
@images = ContainerRepositoriesFinder.new(user: current_user, subject: project, params: params.slice(:name))
.execute
- track_event(:list_repositories)
+ track_package_event(:list_repositories, :container)
serializer = ContainerRepositoriesSerializer
.new(project: project, current_user: current_user)
@@ -31,7 +33,7 @@ module Projects
def destroy
image.delete_scheduled!
DeleteContainerRepositoryWorker.perform_async(current_user.id, image.id) # rubocop:disable CodeReuse/Worker
- track_event(:delete_repository)
+ track_package_event(:delete_repository, :container)
respond_to do |format|
format.json { head :no_content }
diff --git a/app/controllers/projects/registry/tags_controller.rb b/app/controllers/projects/registry/tags_controller.rb
index c42e3f6bdba..ebdb668207f 100644
--- a/app/controllers/projects/registry/tags_controller.rb
+++ b/app/controllers/projects/registry/tags_controller.rb
@@ -3,12 +3,15 @@
module Projects
module Registry
class TagsController < ::Projects::Registry::ApplicationController
+ include PackagesHelper
+
before_action :authorize_destroy_container_image!, only: [:destroy]
LIMIT = 15
def index
- track_event(:list_tags)
+ track_package_event(:list_tags, :tag)
+
respond_to do |format|
format.json do
render json: ContainerTagsSerializer
@@ -23,7 +26,7 @@ module Projects
result = Projects::ContainerRepository::DeleteTagsService
.new(image.project, current_user, tags: [params[:id]])
.execute(image)
- track_event(:delete_tag)
+ track_package_event(:delete_tag, :tag)
respond_to do |format|
format.json { head(result[:status] == :success ? :ok : bad_request) }
@@ -40,7 +43,7 @@ module Projects
result = Projects::ContainerRepository::DeleteTagsService
.new(image.project, current_user, tags: tag_names)
.execute(image)
- track_event(:delete_tag_bulk)
+ track_package_event(:delete_tag_bulk, :tag)
respond_to do |format|
format.json { head(result[:status] == :success ? :no_content : :bad_request) }
diff --git a/app/controllers/projects/releases/evidences_controller.rb b/app/controllers/projects/releases/evidences_controller.rb
index 34e450d903f..1e2dbf8047c 100644
--- a/app/controllers/projects/releases/evidences_controller.rb
+++ b/app/controllers/projects/releases/evidences_controller.rb
@@ -7,6 +7,8 @@ module Projects
before_action :release
before_action :authorize_read_release_evidence!
+ feature_category :release_evidence
+
def show
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/releases_controller.rb b/app/controllers/projects/releases_controller.rb
index bd24aae980c..4e8260d9e53 100644
--- a/app/controllers/projects/releases_controller.rb
+++ b/app/controllers/projects/releases_controller.rb
@@ -6,18 +6,16 @@ class Projects::ReleasesController < Projects::ApplicationController
before_action :release, only: %i[edit show update downloads]
before_action :authorize_read_release!
before_action do
- push_frontend_feature_flag(:release_issue_summary, project, default_enabled: true)
- push_frontend_feature_flag(:release_evidence_collection, project, default_enabled: true)
- push_frontend_feature_flag(:release_show_page, project, default_enabled: true)
- push_frontend_feature_flag(:release_asset_link_editing, project, default_enabled: true)
- push_frontend_feature_flag(:release_asset_link_type, project, default_enabled: true)
push_frontend_feature_flag(:graphql_release_data, project, default_enabled: true)
push_frontend_feature_flag(:graphql_milestone_stats, project, default_enabled: true)
- push_frontend_feature_flag(:graphql_releases_page, project, default_enabled: false)
+ push_frontend_feature_flag(:graphql_releases_page, project, default_enabled: true)
+ push_frontend_feature_flag(:graphql_individual_release_page, project, default_enabled: true)
end
before_action :authorize_update_release!, only: %i[edit update]
before_action :authorize_create_release!, only: :new
+ feature_category :release_orchestration
+
def index
respond_to do |format|
format.html do
@@ -27,10 +25,6 @@ class Projects::ReleasesController < Projects::ApplicationController
end
end
- def show
- return render_404 unless Feature.enabled?(:release_show_page, project, default_enabled: true)
- end
-
def new
unless Feature.enabled?(:new_release_page, project, default_enabled: true)
redirect_to(new_project_tag_path(@project))
diff --git a/app/controllers/projects/repositories_controller.rb b/app/controllers/projects/repositories_controller.rb
index 303326bbe23..ba3ab52e3af 100644
--- a/app/controllers/projects/repositories_controller.rb
+++ b/app/controllers/projects/repositories_controller.rb
@@ -18,6 +18,8 @@ class Projects::RepositoriesController < Projects::ApplicationController
before_action :authorize_admin_project!, only: :create
before_action :redirect_to_external_storage, only: :archive, if: :static_objects_external_storage_enabled?
+ feature_category :source_code_management
+
def create
@project.create_repository
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index cbeb32fd610..d225d5e104c 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -5,6 +5,8 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
layout 'project_settings'
+ feature_category :continuous_integration
+
def create
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index ca62f54813b..544074f9840 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -6,6 +6,8 @@ class Projects::RunnersController < Projects::ApplicationController
layout 'project_settings'
+ feature_category :continuous_integration
+
def index
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
end
@@ -50,6 +52,10 @@ class Projects::RunnersController < Projects::ApplicationController
end
def toggle_shared_runners
+ if Feature.enabled?(:disable_shared_runners_on_group, default_enabled: true) && !project.shared_runners_enabled && project.group && project.group.shared_runners_setting == 'disabled_and_unoverridable'
+ return redirect_to project_runners_path(@project), alert: _("Cannot enable shared runners because parent group does not allow it")
+ end
+
project.toggle!(:shared_runners_enabled)
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-runners-settings')
diff --git a/app/controllers/projects/serverless/functions_controller.rb b/app/controllers/projects/serverless/functions_controller.rb
index 0b55414d390..4168880001c 100644
--- a/app/controllers/projects/serverless/functions_controller.rb
+++ b/app/controllers/projects/serverless/functions_controller.rb
@@ -5,6 +5,8 @@ module Projects
class FunctionsController < Projects::ApplicationController
before_action :authorize_read_cluster!
+ feature_category :serverless
+
def index
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/service_desk_controller.rb b/app/controllers/projects/service_desk_controller.rb
index bcd190bbc2c..f7c0a54fb9e 100644
--- a/app/controllers/projects/service_desk_controller.rb
+++ b/app/controllers/projects/service_desk_controller.rb
@@ -3,6 +3,8 @@
class Projects::ServiceDeskController < Projects::ApplicationController
before_action :authorize_admin_project!
+ feature_category :service_desk
+
def show
json_response
end
diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb
index 9a69ef991dd..93ad549bc50 100644
--- a/app/controllers/projects/services_controller.rb
+++ b/app/controllers/projects/services_controller.rb
@@ -12,13 +12,15 @@ class Projects::ServicesController < Projects::ApplicationController
before_action :set_deprecation_notice_for_prometheus_service, only: [:edit, :update]
before_action :redirect_deprecated_prometheus_service, only: [:update]
before_action only: :edit do
- push_frontend_feature_flag(:jira_issues_integration, @project, { default_enabled: true })
+ push_frontend_feature_flag(:jira_issues_integration, @project, type: :licensed, default_enabled: true)
end
respond_to :html
layout "project_settings"
+ feature_category :integrations
+
def edit
@default_integration = Service.default_integration(service.type, project)
end
diff --git a/app/controllers/projects/settings/access_tokens_controller.rb b/app/controllers/projects/settings/access_tokens_controller.rb
index d6b4c4dd5dc..cbd6716fdf7 100644
--- a/app/controllers/projects/settings/access_tokens_controller.rb
+++ b/app/controllers/projects/settings/access_tokens_controller.rb
@@ -7,6 +7,8 @@ module Projects
before_action :check_feature_availability
+ feature_category :authentication_and_authorization
+
def index
@project_access_token = PersonalAccessToken.new
set_index_vars
diff --git a/app/controllers/projects/settings/ci_cd_controller.rb b/app/controllers/projects/settings/ci_cd_controller.rb
index d0d100fd88c..2963321f803 100644
--- a/app/controllers/projects/settings/ci_cd_controller.rb
+++ b/app/controllers/projects/settings/ci_cd_controller.rb
@@ -3,15 +3,25 @@
module Projects
module Settings
class CiCdController < Projects::ApplicationController
+ include RunnerSetupScripts
+
before_action :authorize_admin_pipeline!
before_action :define_variables
before_action do
push_frontend_feature_flag(:new_variables_ui, @project, default_enabled: true)
push_frontend_feature_flag(:ajax_new_deploy_token, @project)
- push_frontend_feature_flag(:ci_key_autocomplete, default_enabled: true)
end
+ helper_method :highlight_badge
+
+ feature_category :continuous_integration
+
def show
+ if Feature.enabled?(:ci_pipeline_triggers_settings_vue_ui, @project)
+ @triggers_json = ::Ci::TriggerSerializer.new.represent(
+ @project.triggers, current_user: current_user, project: @project
+ ).to_json
+ end
end
def update
@@ -48,8 +58,16 @@ module Projects
redirect_to namespace_project_settings_ci_cd_path
end
+ def runner_setup_scripts
+ private_runner_setup_scripts(project: @project)
+ end
+
private
+ def highlight_badge(name, content, language = nil)
+ Gitlab::Highlight.highlight(name, content, language: language)
+ end
+
def update_params
params.require(:project).permit(*permitted_project_params)
end
@@ -58,9 +76,9 @@ module Projects
[
:runners_token, :builds_enabled, :build_allow_git_fetch,
:build_timeout_human_readable, :build_coverage_regex, :public_builds,
- :auto_cancel_pending_pipelines, :forward_deployment_enabled, :ci_config_path,
+ :auto_cancel_pending_pipelines, :ci_config_path,
auto_devops_attributes: [:id, :domain, :enabled, :deploy_strategy],
- ci_cd_settings_attributes: [:default_git_depth]
+ ci_cd_settings_attributes: [:default_git_depth, :forward_deployment_enabled]
].tap do |list|
list << :max_artifacts_size if can?(current_user, :update_max_artifacts_size, project)
end
@@ -116,6 +134,7 @@ module Projects
def define_triggers_variables
@triggers = @project.triggers
.present(current_user: current_user)
+
@trigger = ::Ci::Trigger.new
.present(current_user: current_user)
end
diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb
index 96bf7f474d1..fba11ff1497 100644
--- a/app/controllers/projects/settings/integrations_controller.rb
+++ b/app/controllers/projects/settings/integrations_controller.rb
@@ -6,6 +6,8 @@ module Projects
before_action :authorize_admin_project!
layout "project_settings"
+ feature_category :integrations
+
def show
@services = @project.find_or_initialize_services
end
diff --git a/app/controllers/projects/settings/operations_controller.rb b/app/controllers/projects/settings/operations_controller.rb
index 781b850ddfe..c407b15e29f 100644
--- a/app/controllers/projects/settings/operations_controller.rb
+++ b/app/controllers/projects/settings/operations_controller.rb
@@ -9,6 +9,9 @@ module Projects
respond_to :json, only: [:reset_alerting_token, :reset_pagerduty_token]
helper_method :error_tracking_setting
+ helper_method :tracing_setting
+
+ feature_category :incident_management
def update
result = ::Projects::Operations::UpdateService.new(project, current_user, update_params).execute
@@ -17,15 +20,6 @@ module Projects
render_update_response(result)
end
- # overridden in EE
- def track_events(result)
- if result[:status] == :success
- ::Gitlab::Tracking::IncidentManagement.track_from_params(
- update_params[:incident_management_setting_attributes]
- )
- end
- end
-
def reset_alerting_token
result = ::Projects::Operations::UpdateService
.new(project, current_user, alerting_params)
@@ -55,6 +49,24 @@ module Projects
private
+ def track_events(result)
+ if result[:status] == :success
+ ::Gitlab::Tracking::IncidentManagement.track_from_params(
+ update_params[:incident_management_setting_attributes]
+ )
+ track_tracing_external_url
+ end
+ end
+
+ def track_tracing_external_url
+ external_url_previous_change = project&.tracing_setting&.external_url_previous_change
+
+ return unless external_url_previous_change
+ return unless external_url_previous_change[0].blank? && external_url_previous_change[1].present?
+
+ ::Gitlab::Tracking.event('project:operations:tracing', 'external_url_populated')
+ end
+
def alerting_params
{ alerting_setting_attributes: { regenerate_token: true } }
end
@@ -106,6 +118,10 @@ module Projects
project.build_error_tracking_setting
end
+ def tracing_setting
+ @tracing_setting ||= project.tracing_setting || project.build_tracing_setting
+ end
+
def update_params
params.require(:project).permit(permitted_project_params)
end
@@ -124,7 +140,8 @@ module Projects
project: [:slug, :name, :organization_slug, :organization_name]
],
- grafana_integration_attributes: [:token, :grafana_url, :enabled]
+ grafana_integration_attributes: [:token, :grafana_url, :enabled],
+ tracing_setting_attributes: [:external_url]
}
if Feature.enabled?(:settings_operations_prometheus_service, project)
diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb
index 35ca9336613..0994bebb1d0 100644
--- a/app/controllers/projects/settings/repository_controller.rb
+++ b/app/controllers/projects/settings/repository_controller.rb
@@ -7,8 +7,12 @@ module Projects
before_action :define_variables, only: [:create_deploy_token]
before_action do
push_frontend_feature_flag(:ajax_new_deploy_token, @project)
+ push_frontend_feature_flag(:deploy_keys_on_protected_branches, @project)
end
+ feature_category :source_code_management, [:show, :cleanup]
+ feature_category :continuous_delivery, [:create_deploy_token]
+
def show
render_show
end
@@ -125,6 +129,7 @@ module Projects
gon.push(protectable_tags_for_dropdown)
gon.push(protectable_branches_for_dropdown)
gon.push(access_levels_options)
+ gon.push(current_project_id: project.id) if project
end
end
end
diff --git a/app/controllers/projects/snippets/application_controller.rb b/app/controllers/projects/snippets/application_controller.rb
index 3f488b07e96..8ee12bf3795 100644
--- a/app/controllers/projects/snippets/application_controller.rb
+++ b/app/controllers/projects/snippets/application_controller.rb
@@ -4,6 +4,8 @@ class Projects::Snippets::ApplicationController < Projects::ApplicationControlle
include FindSnippet
include SnippetAuthorizations
+ feature_category :snippets
+
private
# This overrides the default snippet create authorization
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 632e8db9796..779e149bb9c 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -7,16 +7,11 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController
before_action :check_snippets_available!
- before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
+ before_action :snippet, only: [:show, :edit, :raw, :toggle_award_emoji, :mark_as_spam]
- before_action :authorize_create_snippet!, only: [:new, :create]
- before_action :authorize_read_snippet!, except: [:new, :create, :index]
- before_action :authorize_update_snippet!, only: [:edit, :update]
- before_action :authorize_admin_snippet!, only: [:destroy]
-
- before_action do
- push_frontend_feature_flag(:snippet_multiple_files, current_user)
- end
+ before_action :authorize_create_snippet!, only: :new
+ before_action :authorize_read_snippet!, except: [:new, :index]
+ before_action :authorize_update_snippet!, only: :edit
def index
@snippet_counts = ::Snippets::CountService
@@ -37,14 +32,6 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController
@snippet = @noteable = @project.snippets.build
end
- def create
- create_params = snippet_params.merge(spammable_params)
- service_response = ::Snippets::CreateService.new(project, current_user, create_params).execute
- @snippet = service_response.payload[:snippet]
-
- handle_repository_error(:new)
- end
-
protected
alias_method :awardable, :snippet
@@ -53,8 +40,4 @@ class Projects::SnippetsController < Projects::Snippets::ApplicationController
def spammable_path
project_snippet_path(@project, @snippet)
end
-
- def snippet_params
- params.require(:project_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description)
- end
end
diff --git a/app/controllers/projects/starrers_controller.rb b/app/controllers/projects/starrers_controller.rb
index d9654f4f72a..91f49fc4d66 100644
--- a/app/controllers/projects/starrers_controller.rb
+++ b/app/controllers/projects/starrers_controller.rb
@@ -3,6 +3,8 @@
class Projects::StarrersController < Projects::ApplicationController
include SortingHelper
+ feature_category :projects
+
def index
@starrers = UsersStarProjectsFinder.new(@project, params, current_user: @current_user).execute
@sort = params[:sort].presence || sort_value_name
diff --git a/app/controllers/projects/static_site_editor_controller.rb b/app/controllers/projects/static_site_editor_controller.rb
index e97a8db0b79..7e2e32a843f 100644
--- a/app/controllers/projects/static_site_editor_controller.rb
+++ b/app/controllers/projects/static_site_editor_controller.rb
@@ -13,6 +13,8 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
push_frontend_feature_flag(:sse_image_uploads)
end
+ feature_category :static_site_editor
+
def show
service_response = ::StaticSiteEditor::ConfigService.new(
container: project,
@@ -25,14 +27,32 @@ class Projects::StaticSiteEditorController < Projects::ApplicationController
).execute
if service_response.success?
- @data = service_response.payload
+ Gitlab::UsageDataCounters::StaticSiteEditorCounter.increment_views_count
+
+ @data = serialize_necessary_payload_values_to_json(service_response.payload)
else
- respond_422
+ # TODO: For now, if the service returns any error, the user is redirected
+ # to the root project page with the error message displayed as an alert.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/213285#note_414808004
+ # for discussion of plans to handle this via a page owned by the Static Site Editor.
+ flash[:alert] = service_response.message
+ redirect_to project_path(project)
end
end
private
+ def serialize_necessary_payload_values_to_json(payload)
+ # This will convert booleans, Array-like and Hash-like objects to JSON
+ payload.transform_values do |value|
+ if value.is_a?(String) || value.is_a?(Integer)
+ value
+ else
+ value.to_json
+ end
+ end
+ end
+
def assign_ref_and_path
@ref, @path = extract_ref(params.fetch(:id))
diff --git a/app/controllers/projects/tags/releases_controller.rb b/app/controllers/projects/tags/releases_controller.rb
index c1f4cbce054..8e5539f546b 100644
--- a/app/controllers/projects/tags/releases_controller.rb
+++ b/app/controllers/projects/tags/releases_controller.rb
@@ -8,6 +8,8 @@ class Projects::Tags::ReleasesController < Projects::ApplicationController
before_action :tag
before_action :release
+ feature_category :release_evidence
+
def edit
end
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index 475ca8894e4..1d783241196 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -10,6 +10,9 @@ class Projects::TagsController < Projects::ApplicationController
before_action :authorize_download_code!
before_action :authorize_admin_tag!, only: [:new, :create, :destroy]
+ feature_category :source_code_management, [:index, :show, :new, :destroy]
+ feature_category :release_evidence, [:create]
+
# rubocop: disable CodeReuse/ActiveRecord
def index
params[:sort] = params[:sort].presence || sort_value_recently_updated
@@ -76,25 +79,10 @@ class Projects::TagsController < Projects::ApplicationController
def destroy
result = ::Tags::DestroyService.new(project, current_user).execute(params[:id])
- respond_to do |format|
- if result[:status] == :success
- format.html do
- redirect_to project_tags_path(@project), status: :see_other
- end
-
- format.js
- else
- @error = result[:message]
-
- format.html do
- redirect_to project_tags_path(@project),
- alert: @error, status: :see_other
- end
-
- format.js do
- render status: :ok
- end
- end
+ if result[:status] == :success
+ render json: result
+ else
+ render json: { message: result[:message] }, status: result[:return_code]
end
end
diff --git a/app/controllers/projects/templates_controller.rb b/app/controllers/projects/templates_controller.rb
index 95739f96d39..7ab23e39cf0 100644
--- a/app/controllers/projects/templates_controller.rb
+++ b/app/controllers/projects/templates_controller.rb
@@ -5,6 +5,8 @@ class Projects::TemplatesController < Projects::ApplicationController
before_action :authorize_can_read_issuable!
before_action :get_template_class
+ feature_category :templates
+
def show
template = @template_type.find(params[:key], project)
diff --git a/app/controllers/projects/todos_controller.rb b/app/controllers/projects/todos_controller.rb
index 33205b93317..6ba89ab34f8 100644
--- a/app/controllers/projects/todos_controller.rb
+++ b/app/controllers/projects/todos_controller.rb
@@ -6,6 +6,8 @@ class Projects::TodosController < Projects::ApplicationController
before_action :authenticate_user!, only: [:create]
+ feature_category :issue_tracking
+
private
def issuable
diff --git a/app/controllers/projects/tracings_controller.rb b/app/controllers/projects/tracings_controller.rb
new file mode 100644
index 00000000000..2bc0c590e8d
--- /dev/null
+++ b/app/controllers/projects/tracings_controller.rb
@@ -0,0 +1,28 @@
+# frozen_string_literal: true
+
+module Projects
+ class TracingsController < Projects::ApplicationController
+ content_security_policy do |p|
+ next if p.directives.blank?
+
+ global_frame_src = p.frame_src
+
+ p.frame_src -> { frame_src_csp_policy(global_frame_src) }
+ end
+
+ before_action :authorize_update_environment!
+
+ feature_category :tracing
+
+ def show
+ end
+
+ private
+
+ def frame_src_csp_policy(global_frame_src)
+ external_url = @project&.tracing_setting&.external_url
+
+ external_url.presence || global_frame_src
+ end
+ end
+end
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index 638e1a05c18..b5cfc3990b2 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -15,6 +15,8 @@ class Projects::TreeController < Projects::ApplicationController
before_action :authorize_download_code!
before_action :authorize_edit_tree!, only: [:create_dir]
+ feature_category :source_code_management
+
def show
return render_404 unless @commit
diff --git a/app/controllers/projects/triggers_controller.rb b/app/controllers/projects/triggers_controller.rb
index 7159d0243a3..eec35fcec8d 100644
--- a/app/controllers/projects/triggers_controller.rb
+++ b/app/controllers/projects/triggers_controller.rb
@@ -8,6 +8,8 @@ class Projects::TriggersController < Projects::ApplicationController
layout 'project_settings'
+ feature_category :continuous_integration
+
def index
redirect_to project_settings_ci_cd_path(@project, anchor: 'js-pipeline-triggers')
end
diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb
index 72251988b5e..c15768e7bbb 100644
--- a/app/controllers/projects/uploads_controller.rb
+++ b/app/controllers/projects/uploads_controller.rb
@@ -11,6 +11,8 @@ class Projects::UploadsController < Projects::ApplicationController
before_action :authorize_upload_file!, only: [:create, :authorize]
before_action :verify_workhorse_api!, only: [:authorize]
+ feature_category :not_owned
+
private
def upload_model_class
diff --git a/app/controllers/projects/usage_ping_controller.rb b/app/controllers/projects/usage_ping_controller.rb
index 0e2c8f879b1..9b4ddb326c1 100644
--- a/app/controllers/projects/usage_ping_controller.rb
+++ b/app/controllers/projects/usage_ping_controller.rb
@@ -3,6 +3,8 @@
class Projects::UsagePingController < Projects::ApplicationController
before_action :authenticate_user!
+ feature_category :collection
+
def web_ide_clientside_preview
return render_404 unless Gitlab::CurrentSettings.web_ide_clientside_preview_enabled?
diff --git a/app/controllers/projects/variables_controller.rb b/app/controllers/projects/variables_controller.rb
index 0fd047f90cf..d8efc1b7b54 100644
--- a/app/controllers/projects/variables_controller.rb
+++ b/app/controllers/projects/variables_controller.rb
@@ -3,6 +3,8 @@
class Projects::VariablesController < Projects::ApplicationController
before_action :authorize_admin_build!
+ feature_category :continuous_integration
+
def show
respond_to do |format|
format.json do
diff --git a/app/controllers/projects/web_ide_schemas_controller.rb b/app/controllers/projects/web_ide_schemas_controller.rb
index 3d16a6fafd4..84a191815f4 100644
--- a/app/controllers/projects/web_ide_schemas_controller.rb
+++ b/app/controllers/projects/web_ide_schemas_controller.rb
@@ -3,6 +3,8 @@
class Projects::WebIdeSchemasController < Projects::ApplicationController
before_action :authenticate_user!
+ feature_category :web_ide
+
def show
return respond_422 unless branch_sha
diff --git a/app/controllers/projects/web_ide_terminals_controller.rb b/app/controllers/projects/web_ide_terminals_controller.rb
index 76bcaa9e80c..1d179765ad9 100644
--- a/app/controllers/projects/web_ide_terminals_controller.rb
+++ b/app/controllers/projects/web_ide_terminals_controller.rb
@@ -8,6 +8,8 @@ class Projects::WebIdeTerminalsController < Projects::ApplicationController
before_action :authorize_read_web_ide_terminal!, except: [:check_config, :create]
before_action :authorize_update_web_ide_terminal!, only: [:cancel, :retry]
+ feature_category :web_ide
+
def check_config
return respond_422 unless branch_sha
diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb
index d0aa733cadb..8f794512486 100644
--- a/app/controllers/projects/wikis_controller.rb
+++ b/app/controllers/projects/wikis_controller.rb
@@ -5,6 +5,8 @@ class Projects::WikisController < Projects::ApplicationController
alias_method :container, :project
+ feature_category :wiki
+
def git_access
end
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 848625ff6b5..09e7563cefd 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -37,7 +37,7 @@ class ProjectsController < Projects::ApplicationController
# Experiments
before_action only: [:new, :create] do
frontend_experimentation_tracking_data(:new_create_project_ui, 'click_tab')
- push_frontend_feature_flag(:new_create_project_ui) if experiment_enabled?(:new_create_project_ui)
+ push_frontend_experiment(:new_create_project_ui)
end
before_action only: [:edit] do
@@ -47,6 +47,17 @@ class ProjectsController < Projects::ApplicationController
layout :determine_layout
+ feature_category :projects, [
+ :index, :show, :new, :create, :edit, :update, :transfer,
+ :destroy, :resolve, :archive, :unarchive, :toggle_star
+ ]
+
+ feature_category :source_code_management, [:remove_fork, :housekeeping, :refs]
+ feature_category :issue_tracking, [:preview_markdown, :new_issuable_address]
+ feature_category :importers, [:export, :remove_export, :generate_new_export, :download_export]
+ feature_category :audit_events, [:activity]
+ feature_category :code_review, [:unfoldered_environment_names]
+
def index
redirect_to(current_user ? root_path : explore_root_path)
end
@@ -305,6 +316,16 @@ class ProjectsController < Projects::ApplicationController
end
end
+ def unfoldered_environment_names
+ return render_404 unless Feature.enabled?(:deployment_filters)
+
+ respond_to do |format|
+ format.json do
+ render json: EnvironmentNamesFinder.new(@project, current_user).execute
+ end
+ end
+ end
+
private
# Render project landing depending of which features are available
diff --git a/app/controllers/registrations/experience_levels_controller.rb b/app/controllers/registrations/experience_levels_controller.rb
index 38cffff91eb..23126983eb5 100644
--- a/app/controllers/registrations/experience_levels_controller.rb
+++ b/app/controllers/registrations/experience_levels_controller.rb
@@ -7,12 +7,20 @@ module Registrations
before_action :check_experiment_enabled
before_action :ensure_namespace_path_param
+ feature_category :navigation
+
def update
current_user.experience_level = params[:experience_level]
if current_user.save
hide_advanced_issues
- redirect_to group_path(params[:namespace_path])
+ record_experiment_user(:default_to_issues_board)
+
+ if experiment_enabled?(:default_to_issues_board) && learn_gitlab.available?
+ redirect_to namespace_project_board_path(params[:namespace_path], learn_gitlab.project, learn_gitlab.board)
+ else
+ redirect_to group_path(params[:namespace_path])
+ end
else
render :show
end
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index 204520a3e71..b3dc0e986f4 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -6,19 +6,19 @@ class RegistrationsController < Devise::RegistrationsController
include RecaptchaExperimentHelper
include InvisibleCaptchaOnSignup
+ BLOCKED_PENDING_APPROVAL_STATE = 'blocked_pending_approval'.freeze
+
layout :choose_layout
skip_before_action :required_signup_info, :check_two_factor_requirement, only: [:welcome, :update_registration]
prepend_before_action :check_captcha, only: :create
before_action :whitelist_query_limiting, :ensure_destroy_prerequisites_met, only: [:destroy]
- before_action :ensure_terms_accepted,
- if: -> { action_name == 'create' && Gitlab::CurrentSettings.current_application_settings.enforce_terms? }
before_action :load_recaptcha, only: :new
+ feature_category :authentication_and_authorization
+
def new
if experiment_enabled?(:signup_flow)
- track_experiment_event(:terms_opt_in, 'start')
-
@resource = build_resource
else
redirect_to new_user_session_path(anchor: 'register-pane')
@@ -26,18 +26,18 @@ class RegistrationsController < Devise::RegistrationsController
end
def create
+ set_user_state
accept_pending_invitations
super do |new_user|
persist_accepted_terms_if_required(new_user)
set_role_required(new_user)
- track_terms_experiment(new_user)
yield new_user if block_given?
end
- # Devise sets a flash message on `create` for a successful signup,
- # which we don't want to show.
- flash[:notice] = nil
+ # Devise sets a flash message on both successful & failed signups,
+ # but we only want to show a message if the resource is blocked by a pending approval.
+ flash[:notice] = nil unless resource.blocked_pending_approval?
rescue Gitlab::Access::AccessDeniedError
redirect_to(new_user_session_path)
end
@@ -58,6 +58,8 @@ class RegistrationsController < Devise::RegistrationsController
end
def update_registration
+ return redirect_to new_user_registration_path unless current_user
+
user_params = params.require(:user).permit(:role, :setup_for_company)
result = ::Users::SignupService.new(current_user, user_params).execute
@@ -81,10 +83,8 @@ class RegistrationsController < Devise::RegistrationsController
return unless new_user.persisted?
return unless Gitlab::CurrentSettings.current_application_settings.enforce_terms?
- if terms_accepted?
- terms = ApplicationSetting::Term.latest
- Users::RespondToTermsService.new(new_user, terms).execute(accepted: true)
- end
+ terms = ApplicationSetting::Term.latest
+ Users::RespondToTermsService.new(new_user, terms).execute(accepted: true)
end
def set_role_required(new_user)
@@ -119,6 +119,8 @@ class RegistrationsController < Devise::RegistrationsController
def after_inactive_sign_up_path_for(resource)
Gitlab::AppLogger.info(user_created_message)
+ return new_user_session_path(anchor: 'login-pane') if resource.blocked_pending_approval?
+
Feature.enabled?(:soft_email_confirmation) ? dashboard_projects_path : users_almost_there_path
end
@@ -178,18 +180,6 @@ class RegistrationsController < Devise::RegistrationsController
Gitlab::QueryLimiting.whitelist('https://gitlab.com/gitlab-org/gitlab-foss/issues/42380')
end
- def ensure_terms_accepted
- return if terms_accepted?
-
- redirect_to new_user_session_path, alert: _('You must accept our Terms of Service and privacy policy in order to register an account')
- end
-
- def terms_accepted?
- return true if experiment_enabled?(:terms_opt_in)
-
- Gitlab::Utils.to_boolean(params[:terms_opt_in])
- end
-
def path_for_signed_in_user(user)
if requires_confirmation?(user)
users_almost_there_path
@@ -206,13 +196,6 @@ class RegistrationsController < Devise::RegistrationsController
true
end
- def track_terms_experiment(new_user)
- return unless new_user.persisted?
-
- track_experiment_event(:terms_opt_in, 'end')
- record_experiment_user(:terms_opt_in)
- end
-
def load_recaptcha
Gitlab::Recaptcha.load_configurations!
end
@@ -233,6 +216,13 @@ class RegistrationsController < Devise::RegistrationsController
!helpers.in_oauth_flow? &&
!helpers.in_trial_flow?
end
+
+ def set_user_state
+ return unless Feature.enabled?(:admin_approval_for_new_user_signups, default_enabled: true)
+ return unless Gitlab::CurrentSettings.require_admin_approval_after_user_signup
+
+ resource.state = BLOCKED_PENDING_APPROVAL_STATE
+ end
end
RegistrationsController.prepend_if_ee('EE::RegistrationsController')
diff --git a/app/controllers/repositories/git_http_client_controller.rb b/app/controllers/repositories/git_http_client_controller.rb
index e02955433b2..de452aa69b7 100644
--- a/app/controllers/repositories/git_http_client_controller.rb
+++ b/app/controllers/repositories/git_http_client_controller.rb
@@ -20,6 +20,8 @@ module Repositories
prepend_before_action :authenticate_user, :parse_repo_path
+ feature_category :source_code_management
+
private
def download_request?
diff --git a/app/controllers/runner_setup_controller.rb b/app/controllers/runner_setup_controller.rb
new file mode 100644
index 00000000000..e0e9c5b7c23
--- /dev/null
+++ b/app/controllers/runner_setup_controller.rb
@@ -0,0 +1,9 @@
+# frozen_string_literal: true
+
+class RunnerSetupController < ApplicationController
+ feature_category :continuous_integration
+
+ def platforms
+ render json: Gitlab::Ci::RunnerInstructions::OS.merge(Gitlab::Ci::RunnerInstructions::OTHER_ENVIRONMENTS)
+ end
+end
diff --git a/app/controllers/search_controller.rb b/app/controllers/search_controller.rb
index dedaf0c903a..0380bc1c548 100644
--- a/app/controllers/search_controller.rb
+++ b/app/controllers/search_controller.rb
@@ -8,7 +8,8 @@ class SearchController < ApplicationController
SCOPE_PRELOAD_METHOD = {
projects: :with_web_entity_associations,
- issues: :with_web_entity_associations
+ issues: :with_web_entity_associations,
+ epics: :with_web_entity_associations
}.freeze
track_redis_hll_event :show, name: 'i_search_total', feature: :search_track_unique_users, feature_default_enabled: true
@@ -24,6 +25,8 @@ class SearchController < ApplicationController
layout 'search'
+ feature_category :global_search
+
def show
@project = search_service.project
@group = search_service.group
@@ -38,6 +41,7 @@ class SearchController < ApplicationController
@show_snippets = search_service.show_snippets?
@search_results = search_service.search_results
@search_objects = search_service.search_objects(preload_method)
+ @search_highlight = search_service.search_highlight
render_commits if @scope == 'commits'
eager_load_user_status if @scope == 'users'
@@ -96,8 +100,6 @@ class SearchController < ApplicationController
end
def eager_load_user_status
- return if Feature.disabled?(:users_search, default_enabled: true)
-
@search_objects = @search_objects.eager_load(:status) # rubocop:disable CodeReuse/ActiveRecord
end
diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb
index 20134de81a0..db07b212d00 100644
--- a/app/controllers/sent_notifications_controller.rb
+++ b/app/controllers/sent_notifications_controller.rb
@@ -3,6 +3,8 @@
class SentNotificationsController < ApplicationController
skip_before_action :authenticate_user!
+ feature_category :users
+
def unsubscribe
@sent_notification = SentNotification.for(params[:id])
diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 318553b5e0a..61120c5b7d1 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -49,6 +49,8 @@ class SessionsController < Devise::SessionsController
# token mismatch.
protect_from_forgery with: :exception, prepend: true, except: :destroy
+ feature_category :authentication_and_authorization
+
CAPTCHA_HEADER = 'X-GitLab-Show-Login-Captcha'
MAX_FAILED_LOGIN_ATTEMPTS = 5
@@ -262,8 +264,11 @@ class SessionsController < Devise::SessionsController
end
def valid_otp_attempt?(user)
- user.validate_and_consume_otp!(user_params[:otp_attempt]) ||
- user.invalidate_otp_backup_code!(user_params[:otp_attempt])
+ otp_validation_result =
+ ::Users::ValidateOtpService.new(user).execute(user_params[:otp_attempt])
+ return true if otp_validation_result[:status] == :success
+
+ user.invalidate_otp_backup_code!(user_params[:otp_attempt])
end
def log_audit_event(user, resource, options = {})
diff --git a/app/controllers/snippets/application_controller.rb b/app/controllers/snippets/application_controller.rb
index a533e46a75d..f259f4569ef 100644
--- a/app/controllers/snippets/application_controller.rb
+++ b/app/controllers/snippets/application_controller.rb
@@ -4,6 +4,8 @@ class Snippets::ApplicationController < ApplicationController
include FindSnippet
include SnippetAuthorizations
+ feature_category :snippets
+
private
def authorize_read_snippet!
diff --git a/app/controllers/snippets/notes_controller.rb b/app/controllers/snippets/notes_controller.rb
index a7e8ef0798b..8532257cb8d 100644
--- a/app/controllers/snippets/notes_controller.rb
+++ b/app/controllers/snippets/notes_controller.rb
@@ -8,6 +8,8 @@ class Snippets::NotesController < ApplicationController
before_action :authorize_read_snippet!, only: [:show, :index]
before_action :authorize_create_note!, only: [:create]
+ feature_category :snippets
+
private
def note
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 486c7f1d028..913b1e3bb6e 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -6,21 +6,16 @@ class SnippetsController < Snippets::ApplicationController
include ToggleAwardEmoji
include SpammableActions
- before_action :snippet, only: [:show, :edit, :destroy, :update, :raw, :toggle_award_emoji, :mark_as_spam]
+ before_action :snippet, only: [:show, :edit, :raw, :toggle_award_emoji, :mark_as_spam]
- before_action :authorize_create_snippet!, only: [:new, :create]
+ before_action :authorize_create_snippet!, only: :new
before_action :authorize_read_snippet!, only: [:show, :raw]
- before_action :authorize_update_snippet!, only: [:edit, :update]
- before_action :authorize_admin_snippet!, only: [:destroy]
+ before_action :authorize_update_snippet!, only: :edit
skip_before_action :authenticate_user!, only: [:index, :show, :raw]
layout 'snippets'
- before_action do
- push_frontend_feature_flag(:snippet_multiple_files, current_user)
- end
-
def index
if params[:username].present?
@user = UserFinder.new(params[:username]).find_by_username!
@@ -44,18 +39,6 @@ class SnippetsController < Snippets::ApplicationController
@snippet = PersonalSnippet.new
end
- def create
- create_params = snippet_params.merge(files: params.delete(:files))
- service_response = Snippets::CreateService.new(nil, current_user, create_params).execute
- @snippet = service_response.payload[:snippet]
-
- if service_response.error? && @snippet.errors[:repository].present?
- handle_repository_error(:new)
- else
- recaptcha_check_with_fallback { render :new }
- end
- end
-
protected
alias_method :awardable, :snippet
@@ -64,8 +47,4 @@ class SnippetsController < Snippets::ApplicationController
def spammable_path
snippet_path(@snippet)
end
-
- def snippet_params
- params.require(:personal_snippet).permit(:title, :content, :file_name, :private, :visibility_level, :description).merge(spammable_params)
- end
end
diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb
index 9510734bc9b..6692c285335 100644
--- a/app/controllers/uploads_controller.rb
+++ b/app/controllers/uploads_controller.rb
@@ -25,6 +25,8 @@ class UploadsController < ApplicationController
before_action :authorize_create_access!, only: [:create, :authorize]
before_action :verify_workhorse_api!, only: [:authorize]
+ feature_category :not_owned
+
def uploader_class
PersonalFileUploader
end
diff --git a/app/controllers/user_callouts_controller.rb b/app/controllers/user_callouts_controller.rb
index 06f422b9d90..cfec9d6d905 100644
--- a/app/controllers/user_callouts_controller.rb
+++ b/app/controllers/user_callouts_controller.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class UserCalloutsController < ApplicationController
+ feature_category :navigation
+
def create
callout = ensure_callout
diff --git a/app/controllers/users/terms_controller.rb b/app/controllers/users/terms_controller.rb
index 231e449f733..be670658048 100644
--- a/app/controllers/users/terms_controller.rb
+++ b/app/controllers/users/terms_controller.rb
@@ -14,6 +14,8 @@ module Users
layout 'terms'
+ feature_category :users
+
def index
@redirect = redirect_path
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 75a861423ed..672f36dedc0 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -21,6 +21,8 @@ class UsersController < ApplicationController
before_action :authorize_read_user_profile!,
only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :starred_projects, :snippets]
+ feature_category :users
+
def show
respond_to do |format|
format.html
@@ -106,7 +108,7 @@ class UsersController < ApplicationController
def calendar_activities
@calendar_date = Date.parse(params[:date]) rescue Date.today
- @events = contributions_calendar.events_by_date(@calendar_date)
+ @events = contributions_calendar.events_by_date(@calendar_date).map(&:present)
render 'calendar_activities', layout: false
end
diff --git a/app/controllers/whats_new_controller.rb b/app/controllers/whats_new_controller.rb
new file mode 100644
index 00000000000..7156faa4e49
--- /dev/null
+++ b/app/controllers/whats_new_controller.rb
@@ -0,0 +1,25 @@
+# frozen_string_literal: true
+
+class WhatsNewController < ApplicationController
+ include Gitlab::WhatsNew
+
+ skip_before_action :authenticate_user!
+
+ before_action :check_feature_flag
+
+ feature_category :navigation
+
+ def index
+ respond_to do |format|
+ format.js do
+ render json: whats_new_most_recent_release_items
+ end
+ end
+ end
+
+ private
+
+ def check_feature_flag
+ render_404 unless Feature.enabled?(:whats_new_drawer, current_user)
+ end
+end