diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
commit | 6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch) | |
tree | dc4d20fe6064752c0bd323187252c77e0a89144b /app/controllers/projects | |
parent | 9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff) | |
download | gitlab-ce-6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde.tar.gz |
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'app/controllers/projects')
29 files changed, 406 insertions, 142 deletions
diff --git a/app/controllers/projects/blame_controller.rb b/app/controllers/projects/blame_controller.rb index 5bfda526fb0..2a20c67a23d 100644 --- a/app/controllers/projects/blame_controller.rb +++ b/app/controllers/projects/blame_controller.rb @@ -23,11 +23,10 @@ class Projects::BlameController < Projects::ApplicationController environment_params[:find_latest] = true @environment = ::Environments::EnvironmentsByDeploymentsFinder.new(@project, current_user, environment_params).execute.last - blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page)) + blame_service = Projects::BlameService.new(@blob, @commit, params.permit(:page, :no_pagination)) @blame = Gitlab::View::Presenter::Factory.new(blame_service.blame, project: @project, path: @path, page: blame_service.page).fabricate! - - render locals: { blame_pagination: blame_service.pagination } + @blame_pagination = blame_service.pagination end end diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index 6160dafb177..63c1378ad11 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -5,13 +5,17 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController include ActionView::Helpers::TextHelper include CycleAnalyticsParams include GracefulTimeoutHandling - include RedisTracking + include ProductAnalyticsTracking extend ::Gitlab::Utils::Override before_action :authorize_read_cycle_analytics! before_action :load_value_stream, only: :show - track_redis_hll_event :show, name: 'p_analytics_valuestream' + track_custom_event :show, + name: 'p_analytics_valuestream', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] feature_category :planning_analytics urgency :low @@ -54,4 +58,12 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController permissions: @cycle_analytics.permissions(user: current_user) } end + + def tracking_namespace_source + project.namespace + end + + def tracking_project_source + project + end end diff --git a/app/controllers/projects/environments_controller.rb b/app/controllers/projects/environments_controller.rb index 7ef9fd9daed..4f037cc843e 100644 --- a/app/controllers/projects/environments_controller.rb +++ b/app/controllers/projects/environments_controller.rb @@ -5,7 +5,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController # into app/controllers/projects/metrics_dashboard_controller.rb # See https://gitlab.com/gitlab-org/gitlab/-/issues/226002 for more details. + MIN_SEARCH_LENGTH = 3 + include MetricsDashboard + include ProductAnalyticsTracking layout 'project' @@ -26,6 +29,18 @@ 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] + track_event :index, + :folder, + :show, + :new, + :edit, + :create, + :update, + :stop, + :cancel_auto_stop, + :terminal, + name: 'users_visiting_environments_pages' + feature_category :continuous_delivery urgency :low @@ -35,12 +50,10 @@ class Projects::EnvironmentsController < Projects::ApplicationController respond_to do |format| format.html format.json do - @environments = project.environments - .with_state(params[:scope] || :available) + @environments = search_environments.with_state(params[:scope] || :available) + environments_count_by_state = search_environments.count_by_state Gitlab::PollingInterval.set_header(response, interval: 3_000) - environments_count_by_state = project.environments.count_by_state - render json: { environments: serialize_environments(request, response, params[:nested]), review_app: serialize_review_app, @@ -59,7 +72,8 @@ class Projects::EnvironmentsController < Projects::ApplicationController respond_to do |format| format.html format.json do - folder_environments = project.environments.where(environment_type: params[:id]) + folder_environments = search_environments(type: params[:id]) + @environments = folder_environments.with_state(params[:scope] || :available) .order(:name) @@ -236,6 +250,16 @@ class Projects::EnvironmentsController < Projects::ApplicationController @environment ||= project.environments.find(params[:id]) end + def search_environments(type: nil) + search = params[:search] if params[:search] && params[:search].length >= MIN_SEARCH_LENGTH + + @search_environments ||= + Environments::EnvironmentsFinder.new(project, + current_user, + type: type, + search: search).execute + end + def metrics_params params.require([:start, :end]) end diff --git a/app/controllers/projects/google_cloud/base_controller.rb b/app/controllers/projects/google_cloud/base_controller.rb index d1eb86c5e49..dfb73821b0f 100644 --- a/app/controllers/projects/google_cloud/base_controller.rb +++ b/app/controllers/projects/google_cloud/base_controller.rb @@ -12,7 +12,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController def admin_project_google_cloud! unless can?(current_user, :admin_project_google_cloud, project) - track_event('admin_project_google_cloud!', 'error_access_denied', 'invalid_user') + track_event(:error_invalid_user) access_denied! end end @@ -20,11 +20,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController def google_oauth2_enabled! config = Gitlab::Auth::OAuth::Provider.config_for('google_oauth2') if config.app_id.blank? || config.app_secret.blank? - track_event( - 'google_oauth2_enabled!', - 'error_access_denied', - { reason: 'google_oauth2_not_configured', config: config } - ) + track_event(:error_google_oauth2_not_enabled) access_denied! 'This GitLab instance not configured for Google Oauth2.' end end @@ -35,7 +31,7 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController enabled_for_project = Feature.enabled?(:incubation_5mp_google_cloud, project) feature_is_enabled = enabled_for_user || enabled_for_group || enabled_for_project unless feature_is_enabled - track_event('feature_flag_enabled!', 'error_access_denied', 'feature_flag_not_enabled') + track_event(:error_feature_flag_not_enabled) access_denied! end end @@ -69,16 +65,14 @@ class Projects::GoogleCloud::BaseController < Projects::ApplicationController session[GoogleApi::CloudPlatform::Client.session_key_for_expires_at] end - def track_event(action, label, property) - options = { label: label, project: project, user: current_user } - - if property.is_a?(String) - options[:property] = property - else - options[:extra] = property - end - - Gitlab::Tracking.event('Projects::GoogleCloud', action, **options) + def track_event(action, label = nil) + Gitlab::Tracking.event( + self.class.name, + action.to_s, + label: label, + project: project, + user: current_user + ) end def gcp_projects diff --git a/app/controllers/projects/google_cloud/configuration_controller.rb b/app/controllers/projects/google_cloud/configuration_controller.rb index 8d252c35031..06a6674d578 100644 --- a/app/controllers/projects/google_cloud/configuration_controller.rb +++ b/app/controllers/projects/google_cloud/configuration_controller.rb @@ -16,7 +16,7 @@ module Projects revokeOauthUrl: revoke_oauth_url } @js_data = js_data.to_json - track_event('configuration#index', 'success', js_data) + track_event(:render_page) end private diff --git a/app/controllers/projects/google_cloud/databases_controller.rb b/app/controllers/projects/google_cloud/databases_controller.rb index 7b1cf6e5ce1..8f7554f248b 100644 --- a/app/controllers/projects/google_cloud/databases_controller.rb +++ b/app/controllers/projects/google_cloud/databases_controller.rb @@ -3,14 +3,139 @@ module Projects module GoogleCloud class DatabasesController < Projects::GoogleCloud::BaseController + before_action :validate_gcp_token! + before_action :validate_product, only: :new + def index js_data = { configurationUrl: project_google_cloud_configuration_path(project), deploymentsUrl: project_google_cloud_deployments_path(project), - databasesUrl: project_google_cloud_databases_path(project) + databasesUrl: project_google_cloud_databases_path(project), + cloudsqlPostgresUrl: new_project_google_cloud_database_path(project, :postgres), + cloudsqlMysqlUrl: new_project_google_cloud_database_path(project, :mysql), + cloudsqlSqlserverUrl: new_project_google_cloud_database_path(project, :sqlserver), + cloudsqlInstances: ::GoogleCloud::GetCloudsqlInstancesService.new(project).execute, + emptyIllustrationUrl: ActionController::Base.helpers.image_path('illustrations/pipelines_empty.svg') } @js_data = js_data.to_json - track_event('databases#index', 'success', js_data) + + track_event(:render_page) + end + + def new + product = permitted_params[:product].to_sym + + @title = title(product) + + @js_data = { + gcpProjects: gcp_projects, + refs: refs, + cancelPath: project_google_cloud_databases_path(project), + formTitle: form_title(product), + formDescription: description(product), + databaseVersions: Projects::GoogleCloud::CloudsqlHelper::VERSIONS[product], + tiers: Projects::GoogleCloud::CloudsqlHelper::TIERS + }.to_json + + track_event(:render_form) + render template: 'projects/google_cloud/databases/cloudsql_form', formats: :html + end + + def create + enable_response = ::GoogleCloud::EnableCloudsqlService + .new(project, current_user, enable_service_params) + .execute + + if enable_response[:status] == :error + track_event(:error_enable_cloudsql_services) + flash[:error] = error_message(enable_response[:message]) + else + permitted_params = params.permit(:gcp_project, :ref, :database_version, :tier) + create_response = ::GoogleCloud::CreateCloudsqlInstanceService + .new(project, current_user, create_service_params(permitted_params)) + .execute + + if create_response[:status] == :error + track_event(:error_create_cloudsql_instance) + flash[:warning] = error_message(create_response[:message]) + else + track_event(:create_cloudsql_instance, permitted_params.to_s) + flash[:notice] = success_message + end + end + + redirect_to project_google_cloud_databases_path(project) + end + + private + + def enable_service_params + { google_oauth2_token: token_in_session } + end + + def create_service_params(permitted_params) + { + google_oauth2_token: token_in_session, + gcp_project_id: permitted_params[:gcp_project], + environment_name: permitted_params[:ref], + database_version: permitted_params[:database_version], + tier: permitted_params[:tier] + } + end + + def error_message(message) + format(s_("CloudSeed|Google Cloud Error - %{message}"), message: message) + end + + def success_message + s_('CloudSeed|Cloud SQL instance creation request successful. Expected resolution time is ~5 minutes.') + end + + def validate_product + not_found unless permitted_params[:product].in?(%w[postgres mysql sqlserver]) + end + + def permitted_params + params.permit(:product) + end + + def title(product) + case product + when :postgres + s_('CloudSeed|Create Postgres Instance') + when :mysql + s_('CloudSeed|Create MySQL Instance') + else + s_('CloudSeed|Create MySQL Instance') + end + end + + def form_title(product) + case product + when :postgres + s_('CloudSeed|Cloud SQL for Postgres') + when :mysql + s_('CloudSeed|Cloud SQL for MySQL') + else + s_('CloudSeed|Cloud SQL for SQL Server') + end + end + + def description(product) + case product + when :postgres + s_('CloudSeed|Cloud SQL instances are fully managed, relational PostgreSQL databases. '\ + 'Google handles replication, patch management, and database management '\ + 'to ensure availability and performance.') + when :mysql + s_('Cloud SQL instances are fully managed, relational MySQL databases. '\ + 'Google handles replication, patch management, and database management '\ + 'to ensure availability and performance.') + else + s_('Cloud SQL instances are fully managed, relational SQL Server databases. ' \ + 'Google handles replication, patch management, and database management ' \ + 'to ensure availability and performance.') + end end end end diff --git a/app/controllers/projects/google_cloud/deployments_controller.rb b/app/controllers/projects/google_cloud/deployments_controller.rb index 1ac4697a63f..f6cc8d5eafb 100644 --- a/app/controllers/projects/google_cloud/deployments_controller.rb +++ b/app/controllers/projects/google_cloud/deployments_controller.rb @@ -12,7 +12,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base enableCloudStorageUrl: project_google_cloud_deployments_cloud_storage_path(project) } @js_data = js_data.to_json - track_event('deployments#index', 'success', js_data) + track_event(:render_page) end def cloud_run @@ -21,7 +21,7 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base .new(project, current_user, params).execute if enable_cloud_run_response[:status] == :error - track_event('deployments#cloud_run', 'error_enable_cloud_run', enable_cloud_run_response) + track_event(:error_enable_services) flash[:error] = enable_cloud_run_response[:message] redirect_to project_google_cloud_deployments_path(project) else @@ -30,17 +30,17 @@ class Projects::GoogleCloud::DeploymentsController < Projects::GoogleCloud::Base .new(project, current_user, params).execute if generate_pipeline_response[:status] == :error - track_event('deployments#cloud_run', 'error_generate_pipeline', generate_pipeline_response) + track_event(:error_generate_cloudrun_pipeline) flash[:error] = 'Failed to generate pipeline' redirect_to project_google_cloud_deployments_path(project) else cloud_run_mr_params = cloud_run_mr_params(generate_pipeline_response[:branch_name]) - track_event('deployments#cloud_run', 'success', cloud_run_mr_params) + track_event(:generate_cloudrun_pipeline) redirect_to project_new_merge_request_path(project, merge_request: cloud_run_mr_params) end end - rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e - track_event('deployments#cloud_run', 'error_gcp', e) + rescue Google::Apis::Error => e + track_event(:error_google_api) flash[:warning] = _('Google Cloud Error - %{error}') % { error: e } redirect_to project_google_cloud_deployments_path(project) end diff --git a/app/controllers/projects/google_cloud/gcp_regions_controller.rb b/app/controllers/projects/google_cloud/gcp_regions_controller.rb index 39f33624804..2f0bc05030f 100644 --- a/app/controllers/projects/google_cloud/gcp_regions_controller.rb +++ b/app/controllers/projects/google_cloud/gcp_regions_controller.rb @@ -15,13 +15,13 @@ class Projects::GoogleCloud::GcpRegionsController < Projects::GoogleCloud::BaseC cancelPath: project_google_cloud_configuration_path(project) } @js_data = js_data.to_json - track_event('gcp_regions#index', 'success', js_data) + track_event(:render_form) end def create permitted_params = params.permit(:ref, :gcp_region) - response = GoogleCloud::GcpRegionAddOrReplaceService.new(project).execute(permitted_params[:ref], permitted_params[:gcp_region]) - track_event('gcp_regions#create', 'success', response) + GoogleCloud::GcpRegionAddOrReplaceService.new(project).execute(permitted_params[:ref], permitted_params[:gcp_region]) + track_event(:configure_region) redirect_to project_google_cloud_configuration_path(project), notice: _('GCP region configured') end end diff --git a/app/controllers/projects/google_cloud/revoke_oauth_controller.rb b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb index 1a9a2daf4f2..dbf91806722 100644 --- a/app/controllers/projects/google_cloud/revoke_oauth_controller.rb +++ b/app/controllers/projects/google_cloud/revoke_oauth_controller.rb @@ -9,10 +9,10 @@ class Projects::GoogleCloud::RevokeOauthController < Projects::GoogleCloud::Base if response.success? redirect_message = { notice: s_('GoogleCloud|Google OAuth2 token revocation requested') } - track_event('revoke_oauth#create', 'success', response.to_json) + track_event(:revoke_oauth) else redirect_message = { alert: s_('GoogleCloud|Google OAuth2 token revocation request failed') } - track_event('revoke_oauth#create', 'error', response.to_json) + track_event(:error) end session.delete(GoogleApi::CloudPlatform::Client.session_key_for_token) diff --git a/app/controllers/projects/google_cloud/service_accounts_controller.rb b/app/controllers/projects/google_cloud/service_accounts_controller.rb index 7f25054177e..89d624764df 100644 --- a/app/controllers/projects/google_cloud/service_accounts_controller.rb +++ b/app/controllers/projects/google_cloud/service_accounts_controller.rb @@ -5,7 +5,7 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: def index if gcp_projects.empty? - track_event('service_accounts#index', 'error_form', 'no_gcp_projects') + track_event(:error_no_gcp_projects) flash[:warning] = _('No Google Cloud projects - You need at least one Google Cloud project') redirect_to project_google_cloud_configuration_path(project) else @@ -16,10 +16,10 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: } @js_data = js_data.to_json - track_event('service_accounts#index', 'success', js_data) + track_event(:render_form) end - rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e - track_event('service_accounts#index', 'error_gcp', e) + rescue Google::Apis::Error => e + track_event(:error_google_api) flash[:warning] = _('Google Cloud Error - %{error}') % { error: e } redirect_to project_google_cloud_configuration_path(project) end @@ -35,10 +35,10 @@ class Projects::GoogleCloud::ServiceAccountsController < Projects::GoogleCloud:: environment_name: permitted_params[:ref] ).execute - track_event('service_accounts#create', 'success', response) + track_event(:create_service_account) redirect_to project_google_cloud_configuration_path(project), notice: response.message - rescue Google::Apis::ClientError, Google::Apis::ServerError, Google::Apis::AuthorizationError => e - track_event('service_accounts#create', 'error_gcp', e) + rescue Google::Apis::Error => e + track_event(:error_google_api) flash[:warning] = _('Google Cloud Error - %{error}') % { error: e } redirect_to project_google_cloud_configuration_path(project) end diff --git a/app/controllers/projects/graphs_controller.rb b/app/controllers/projects/graphs_controller.rb index 63309cce1e5..47557133ac8 100644 --- a/app/controllers/projects/graphs_controller.rb +++ b/app/controllers/projects/graphs_controller.rb @@ -2,14 +2,18 @@ class Projects::GraphsController < Projects::ApplicationController include ExtractsPath - include RedisTracking + include ProductAnalyticsTracking # Authorize before_action :require_non_empty_project before_action :assign_ref_vars before_action :authorize_read_repository_graphs! - track_redis_hll_event :charts, name: 'p_analytics_repo' + track_custom_event :charts, + name: 'p_analytics_repo', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] feature_category :source_code_management, [:show, :commits, :languages, :charts] urgency :low, [:show] @@ -102,6 +106,14 @@ class Projects::GraphsController < Projects::ApplicationController render json: @log.to_json end + + def tracking_namespace_source + project.namespace + end + + def tracking_project_source + project + end end Projects::GraphsController.prepend_mod diff --git a/app/controllers/projects/hook_logs_controller.rb b/app/controllers/projects/hook_logs_controller.rb index 0ca3d71f728..3ab4c34737d 100644 --- a/app/controllers/projects/hook_logs_controller.rb +++ b/app/controllers/projects/hook_logs_controller.rb @@ -1,40 +1,19 @@ # frozen_string_literal: true class Projects::HookLogsController < Projects::ApplicationController - include ::Integrations::HooksExecution - before_action :authorize_admin_project! - before_action :hook, only: [:show, :retry] - before_action :hook_log, only: [:show, :retry] - - respond_to :html + include WebHooks::HookLogActions layout 'project_settings' - feature_category :integrations - urgency :low, [:retry] - - def show - end - - def retry - execute_hook - redirect_to edit_project_hook_path(@project, @hook) - end - private - def execute_hook - result = hook.execute(hook_log.request_data, hook_log.trigger) - set_hook_execution_notice(result) - end - def hook @hook ||= @project.hooks.find(params[:hook_id]) end - def hook_log - @hook_log ||= hook.web_hook_logs.find(params[:id]) + def after_retry_redirect_path + edit_project_hook_path(@project, hook) end end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index 50f388324f1..22b6bf6faf0 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class Projects::HooksController < Projects::ApplicationController - include ::Integrations::HooksExecution + include ::WebHooks::HookActions # Authorize before_action :authorize_admin_project! @@ -35,7 +35,7 @@ class Projects::HooksController < Projects::ApplicationController end def hook_logs - @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]) + @hook_logs ||= hook.web_hook_logs.recent.page(params[:page]).without_count end def trigger_values diff --git a/app/controllers/projects/incidents_controller.rb b/app/controllers/projects/incidents_controller.rb index 36b52533e78..cbf0c756e1e 100644 --- a/app/controllers/projects/incidents_controller.rb +++ b/app/controllers/projects/incidents_controller.rb @@ -11,6 +11,7 @@ class Projects::IncidentsController < Projects::ApplicationController push_force_frontend_feature_flag(:work_items, @project&.work_items_feature_flag_enabled?) push_force_frontend_feature_flag(:work_items_mvc_2, @project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:work_items_hierarchy, @project) + push_frontend_feature_flag(:remove_user_attributes_projects, @project) end feature_category :incident_management @@ -28,7 +29,7 @@ class Projects::IncidentsController < Projects::ApplicationController .inc_relations_for_view .iid_in(params[:id]) .without_order - .first + .take # rubocop:disable CodeReuse/ActiveRecord end end diff --git a/app/controllers/projects/integrations/shimos_controller.rb b/app/controllers/projects/integrations/shimos_controller.rb index 827dbb8f3f9..6c8313d0805 100644 --- a/app/controllers/projects/integrations/shimos_controller.rb +++ b/app/controllers/projects/integrations/shimos_controller.rb @@ -12,7 +12,7 @@ module Projects private def ensure_renderable - render_404 unless Feature.enabled?(:shimo_integration, project) && project.has_shimo? && project.shimo_integration&.render? + render_404 unless project.has_shimo? && project.shimo_integration&.render? end end end diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index d19db2b11ab..800a7df2566 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -42,6 +42,7 @@ class Projects::IssuesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:incident_timeline, project) + push_frontend_feature_flag(:remove_user_attributes_projects, project) end before_action only: [:index, :show] do @@ -53,6 +54,7 @@ class Projects::IssuesController < Projects::ApplicationController push_frontend_feature_flag(:realtime_labels, project) push_force_frontend_feature_flag(:work_items_mvc_2, project&.work_items_mvc_2_feature_flag_enabled?) push_frontend_feature_flag(:work_items_hierarchy, project) + push_frontend_feature_flag(:epic_widget_edit_confirmation, project) push_force_frontend_feature_flag(:work_items_create_from_markdown, project&.work_items_create_from_markdown_feature_flag_enabled?) end @@ -63,19 +65,19 @@ class Projects::IssuesController < Projects::ApplicationController alias_method :designs, :show feature_category :team_planning, [ - :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 - ] + :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 + ] urgency :low, [ - :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 - ] + :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] urgency :low, [:service_desk] diff --git a/app/controllers/projects/jobs_controller.rb b/app/controllers/projects/jobs_controller.rb index 7878ace5015..557ac566733 100644 --- a/app/controllers/projects/jobs_controller.rb +++ b/app/controllers/projects/jobs_controller.rb @@ -20,6 +20,9 @@ class Projects::JobsController < Projects::ApplicationController before_action :verify_proxy_request!, only: :proxy_websocket_authorize before_action :push_job_log_jump_to_failures, only: [:show] before_action :reject_if_build_artifacts_size_refreshing!, only: [:erase] + before_action do + push_frontend_feature_flag(:graphql_job_app, project, type: :development) + end layout 'project' @@ -120,11 +123,13 @@ class Projects::JobsController < Projects::ApplicationController end def erase - if @build.erase(erased_by: current_user) + service_response = Ci::BuildEraseService.new(@build, current_user).execute + + if service_response.success? redirect_to project_job_path(project, @build), notice: _("Job has been successfully erased!") else - respond_422 + head service_response.http_status end end diff --git a/app/controllers/projects/merge_requests/diffs_controller.rb b/app/controllers/projects/merge_requests/diffs_controller.rb index 279fd4c457e..a68c2ffa06d 100644 --- a/app/controllers/projects/merge_requests/diffs_controller.rb +++ b/app/controllers/projects/merge_requests/diffs_controller.rb @@ -29,6 +29,7 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic render_diffs end + # rubocop: disable Metrics/AbcSize def diffs_batch diff_options_hash = diff_options diff_options_hash[:paths] = params[:paths] if params[:paths] @@ -61,21 +62,11 @@ class Projects::MergeRequests::DiffsController < Projects::MergeRequests::Applic options[:allow_tree_conflicts] ] - if Feature.enabled?(:etag_merge_request_diff_batches, @merge_request.project) - return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs]) - end + return unless stale?(etag: [cache_context + diff_options_hash.fetch(:paths, []), diffs]) - if diff_options_hash[:paths].blank? - render_cached( - diffs, - with: PaginatedDiffSerializer.new(current_user: current_user), - cache_context: -> (_) { [Digest::SHA256.hexdigest(cache_context.to_s)] }, - **options - ) - else - render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) - end + render json: PaginatedDiffSerializer.new(current_user: current_user).represent(diffs, options) end + # rubocop: enable Metrics/AbcSize def diffs_metadata diffs = @compare.diffs(diff_options) diff --git a/app/controllers/projects/merge_requests/drafts_controller.rb b/app/controllers/projects/merge_requests/drafts_controller.rb index ff6b6bfaf27..74bb3ad1a63 100644 --- a/app/controllers/projects/merge_requests/drafts_controller.rb +++ b/app/controllers/projects/merge_requests/drafts_controller.rb @@ -49,8 +49,24 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli def publish result = DraftNotes::PublishService.new(merge_request, current_user).execute(draft_note(allow_nil: true)) - if Feature.enabled?(:mr_review_submit_comment, @project) && create_note_params[:note] - Notes::CreateService.new(@project, current_user, create_note_params).execute + if Feature.enabled?(:mr_review_submit_comment, @project) + if create_note_params[:note] + ::Notes::CreateService.new(@project, current_user, create_note_params).execute + + merge_request_activity_counter.track_submit_review_comment(user: current_user) + end + + if Gitlab::Utils.to_boolean(approve_params[:approve]) + unless merge_request.approved_by?(current_user) + success = ::MergeRequests::ApprovalService.new(project: @project, current_user: current_user, params: approve_params).execute(merge_request) + + unless success + return render json: { message: _('An error occurred while approving, please try again.') }, status: :internal_server_error + end + end + + merge_request_activity_counter.track_submit_review_approve(user: current_user) + end end if result[:status] == :success @@ -115,6 +131,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli end end + def approve_params + params.permit(:approve) + end + def prepare_notes_for_rendering(notes) return [] unless notes @@ -147,4 +167,10 @@ class Projects::MergeRequests::DraftsController < Projects::MergeRequests::Appli def authorize_create_note! access_denied! unless can?(current_user, :create_note, merge_request) end + + def merge_request_activity_counter + Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter + end end + +Projects::MergeRequests::DraftsController.prepend_mod diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 870c57fd6f3..5a212e9a152 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -45,6 +45,7 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo push_frontend_feature_flag(:paginated_mr_discussions, project) push_frontend_feature_flag(:mr_review_submit_comment, project) push_frontend_feature_flag(:mr_experience_survey, project) + push_frontend_feature_flag(:remove_user_attributes_projects, @project) end before_action do @@ -56,11 +57,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo after_action :log_merge_request_show, only: [:show] feature_category :code_review, [ - :assign_related_issues, :bulk_update, :cancel_auto_merge, - :commit_change_content, :commits, :context_commits, :destroy, - :discussions, :edit, :index, :merge, :rebase, :remove_wip, - :show, :toggle_award_emoji, :toggle_subscription, :update - ] + :assign_related_issues, :bulk_update, :cancel_auto_merge, + :commit_change_content, :commits, :context_commits, :destroy, + :discussions, :edit, :index, :merge, :rebase, :remove_wip, + :show, :toggle_award_emoji, :toggle_subscription, :update + ] feature_category :code_testing, [:test_reports, :coverage_reports] feature_category :code_quality, [:codequality_reports, :codequality_mr_diff_reports] @@ -219,7 +220,13 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo def context_commits # Get commits from repository # or from cache if already merged - commits = ContextCommitsFinder.new(project, @merge_request, { search: params[:search], limit: params[:limit], offset: params[:offset] }).execute + commits = ContextCommitsFinder.new(project, @merge_request, { + search: params[:search], + author: params[:author], + committed_before: convert_date_to_epoch(params[:committed_before]), + committed_after: convert_date_to_epoch(params[:committed_after]), + limit: params[:limit] + }).execute render json: CommitEntity.represent(commits, { type: :full, request: merge_request }) end @@ -552,6 +559,11 @@ class Projects::MergeRequestsController < Projects::MergeRequests::ApplicationCo diffs_metadata_project_json_merge_request_path(project, merge_request, 'json', params) end + + def convert_date_to_epoch(date) + Date.strptime(date, "%Y-%m-%d")&.to_time&.to_i if date + rescue Date::Error, TypeError + end end Projects::MergeRequestsController.prepend_mod_with('Projects::MergeRequestsController') diff --git a/app/controllers/projects/packages/package_files_controller.rb b/app/controllers/projects/packages/package_files_controller.rb index 32aadb4fcf4..1aa91ee1189 100644 --- a/app/controllers/projects/packages/package_files_controller.rb +++ b/app/controllers/projects/packages/package_files_controller.rb @@ -11,6 +11,7 @@ module Projects def download package_file = project.package_files.find(params[:id]) + package_file.package.touch_last_downloaded_at send_upload(package_file.file, attachment: package_file.file_name) end end diff --git a/app/controllers/projects/pipelines/tests_controller.rb b/app/controllers/projects/pipelines/tests_controller.rb index 8ac370b1bd4..d77cf095a4f 100644 --- a/app/controllers/projects/pipelines/tests_controller.rb +++ b/app/controllers/projects/pipelines/tests_controller.rb @@ -51,7 +51,8 @@ module Projects def test_suite suite = builds.sum do |build| - build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report.get_suite(build.test_suite_name) end Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, project).load! diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index b2aa1d9f4ca..2a8f7171f9c 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -3,6 +3,7 @@ class Projects::PipelinesController < Projects::ApplicationController include ::Gitlab::Utils::StrongMemoize include RedisTracking + include ProductAnalyticsTracking include ProjectStatsRefreshConflictsGuard include ZuoraCSP @@ -25,6 +26,7 @@ class Projects::PipelinesController < Projects::ApplicationController before_action do push_frontend_feature_flag(:pipeline_tabs_vue, @project) + push_frontend_feature_flag(:run_pipeline_graphql, @project) end # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/225596 @@ -32,8 +34,11 @@ class Projects::PipelinesController < Projects::ApplicationController around_action :allow_gitaly_ref_name_caching, only: [:index, :show] - # Will be removed with https://gitlab.com/gitlab-org/gitlab/-/issues/345074 - track_redis_hll_event :charts, name: 'p_analytics_pipelines' + track_custom_event :charts, + name: 'p_analytics_pipelines', + action: 'perform_analytics_usage_action', + label: 'redis_hll_counters.analytics.analytics_total_unique_counts_monthly', + destinations: %i[redis_hll snowplow] track_redis_hll_event :charts, name: 'p_analytics_ci_cd_pipelines', if: -> { should_track_ci_cd_pipelines? } track_redis_hll_event :charts, name: 'p_analytics_ci_cd_deployment_frequency', if: -> { should_track_ci_cd_deployment_frequency? } @@ -46,10 +51,10 @@ class Projects::PipelinesController < Projects::ApplicationController POLLING_INTERVAL = 10_000 feature_category :continuous_integration, [ - :charts, :show, :config_variables, :stage, :cancel, :retry, - :builds, :dag, :failures, :status, - :index, :create, :new, :destroy - ] + :charts, :show, :config_variables, :stage, :cancel, :retry, + :builds, :dag, :failures, :status, + :index, :create, :new, :destroy + ] feature_category :code_testing, [:test_report] feature_category :build_artifacts, [:downloadable_artifacts] @@ -371,6 +376,14 @@ class Projects::PipelinesController < Projects::ApplicationController def should_track_ci_cd_change_failure_rate? params[:chart] == 'change-failure-rate' end + + def tracking_namespace_source + project.namespace + end + + def tracking_project_source + project + end end Projects::PipelinesController.prepend_mod_with('Projects::PipelinesController') diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb index ba9576795ec..ee12b85b3a4 100644 --- a/app/controllers/projects/runners_controller.rb +++ b/app/controllers/projects/runners_controller.rb @@ -15,7 +15,7 @@ class Projects::RunnersController < Projects::ApplicationController end def update - if Ci::Runners::UpdateRunnerService.new(@runner).update(runner_params) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(runner_params).success? redirect_to project_runner_path(@project, @runner), notice: _('Runner was successfully updated.') else render 'edit' @@ -31,7 +31,7 @@ class Projects::RunnersController < Projects::ApplicationController end def resume - if Ci::Runners::UpdateRunnerService.new(@runner).update(active: true) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: true).success? redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.') else redirect_to project_runners_path(@project), alert: _('Runner was not updated.') @@ -39,7 +39,7 @@ class Projects::RunnersController < Projects::ApplicationController end def pause - if Ci::Runners::UpdateRunnerService.new(@runner).update(active: false) + if Ci::Runners::UpdateRunnerService.new(@runner).execute(active: false).success? redirect_to project_runners_path(@project), notice: _('Runner was successfully updated.') else redirect_to project_runners_path(@project), alert: _('Runner was not updated.') diff --git a/app/controllers/projects/settings/integration_hook_logs_controller.rb b/app/controllers/projects/settings/integration_hook_logs_controller.rb index 1e42fbce4c4..3a921ecad0d 100644 --- a/app/controllers/projects/settings/integration_hook_logs_controller.rb +++ b/app/controllers/projects/settings/integration_hook_logs_controller.rb @@ -7,13 +7,13 @@ module Projects before_action :integration, only: [:show, :retry] - def retry - execute_hook - redirect_to edit_project_settings_integration_path(@project, @integration) - end - private + override :after_retry_redirect_path + def after_retry_redirect_path + edit_project_settings_integration_path(@project, @integration) + end + def integration @integration ||= @project.find_or_initialize_integration(params[:integration_id]) end diff --git a/app/controllers/projects/settings/integrations_controller.rb b/app/controllers/projects/settings/integrations_controller.rb index 03ef434456f..2bbcd9fe20c 100644 --- a/app/controllers/projects/settings/integrations_controller.rb +++ b/app/controllers/projects/settings/integrations_controller.rb @@ -11,7 +11,7 @@ module Projects before_action :integration, only: [:edit, :update, :test] before_action :default_integration, only: [:edit, :update] before_action :web_hook_logs, only: [:edit, :update] - before_action -> { check_rate_limit!(:project_testing_integration, scope: [@project, current_user]) }, only: :test + before_action -> { check_test_rate_limit! }, only: :test respond_to :html @@ -124,7 +124,7 @@ module Projects def web_hook_logs return unless integration.try(:service_hook).present? - @web_hook_logs ||= integration.service_hook.web_hook_logs.recent.page(params[:page]) + @web_hook_logs ||= integration.service_hook.web_hook_logs.recent.page(params[:page]).without_count end def ensure_integration_enabled @@ -140,6 +140,15 @@ module Projects def use_inherited_settings?(attributes) default_integration && attributes[:inherit_from_id] == default_integration.id.to_s end + + def check_test_rate_limit! + check_rate_limit!(:project_testing_integration, scope: [@project, current_user]) do + render json: { + error: true, + message: _('This endpoint has been requested too many times. Try again later.') + }, status: :ok + end + end end end end diff --git a/app/controllers/projects/settings/merge_requests_controller.rb b/app/controllers/projects/settings/merge_requests_controller.rb new file mode 100644 index 00000000000..93e10695767 --- /dev/null +++ b/app/controllers/projects/settings/merge_requests_controller.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +module Projects + module Settings + class MergeRequestsController < Projects::ApplicationController + layout 'project_settings' + + before_action :merge_requests_enabled? + before_action :present_project, only: [:edit] + before_action :authorize_admin_project! + + feature_category :code_review + + def update + result = ::Projects::UpdateService.new(@project, current_user, project_params).execute + + if result[:status] == :success + flash[:notice] = format(_("Project '%{project_name}' was successfully updated."), project_name: @project.name) + redirect_to project_settings_merge_requests_path(@project) + else + # Refresh the repo in case anything changed + @repository = @project.repository.reset + + flash[:alert] = result[:message] + @project.reset + render 'show' + end + end + + private + + def merge_requests_enabled? + render_404 unless @project.merge_requests_enabled? + end + + def project_params + params.require(:project) + .permit(project_params_attributes) + end + + def project_setting_attributes + %i[ + squash_option + allow_editing_commit_messages + mr_default_target_self + ] + end + + def project_params_attributes + [ + :allow_merge_on_skipped_pipeline, + :resolve_outdated_diff_discussions, + :only_allow_merge_if_all_discussions_are_resolved, + :only_allow_merge_if_pipeline_succeeds, + :printing_merge_request_link_enabled, + :remove_source_branch_after_merge, + :merge_method, + :merge_commit_template_or_default, + :squash_commit_template_or_default, + :suggestion_commit_message + ] + [project_setting_attributes: project_setting_attributes] + end + end + end +end + +Projects::Settings::MergeRequestsController.prepend_mod_with('Projects::Settings::MergeRequestsController') diff --git a/app/controllers/projects/settings/repository_controller.rb b/app/controllers/projects/settings/repository_controller.rb index a178b8f7aa3..43c6451577a 100644 --- a/app/controllers/projects/settings/repository_controller.rb +++ b/app/controllers/projects/settings/repository_controller.rb @@ -34,13 +34,13 @@ module Projects def create_deploy_token result = Projects::DeployTokens::CreateService.new(@project, current_user, deploy_token_params).execute - @new_deploy_token = result[:deploy_token] if result[:status] == :success + @created_deploy_token = result[:deploy_token] respond_to do |format| format.json do # IMPORTANT: It's a security risk to expose the token value more than just once here! - json = API::Entities::DeployTokenWithToken.represent(@new_deploy_token).as_json + json = API::Entities::DeployTokenWithToken.represent(@created_deploy_token).as_json render json: json, status: result[:http_status] end format.html do @@ -49,6 +49,7 @@ module Projects end end else + @new_deploy_token = result[:deploy_token] respond_to do |format| format.json { render json: { message: result[:message] }, status: result[:http_status] } format.html do diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index a364668ea5f..8f4987a07f6 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -35,14 +35,4 @@ class Projects::UploadsController < Projects::ApplicationController Project.find_by_full_path("#{namespace}/#{id}") end - - # Overrides ApplicationController#build_canonical_path since there are - # multiple routes that match project uploads: - # https://gitlab.com/gitlab-org/gitlab/issues/196396 - def build_canonical_path(project) - return super unless action_name == 'show' - return super unless params[:secret] && params[:filename] - - show_namespace_project_uploads_url(project.namespace.to_param, project.to_param, params[:secret], params[:filename]) - end end |