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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
|
# frozen_string_literal: true
module Resolvers
class BaseResolver < GraphQL::Schema::Resolver
extend ::Gitlab::Utils::Override
include ::Gitlab::Utils::StrongMemoize
include ::Gitlab::Graphql::GlobalIDCompatibility
argument_class ::Types::BaseArgument
def self.requires_argument!
@requires_argument = true
end
def self.field_options
super.merge(requires_argument: @requires_argument)
end
def self.singular_type
return unless type
unwrapped = type.unwrap
%i[node_type relay_node_type of_type itself].reduce(nil) do |t, m|
t || unwrapped.try(m)
end
end
def self.when_single(&block)
as_single << block
# Have we been called after defining the single version of this resolver?
if @single.present?
@single.instance_exec(&block)
end
end
def self.as_single
@as_single ||= []
end
def self.single_definition_blocks
ancestors.flat_map { |klass| klass.try(:as_single) || [] }
end
def self.single
@single ||= begin
parent = self
klass = Class.new(self) do
type parent.singular_type, null: true
def ready?(**args)
ready, early_return = super
[ready, select_result(early_return)]
end
def resolve(**args)
select_result(super)
end
def single?
true
end
def select_result(results)
results&.first
end
define_singleton_method :to_s do
"#{parent}.single"
end
end
single_definition_blocks.each do |definition|
klass.instance_exec(&definition)
end
klass
end
end
def self.last
parent = self
@last ||= Class.new(self.single) do
type parent.singular_type, null: true
def select_result(results)
results&.last
end
define_singleton_method :to_s do
"#{parent}.last"
end
end
end
def self.complexity
0
end
def self.resolver_complexity(args, child_complexity:)
complexity = 1
complexity += 1 if args[:sort]
complexity += 5 if args[:search]
complexity
end
def self.complexity_multiplier(args)
# When fetching many items, additional complexity is added to the field
# depending on how many items is fetched. For each item we add 1% of the
# original complexity - this means that loading 100 items (our default
# maxp_age_size limit) doubles the original complexity.
#
# Complexity is not increased when searching by specific ID(s), because
# complexity difference is minimal in this case.
[args[:iid], args[:iids]].any? ? 0 : 0.01
end
def offset_pagination(relation)
::Gitlab::Graphql::Pagination::OffsetPaginatedRelation.new(relation)
end
override :object
def object
super.tap do |obj|
# If the field this resolver is used in is wrapped in a presenter, unwrap its subject
break obj.subject if obj.is_a?(Gitlab::View::Presenter::Base)
end
end
# TODO: remove! This should never be necessary
# Remove as part of https://gitlab.com/gitlab-org/gitlab/-/issues/13984,
# since once we use that authorization approach, the object is guaranteed to
# be synchronized before any field.
def synchronized_object
strong_memoize(:synchronized_object) do
::Gitlab::Graphql::Lazy.force(object)
end
end
def single?
false
end
def current_user
context[:current_user]
end
# Overridden in sub-classes (see .single, .last)
def select_result(results)
results
end
end
end
|