From 9f46488805e86b1bc341ea1620b866016c2ce5ed Mon Sep 17 00:00:00 2001 From: GitLab Bot Date: Wed, 20 May 2020 14:34:42 +0000 Subject: Add latest changes from gitlab-org/gitlab@13-0-stable-ee --- app/finders/alert_management/alerts_finder.rb | 55 +++++++++ app/finders/artifacts_finder.rb | 24 ---- .../ci/daily_build_group_report_results_finder.rb | 37 ++++++ app/finders/ci/job_artifacts_finder.rb | 26 ++++ app/finders/clusters/knative_services_finder.rb | 1 + app/finders/container_repositories_finder.rb | 13 +- app/finders/design_management/designs_finder.rb | 57 +++++++++ app/finders/design_management/versions_finder.rb | 58 +++++++++ app/finders/freeze_periods_finder.rb | 14 +++ app/finders/group_members_finder.rb | 2 - app/finders/issuable_finder.rb | 133 +++++++++++++++------ app/finders/issuable_finder/params.rb | 2 + app/finders/issues_finder/params.rb | 2 +- app/finders/members_finder.rb | 16 +-- .../metrics/users_starred_dashboards_finder.rb | 35 ++++++ .../projects/serverless/functions_finder.rb | 1 + app/finders/projects_finder.rb | 4 +- app/finders/releases_finder.rb | 20 +++- app/finders/todos_finder.rb | 2 +- 19 files changed, 422 insertions(+), 80 deletions(-) create mode 100644 app/finders/alert_management/alerts_finder.rb delete mode 100644 app/finders/artifacts_finder.rb create mode 100644 app/finders/ci/daily_build_group_report_results_finder.rb create mode 100644 app/finders/ci/job_artifacts_finder.rb create mode 100644 app/finders/design_management/designs_finder.rb create mode 100644 app/finders/design_management/versions_finder.rb create mode 100644 app/finders/freeze_periods_finder.rb create mode 100644 app/finders/metrics/users_starred_dashboards_finder.rb (limited to 'app/finders') diff --git a/app/finders/alert_management/alerts_finder.rb b/app/finders/alert_management/alerts_finder.rb new file mode 100644 index 00000000000..cb35be43c15 --- /dev/null +++ b/app/finders/alert_management/alerts_finder.rb @@ -0,0 +1,55 @@ +# frozen_string_literal: true + +module AlertManagement + class AlertsFinder + # @return [Hash] Mapping of status id to count + # ex) { 0: 6, ...etc } + def self.counts_by_status(current_user, project, params = {}) + new(current_user, project, params).execute.counts_by_status + end + + def initialize(current_user, project, params) + @current_user = current_user + @project = project + @params = params + end + + def execute + return AlertManagement::Alert.none unless authorized? + + collection = project.alert_management_alerts + collection = by_status(collection) + collection = by_search(collection) + collection = by_iid(collection) + sort(collection) + end + + private + + attr_reader :current_user, :project, :params + + def by_iid(collection) + return collection unless params[:iid] + + collection.for_iid(params[:iid]) + end + + def by_status(collection) + values = AlertManagement::Alert::STATUSES.values & Array(params[:status]) + + values.present? ? collection.for_status(values) : collection + end + + def by_search(collection) + params[:search].present? ? collection.search(params[:search]) : collection + end + + def sort(collection) + params[:sort] ? collection.sort_by_attribute(params[:sort]) : collection + end + + def authorized? + Ability.allowed?(current_user, :read_alert_management_alert, project) + end + end +end diff --git a/app/finders/artifacts_finder.rb b/app/finders/artifacts_finder.rb deleted file mode 100644 index 81c5168d782..00000000000 --- a/app/finders/artifacts_finder.rb +++ /dev/null @@ -1,24 +0,0 @@ -# frozen_string_literal: true - -class ArtifactsFinder - def initialize(project, params = {}) - @project = project - @params = params - end - - def execute - artifacts = @project.job_artifacts - - sort(artifacts) - end - - private - - def sort_key - @params[:sort] || 'created_desc' - end - - def sort(artifacts) - artifacts.order_by(sort_key) - end -end diff --git a/app/finders/ci/daily_build_group_report_results_finder.rb b/app/finders/ci/daily_build_group_report_results_finder.rb new file mode 100644 index 00000000000..3c3c24c1479 --- /dev/null +++ b/app/finders/ci/daily_build_group_report_results_finder.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +module Ci + class DailyBuildGroupReportResultsFinder + include Gitlab::Allowable + + def initialize(current_user:, project:, ref_path:, start_date:, end_date:, limit: nil) + @current_user = current_user + @project = project + @ref_path = ref_path + @start_date = start_date + @end_date = end_date + @limit = limit + end + + def execute + return none unless can?(current_user, :download_code, project) + + Ci::DailyBuildGroupReportResult.recent_results( + { + project_id: project, + ref_path: ref_path, + date: start_date..end_date + }, + limit: @limit + ) + end + + private + + attr_reader :current_user, :project, :ref_path, :start_date, :end_date + + def none + Ci::DailyBuildGroupReportResult.none + end + end +end diff --git a/app/finders/ci/job_artifacts_finder.rb b/app/finders/ci/job_artifacts_finder.rb new file mode 100644 index 00000000000..808c159ced1 --- /dev/null +++ b/app/finders/ci/job_artifacts_finder.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Ci + class JobArtifactsFinder + def initialize(project, params = {}) + @project = project + @params = params + end + + def execute + artifacts = @project.job_artifacts + + sort(artifacts) + end + + private + + def sort_key + @params[:sort] || 'created_desc' + end + + def sort(artifacts) + artifacts.order_by(sort_key) + end + end +end diff --git a/app/finders/clusters/knative_services_finder.rb b/app/finders/clusters/knative_services_finder.rb index 71cebe4495e..af8c42f672f 100644 --- a/app/finders/clusters/knative_services_finder.rb +++ b/app/finders/clusters/knative_services_finder.rb @@ -11,6 +11,7 @@ module Clusters }.freeze self.reactive_cache_key = ->(finder) { finder.model_name } + self.reactive_cache_work_type = :external_dependency self.reactive_cache_worker_finder = ->(_id, *cache_args) { from_cache(*cache_args) } attr_reader :cluster, :environment diff --git a/app/finders/container_repositories_finder.rb b/app/finders/container_repositories_finder.rb index 34921df840b..5109efb361b 100644 --- a/app/finders/container_repositories_finder.rb +++ b/app/finders/container_repositories_finder.rb @@ -3,17 +3,18 @@ class ContainerRepositoriesFinder VALID_SUBJECTS = [Group, Project].freeze - def initialize(user:, subject:) + def initialize(user:, subject:, params: {}) @user = user @subject = subject + @params = params end def execute raise ArgumentError, "invalid subject_type" unless valid_subject_type? return unless authorized? - return project_repositories if @subject.is_a?(Project) - return group_repositories if @subject.is_a?(Group) + repositories = @subject.is_a?(Project) ? project_repositories : group_repositories + filter_by_image_name(repositories) end private @@ -32,6 +33,12 @@ class ContainerRepositoriesFinder ContainerRepository.for_group_and_its_subgroups(@subject) end + def filter_by_image_name(repositories) + return repositories unless @params[:name] + + repositories.search_by_name(@params[:name]) + end + def authorized? Ability.allowed?(@user, :read_container_image, @subject) end diff --git a/app/finders/design_management/designs_finder.rb b/app/finders/design_management/designs_finder.rb new file mode 100644 index 00000000000..10f95520d1e --- /dev/null +++ b/app/finders/design_management/designs_finder.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +module DesignManagement + class DesignsFinder + include Gitlab::Allowable + + # Params: + # ids: integer[] + # filenames: string[] + # visible_at_version: ?version + # filenames: String[] + def initialize(issue, current_user, params = {}) + @issue = issue + @current_user = current_user + @params = params + end + + def execute + items = init_collection + + items = by_visible_at_version(items) + items = by_filename(items) + items = by_id(items) + + items + end + + private + + attr_reader :issue, :current_user, :params + + def init_collection + return ::DesignManagement::Design.none unless can?(current_user, :read_design, issue) + + issue.designs + end + + # Returns all designs that existed at a particular design version + def by_visible_at_version(items) + items.visible_at_version(params[:visible_at_version]) + end + + def by_filename(items) + return items if params[:filenames].nil? + return ::DesignManagement::Design.none if params[:filenames].empty? + + items.with_filename(params[:filenames]) + end + + def by_id(items) + return items if params[:ids].nil? + return ::DesignManagement::Design.none if params[:ids].empty? + + items.id_in(params[:ids]) + end + end +end diff --git a/app/finders/design_management/versions_finder.rb b/app/finders/design_management/versions_finder.rb new file mode 100644 index 00000000000..c4aefd3078e --- /dev/null +++ b/app/finders/design_management/versions_finder.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module DesignManagement + class VersionsFinder + attr_reader :design_or_collection, :current_user, :params + + # The `design_or_collection` argument should be either a: + # + # - DesignManagement::Design, or + # - DesignManagement::DesignCollection + # + # The object will have `#versions` called on it to set up the + # initial scope of the versions. + # + # valid params: + # - earlier_or_equal_to: Version + # - sha: String + # - version_id: Integer + # + def initialize(design_or_collection, current_user, params = {}) + @design_or_collection = design_or_collection + @current_user = current_user + @params = params + end + + def execute + unless Ability.allowed?(current_user, :read_design, design_or_collection) + return ::DesignManagement::Version.none + end + + items = design_or_collection.versions + items = by_earlier_or_equal_to(items) + items = by_sha(items) + items = by_version_id(items) + items.ordered + end + + private + + def by_earlier_or_equal_to(items) + return items unless params[:earlier_or_equal_to] + + items.earlier_or_equal_to(params[:earlier_or_equal_to]) + end + + def by_version_id(items) + return items unless params[:version_id] + + items.id_in(params[:version_id]) + end + + def by_sha(items) + return items unless params[:sha] + + items.by_sha(params[:sha]) + end + end +end diff --git a/app/finders/freeze_periods_finder.rb b/app/finders/freeze_periods_finder.rb new file mode 100644 index 00000000000..2a9bfbe12ba --- /dev/null +++ b/app/finders/freeze_periods_finder.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class FreezePeriodsFinder + def initialize(project, current_user = nil) + @project = project + @current_user = current_user + end + + def execute + return Ci::FreezePeriod.none unless Ability.allowed?(@current_user, :read_freeze_period, @project) + + @project.freeze_periods + end +end diff --git a/app/finders/group_members_finder.rb b/app/finders/group_members_finder.rb index a56d4ebb368..949af103eb3 100644 --- a/app/finders/group_members_finder.rb +++ b/app/finders/group_members_finder.rb @@ -9,7 +9,6 @@ class GroupMembersFinder < UnionFinder # search: string # created_after: datetime # created_before: datetime - attr_reader :params def initialize(group, user = nil, params: {}) @@ -22,7 +21,6 @@ class GroupMembersFinder < UnionFinder def execute(include_relations: [:inherited, :direct]) group_members = group.members relations = [] - @params = params return group_members if include_relations == [:direct] diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb index 5687b375cf0..7014f2ec205 100644 --- a/app/finders/issuable_finder.rb +++ b/app/finders/issuable_finder.rb @@ -40,7 +40,7 @@ class IssuableFinder requires_cross_project_access unless: -> { params.project? } - NEGATABLE_PARAMS_HELPER_KEYS = %i[include_subgroups in].freeze + NEGATABLE_PARAMS_HELPER_KEYS = %i[project_id scope status include_subgroups].freeze attr_accessor :current_user, :params @@ -68,7 +68,7 @@ class IssuableFinder # This should not be used in controller strong params! def negatable_scalar_params - @negatable_scalar_params ||= scalar_params + %i[project_id group_id] + @negatable_scalar_params ||= scalar_params - %i[search in] end # This should not be used in controller strong params! @@ -100,7 +100,7 @@ class IssuableFinder items = filter_items(items) # Let's see if we have to negate anything - items = by_negation(items) + items = filter_negated_items(items) # This has to be last as we use a CTE as an optimization fence # for counts by passing the force_cte param and enabling the @@ -132,6 +132,22 @@ class IssuableFinder by_my_reaction_emoji(items) end + # Negates all params found in `negatable_params` + def filter_negated_items(items) + return items unless Feature.enabled?(:not_issuable_queries, params.group || params.project, default_enabled: true) + + # API endpoints send in `nil` values so we test if there are any non-nil + return items unless not_params.present? && not_params.values.any? + + items = by_negated_author(items) + items = by_negated_assignee(items) + items = by_negated_label(items) + items = by_negated_milestone(items) + items = by_negated_release(items) + items = by_negated_my_reaction_emoji(items) + by_negated_iids(items) + end + def row_count Gitlab::IssuablesCountForState.new(self).for_state_or_opened(params[:state]) end @@ -189,6 +205,21 @@ class IssuableFinder private + def not_params + strong_memoize(:not_params) do + params_class.new(params[:not].dup, current_user, klass).tap do |not_params| + next unless not_params.present? + + # These are "helper" params that modify the results, like :in and :search. They usually come in at the top-level + # params, but if they do come in inside the `:not` params, the inner ones should take precedence. + not_helpers = params.slice(*NEGATABLE_PARAMS_HELPER_KEYS).merge(params[:not].slice(*NEGATABLE_PARAMS_HELPER_KEYS)) + not_helpers.each do |key, value| + not_params[key] = value unless not_params[key].present? + end + end + end + end + def force_cte? !!params[:force_cte] end @@ -215,33 +246,6 @@ class IssuableFinder klass.available_states.key(value) end - # Negates all params found in `negatable_params` - # rubocop: disable CodeReuse/ActiveRecord - def by_negation(items) - not_params = params[:not].dup - # API endpoints send in `nil` values so we test if there are any non-nil - return items unless not_params.present? && not_params.values.any? - - not_params.keep_if { |_k, v| v.present? }.each do |(key, value)| - # These aren't negatable params themselves, but rather help other searches, so we skip them. - # They will be added into all the NOT searches. - next if NEGATABLE_PARAMS_HELPER_KEYS.include?(key.to_sym) - next unless self.class.negatable_params.include?(key.to_sym) - - # These are "helper" params that are required inside the NOT to get the right results. They usually come in - # at the top-level params, but if they do come in inside the `:not` params, they should take precedence. - not_helpers = params.slice(*NEGATABLE_PARAMS_HELPER_KEYS).merge(params[:not].slice(*NEGATABLE_PARAMS_HELPER_KEYS)) - not_param = { key => value }.with_indifferent_access.merge(not_helpers).merge(not_query: true) - - items_to_negate = self.class.new(current_user, not_param).execute - - items = items.where.not(id: items_to_negate) - end - - items - end - # rubocop: enable CodeReuse/ActiveRecord - # rubocop: disable CodeReuse/ActiveRecord def by_scope(items) return items.none if params.current_user_related? && !current_user @@ -325,6 +329,12 @@ class IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord + def by_negated_iids(items) + not_params[:iids].present? ? items.where.not(iid: not_params[:iids]) : items + end + # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord def sort(items) # Ensure we always have an explicit sort order (instead of inheriting @@ -347,9 +357,19 @@ class IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord - def by_assignee(items) - return items.assigned_to(params.assignees) if not_query? && params.assignees.any? + # rubocop: disable CodeReuse/ActiveRecord + def by_negated_author(items) + if not_params.author + items.where.not(author_id: not_params.author.id) + elsif not_params.author_id? || not_params.author_username? # author not found + items.none + else + items + end + end + # rubocop: enable CodeReuse/ActiveRecord + def by_assignee(items) if params.filter_by_no_assignee? items.unassigned elsif params.filter_by_any_assignee? @@ -363,6 +383,17 @@ class IssuableFinder end end + def by_negated_assignee(items) + # We want CE users to be able to say "Issues not assigned to either PersonA nor PersonB" + if not_params.assignees.present? + items.not_assigned_to(not_params.assignees) + elsif not_params.assignee_id? || not_params.assignee_username? # assignee not found + items.none + else + items + end + end + # rubocop: disable CodeReuse/ActiveRecord def by_milestone(items) return items unless params.milestones? @@ -382,6 +413,20 @@ class IssuableFinder end # rubocop: enable CodeReuse/ActiveRecord + # rubocop: disable CodeReuse/ActiveRecord + def by_negated_milestone(items) + return items unless not_params.milestones? + + if not_params.filter_by_upcoming_milestone? + items.joins(:milestone).merge(Milestone.not_upcoming) + elsif not_params.filter_by_started_milestone? + items.joins(:milestone).merge(Milestone.not_started) + else + items.without_particular_milestone(not_params[:milestone_title]) + end + end + # rubocop: enable CodeReuse/ActiveRecord + def by_release(items) return items unless params.releases? @@ -394,6 +439,12 @@ class IssuableFinder end end + def by_negated_release(items) + return items unless not_params.releases? + + items.without_particular_release(not_params[:release_tag], not_params[:project_id]) + end + def by_label(items) return items unless params.labels? @@ -402,10 +453,16 @@ class IssuableFinder elsif params.filter_by_any_label? items.any_label else - items.with_label(params.label_names, params[:sort], not_query: not_query?) + items.with_label(params.label_names, params[:sort]) end end + def by_negated_label(items) + return items unless not_params.labels? + + items.without_particular_labels(not_params.label_names) + end + def by_my_reaction_emoji(items) return items unless params[:my_reaction_emoji] && current_user @@ -418,11 +475,13 @@ class IssuableFinder end end - def by_non_archived(items) - params[:non_archived].present? ? items.non_archived : items + def by_negated_my_reaction_emoji(items) + return items unless not_params[:my_reaction_emoji] && current_user + + items.not_awarded(current_user, not_params[:my_reaction_emoji]) end - def not_query? - !!params[:not_query] + def by_non_archived(items) + params[:non_archived].present? ? items.non_archived : items end end diff --git a/app/finders/issuable_finder/params.rb b/app/finders/issuable_finder/params.rb index 120ef364368..adf9f1ca9d8 100644 --- a/app/finders/issuable_finder/params.rb +++ b/app/finders/issuable_finder/params.rb @@ -132,6 +132,8 @@ class IssuableFinder def project strong_memoize(:project) do + next nil unless params[:project_id].present? + project = Project.find(params[:project_id]) project = nil unless Ability.allowed?(current_user, :"read_#{klass.to_ability_name}", project) diff --git a/app/finders/issues_finder/params.rb b/app/finders/issues_finder/params.rb index aaeead7c709..cd92b79265d 100644 --- a/app/finders/issues_finder/params.rb +++ b/app/finders/issues_finder/params.rb @@ -50,4 +50,4 @@ class IssuesFinder end end -IssuableFinder::Params.prepend_if_ee('EE::IssuesFinder::Params') +IssuesFinder::Params.prepend_if_ee('EE::IssuesFinder::Params') diff --git a/app/finders/members_finder.rb b/app/finders/members_finder.rb index 0617f34dc8c..e08ed737ca6 100644 --- a/app/finders/members_finder.rb +++ b/app/finders/members_finder.rb @@ -4,17 +4,19 @@ class MembersFinder # Params can be any of the following: # sort: string # search: string + attr_reader :params - def initialize(project, current_user) + def initialize(project, current_user, params: {}) @project = project - @current_user = current_user @group = project.group + @current_user = current_user + @params = params end - def execute(include_relations: [:inherited, :direct], params: {}) - members = find_members(include_relations, params) + def execute(include_relations: [:inherited, :direct]) + members = find_members(include_relations) - filter_members(members, params) + filter_members(members) end def can?(*args) @@ -25,7 +27,7 @@ class MembersFinder attr_reader :project, :current_user, :group - def find_members(include_relations, params) + def find_members(include_relations) project_members = project.project_members project_members = project_members.non_invite unless can?(current_user, :admin_project, project) @@ -39,7 +41,7 @@ class MembersFinder distinct_union_of_members(union_members) end - def filter_members(members, params) + def filter_members(members) members = members.search(params[:search]) if params[:search].present? members = members.sort_by_attribute(params[:sort]) if params[:sort].present? members diff --git a/app/finders/metrics/users_starred_dashboards_finder.rb b/app/finders/metrics/users_starred_dashboards_finder.rb new file mode 100644 index 00000000000..7244c51f9a7 --- /dev/null +++ b/app/finders/metrics/users_starred_dashboards_finder.rb @@ -0,0 +1,35 @@ +# frozen_string_literal: true + +module Metrics + class UsersStarredDashboardsFinder + def initialize(user:, project:, params: {}) + @user, @project, @params = user, project, params + end + + def execute + return ::Metrics::UsersStarredDashboard.none unless Ability.allowed?(user, :read_metrics_user_starred_dashboard, project) + + items = starred_dashboards + items = by_project(items) + by_dashboard(items) + end + + private + + attr_reader :user, :project, :params + + def by_project(items) + items.for_project(project) + end + + def by_dashboard(items) + return items unless params[:dashboard_path] + + items.merge(starred_dashboards.for_project_dashboard(project, params[:dashboard_path])) + end + + def starred_dashboards + @starred_dashboards ||= user.metrics_users_starred_dashboards + end + end +end diff --git a/app/finders/projects/serverless/functions_finder.rb b/app/finders/projects/serverless/functions_finder.rb index 3b4ecbb5387..13f84e0e3a5 100644 --- a/app/finders/projects/serverless/functions_finder.rb +++ b/app/finders/projects/serverless/functions_finder.rb @@ -9,6 +9,7 @@ module Projects attr_reader :project self.reactive_cache_key = ->(finder) { finder.cache_key } + self.reactive_cache_work_type = :external_dependency self.reactive_cache_worker_finder = ->(_id, *args) { from_cache(*args) } MAX_CLUSTERS = 10 diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb index 3a84600b09f..8846ff54eb2 100644 --- a/app/finders/projects_finder.rb +++ b/app/finders/projects_finder.rb @@ -151,11 +151,11 @@ class ProjectsFinder < UnionFinder end def by_personal(items) - (params[:personal].present? && current_user) ? items.personal(current_user) : items + params[:personal].present? && current_user ? items.personal(current_user) : items end def by_starred(items) - (params[:starred].present? && current_user) ? items.starred_by(current_user) : items + params[:starred].present? && current_user ? items.starred_by(current_user) : items end def by_trending(items) diff --git a/app/finders/releases_finder.rb b/app/finders/releases_finder.rb index e58a90922a5..6a754fdb5a1 100644 --- a/app/finders/releases_finder.rb +++ b/app/finders/releases_finder.rb @@ -1,17 +1,31 @@ # frozen_string_literal: true class ReleasesFinder - def initialize(project, current_user = nil) + attr_reader :project, :current_user, :params + + def initialize(project, current_user = nil, params = {}) @project = project @current_user = current_user + @params = params end def execute(preload: true) - return Release.none unless Ability.allowed?(@current_user, :read_release, @project) + return Release.none unless Ability.allowed?(current_user, :read_release, project) # See https://gitlab.com/gitlab-org/gitlab/-/issues/211988 - releases = @project.releases.where.not(tag: nil) # rubocop:disable CodeReuse/ActiveRecord + releases = project.releases.where.not(tag: nil) # rubocop:disable CodeReuse/ActiveRecord + releases = by_tag(releases) releases = releases.preloaded if preload releases.sorted end + + private + + # rubocop: disable CodeReuse/ActiveRecord + def by_tag(releases) + return releases unless params[:tag].present? + + releases.where(tag: params[:tag]) + end + # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/finders/todos_finder.rb b/app/finders/todos_finder.rb index e56009be33d..672bbd52b07 100644 --- a/app/finders/todos_finder.rb +++ b/app/finders/todos_finder.rb @@ -23,7 +23,7 @@ class TodosFinder NONE = '0' - TODO_TYPES = Set.new(%w(Issue MergeRequest)).freeze + TODO_TYPES = Set.new(%w(Issue MergeRequest DesignManagement::Design)).freeze attr_accessor :current_user, :params -- cgit v1.2.3