diff options
Diffstat (limited to 'app/graphql/resolvers/concerns')
-rw-r--r-- | app/graphql/resolvers/concerns/looks_ahead.rb | 52 | ||||
-rw-r--r-- | app/graphql/resolvers/concerns/resolves_merge_requests.rb | 67 | ||||
-rw-r--r-- | app/graphql/resolvers/concerns/resolves_project.rb | 15 |
3 files changed, 134 insertions, 0 deletions
diff --git a/app/graphql/resolvers/concerns/looks_ahead.rb b/app/graphql/resolvers/concerns/looks_ahead.rb new file mode 100644 index 00000000000..becc6debd33 --- /dev/null +++ b/app/graphql/resolvers/concerns/looks_ahead.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module LooksAhead + extend ActiveSupport::Concern + + FEATURE_FLAG = :graphql_lookahead_support + + included do + attr_accessor :lookahead + end + + def resolve(**args) + self.lookahead = args.delete(:lookahead) + + resolve_with_lookahead(**args) + end + + def apply_lookahead(query) + return query unless Feature.enabled?(FEATURE_FLAG) + + selection = node_selection + + includes = preloads.each.flat_map do |name, requirements| + selection&.selects?(name) ? requirements : [] + end + preloads = (unconditional_includes + includes).uniq + + return query if preloads.empty? + + query.preload(*preloads) # rubocop: disable CodeReuse/ActiveRecord + end + + private + + def unconditional_includes + [] + end + + def preloads + {} + end + + def node_selection + return unless lookahead + + if lookahead.selects?(:nodes) + lookahead.selection(:nodes) + elsif lookahead.selects?(:edges) + lookahead.selection(:edges).selection(:nodes) + end + end +end diff --git a/app/graphql/resolvers/concerns/resolves_merge_requests.rb b/app/graphql/resolvers/concerns/resolves_merge_requests.rb new file mode 100644 index 00000000000..a2140728a27 --- /dev/null +++ b/app/graphql/resolvers/concerns/resolves_merge_requests.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true + +# Mixin for resolving merge requests. All arguments must be in forms +# that `MergeRequestsFinder` can handle, so you may need to use aliasing. +module ResolvesMergeRequests + extend ActiveSupport::Concern + include LooksAhead + + included do + type Types::MergeRequestType, null: true + end + + def resolve_with_lookahead(**args) + args[:iids] = Array.wrap(args[:iids]) if args[:iids] + args.compact! + + if project && args.keys == [:iids] + batch_load_merge_requests(args[:iids]) + else + args[:project_id] ||= project + + apply_lookahead(MergeRequestsFinder.new(current_user, args).execute) + end.then(&(single? ? :first : :itself)) + end + + def ready?(**args) + return early_return if no_results_possible?(args) + + super + end + + def early_return + [false, single? ? nil : MergeRequest.none] + end + + private + + def batch_load_merge_requests(iids) + iids.map { |iid| batch_load(iid) }.select(&:itself) # .compact doesn't work on BatchLoader + end + + # rubocop: disable CodeReuse/ActiveRecord + def batch_load(iid) + BatchLoader::GraphQL.for(iid.to_s).batch(key: project) do |iids, loader, args| + query = args[:key].merge_requests.where(iid: iids) + + apply_lookahead(query).each do |mr| + loader.call(mr.iid.to_s, mr) + end + end + end + # rubocop: enable CodeReuse/ActiveRecord + + def unconditional_includes + [:target_project] + end + + def preloads + { + assignees: [:assignees], + labels: [:labels], + author: [:author], + milestone: [:milestone], + head_pipeline: [:merge_request_diff, { head_pipeline: [:merge_request] }] + } + end +end diff --git a/app/graphql/resolvers/concerns/resolves_project.rb b/app/graphql/resolvers/concerns/resolves_project.rb new file mode 100644 index 00000000000..3c5ce3dab01 --- /dev/null +++ b/app/graphql/resolvers/concerns/resolves_project.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +module ResolvesProject + def resolve_project(full_path: nil, project_id: nil) + unless full_path.present? ^ project_id.present? + raise ::Gitlab::Graphql::Errors::ArgumentError, 'Incompatible arguments: projectId, projectPath.' + end + + if full_path.present? + ::Gitlab::Graphql::Loaders::FullPathModelLoader.new(Project, full_path).find + else + ::GitlabSchema.object_from_id(project_id, expected_type: Project) + end + end +end |