diff options
Diffstat (limited to 'app/finders')
40 files changed, 756 insertions, 212 deletions
diff --git a/app/finders/abuse_reports_finder.rb b/app/finders/abuse_reports_finder.rb index 04043f36426..6a6d0413194 100644 --- a/app/finders/abuse_reports_finder.rb +++ b/app/finders/abuse_reports_finder.rb @@ -1,18 +1,93 @@ # frozen_string_literal: true class AbuseReportsFinder - attr_reader :params + attr_reader :params, :reports + + DEFAULT_STATUS_FILTER = 'open' + DEFAULT_SORT = 'created_at_desc' + ALLOWED_SORT = [DEFAULT_SORT, *%w[created_at_asc updated_at_desc updated_at_asc]].freeze def initialize(params = {}) @params = params + @reports = AbuseReport.all end def execute - reports = AbuseReport.all - reports = reports.by_user(params[:user_id]) if params[:user_id].present? + filter_reports + sort_reports + + reports.with_users.page(params[:page]) + end + + private + + def filter_reports + filter_by_user_id + + filter_by_user + filter_by_reporter + filter_by_status + filter_by_category + end + + def filter_by_status + return unless Feature.enabled?(:abuse_reports_list) + return unless params[:status].present? + + status = params[:status] + status = DEFAULT_STATUS_FILTER unless status.in?(AbuseReport.statuses.keys) + + case status + when 'open' + @reports = @reports.open + when 'closed' + @reports = @reports.closed + end + end + + def filter_by_category + return unless params[:category].present? + + @reports = @reports.by_category(params[:category]) + end + + def filter_by_user + return unless params[:user].present? + + user_id = find_user_id(params[:user]) + return unless user_id + + @reports = @reports.by_user_id(user_id) + end + + def filter_by_reporter + return unless params[:reporter].present? + + user_id = find_user_id(params[:reporter]) + return unless user_id + + @reports = @reports.by_reporter_id(user_id) + end + + def filter_by_user_id + return unless params[:user_id].present? + + @reports = @reports.by_user_id(params[:user_id]) + end + + def sort_reports + if Feature.disabled?(:abuse_reports_list) + @reports = @reports.with_order_id_desc + return + end + + sort_by = params[:sort] + sort_by = DEFAULT_SORT unless sort_by.in?(ALLOWED_SORT) + + @reports = @reports.order_by(sort_by) + end - reports.with_order_id_desc - .with_users - .page(params[:page]) + def find_user_id(username) + User.by_username(username).pick(:id) end end diff --git a/app/finders/access_requests_finder.rb b/app/finders/access_requests_finder.rb index 7b98df68f29..140d68cfe91 100644 --- a/app/finders/access_requests_finder.rb +++ b/app/finders/access_requests_finder.rb @@ -18,11 +18,7 @@ class AccessRequestsFinder def execute!(current_user) raise Gitlab::Access::AccessDeniedError unless can_see_access_requests?(current_user) - if Feature.enabled?(:project_members_index_by_project_namespace, source) - source.namespace_requesters - else - source.requesters - end + source.namespace_requesters end private diff --git a/app/finders/achievements/achievements_finder.rb b/app/finders/achievements/achievements_finder.rb new file mode 100644 index 00000000000..98bd12afcd4 --- /dev/null +++ b/app/finders/achievements/achievements_finder.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +module Achievements + class AchievementsFinder + attr_reader :namespace, :params + + def initialize(namespace, params = {}) + @namespace = namespace + @params = params + end + + def execute + achievements = namespace.achievements + by_ids(achievements) + end + + private + + def by_ids(achievements) + return achievements unless ids? + + achievements.id_in(params[:ids]) + end + + def ids? + params[:ids].present? + end + end +end diff --git a/app/finders/autocomplete/users_finder.rb b/app/finders/autocomplete/users_finder.rb index bb91f84de99..7ecf5c98ac0 100644 --- a/app/finders/autocomplete/users_finder.rb +++ b/app/finders/autocomplete/users_finder.rb @@ -11,8 +11,8 @@ module Autocomplete LIMIT = 20 attr_reader :current_user, :project, :group, :search, :skip_users, - :author_id, :todo_filter, :todo_state_filter, - :filter_by_current_user, :states + :author_id, :todo_filter, :todo_state_filter, + :filter_by_current_user, :states def initialize(params:, current_user:, project:, group:) @current_user = current_user @@ -98,7 +98,7 @@ module Autocomplete # rubocop: disable CodeReuse/ActiveRecord def preload_associations(items) - ActiveRecord::Associations::Preloader.new.preload(items, :status) + ActiveRecord::Associations::Preloader.new(records: items, associations: :status).call end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/ci/pipelines_finder.rb b/app/finders/ci/pipelines_finder.rb index a2d1805286d..e52fc510628 100644 --- a/app/finders/ci/pipelines_finder.rb +++ b/app/finders/ci/pipelines_finder.rb @@ -2,6 +2,8 @@ module Ci class PipelinesFinder + include UpdatedAtFilter + attr_reader :project, :pipelines, :params, :current_user ALLOWED_INDEXED_COLUMNS = %w[id status ref updated_at user_id].freeze @@ -146,13 +148,6 @@ module Ci end # rubocop: enable CodeReuse/ActiveRecord - def by_updated_at(items) - items = items.updated_before(params[:updated_before]) if params[:updated_before].present? - items = items.updated_after(params[:updated_after]) if params[:updated_after].present? - - items - end - def by_name(items) return items unless Feature.enabled?(:pipeline_name_search, project) && diff --git a/app/finders/ci/runners_finder.rb b/app/finders/ci/runners_finder.rb index bc1dcb3ad5f..5f03ae77338 100644 --- a/app/finders/ci/runners_finder.rb +++ b/app/finders/ci/runners_finder.rb @@ -74,7 +74,7 @@ module Ci end def project_runners - raise Gitlab::Access::AccessDeniedError unless can?(@current_user, :admin_project, @project) + raise Gitlab::Access::AccessDeniedError unless can?(@current_user, :read_project_runners, @project) @runners = ::Ci::Runner.owned_or_instance_wide(@project.id) end diff --git a/app/finders/clusters/agent_authorizations_finder.rb b/app/finders/clusters/agent_authorizations_finder.rb deleted file mode 100644 index 70c0868cc7f..00000000000 --- a/app/finders/clusters/agent_authorizations_finder.rb +++ /dev/null @@ -1,69 +0,0 @@ -# frozen_string_literal: true - -module Clusters - class AgentAuthorizationsFinder - def initialize(project) - @project = project - end - - def execute - # closest, most-specific authorization for a given agent wins - (project_authorizations + implicit_authorizations + group_authorizations) - .uniq(&:agent_id) - end - - private - - attr_reader :project - - def implicit_authorizations - project.cluster_agents.map do |agent| - Clusters::Agents::ImplicitAuthorization.new(agent: agent) - end - end - - # rubocop: disable CodeReuse/ActiveRecord - def project_authorizations - namespace_ids = project.group ? all_namespace_ids : project.namespace_id - - Clusters::Agents::ProjectAuthorization - .where(project_id: project.id) - .joins(agent: :project) - .preload(agent: :project) - .where(cluster_agents: { projects: { namespace_id: namespace_ids } }) - .with_available_ci_access_fields(project) - .to_a - end - - def group_authorizations - return [] unless project.group - - authorizations = Clusters::Agents::GroupAuthorization.arel_table - - ordered_ancestors_cte = Gitlab::SQL::CTE.new( - :ordered_ancestors, - project.group.self_and_ancestors(hierarchy_order: :asc).reselect(:id) - ) - - cte_join_sources = authorizations.join(ordered_ancestors_cte.table).on( - authorizations[:group_id].eq(ordered_ancestors_cte.table[:id]) - ).join_sources - - Clusters::Agents::GroupAuthorization - .with(ordered_ancestors_cte.to_arel) - .joins(cte_join_sources) - .joins(agent: :project) - .with_available_ci_access_fields(project) - .where(projects: { namespace_id: all_namespace_ids }) - .order(Arel.sql('agent_id, array_position(ARRAY(SELECT id FROM ordered_ancestors)::bigint[], agent_group_authorizations.group_id)')) - .select('DISTINCT ON (agent_id) agent_group_authorizations.*') - .preload(agent: :project) - .to_a - end - # rubocop: enable CodeReuse/ActiveRecord - - def all_namespace_ids - project.root_ancestor.self_and_descendants.select(:id) - end - end -end diff --git a/app/finders/clusters/agent_tokens_finder.rb b/app/finders/clusters/agent_tokens_finder.rb index 72692777bc6..9ec00245250 100644 --- a/app/finders/clusters/agent_tokens_finder.rb +++ b/app/finders/clusters/agent_tokens_finder.rb @@ -11,21 +11,31 @@ module Clusters end def execute - return ::Clusters::AgentToken.none unless can_read_cluster_agents? + return ::Clusters::AgentToken.none unless can_read_cluster_agent? - agent.agent_tokens.then { |agent_tokens| by_status(agent_tokens) } + agent_tokens_by_status end private attr_reader :agent, :current_user, :params - def by_status(agent_tokens) - params[:status].present? ? agent_tokens.with_status(params[:status]) : agent_tokens + def agent_tokens_by_status + # If the `status` parameter is set to `active`, we use the `active_agent_tokens` scope + # in case this called from GraphQL's AgentTokensResolver. This prevents a repeat query + # to the database, because `active_agent_tokens` is already preloaded in the AgentsResolver + return agent.active_agent_tokens if active_tokens_only? + + # Else, we use the `agent_tokens` scope combined with `with_status` if necessary + params[:status].present? ? agent.agent_tokens.with_status(params[:status]) : agent.agent_tokens + end + + def active_tokens_only? + params[:status].present? && params[:status].to_sym == :active end - def can_read_cluster_agents? - current_user&.can?(:read_cluster, agent&.project) + def can_read_cluster_agent? + current_user&.can?(:read_cluster_agent, agent) end end end diff --git a/app/finders/clusters/agents/authorizations/ci_access/finder.rb b/app/finders/clusters/agents/authorizations/ci_access/finder.rb new file mode 100644 index 00000000000..97d378669a4 --- /dev/null +++ b/app/finders/clusters/agents/authorizations/ci_access/finder.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module CiAccess + class Finder + def initialize(project) + @project = project + end + + def execute + # closest, most-specific authorization for a given agent wins + (project_authorizations + implicit_authorizations + group_authorizations) + .uniq(&:agent_id) + end + + private + + attr_reader :project + + def implicit_authorizations + project.cluster_agents.map do |agent| + Clusters::Agents::Authorizations::CiAccess::ImplicitAuthorization.new(agent: agent) + end + end + + # rubocop: disable CodeReuse/ActiveRecord + def project_authorizations + namespace_ids = project.group ? all_namespace_ids : project.namespace_id + + Clusters::Agents::Authorizations::CiAccess::ProjectAuthorization + .where(project_id: project.id) + .joins(agent: :project) + .preload(agent: :project) + .where(cluster_agents: { projects: { namespace_id: namespace_ids } }) + .with_available_ci_access_fields(project) + .to_a + end + + def group_authorizations + return [] unless project.group + + authorizations = Clusters::Agents::Authorizations::CiAccess::GroupAuthorization.arel_table + + ordered_ancestors_cte = Gitlab::SQL::CTE.new( + :ordered_ancestors, + project.group.self_and_ancestors(hierarchy_order: :asc).reselect(:id) + ) + + cte_join_sources = authorizations.join(ordered_ancestors_cte.table).on( + authorizations[:group_id].eq(ordered_ancestors_cte.table[:id]) + ).join_sources + + Clusters::Agents::Authorizations::CiAccess::GroupAuthorization + .with(ordered_ancestors_cte.to_arel) + .joins(cte_join_sources) + .joins(agent: :project) + .with_available_ci_access_fields(project) + .where(projects: { namespace_id: all_namespace_ids }) + .order(Arel.sql('agent_id, array_position(ARRAY(SELECT id FROM ordered_ancestors)::bigint[], agent_group_authorizations.group_id)')) + .select('DISTINCT ON (agent_id) agent_group_authorizations.*') + .preload(agent: :project) + .to_a + end + # rubocop: enable CodeReuse/ActiveRecord + + def all_namespace_ids + project.root_ancestor.self_and_descendants.select(:id) + end + end + end + end + end +end diff --git a/app/finders/clusters/agents/authorizations/user_access/finder.rb b/app/finders/clusters/agents/authorizations/user_access/finder.rb new file mode 100644 index 00000000000..bde75fa46cb --- /dev/null +++ b/app/finders/clusters/agents/authorizations/user_access/finder.rb @@ -0,0 +1,69 @@ +# frozen_string_literal: true + +module Clusters + module Agents + module Authorizations + module UserAccess + class Finder + def initialize(user, agent: nil, project: nil, preload: true, limit: nil) + @user = user + @agent = agent + @project = project + @limit = limit + @preload = preload + end + + def execute + project_authorizations + group_authorizations + end + + private + + attr_reader :user, :agent, :project, :preload, :limit + + def project_authorizations + authorizations = Clusters::Agents::Authorizations::UserAccess::ProjectAuthorization.for_user(user) + authorizations = filter_by_agent(authorizations) + authorizations = filter_by_project(authorizations) + authorizations = apply_limit(authorizations) + authorizations = apply_preload(authorizations) + authorizations.to_a + end + + def group_authorizations + authorizations = Clusters::Agents::Authorizations::UserAccess::GroupAuthorization.for_user(user) + authorizations = filter_by_agent(authorizations) + authorizations = filter_by_project(authorizations) + authorizations = apply_limit(authorizations) + authorizations = apply_preload(authorizations) + authorizations.to_a + end + + def filter_by_agent(authorizations) + return authorizations unless agent.present? + + authorizations.for_agent(agent) + end + + def filter_by_project(authorizations) + return authorizations unless project.present? + + authorizations.for_project(project) + end + + def apply_limit(authorizations) + return authorizations unless limit.present? + + authorizations.limit(limit) + end + + def apply_preload(authorizations) + return authorizations unless preload + + authorizations.preloaded + end + end + end + end + end +end diff --git a/app/finders/clusters/agents_finder.rb b/app/finders/clusters/agents_finder.rb index 14277db3f85..0cdebe93f32 100644 --- a/app/finders/clusters/agents_finder.rb +++ b/app/finders/clusters/agents_finder.rb @@ -29,7 +29,7 @@ module Clusters end def can_read_cluster_agents? - current_user&.can?(:read_cluster, object) + current_user&.can?(:read_cluster_agent, object) end end end diff --git a/app/finders/concerns/finder_with_group_hierarchy.rb b/app/finders/concerns/finder_with_group_hierarchy.rb index 4ced544ba2c..99b58fa6954 100644 --- a/app/finders/concerns/finder_with_group_hierarchy.rb +++ b/app/finders/concerns/finder_with_group_hierarchy.rb @@ -27,11 +27,7 @@ module FinderWithGroupHierarchy # we can preset root group for all of them to optimize permission checks Group.preset_root_ancestor_for(groups) - # Preloading the max access level for the given groups to avoid N+1 queries - # during the access check. - if !skip_authorization && current_user && Feature.enabled?(:preload_max_access_levels_for_labels_finder, group) - Preloaders::UserMaxAccessLevelInGroupsPreloader.new(groups, current_user).execute - end + preload_associations(groups) if !skip_authorization && current_user groups_user_can_read_items(groups).map(&:id) end @@ -77,4 +73,10 @@ module FinderWithGroupHierarchy groups.select { |group| authorized_to_read_item?(group) } end end + + def preload_associations(groups) + Preloaders::UserMaxAccessLevelInGroupsPreloader.new(groups, current_user).execute + end end + +FinderWithGroupHierarchy.prepend_mod_with('FinderWithGroupHierarchy') diff --git a/app/finders/concerns/updated_at_filter.rb b/app/finders/concerns/updated_at_filter.rb new file mode 100644 index 00000000000..0e9a3fb5e8c --- /dev/null +++ b/app/finders/concerns/updated_at_filter.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +module UpdatedAtFilter + def by_updated_at(items) + updated_before = params[:updated_before]&.in_time_zone + updated_after = params[:updated_after]&.in_time_zone + return items.none if [updated_before, updated_after].all?(&:present?) && updated_before < updated_after + + items = items.updated_before(updated_before) if updated_before.present? + items = items.updated_after(updated_after) if updated_after.present? + + items + end +end diff --git a/app/finders/context_commits_finder.rb b/app/finders/context_commits_finder.rb index 4a45817cc61..a186ca92c7b 100644 --- a/app/finders/context_commits_finder.rb +++ b/app/finders/context_commits_finder.rb @@ -21,20 +21,24 @@ class ContextCommitsFinder attr_reader :project, :merge_request, :search, :author, :committed_before, :committed_after, :limit def init_collection - if search.present? + if search_params_present? search_commits else project.repository.commits(merge_request.target_branch, { limit: limit }) end end + def search_params_present? + [search, author, committed_before, committed_after].map(&:present?).any? + end + def filter_existing_commits(commits) commits.select! { |commit| already_included_ids.exclude?(commit.id) } commits end def search_commits - key = search.strip + key = search&.strip commits = [] if Commit.valid_hash?(key) mr_existing_commits_ids = merge_request.commits.map(&:id) diff --git a/app/finders/data_transfer/group_data_transfer_finder.rb b/app/finders/data_transfer/group_data_transfer_finder.rb new file mode 100644 index 00000000000..19ab99d4477 --- /dev/null +++ b/app/finders/data_transfer/group_data_transfer_finder.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module DataTransfer + class GroupDataTransferFinder + def initialize(group:, from:, to:, user:) + @group = group + @from = from + @to = to + @user = user + end + + def execute + return ::Projects::DataTransfer.none unless Ability.allowed?(user, :read_usage_quotas, group) + + ::Projects::DataTransfer + .with_namespace_between_dates(group, from, to) + .select('SUM(repository_egress + + artifacts_egress + + packages_egress + + registry_egress + ) as total_egress, + SUM(repository_egress) as repository_egress, + SUM(artifacts_egress) as artifacts_egress, + SUM(packages_egress) as packages_egress, + SUM(registry_egress) as registry_egress, + date, + namespace_id') + end + + private + + attr_reader :group, :from, :to, :user + end +end diff --git a/app/finders/data_transfer/mocked_transfer_finder.rb b/app/finders/data_transfer/mocked_transfer_finder.rb new file mode 100644 index 00000000000..9c5551005ea --- /dev/null +++ b/app/finders/data_transfer/mocked_transfer_finder.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +# Mocked data for data transfer +# Follow this epic for recent progress: https://gitlab.com/groups/gitlab-org/-/epics/9330 +module DataTransfer + class MockedTransferFinder + def execute + start_date = Date.new(2023, 0o1, 0o1) + date_for_index = ->(i) { (start_date + i.months).strftime('%Y-%m-%d') } + + 0.upto(11).map do |i| + { + date: date_for_index.call(i), + repository_egress: rand(70000..550000), + artifacts_egress: rand(70000..550000), + packages_egress: rand(70000..550000), + registry_egress: rand(70000..550000) + }.tap do |hash| + hash[:total_egress] = hash + .slice(:repository_egress, :artifacts_egress, :packages_egress, :registry_egress) + .values + .sum + end + end + end + end +end diff --git a/app/finders/data_transfer/project_data_transfer_finder.rb b/app/finders/data_transfer/project_data_transfer_finder.rb new file mode 100644 index 00000000000..bcabbdb00a5 --- /dev/null +++ b/app/finders/data_transfer/project_data_transfer_finder.rb @@ -0,0 +1,25 @@ +# frozen_string_literal: true + +module DataTransfer + class ProjectDataTransferFinder + def initialize(project:, from:, to:, user:) + @project = project + @from = from + @to = to + @user = user + end + + def execute + return ::Projects::DataTransfer.none unless Ability.allowed?(user, :read_usage_quotas, project) + + ::Projects::DataTransfer + .with_project_between_dates(project, from, to) + .select(:project_id, :date, :repository_egress, :artifacts_egress, :packages_egress, :registry_egress, + "repository_egress + artifacts_egress + packages_egress + registry_egress as total_egress") + end + + private + + attr_reader :project, :from, :to, :user + end +end diff --git a/app/finders/deployments_finder.rb b/app/finders/deployments_finder.rb index 21869f6f31d..316dffcb3b2 100644 --- a/app/finders/deployments_finder.rb +++ b/app/finders/deployments_finder.rb @@ -14,6 +14,8 @@ # order_by: String (see ALLOWED_SORT_VALUES constant) # sort: String (asc | desc) class DeploymentsFinder + include UpdatedAtFilter + attr_reader :params # Warning: @@ -50,10 +52,6 @@ class DeploymentsFinder private - def raise_for_inefficient_updated_at_query? - params.fetch(:raise_for_inefficient_updated_at_query, Rails.env.development? || Rails.env.test?) - end - 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' @@ -61,12 +59,20 @@ class DeploymentsFinder # 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?) - error = InefficientQueryError.new('`updated_at` filter and `updated_at` sorting must be paired') + if filter_by_updated_at? && !order_by_updated_at? + error = InefficientQueryError.new('`updated_at` filter requires `updated_at` sort') Gitlab::ErrorTracking.log_exception(error) - raise error if raise_for_inefficient_updated_at_query? + # We are adding a Feature Flag to introduce the breaking change indicated in + # https://gitlab.com/gitlab-org/gitlab/-/issues/328500 + # We are also adding a way to override this flag for special case users that + # are running into large volume of errors when the flag is enabled. + # These Feature Flags must be removed by 16.1 + if Feature.enabled?(:deployments_raise_updated_at_inefficient_error) && + Feature.disabled?(:deployments_raise_updated_at_inefficient_error_override, params[:project]) + raise error + end end if filter_by_finished_at? && !order_by_finished_at? @@ -109,13 +115,6 @@ class DeploymentsFinder items.order(sort_params) # rubocop: disable CodeReuse/ActiveRecord end - def by_updated_at(items) - items = items.updated_before(params[:updated_before]) if params[:updated_before].present? - items = items.updated_after(params[:updated_after]) if params[:updated_after].present? - - items - end - def by_finished_at(items) items = items.finished_before(params[:finished_before]) if params[:finished_before].present? items = items.finished_after(params[:finished_after]) if params[:finished_after].present? diff --git a/app/finders/fork_targets_finder.rb b/app/finders/fork_targets_finder.rb index c1769ea28f9..a96acd5838e 100644 --- a/app/finders/fork_targets_finder.rb +++ b/app/finders/fork_targets_finder.rb @@ -24,7 +24,7 @@ class ForkTargetsFinder def fork_targets(options) if options[:only_groups] - user.manageable_groups(include_groups_with_developer_maintainer_access: true) + Groups::AcceptingProjectCreationsFinder.new(user).execute # rubocop: disable CodeReuse/Finder else user.forkable_namespaces.sort_by_type end diff --git a/app/finders/group_descendants_finder.rb b/app/finders/group_descendants_finder.rb index 033af0f42a6..07f39f98b12 100644 --- a/app/finders/group_descendants_finder.rb +++ b/app/finders/group_descendants_finder.rb @@ -64,9 +64,7 @@ class GroupDescendantsFinder def direct_child_groups # rubocop: disable CodeReuse/Finder - GroupsFinder.new(current_user, - parent: parent_group, - all_available: true).execute + GroupsFinder.new(current_user, parent: parent_group, all_available: true).execute # rubocop: enable CodeReuse/Finder end @@ -78,12 +76,11 @@ class GroupDescendantsFinder .in(Gitlab::VisibilityLevel.levels_for_user(current_user)) if current_user - authorized_groups = GroupsFinder.new(current_user, - all_available: false) - .execute.arel.as('authorized') + authorized_groups = GroupsFinder.new(current_user, all_available: false) + .execute.arel.as('authorized') authorized_to_user = groups_table.project(1).from(authorized_groups) - .where(authorized_groups[:id].eq(groups_table[:id])) - .exists + .where(authorized_groups[:id].eq(groups_table[:id])) + .exists visible_to_user = visible_to_user.or(authorized_to_user) end @@ -161,9 +158,11 @@ class GroupDescendantsFinder projects_nested_in_group = Project.where(namespace_id: parent_group.self_and_descendants.as_ids) params_with_search = params.merge(search: params[:filter]) - ProjectsFinder.new(params: params_with_search, - current_user: current_user, - project_ids_relation: projects_nested_in_group).execute + ProjectsFinder.new( + params: params_with_search, + current_user: current_user, + project_ids_relation: projects_nested_in_group + ).execute # rubocop: enable CodeReuse/Finder end # rubocop: enable CodeReuse/ActiveRecord diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb index 47ed623b252..1025e0ebc9b 100644 --- a/app/finders/group_members_finder.rb +++ b/app/finders/group_members_finder.rb @@ -30,7 +30,11 @@ class GroupMembersFinder < UnionFinder def execute(include_relations: DEFAULT_RELATIONS) groups = groups_by_relations(include_relations) - members = all_group_members(groups).distinct_on_user_with_max_access_level + shared_from_groups = if include_relations&.include?(:shared_from_groups) + Group.shared_into_ancestors(group).public_or_visible_to_user(user) + end + + members = all_group_members(groups, shared_from_groups).distinct_on_user_with_max_access_level filter_members(members) end @@ -47,9 +51,8 @@ class GroupMembersFinder < UnionFinder related_groups << Group.by_id(group.id) if include_relations&.include?(:direct) related_groups << group.ancestors if include_relations&.include?(:inherited) related_groups << group.descendants if include_relations&.include?(:descendants) - related_groups << Group.shared_into_ancestors(group).public_or_visible_to_user(user) if include_relations&.include?(:shared_from_groups) - find_union(related_groups, Group) + related_groups end def filter_members(members) @@ -64,6 +67,7 @@ class GroupMembersFinder < UnionFinder members = members.by_access_level(params[:access_levels]) end + members = filter_by_user_type(members) members = apply_additional_filters(members) by_created_at(members) @@ -77,12 +81,49 @@ class GroupMembersFinder < UnionFinder group.members end - def all_group_members(groups) - members_of_groups(groups).non_minimal_access + def all_group_members(groups, shared_from_groups) + members_of_groups(groups, shared_from_groups).non_minimal_access + end + + def members_of_groups(groups, shared_from_groups) + if Feature.disabled?(:members_with_shared_group_access, @group.root_ancestor) + groups << shared_from_groups unless shared_from_groups.nil? + return GroupMember.non_request.of_groups(find_union(groups, Group)) + end + + members = GroupMember.non_request.of_groups(find_union(groups, Group)) + return members if shared_from_groups.nil? + + shared_members = GroupMember.non_request.of_groups(shared_from_groups) + select_attributes = GroupMember.attribute_names + members_shared_with_group_access = members_shared_with_group_access(shared_members, select_attributes) + + # `members` and `members_shared_with_group_access` should have even select values + find_union([members.select(select_attributes), members_shared_with_group_access], GroupMember) + end + + def members_shared_with_group_access(shared_members, select_attributes) + group_group_link_table = GroupGroupLink.arel_table + group_member_table = GroupMember.arel_table + + member_columns = select_attributes.map do |column_name| + if column_name == 'access_level' + args = [group_group_link_table[:group_access], group_member_table[:access_level]] + smallest_value_arel(args, 'access_level') + else + group_member_table[column_name] + end + end + + # rubocop:disable CodeReuse/ActiveRecord + shared_members + .joins("LEFT OUTER JOIN group_group_links ON members.source_id = group_group_links.shared_with_group_id") + .select(member_columns) + # rubocop:enable CodeReuse/ActiveRecord end - def members_of_groups(groups) - GroupMember.non_request.of_groups(groups) + def smallest_value_arel(args, column_alias) + Arel::Nodes::As.new(Arel::Nodes::NamedFunction.new('LEAST', args), Arel::Nodes::SqlLiteral.new(column_alias)) end def check_relation_arguments!(include_relations) @@ -91,6 +132,12 @@ class GroupMembersFinder < UnionFinder end end + def filter_by_user_type(members) + return members unless params[:user_type] && can_manage_members + + members.filter_by_user_type(params[:user_type]) + end + def apply_additional_filters(members) # overridden in EE to include additional filtering conditions. members diff --git a/app/finders/groups/accepting_project_creations_finder.rb b/app/finders/groups/accepting_project_creations_finder.rb new file mode 100644 index 00000000000..76463086943 --- /dev/null +++ b/app/finders/groups/accepting_project_creations_finder.rb @@ -0,0 +1,101 @@ +# frozen_string_literal: true + +module Groups + class AcceptingProjectCreationsFinder + def initialize(current_user) + @current_user = current_user + end + + def execute + groups_accepting_project_creations = + [ + current_user + .manageable_groups(include_groups_with_developer_maintainer_access: true) + .project_creation_allowed, + owner_maintainer_groups_originating_from_group_shares + .project_creation_allowed, + *developer_groups_originating_from_group_shares + ] + + # We move the UNION query into a materialized CTE to improve query performance during text search. + union_query = ::Group.from_union(groups_accepting_project_creations) + cte = Gitlab::SQL::CTE.new(:my_union_cte, union_query) + + Group.with(cte.to_arel).from(cte.alias_to(Group.arel_table)) # rubocop: disable CodeReuse/ActiveRecord + end + + private + + attr_reader :current_user + + def owner_maintainer_groups_originating_from_group_shares + GroupGroupLink + .with_owner_or_maintainer_access + .groups_accessible_via( + groups_that_user_has_owner_or_maintainer_access_via_direct_membership + .select(:id) + ) + end + + def groups_that_user_has_owner_or_maintainer_access_via_direct_membership + current_user.owned_or_maintainers_groups + end + + def developer_groups_originating_from_group_shares + # Example: + # + # Group A -----shared to---> Group B + # + + # Now, there are 2 ways a user in Group A can get "Developer" access to Group B (and it's subgroups) + [ + # 1. User has Developer or above access in Group A, + # but the group_group_link has MAX access level set to Developer + GroupGroupLink + .with_developer_access + .groups_accessible_via( + groups_that_user_has_developer_access_and_above_via_direct_membership + .select(:id) + ).with_project_creation_levels(project_creations_levels_allowing_developers_to_create_projects), + + # 2. User has exactly Developer access in Group A, + # but the group_group_link has MAX access level set to Developer or above. + GroupGroupLink + .with_developer_maintainer_owner_access + .groups_accessible_via( + groups_that_user_has_developer_access_via_direct_membership + .select(:id) + ).with_project_creation_levels(project_creations_levels_allowing_developers_to_create_projects) + ] + + # Lastly, we should make sure that such groups indeed allow Developers to create projects in them, + # based on the value of `groups.project_creation_level`, + # which is why we use the scope .with_project_creation_levels on each set. + end + + def groups_that_user_has_developer_access_and_above_via_direct_membership + current_user.developer_maintainer_owned_groups + end + + def groups_that_user_has_developer_access_via_direct_membership + current_user.developer_groups + end + + def project_creations_levels_allowing_developers_to_create_projects + project_creation_levels = [::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS] + + # When the value of application_settings.default_project_creation is set to `DEVELOPER_MAINTAINER_PROJECT_ACCESS`, + # it means that a `nil` value for `groups.project_creation_level` is telling us: + # such groups also have `project_creation_level` implicitly set to `DEVELOPER_MAINTAINER_PROJECT_ACCESS`. + # ie, `nil` is a placeholder value for inheriting the value from the ApplicationSetting. + # So we will include `nil` in the list, + # when the application_setting's value is `DEVELOPER_MAINTAINER_PROJECT_ACCESS` + + if ::Gitlab::CurrentSettings.default_project_creation == ::Gitlab::Access::DEVELOPER_MAINTAINER_PROJECT_ACCESS + project_creation_levels << nil + end + + project_creation_levels + end + end +end diff --git a/app/finders/groups/accepting_project_imports_finder.rb b/app/finders/groups/accepting_project_imports_finder.rb new file mode 100644 index 00000000000..55d72edf7bb --- /dev/null +++ b/app/finders/groups/accepting_project_imports_finder.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Groups + class AcceptingProjectImportsFinder + def initialize(current_user) + @current_user = current_user + end + + def execute + ::Group.from_union( + [ + current_user.manageable_groups, + managable_groups_originating_from_group_shares + ] + ) + end + + private + + attr_reader :current_user + + def managable_groups_originating_from_group_shares + GroupGroupLink + .with_owner_or_maintainer_access + .groups_accessible_via( + current_user.owned_or_maintainers_groups + .select(:id) + ) + end + end +end diff --git a/app/finders/groups/accepting_project_shares_finder.rb b/app/finders/groups/accepting_project_shares_finder.rb index c4963fcc352..c85e5a0f538 100644 --- a/app/finders/groups/accepting_project_shares_finder.rb +++ b/app/finders/groups/accepting_project_shares_finder.rb @@ -25,7 +25,9 @@ module Groups groups_with_guest_access_plus end - groups = groups.search(params[:search]) if params[:search].present? + groups = by_hierarchy(groups) + groups = by_ignorable(groups) + groups = by_search(groups) sort(groups).with_route end @@ -48,5 +50,25 @@ module Groups Ability.allowed?(current_user, :admin_project, project_to_be_shared) && project_to_be_shared.allowed_to_share_with_group? end + + def by_ignorable(groups) + # groups already linked to this project or groups above the project's + # current hierarchy needs to be ignored. + groups.id_not_in(project_to_be_shared.related_group_ids) + end + + def by_hierarchy(groups) + return groups if project_to_be_shared.personal? || sharing_outside_hierarchy_allowed? + + groups.id_in(root_ancestor.self_and_descendants_ids) + end + + def sharing_outside_hierarchy_allowed? + !root_ancestor.prevent_sharing_groups_outside_hierarchy + end + + def root_ancestor + project_to_be_shared.root_ancestor + end end end diff --git a/app/finders/groups/user_groups_finder.rb b/app/finders/groups/user_groups_finder.rb index b58c1323b1f..536b81b2300 100644 --- a/app/finders/groups/user_groups_finder.rb +++ b/app/finders/groups/user_groups_finder.rb @@ -36,9 +36,11 @@ module Groups def by_permission_scope if permission_scope_create_projects? - target_user.manageable_groups(include_groups_with_developer_maintainer_access: true) + Groups::AcceptingProjectCreationsFinder.new(target_user).execute # rubocop: disable CodeReuse/Finder elsif permission_scope_transfer_projects? Groups::AcceptingProjectTransfersFinder.new(target_user).execute # rubocop: disable CodeReuse/Finder + elsif permission_scope_import_projects? + Groups::AcceptingProjectImportsFinder.new(target_user).execute # rubocop: disable CodeReuse/Finder else target_user.groups end @@ -51,5 +53,9 @@ module Groups def permission_scope_transfer_projects? params[:permission_scope] == :transfer_projects end + + def permission_scope_import_projects? + params[:permission_scope] == :import_projects + end end end diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 159836062cb..478a2ba622c 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -43,6 +43,7 @@ class IssuableFinder include FinderMethods include CreatedAtFilter include Gitlab::Utils::StrongMemoize + include UpdatedAtFilter requires_cross_project_access unless: -> { params.project? } @@ -289,13 +290,6 @@ class IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord - def by_updated_at(items) - items = items.updated_after(params[:updated_after]) if params[:updated_after].present? - items = items.updated_before(params[:updated_before]) if params[:updated_before].present? - - items - end - def by_closed_at(items) items = items.closed_after(params[:closed_after]) if params[:closed_after].present? items = items.closed_before(params[:closed_before]) if params[:closed_before].present? diff --git a/app/finders/labels_finder.rb b/app/finders/labels_finder.rb index 9f9d0da6efd..b1387f2a104 100644 --- a/app/finders/labels_finder.rb +++ b/app/finders/labels_finder.rb @@ -11,6 +11,11 @@ class LabelsFinder < UnionFinder def initialize(current_user, params = {}) @current_user = current_user @params = params + # Preload container records (project, group) by default, in some cases we invoke + # the LabelsPreloader on the loaded records to prevent all N+1 queries. + # In that case we disable the default with_preloaded_container scope because it + # interferes with the LabelsPreloader. + @preload_parent_association = params.fetch(:preload_parent_association, true) end def execute(skip_authorization: false) @@ -19,7 +24,9 @@ class LabelsFinder < UnionFinder items = with_title(items) items = by_subscription(items) items = by_search(items) - sort(items.with_preloaded_container) + + items = items.with_preloaded_container if @preload_parent_association + sort(items) end private diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index 1641219a14c..3c0714441b2 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true class MembersFinder - RELATIONS = %i[direct inherited descendants invited_groups].freeze + RELATIONS = %i[direct inherited descendants invited_groups shared_into_ancestors].freeze DEFAULT_RELATIONS = %i[direct inherited].freeze # Params can be any of the following: @@ -31,11 +31,7 @@ class MembersFinder attr_reader :project, :current_user, :group def find_members(include_relations) - project_members = if Feature.enabled?(:project_members_index_by_project_namespace, project) - project.namespace_members - else - project.project_members - end + project_members = project.namespace_members if params[:active_without_invites_and_requests].present? project_members = project_members.active_without_invites_and_requests @@ -62,15 +58,20 @@ class MembersFinder def group_union_members(include_relations) [].tap do |members| - members << direct_group_members(include_relations.include?(:descendants)) if group + members << direct_group_members(include_relations) if group members << project_invited_groups if include_relations.include?(:invited_groups) end end - def direct_group_members(include_descendants) + def direct_group_members(include_relations) requested_relations = [:inherited, :direct] - requested_relations << :descendants if include_descendants - GroupMembersFinder.new(group).execute(include_relations: requested_relations).non_invite.non_minimal_access # rubocop: disable CodeReuse/Finder + requested_relations << :descendants if include_relations.include?(:descendants) + requested_relations << :shared_from_groups if include_relations.include?(:shared_into_ancestors) + + GroupMembersFinder.new(group, current_user) # rubocop: disable CodeReuse/Finder + .execute(include_relations: requested_relations) + .non_invite + .non_minimal_access end def project_invited_groups diff --git a/app/finders/merge_requests_finder.rb b/app/finders/merge_requests_finder.rb index ffa912afd1e..f1c5d5e08ad 100644 --- a/app/finders/merge_requests_finder.rb +++ b/app/finders/merge_requests_finder.rb @@ -36,6 +36,7 @@ class MergeRequestsFinder < IssuableFinder def self.scalar_params @scalar_params ||= super + [ + :approved, :approved_by_ids, :deployed_after, :deployed_before, @@ -71,8 +72,9 @@ class MergeRequestsFinder < IssuableFinder items = by_approvals(items) items = by_deployments(items) items = by_reviewer(items) + items = by_source_project_id(items) - by_source_project_id(items) + by_approved(items) end def filter_negated_items(items) @@ -183,6 +185,17 @@ class MergeRequestsFinder < IssuableFinder end # rubocop: enable CodeReuse/Finder + def by_approved(items) + approved_param = Gitlab::Utils.to_boolean(params.fetch(:approved, nil)) + return items if approved_param.nil? || Feature.disabled?(:mr_approved_filter, type: :ops) + + if approved_param + items.with_approvals + else + items.without_approvals + end + end + def by_deployments(items) env = params[:environment] before = parse_datetime(params[:deployed_before]) diff --git a/app/finders/merge_requests_finder/params.rb b/app/finders/merge_requests_finder/params.rb index e44e96054d3..2c218898dcf 100644 --- a/app/finders/merge_requests_finder/params.rb +++ b/app/finders/merge_requests_finder/params.rb @@ -16,8 +16,6 @@ class MergeRequestsFinder User.find_by_id(params[:reviewer_id]) elsif reviewer_username? User.find_by_username(params[:reviewer_username]) - else - nil end end end diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb index 5fe55e88086..9ffd623338f 100644 --- a/app/finders/milestones_finder.rb +++ b/app/finders/milestones_finder.rb @@ -15,6 +15,7 @@ class MilestonesFinder include FinderMethods include TimeFrameFilter + include UpdatedAtFilter attr_reader :params @@ -30,9 +31,12 @@ class MilestonesFinder items = by_groups_and_projects(items) items = by_title(items) items = by_search_title(items) + items = by_search(items) items = by_state(items) items = by_timeframe(items) items = containing_date(items) + items = by_updated_at(items) + items = by_iids(items) order(items) end @@ -67,6 +71,12 @@ class MilestonesFinder end end + def by_search(items) + return items if params[:search].blank? + + items.search(params[:search]) + end + def by_state(items) Milestone.filter_by_state(items, params[:state]) end @@ -84,4 +94,10 @@ class MilestonesFinder def sort_by_expired_last?(sort_by) EXPIRED_LAST_SORTS.include?(sort_by) end + + def by_iids(items) + return items unless params[:iids].present? && !params[:include_parent_milestones] + + items.by_iid(params[:iids]) + end end diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb index 81017290f12..3d764f67990 100644 --- a/app/finders/notes_finder.rb +++ b/app/finders/notes_finder.rb @@ -31,6 +31,7 @@ class NotesFinder notes = since_fetch_at(notes) notes = notes.with_notes_filter(@params[:notes_filter]) if notes_filter? notes = redact_internal(notes) + notes = notes.without_hidden if without_hidden_notes? sort(notes) end @@ -189,6 +190,13 @@ class NotesFinder notes.not_internal end + + def without_hidden_notes? + return false unless Feature.enabled?(:hidden_notes) + return false if @current_user&.can_admin_all_resources? + + true + end end NotesFinder.prepend_mod_with('NotesFinder') diff --git a/app/finders/packages/conan/package_finder.rb b/app/finders/packages/conan/package_finder.rb index 210b37635b3..161a3d0d409 100644 --- a/app/finders/packages/conan/package_finder.rb +++ b/app/finders/packages/conan/package_finder.rb @@ -3,25 +3,43 @@ module Packages module Conan class PackageFinder - attr_reader :current_user, :query + MAX_PACKAGES_COUNT = 500 - def initialize(current_user, params) + def initialize(current_user, params, project: nil) @current_user = current_user @query = params[:query] + @project = project end def execute - packages_for_current_user.installable.with_name_like(query).order_name_asc if query + return ::Packages::Package.none unless query + + packages end private + attr_reader :current_user, :query, :project + def packages - Packages::Package.conan + base + .conan + .installable + .preload_conan_metadatum + .with_name_like(query) + .limit_recent(MAX_PACKAGES_COUNT) + end + + def base + project ? packages_of_project : packages_for_current_user + end + + def packages_of_project + project.packages end def packages_for_current_user - packages.for_projects(projects_visible_to_current_user) + Packages::Package.for_projects(projects_visible_to_current_user) end def projects_visible_to_current_user diff --git a/app/finders/packages/npm/package_finder.rb b/app/finders/packages/npm/package_finder.rb index a367fda37de..953e8299138 100644 --- a/app/finders/packages/npm/package_finder.rb +++ b/app/finders/packages/npm/package_finder.rb @@ -21,7 +21,11 @@ module Packages return result unless @last_of_each_version - result.last_of_each_version + if Feature.enabled?(:npm_allow_packages_in_multiple_projects) + Packages::Package.id_in(result.last_of_each_version_ids) + else + result.last_of_each_version + end end private diff --git a/app/finders/pending_todos_finder.rb b/app/finders/pending_todos_finder.rb index babff65cc37..a1a72840236 100644 --- a/app/finders/pending_todos_finder.rb +++ b/app/finders/pending_todos_finder.rb @@ -13,24 +13,36 @@ class PendingTodosFinder attr_reader :users, :params - # users - The list of users to retrieve the todos for. + # users - The list of users to retrieve the todos for. If nil is passed, it won't filter todos based on users # params - A Hash containing columns and values to use for filtering todos. - def initialize(users, params = {}) - @users = users + def initialize(params = {}) @params = params + + # To prevent N+1 queries when fetching the users of the PendingTodos. + @preload_user_association = params.fetch(:preload_user_association, false) end def execute - todos = Todo.for_user(users) - todos = todos.pending + todos = Todo.pending + todos = by_users(todos) todos = by_project(todos) todos = by_target_id(todos) todos = by_target_type(todos) + todos = by_author_id(todos) todos = by_discussion(todos) todos = by_commit_id(todos) + + todos = todos.with_preloaded_user if @preload_user_association + by_action(todos) end + def by_users(todos) + return todos unless params[:users].present? + + todos.for_user(params[:users]) + end + def by_project(todos) if (id = params[:project_id]) todos.for_project(id) @@ -55,6 +67,12 @@ class PendingTodosFinder end end + def by_author_id(todos) + return todos unless params[:author_id] + + todos.for_author(params[:author_id]) + end + def by_commit_id(todos) if (id = params[:commit_id]) todos.for_commit(id) diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 401bc473216..57a9538db15 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -31,6 +31,7 @@ # class ProjectsFinder < UnionFinder include CustomAttributesFilter + include UpdatedAtFilter attr_accessor :params attr_reader :current_user, :project_ids_relation @@ -87,6 +88,7 @@ class ProjectsFinder < UnionFinder collection = by_last_activity_before(collection) collection = by_language(collection) collection = by_feature_availability(collection) + collection = by_updated_at(collection) by_repository_storage(collection) end diff --git a/app/finders/security/security_jobs_finder.rb b/app/finders/security/security_jobs_finder.rb index 5754492cfa7..8cfb699a62a 100644 --- a/app/finders/security/security_jobs_finder.rb +++ b/app/finders/security/security_jobs_finder.rb @@ -13,7 +13,7 @@ module Security class SecurityJobsFinder < JobsFinder def self.allowed_job_types - [:sast, :sast_iac, :dast, :dependency_scanning, :container_scanning, :secret_detection, :coverage_fuzzing, :api_fuzzing, :cluster_image_scanning] + [:sast, :sast_iac, :breach_and_attack_simulation, :dast, :dependency_scanning, :container_scanning, :secret_detection, :coverage_fuzzing, :api_fuzzing, :cluster_image_scanning] end end end diff --git a/app/finders/serverless_domain_finder.rb b/app/finders/serverless_domain_finder.rb deleted file mode 100644 index 661cd0ca363..00000000000 --- a/app/finders/serverless_domain_finder.rb +++ /dev/null @@ -1,35 +0,0 @@ -# frozen_string_literal: true - -class ServerlessDomainFinder - attr_reader :match, :serverless_domain_cluster, :environment - - def initialize(uri) - @match = ::Serverless::Domain::REGEXP.match(uri) - end - - def execute - return unless serverless? - - @serverless_domain_cluster = ::Serverless::DomainCluster.for_uuid(serverless_domain_cluster_uuid) - return unless serverless_domain_cluster&.knative&.external_ip - - @environment = ::Environment.for_id_and_slug(match[:environment_id].to_i(16), match[:environment_slug]) - return unless environment - - ::Serverless::Domain.new( - function_name: match[:function_name], - serverless_domain_cluster: serverless_domain_cluster, - environment: environment - ) - end - - def serverless_domain_cluster_uuid - return unless serverless? - - match[:cluster_left] + match[:cluster_middle] + match[:cluster_right] - end - - def serverless? - !!match - end -end diff --git a/app/finders/snippets_finder.rb b/app/finders/snippets_finder.rb index bf20a2c2c3d..9dd7e508c22 100644 --- a/app/finders/snippets_finder.rb +++ b/app/finders/snippets_finder.rb @@ -179,8 +179,6 @@ class SnippetsFinder < UnionFinder Snippet::INTERNAL when 'are_public' Snippet::PUBLIC - else - nil end end diff --git a/app/finders/template_finder.rb b/app/finders/template_finder.rb index b82b601541c..c6c5c30cbf7 100644 --- a/app/finders/template_finder.rb +++ b/app/finders/template_finder.rb @@ -16,16 +16,27 @@ class TemplateFinder def build(type, project, params = {}) if type.to_s == 'licenses' LicenseTemplateFinder.new(project, params) # rubocop: disable CodeReuse/Finder - else + elsif type_allowed?(type) new(type, project, params) end end def all_template_names(project, type) - return {} if !VENDORED_TEMPLATES.key?(type.to_s) && type.to_s != 'licenses' + return {} unless type_allowed?(type) build(type, project).template_names end + + def type_allowed?(type) + case type.to_s + when 'licenses' + true + when 'metrics_dashboard_ymls' + !Feature.enabled?(:remove_monitor_metrics) + else + VENDORED_TEMPLATES.key?(type) + end + end end attr_reader :type, :project, :params |