summaryrefslogtreecommitdiff
path: root/app/finders
diff options
context:
space:
mode:
Diffstat (limited to 'app/finders')
-rw-r--r--app/finders/alert_management/alerts_finder.rb2
-rw-r--r--app/finders/alert_management/http_integrations_finder.rb2
-rw-r--r--app/finders/analytics/cycle_analytics/stage_finder.rb37
-rw-r--r--app/finders/autocomplete/users_finder.rb2
-rw-r--r--app/finders/ci/daily_build_group_report_results_finder.rb2
-rw-r--r--app/finders/ci/pipelines_finder.rb2
-rw-r--r--app/finders/ci/pipelines_for_merge_request_finder.rb7
-rw-r--r--app/finders/ci/runners_finder.rb7
-rw-r--r--app/finders/concerns/packages/finder_helper.rb16
-rw-r--r--app/finders/deploy_tokens/tokens_finder.rb43
-rw-r--r--app/finders/deployments_finder.rb105
-rw-r--r--app/finders/environment_names_finder.rb57
-rw-r--r--app/finders/environments/environment_names_finder.rb59
-rw-r--r--app/finders/environments/environments_by_deployments_finder.rb69
-rw-r--r--app/finders/environments/environments_finder.rb65
-rw-r--r--app/finders/environments_by_deployments_finder.rb67
-rw-r--r--app/finders/environments_finder.rb63
-rw-r--r--app/finders/fork_targets_finder.rb2
-rw-r--r--app/finders/group_members_finder.rb8
-rw-r--r--app/finders/group_projects_finder.rb2
-rw-r--r--app/finders/issuable_finder.rb14
-rw-r--r--app/finders/issuables/author_filter.rb2
-rw-r--r--app/finders/issuables/base_filter.rb7
-rw-r--r--app/finders/issues_finder.rb4
-rw-r--r--app/finders/issues_finder/params.rb2
-rw-r--r--app/finders/license_template_finder.rb2
-rw-r--r--app/finders/merge_requests_finder.rb2
-rw-r--r--app/finders/namespaces/projects_finder.rb2
-rw-r--r--app/finders/notes_finder.rb2
-rw-r--r--app/finders/packages/composer/packages_finder.rb2
-rw-r--r--app/finders/packages/conan/package_finder.rb2
-rw-r--r--app/finders/packages/generic/package_finder.rb1
-rw-r--r--app/finders/packages/go/package_finder.rb1
-rw-r--r--app/finders/packages/go/version_finder.rb2
-rw-r--r--app/finders/packages/group_or_project_package_finder.rb45
-rw-r--r--app/finders/packages/group_packages_finder.rb6
-rw-r--r--app/finders/packages/maven/package_finder.rb72
-rw-r--r--app/finders/packages/npm/package_finder.rb1
-rw-r--r--app/finders/packages/nuget/package_finder.rb37
-rw-r--r--app/finders/packages/package_finder.rb2
-rw-r--r--app/finders/packages/packages_finder.rb1
-rw-r--r--app/finders/packages/pypi/package_finder.rb17
-rw-r--r--app/finders/packages/pypi/packages_finder.rb20
-rw-r--r--app/finders/projects/groups_finder.rb35
-rw-r--r--app/finders/projects/members/effective_access_level_finder.rb125
-rw-r--r--app/finders/projects_finder.rb2
-rw-r--r--app/finders/repositories/branch_names_finder.rb12
-rw-r--r--app/finders/snippets_finder.rb2
-rw-r--r--app/finders/template_finder.rb3
-rw-r--r--app/finders/todos_finder.rb2
-rw-r--r--app/finders/users_finder.rb2
-rw-r--r--app/finders/users_with_pending_todos_finder.rb16
52 files changed, 677 insertions, 385 deletions
diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb
index 8e0444d324a..b4f66a38faa 100644
--- a/app/finders/alert_management/alerts_finder.rb
+++ b/app/finders/alert_management/alerts_finder.rb
@@ -67,4 +67,4 @@ module AlertManagement
end
end
-AlertManagement::AlertsFinder.prepend_if_ee('EE::AlertManagement::AlertsFinder')
+AlertManagement::AlertsFinder.prepend_mod_with('AlertManagement::AlertsFinder')
diff --git a/app/finders/alert_management/http_integrations_finder.rb b/app/finders/alert_management/http_integrations_finder.rb
index 5d4c9b6fbe3..e8e85da11b7 100644
--- a/app/finders/alert_management/http_integrations_finder.rb
+++ b/app/finders/alert_management/http_integrations_finder.rb
@@ -51,4 +51,4 @@ module AlertManagement
end
end
-::AlertManagement::HttpIntegrationsFinder.prepend_if_ee('EE::AlertManagement::HttpIntegrationsFinder')
+::AlertManagement::HttpIntegrationsFinder.prepend_mod_with('AlertManagement::HttpIntegrationsFinder')
diff --git a/app/finders/analytics/cycle_analytics/stage_finder.rb b/app/finders/analytics/cycle_analytics/stage_finder.rb
new file mode 100644
index 00000000000..732e9ff3e00
--- /dev/null
+++ b/app/finders/analytics/cycle_analytics/stage_finder.rb
@@ -0,0 +1,37 @@
+# frozen_string_literal: true
+
+module Analytics
+ module CycleAnalytics
+ class StageFinder
+ def initialize(parent:, stage_id:)
+ @parent = parent
+ @stage_id = stage_id
+ end
+
+ def execute
+ build_in_memory_stage_by_name
+ end
+
+ private
+
+ attr_reader :parent, :stage_id
+
+ def build_in_memory_stage_by_name
+ parent.cycle_analytics_stages.build(find_in_memory_stage)
+ end
+
+ def find_in_memory_stage
+ # raise ActiveRecord::RecordNotFound, so it will behave similarly to AR models and produce 404 response in the controller
+ raw_stage = Gitlab::Analytics::CycleAnalytics::DefaultStages.all.find do |hash|
+ hash[:name].eql?(stage_id)
+ end
+
+ raise(ActiveRecord::RecordNotFound, "Stage with id '#{stage_id}' could not be found") unless raw_stage
+
+ raw_stage
+ end
+ end
+ end
+end
+
+Analytics::CycleAnalytics::StageFinder.prepend_mod_with('Analytics::CycleAnalytics::StageFinder')
diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb
index ff5d9ea7d19..a9fffd3f411 100644
--- a/app/finders/autocomplete/users_finder.rb
+++ b/app/finders/autocomplete/users_finder.rb
@@ -102,4 +102,4 @@ module Autocomplete
end
end
-Autocomplete::UsersFinder.prepend_if_ee('EE::Autocomplete::UsersFinder')
+Autocomplete::UsersFinder.prepend_mod_with('Autocomplete::UsersFinder')
diff --git a/app/finders/ci/daily_build_group_report_results_finder.rb b/app/finders/ci/daily_build_group_report_results_finder.rb
index 5ac1bbd0670..33aefe29392 100644
--- a/app/finders/ci/daily_build_group_report_results_finder.rb
+++ b/app/finders/ci/daily_build_group_report_results_finder.rb
@@ -94,4 +94,4 @@ module Ci
end
end
-Ci::DailyBuildGroupReportResultsFinder.prepend_if_ee('::EE::Ci::DailyBuildGroupReportResultsFinder')
+Ci::DailyBuildGroupReportResultsFinder.prepend_mod_with('Ci::DailyBuildGroupReportResultsFinder')
diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb
index e509cf940b8..af7b23278a4 100644
--- a/app/finders/ci/pipelines_finder.rb
+++ b/app/finders/ci/pipelines_finder.rb
@@ -133,7 +133,7 @@ module Ci
when true
items.where.not(yaml_errors: nil)
when false
- items.where("yaml_errors IS NULL")
+ items.where(yaml_errors: nil)
else
items
end
diff --git a/app/finders/ci/pipelines_for_merge_request_finder.rb b/app/finders/ci/pipelines_for_merge_request_finder.rb
index 1f6ee9d75ad..be65b1f6b3c 100644
--- a/app/finders/ci/pipelines_for_merge_request_finder.rb
+++ b/app/finders/ci/pipelines_for_merge_request_finder.rb
@@ -45,8 +45,12 @@ module Ci
private
+ # rubocop: disable CodeReuse/ActiveRecord
def pipelines_using_cte
- cte = Gitlab::SQL::CTE.new(:shas, merge_request.all_commits.select(:sha))
+ sha_relation = merge_request.all_commits.select(:sha)
+ sha_relation = sha_relation.distinct if Feature.enabled?(:use_distinct_in_shas_cte)
+
+ cte = Gitlab::SQL::CTE.new(:shas, sha_relation)
pipelines_for_merge_requests = triggered_by_merge_request
pipelines_for_branch = filter_by_sha(triggered_for_branch, cte)
@@ -54,6 +58,7 @@ module Ci
Ci::Pipeline.with(cte.to_arel) # rubocop: disable CodeReuse/ActiveRecord
.from_union([pipelines_for_merge_requests, pipelines_for_branch])
end
+ # rubocop: enable CodeReuse/ActiveRecord
def filter_by_sha(pipelines, cte)
hex = Arel::Nodes::SqlLiteral.new("'hex'")
diff --git a/app/finders/ci/runners_finder.rb b/app/finders/ci/runners_finder.rb
index 1b76211c524..60dd977ff94 100644
--- a/app/finders/ci/runners_finder.rb
+++ b/app/finders/ci/runners_finder.rb
@@ -4,8 +4,6 @@ module Ci
class RunnersFinder < UnionFinder
include Gitlab::Allowable
- NUMBER_OF_RUNNERS_PER_PAGE = 30
-
def initialize(current_user:, group: nil, params:)
@params = params
@group = group
@@ -18,7 +16,6 @@ module Ci
filter_by_runner_type!
filter_by_tag_list!
sort!
- paginate!
@runners.with_tags
@@ -77,10 +74,6 @@ module Ci
@runners = @runners.order_by(sort_key)
end
- def paginate!
- @runners = @runners.page(@params[:page]).per(NUMBER_OF_RUNNERS_PER_PAGE)
- end
-
def filter_by!(scope_name, available_scopes)
scope = @params[scope_name]
diff --git a/app/finders/concerns/packages/finder_helper.rb b/app/finders/concerns/packages/finder_helper.rb
index 39c018818d1..f0ad998cadb 100644
--- a/app/finders/concerns/packages/finder_helper.rb
+++ b/app/finders/concerns/packages/finder_helper.rb
@@ -9,12 +9,16 @@ module Packages
private
+ def packages_for_project(project)
+ project.packages.installable
+ end
+
def packages_visible_to_user(user, within_group:)
return ::Packages::Package.none unless within_group
return ::Packages::Package.none unless Ability.allowed?(user, :read_group, within_group)
projects = projects_visible_to_reporters(user, within_group: within_group)
- ::Packages::Package.for_projects(projects.select(:id))
+ ::Packages::Package.for_projects(projects.select(:id)).installable
end
def projects_visible_to_user(user, within_group:)
@@ -25,7 +29,7 @@ module Packages
end
def projects_visible_to_reporters(user, within_group:)
- if user.is_a?(DeployToken) && Feature.enabled?(:packages_finder_helper_deploy_token)
+ if user.is_a?(DeployToken) && Feature.enabled?(:packages_finder_helper_deploy_token, default_enabled: :yaml)
user.accessible_projects
else
within_group.all_projects
@@ -38,7 +42,7 @@ module Packages
end
def filter_by_package_type(packages)
- return packages unless package_type
+ return packages.without_package_type(:terraform_module) unless package_type
raise InvalidPackageTypeError unless ::Packages::Package.package_types.key?(package_type)
packages.with_package_type(package_type)
@@ -50,6 +54,12 @@ module Packages
packages.search_by_name(params[:package_name])
end
+ def filter_by_package_version(packages)
+ return packages unless params[:package_version].present?
+
+ packages.with_version(params[:package_version])
+ end
+
def filter_with_version(packages)
return packages if params[:include_versionless].present?
diff --git a/app/finders/deploy_tokens/tokens_finder.rb b/app/finders/deploy_tokens/tokens_finder.rb
new file mode 100644
index 00000000000..98456628375
--- /dev/null
+++ b/app/finders/deploy_tokens/tokens_finder.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+# Arguments:
+# current_user: The currently logged in user.
+# scope: A Project or Group to scope deploy tokens to (or :all for all tokens).
+# params:
+# active: Boolean - When true, only return active deployments.
+module DeployTokens
+ class TokensFinder
+ attr_reader :current_user, :params, :scope
+
+ def initialize(current_user, scope, params = {})
+ @current_user = current_user
+ @scope = scope
+ @params = params
+ end
+
+ def execute
+ by_active(init_collection)
+ end
+
+ private
+
+ def init_collection
+ case scope
+ when Group, Project
+ raise Gitlab::Access::AccessDeniedError unless current_user.can?(:read_deploy_token, scope)
+
+ scope.deploy_tokens
+ when :all
+ raise Gitlab::Access::AccessDeniedError unless current_user.can_read_all_resources?
+
+ DeployToken.all
+ else
+ raise ArgumentError, "Scope must be a Group, a Project, or the :all symbol."
+ end
+ end
+
+ def by_active(items)
+ params[:active] ? items.active : items
+ end
+ end
+end
diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb
index ae26fc14ad5..acce038dba6 100644
--- a/app/finders/deployments_finder.rb
+++ b/app/finders/deployments_finder.rb
@@ -16,14 +16,25 @@
class DeploymentsFinder
attr_reader :params
+ # Warning:
+ # These const are directly used in Deployment Rest API, thus
+ # modifying these values could implicity change the API interface or introduce a breaking change.
+ # Also, if you add a sort value, make sure that the new query will stay
+ # performant with the other filtering/sorting parameters.
+ # The composed query could be significantly slower when the filtering and sorting columns are different.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/325627 for example.
ALLOWED_SORT_VALUES = %w[id iid created_at updated_at ref finished_at].freeze
DEFAULT_SORT_VALUE = 'id'
ALLOWED_SORT_DIRECTIONS = %w[asc desc].freeze
DEFAULT_SORT_DIRECTION = 'asc'
+ InefficientQueryError = Class.new(StandardError)
+
def initialize(params = {})
@params = params
+
+ validate!
end
def execute
@@ -38,15 +49,45 @@ class DeploymentsFinder
private
+ def validate!
+ if filter_by_updated_at? && filter_by_finished_at?
+ raise InefficientQueryError, 'Both `updated_at` filter and `finished_at` filter can not be specified'
+ end
+
+ # Currently, the inefficient parameters are allowed in order to avoid breaking changes in Deployment API.
+ # We'll switch to a hard error in https://gitlab.com/gitlab-org/gitlab/-/issues/328500.
+ if (filter_by_updated_at? && !order_by_updated_at?) || (!filter_by_updated_at? && order_by_updated_at?)
+ Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
+ InefficientQueryError.new('`updated_at` filter and `updated_at` sorting must be paired')
+ )
+ end
+
+ if (filter_by_finished_at? && !order_by_finished_at?) || (!filter_by_finished_at? && order_by_finished_at?)
+ raise InefficientQueryError, '`finished_at` filter and `finished_at` sorting must be paired'
+ end
+
+ if filter_by_finished_at? && !filter_by_successful_deployment?
+ raise InefficientQueryError, '`finished_at` filter must be combined with `success` status filter.'
+ end
+
+ if params[:environment].present? && !params[:project].present?
+ raise InefficientQueryError, '`environment` filter must be combined with `project` scope.'
+ end
+ end
+
def init_collection
- if params[:project]
+ if params[:project].present?
params[:project].deployments
+ elsif params[:group].present?
+ ::Deployment.for_projects(params[:group].all_projects)
else
- Deployment.none
+ ::Deployment.none
end
end
def sort(items)
+ sort_params = build_sort_params
+ optimize_sort_params!(sort_params)
items.order(sort_params) # rubocop: disable CodeReuse/ActiveRecord
end
@@ -65,8 +106,8 @@ class DeploymentsFinder
end
def by_environment(items)
- if params[:environment].present?
- items.for_environment_name(params[:environment])
+ if params[:project].present? && params[:environment].present?
+ items.for_environment_name(params[:project], params[:environment])
else
items
end
@@ -82,14 +123,60 @@ class DeploymentsFinder
items.for_status(params[:status])
end
- def sort_params
+ def build_sort_params
order_by = ALLOWED_SORT_VALUES.include?(params[:order_by]) ? params[:order_by] : DEFAULT_SORT_VALUE
order_direction = ALLOWED_SORT_DIRECTIONS.include?(params[:sort]) ? params[:sort] : DEFAULT_SORT_DIRECTION
- { order_by => order_direction }.tap do |sort_values|
- sort_values['id'] = 'desc' if sort_values['updated_at']
- sort_values['id'] = sort_values.delete('created_at') if sort_values['created_at'] # Sorting by `id` produces the same result as sorting by `created_at`
+ { order_by => order_direction }
+ end
+
+ def optimize_sort_params!(sort_params)
+ sort_direction = sort_params.each_value.first
+
+ # Implicitly enforce the ordering when filtered by `updated_at` column for performance optimization.
+ # See https://gitlab.com/gitlab-org/gitlab/-/issues/325627#note_552417509.
+ # We remove this in https://gitlab.com/gitlab-org/gitlab/-/issues/328500.
+ if filter_by_updated_at? && implicitly_enforce_ordering_for_updated_at_filter?
+ sort_params.replace('updated_at' => sort_direction)
end
+
+ if sort_params['created_at'] || sort_params['iid']
+ # Sorting by `id` produces the same result as sorting by `created_at` or `iid`
+ sort_params.replace(id: sort_direction)
+ elsif sort_params['updated_at']
+ # This adds the order as a tie-breaker when multiple rows have the same updated_at value.
+ # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/20848.
+ sort_params.merge!(id: sort_direction)
+ end
+ end
+
+ def filter_by_updated_at?
+ params[:updated_before].present? || params[:updated_after].present?
+ end
+
+ def filter_by_finished_at?
+ params[:finished_before].present? || params[:finished_after].present?
+ end
+
+ def filter_by_successful_deployment?
+ params[:status].to_s == 'success'
+ end
+
+ def order_by_updated_at?
+ params[:order_by].to_s == 'updated_at'
+ end
+
+ def order_by_finished_at?
+ params[:order_by].to_s == 'finished_at'
+ end
+
+ def implicitly_enforce_ordering_for_updated_at_filter?
+ return false unless params[:project].present?
+
+ ::Feature.enabled?(
+ :deployments_finder_implicitly_enforce_ordering_for_updated_at_filter,
+ params[:project],
+ default_enabled: :yaml)
end
# rubocop: disable CodeReuse/ActiveRecord
@@ -113,5 +200,3 @@ class DeploymentsFinder
end
# rubocop: enable CodeReuse/ActiveRecord
end
-
-DeploymentsFinder.prepend_if_ee('EE::DeploymentsFinder')
diff --git a/app/finders/environment_names_finder.rb b/app/finders/environment_names_finder.rb
deleted file mode 100644
index e9063ef4c90..00000000000
--- a/app/finders/environment_names_finder.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-# frozen_string_literal: true
-
-# Finder for obtaining the unique environment names of a project or group.
-#
-# This finder exists so that the merge requests "environments" filter can be
-# populated with a unique list of environment names. If we retrieve _just_ the
-# environments, duplicates may be present (e.g. multiple projects in a group
-# having a "staging" environment).
-#
-# In addition, this finder only produces unfoldered environments. We do this
-# because when searching for environments we want to exclude review app
-# environments.
-class EnvironmentNamesFinder
- attr_reader :project_or_group, :current_user
-
- def initialize(project_or_group, current_user = nil)
- @project_or_group = project_or_group
- @current_user = current_user
- end
-
- def execute
- all_environments.unfoldered.order_by_name.pluck_unique_names
- end
-
- def all_environments
- if project_or_group.is_a?(Namespace)
- namespace_environments
- else
- project_environments
- end
- end
-
- def namespace_environments
- # We assume reporter access is needed for the :read_environment permission
- # here. This expection is also present in
- # IssuableFinder::Params#min_access_level, which is used for filtering out
- # merge requests that don't have the right permissions.
- #
- # We use this approach so we don't need to load every project into memory
- # just to verify if we can see their environments. Doing so would not be
- # efficient, and possibly mess up pagination if certain projects are not
- # meant to be visible.
- projects = project_or_group
- .all_projects
- .public_or_visible_to_user(current_user, Gitlab::Access::REPORTER)
-
- Environment.for_project(projects)
- end
-
- def project_environments
- if Ability.allowed?(current_user, :read_environment, project_or_group)
- project_or_group.environments
- else
- Environment.none
- end
- end
-end
diff --git a/app/finders/environments/environment_names_finder.rb b/app/finders/environments/environment_names_finder.rb
new file mode 100644
index 00000000000..d4928f0fc84
--- /dev/null
+++ b/app/finders/environments/environment_names_finder.rb
@@ -0,0 +1,59 @@
+# frozen_string_literal: true
+
+module Environments
+ # Finder for obtaining the unique environment names of a project or group.
+ #
+ # This finder exists so that the merge requests "environments" filter can be
+ # populated with a unique list of environment names. If we retrieve _just_ the
+ # environments, duplicates may be present (e.g. multiple projects in a group
+ # having a "staging" environment).
+ #
+ # In addition, this finder only produces unfoldered environments. We do this
+ # because when searching for environments we want to exclude review app
+ # environments.
+ class EnvironmentNamesFinder
+ attr_reader :project_or_group, :current_user
+
+ def initialize(project_or_group, current_user = nil)
+ @project_or_group = project_or_group
+ @current_user = current_user
+ end
+
+ def execute
+ all_environments.unfoldered.order_by_name.pluck_unique_names
+ end
+
+ def all_environments
+ if project_or_group.is_a?(Namespace)
+ namespace_environments
+ else
+ project_environments
+ end
+ end
+
+ def namespace_environments
+ # We assume reporter access is needed for the :read_environment permission
+ # here. This expection is also present in
+ # IssuableFinder::Params#min_access_level, which is used for filtering out
+ # merge requests that don't have the right permissions.
+ #
+ # We use this approach so we don't need to load every project into memory
+ # just to verify if we can see their environments. Doing so would not be
+ # efficient, and possibly mess up pagination if certain projects are not
+ # meant to be visible.
+ projects = project_or_group
+ .all_projects
+ .public_or_visible_to_user(current_user, Gitlab::Access::REPORTER)
+
+ Environment.for_project(projects)
+ end
+
+ def project_environments
+ if Ability.allowed?(current_user, :read_environment, project_or_group)
+ project_or_group.environments
+ else
+ Environment.none
+ end
+ end
+ end
+end
diff --git a/app/finders/environments/environments_by_deployments_finder.rb b/app/finders/environments/environments_by_deployments_finder.rb
new file mode 100644
index 00000000000..e0ecc98b1c0
--- /dev/null
+++ b/app/finders/environments/environments_by_deployments_finder.rb
@@ -0,0 +1,69 @@
+# frozen_string_literal: true
+
+module Environments
+ class EnvironmentsByDeploymentsFinder
+ attr_reader :project, :current_user, :params
+
+ def initialize(project, current_user, params = {})
+ @project = project
+ @current_user = current_user
+ @params = params
+ end
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ def execute
+ deployments = project.deployments
+ deployments =
+ if ref
+ deployments_query = params[:with_tags] ? 'ref = :ref OR tag IS TRUE' : 'ref = :ref'
+ deployments.where(deployments_query, ref: ref.to_s)
+ elsif commit
+ deployments.where(sha: commit.sha)
+ else
+ deployments.none
+ end
+
+ environment_ids = deployments
+ .group(:environment_id)
+ .select(:environment_id)
+
+ environments = project.environments.available
+ .where(id: environment_ids)
+
+ if params[:find_latest]
+ find_one(environments.order_by_last_deployed_at_desc)
+ else
+ find_all(environments.order_by_last_deployed_at.to_a)
+ end
+ end
+ # rubocop: enable CodeReuse/ActiveRecord
+
+ private
+
+ def find_one(environments)
+ [environments.find { |environment| valid_environment?(environment) }].compact
+ end
+
+ def find_all(environments)
+ environments.select { |environment| valid_environment?(environment) }
+ end
+
+ def valid_environment?(environment)
+ # Go in order of cost: SQL calls are cheaper than Gitaly calls
+ return false unless Ability.allowed?(current_user, :read_environment, environment)
+
+ return false if ref && params[:recently_updated] && !environment.recently_updated_on_branch?(ref)
+ return false if ref && commit && !environment.includes_commit?(commit)
+
+ true
+ end
+
+ def ref
+ params[:ref].try(:to_s)
+ end
+
+ def commit
+ params[:commit]
+ end
+ end
+end
diff --git a/app/finders/environments/environments_finder.rb b/app/finders/environments/environments_finder.rb
new file mode 100644
index 00000000000..190cdb3dec3
--- /dev/null
+++ b/app/finders/environments/environments_finder.rb
@@ -0,0 +1,65 @@
+# frozen_string_literal: true
+
+module Environments
+ class EnvironmentsFinder
+ attr_reader :project, :current_user, :params
+
+ InvalidStatesError = Class.new(StandardError)
+
+ def initialize(project, current_user, params = {})
+ @project = project
+ @current_user = current_user
+ @params = params
+ end
+
+ def execute
+ environments = project.environments
+ environments = by_name(environments)
+ environments = by_search(environments)
+
+ # Raises InvalidStatesError if params[:states] contains invalid states.
+ by_states(environments)
+ end
+
+ private
+
+ def by_name(environments)
+ if params[:name].present?
+ environments.for_name(params[:name])
+ else
+ environments
+ end
+ end
+
+ def by_search(environments)
+ if params[:search].present?
+ environments.for_name_like(params[:search], limit: nil)
+ else
+ environments
+ end
+ end
+
+ def by_states(environments)
+ if params[:states].present?
+ environments_with_states(environments)
+ else
+ environments
+ end
+ end
+
+ def environments_with_states(environments)
+ # Convert to array of strings
+ states = Array(params[:states]).map(&:to_s)
+
+ raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states)
+
+ environments.with_states(states)
+ end
+
+ def valid_states?(states)
+ valid_states = Environment.valid_states.map(&:to_s)
+
+ (states - valid_states).empty?
+ end
+ end
+end
diff --git a/app/finders/environments_by_deployments_finder.rb b/app/finders/environments_by_deployments_finder.rb
deleted file mode 100644
index 76e1c050ea5..00000000000
--- a/app/finders/environments_by_deployments_finder.rb
+++ /dev/null
@@ -1,67 +0,0 @@
-# frozen_string_literal: true
-
-class EnvironmentsByDeploymentsFinder
- attr_reader :project, :current_user, :params
-
- def initialize(project, current_user, params = {})
- @project = project
- @current_user = current_user
- @params = params
- end
-
- # rubocop: disable CodeReuse/ActiveRecord
- def execute
- deployments = project.deployments
- deployments =
- if ref
- deployments_query = params[:with_tags] ? 'ref = :ref OR tag IS TRUE' : 'ref = :ref'
- deployments.where(deployments_query, ref: ref.to_s)
- elsif commit
- deployments.where(sha: commit.sha)
- else
- deployments.none
- end
-
- environment_ids = deployments
- .group(:environment_id)
- .select(:environment_id)
-
- environments = project.environments.available
- .where(id: environment_ids)
-
- if params[:find_latest]
- find_one(environments.order_by_last_deployed_at_desc)
- else
- find_all(environments.order_by_last_deployed_at.to_a)
- end
- end
- # rubocop: enable CodeReuse/ActiveRecord
-
- private
-
- def find_one(environments)
- [environments.find { |environment| valid_environment?(environment) }].compact
- end
-
- def find_all(environments)
- environments.select { |environment| valid_environment?(environment) }
- end
-
- def valid_environment?(environment)
- # Go in order of cost: SQL calls are cheaper than Gitaly calls
- return false unless Ability.allowed?(current_user, :read_environment, environment)
-
- return false if ref && params[:recently_updated] && !environment.recently_updated_on_branch?(ref)
- return false if ref && commit && !environment.includes_commit?(commit)
-
- true
- end
-
- def ref
- params[:ref].try(:to_s)
- end
-
- def commit
- params[:commit]
- end
-end
diff --git a/app/finders/environments_finder.rb b/app/finders/environments_finder.rb
deleted file mode 100644
index c64e850f440..00000000000
--- a/app/finders/environments_finder.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-# frozen_string_literal: true
-
-class EnvironmentsFinder
- attr_reader :project, :current_user, :params
-
- InvalidStatesError = Class.new(StandardError)
-
- def initialize(project, current_user, params = {})
- @project = project
- @current_user = current_user
- @params = params
- end
-
- def execute
- environments = project.environments
- environments = by_name(environments)
- environments = by_search(environments)
-
- # Raises InvalidStatesError if params[:states] contains invalid states.
- by_states(environments)
- end
-
- private
-
- def by_name(environments)
- if params[:name].present?
- environments.for_name(params[:name])
- else
- environments
- end
- end
-
- def by_search(environments)
- if params[:search].present?
- environments.for_name_like(params[:search], limit: nil)
- else
- environments
- end
- end
-
- def by_states(environments)
- if params[:states].present?
- environments_with_states(environments)
- else
- environments
- end
- end
-
- def environments_with_states(environments)
- # Convert to array of strings
- states = Array(params[:states]).map(&:to_s)
-
- raise InvalidStatesError, _('Requested states are invalid') unless valid_states?(states)
-
- environments.with_states(states)
- end
-
- def valid_states?(states)
- valid_states = Environment.valid_states.map(&:to_s)
-
- (states - valid_states).empty?
- end
-end
diff --git a/app/finders/fork_targets_finder.rb b/app/finders/fork_targets_finder.rb
index 719c244a207..3a79b216853 100644
--- a/app/finders/fork_targets_finder.rb
+++ b/app/finders/fork_targets_finder.rb
@@ -19,4 +19,4 @@ class ForkTargetsFinder
attr_reader :project, :user
end
-ForkTargetsFinder.prepend_if_ee('EE::ForkTargetsFinder')
+ForkTargetsFinder.prepend_mod_with('ForkTargetsFinder')
diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb
index a6ecd835527..982234f7506 100644
--- a/app/finders/group_members_finder.rb
+++ b/app/finders/group_members_finder.rb
@@ -4,6 +4,12 @@ class GroupMembersFinder < UnionFinder
RELATIONS = %i(direct inherited descendants).freeze
DEFAULT_RELATIONS = %i(direct inherited).freeze
+ RELATIONS_DESCRIPTIONS = {
+ direct: 'Members in the group itself',
+ inherited: "Members in the group's ancestor groups",
+ descendants: "Members in the group's subgroups"
+ }.freeze
+
include CreatedAtFilter
# Params can be any of the following:
@@ -82,4 +88,4 @@ class GroupMembersFinder < UnionFinder
end
end
-GroupMembersFinder.prepend_if_ee('EE::GroupMembersFinder')
+GroupMembersFinder.prepend_mod_with('GroupMembersFinder')
diff --git a/app/finders/group_projects_finder.rb b/app/finders/group_projects_finder.rb
index dfdf821e3f0..d3c26fd845c 100644
--- a/app/finders/group_projects_finder.rb
+++ b/app/finders/group_projects_finder.rb
@@ -126,4 +126,4 @@ class GroupProjectsFinder < ProjectsFinder
end
end
-GroupProjectsFinder.prepend_if_ee('EE::GroupProjectsFinder')
+GroupProjectsFinder.prepend_mod_with('GroupProjectsFinder')
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 40a4e2b4f26..d1885b5ae08 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -142,8 +142,6 @@ class IssuableFinder
end
def should_filter_negated_args?
- return false unless not_filters_enabled?
-
# API endpoints send in `nil` values so we test if there are any non-nil
not_params.present? && not_params.values.any?
end
@@ -336,8 +334,7 @@ class IssuableFinder
return items if items.is_a?(ActiveRecord::NullRelation)
if use_cte_for_search?
- cte = Gitlab::SQL::RecursiveCTE.new(klass.table_name)
- cte << items
+ cte = Gitlab::SQL::CTE.new(klass.table_name, items)
items = klass.with(cte.to_arel).from(klass.table_name)
end
@@ -370,8 +367,7 @@ class IssuableFinder
Issuables::AuthorFilter.new(
items,
params: original_params,
- or_filters_enabled: or_filters_enabled?,
- not_filters_enabled: not_filters_enabled?
+ or_filters_enabled: or_filters_enabled?
).filter
end
@@ -496,12 +492,6 @@ class IssuableFinder
end
end
- def not_filters_enabled?
- strong_memoize(:not_filters_enabled) do
- Feature.enabled?(:not_issuable_queries, feature_flag_scope, default_enabled: :yaml)
- end
- end
-
def feature_flag_scope
params.group || params.project
end
diff --git a/app/finders/issuables/author_filter.rb b/app/finders/issuables/author_filter.rb
index ce68dbafb95..522751a384e 100644
--- a/app/finders/issuables/author_filter.rb
+++ b/app/finders/issuables/author_filter.rb
@@ -27,7 +27,7 @@ module Issuables
end
def by_negated_author(issuables)
- return issuables unless not_filters_enabled? && not_params.present?
+ return issuables unless not_params.present?
if not_params[:author_id].present?
issuables.not_authored(not_params[:author_id])
diff --git a/app/finders/issuables/base_filter.rb b/app/finders/issuables/base_filter.rb
index 641ae2568cc..6d1a3f96062 100644
--- a/app/finders/issuables/base_filter.rb
+++ b/app/finders/issuables/base_filter.rb
@@ -4,11 +4,10 @@ module Issuables
class BaseFilter
attr_reader :issuables, :params
- def initialize(issuables, params:, or_filters_enabled: false, not_filters_enabled: false)
+ def initialize(issuables, params:, or_filters_enabled: false)
@issuables = issuables
@params = params
@or_filters_enabled = or_filters_enabled
- @not_filters_enabled = not_filters_enabled
end
def filter
@@ -28,9 +27,5 @@ module Issuables
def or_filters_enabled?
@or_filters_enabled
end
-
- def not_filters_enabled?
- @not_filters_enabled
- end
end
end
diff --git a/app/finders/issues_finder.rb b/app/finders/issues_finder.rb
index e1a334413f8..eb9099fe256 100644
--- a/app/finders/issues_finder.rb
+++ b/app/finders/issues_finder.rb
@@ -25,7 +25,7 @@
# updated_after: datetime
# updated_before: datetime
# confidential: boolean
-# issue_type: array of strings (one of Issue.issue_types)
+# issue_types: array of strings (one of Issue.issue_types)
#
class IssuesFinder < IssuableFinder
CONFIDENTIAL_ACCESS_LEVEL = Gitlab::Access::REPORTER
@@ -117,4 +117,4 @@ class IssuesFinder < IssuableFinder
end
end
-IssuesFinder.prepend_if_ee('EE::IssuesFinder')
+IssuesFinder.prepend_mod_with('IssuesFinder')
diff --git a/app/finders/issues_finder/params.rb b/app/finders/issues_finder/params.rb
index 668d969f7c0..1de117216f8 100644
--- a/app/finders/issues_finder/params.rb
+++ b/app/finders/issues_finder/params.rb
@@ -45,4 +45,4 @@ class IssuesFinder
end
end
-IssuesFinder::Params.prepend_if_ee('EE::IssuesFinder::Params')
+IssuesFinder::Params.prepend_mod_with('IssuesFinder::Params')
diff --git a/app/finders/license_template_finder.rb b/app/finders/license_template_finder.rb
index c4cb33235af..b4235a77867 100644
--- a/app/finders/license_template_finder.rb
+++ b/app/finders/license_template_finder.rb
@@ -56,4 +56,4 @@ class LicenseTemplateFinder
end
end
-LicenseTemplateFinder.prepend_if_ee('::EE::LicenseTemplateFinder')
+LicenseTemplateFinder.prepend_mod_with('LicenseTemplateFinder')
diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb
index 9f9e2afa7fe..19fcd91a5b8 100644
--- a/app/finders/merge_requests_finder.rb
+++ b/app/finders/merge_requests_finder.rb
@@ -220,4 +220,4 @@ class MergeRequestsFinder < IssuableFinder
end
end
-MergeRequestsFinder.prepend_if_ee('EE::MergeRequestsFinder')
+MergeRequestsFinder.prepend_mod_with('MergeRequestsFinder')
diff --git a/app/finders/namespaces/projects_finder.rb b/app/finders/namespaces/projects_finder.rb
index bac5328d077..589a9696ea6 100644
--- a/app/finders/namespaces/projects_finder.rb
+++ b/app/finders/namespaces/projects_finder.rb
@@ -60,4 +60,4 @@ module Namespaces
end
end
-Namespaces::ProjectsFinder.prepend_if_ee('::EE::Namespaces::ProjectsFinder')
+Namespaces::ProjectsFinder.prepend_mod_with('Namespaces::ProjectsFinder')
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index 96966001e85..42bd7a24888 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -183,4 +183,4 @@ class NotesFinder
end
end
-NotesFinder.prepend_if_ee('EE::NotesFinder')
+NotesFinder.prepend_mod_with('NotesFinder')
diff --git a/app/finders/packages/composer/packages_finder.rb b/app/finders/packages/composer/packages_finder.rb
index e63b2ee03fa..b5a1b19216f 100644
--- a/app/finders/packages/composer/packages_finder.rb
+++ b/app/finders/packages/composer/packages_finder.rb
@@ -9,7 +9,7 @@ module Packages
end
def execute
- packages_for_group_projects.composer.preload_composer
+ packages_for_group_projects(installable_only: true).composer.preload_composer
end
end
end
diff --git a/app/finders/packages/conan/package_finder.rb b/app/finders/packages/conan/package_finder.rb
index 26e9182f4e1..8ebdd358ba6 100644
--- a/app/finders/packages/conan/package_finder.rb
+++ b/app/finders/packages/conan/package_finder.rb
@@ -11,7 +11,7 @@ module Packages
end
def execute
- packages_for_current_user.with_name_like(query).order_name_asc if query
+ packages_for_current_user.installable.with_name_like(query).order_name_asc if query
end
private
diff --git a/app/finders/packages/generic/package_finder.rb b/app/finders/packages/generic/package_finder.rb
index 3a260e11fa3..8ec88754901 100644
--- a/app/finders/packages/generic/package_finder.rb
+++ b/app/finders/packages/generic/package_finder.rb
@@ -11,6 +11,7 @@ module Packages
project
.packages
.generic
+ .installable
.by_name_and_version!(package_name, package_version)
end
diff --git a/app/finders/packages/go/package_finder.rb b/app/finders/packages/go/package_finder.rb
index 4573417d11f..553e731895d 100644
--- a/app/finders/packages/go/package_finder.rb
+++ b/app/finders/packages/go/package_finder.rb
@@ -21,6 +21,7 @@ module Packages
@project
.packages
.golang
+ .installable
.with_name(@module_name)
.with_version(@module_version)
end
diff --git a/app/finders/packages/go/version_finder.rb b/app/finders/packages/go/version_finder.rb
index 6ee02b8c6f6..8500a441fb7 100644
--- a/app/finders/packages/go/version_finder.rb
+++ b/app/finders/packages/go/version_finder.rb
@@ -37,7 +37,7 @@ module Packages
@mod.version_by(commit: target)
else
- raise ArgumentError.new 'not a valid target'
+ raise ArgumentError, 'not a valid target'
end
end
end
diff --git a/app/finders/packages/group_or_project_package_finder.rb b/app/finders/packages/group_or_project_package_finder.rb
new file mode 100644
index 00000000000..fb8bcfc7d42
--- /dev/null
+++ b/app/finders/packages/group_or_project_package_finder.rb
@@ -0,0 +1,45 @@
+# frozen_string_literal: true
+
+module Packages
+ class GroupOrProjectPackageFinder
+ include ::Packages::FinderHelper
+
+ def initialize(current_user, project_or_group, params = {})
+ @current_user = current_user
+ @project_or_group = project_or_group
+ @params = params
+ end
+
+ def execute
+ raise NotImplementedError
+ end
+
+ def execute!
+ raise NotImplementedError
+ end
+
+ private
+
+ def packages
+ raise NotImplementedError
+ end
+
+ def base
+ if project?
+ packages_for_project(@project_or_group)
+ elsif group?
+ packages_visible_to_user(@current_user, within_group: @project_or_group)
+ else
+ ::Packages::Package.none
+ end
+ end
+
+ def project?
+ @project_or_group.is_a?(::Project)
+ end
+
+ def group?
+ @project_or_group.is_a?(::Group)
+ end
+ end
+end
diff --git a/app/finders/packages/group_packages_finder.rb b/app/finders/packages/group_packages_finder.rb
index 8771bf90e75..e753fa4d455 100644
--- a/app/finders/packages/group_packages_finder.rb
+++ b/app/finders/packages/group_packages_finder.rb
@@ -20,19 +20,19 @@ module Packages
attr_reader :current_user, :group, :params
- def packages_for_group_projects
+ def packages_for_group_projects(installable_only: false)
packages = ::Packages::Package
.including_build_info
.including_project_route
.including_tags
.for_projects(group_projects_visible_to_current_user.select(:id))
- .processed
.sort_by_attribute("#{params[:order_by]}_#{params[:sort]}")
packages = filter_with_version(packages)
packages = filter_by_package_type(packages)
packages = filter_by_package_name(packages)
- filter_by_status(packages)
+ packages = filter_by_package_version(packages)
+ installable_only ? packages.installable : filter_by_status(packages)
end
def group_projects_visible_to_current_user
diff --git a/app/finders/packages/maven/package_finder.rb b/app/finders/packages/maven/package_finder.rb
index eefcdaba3c8..cc28d951f52 100644
--- a/app/finders/packages/maven/package_finder.rb
+++ b/app/finders/packages/maven/package_finder.rb
@@ -2,83 +2,23 @@
module Packages
module Maven
- class PackageFinder
- include ::Packages::FinderHelper
- include Gitlab::Utils::StrongMemoize
-
- def initialize(path, current_user, project: nil, group: nil, order_by_package_file: false)
- @path = path
- @current_user = current_user
- @project = project
- @group = group
- @order_by_package_file = order_by_package_file
- end
-
+ class PackageFinder < ::Packages::GroupOrProjectPackageFinder
def execute
- packages_with_path.last
+ packages.last
end
def execute!
- packages_with_path.last!
+ packages.last!
end
private
- def base
- if @project
- packages_for_a_single_project
- elsif @group
- packages_for_multiple_projects
- else
- ::Packages::Package.none
- end
- end
-
- def packages_with_path
- matching_packages = base.only_maven_packages_with_path(@path, use_cte: @group.present?)
-
- if group_level_improvements?
- matching_packages = matching_packages.order_by_package_file if @order_by_package_file
- else
- matching_packages = matching_packages.order_by_package_file if versionless_package?(matching_packages)
- end
+ def packages
+ matching_packages = base.only_maven_packages_with_path(@params[:path], use_cte: group?)
+ matching_packages = matching_packages.order_by_package_file if @params[:order_by_package_file]
matching_packages
end
-
- def versionless_package?(matching_packages)
- return if matching_packages.empty?
-
- # if one matching package is versionless, they all are.
- matching_packages.first&.version.nil?
- end
-
- # Produces a query that retrieves packages from a single project.
- def packages_for_a_single_project
- @project.packages
- end
-
- # Produces a query that retrieves packages from multiple projects that
- # the current user can view within a group.
- def packages_for_multiple_projects
- if group_level_improvements?
- packages_visible_to_user(@current_user, within_group: @group)
- else
- ::Packages::Package.for_projects(projects_visible_to_current_user)
- end
- end
-
- # Returns the projects that the current user can view within a group.
- def projects_visible_to_current_user
- @group.all_projects
- .public_or_visible_to_user(@current_user)
- end
-
- def group_level_improvements?
- strong_memoize(:group_level_improvements) do
- Feature.enabled?(:maven_packages_group_level_improvements, default_enabled: :yaml)
- end
- end
end
end
end
diff --git a/app/finders/packages/npm/package_finder.rb b/app/finders/packages/npm/package_finder.rb
index 3b79785d0e1..92ceac297ee 100644
--- a/app/finders/packages/npm/package_finder.rb
+++ b/app/finders/packages/npm/package_finder.rb
@@ -14,6 +14,7 @@ module Packages
def execute
base.npm
.with_name(@package_name)
+ .installable
.last_of_each_version
.preload_files
end
diff --git a/app/finders/packages/nuget/package_finder.rb b/app/finders/packages/nuget/package_finder.rb
index 2f66bd145ee..9ae52745bb2 100644
--- a/app/finders/packages/nuget/package_finder.rb
+++ b/app/finders/packages/nuget/package_finder.rb
@@ -2,51 +2,22 @@
module Packages
module Nuget
- class PackageFinder
- include ::Packages::FinderHelper
-
+ class PackageFinder < ::Packages::GroupOrProjectPackageFinder
MAX_PACKAGES_COUNT = 300
- def initialize(current_user, project_or_group, package_name:, package_version: nil, limit: MAX_PACKAGES_COUNT)
- @current_user = current_user
- @project_or_group = project_or_group
- @package_name = package_name
- @package_version = package_version
- @limit = limit
- end
-
def execute
- packages.limit_recent(@limit)
+ packages.limit_recent(@params[:limit] || MAX_PACKAGES_COUNT)
end
private
- def base
- if project?
- @project_or_group.packages
- elsif group?
- packages_visible_to_user(@current_user, within_group: @project_or_group)
- else
- ::Packages::Package.none
- end
- end
-
def packages
result = base.nuget
.has_version
- .processed
- .with_name_like(@package_name)
- result = result.with_version(@package_version) if @package_version.present?
+ .with_name_like(@params[:package_name])
+ result = result.with_version(@params[:package_version]) if @params[:package_version].present?
result
end
-
- def project?
- @project_or_group.is_a?(::Project)
- end
-
- def group?
- @project_or_group.is_a?(::Group)
- end
end
end
end
diff --git a/app/finders/packages/package_finder.rb b/app/finders/packages/package_finder.rb
index f1874b77845..ee96896e350 100644
--- a/app/finders/packages/package_finder.rb
+++ b/app/finders/packages/package_finder.rb
@@ -12,7 +12,7 @@ module Packages
.including_build_info
.including_project_route
.including_tags
- .processed
+ .displayable
.find(@package_id)
end
end
diff --git a/app/finders/packages/packages_finder.rb b/app/finders/packages/packages_finder.rb
index 840cbbf7b9d..552468ecfd1 100644
--- a/app/finders/packages/packages_finder.rb
+++ b/app/finders/packages/packages_finder.rb
@@ -17,7 +17,6 @@ module Packages
.including_build_info
.including_project_route
.including_tags
- .processed
packages = filter_with_version(packages)
packages = filter_by_package_type(packages)
packages = filter_by_package_name(packages)
diff --git a/app/finders/packages/pypi/package_finder.rb b/app/finders/packages/pypi/package_finder.rb
new file mode 100644
index 00000000000..574e9770363
--- /dev/null
+++ b/app/finders/packages/pypi/package_finder.rb
@@ -0,0 +1,17 @@
+# frozen_string_literal: true
+
+module Packages
+ module Pypi
+ class PackageFinder < ::Packages::GroupOrProjectPackageFinder
+ def execute
+ packages.by_file_name_and_sha256(@params[:filename], @params[:sha256])
+ end
+
+ private
+
+ def packages
+ base.pypi.has_version
+ end
+ end
+ end
+end
diff --git a/app/finders/packages/pypi/packages_finder.rb b/app/finders/packages/pypi/packages_finder.rb
new file mode 100644
index 00000000000..642ca2cf2e6
--- /dev/null
+++ b/app/finders/packages/pypi/packages_finder.rb
@@ -0,0 +1,20 @@
+# frozen_string_literal: true
+
+module Packages
+ module Pypi
+ class PackagesFinder < ::Packages::GroupOrProjectPackageFinder
+ def execute!
+ results = packages.with_normalized_pypi_name(@params[:package_name])
+ raise ActiveRecord::RecordNotFound if results.empty?
+
+ results
+ end
+
+ private
+
+ def packages
+ base.pypi.has_version
+ end
+ end
+ end
+end
diff --git a/app/finders/projects/groups_finder.rb b/app/finders/projects/groups_finder.rb
index d0c42ad5611..fb98edabf58 100644
--- a/app/finders/projects/groups_finder.rb
+++ b/app/finders/projects/groups_finder.rb
@@ -7,6 +7,7 @@
# current_user - which user is requesting groups
# params:
# with_shared: boolean (optional)
+# shared_visible_only: boolean (optional)
# shared_min_access_level: integer (optional)
# skip_groups: array of integers (optional)
#
@@ -37,25 +38,35 @@ module Projects
Ability.allowed?(current_user, :read_project, project)
end
- # rubocop: disable CodeReuse/ActiveRecord
def all_groups
groups = []
- groups << project.group.self_and_ancestors if project.group
+ groups += [project.group.self_and_ancestors] if project.group
+ groups += with_shared_groups if params[:with_shared]
+
+ return [Group.none] if groups.compact.empty?
- if params[:with_shared]
- shared_groups = project.invited_groups
+ groups
+ end
- if params[:shared_min_access_level]
- shared_groups = shared_groups.where(
- 'project_group_links.group_access >= ?', params[:shared_min_access_level]
- )
- end
+ def with_shared_groups
+ shared_groups = project.invited_groups
+ shared_groups = apply_min_access_level(shared_groups)
- groups << shared_groups
+ if params[:shared_visible_only]
+ [
+ shared_groups.public_to_user(current_user),
+ shared_groups.for_authorized_group_members(current_user&.id)
+ ]
+ else
+ [shared_groups]
end
+ end
- groups << Group.none if groups.compact.empty?
- groups
+ # rubocop: disable CodeReuse/ActiveRecord
+ def apply_min_access_level(groups)
+ return groups unless params[:shared_min_access_level]
+
+ groups.where('project_group_links.group_access >= ?', params[:shared_min_access_level])
end
# rubocop: enable CodeReuse/ActiveRecord
diff --git a/app/finders/projects/members/effective_access_level_finder.rb b/app/finders/projects/members/effective_access_level_finder.rb
new file mode 100644
index 00000000000..2880d6667ce
--- /dev/null
+++ b/app/finders/projects/members/effective_access_level_finder.rb
@@ -0,0 +1,125 @@
+# frozen_string_literal: true
+
+module Projects
+ module Members
+ class EffectiveAccessLevelFinder
+ include Gitlab::Utils::StrongMemoize
+
+ USER_ID_AND_ACCESS_LEVEL = [:user_id, :access_level].freeze
+ BATCH_SIZE = 5
+
+ def initialize(project)
+ @project = project
+ end
+
+ def execute
+ return Member.none if no_members?
+
+ # rubocop: disable CodeReuse/ActiveRecord
+ Member.from(generate_from_statement(user_ids_and_access_levels_from_all_memberships))
+ .select([:user_id, 'MAX(access_level) AS access_level'])
+ .group(:user_id)
+ # rubocop: enable CodeReuse/ActiveRecord
+ end
+
+ private
+
+ attr_reader :project
+
+ def generate_from_statement(user_ids_and_access_levels)
+ "(VALUES #{generate_values_expression(user_ids_and_access_levels)}) members (user_id, access_level)"
+ end
+
+ def generate_values_expression(user_ids_and_access_levels)
+ user_ids_and_access_levels.map do |user_id, access_level|
+ "(#{user_id}, #{access_level})"
+ end.join(",")
+ end
+
+ def no_members?
+ user_ids_and_access_levels_from_all_memberships.blank?
+ end
+
+ def all_possible_avenues_of_membership
+ avenues = [authorizable_project_members]
+
+ avenues << if project.personal?
+ project_owner_acting_as_maintainer
+ else
+ authorizable_group_members
+ end
+
+ if include_membership_from_project_group_shares?
+ avenues << members_from_project_group_shares
+ end
+
+ avenues
+ end
+
+ # @return [Array<[user_id, access_level]>]
+ def user_ids_and_access_levels_from_all_memberships
+ strong_memoize(:user_ids_and_access_levels_from_all_memberships) do
+ all_possible_avenues_of_membership.flat_map do |relation|
+ relation.pluck(*USER_ID_AND_ACCESS_LEVEL) # rubocop: disable CodeReuse/ActiveRecord
+ end
+ end
+ end
+
+ def authorizable_project_members
+ project.members.authorizable
+ end
+
+ def authorizable_group_members
+ project.group.authorizable_members_with_parents
+ end
+
+ def members_from_project_group_shares
+ members = []
+
+ project.project_group_links.each_batch(of: BATCH_SIZE) do |relation|
+ members_per_batch = []
+
+ relation.includes(:group).each do |link| # rubocop: disable CodeReuse/ActiveRecord
+ members_per_batch << link.group.authorizable_members_with_parents.select(*user_id_and_access_level_for_project_group_shares(link))
+ end
+
+ members << Member.from_union(members_per_batch)
+ end
+
+ members.flatten
+ end
+
+ def project_owner_acting_as_maintainer
+ user_id = project.namespace.owner.id
+ access_level = Gitlab::Access::MAINTAINER
+
+ Member
+ .from(generate_from_statement([[user_id, access_level]])) # rubocop: disable CodeReuse/ActiveRecord
+ .limit(1)
+ end
+
+ def include_membership_from_project_group_shares?
+ project.allowed_to_share_with_group? && project.project_group_links.any?
+ end
+
+ # methods for `select` options
+
+ def user_id_and_access_level_for_project_group_shares(link)
+ least_access_level_among_group_membership_and_project_share =
+ smallest_value_arel([link.group_access, GroupMember.arel_table[:access_level]], 'access_level')
+
+ [
+ :user_id,
+ least_access_level_among_group_membership_and_project_share
+ ]
+ end
+
+ def smallest_value_arel(args, column_alias)
+ Arel::Nodes::As.new(
+ Arel::Nodes::NamedFunction.new('LEAST', args),
+ Arel.sql(column_alias)
+ )
+ end
+ end
+ end
+end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index 893e89daa3c..272747a124e 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -247,4 +247,4 @@ class ProjectsFinder < UnionFinder
end
end
-ProjectsFinder.prepend_if_ee('::EE::ProjectsFinder')
+ProjectsFinder.prepend_mod_with('ProjectsFinder')
diff --git a/app/finders/repositories/branch_names_finder.rb b/app/finders/repositories/branch_names_finder.rb
index 5bb67425aa5..8c8c7405407 100644
--- a/app/finders/repositories/branch_names_finder.rb
+++ b/app/finders/repositories/branch_names_finder.rb
@@ -10,9 +10,9 @@ module Repositories
end
def execute
- return unless search
+ return unless search && offset && limit
- repository.search_branch_names(search)
+ repository.search_branch_names(search).lazy.drop(offset).take(limit) # rubocop:disable CodeReuse/ActiveRecord
end
private
@@ -20,5 +20,13 @@ module Repositories
def search
@params[:search].presence
end
+
+ def offset
+ @params[:offset]
+ end
+
+ def limit
+ @params[:limit]
+ end
end
end
diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb
index 941abb70400..81643826782 100644
--- a/app/finders/snippets_finder.rb
+++ b/app/finders/snippets_finder.rb
@@ -221,4 +221,4 @@ class SnippetsFinder < UnionFinder
end
end
-SnippetsFinder.prepend_if_ee('EE::SnippetsFinder')
+SnippetsFinder.prepend_mod_with('SnippetsFinder')
diff --git a/app/finders/template_finder.rb b/app/finders/template_finder.rb
index 739beee236c..0f5622f2df0 100644
--- a/app/finders/template_finder.rb
+++ b/app/finders/template_finder.rb
@@ -7,7 +7,6 @@ class TemplateFinder
dockerfiles: ::Gitlab::Template::DockerfileTemplate,
gitignores: ::Gitlab::Template::GitignoreTemplate,
gitlab_ci_ymls: ::Gitlab::Template::GitlabCiYmlTemplate,
- gitlab_ci_syntax_ymls: ::Gitlab::Template::GitlabCiSyntaxYmlTemplate,
metrics_dashboard_ymls: ::Gitlab::Template::MetricsDashboardTemplate,
issues: ::Gitlab::Template::IssueTemplate,
merge_requests: ::Gitlab::Template::MergeRequestTemplate
@@ -71,4 +70,4 @@ class TemplateFinder
end
end
-TemplateFinder.prepend_if_ee('::EE::TemplateFinder')
+TemplateFinder.prepend_mod_with('TemplateFinder')
diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb
index f28e1281488..e83018ed24c 100644
--- a/app/finders/todos_finder.rb
+++ b/app/finders/todos_finder.rb
@@ -215,4 +215,4 @@ class TodosFinder
end
end
-TodosFinder.prepend_if_ee('EE::TodosFinder')
+TodosFinder.prepend_mod_with('TodosFinder')
diff --git a/app/finders/users_finder.rb b/app/finders/users_finder.rb
index 5ac905e0dd4..8054ecbd502 100644
--- a/app/finders/users_finder.rb
+++ b/app/finders/users_finder.rb
@@ -143,4 +143,4 @@ class UsersFinder
# rubocop: enable CodeReuse/ActiveRecord
end
-UsersFinder.prepend_if_ee('EE::UsersFinder')
+UsersFinder.prepend_mod_with('UsersFinder')
diff --git a/app/finders/users_with_pending_todos_finder.rb b/app/finders/users_with_pending_todos_finder.rb
deleted file mode 100644
index 461bd92a366..00000000000
--- a/app/finders/users_with_pending_todos_finder.rb
+++ /dev/null
@@ -1,16 +0,0 @@
-# frozen_string_literal: true
-
-# Finder that given a target (e.g. an issue) finds all the users that have
-# pending todos for said target.
-class UsersWithPendingTodosFinder
- attr_reader :target
-
- # target - The target, such as an Issue or MergeRequest.
- def initialize(target)
- @target = target
- end
-
- def execute
- User.for_todos(target.todos.pending)
- end
-end