diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-09-19 23:18:09 +0000 |
commit | 6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde (patch) | |
tree | dc4d20fe6064752c0bd323187252c77e0a89144b /app/graphql/resolvers | |
parent | 9868dae7fc0655bd7ce4a6887d4e6d487690eeed (diff) | |
download | gitlab-ce-6ed4ec3e0b1340f96b7c043ef51d1b33bbe85fde.tar.gz |
Add latest changes from gitlab-org/gitlab@15-4-stable-eev15.4.0-rc42
Diffstat (limited to 'app/graphql/resolvers')
22 files changed, 385 insertions, 119 deletions
diff --git a/app/graphql/resolvers/ci/job_token_scope_resolver.rb b/app/graphql/resolvers/ci/job_token_scope_resolver.rb index ca76a7b94fc..7c6aedad1d6 100644 --- a/app/graphql/resolvers/ci/job_token_scope_resolver.rb +++ b/app/graphql/resolvers/ci/job_token_scope_resolver.rb @@ -6,14 +6,12 @@ module Resolvers include Gitlab::Graphql::Authorize::AuthorizeResource authorize :admin_project - description 'Container for resources that can be accessed by a CI job token from the current project. Null if job token scope setting is disabled.' + description 'Container for resources that can be accessed by a CI job token from the current project.' type ::Types::Ci::JobTokenScopeType, null: true def resolve authorize!(object) - return unless object.ci_job_token_scope_enabled? - ::Ci::JobToken::Scope.new(object) end end diff --git a/app/graphql/resolvers/ci/runner_jobs_resolver.rb b/app/graphql/resolvers/ci/runner_jobs_resolver.rb index 2f6ca09d031..de00aadaea8 100644 --- a/app/graphql/resolvers/ci/runner_jobs_resolver.rb +++ b/app/graphql/resolvers/ci/runner_jobs_resolver.rb @@ -9,6 +9,7 @@ module Resolvers type ::Types::Ci::JobType.connection_type, null: true authorize :read_builds authorizes_object! + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 argument :statuses, [::Types::Ci::JobStatusEnum], required: false, @@ -16,15 +17,6 @@ module Resolvers alias_method :runner, :object - def ready?(**args) - context[self.class] ||= { executions: 0 } - context[self.class][:executions] += 1 - - raise GraphQL::ExecutionError, "Jobs can be requested for only one runner at a time" if context[self.class][:executions] > 1 - - super - end - def resolve_with_lookahead(statuses: nil) jobs = ::Ci::JobsFinder.new(current_user: current_user, runner: runner, params: { scope: statuses }).execute diff --git a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb index 14b5f8f90eb..da8fab93619 100644 --- a/app/graphql/resolvers/ci/runner_owner_project_resolver.rb +++ b/app/graphql/resolvers/ci/runner_owner_project_resolver.rb @@ -9,7 +9,7 @@ module Resolvers alias_method :runner, :object - def resolve_with_lookahead(**args) + def resolve_with_lookahead(**_args) resolve_owner end @@ -19,6 +19,8 @@ module Resolvers } end + private + def filtered_preloads selection = lookahead @@ -27,8 +29,6 @@ module Resolvers end end - private - def resolve_owner return unless runner.project_type? @@ -48,14 +48,13 @@ module Resolvers .transform_values { |runner_projects| runner_projects.first.project_id } project_ids = owner_project_id_by_runner_id.values.uniq - all_preloads = unconditional_includes + filtered_preloads - owner_relation = Project.all - owner_relation = owner_relation.preload(*all_preloads) if all_preloads.any? - projects = owner_relation.where(id: project_ids).index_by(&:id) + projects = Project.where(id: project_ids) + Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute + projects_by_id = projects.index_by(&:id) runner_ids.each do |runner_id| owner_project_id = owner_project_id_by_runner_id[runner_id] - loader.call(runner_id, projects[owner_project_id]) + loader.call(runner_id, projects_by_id[owner_project_id]) end # rubocop: enable CodeReuse/ActiveRecord end diff --git a/app/graphql/resolvers/ci/runner_projects_resolver.rb b/app/graphql/resolvers/ci/runner_projects_resolver.rb new file mode 100644 index 00000000000..ca3b4ebb797 --- /dev/null +++ b/app/graphql/resolvers/ci/runner_projects_resolver.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Resolvers + module Ci + class RunnerProjectsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + include LooksAhead + include ProjectSearchArguments + + type Types::ProjectType.connection_type, null: true + authorize :read_runner + authorizes_object! + + alias_method :runner, :object + + argument :sort, GraphQL::Types::String, + required: false, + default_value: 'id_asc', # TODO: Remove in %16.0 and move :sort to ProjectSearchArguments, see https://gitlab.com/gitlab-org/gitlab/-/issues/372117 + deprecated: { + reason: 'Default sort order will change in 16.0. ' \ + 'Specify `"id_asc"` if query results\' order is important', + milestone: '15.4' + }, + description: "Sort order of results. Format: '<field_name>_<sort_direction>', " \ + "for example: 'id_desc' or 'name_asc'" + + def resolve_with_lookahead(**args) + return unless runner.project_type? + + # rubocop:disable CodeReuse/ActiveRecord + BatchLoader::GraphQL.for(runner.id).batch(key: :runner_projects) do |runner_ids, loader| + plucked_runner_and_project_ids = ::Ci::RunnerProject + .select(:runner_id, :project_id) + .where(runner_id: runner_ids) + .pluck(:runner_id, :project_id) + + project_ids = plucked_runner_and_project_ids.collect { |_runner_id, project_id| project_id }.uniq + projects = ProjectsFinder + .new(current_user: current_user, + params: project_finder_params(args), + project_ids_relation: project_ids) + .execute + Preloaders::ProjectPolicyPreloader.new(projects, current_user).execute + projects_by_id = projects.index_by(&:id) + + # In plucked_runner_and_project_ids, first() represents the runner ID, and second() the project ID, + # so let's group the project IDs by runner ID + runner_project_ids_by_runner_id = + plucked_runner_and_project_ids + .group_by(&:first) + .transform_values { |values| values.map(&:second).filter_map { |project_id| projects_by_id[project_id] } } + + runner_ids.each do |runner_id| + runner_projects = runner_project_ids_by_runner_id[runner_id] || [] + + loader.call(runner_id, runner_projects) + end + end + # rubocop:enable CodeReuse/ActiveRecord + end + end + end +end diff --git a/app/graphql/resolvers/ci/test_suite_resolver.rb b/app/graphql/resolvers/ci/test_suite_resolver.rb index f758e217b47..a2d3af9c664 100644 --- a/app/graphql/resolvers/ci/test_suite_resolver.rb +++ b/app/graphql/resolvers/ci/test_suite_resolver.rb @@ -28,7 +28,8 @@ module Resolvers def load_test_suite_data(builds) suite = builds.sum do |build| - build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report = build.collect_test_reports!(Gitlab::Ci::Reports::TestReport.new) + test_report.get_suite(build.test_suite_name) end Gitlab::Ci::Reports::TestFailureHistory.new(suite.failed.values, pipeline.project).load! diff --git a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb index fe213936f55..8295bd58388 100644 --- a/app/graphql/resolvers/concerns/issue_resolver_arguments.rb +++ b/app/graphql/resolvers/concerns/issue_resolver_arguments.rb @@ -76,24 +76,11 @@ module IssueResolverArguments end def resolve_with_lookahead(**args) - # The project could have been loaded in batch by `BatchLoader`. - # At this point we need the `id` of the project to query for issues, so - # make sure it's loaded and not `nil` before continuing. - parent = object.respond_to?(:sync) ? object.sync : object - return Issue.none if parent.nil? - - # Will need to be made group & namespace aware with - # https://gitlab.com/gitlab-org/gitlab-foss/issues/54520 - args[:not] = args[:not].to_h if args[:not].present? - args[:iids] ||= [args.delete(:iid)].compact if args[:iid] - args[:attempt_project_search_optimizations] = true if args[:search].present? + return Issue.none if resource_parent.nil? - prepare_assignee_username_params(args) - prepare_release_tag_params(args) + finder = IssuesFinder.new(current_user, prepare_finder_params(args)) - finder = IssuesFinder.new(current_user, args) - - continue_issue_resolve(parent, finder, **args) + continue_issue_resolve(resource_parent, finder, **args) end def ready?(**args) @@ -103,7 +90,6 @@ module IssueResolverArguments params_not_mutually_exclusive(args, mutually_exclusive_milestone_args) params_not_mutually_exclusive(args.fetch(:not, {}), mutually_exclusive_milestone_args) params_not_mutually_exclusive(args, mutually_exclusive_release_tag_args) - validate_anonymous_search_access! if args[:search].present? super end @@ -128,6 +114,17 @@ module IssueResolverArguments private + def prepare_finder_params(args) + params = super(args) + params[:iids] ||= [params.delete(:iid)].compact if params[:iid] + params[:attempt_project_search_optimizations] = true if params[:search].present? + + prepare_assignee_username_params(params) + prepare_release_tag_params(params) + + params + end + def prepare_release_tag_params(args) release_tag_wildcard = args.delete(:release_tag_wildcard_id) return if release_tag_wildcard.blank? @@ -135,20 +132,13 @@ module IssueResolverArguments args[:release_tag] ||= release_tag_wildcard end - def mutually_exclusive_release_tag_args - [:release_tag, :release_tag_wildcard_id] - end - def prepare_assignee_username_params(args) args[:assignee_username] = args.delete(:assignee_usernames) if args[:assignee_usernames].present? args[:not][:assignee_username] = args[:not].delete(:assignee_usernames) if args.dig(:not, :assignee_usernames).present? end - def params_not_mutually_exclusive(args, mutually_exclusive_args) - if args.slice(*mutually_exclusive_args).compact.size > 1 - arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ') - raise ::Gitlab::Graphql::Errors::ArgumentError, "only one of [#{arg_str}] arguments is allowed at the same time." - end + def mutually_exclusive_release_tag_args + [:release_tag, :release_tag_wildcard_id] end def mutually_exclusive_milestone_args @@ -158,4 +148,20 @@ module IssueResolverArguments def mutually_exclusive_assignee_username_args [:assignee_usernames, :assignee_username] end + + def params_not_mutually_exclusive(args, mutually_exclusive_args) + if args.slice(*mutually_exclusive_args).compact.size > 1 + arg_str = mutually_exclusive_args.map { |x| x.to_s.camelize(:lower) }.join(', ') + raise ::Gitlab::Graphql::Errors::ArgumentError, "only one of [#{arg_str}] arguments is allowed at the same time." + end + end + + def resource_parent + # The project could have been loaded in batch by `BatchLoader`. + # At this point we need the `id` of the project to query for issues, so + # make sure it's loaded and not `nil` before continuing. + strong_memoize(:resource_parent) do + object.respond_to?(:sync) ? object.sync : object + end + end end diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb index 644b2a11460..b548dc1e175 100644 --- a/app/graphql/resolvers/concerns/looks_ahead.rb +++ b/app/graphql/resolvers/concerns/looks_ahead.rb @@ -33,10 +33,14 @@ module LooksAhead end def filtered_preloads - selection = node_selection + nodes = node_selection + + return [] unless nodes + + selected_fields = nodes.selections.map(&:name) preloads.each.flat_map do |name, requirements| - selection&.selects?(name) ? requirements : [] + selected_fields.include?(name) ? requirements : [] end end diff --git a/app/graphql/resolvers/concerns/project_search_arguments.rb b/app/graphql/resolvers/concerns/project_search_arguments.rb new file mode 100644 index 00000000000..7e03963f412 --- /dev/null +++ b/app/graphql/resolvers/concerns/project_search_arguments.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module ProjectSearchArguments + extend ActiveSupport::Concern + + included do + argument :membership, GraphQL::Types::Boolean, + required: false, + description: 'Return only projects that the current user is a member of.' + + argument :search, GraphQL::Types::String, + required: false, + description: 'Search query, which can be for the project name, a path, or a description.' + + argument :search_namespaces, GraphQL::Types::Boolean, + required: false, + description: 'Include namespace in project search.' + + argument :topics, type: [GraphQL::Types::String], + required: false, + description: 'Filter projects by topics.' + end + + private + + def project_finder_params(params) + { + without_deleted: true, + non_public: params[:membership], + search: params[:search], + search_namespaces: params[:search_namespaces], + sort: params[:sort], + topic: params[:topics] + }.compact + end +end diff --git a/app/graphql/resolvers/concerns/search_arguments.rb b/app/graphql/resolvers/concerns/search_arguments.rb index 7f480f9d0b6..95c6dbf7497 100644 --- a/app/graphql/resolvers/concerns/search_arguments.rb +++ b/app/graphql/resolvers/concerns/search_arguments.rb @@ -7,12 +7,49 @@ module SearchArguments argument :search, GraphQL::Types::String, required: false, description: 'Search query for title or description.' + argument :in, [Types::IssuableSearchableFieldEnum], + required: false, + description: <<~DESC + Specify the fields to perform the search in. + Defaults to `[TITLE, DESCRIPTION]`. Requires the `search` argument.' + DESC + end + + def ready?(**args) + validate_search_in_params!(args) + validate_anonymous_search_access!(args) + + super end - def validate_anonymous_search_access! + private + + def validate_anonymous_search_access!(args) + return unless args[:search].present? return if current_user.present? || Feature.disabled?(:disable_anonymous_search, type: :ops) raise ::Gitlab::Graphql::Errors::ArgumentError, "User must be authenticated to include the `search` argument." end + + def validate_search_in_params!(args) + return unless args[:in].present? && args[:search].blank? + + raise Gitlab::Graphql::Errors::ArgumentError, + '`search` should be present when including the `in` argument' + end + + def prepare_finder_params(args) + prepare_search_params(args) + end + + def prepare_search_params(args) + return args unless args[:search].present? + + parent_type = resource_parent.is_a?(Project) ? :project : :group + args[:"attempt_#{parent_type}_search_optimizations"] = true + args[:in] = args[:in].join(',') if args[:in].present? + + args + end end diff --git a/app/graphql/resolvers/crm/organization_state_counts_resolver.rb b/app/graphql/resolvers/crm/organization_state_counts_resolver.rb new file mode 100644 index 00000000000..c16a4bd24ea --- /dev/null +++ b/app/graphql/resolvers/crm/organization_state_counts_resolver.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +module Resolvers + module Crm + class OrganizationStateCountsResolver < BaseResolver + include Gitlab::Graphql::Authorize::AuthorizeResource + + authorize :read_crm_organization + authorizes_object! + + type Types::CustomerRelations::OrganizationStateCountsType, null: true + + argument :search, GraphQL::Types::String, + required: false, + description: 'Search term to find organizations with.' + + argument :state, Types::CustomerRelations::OrganizationStateEnum, + required: false, + description: 'State of the organizations to search for.' + + def resolve(**args) + ::Crm::OrganizationsFinder.counts_by_state(context[:current_user], args.merge({ group: object })) + end + end + end +end diff --git a/app/graphql/resolvers/crm/organizations_resolver.rb b/app/graphql/resolvers/crm/organizations_resolver.rb index ca0a908ee22..719834f406d 100644 --- a/app/graphql/resolvers/crm/organizations_resolver.rb +++ b/app/graphql/resolvers/crm/organizations_resolver.rb @@ -10,6 +10,11 @@ module Resolvers type Types::CustomerRelations::OrganizationType, null: true + argument :sort, Types::CustomerRelations::OrganizationSortEnum, + description: 'Criteria to sort organizations by.', + required: false, + default_value: { field: 'name', direction: :asc } + argument :search, GraphQL::Types::String, required: false, description: 'Search term used to find organizations with.' @@ -24,6 +29,7 @@ module Resolvers def resolve(**args) args[:ids] = resolve_ids(args.delete(:ids)) + args.delete(:state) if args[:state] == :all ::Crm::OrganizationsFinder.new(current_user, { group: group }.merge(args)).execute end diff --git a/app/graphql/resolvers/deployment_resolver.rb b/app/graphql/resolvers/deployment_resolver.rb new file mode 100644 index 00000000000..7d9ce0f023c --- /dev/null +++ b/app/graphql/resolvers/deployment_resolver.rb @@ -0,0 +1,20 @@ +# frozen_string_literal: true + +module Resolvers + class DeploymentResolver < BaseResolver + argument :iid, + GraphQL::Types::ID, + required: true, + description: 'Project-level internal ID of the Deployment.' + + type Types::DeploymentType, null: true + + alias_method :project, :object + + def resolve(iid:) + return unless project.present? && project.is_a?(::Project) + + Deployment.for_iid(project, iid) + end + end +end diff --git a/app/graphql/resolvers/deployments_resolver.rb b/app/graphql/resolvers/deployments_resolver.rb new file mode 100644 index 00000000000..341d23c2ccb --- /dev/null +++ b/app/graphql/resolvers/deployments_resolver.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Resolvers + class DeploymentsResolver < BaseResolver + argument :statuses, [Types::DeploymentStatusEnum], + description: 'Statuses of the deployments.', + required: false, + as: :status + + argument :order_by, Types::DeploymentsOrderByInputType, + description: 'Order by a specified field.', + required: false + + type Types::DeploymentType, null: true + + alias_method :environment, :object + + def resolve(**args) + return unless environment.present? && environment.is_a?(::Environment) + + args = transform_args_for_finder(**args) + + # GraphQL BatchLoader shouldn't be used here because pagination query will be inefficient + # that fetches thousands of rows before limiting and offsetting. + DeploymentsFinder.new(environment: environment.id, **args).execute + end + + private + + def transform_args_for_finder(**args) + if (order_by = args.delete(:order_by)) + order_by = order_by.to_h.map { |k, v| { order_by: k.to_s, sort: v } }.first + args.merge!(order_by) + end + + args + end + end +end diff --git a/app/graphql/resolvers/environments/last_deployment_resolver.rb b/app/graphql/resolvers/environments/last_deployment_resolver.rb new file mode 100644 index 00000000000..76f80112673 --- /dev/null +++ b/app/graphql/resolvers/environments/last_deployment_resolver.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +module Resolvers + module Environments + class LastDeploymentResolver < BaseResolver + argument :status, + Types::DeploymentStatusEnum, + required: true, + description: 'Status of the Deployment.' + + type Types::DeploymentType, null: true + + def resolve(status:) + return unless object.present? && object.is_a?(::Environment) + + validate!(status) + + find_last_deployment(status) + end + + private + + def find_last_deployment(status) + BatchLoader::GraphQL.for(object).batch(key: status) do |environments, loader, args| + association_name = "last_#{args[:key]}_deployment".to_sym + + Preloaders::Environments::DeploymentPreloader.new(environments) + .execute_with_union(association_name, {}) + + environments.each do |environment| + loader.call(environment, environment.public_send(association_name)) # rubocop:disable GitlabSecurity/PublicSend + end + end + end + + def validate!(status) + unless Deployment::FINISHED_STATUSES.include?(status.to_sym) || + Deployment::UPCOMING_STATUSES.include?(status.to_sym) + raise Gitlab::Graphql::Errors::ArgumentError, "\"#{status}\" status is not supported." + end + end + end + end +end diff --git a/app/graphql/resolvers/environments_resolver.rb b/app/graphql/resolvers/environments_resolver.rb index 934c1ba2738..f265e2183d0 100644 --- a/app/graphql/resolvers/environments_resolver.rb +++ b/app/graphql/resolvers/environments_resolver.rb @@ -21,8 +21,8 @@ module Resolvers def resolve(**args) return unless project.present? - Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute - rescue Environments::EnvironmentsFinder::InvalidStatesError => e + ::Environments::EnvironmentsFinder.new(project, context[:current_user], args).execute + rescue ::Environments::EnvironmentsFinder::InvalidStatesError => e raise Gitlab::Graphql::Errors::ArgumentError, e.message end end diff --git a/app/graphql/resolvers/group_packages_resolver.rb b/app/graphql/resolvers/group_packages_resolver.rb index b48e0b75190..e6a6abb39dd 100644 --- a/app/graphql/resolvers/group_packages_resolver.rb +++ b/app/graphql/resolvers/group_packages_resolver.rb @@ -5,6 +5,8 @@ module Resolvers class GroupPackagesResolver < PackagesBaseResolver # The GraphQL type is defined in the extended class + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + argument :sort, Types::Packages::PackageGroupSortEnum, description: 'Sort packages by this criteria.', required: false, @@ -15,14 +17,6 @@ module Resolvers project_path_asc: { order_by: 'project_path', sort: 'asc' } }).freeze - def ready?(**args) - context[self.class] ||= { executions: 0 } - context[self.class][:executions] += 1 - raise GraphQL::ExecutionError, "Packages can be requested only for one group at a time" if context[self.class][:executions] > 1 - - super - end - def resolve(sort:, **filters) return unless packages_available? diff --git a/app/graphql/resolvers/members_resolver.rb b/app/graphql/resolvers/members_resolver.rb index 827db54134a..3d7894fdd6a 100644 --- a/app/graphql/resolvers/members_resolver.rb +++ b/app/graphql/resolvers/members_resolver.rb @@ -11,6 +11,10 @@ module Resolvers required: false, description: 'Search query.' + argument :sort, ::Types::MemberSortEnum, + required: false, + description: 'sort query.' + def resolve_with_lookahead(**args) authorize!(object) diff --git a/app/graphql/resolvers/package_details_resolver.rb b/app/graphql/resolvers/package_details_resolver.rb index 705d3900cd2..b77c6b1112b 100644 --- a/app/graphql/resolvers/package_details_resolver.rb +++ b/app/graphql/resolvers/package_details_resolver.rb @@ -2,20 +2,14 @@ module Resolvers class PackageDetailsResolver < BaseResolver + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 + type ::Types::Packages::PackageDetailsType, null: true argument :id, ::Types::GlobalIDType[::Packages::Package], required: true, description: 'Global ID of the package.' - def ready?(**args) - context[self.class] ||= { executions: 0 } - context[self.class][:executions] += 1 - raise GraphQL::ExecutionError, "Package details can be requested only for one package at a time" if context[self.class][:executions] > 1 - - super - end - def resolve(id:) GitlabSchema.find_by_gid(id) end diff --git a/app/graphql/resolvers/project_jobs_resolver.rb b/app/graphql/resolvers/project_jobs_resolver.rb index b09158d475d..4d13a4a3fae 100644 --- a/app/graphql/resolvers/project_jobs_resolver.rb +++ b/app/graphql/resolvers/project_jobs_resolver.rb @@ -8,6 +8,7 @@ module Resolvers type ::Types::Ci::JobType.connection_type, null: true authorize :read_build authorizes_object! + extension ::Gitlab::Graphql::Limit::FieldCallCount, limit: 1 argument :statuses, [::Types::Ci::JobStatusEnum], required: false, @@ -15,15 +16,6 @@ module Resolvers alias_method :project, :object - def ready?(**args) - context[self.class] ||= { executions: 0 } - context[self.class][:executions] += 1 - - raise GraphQL::ExecutionError, "Jobs can be requested for only one project at a time" if context[self.class][:executions] > 1 - - super - end - def resolve_with_lookahead(statuses: nil) jobs = ::Ci::JobsFinder.new(current_user: current_user, project: project, params: { scope: statuses }).execute diff --git a/app/graphql/resolvers/projects/branch_rules_resolver.rb b/app/graphql/resolvers/projects/branch_rules_resolver.rb new file mode 100644 index 00000000000..6c8b416bcea --- /dev/null +++ b/app/graphql/resolvers/projects/branch_rules_resolver.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module Resolvers + module Projects + class BranchRulesResolver < BaseResolver + type Types::Projects::BranchRuleType.connection_type, null: false + + alias_method :project, :object + + def resolve(**args) + project.protected_branches + end + end + end +end diff --git a/app/graphql/resolvers/projects_resolver.rb b/app/graphql/resolvers/projects_resolver.rb index facf8ffe36f..4d1e1b867da 100644 --- a/app/graphql/resolvers/projects_resolver.rb +++ b/app/graphql/resolvers/projects_resolver.rb @@ -2,31 +2,18 @@ module Resolvers class ProjectsResolver < BaseResolver - type Types::ProjectType, null: true - - argument :membership, GraphQL::Types::Boolean, - required: false, - description: 'Limit projects that the current user is a member of.' + include ProjectSearchArguments - argument :search, GraphQL::Types::String, - required: false, - description: 'Search query for project name, path, or description.' + type Types::ProjectType, null: true argument :ids, [GraphQL::Types::ID], required: false, description: 'Filter projects by IDs.' - argument :search_namespaces, GraphQL::Types::Boolean, - required: false, - description: 'Include namespace in project search.' - argument :sort, GraphQL::Types::String, required: false, - description: 'Sort order of results.' - - argument :topics, type: [GraphQL::Types::String], - required: false, - description: 'Filters projects by topics.' + description: "Sort order of results. Format: '<field_name>_<sort_direction>', " \ + "for example: 'id_desc' or 'name_asc'" def resolve(**args) ProjectsFinder @@ -36,17 +23,6 @@ module Resolvers private - def project_finder_params(params) - { - without_deleted: true, - non_public: params[:membership], - search: params[:search], - search_namespaces: params[:search_namespaces], - sort: params[:sort], - topic: params[:topics] - }.compact - end - def parse_gids(gids) gids&.map { |gid| GitlabSchema.parse_gid(gid, expected_type: ::Project).model_id } end diff --git a/app/graphql/resolvers/work_items_resolver.rb b/app/graphql/resolvers/work_items_resolver.rb index 055984db3cb..9c7931a4edb 100644 --- a/app/graphql/resolvers/work_items_resolver.rb +++ b/app/graphql/resolvers/work_items_resolver.rb @@ -26,27 +26,31 @@ module Resolvers required: false def resolve_with_lookahead(**args) - # The project could have been loaded in batch by `BatchLoader`. - # At this point we need the `id` of the project to query for issues, so - # make sure it's loaded and not `nil` before continuing. - parent = object.respond_to?(:sync) ? object.sync : object - return WorkItem.none if parent.nil? || !parent.work_items_feature_flag_enabled? + return WorkItem.none if resource_parent.nil? || !resource_parent.work_items_feature_flag_enabled? - args[:iids] ||= [args.delete(:iid)].compact if args[:iid] - args[:attempt_project_search_optimizations] = true if args[:search].present? + finder = ::WorkItems::WorkItemsFinder.new(current_user, prepare_finder_params(args)) - finder = ::WorkItems::WorkItemsFinder.new(current_user, args) - - Gitlab::Graphql::Loaders::IssuableLoader.new(parent, finder).batching_find_all { |q| apply_lookahead(q) } + Gitlab::Graphql::Loaders::IssuableLoader.new(resource_parent, finder).batching_find_all { |q| apply_lookahead(q) } end - def ready?(**args) - validate_anonymous_search_access! if args[:search].present? + private - super + def preloads + { + last_edited_by: :last_edited_by + } end - private + # Allows to apply lookahead for fields + # selected from WidgetInterface + override :node_selection + def node_selection + selected_fields = super + + return unless selected_fields + + selected_fields.selection(:widgets) + end def unconditional_includes [ @@ -56,6 +60,22 @@ module Resolvers :author ] end + + def prepare_finder_params(args) + params = super(args) + params[:iids] ||= [params.delete(:iid)].compact if params[:iid] + + params + end + + def resource_parent + # The project could have been loaded in batch by `BatchLoader`. + # At this point we need the `id` of the project to query for work items, so + # make sure it's loaded and not `nil` before continuing. + strong_memoize(:resource_parent) do + object.respond_to?(:sync) ? object.sync : object + end + end end end |