2018-09-23 15:44:14 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2015-04-30 13:06:18 -04:00
|
|
|
class Dashboard::ProjectsController < Dashboard::ApplicationController
|
2017-03-03 05:35:04 -05:00
|
|
|
include ParamsBackwardCompatibility
|
2017-12-07 04:11:41 -05:00
|
|
|
include RendersMemberAccess
|
2019-06-26 23:48:21 -04:00
|
|
|
include OnboardingExperimentHelper
|
2019-08-21 06:13:45 -04:00
|
|
|
include SortingHelper
|
|
|
|
include SortingPreference
|
2017-03-03 05:35:04 -05:00
|
|
|
|
2018-11-28 14:06:02 -05:00
|
|
|
prepend_before_action(only: [:index]) { authenticate_sessionless_user!(:rss) }
|
2017-03-03 05:35:04 -05:00
|
|
|
before_action :set_non_archived_param
|
2019-08-21 06:13:45 -04:00
|
|
|
before_action :set_sorting
|
2019-07-04 05:29:06 -04:00
|
|
|
before_action :projects, only: [:index]
|
2017-12-11 09:21:06 -05:00
|
|
|
skip_cross_project_access_check :index, :starred
|
2016-03-04 10:11:33 -05:00
|
|
|
|
2015-09-08 09:49:20 -04:00
|
|
|
def index
|
|
|
|
respond_to do |format|
|
2019-02-24 22:15:43 -05:00
|
|
|
format.html do
|
2019-06-04 20:10:24 -04:00
|
|
|
render_projects
|
2019-02-24 22:15:43 -05:00
|
|
|
end
|
2015-09-08 09:49:20 -04:00
|
|
|
format.atom do
|
|
|
|
load_events
|
2017-06-12 13:21:13 -04:00
|
|
|
render layout: 'xml.atom'
|
2015-09-08 09:49:20 -04:00
|
|
|
end
|
2016-02-03 16:33:01 -05:00
|
|
|
format.json do
|
|
|
|
render json: {
|
2019-03-18 20:01:49 -04:00
|
|
|
html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
|
2016-02-03 16:33:01 -05:00
|
|
|
}
|
|
|
|
end
|
2015-09-08 09:49:20 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2015-03-09 17:12:03 -04:00
|
|
|
def starred
|
2017-06-21 09:48:12 -04:00
|
|
|
@projects = load_projects(params.merge(starred: true))
|
2018-07-06 03:51:31 -04:00
|
|
|
.includes(:forked_from_project, :tags)
|
2016-02-03 18:21:14 -05:00
|
|
|
|
2015-03-09 17:12:03 -04:00
|
|
|
@groups = []
|
|
|
|
|
|
|
|
respond_to do |format|
|
|
|
|
format.html
|
|
|
|
format.json do
|
2016-02-03 18:21:14 -05:00
|
|
|
render json: {
|
2019-03-18 20:01:49 -04:00
|
|
|
html: view_to_html_string("dashboard/projects/_projects", projects: @projects)
|
2016-02-03 18:21:14 -05:00
|
|
|
}
|
2015-03-09 17:12:03 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2015-03-09 17:12:03 -04:00
|
|
|
|
|
|
|
private
|
|
|
|
|
2019-06-04 20:10:24 -04:00
|
|
|
def projects
|
|
|
|
@projects ||= load_projects(params.merge(non_public: true))
|
|
|
|
end
|
|
|
|
|
|
|
|
def render_projects
|
2019-09-18 10:02:45 -04:00
|
|
|
# n+1: https://gitlab.com/gitlab-org/gitlab-foss/issues/40260
|
2019-06-04 20:10:24 -04:00
|
|
|
Gitlab::GitalyClient.allow_n_plus_1_calls do
|
|
|
|
render
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-03-03 05:35:04 -05:00
|
|
|
def load_projects(finder_params)
|
2018-12-07 05:05:17 -05:00
|
|
|
@total_user_projects_count = ProjectsFinder.new(params: { non_public: true }, current_user: current_user).execute
|
|
|
|
@total_starred_projects_count = ProjectsFinder.new(params: { starred: true }, current_user: current_user).execute
|
|
|
|
|
2017-12-07 04:11:41 -05:00
|
|
|
projects = ProjectsFinder
|
|
|
|
.new(params: finder_params, current_user: current_user)
|
|
|
|
.execute
|
2018-11-13 22:14:50 -05:00
|
|
|
.includes(:route, :creator, :group, namespace: [:route, :owner])
|
Remove N+1 SQL query loading project feature in dashboard
Projects that have a pipeline may need to check whether the user has
permission to read the build (`can?(current_user, :read_build,
project)`), which requires checking the `project_features` table.
This would cause an N+1 SQL query for each project.
This change also has a beneficial side effect that may avoid a race
condition. When a user deletes a project, the project is queued for
deletion and the user is redirected back to the dashboard page. However,
the following may happen:
1. The dashboard page may load this deleted project in the list of
20 projects.
2. The view will load the project pipeline status from the cache and
attempt to show each project.
3. When the view encounters the deleted project, it calls
`can?(current_user, :read_build, project)` to determine whether to
display the pipeline status.
4. Sidekiq deletes the project from the database.
5. However, since the deleted project is still loaded in memory, it will
attempt to call `project.project_feature.access_level`.
6. Since `project_feature` was not eager loaded, a lazy `SELECT` call is
made to the database.
7. This `SELECT` call returns nothing, and the user sees a 500 error.
By eager loading `project_feature`, we can ensure that we have a
consistent view and avoid records from being deleted later.
Closes https://gitlab.com/gitlab-org/gitlab-ce/issues/66482
2019-08-23 16:53:02 -04:00
|
|
|
.preload(:project_feature)
|
2018-07-06 03:51:31 -04:00
|
|
|
.page(finder_params[:page])
|
2017-12-07 04:11:41 -05:00
|
|
|
|
|
|
|
prepare_projects_for_rendering(projects)
|
2017-02-13 09:19:03 -05:00
|
|
|
end
|
2018-08-27 11:31:01 -04:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2017-02-13 09:19:03 -05:00
|
|
|
|
2015-03-09 17:12:03 -04:00
|
|
|
def load_events
|
2017-07-27 13:42:15 -04:00
|
|
|
projects = load_projects(params.merge(non_public: true))
|
|
|
|
|
|
|
|
@events = EventCollection
|
|
|
|
.new(projects, offset: params[:offset].to_i, filter: event_filter)
|
|
|
|
.to_a
|
2017-11-06 11:52:56 -05:00
|
|
|
|
|
|
|
Events::RenderService.new(current_user).execute(@events, atom_request: request.format.atom?)
|
2015-03-09 17:12:03 -04:00
|
|
|
end
|
2019-08-21 06:13:45 -04:00
|
|
|
|
|
|
|
def set_sorting
|
|
|
|
params[:sort] = set_sort_order
|
|
|
|
@sort = params[:sort]
|
|
|
|
end
|
|
|
|
|
|
|
|
def default_sort_order
|
|
|
|
sort_value_latest_activity
|
|
|
|
end
|
|
|
|
|
|
|
|
def sorting_field
|
|
|
|
Project::SORTING_PREFERENCE_FIELD
|
|
|
|
end
|
2015-03-09 17:12:03 -04:00
|
|
|
end
|
2019-09-13 09:26:31 -04:00
|
|
|
|
|
|
|
Dashboard::ProjectsController.prepend_if_ee('EE::Dashboard::ProjectsController')
|