2018-09-11 19:08:34 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2014-09-02 12:28:27 +00:00
|
|
|
# IssuableFinder
|
2014-01-15 14:16:23 +00:00
|
|
|
#
|
|
|
|
# Used to filter Issues and MergeRequests collections by set of params
|
|
|
|
#
|
|
|
|
# Arguments:
|
|
|
|
# klass - actual class like Issue or MergeRequest
|
|
|
|
# current_user - which user use
|
|
|
|
# params:
|
2018-05-14 02:07:53 +00:00
|
|
|
# scope: 'created_by_me' or 'assigned_to_me' or 'all'
|
2018-06-26 19:30:29 +00:00
|
|
|
# state: 'opened' or 'closed' or 'locked' or 'all'
|
2014-01-15 14:16:23 +00:00
|
|
|
# group_id: integer
|
|
|
|
# project_id: integer
|
2015-06-28 20:12:32 +00:00
|
|
|
# milestone_title: string
|
2017-07-20 20:44:48 +00:00
|
|
|
# author_id: integer
|
2018-11-09 14:29:45 +00:00
|
|
|
# author_username: string
|
2018-10-25 07:02:28 +00:00
|
|
|
# assignee_id: integer or 'None' or 'Any'
|
2018-11-09 14:29:45 +00:00
|
|
|
# assignee_username: string
|
2014-01-15 14:16:23 +00:00
|
|
|
# search: string
|
2019-02-05 08:32:27 +00:00
|
|
|
# in: 'title', 'description', or a string joining them with comma
|
2014-01-15 14:16:23 +00:00
|
|
|
# label_name: string
|
|
|
|
# sort: string
|
2016-11-27 10:33:15 +00:00
|
|
|
# non_archived: boolean
|
2017-02-17 18:28:32 +00:00
|
|
|
# iids: integer[]
|
2017-08-30 07:48:55 +00:00
|
|
|
# my_reaction_emoji: string
|
2018-02-28 11:16:29 +00:00
|
|
|
# created_after: datetime
|
|
|
|
# created_before: datetime
|
|
|
|
# updated_after: datetime
|
|
|
|
# updated_before: datetime
|
2018-11-29 12:52:48 +00:00
|
|
|
# attempt_group_search_optimizations: boolean
|
2019-05-07 11:08:25 +00:00
|
|
|
# attempt_project_search_optimizations: boolean
|
2014-01-15 14:16:23 +00:00
|
|
|
#
|
2014-09-02 12:28:27 +00:00
|
|
|
class IssuableFinder
|
2017-12-11 14:21:06 +00:00
|
|
|
prepend FinderWithCrossProjectAccess
|
|
|
|
include FinderMethods
|
2017-07-07 16:31:50 +00:00
|
|
|
include CreatedAtFilter
|
2018-11-29 12:52:48 +00:00
|
|
|
include Gitlab::Utils::StrongMemoize
|
2017-07-11 12:19:43 +00:00
|
|
|
|
2017-12-11 14:21:06 +00:00
|
|
|
requires_cross_project_access unless: -> { project? }
|
|
|
|
|
2018-10-25 07:02:28 +00:00
|
|
|
# This is used as a common filter for None / Any
|
2018-10-26 02:47:14 +00:00
|
|
|
FILTER_NONE = 'none'.freeze
|
|
|
|
FILTER_ANY = 'any'.freeze
|
2018-10-25 07:02:28 +00:00
|
|
|
|
2019-06-05 20:24:38 +00:00
|
|
|
# This is used in unassigning users
|
2017-02-21 23:32:18 +00:00
|
|
|
NONE = '0'.freeze
|
2015-03-27 01:56:42 +00:00
|
|
|
|
2014-02-25 17:15:08 +00:00
|
|
|
attr_accessor :current_user, :params
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2018-02-20 12:33:49 +00:00
|
|
|
def self.scalar_params
|
|
|
|
@scalar_params ||= %i[
|
|
|
|
assignee_id
|
|
|
|
assignee_username
|
|
|
|
author_id
|
|
|
|
author_username
|
2019-04-19 06:50:54 +00:00
|
|
|
label_name
|
2018-02-20 12:33:49 +00:00
|
|
|
milestone_title
|
|
|
|
my_reaction_emoji
|
|
|
|
search
|
2019-01-13 16:24:31 +00:00
|
|
|
in
|
2018-02-20 12:33:49 +00:00
|
|
|
]
|
|
|
|
end
|
|
|
|
|
|
|
|
def self.array_params
|
2018-11-09 14:29:45 +00:00
|
|
|
@array_params ||= { label_name: [], assignee_username: [] }
|
2018-02-20 12:33:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.valid_params
|
|
|
|
@valid_params ||= scalar_params + [array_params]
|
|
|
|
end
|
|
|
|
|
2016-11-22 10:25:04 +00:00
|
|
|
def initialize(current_user, params = {})
|
2014-01-15 14:16:23 +00:00
|
|
|
@current_user = current_user
|
|
|
|
@params = params
|
2015-05-25 11:36:28 +00:00
|
|
|
end
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2015-05-25 11:36:28 +00:00
|
|
|
def execute
|
2014-02-03 15:02:44 +00:00
|
|
|
items = init_collection
|
2018-02-20 12:33:49 +00:00
|
|
|
items = filter_items(items)
|
|
|
|
|
2019-02-19 20:51:11 +00:00
|
|
|
# This has to be last as we use a CTE as an optimization fence
|
|
|
|
# for counts by passing the force_cte param and enabling the
|
|
|
|
# attempt_group_search_optimizations feature flag
|
2018-06-05 17:31:32 +00:00
|
|
|
# https://www.postgresql.org/docs/current/static/queries-with.html
|
|
|
|
items = by_search(items)
|
2018-02-20 12:33:49 +00:00
|
|
|
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
items = sort(items)
|
2019-02-19 20:51:11 +00:00
|
|
|
|
|
|
|
items
|
2018-02-20 12:33:49 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def filter_items(items)
|
2018-06-05 17:31:32 +00:00
|
|
|
items = by_project(items)
|
2018-11-29 12:52:48 +00:00
|
|
|
items = by_group(items)
|
2014-02-03 15:02:44 +00:00
|
|
|
items = by_scope(items)
|
2017-07-07 16:31:50 +00:00
|
|
|
items = by_created_at(items)
|
2018-02-28 11:16:29 +00:00
|
|
|
items = by_updated_at(items)
|
2019-01-17 17:49:07 +00:00
|
|
|
items = by_closed_at(items)
|
2014-01-15 14:16:23 +00:00
|
|
|
items = by_state(items)
|
|
|
|
items = by_group(items)
|
|
|
|
items = by_assignee(items)
|
2014-12-05 15:13:07 +00:00
|
|
|
items = by_author(items)
|
2016-11-27 10:33:15 +00:00
|
|
|
items = by_non_archived(items)
|
2017-02-17 18:28:32 +00:00
|
|
|
items = by_iids(items)
|
2017-02-07 13:15:07 +00:00
|
|
|
items = by_milestone(items)
|
|
|
|
items = by_label(items)
|
2018-02-20 12:33:49 +00:00
|
|
|
by_my_reaction_emoji(items)
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|
|
|
|
|
2017-08-24 16:17:04 +00:00
|
|
|
def row_count
|
|
|
|
Gitlab::IssuablesCountForState.new(self).for_state_or_opened(params[:state])
|
|
|
|
end
|
|
|
|
|
2016-11-25 13:57:56 +00:00
|
|
|
# We often get counts for each state by running a query per state, and
|
|
|
|
# counting those results. This is typically slower than running one query
|
|
|
|
# (even if that query is slower than any of the individual state queries) and
|
|
|
|
# grouping and counting within that query.
|
|
|
|
#
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2016-11-25 13:57:56 +00:00
|
|
|
def count_by_state
|
2019-02-19 20:51:11 +00:00
|
|
|
count_params = params.merge(state: nil, sort: nil, force_cte: true)
|
2016-11-25 13:57:56 +00:00
|
|
|
finder = self.class.new(current_user, count_params)
|
2019-02-19 20:51:11 +00:00
|
|
|
|
2016-11-25 13:57:56 +00:00
|
|
|
counts = Hash.new(0)
|
|
|
|
|
|
|
|
# Searching by label includes a GROUP BY in the query, but ours will be last
|
|
|
|
# because it is added last. Searching by multiple labels also includes a row
|
|
|
|
# per issuable, so we have to count those in Ruby - which is bad, but still
|
|
|
|
# better than performing multiple queries.
|
|
|
|
#
|
2018-06-05 17:31:32 +00:00
|
|
|
# This does not apply when we are using a CTE for the search, as the labels
|
|
|
|
# GROUP BY is inside the subquery in that case, so we set labels_count to 1.
|
2019-02-19 20:51:11 +00:00
|
|
|
#
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
# Groups and projects have separate feature flags to suggest the use
|
|
|
|
# of a CTE. The CTE will not be used if the sort doesn't support it,
|
|
|
|
# but will always be used for the counts here as we ignore sorting
|
|
|
|
# anyway.
|
2018-06-05 17:31:32 +00:00
|
|
|
labels_count = label_names.any? ? label_names.count : 1
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
labels_count = 1 if use_cte_for_search?
|
2018-06-05 17:31:32 +00:00
|
|
|
|
2016-11-25 13:57:56 +00:00
|
|
|
finder.execute.reorder(nil).group(:state).count.each do |key, value|
|
2018-10-01 11:45:15 +00:00
|
|
|
counts[count_key(key)] += value / labels_count
|
2016-11-25 13:57:56 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
counts[:all] = counts.values.sum
|
|
|
|
|
2018-07-27 10:13:36 +00:00
|
|
|
counts.with_indifferent_access
|
2016-11-25 13:57:56 +00:00
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2016-11-25 13:57:56 +00:00
|
|
|
|
2015-05-25 11:36:28 +00:00
|
|
|
def group
|
|
|
|
return @group if defined?(@group)
|
|
|
|
|
2015-06-18 17:15:17 +00:00
|
|
|
@group =
|
2015-05-25 11:36:28 +00:00
|
|
|
if params[:group_id].present?
|
|
|
|
Group.find(params[:group_id])
|
2015-06-18 17:15:17 +00:00
|
|
|
else
|
2015-05-25 11:36:28 +00:00
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-11-15 06:56:51 +00:00
|
|
|
def related_groups
|
|
|
|
if project? && project && project.group && Ability.allowed?(current_user, :read_group, project.group)
|
|
|
|
project.group.self_and_ancestors
|
|
|
|
elsif group
|
|
|
|
[group]
|
|
|
|
elsif current_user
|
2018-12-17 12:59:23 +00:00
|
|
|
Gitlab::ObjectHierarchy.new(current_user.authorized_groups, current_user.groups).all_objects
|
2018-11-15 06:56:51 +00:00
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-10-14 10:23:49 +00:00
|
|
|
def project?
|
|
|
|
params[:project_id].present?
|
|
|
|
end
|
|
|
|
|
2015-05-25 11:36:28 +00:00
|
|
|
def project
|
|
|
|
return @project if defined?(@project)
|
|
|
|
|
2016-10-26 17:34:06 +00:00
|
|
|
project = Project.find(params[:project_id])
|
|
|
|
project = nil unless Ability.allowed?(current_user, :"read_#{klass.to_ability_name}", project)
|
2015-11-11 11:49:16 +00:00
|
|
|
|
2016-10-26 17:34:06 +00:00
|
|
|
@project = project
|
2015-10-14 10:23:49 +00:00
|
|
|
end
|
|
|
|
|
2018-11-15 06:56:51 +00:00
|
|
|
def projects
|
|
|
|
return @projects if defined?(@projects)
|
|
|
|
|
|
|
|
return @projects = [project] if project?
|
2016-10-26 17:34:06 +00:00
|
|
|
|
|
|
|
projects =
|
|
|
|
if current_user && params[:authorized_only].presence && !current_user_related?
|
2019-05-07 11:08:25 +00:00
|
|
|
current_user.authorized_projects(min_access_level)
|
2016-10-26 17:34:06 +00:00
|
|
|
elsif group
|
2019-05-07 11:08:25 +00:00
|
|
|
find_group_projects
|
2016-10-26 17:34:06 +00:00
|
|
|
else
|
2019-05-07 11:08:25 +00:00
|
|
|
Project.public_or_visible_to_user(current_user, min_access_level)
|
2016-10-26 17:34:06 +00:00
|
|
|
end
|
2015-10-14 10:23:49 +00:00
|
|
|
|
2019-05-07 11:08:25 +00:00
|
|
|
@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil) # rubocop: disable CodeReuse/ActiveRecord
|
|
|
|
end
|
|
|
|
|
|
|
|
def find_group_projects
|
|
|
|
return Project.none unless group
|
|
|
|
|
|
|
|
if params[:include_subgroups]
|
|
|
|
Project.where(namespace_id: group.self_and_descendants) # rubocop: disable CodeReuse/ActiveRecord
|
|
|
|
else
|
|
|
|
group.projects
|
|
|
|
end.public_or_visible_to_user(current_user, min_access_level)
|
2015-05-25 11:36:28 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def search
|
|
|
|
params[:search].presence
|
|
|
|
end
|
|
|
|
|
|
|
|
def milestones?
|
|
|
|
params[:milestone_title].present?
|
|
|
|
end
|
|
|
|
|
|
|
|
def milestones
|
|
|
|
return @milestones if defined?(@milestones)
|
|
|
|
|
|
|
|
@milestones =
|
2015-10-06 16:57:13 +00:00
|
|
|
if milestones?
|
2017-07-07 15:08:49 +00:00
|
|
|
if project?
|
|
|
|
group_id = project.group&.id
|
|
|
|
project_id = project.id
|
|
|
|
end
|
|
|
|
|
|
|
|
group_id = group.id if group
|
2015-10-14 10:20:48 +00:00
|
|
|
|
2017-07-07 15:08:49 +00:00
|
|
|
search_params =
|
|
|
|
{ title: params[:milestone_title], project_ids: project_id, group_ids: group_id }
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
MilestonesFinder.new(search_params).execute # rubocop: disable CodeReuse/Finder
|
2015-05-25 11:36:28 +00:00
|
|
|
else
|
2016-07-23 23:28:12 +00:00
|
|
|
Milestone.none
|
2015-05-25 11:36:28 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2015-10-16 09:43:26 +00:00
|
|
|
def labels?
|
|
|
|
params[:label_name].present?
|
|
|
|
end
|
|
|
|
|
2015-10-19 09:46:22 +00:00
|
|
|
def filter_by_no_label?
|
2018-10-29 09:50:18 +00:00
|
|
|
downcased = label_names.map(&:downcase)
|
|
|
|
|
2019-06-05 20:24:38 +00:00
|
|
|
downcased.include?(FILTER_NONE)
|
2018-10-29 09:50:18 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_any_label?
|
|
|
|
label_names.map(&:downcase).include?(FILTER_ANY)
|
2015-10-16 09:43:26 +00:00
|
|
|
end
|
|
|
|
|
2016-02-19 05:27:41 +00:00
|
|
|
def labels
|
|
|
|
return @labels if defined?(@labels)
|
|
|
|
|
2016-10-17 18:55:46 +00:00
|
|
|
@labels =
|
|
|
|
if labels? && !filter_by_no_label?
|
2018-08-27 15:31:01 +00:00
|
|
|
LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true) # rubocop: disable CodeReuse/Finder
|
2016-10-17 18:55:46 +00:00
|
|
|
else
|
|
|
|
Label.none
|
2016-02-19 05:27:41 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2016-11-07 22:33:51 +00:00
|
|
|
def assignee_id?
|
2018-10-25 07:02:28 +00:00
|
|
|
params[:assignee_id].present?
|
2015-05-25 11:36:28 +00:00
|
|
|
end
|
|
|
|
|
2016-11-07 22:33:51 +00:00
|
|
|
def assignee_username?
|
2018-10-25 07:02:28 +00:00
|
|
|
params[:assignee_username].present?
|
2016-11-07 22:33:51 +00:00
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2015-05-25 11:36:28 +00:00
|
|
|
def assignee
|
|
|
|
return @assignee if defined?(@assignee)
|
|
|
|
|
2015-06-18 17:15:17 +00:00
|
|
|
@assignee =
|
2016-12-16 11:58:59 +00:00
|
|
|
if assignee_id?
|
2016-12-16 12:12:59 +00:00
|
|
|
User.find_by(id: params[:assignee_id])
|
2016-12-16 11:58:59 +00:00
|
|
|
elsif assignee_username?
|
2018-10-18 09:06:44 +00:00
|
|
|
User.find_by_username(params[:assignee_username])
|
2015-05-25 11:36:28 +00:00
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2015-05-25 11:36:28 +00:00
|
|
|
|
2016-11-07 22:33:51 +00:00
|
|
|
def author_id?
|
2016-12-16 11:58:59 +00:00
|
|
|
params[:author_id].present? && params[:author_id] != NONE
|
2015-05-25 11:36:28 +00:00
|
|
|
end
|
|
|
|
|
2016-11-07 22:33:51 +00:00
|
|
|
def author_username?
|
2016-12-16 11:58:59 +00:00
|
|
|
params[:author_username].present? && params[:author_username] != NONE
|
2016-11-07 22:33:51 +00:00
|
|
|
end
|
|
|
|
|
2016-12-16 14:28:18 +00:00
|
|
|
def no_author?
|
2017-01-11 01:51:57 +00:00
|
|
|
# author_id takes precedence over author_username
|
2016-12-16 14:28:18 +00:00
|
|
|
params[:author_id] == NONE || params[:author_username] == NONE
|
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2015-05-25 11:36:28 +00:00
|
|
|
def author
|
|
|
|
return @author if defined?(@author)
|
|
|
|
|
2015-06-18 17:15:17 +00:00
|
|
|
@author =
|
2016-12-16 12:12:59 +00:00
|
|
|
if author_id?
|
|
|
|
User.find_by(id: params[:author_id])
|
|
|
|
elsif author_username?
|
2018-10-18 09:06:44 +00:00
|
|
|
User.find_by_username(params[:author_username])
|
2015-05-25 11:36:28 +00:00
|
|
|
else
|
|
|
|
nil
|
|
|
|
end
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2015-05-25 11:36:28 +00:00
|
|
|
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
def use_cte_for_search?
|
|
|
|
strong_memoize(:use_cte_for_search) do
|
|
|
|
next false unless search
|
|
|
|
# Only simple unsorted & simple sorts can use CTE
|
|
|
|
next false if params[:sort].present? && !params[:sort].in?(klass.simple_sorts.keys)
|
2018-11-29 12:52:48 +00:00
|
|
|
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
attempt_group_search_optimizations? || attempt_project_search_optimizations?
|
2018-11-29 12:52:48 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2014-01-15 14:16:23 +00:00
|
|
|
private
|
|
|
|
|
2019-02-19 20:51:11 +00:00
|
|
|
def force_cte?
|
|
|
|
!!params[:force_cte]
|
|
|
|
end
|
|
|
|
|
2014-02-03 15:02:44 +00:00
|
|
|
def init_collection
|
2015-10-14 10:23:49 +00:00
|
|
|
klass.all
|
2014-02-03 15:02:44 +00:00
|
|
|
end
|
|
|
|
|
2018-11-29 12:52:48 +00:00
|
|
|
def attempt_group_search_optimizations?
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
params[:attempt_group_search_optimizations] &&
|
2019-02-19 20:51:11 +00:00
|
|
|
Feature.enabled?(:attempt_group_search_optimizations, default_enabled: true)
|
2018-11-29 12:52:48 +00:00
|
|
|
end
|
|
|
|
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
def attempt_project_search_optimizations?
|
|
|
|
params[:attempt_project_search_optimizations] &&
|
2019-05-21 12:19:35 +00:00
|
|
|
Feature.enabled?(:attempt_project_search_optimizations, default_enabled: true)
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
end
|
|
|
|
|
2018-10-01 11:45:15 +00:00
|
|
|
def count_key(value)
|
|
|
|
Array(value).last.to_sym
|
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2014-02-03 15:02:44 +00:00
|
|
|
def by_scope(items)
|
2017-09-18 17:29:17 +00:00
|
|
|
return items.none if current_user_related? && !current_user
|
|
|
|
|
2016-03-21 23:09:20 +00:00
|
|
|
case params[:scope]
|
2018-05-14 02:07:53 +00:00
|
|
|
when 'created_by_me', 'authored'
|
2014-02-10 13:04:52 +00:00
|
|
|
items.where(author_id: current_user.id)
|
2018-05-14 02:07:53 +00:00
|
|
|
when 'assigned_to_me'
|
2017-05-04 12:11:15 +00:00
|
|
|
items.assigned_to(current_user)
|
2014-01-15 14:16:23 +00:00
|
|
|
else
|
2016-03-21 23:09:20 +00:00
|
|
|
items
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2018-02-28 11:16:29 +00:00
|
|
|
def by_updated_at(items)
|
|
|
|
items = items.updated_after(params[:updated_after]) if params[:updated_after].present?
|
|
|
|
items = items.updated_before(params[:updated_before]) if params[:updated_before].present?
|
|
|
|
|
|
|
|
items
|
|
|
|
end
|
|
|
|
|
2019-01-17 17:49:07 +00:00
|
|
|
def by_closed_at(items)
|
|
|
|
items = items.closed_after(params[:closed_after]) if params[:closed_after].present?
|
|
|
|
items = items.closed_before(params[:closed_before]) if params[:closed_before].present?
|
|
|
|
|
|
|
|
items
|
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
def by_state(items)
|
2016-11-30 08:11:43 +00:00
|
|
|
case params[:state].to_s
|
|
|
|
when 'closed'
|
|
|
|
items.closed
|
|
|
|
when 'merged'
|
|
|
|
items.respond_to?(:merged) ? items.merged : items.closed
|
|
|
|
when 'opened'
|
|
|
|
items.opened
|
2018-06-26 19:30:29 +00:00
|
|
|
when 'locked'
|
|
|
|
items.where(state: 'locked')
|
2014-01-15 14:16:23 +00:00
|
|
|
else
|
2016-09-23 13:16:11 +00:00
|
|
|
items
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
|
|
|
|
def by_group(items)
|
2016-03-20 20:14:39 +00:00
|
|
|
# Selection by group is already covered by `by_project` and `projects`
|
2014-01-15 14:16:23 +00:00
|
|
|
items
|
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
def by_project(items)
|
2015-10-14 10:23:49 +00:00
|
|
|
items =
|
2015-11-11 11:50:36 +00:00
|
|
|
if project?
|
2018-04-06 10:24:33 +00:00
|
|
|
items.of_projects(projects).references_project
|
|
|
|
elsif projects
|
|
|
|
items.merge(projects.reorder(nil)).join_project
|
2015-10-14 10:23:49 +00:00
|
|
|
else
|
|
|
|
items.none
|
|
|
|
end
|
2014-01-15 14:16:23 +00:00
|
|
|
|
|
|
|
items
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
def by_search(items)
|
2018-06-05 17:31:32 +00:00
|
|
|
return items unless search
|
|
|
|
|
Extend CTE search optimisation to projects
When we use the `search` param on an `IssuableFinder`, we can run into
issues. We have trigram indexes to support these searches. On
GitLab.com, we often see Postgres's optimiser prioritise the (global)
trigram indexes over the index on `project_id`. For group and project
searches, we know that it will be quicker to filter by `project_id`
first, as it returns fewer rows in most cases.
For group issues search, we ran into this issue previously, and went
through the following iterations:
1. Use a CTE on the project IDs as an optimisation fence. This prevents
the planner from disregarding the index on `project_id`.
Unfortunately it breaks some types of sorting, like priority and
popularity, as they sort on a joined table.
2. Use a subquery for listing issues, and a CTE for counts. The subquery
- in the case of group lists - didn't help as much as the CTE, but
was faster than not including it. We can safely use a CTE for counts
as they don't have sorting.
Now, however, we're seeing the same issue in a project context. The
subquery doesn't help at all there (it would only return one row, after
all). In an attempt to keep total code complexity under control, this
commit removes the subquery optimisation and applies the CTE
optimisation only for sorts we know that are safe.
This means that for more complicated sorts (like priority and
popularity), the search will continue to be very slow. If this is a
high-priority issue, we can consider introducing further optimisations,
but this finder is already very complicated and additional complexity
has a cost.
The group CTE optimisation is controlled by the same feature flag as
before, `attempt_group_search_optimizations`, which is enabled by
default. The new project CTE optimisation is controlled by a new feature
flag, `attempt_project_search_optimizations`, which is disabled by
default.
2019-04-03 09:46:13 +00:00
|
|
|
if use_cte_for_search?
|
2018-06-05 17:31:32 +00:00
|
|
|
cte = Gitlab::SQL::RecursiveCTE.new(klass.table_name)
|
|
|
|
cte << items
|
|
|
|
|
|
|
|
items = klass.with(cte.to_arel).from(klass.table_name)
|
|
|
|
end
|
|
|
|
|
2019-06-13 00:08:44 +00:00
|
|
|
items.full_search(search, matched_columns: params[:in], use_minimum_char_limit: !use_cte_for_search?)
|
2017-02-17 18:28:32 +00:00
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2017-02-17 18:28:32 +00:00
|
|
|
def by_iids(items)
|
|
|
|
params[:iids].present? ? items.where(iid: params[:iids]) : items
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
def sort(items)
|
2015-11-11 11:50:36 +00:00
|
|
|
# Ensure we always have an explicit sort order (instead of inheriting
|
|
|
|
# multiple orders when combining ActiveRecord::Relation objects).
|
2018-04-04 09:19:47 +00:00
|
|
|
params[:sort] ? items.sort_by_attribute(params[:sort], excluded_labels: label_names) : items.reorder(id: :desc)
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2018-10-27 02:52:06 +00:00
|
|
|
def filter_by_no_assignee?
|
2019-06-05 20:24:38 +00:00
|
|
|
params[:assignee_id].to_s.downcase == FILTER_NONE
|
2018-10-27 02:52:06 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_any_assignee?
|
|
|
|
params[:assignee_id].to_s.downcase == FILTER_ANY
|
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2014-12-05 15:13:07 +00:00
|
|
|
def by_author(items)
|
2016-12-16 12:12:59 +00:00
|
|
|
if author
|
|
|
|
items = items.where(author_id: author.id)
|
2016-12-16 14:28:18 +00:00
|
|
|
elsif no_author?
|
|
|
|
items = items.where(author_id: nil)
|
2016-12-16 12:12:59 +00:00
|
|
|
elsif author_id? || author_username? # author not found
|
|
|
|
items = items.none
|
2014-12-05 15:13:07 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
items
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2014-12-05 15:13:07 +00:00
|
|
|
|
2019-04-07 18:35:16 +00:00
|
|
|
def by_assignee(items)
|
|
|
|
if filter_by_no_assignee?
|
|
|
|
items.unassigned
|
|
|
|
elsif filter_by_any_assignee?
|
|
|
|
items.assigned
|
|
|
|
elsif assignee
|
|
|
|
items.assigned_to(assignee)
|
|
|
|
elsif assignee_id? || assignee_username? # assignee not found
|
|
|
|
items.none
|
|
|
|
else
|
|
|
|
items
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: disable CodeReuse/ActiveRecord
|
2015-10-16 09:43:26 +00:00
|
|
|
def by_milestone(items)
|
2019-07-31 13:51:46 +00:00
|
|
|
return items unless milestones?
|
|
|
|
return items if filter_by_any_milestone?
|
|
|
|
|
|
|
|
if filter_by_no_milestone?
|
|
|
|
items.left_joins_milestones.where(milestone_id: nil)
|
|
|
|
elsif filter_by_upcoming_milestone?
|
|
|
|
upcoming_ids = Milestone.upcoming_ids(projects, related_groups)
|
|
|
|
items.left_joins_milestones.where(milestone_id: upcoming_ids)
|
|
|
|
elsif filter_by_started_milestone?
|
|
|
|
items.left_joins_milestones.merge(Milestone.started)
|
|
|
|
else
|
|
|
|
items.with_milestone(params[:milestone_title])
|
2015-10-16 09:43:26 +00:00
|
|
|
end
|
|
|
|
end
|
2018-08-27 15:31:01 +00:00
|
|
|
# rubocop: enable CodeReuse/ActiveRecord
|
2015-10-16 09:43:26 +00:00
|
|
|
|
2018-10-27 02:54:05 +00:00
|
|
|
def filter_by_no_milestone?
|
|
|
|
# Accepts `No Milestone` for compatibility
|
|
|
|
params[:milestone_title].to_s.downcase == FILTER_NONE || params[:milestone_title] == Milestone::None.title
|
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_any_milestone?
|
|
|
|
# Accepts `Any Milestone` for compatibility
|
|
|
|
params[:milestone_title].to_s.downcase == FILTER_ANY || params[:milestone_title] == Milestone::Any.title
|
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_upcoming_milestone?
|
|
|
|
params[:milestone_title] == Milestone::Upcoming.name
|
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_started_milestone?
|
|
|
|
params[:milestone_title] == Milestone::Started.name
|
|
|
|
end
|
|
|
|
|
2014-01-15 14:16:23 +00:00
|
|
|
def by_label(items)
|
2017-12-26 19:16:43 +00:00
|
|
|
return items unless labels?
|
|
|
|
|
|
|
|
items =
|
2015-10-19 09:46:22 +00:00
|
|
|
if filter_by_no_label?
|
2017-12-26 19:16:43 +00:00
|
|
|
items.without_label
|
2018-10-29 09:50:18 +00:00
|
|
|
elsif filter_by_any_label?
|
|
|
|
items.any_label
|
2015-10-06 16:57:13 +00:00
|
|
|
else
|
2017-12-26 19:16:43 +00:00
|
|
|
items.with_label(label_names, params[:sort])
|
2015-10-06 16:57:13 +00:00
|
|
|
end
|
2014-01-15 14:16:23 +00:00
|
|
|
|
2016-04-21 16:49:08 +00:00
|
|
|
items
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|
2014-01-31 16:06:13 +00:00
|
|
|
|
2017-08-30 07:48:55 +00:00
|
|
|
def by_my_reaction_emoji(items)
|
|
|
|
if params[:my_reaction_emoji].present? && current_user
|
2018-10-27 15:52:26 +00:00
|
|
|
items =
|
|
|
|
if filter_by_no_reaction?
|
|
|
|
items.not_awarded(current_user)
|
|
|
|
elsif filter_by_any_reaction?
|
|
|
|
items.awarded(current_user)
|
|
|
|
else
|
|
|
|
items.awarded(current_user, params[:my_reaction_emoji])
|
|
|
|
end
|
2017-08-30 07:48:55 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
items
|
|
|
|
end
|
|
|
|
|
2018-10-27 02:52:06 +00:00
|
|
|
def filter_by_no_reaction?
|
|
|
|
params[:my_reaction_emoji].to_s.downcase == FILTER_NONE
|
|
|
|
end
|
|
|
|
|
|
|
|
def filter_by_any_reaction?
|
|
|
|
params[:my_reaction_emoji].to_s.downcase == FILTER_ANY
|
|
|
|
end
|
|
|
|
|
2016-02-19 05:27:41 +00:00
|
|
|
def label_names
|
2016-03-14 09:46:26 +00:00
|
|
|
if labels?
|
|
|
|
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
|
|
|
|
else
|
|
|
|
[]
|
|
|
|
end
|
2016-02-19 05:27:41 +00:00
|
|
|
end
|
|
|
|
|
2016-11-27 10:33:15 +00:00
|
|
|
def by_non_archived(items)
|
|
|
|
params[:non_archived].present? ? items.non_archived : items
|
|
|
|
end
|
|
|
|
|
2014-10-27 09:02:20 +00:00
|
|
|
def current_user_related?
|
2018-05-14 02:07:53 +00:00
|
|
|
scope = params[:scope]
|
|
|
|
scope == 'created_by_me' || scope == 'authored' || scope == 'assigned_to_me'
|
2014-10-27 09:02:20 +00:00
|
|
|
end
|
2019-05-07 11:08:25 +00:00
|
|
|
|
|
|
|
def min_access_level
|
|
|
|
ProjectFeature.required_minimum_access_level(klass)
|
|
|
|
end
|
2014-01-15 14:16:23 +00:00
|
|
|
end
|