Commit e252b6f9 authored by Mario de la Ossa's avatar Mario de la Ossa

Add (IssuableFinder|IssuesFinder)::Params class

These classes clearly separate the finder methods from param helper
methods, making the IssuableFinder and IssuesFinder classes much
cleaner.
parent 713d19c2
...@@ -38,19 +38,14 @@ class IssuableFinder ...@@ -38,19 +38,14 @@ class IssuableFinder
include CreatedAtFilter include CreatedAtFilter
include Gitlab::Utils::StrongMemoize include Gitlab::Utils::StrongMemoize
requires_cross_project_access unless: -> { project? } requires_cross_project_access unless: -> { params.project? }
# This is used as a common filter for None / Any
FILTER_NONE = 'none'
FILTER_ANY = 'any'
# This is used in unassigning users
NONE = '0'
NEGATABLE_PARAMS_HELPER_KEYS = %i[include_subgroups in].freeze NEGATABLE_PARAMS_HELPER_KEYS = %i[include_subgroups in].freeze
attr_accessor :current_user, :params attr_accessor :current_user, :params
delegate(*%i[assignee milestones], to: :params)
class << self class << self
def scalar_params def scalar_params
@scalar_params ||= %i[ @scalar_params ||= %i[
...@@ -91,9 +86,13 @@ class IssuableFinder ...@@ -91,9 +86,13 @@ class IssuableFinder
end end
end end
def params_class
IssuableFinder::Params
end
def initialize(current_user, params = {}) def initialize(current_user, params = {})
@current_user = current_user @current_user = current_user
@params = params @params = params_class.new(params, current_user, klass)
end end
def execute def execute
...@@ -161,7 +160,7 @@ class IssuableFinder ...@@ -161,7 +160,7 @@ class IssuableFinder
# of a CTE. The CTE will not be used if the sort doesn't support it, # 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 # but will always be used for the counts here as we ignore sorting
# anyway. # anyway.
labels_count = label_names.any? ? label_names.count : 1 labels_count = params.label_names.any? ? params.label_names.count : 1
labels_count = 1 if use_cte_for_search? labels_count = 1 if use_cte_for_search?
finder.execute.reorder(nil).group(:state_id).count.each do |key, value| finder.execute.reorder(nil).group(:state_id).count.each do |key, value|
...@@ -174,192 +173,10 @@ class IssuableFinder ...@@ -174,192 +173,10 @@ class IssuableFinder
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def group
return @group if defined?(@group)
@group =
if params[:group_id].present?
Group.find(params[:group_id])
else
nil
end
end
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
Gitlab::ObjectHierarchy.new(current_user.authorized_groups, current_user.groups).all_objects
else
[]
end
end
def project?
params[:project_id].present?
end
def project
return @project if defined?(@project)
project = Project.find(params[:project_id])
project = nil unless Ability.allowed?(current_user, :"read_#{klass.to_ability_name}", project)
@project = project
end
def projects
return @projects if defined?(@projects)
return @projects = [project] if project?
projects =
if current_user && params[:authorized_only].presence && !current_user_related?
current_user.authorized_projects(min_access_level)
else
projects_public_or_visible_to_user
end
@projects = projects.with_feature_available_for_user(klass, current_user).reorder(nil) # rubocop: disable CodeReuse/ActiveRecord
end
def projects_public_or_visible_to_user
projects =
if group
if params[:projects]
find_group_projects.id_in(params[:projects])
else
find_group_projects
end
elsif params[:projects]
Project.id_in(params[:projects])
else
Project
end
projects.public_or_visible_to_user(current_user, min_access_level)
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
end
def search def search
params[:search].presence params[:search].presence
end end
def milestones?
params[:milestone_title].present?
end
def milestones
return @milestones if defined?(@milestones)
@milestones =
if milestones?
if project?
group_id = project.group&.id
project_id = project.id
end
group_id = group.id if group
search_params =
{ title: params[:milestone_title], project_ids: project_id, group_ids: group_id }
MilestonesFinder.new(search_params).execute # rubocop: disable CodeReuse/Finder
else
Milestone.none
end
end
def labels?
params[:label_name].present?
end
def filter_by_no_label?
downcased = label_names.map(&:downcase)
downcased.include?(FILTER_NONE)
end
def filter_by_any_label?
label_names.map(&:downcase).include?(FILTER_ANY)
end
def labels
return @labels if defined?(@labels)
@labels =
if labels? && !filter_by_no_label?
LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true) # rubocop: disable CodeReuse/Finder
else
Label.none
end
end
def assignee_id?
params[:assignee_id].present?
end
def assignee_username?
params[:assignee_username].present?
end
def assignee
assignees.first
end
# rubocop: disable CodeReuse/ActiveRecord
def assignees
strong_memoize(:assignees) do
if assignee_id?
User.where(id: params[:assignee_id])
elsif assignee_username?
User.where(username: params[:assignee_username])
else
User.none
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def author_id?
params[:author_id].present? && params[:author_id] != NONE
end
def author_username?
params[:author_username].present? && params[:author_username] != NONE
end
def no_author?
# author_id takes precedence over author_username
params[:author_id] == NONE || params[:author_username] == NONE
end
# rubocop: disable CodeReuse/ActiveRecord
def author
return @author if defined?(@author)
@author =
if author_id?
User.find_by(id: params[:author_id])
elsif author_username?
User.find_by_username(params[:author_username])
else
nil
end
end
# rubocop: enable CodeReuse/ActiveRecord
def use_cte_for_search? def use_cte_for_search?
strong_memoize(:use_cte_for_search) do strong_memoize(:use_cte_for_search) do
next false unless search next false unless search
...@@ -370,10 +187,6 @@ class IssuableFinder ...@@ -370,10 +187,6 @@ class IssuableFinder
end end
end end
def releases?
params[:release_tag].present?
end
private private
def force_cte? def force_cte?
...@@ -431,7 +244,7 @@ class IssuableFinder ...@@ -431,7 +244,7 @@ class IssuableFinder
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_scope(items) def by_scope(items)
return items.none if current_user_related? && !current_user return items.none if params.current_user_related? && !current_user
case params[:scope] case params[:scope]
when 'created_by_me', 'authored' when 'created_by_me', 'authored'
...@@ -480,16 +293,13 @@ class IssuableFinder ...@@ -480,16 +293,13 @@ class IssuableFinder
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_project(items) def by_project(items)
items = if params.project?
if project? items.of_projects(params.projects).references_project
items.of_projects(projects).references_project elsif params.projects
elsif projects items.merge(params.projects.reorder(nil)).join_project
items.merge(projects.reorder(nil)).join_project
else else
items.none items.none
end end
items
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
...@@ -519,42 +329,34 @@ class IssuableFinder ...@@ -519,42 +329,34 @@ class IssuableFinder
def sort(items) def sort(items)
# Ensure we always have an explicit sort order (instead of inheriting # Ensure we always have an explicit sort order (instead of inheriting
# multiple orders when combining ActiveRecord::Relation objects). # multiple orders when combining ActiveRecord::Relation objects).
params[:sort] ? items.sort_by_attribute(params[:sort], excluded_labels: label_names) : items.reorder(id: :desc) params[:sort] ? items.sort_by_attribute(params[:sort], excluded_labels: params.label_names) : items.reorder(id: :desc)
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def filter_by_no_assignee?
params[:assignee_id].to_s.downcase == FILTER_NONE
end
def filter_by_any_assignee?
params[:assignee_id].to_s.downcase == FILTER_ANY
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_author(items) def by_author(items)
if author if params.author
items = items.where(author_id: author.id) items.where(author_id: params.author.id)
elsif no_author? elsif params.no_author?
items = items.where(author_id: nil) items.where(author_id: nil)
elsif author_id? || author_username? # author not found elsif params.author_id? || params.author_username? # author not found
items = items.none items.none
end else
items items
end end
end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def by_assignee(items) def by_assignee(items)
return items.assigned_to(assignees) if not_query? && assignees.any? return items.assigned_to(params.assignees) if not_query? && params.assignees.any?
if filter_by_no_assignee? if params.filter_by_no_assignee?
items.unassigned items.unassigned
elsif filter_by_any_assignee? elsif params.filter_by_any_assignee?
items.assigned items.assigned
elsif assignee elsif params.assignee
items.assigned_to(assignee) items.assigned_to(params.assignee)
elsif assignee_id? || assignee_username? # assignee not found elsif params.assignee_id? || params.assignee_username? # assignee not found
items.none items.none
else else
items items
...@@ -563,122 +365,63 @@ class IssuableFinder ...@@ -563,122 +365,63 @@ class IssuableFinder
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_milestone(items) def by_milestone(items)
if milestones? return items unless params.milestones?
if filter_by_no_milestone?
items = items.left_joins_milestones.where(milestone_id: [-1, nil]) if params.filter_by_no_milestone?
elsif filter_by_any_milestone? items.left_joins_milestones.where(milestone_id: [-1, nil])
items = items.any_milestone elsif params.filter_by_any_milestone?
elsif filter_by_upcoming_milestone? items.any_milestone
upcoming_ids = Milestone.upcoming_ids(projects, related_groups) elsif params.filter_by_upcoming_milestone?
items = items.left_joins_milestones.where(milestone_id: upcoming_ids) upcoming_ids = Milestone.upcoming_ids(params.projects, params.related_groups)
elsif filter_by_started_milestone? items.left_joins_milestones.where(milestone_id: upcoming_ids)
items = items.left_joins_milestones.merge(Milestone.started) elsif params.filter_by_started_milestone?
items.left_joins_milestones.merge(Milestone.started)
else else
items = items.with_milestone(params[:milestone_title]) items.with_milestone(params[:milestone_title])
end end
end end
items
end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def by_release(items) def by_release(items)
return items unless releases? return items unless params.releases?
if filter_by_no_release? if params.filter_by_no_release?
items.without_release items.without_release
elsif filter_by_any_release? elsif params.filter_by_any_release?
items.any_release items.any_release
else else
items.with_release(params[:release_tag], params[:project_id]) items.with_release(params[:release_tag], params[:project_id])
end end
end end
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
def filter_by_no_release?
params[:release_tag].to_s.downcase == FILTER_NONE
end
def filter_by_any_release?
params[:release_tag].to_s.downcase == FILTER_ANY
end
def by_label(items) def by_label(items)
return items unless labels? return items unless params.labels?
items = if params.filter_by_no_label?
if filter_by_no_label?
items.without_label items.without_label
elsif filter_by_any_label? elsif params.filter_by_any_label?
items.any_label items.any_label
else else
items.with_label(label_names, params[:sort], not_query: not_query?) items.with_label(params.label_names, params[:sort], not_query: not_query?)
end end
items
end end
def by_my_reaction_emoji(items) def by_my_reaction_emoji(items)
if params[:my_reaction_emoji].present? && current_user return items unless params[:my_reaction_emoji] && current_user
items =
if filter_by_no_reaction? if params.filter_by_no_reaction?
items.not_awarded(current_user) items.not_awarded(current_user)
elsif filter_by_any_reaction? elsif params.filter_by_any_reaction?
items.awarded(current_user) items.awarded(current_user)
else else
items.awarded(current_user, params[:my_reaction_emoji]) items.awarded(current_user, params[:my_reaction_emoji])
end end
end end
items
end
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
def label_names
if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
else
[]
end
end
def by_non_archived(items) def by_non_archived(items)
params[:non_archived].present? ? items.non_archived : items params[:non_archived].present? ? items.non_archived : items
end end
def current_user_related?
scope = params[:scope]
scope == 'created_by_me' || scope == 'authored' || scope == 'assigned_to_me'
end
def min_access_level
ProjectFeature.required_minimum_access_level(klass)
end
def not_query? def not_query?
!!params[:not_query] !!params[:not_query]
end end
......
# frozen_string_literal: true
class IssuableFinder
class Params < SimpleDelegator
include Gitlab::Utils::StrongMemoize
# This is used as a common filter for None / Any
FILTER_NONE = 'none'
FILTER_ANY = 'any'
# This is used in unassigning users
NONE = '0'
alias_method :params, :__getobj__
attr_accessor :current_user, :klass
def initialize(params, current_user, klass)
@current_user = current_user
@klass = klass
# We turn the params into a HashWithIndifferentAccess. We must use #to_h first because sometimes
# we get ActionController::Params and IssuableFinder::Params objects here.
super(params.to_h.with_indifferent_access)
end
def present?
params.present?
end
def author_id?
params[:author_id].present? && params[:author_id] != NONE
end
def author_username?
params[:author_username].present? && params[:author_username] != NONE
end
def no_author?
# author_id takes precedence over author_username
params[:author_id] == NONE || params[:author_username] == NONE
end
def filter_by_no_assignee?
params[:assignee_id].to_s.downcase == FILTER_NONE
end
def filter_by_any_assignee?
params[:assignee_id].to_s.downcase == FILTER_ANY
end
def filter_by_no_label?
downcased = label_names.map(&:downcase)
downcased.include?(FILTER_NONE)
end
def filter_by_any_label?
label_names.map(&:downcase).include?(FILTER_ANY)
end
def labels?
params[:label_name].present?
end
def milestones?
params[:milestone_title].present?
end
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
def filter_by_no_release?
params[:release_tag].to_s.downcase == FILTER_NONE
end
def filter_by_any_release?
params[:release_tag].to_s.downcase == FILTER_ANY
end
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
def releases?
params[:release_tag].present?
end
def project?
params[:project_id].present?
end
def group
strong_memoize(:group) do
if params[:group_id].present?
Group.find(params[:group_id])
else
nil
end
end
end
def related_groups
if project? && project&.group && Ability.allowed?(current_user, :read_group, project.group)
project.group.self_and_ancestors
elsif group
[group]
elsif current_user
Gitlab::ObjectHierarchy.new(current_user.authorized_groups, current_user.groups).all_objects
else
[]
end
end
def project
strong_memoize(:project) do
project = Project.find(params[:project_id])
project = nil unless Ability.allowed?(current_user, :"read_#{klass.to_ability_name}", project)
project
end
end
def projects
strong_memoize(:projects) do
next [project] if project?
projects =
if current_user && params[:authorized_only].presence && !current_user_related?
current_user.authorized_projects(min_access_level)
else
projects_public_or_visible_to_user
end
projects.with_feature_available_for_user(klass, current_user).reorder(nil) # rubocop: disable CodeReuse/ActiveRecord
end
end
# rubocop: disable CodeReuse/ActiveRecord
def author
strong_memoize(:author) do
if author_id?
User.find_by(id: params[:author_id])
elsif author_username?
User.find_by_username(params[:author_username])
else
nil
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
# rubocop: disable CodeReuse/ActiveRecord
def assignees
strong_memoize(:assignees) do
if assignee_id?
User.where(id: params[:assignee_id])
elsif assignee_username?
User.where(username: params[:assignee_username])
else
User.none
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def assignee
assignees.first
end
def label_names
if labels?
params[:label_name].is_a?(String) ? params[:label_name].split(',') : params[:label_name]
else
[]
end
end
def labels
strong_memoize(:labels) do
if labels? && !filter_by_no_label?
LabelsFinder.new(current_user, project_ids: projects, title: label_names).execute(skip_authorization: true) # rubocop: disable CodeReuse/Finder
else
Label.none
end
end
end
def milestones
strong_memoize(:milestones) do
if milestones?
if project?
group_id = project.group&.id
project_id = project.id
end
group_id = group.id if group
search_params =
{ title: params[:milestone_title], project_ids: project_id, group_ids: group_id }
MilestonesFinder.new(search_params).execute # rubocop: disable CodeReuse/Finder
else
Milestone.none
end
end
end
def current_user_related?
scope = params[:scope]
scope == 'created_by_me' || scope == 'authored' || scope == 'assigned_to_me'
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
end
# We use Hash#merge in a few places, so let's support it
def merge(other)
self.class.new(params.merge(other), current_user, klass)
end
# Just for symmetry, and in case someone tries to use it
def merge!(other)
params.merge!(other)
end
private
def projects_public_or_visible_to_user
projects =
if group
if params[:projects]
find_group_projects.id_in(params[:projects])
else
find_group_projects
end
elsif params[:projects]
Project.id_in(params[:projects])
else
Project
end
projects.public_or_visible_to_user(current_user, min_access_level)
end
def min_access_level
ProjectFeature.required_minimum_access_level(klass)
end
def method_missing(method_name, *args, &block)
if method_name[-1] == '?'
params[method_name[0..-2].to_sym].present?
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
method_name[-1] == '?'
end
end
end
...@@ -38,10 +38,14 @@ class IssuesFinder < IssuableFinder ...@@ -38,10 +38,14 @@ class IssuesFinder < IssuableFinder
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def params_class
IssuesFinder::Params
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def with_confidentiality_access_check def with_confidentiality_access_check
return Issue.all if user_can_see_all_confidential_issues? return Issue.all if params.user_can_see_all_confidential_issues?
return Issue.where('issues.confidential IS NOT TRUE') if user_cannot_see_confidential_issues? return Issue.where('issues.confidential IS NOT TRUE') if params.user_cannot_see_confidential_issues?
Issue.where(' Issue.where('
issues.confidential IS NOT TRUE issues.confidential IS NOT TRUE
...@@ -57,17 +61,13 @@ class IssuesFinder < IssuableFinder ...@@ -57,17 +61,13 @@ class IssuesFinder < IssuableFinder
private private
def init_collection def init_collection
if public_only? if params.public_only?
Issue.public_only Issue.public_only
else else
with_confidentiality_access_check with_confidentiality_access_check
end end
end end
def public_only?
params.fetch(:public_only, false)
end
def filter_items(items) def filter_items(items)
issues = super issues = super
issues = by_due_date(issues) issues = by_due_date(issues)
...@@ -82,67 +82,19 @@ class IssuesFinder < IssuableFinder ...@@ -82,67 +82,19 @@ class IssuesFinder < IssuableFinder
end end
def by_due_date(items) def by_due_date(items)
if due_date? return items unless params.due_date?
if filter_by_no_due_date?
items = items.without_due_date if params.filter_by_no_due_date?
elsif filter_by_overdue? items.without_due_date
items = items.due_before(Date.today) elsif params.filter_by_overdue?
elsif filter_by_due_this_week? items.due_before(Date.today)
items = items.due_between(Date.today.beginning_of_week, Date.today.end_of_week) elsif params.filter_by_due_this_week?
elsif filter_by_due_this_month? items.due_between(Date.today.beginning_of_week, Date.today.end_of_week)
items = items.due_between(Date.today.beginning_of_month, Date.today.end_of_month) elsif params.filter_by_due_this_month?
elsif filter_by_due_next_month_and_previous_two_weeks? items.due_between(Date.today.beginning_of_month, Date.today.end_of_month)
items = items.due_between(Date.today - 2.weeks, (Date.today + 1.month).end_of_month) elsif params.filter_by_due_next_month_and_previous_two_weeks?
end items.due_between(Date.today - 2.weeks, (Date.today + 1.month).end_of_month)
end
items
end end
def filter_by_no_due_date?
due_date? && params[:due_date] == Issue::NoDueDate.name
end
def filter_by_overdue?
due_date? && params[:due_date] == Issue::Overdue.name
end
def filter_by_due_this_week?
due_date? && params[:due_date] == Issue::DueThisWeek.name
end
def filter_by_due_this_month?
due_date? && params[:due_date] == Issue::DueThisMonth.name
end
def filter_by_due_next_month_and_previous_two_weeks?
due_date? && params[:due_date] == Issue::DueNextMonthAndPreviousTwoWeeks.name
end
def due_date?
params[:due_date].present?
end
def user_can_see_all_confidential_issues?
return @user_can_see_all_confidential_issues if defined?(@user_can_see_all_confidential_issues)
return @user_can_see_all_confidential_issues = false if current_user.blank?
return @user_can_see_all_confidential_issues = true if current_user.can_read_all_resources?
@user_can_see_all_confidential_issues =
if project? && project
project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
elsif group
group.max_member_access_for_user(current_user) >= CONFIDENTIAL_ACCESS_LEVEL
else
false
end
end
def user_cannot_see_confidential_issues?
return false if user_can_see_all_confidential_issues?
current_user.blank?
end end
end end
......
# frozen_string_literal: true
class IssuesFinder
class Params < IssuableFinder::Params
def public_only?
params.fetch(:public_only, false)
end
def filter_by_no_due_date?
due_date? && params[:due_date] == Issue::NoDueDate.name
end
def filter_by_overdue?
due_date? && params[:due_date] == Issue::Overdue.name
end
def filter_by_due_this_week?
due_date? && params[:due_date] == Issue::DueThisWeek.name
end
def filter_by_due_this_month?
due_date? && params[:due_date] == Issue::DueThisMonth.name
end
def filter_by_due_next_month_and_previous_two_weeks?
due_date? && params[:due_date] == Issue::DueNextMonthAndPreviousTwoWeeks.name
end
def user_can_see_all_confidential_issues?
return @user_can_see_all_confidential_issues if defined?(@user_can_see_all_confidential_issues)
return @user_can_see_all_confidential_issues = false if current_user.blank?
return @user_can_see_all_confidential_issues = true if current_user.can_read_all_resources?
@user_can_see_all_confidential_issues =
if project? && project
project.team.max_member_access(current_user.id) >= CONFIDENTIAL_ACCESS_LEVEL
elsif group
group.max_member_access_for_user(current_user) >= CONFIDENTIAL_ACCESS_LEVEL
else
false
end
end
def user_cannot_see_confidential_issues?
return false if user_can_see_all_confidential_issues?
current_user.blank?
end
end
end
IssuableFinder::Params.prepend_if_ee('EE::IssuesFinder::Params')
...@@ -21,7 +21,7 @@ module Issuable ...@@ -21,7 +21,7 @@ module Issuable
params.delete(key) unless params[key].present? params.delete(key) unless params[key].present?
end end
if params[:assignee_ids] == [IssuableFinder::NONE.to_s] if params[:assignee_ids] == [IssuableFinder::Params::NONE.to_s]
params[:assignee_ids] = [] params[:assignee_ids] = []
end end
......
...@@ -46,7 +46,7 @@ class IssuableBaseService < BaseService ...@@ -46,7 +46,7 @@ class IssuableBaseService < BaseService
assignee_ids = params[:assignee_ids].select { |assignee_id| assignee_can_read?(issuable, assignee_id) } assignee_ids = params[:assignee_ids].select { |assignee_id| assignee_can_read?(issuable, assignee_id) }
if params[:assignee_ids].map(&:to_s) == [IssuableFinder::NONE] if params[:assignee_ids].map(&:to_s) == [IssuableFinder::Params::NONE]
params[:assignee_ids] = [] params[:assignee_ids] = []
elsif assignee_ids.any? elsif assignee_ids.any?
params[:assignee_ids] = assignee_ids params[:assignee_ids] = assignee_ids
...@@ -70,7 +70,7 @@ class IssuableBaseService < BaseService ...@@ -70,7 +70,7 @@ class IssuableBaseService < BaseService
milestone_id = params[:milestone_id] milestone_id = params[:milestone_id]
return unless milestone_id return unless milestone_id
params[:milestone_id] = '' if milestone_id == IssuableFinder::NONE params[:milestone_id] = '' if milestone_id == IssuableFinder::Params::NONE
groups = project.group&.self_and_ancestors&.select(:id) groups = project.group&.self_and_ancestors&.select(:id)
milestone = milestone =
......
...@@ -25,11 +25,11 @@ module EE ...@@ -25,11 +25,11 @@ module EE
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def by_weight(items) def by_weight(items)
return items unless weights? return items unless params.weights?
if filter_by_no_weight? if params.filter_by_no_weight?
items.where(weight: [-1, nil]) items.where(weight: [-1, nil])
elsif filter_by_any_weight? elsif params.filter_by_any_weight?
items.where.not(weight: [-1, nil]) items.where.not(weight: [-1, nil])
else else
items.where(weight: params[:weight]) items.where(weight: params[:weight])
...@@ -37,26 +37,10 @@ module EE ...@@ -37,26 +37,10 @@ module EE
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def weights?
params[:weight].present? && params[:weight] != ::Issue::WEIGHT_ALL
end
def filter_by_no_weight?
params[:weight].to_s.downcase == ::IssuesFinder::FILTER_NONE
end
def filter_by_any_weight?
params[:weight].to_s.downcase == ::IssuesFinder::FILTER_ANY
end
def assignee_ids?
params[:assignee_ids].present?
end
override :by_assignee override :by_assignee
def by_assignee(items) def by_assignee(items)
if assignees.any? && !not_query? if params.assignees.any? && !not_query?
assignees.each do |assignee| params.assignees.each do |assignee|
items = items.assigned_to(assignee) items = items.assigned_to(assignee)
end end
...@@ -66,42 +50,13 @@ module EE ...@@ -66,42 +50,13 @@ module EE
super super
end end
override :assignees
# rubocop: disable CodeReuse/ActiveRecord
def assignees
strong_memoize(:assignees) do
if assignee_ids?
::User.where(id: params[:assignee_ids])
else
super
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def by_epic?
params[:epic_id].present?
end
def filter_by_no_epic?
params[:epic_id].to_s.downcase == ::IssuesFinder::FILTER_NONE
end
def epics
if params[:include_subepics]
::Gitlab::ObjectHierarchy.new(::Epic.for_ids(params[:epic_id])).base_and_descendants.select(:id)
else
params[:epic_id]
end
end
def by_epic(items) def by_epic(items)
return items unless by_epic? return items unless params.by_epic?
if filter_by_no_epic? if params.filter_by_no_epic?
items.no_epic items.no_epic
else else
items.in_epics(epics) items.in_epics(params.epics)
end end
end end
end end
......
# frozen_string_literal: true
module EE
module IssuesFinder
module Params
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
def by_epic?
params[:epic_id].present?
end
def filter_by_no_epic?
params[:epic_id].to_s.downcase == ::IssuableFinder::Params::FILTER_NONE
end
def weights?
params[:weight].present? && params[:weight] != ::Issue::WEIGHT_ALL
end
def filter_by_no_weight?
params[:weight].to_s.downcase == ::IssuableFinder::Params::FILTER_NONE
end
def filter_by_any_weight?
params[:weight].to_s.downcase == ::IssuableFinder::Params::FILTER_ANY
end
override :assignees
# rubocop: disable CodeReuse/ActiveRecord
def assignees
strong_memoize(:assignees) do
if assignee_ids?
::User.where(id: params[:assignee_ids])
else
super
end
end
end
# rubocop: enable CodeReuse/ActiveRecord
def epics
if params[:include_subepics]
::Gitlab::ObjectHierarchy.new(::Epic.for_ids(params[:epic_id])).base_and_descendants.select(:id)
else
params[:epic_id]
end
end
end
end
end
...@@ -42,14 +42,14 @@ module MergeRequests ...@@ -42,14 +42,14 @@ module MergeRequests
# #
# @return [Boolean] whether special condition "None" is being used # @return [Boolean] whether special condition "None" is being used
def by_no_approvals? def by_no_approvals?
includes_special_label?(IssuableFinder::FILTER_NONE) includes_special_label?(IssuableFinder::Params::FILTER_NONE)
end end
# Is param using special condition: "Any" ? # Is param using special condition: "Any" ?
# #
# @return [Boolean] whether special condition "Any"" is being used # @return [Boolean] whether special condition "Any"" is being used
def by_any_approvals? def by_any_approvals?
includes_special_label?(IssuableFinder::FILTER_ANY) includes_special_label?(IssuableFinder::Params::FILTER_ANY)
end end
# Check if we have the special label in ids or usernames field # Check if we have the special label in ids or usernames field
......
...@@ -30,11 +30,11 @@ module MergeRequests ...@@ -30,11 +30,11 @@ module MergeRequests
private private
def by_no_approvers? def by_no_approvers?
includes_custom_label?(IssuableFinder::FILTER_NONE) includes_custom_label?(IssuableFinder::Params::FILTER_NONE)
end end
def by_any_approvers? def by_any_approvers?
includes_custom_label?(IssuableFinder::FILTER_ANY) includes_custom_label?(IssuableFinder::Params::FILTER_ANY)
end end
def includes_custom_label?(label) def includes_custom_label?(label)
......
...@@ -19,7 +19,7 @@ class MergeRequestsComplianceFinder < MergeRequestsFinder ...@@ -19,7 +19,7 @@ class MergeRequestsComplianceFinder < MergeRequestsFinder
.limit(1) .limit(1)
.to_sql .to_sql
sql = find_group_projects.arel.as('projects').to_sql sql = params.find_group_projects.arel.as('projects').to_sql
records = Project records = Project
.select('projects.id, events.target_id as merge_request_id') .select('projects.id, events.target_id as merge_request_id')
.from([Arel.sql("#{sql} JOIN LATERAL (#{lateral}) #{Event.table_name} ON true")]) .from([Arel.sql("#{sql} JOIN LATERAL (#{lateral}) #{Event.table_name} ON true")])
......
...@@ -87,7 +87,7 @@ describe IssuesFinder do ...@@ -87,7 +87,7 @@ describe IssuesFinder do
let_it_be(:issue_subepic) { create(:issue, project: project1, epic: sub_epic) } let_it_be(:issue_subepic) { create(:issue, project: project1, epic: sub_epic) }
context 'filter issues with no epic' do context 'filter issues with no epic' do
let(:params) { { epic_id: ::IssuesFinder::FILTER_NONE } } let(:params) { { epic_id: ::IssuableFinder::Params::FILTER_NONE } }
it 'returns filtered issues' do it 'returns filtered issues' do
expect(issues).to contain_exactly(issue1, issue2, issue3, issue4) expect(issues).to contain_exactly(issue1, issue2, issue3, issue4)
......
...@@ -315,25 +315,25 @@ describe API::Epics do ...@@ -315,25 +315,25 @@ describe API::Epics do
end end
it 'returns an array of epics with any label' do it 'returns an array of epics with any label' do
get api(url), params: { labels: IssuesFinder::FILTER_ANY } get api(url), params: { labels: IssuableFinder::Params::FILTER_ANY }
expect_paginated_array_response(epic2.id) expect_paginated_array_response(epic2.id)
end end
it 'returns an array of epics with any label with labels param as array' do it 'returns an array of epics with any label with labels param as array' do
get api(url), params: { labels: [IssuesFinder::FILTER_ANY] } get api(url), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
expect_paginated_array_response(epic2.id) expect_paginated_array_response(epic2.id)
end end
it 'returns an array of epics with no label' do it 'returns an array of epics with no label' do
get api(url), params: { labels: IssuesFinder::FILTER_NONE } get api(url), params: { labels: IssuableFinder::Params::FILTER_NONE }
expect_paginated_array_response(epic.id) expect_paginated_array_response(epic.id)
end end
it 'returns an array of epics with no label with labels param as array' do it 'returns an array of epics with no label with labels param as array' do
get api(url), params: { labels: [IssuesFinder::FILTER_NONE] } get api(url), params: { labels: [IssuableFinder::Params::FILTER_NONE] }
expect_paginated_array_response(epic.id) expect_paginated_array_response(epic.id)
end end
......
...@@ -38,7 +38,7 @@ module API ...@@ -38,7 +38,7 @@ module API
value = params[attr_name] value = params[attr_name]
return if value.is_a?(Integer) || return if value.is_a?(Integer) ||
[IssuableFinder::FILTER_NONE, IssuableFinder::FILTER_ANY].include?(value.to_s.downcase) [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
message: "should be an integer, 'None' or 'Any'" message: "should be an integer, 'None' or 'Any'"
...@@ -50,7 +50,7 @@ module API ...@@ -50,7 +50,7 @@ module API
value = params[attr_name] value = params[attr_name]
return if value.is_a?(Array) || return if value.is_a?(Array) ||
[IssuableFinder::FILTER_NONE, IssuableFinder::FILTER_ANY].include?(value.to_s.downcase) [IssuableFinder::Params::FILTER_NONE, IssuableFinder::Params::FILTER_ANY].include?(value.to_s.downcase)
raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)], raise Grape::Exceptions::Validation, params: [@scope.full_name(attr_name)],
message: "should be an array, 'None' or 'Any'" message: "should be an array, 'None' or 'Any'"
......
...@@ -24,7 +24,7 @@ describe 'User filters issues' do ...@@ -24,7 +24,7 @@ describe 'User filters issues' do
let(:issue) { @issue } let(:issue) { @issue }
it 'allows filtering by issues with no specified assignee' do it 'allows filtering by issues with no specified assignee' do
visit project_issues_path(project, assignee_id: IssuableFinder::FILTER_NONE) visit project_issues_path(project, assignee_id: IssuableFinder::Params::FILTER_NONE)
expect(page).to have_content 'foobar' expect(page).to have_content 'foobar'
expect(page).not_to have_content 'barbaz' expect(page).not_to have_content 'barbaz'
......
...@@ -35,7 +35,7 @@ describe 'Merge requests > User lists merge requests' do ...@@ -35,7 +35,7 @@ describe 'Merge requests > User lists merge requests' do
end end
it 'filters on no assignee' do it 'filters on no assignee' do
visit_merge_requests(project, assignee_id: IssuableFinder::FILTER_NONE) visit_merge_requests(project, assignee_id: IssuableFinder::Params::FILTER_NONE)
expect(current_path).to eq(project_merge_requests_path(project)) expect(current_path).to eq(project_merge_requests_path(project))
expect(page).to have_content 'merge-test' expect(page).to have_content 'merge-test'
......
...@@ -429,7 +429,7 @@ describe IssuesFinder do ...@@ -429,7 +429,7 @@ describe IssuesFinder do
end end
context 'filtering by no label' do context 'filtering by no label' do
let(:params) { { label_name: described_class::FILTER_NONE } } let(:params) { { label_name: described_class::Params::FILTER_NONE } }
it 'returns issues with no labels' do it 'returns issues with no labels' do
expect(issues).to contain_exactly(issue1, issue4) expect(issues).to contain_exactly(issue1, issue4)
...@@ -437,7 +437,7 @@ describe IssuesFinder do ...@@ -437,7 +437,7 @@ describe IssuesFinder do
end end
context 'filtering by any label' do context 'filtering by any label' do
let(:params) { { label_name: described_class::FILTER_ANY } } let(:params) { { label_name: described_class::Params::FILTER_ANY } }
it 'returns issues that have one or more label' do it 'returns issues that have one or more label' do
create_list(:label_link, 2, label: create(:label, project: project2), target: issue3) create_list(:label_link, 2, label: create(:label, project: project2), target: issue3)
......
...@@ -51,11 +51,11 @@ describe Resolvers::IssuesResolver do ...@@ -51,11 +51,11 @@ describe Resolvers::IssuesResolver do
end end
it 'filters by any assignee' do it 'filters by any assignee' do
expect(resolve_issues(assignee_id: IssuableFinder::FILTER_ANY)).to contain_exactly(issue2) expect(resolve_issues(assignee_id: IssuableFinder::Params::FILTER_ANY)).to contain_exactly(issue2)
end end
it 'filters by no assignee' do it 'filters by no assignee' do
expect(resolve_issues(assignee_id: IssuableFinder::FILTER_NONE)).to contain_exactly(issue1) expect(resolve_issues(assignee_id: IssuableFinder::Params::FILTER_NONE)).to contain_exactly(issue1)
end end
it 'filters by labels' do it 'filters by labels' do
......
...@@ -475,27 +475,27 @@ describe API::Issues do ...@@ -475,27 +475,27 @@ describe API::Issues do
end end
it 'returns an array of group issues with any label' do it 'returns an array of group issues with any label' do
get api(base_url, user), params: { labels: IssuesFinder::FILTER_ANY } get api(base_url, user), params: { labels: IssuableFinder::Params::FILTER_ANY }
expect_paginated_array_response(group_issue.id) expect_paginated_array_response(group_issue.id)
expect(json_response.first['id']).to eq(group_issue.id) expect(json_response.first['id']).to eq(group_issue.id)
end end
it 'returns an array of group issues with any label with labels param as array' do it 'returns an array of group issues with any label with labels param as array' do
get api(base_url, user), params: { labels: [IssuesFinder::FILTER_ANY] } get api(base_url, user), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
expect_paginated_array_response(group_issue.id) expect_paginated_array_response(group_issue.id)
expect(json_response.first['id']).to eq(group_issue.id) expect(json_response.first['id']).to eq(group_issue.id)
end end
it 'returns an array of group issues with no label' do it 'returns an array of group issues with no label' do
get api(base_url, user), params: { labels: IssuesFinder::FILTER_NONE } get api(base_url, user), params: { labels: IssuableFinder::Params::FILTER_NONE }
expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id]) expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id])
end end
it 'returns an array of group issues with no label with labels param as array' do it 'returns an array of group issues with no label with labels param as array' do
get api(base_url, user), params: { labels: [IssuesFinder::FILTER_NONE] } get api(base_url, user), params: { labels: [IssuableFinder::Params::FILTER_NONE] }
expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id]) expect_paginated_array_response([group_closed_issue.id, group_confidential_issue.id])
end end
......
...@@ -350,25 +350,25 @@ describe API::Issues do ...@@ -350,25 +350,25 @@ describe API::Issues do
end end
it 'returns an array of project issues with any label' do it 'returns an array of project issues with any label' do
get api("#{base_url}/issues", user), params: { labels: IssuesFinder::FILTER_ANY } get api("#{base_url}/issues", user), params: { labels: IssuableFinder::Params::FILTER_ANY }
expect_paginated_array_response(issue.id) expect_paginated_array_response(issue.id)
end end
it 'returns an array of project issues with any label with labels param as array' do it 'returns an array of project issues with any label with labels param as array' do
get api("#{base_url}/issues", user), params: { labels: [IssuesFinder::FILTER_ANY] } get api("#{base_url}/issues", user), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
expect_paginated_array_response(issue.id) expect_paginated_array_response(issue.id)
end end
it 'returns an array of project issues with no label' do it 'returns an array of project issues with no label' do
get api("#{base_url}/issues", user), params: { labels: IssuesFinder::FILTER_NONE } get api("#{base_url}/issues", user), params: { labels: IssuableFinder::Params::FILTER_NONE }
expect_paginated_array_response([confidential_issue.id, closed_issue.id]) expect_paginated_array_response([confidential_issue.id, closed_issue.id])
end end
it 'returns an array of project issues with no label with labels param as array' do it 'returns an array of project issues with no label with labels param as array' do
get api("#{base_url}/issues", user), params: { labels: [IssuesFinder::FILTER_NONE] } get api("#{base_url}/issues", user), params: { labels: [IssuableFinder::Params::FILTER_NONE] }
expect_paginated_array_response([confidential_issue.id, closed_issue.id]) expect_paginated_array_response([confidential_issue.id, closed_issue.id])
end end
......
...@@ -476,25 +476,25 @@ describe API::Issues do ...@@ -476,25 +476,25 @@ describe API::Issues do
end end
it 'returns an array of issues with any label' do it 'returns an array of issues with any label' do
get api('/issues', user), params: { labels: IssuesFinder::FILTER_ANY } get api('/issues', user), params: { labels: IssuableFinder::Params::FILTER_ANY }
expect_paginated_array_response(issue.id) expect_paginated_array_response(issue.id)
end end
it 'returns an array of issues with any label with labels param as array' do it 'returns an array of issues with any label with labels param as array' do
get api('/issues', user), params: { labels: [IssuesFinder::FILTER_ANY] } get api('/issues', user), params: { labels: [IssuableFinder::Params::FILTER_ANY] }
expect_paginated_array_response(issue.id) expect_paginated_array_response(issue.id)
end end
it 'returns an array of issues with no label' do it 'returns an array of issues with no label' do
get api('/issues', user), params: { labels: IssuesFinder::FILTER_NONE } get api('/issues', user), params: { labels: IssuableFinder::Params::FILTER_NONE }
expect_paginated_array_response(closed_issue.id) expect_paginated_array_response(closed_issue.id)
end end
it 'returns an array of issues with no label with labels param as array' do it 'returns an array of issues with no label with labels param as array' do
get api('/issues', user), params: { labels: [IssuesFinder::FILTER_NONE] } get api('/issues', user), params: { labels: [IssuableFinder::Params::FILTER_NONE] }
expect_paginated_array_response(closed_issue.id) expect_paginated_array_response(closed_issue.id)
end end
......
...@@ -281,14 +281,14 @@ describe API::MergeRequests do ...@@ -281,14 +281,14 @@ describe API::MergeRequests do
end end
it 'returns an array of merge requests with any label when filtering by any label' do it 'returns an array of merge requests with any label when filtering by any label' do
get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_ANY } get api(endpoint_path, user), params: { labels: IssuableFinder::Params::FILTER_ANY }
expect_paginated_array_response([merge_request.id]) expect_paginated_array_response([merge_request.id])
expect(json_response.first['id']).to eq(merge_request.id) expect(json_response.first['id']).to eq(merge_request.id)
end end
it 'returns an array of merge requests without a label when filtering by no label' do it 'returns an array of merge requests without a label when filtering by no label' do
get api(endpoint_path, user), params: { labels: IssuesFinder::FILTER_NONE } get api(endpoint_path, user), params: { labels: IssuableFinder::Params::FILTER_NONE }
expect_response_contain_exactly( expect_response_contain_exactly(
merge_request_merged.id, merge_request_locked.id, merge_request_closed.id merge_request_merged.id, merge_request_locked.id, merge_request_closed.id
......
...@@ -245,9 +245,9 @@ describe Issuable::BulkUpdateService do ...@@ -245,9 +245,9 @@ describe Issuable::BulkUpdateService do
end end
end end
context "when the new assignee ID is #{IssuableFinder::NONE}" do context "when the new assignee ID is #{IssuableFinder::Params::NONE}" do
it 'unassigns the issues' do it 'unassigns the issues' do
expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::NONE]) } expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::Params::NONE]) }
.to change { merge_request.reload.assignee_ids }.to([]) .to change { merge_request.reload.assignee_ids }.to([])
end end
end end
...@@ -282,9 +282,9 @@ describe Issuable::BulkUpdateService do ...@@ -282,9 +282,9 @@ describe Issuable::BulkUpdateService do
end end
end end
context "when the new assignee ID is #{IssuableFinder::NONE}" do context "when the new assignee ID is #{IssuableFinder::Params::NONE}" do
it "unassigns the issues" do it "unassigns the issues" do
expect { bulk_update(issue, assignee_ids: [IssuableFinder::NONE.to_s]) } expect { bulk_update(issue, assignee_ids: [IssuableFinder::Params::NONE.to_s]) }
.to change { issue.reload.assignees.count }.from(1).to(0) .to change { issue.reload.assignees.count }.from(1).to(0)
end end
end end
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment