summaryrefslogtreecommitdiff
path: root/app/finders/issuable_finder.rb
diff options
context:
space:
mode:
Diffstat (limited to 'app/finders/issuable_finder.rb')
-rw-r--r--app/finders/issuable_finder.rb65
1 files changed, 57 insertions, 8 deletions
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 8ed6ff56e2b..2364777cdc5 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -46,10 +46,13 @@ class IssuableFinder
# This is used in unassigning users
NONE = '0'
+ NEGATABLE_PARAMS_HELPER_KEYS = %i[include_subgroups in].freeze
+
attr_accessor :current_user, :params
- def self.scalar_params
- @scalar_params ||= %i[
+ class << self
+ def scalar_params
+ @scalar_params ||= %i[
assignee_id
assignee_username
author_id
@@ -60,14 +63,30 @@ class IssuableFinder
search
in
]
- end
+ end
- def self.array_params
- @array_params ||= { label_name: [], assignee_username: [] }
- end
+ def array_params
+ @array_params ||= { label_name: [], assignee_username: [] }
+ end
+
+ # This should not be used in controller strong params!
+ def negatable_scalar_params
+ @negatable_scalar_params ||= scalar_params + %i[project_id group_id]
+ end
+
+ # This should not be used in controller strong params!
+ def negatable_array_params
+ @negatable_array_params ||= array_params.keys.append(:iids)
+ end
- def self.valid_params
- @valid_params ||= scalar_params + [array_params]
+ # This should not be used in controller strong params!
+ def negatable_params
+ @negatable_params ||= negatable_scalar_params + negatable_array_params
+ end
+
+ def valid_params
+ @valid_params ||= scalar_params + [array_params] + [{ not: [] }]
+ end
end
def initialize(current_user, params = {})
@@ -79,6 +98,9 @@ class IssuableFinder
items = init_collection
items = filter_items(items)
+ # Let's see if we have to negate anything
+ items = by_negation(items)
+
# 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
@@ -366,6 +388,33 @@ class IssuableFinder
Array(value).last.to_sym
end
+ # Negates all params found in `negatable_params`
+ # rubocop: disable CodeReuse/ActiveRecord
+ def by_negation(items)
+ not_params = params[:not].dup
+ # API endpoints send in `nil` values so we test if there are any non-nil
+ return items unless not_params.present? && not_params.values.any?
+
+ not_params.keep_if { |_k, v| v.present? }.each do |(key, value)|
+ # These aren't negatable params themselves, but rather help other searches, so we skip them.
+ # They will be added into all the NOT searches.
+ next if NEGATABLE_PARAMS_HELPER_KEYS.include?(key.to_sym)
+ next unless self.class.negatable_params.include?(key.to_sym)
+
+ # These are "helper" params that are required inside the NOT to get the right results. They usually come in
+ # at the top-level params, but if they do come in inside the `:not` params, they should take precedence.
+ not_helpers = params.slice(*NEGATABLE_PARAMS_HELPER_KEYS).merge(params[:not].slice(*NEGATABLE_PARAMS_HELPER_KEYS))
+ not_param = { key => value }.with_indifferent_access.merge(not_helpers)
+
+ items_to_negate = self.class.new(current_user, not_param).execute
+
+ items = items.where.not(id: items_to_negate)
+ end
+
+ items
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
# rubocop: disable CodeReuse/ActiveRecord
def by_scope(items)
return items.none if current_user_related? && !current_user