summaryrefslogtreecommitdiff
path: root/app/graphql/types/base_field.rb
blob: 42c7eb6b485b8ba12b923f6937dc75b00a9631ff (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# frozen_string_literal: true

module Types
  class BaseField < GraphQL::Schema::Field
    prepend Gitlab::Graphql::Authorize

    attr_reader :calls_gitaly

    DEFAULT_COMPLEXITY = 1

    def initialize(*args, **kwargs, &block)
      @calls_gitaly = !!kwargs.delete(:calls_gitaly)
      kwargs[:complexity] ||= field_complexity(kwargs[:resolver_class])

      super(*args, **kwargs, &block)
    end

    def base_complexity
      complexity = DEFAULT_COMPLEXITY
      complexity += 1 if @calls_gitaly
      complexity
    end

    def calls_gitaly_check(calls)
      return if @calls_gitaly
      return if calls < 1

      # Will inform you if :calls_gitaly should be true or false based on the number of Gitaly calls
      # involved with the request.
      raise "Gitaly is called for field '#{name}' #{"on type #{owner.name} " if owner}- please add `calls_gitaly: true` to the field declaration"
    rescue => e
      Gitlab::Sentry.track_exception(e)
    end

    private

    def field_complexity(resolver_class)
      if resolver_class
        field_resolver_complexity
      else
        base_complexity
      end
    end

    def field_resolver_complexity
      # Complexity can be either integer or proc. If proc is used then it's
      # called when computing a query complexity and context and query
      # arguments are available for computing complexity.  For resolvers we use
      # proc because we set complexity depending on arguments and number of
      # items which can be loaded.
      proc do |ctx, args, child_complexity|
        # Resolvers may add extra complexity depending on used arguments
        complexity = child_complexity + self.resolver&.try(:resolver_complexity, args, child_complexity: child_complexity).to_i

        field_defn = to_graphql

        if field_defn.connection?
          # Resolvers may add extra complexity depending on number of items being loaded.
          page_size   = field_defn.connection_max_page_size || ctx.schema.default_max_page_size
          limit_value = [args[:first], args[:last], page_size].compact.min
          multiplier  = self.resolver&.try(:complexity_multiplier, args).to_f
          complexity += complexity * limit_value * multiplier
        end

        complexity.to_i
      end
    end
  end
end