graphql 2.3.7 → 2.4.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/field_usage.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +3 -3
- data/lib/graphql/analysis/visitor.rb +8 -7
- data/lib/graphql/analysis.rb +4 -4
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/current.rb +52 -0
- data/lib/graphql/dataloader/async_dataloader.rb +7 -6
- data/lib/graphql/dataloader/source.rb +7 -4
- data/lib/graphql/dataloader.rb +40 -19
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +13 -9
- data/lib/graphql/execution/interpreter/runtime.rb +35 -31
- data/lib/graphql/execution/interpreter.rb +6 -4
- data/lib/graphql/execution/lookahead.rb +18 -11
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +6 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/invalid_null_error.rb +1 -1
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/document_from_schema_definition.rb +62 -34
- data/lib/graphql/language/lexer.rb +18 -15
- data/lib/graphql/language/nodes.rb +24 -16
- data/lib/graphql/language/parser.rb +14 -1
- data/lib/graphql/language/printer.rb +31 -15
- data/lib/graphql/language/sanitized_printer.rb +1 -1
- data/lib/graphql/language.rb +6 -6
- data/lib/graphql/pagination/connection.rb +1 -1
- data/lib/graphql/query/context/scoped_context.rb +1 -1
- data/lib/graphql/query/context.rb +13 -6
- data/lib/graphql/query/null_context.rb +3 -5
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query.rb +72 -18
- data/lib/graphql/railtie.rb +7 -0
- data/lib/graphql/rubocop/graphql/field_type_in_block.rb +144 -0
- data/lib/graphql/rubocop/graphql/root_types_in_block.rb +38 -0
- data/lib/graphql/rubocop.rb +2 -0
- data/lib/graphql/schema/addition.rb +2 -1
- data/lib/graphql/schema/always_visible.rb +6 -2
- data/lib/graphql/schema/argument.rb +14 -1
- data/lib/graphql/schema/build_from_definition.rb +9 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive.rb +1 -1
- data/lib/graphql/schema/enum.rb +71 -23
- data/lib/graphql/schema/enum_value.rb +10 -2
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +102 -47
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +5 -2
- data/lib/graphql/schema/input_object.rb +90 -39
- data/lib/graphql/schema/interface.rb +22 -5
- data/lib/graphql/schema/introspection_system.rb +5 -16
- data/lib/graphql/schema/loader.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +15 -0
- data/lib/graphql/schema/member/has_arguments.rb +36 -23
- data/lib/graphql/schema/member/has_directives.rb +3 -3
- data/lib/graphql/schema/member/has_fields.rb +26 -6
- data/lib/graphql/schema/member/has_interfaces.rb +4 -4
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +5 -1
- data/lib/graphql/schema/member/has_validators.rb +1 -1
- data/lib/graphql/schema/object.rb +8 -0
- data/lib/graphql/schema/printer.rb +1 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +0 -1
- data/lib/graphql/schema/resolver.rb +12 -14
- data/lib/graphql/schema/subscription.rb +2 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +28 -4
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +188 -0
- data/lib/graphql/schema/visibility/profile.rb +359 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +294 -0
- data/lib/graphql/schema/warden.rb +179 -16
- data/lib/graphql/schema.rb +348 -94
- data/lib/graphql/static_validation/base_visitor.rb +6 -5
- data/lib/graphql/static_validation/literal_validator.rb +4 -4
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +3 -2
- data/lib/graphql/static_validation/rules/directives_are_defined.rb +3 -3
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +2 -0
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +12 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +8 -7
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/fragment_types_exist.rb +12 -2
- data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
- data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/no_definitions_are_present.rb +1 -1
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +3 -3
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +18 -27
- data/lib/graphql/static_validation/rules/variable_names_are_unique.rb +1 -1
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +11 -2
- data/lib/graphql/static_validation/validation_context.rb +18 -2
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +3 -2
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +10 -4
- data/lib/graphql/subscriptions/event.rb +1 -1
- data/lib/graphql/subscriptions/serialize.rb +2 -0
- data/lib/graphql/subscriptions.rb +6 -4
- data/lib/graphql/testing/helpers.rb +10 -6
- data/lib/graphql/tracing/notifications_trace.rb +2 -2
- data/lib/graphql/types/relay/connection_behaviors.rb +12 -2
- data/lib/graphql/types/relay/edge_behaviors.rb +11 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -11
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +53 -45
- metadata +31 -8
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
data/lib/graphql/schema/enum.rb
CHANGED
@@ -22,9 +22,21 @@ module GraphQL
|
|
22
22
|
class Enum < GraphQL::Schema::Member
|
23
23
|
extend GraphQL::Schema::Member::ValidatesInput
|
24
24
|
|
25
|
+
# This is raised when either:
|
26
|
+
#
|
27
|
+
# - A resolver returns a value which doesn't match any of the enum's configured values;
|
28
|
+
# - Or, the resolver returns a value which matches a value, but that value's `authorized?` check returns false.
|
29
|
+
#
|
30
|
+
# In either case, the field should be modified so that the invalid value isn't returned.
|
31
|
+
#
|
32
|
+
# {GraphQL::Schema::Enum} subclasses get their own subclass of this error, so that bug trackers can better show where they came from.
|
25
33
|
class UnresolvedValueError < GraphQL::Error
|
26
|
-
def initialize(value:, enum:, context:)
|
27
|
-
fix_message =
|
34
|
+
def initialize(value:, enum:, context:, authorized:)
|
35
|
+
fix_message = if authorized == false
|
36
|
+
", but this value was unauthorized. Update the field or resolver to return a different value in this case (or return `nil`)."
|
37
|
+
else
|
38
|
+
", but this isn't a valid value for `#{enum.graphql_name}`. Update the field or resolver to return one of `#{enum.graphql_name}`'s values instead."
|
39
|
+
end
|
28
40
|
message = if (cp = context[:current_path]) && (cf = context[:current_field])
|
29
41
|
"`#{cf.path}` returned `#{value.inspect}` at `#{cp.join(".")}`#{fix_message}"
|
30
42
|
else
|
@@ -34,6 +46,8 @@ module GraphQL
|
|
34
46
|
end
|
35
47
|
end
|
36
48
|
|
49
|
+
# Raised when a {GraphQL::Schema::Enum} is defined to have no values.
|
50
|
+
# This can also happen when all values return false for `.visible?`.
|
37
51
|
class MissingValuesError < GraphQL::Error
|
38
52
|
def initialize(enum_type)
|
39
53
|
@enum_type = enum_type
|
@@ -43,10 +57,11 @@ module GraphQL
|
|
43
57
|
|
44
58
|
class << self
|
45
59
|
# Define a value for this enum
|
46
|
-
# @
|
47
|
-
# @
|
48
|
-
# @
|
49
|
-
# @
|
60
|
+
# @option kwargs [String, Symbol] :graphql_name the GraphQL value for this, usually `SCREAMING_CASE`
|
61
|
+
# @option kwargs [String] :description, the GraphQL description for this value, present in documentation
|
62
|
+
# @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
|
63
|
+
# @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
|
64
|
+
# @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
|
50
65
|
# @return [void]
|
51
66
|
# @see {Schema::EnumValue} which handles these inputs by default
|
52
67
|
def value(*args, **kwargs, &block)
|
@@ -71,10 +86,24 @@ module GraphQL
|
|
71
86
|
def enum_values(context = GraphQL::Query::NullContext.instance)
|
72
87
|
inherited_values = superclass.respond_to?(:enum_values) ? superclass.enum_values(context) : nil
|
73
88
|
visible_values = []
|
74
|
-
|
89
|
+
types = Warden.types_from_context(context)
|
75
90
|
own_values.each do |key, values_entry|
|
76
|
-
|
77
|
-
|
91
|
+
visible_value = nil
|
92
|
+
if values_entry.is_a?(Array)
|
93
|
+
values_entry.each do |v|
|
94
|
+
if types.visible_enum_value?(v, context)
|
95
|
+
if visible_value.nil?
|
96
|
+
visible_value = v
|
97
|
+
visible_values << v
|
98
|
+
else
|
99
|
+
raise DuplicateNamesError.new(
|
100
|
+
duplicated_name: v.path, duplicated_definition_1: visible_value.inspect, duplicated_definition_2: v.inspect
|
101
|
+
)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
elsif types.visible_enum_value?(values_entry, context)
|
106
|
+
visible_values << values_entry
|
78
107
|
end
|
79
108
|
end
|
80
109
|
|
@@ -130,7 +159,7 @@ module GraphQL
|
|
130
159
|
end
|
131
160
|
|
132
161
|
def validate_non_null_input(value_name, ctx, max_errors: nil)
|
133
|
-
allowed_values = ctx.
|
162
|
+
allowed_values = ctx.types.enum_values(self)
|
134
163
|
matching_value = allowed_values.find { |v| v.graphql_name == value_name }
|
135
164
|
|
136
165
|
if matching_value.nil?
|
@@ -138,35 +167,54 @@ module GraphQL
|
|
138
167
|
else
|
139
168
|
nil
|
140
169
|
end
|
170
|
+
# rescue MissingValuesError
|
171
|
+
# nil
|
141
172
|
end
|
142
173
|
|
174
|
+
# Called by the runtime when a field returns a value to give back to the client.
|
175
|
+
# This method checks that the incoming {value} matches one of the enum's defined values.
|
176
|
+
# @param value [Object] Any value matching the values for this enum.
|
177
|
+
# @param ctx [GraphQL::Query::Context]
|
178
|
+
# @raise [GraphQL::Schema::Enum::UnresolvedValueError] if {value} doesn't match a configured value or if the matching value isn't authorized.
|
179
|
+
# @return [String] The GraphQL-ready string for {value}
|
143
180
|
def coerce_result(value, ctx)
|
144
|
-
|
145
|
-
all_values =
|
181
|
+
types = ctx.types
|
182
|
+
all_values = types ? types.enum_values(self) : values.each_value
|
146
183
|
enum_value = all_values.find { |val| val.value == value }
|
147
|
-
if enum_value
|
184
|
+
if enum_value && (was_authed = enum_value.authorized?(ctx))
|
148
185
|
enum_value.graphql_name
|
149
186
|
else
|
150
|
-
raise self::UnresolvedValueError.new(enum: self, value: value, context: ctx)
|
187
|
+
raise self::UnresolvedValueError.new(enum: self, value: value, context: ctx, authorized: was_authed)
|
151
188
|
end
|
152
189
|
end
|
153
190
|
|
191
|
+
# Called by the runtime with incoming string representations from a query.
|
192
|
+
# It will match the string to a configured by name or by Ruby value.
|
193
|
+
# @param value_name [String, Object] A string from a GraphQL query, or a Ruby value matching a `value(..., value: ...)` configuration
|
194
|
+
# @param ctx [GraphQL::Query::Context]
|
195
|
+
# @raise [GraphQL::UnauthorizedEnumValueError] if an {EnumValue} matches but returns false for `.authorized?`. Goes to {Schema.unauthorized_object}.
|
196
|
+
# @return [Object] The Ruby value for the matched {GraphQL::Schema::EnumValue}
|
154
197
|
def coerce_input(value_name, ctx)
|
155
|
-
all_values = ctx.
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
198
|
+
all_values = ctx.types ? ctx.types.enum_values(self) : values.each_value
|
199
|
+
|
200
|
+
# This tries matching by incoming GraphQL string, then checks Ruby-defined values
|
201
|
+
if v = (all_values.find { |val| val.graphql_name == value_name } || all_values.find { |val| val.value == value_name })
|
202
|
+
if v.authorized?(ctx)
|
203
|
+
v.value
|
204
|
+
else
|
205
|
+
raise GraphQL::UnauthorizedEnumValueError.new(type: self, enum_value: v, context: ctx)
|
206
|
+
end
|
163
207
|
else
|
164
208
|
nil
|
165
209
|
end
|
166
210
|
end
|
167
211
|
|
168
212
|
def inherited(child_class)
|
169
|
-
child_class.
|
213
|
+
if child_class.name
|
214
|
+
# Don't assign a custom error class to anonymous classes
|
215
|
+
# because they would end up with names like `#<Class0x1234>::UnresolvedValueError` which messes up bug trackers
|
216
|
+
child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
|
217
|
+
end
|
170
218
|
super
|
171
219
|
end
|
172
220
|
|
@@ -30,10 +30,11 @@ module GraphQL
|
|
30
30
|
# @return [Class] The enum type that owns this value
|
31
31
|
attr_reader :owner
|
32
32
|
|
33
|
-
def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, value: NOT_CONFIGURED, deprecation_reason: nil, &block)
|
33
|
+
def initialize(graphql_name, desc = nil, owner:, ast_node: nil, directives: nil, description: nil, comment: nil, value: NOT_CONFIGURED, deprecation_reason: nil, &block)
|
34
34
|
@graphql_name = graphql_name.to_s
|
35
35
|
GraphQL::NameValidator.validate!(@graphql_name)
|
36
36
|
@description = desc || description
|
37
|
+
@comment = comment
|
37
38
|
@value = value == NOT_CONFIGURED ? @graphql_name : value
|
38
39
|
if deprecation_reason
|
39
40
|
self.deprecation_reason = deprecation_reason
|
@@ -58,6 +59,13 @@ module GraphQL
|
|
58
59
|
@description
|
59
60
|
end
|
60
61
|
|
62
|
+
def comment(new_comment = nil)
|
63
|
+
if new_comment
|
64
|
+
@comment = new_comment
|
65
|
+
end
|
66
|
+
@comment
|
67
|
+
end
|
68
|
+
|
61
69
|
def value(new_val = nil)
|
62
70
|
unless new_val.nil?
|
63
71
|
@value = new_val
|
@@ -66,7 +74,7 @@ module GraphQL
|
|
66
74
|
end
|
67
75
|
|
68
76
|
def inspect
|
69
|
-
"#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}>"
|
77
|
+
"#<#{self.class} #{path} @value=#{@value.inspect}#{description ? " @description=#{description.inspect}" : ""}#{deprecation_reason ? " @deprecation_reason=#{deprecation_reason.inspect}" : ""}>"
|
70
78
|
end
|
71
79
|
|
72
80
|
def visible?(_ctx); true; end
|
@@ -50,7 +50,7 @@ module GraphQL
|
|
50
50
|
if field.has_default_page_size? && !value.has_default_page_size_override?
|
51
51
|
value.default_page_size = field.default_page_size
|
52
52
|
end
|
53
|
-
if
|
53
|
+
if (custom_t = context.schema.connections.edge_class_for_field(@field))
|
54
54
|
value.edge_class = custom_t
|
55
55
|
end
|
56
56
|
value
|
@@ -12,7 +12,7 @@ module GraphQL
|
|
12
12
|
if ret_type.respond_to?(:scope_items)
|
13
13
|
scoped_items = ret_type.scope_items(value, context)
|
14
14
|
if !scoped_items.equal?(value) && !ret_type.reauthorize_scoped_objects
|
15
|
-
if (current_runtime_state =
|
15
|
+
if (current_runtime_state = Fiber[:__graphql_runtime_info]) &&
|
16
16
|
(query_runtime_state = current_runtime_state[context.query])
|
17
17
|
query_runtime_state.was_authorized_by_scope_items = true
|
18
18
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -42,8 +42,8 @@ module GraphQL
|
|
42
42
|
end
|
43
43
|
|
44
44
|
def directives
|
45
|
-
if @resolver_class && (r_dirs = @resolver_class.directives).
|
46
|
-
if (own_dirs = super).
|
45
|
+
if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
|
46
|
+
if !(own_dirs = super).empty?
|
47
47
|
own_dirs + r_dirs
|
48
48
|
else
|
49
49
|
r_dirs
|
@@ -81,7 +81,7 @@ module GraphQL
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def inspect
|
84
|
-
"#<#{self.class} #{path}#{all_argument_definitions.
|
84
|
+
"#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
|
85
85
|
end
|
86
86
|
|
87
87
|
alias :mutation :resolver
|
@@ -106,7 +106,7 @@ module GraphQL
|
|
106
106
|
# @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
|
107
107
|
# @return [GraphQL::Schema:Field] an instance of `self`
|
108
108
|
# @see {.initialize} for other options
|
109
|
-
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
109
|
+
def self.from_options(name = nil, type = nil, desc = nil, comment: nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
110
110
|
if (resolver_class = resolver || mutation || subscription)
|
111
111
|
# Add a reference to that parent class
|
112
112
|
kwargs[:resolver_class] = resolver_class
|
@@ -116,6 +116,10 @@ module GraphQL
|
|
116
116
|
kwargs[:name] = name
|
117
117
|
end
|
118
118
|
|
119
|
+
if comment
|
120
|
+
kwargs[:comment] = comment
|
121
|
+
end
|
122
|
+
|
119
123
|
if !type.nil?
|
120
124
|
if desc
|
121
125
|
if kwargs[:description]
|
@@ -146,11 +150,16 @@ module GraphQL
|
|
146
150
|
Member::BuildType.to_type_name(@return_type_expr)
|
147
151
|
elsif @resolver_class && @resolver_class.type
|
148
152
|
Member::BuildType.to_type_name(@resolver_class.type)
|
149
|
-
|
153
|
+
elsif type
|
150
154
|
# As a last ditch, try to force loading the return type:
|
151
155
|
type.unwrap.name
|
152
156
|
end
|
153
|
-
|
157
|
+
if return_type_name
|
158
|
+
@connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
|
159
|
+
else
|
160
|
+
# TODO set this when type is set by method
|
161
|
+
false # not loaded yet?
|
162
|
+
end
|
154
163
|
else
|
155
164
|
@connection
|
156
165
|
end
|
@@ -207,6 +216,7 @@ module GraphQL
|
|
207
216
|
# @param owner [Class] The type that this field belongs to
|
208
217
|
# @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
|
209
218
|
# @param description [String] Field description
|
219
|
+
# @param comment [String] Field comment
|
210
220
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
211
221
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
212
222
|
# @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
|
@@ -231,13 +241,13 @@ module GraphQL
|
|
231
241
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
232
242
|
# @param validates [Array<Hash>] Configurations for validating this field
|
233
243
|
# @param fallback_value [Object] A fallback value if the method is not defined
|
234
|
-
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
244
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
235
245
|
if name.nil?
|
236
246
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
237
247
|
end
|
238
248
|
if !(resolver_class)
|
239
|
-
if type.nil?
|
240
|
-
raise ArgumentError, "missing second `type` argument or
|
249
|
+
if type.nil? && !block_given?
|
250
|
+
raise ArgumentError, "missing second `type` argument, keyword `type:`, or a block containing `type(...)`"
|
241
251
|
end
|
242
252
|
end
|
243
253
|
@original_name = name
|
@@ -247,6 +257,7 @@ module GraphQL
|
|
247
257
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
248
258
|
|
249
259
|
@description = description
|
260
|
+
@comment = comment
|
250
261
|
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
|
251
262
|
|
252
263
|
self.deprecation_reason = deprecation_reason
|
@@ -302,6 +313,7 @@ module GraphQL
|
|
302
313
|
@ast_node = ast_node
|
303
314
|
@method_conflict_warning = method_conflict_warning
|
304
315
|
@fallback_value = fallback_value
|
316
|
+
@definition_block = definition_block
|
305
317
|
|
306
318
|
arguments.each do |name, arg|
|
307
319
|
case arg
|
@@ -321,28 +333,17 @@ module GraphQL
|
|
321
333
|
|
322
334
|
@extensions = EMPTY_ARRAY
|
323
335
|
@call_after_define = false
|
324
|
-
|
325
|
-
# but should it run after the definition block?
|
326
|
-
if scoped?
|
327
|
-
self.extension(ScopeExtension)
|
328
|
-
end
|
329
|
-
|
330
|
-
# The problem with putting this after the definition_block
|
331
|
-
# is that it would override arguments
|
332
|
-
if connection? && connection_extension
|
333
|
-
self.extension(connection_extension)
|
334
|
-
end
|
335
|
-
|
336
|
+
set_pagination_extensions(connection_extension: connection_extension)
|
336
337
|
# Do this last so we have as much context as possible when initializing them:
|
337
|
-
if extensions.
|
338
|
+
if !extensions.empty?
|
338
339
|
self.extensions(extensions)
|
339
340
|
end
|
340
341
|
|
341
|
-
if resolver_class && resolver_class.extensions.
|
342
|
+
if resolver_class && !resolver_class.extensions.empty?
|
342
343
|
self.extensions(resolver_class.extensions)
|
343
344
|
end
|
344
345
|
|
345
|
-
if directives.
|
346
|
+
if !directives.empty?
|
346
347
|
directives.each do |(dir_class, options)|
|
347
348
|
self.directive(dir_class, **options)
|
348
349
|
end
|
@@ -352,16 +353,29 @@ module GraphQL
|
|
352
353
|
self.validates(validates)
|
353
354
|
end
|
354
355
|
|
355
|
-
if
|
356
|
-
|
357
|
-
|
356
|
+
if @definition_block.nil?
|
357
|
+
self.extensions.each(&:after_define_apply)
|
358
|
+
@call_after_define = true
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
# Calls the definition block, if one was given.
|
363
|
+
# This is deferred so that references to the return type
|
364
|
+
# can be lazily evaluated, reducing Rails boot time.
|
365
|
+
# @return [self]
|
366
|
+
# @api private
|
367
|
+
def ensure_loaded
|
368
|
+
if @definition_block
|
369
|
+
if @definition_block.arity == 1
|
370
|
+
@definition_block.call(self)
|
358
371
|
else
|
359
|
-
instance_eval(
|
372
|
+
instance_eval(&@definition_block)
|
360
373
|
end
|
374
|
+
self.extensions.each(&:after_define_apply)
|
375
|
+
@call_after_define = true
|
376
|
+
@definition_block = nil
|
361
377
|
end
|
362
|
-
|
363
|
-
self.extensions.each(&:after_define_apply)
|
364
|
-
@call_after_define = true
|
378
|
+
self
|
365
379
|
end
|
366
380
|
|
367
381
|
attr_accessor :dynamic_introspection
|
@@ -393,6 +407,20 @@ module GraphQL
|
|
393
407
|
end
|
394
408
|
end
|
395
409
|
|
410
|
+
# @param text [String]
|
411
|
+
# @return [String, nil]
|
412
|
+
def comment(text = nil)
|
413
|
+
if text
|
414
|
+
@comment = text
|
415
|
+
elsif !NOT_CONFIGURED.equal?(@comment)
|
416
|
+
@comment
|
417
|
+
elsif @resolver_class
|
418
|
+
@resolver_class.comment
|
419
|
+
else
|
420
|
+
nil
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
396
424
|
# Read extension instances from this field,
|
397
425
|
# or add new classes/options to be initialized on this field.
|
398
426
|
# Extensions are executed in the order they are added.
|
@@ -413,7 +441,7 @@ module GraphQL
|
|
413
441
|
new_extensions.each do |extension_config|
|
414
442
|
if extension_config.is_a?(Hash)
|
415
443
|
extension_class, options = *extension_config.to_a[0]
|
416
|
-
self.extension(extension_class, options)
|
444
|
+
self.extension(extension_class, **options)
|
417
445
|
else
|
418
446
|
self.extension(extension_config)
|
419
447
|
end
|
@@ -433,7 +461,7 @@ module GraphQL
|
|
433
461
|
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
434
462
|
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
435
463
|
# @return [void]
|
436
|
-
def extension(extension_class, options
|
464
|
+
def extension(extension_class, **options)
|
437
465
|
extension_inst = extension_class.new(field: self, options: options)
|
438
466
|
if @extensions.frozen?
|
439
467
|
@extensions = @extensions.dup
|
@@ -454,7 +482,7 @@ module GraphQL
|
|
454
482
|
if new_extras.nil?
|
455
483
|
# Read the value
|
456
484
|
field_extras = @extras
|
457
|
-
if @resolver_class &&
|
485
|
+
if @resolver_class && !@resolver_class.extras.empty?
|
458
486
|
field_extras + @resolver_class.extras
|
459
487
|
else
|
460
488
|
field_extras
|
@@ -483,7 +511,7 @@ module GraphQL
|
|
483
511
|
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
484
512
|
max_possible_page_size = arguments[:last]
|
485
513
|
end
|
486
|
-
elsif arguments.is_a?(GraphQL::UnauthorizedError)
|
514
|
+
elsif arguments.is_a?(GraphQL::ExecutionError) || arguments.is_a?(GraphQL::UnauthorizedError)
|
487
515
|
raise arguments
|
488
516
|
end
|
489
517
|
|
@@ -577,16 +605,29 @@ module GraphQL
|
|
577
605
|
class MissingReturnTypeError < GraphQL::Error; end
|
578
606
|
attr_writer :type
|
579
607
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
608
|
+
# Get or set the return type of this field.
|
609
|
+
#
|
610
|
+
# It may return nil if no type was configured or if the given definition block wasn't called yet.
|
611
|
+
# @param new_type [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List] A GraphQL return type
|
612
|
+
# @return [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List, nil] the configured type for this field
|
613
|
+
def type(new_type = NOT_CONFIGURED)
|
614
|
+
if NOT_CONFIGURED.equal?(new_type)
|
615
|
+
if @resolver_class
|
616
|
+
return_type = @return_type_expr || @resolver_class.type_expr
|
617
|
+
if return_type.nil?
|
618
|
+
raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
|
619
|
+
end
|
620
|
+
nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
|
621
|
+
Member::BuildType.parse_type(return_type, null: nullable)
|
622
|
+
elsif !@return_type_expr.nil?
|
623
|
+
@type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
585
624
|
end
|
586
|
-
nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
|
587
|
-
Member::BuildType.parse_type(return_type, null: nullable)
|
588
625
|
else
|
589
|
-
@
|
626
|
+
@return_type_expr = new_type
|
627
|
+
# If `type` is set in the definition block, then the `connection_extension: ...` given as a keyword won't be used, hmm...
|
628
|
+
# Also, arguments added by `connection_extension` will clobber anything previously defined,
|
629
|
+
# so `type(...)` should go first.
|
630
|
+
set_pagination_extensions(connection_extension: self.class.connection_extension)
|
590
631
|
end
|
591
632
|
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
592
633
|
# Let this propagate up
|
@@ -618,7 +659,7 @@ module GraphQL
|
|
618
659
|
using_arg_values = false
|
619
660
|
end
|
620
661
|
|
621
|
-
args = context.
|
662
|
+
args = context.types.arguments(self)
|
622
663
|
args.each do |arg|
|
623
664
|
arg_key = arg.keyword
|
624
665
|
if arg_values.key?(arg_key)
|
@@ -691,7 +732,7 @@ module GraphQL
|
|
691
732
|
method_to_call = resolver_method
|
692
733
|
method_receiver = obj
|
693
734
|
# Call the method with kwargs, if there are any
|
694
|
-
if ruby_kwargs.
|
735
|
+
if !ruby_kwargs.empty?
|
695
736
|
obj.public_send(resolver_method, **ruby_kwargs)
|
696
737
|
else
|
697
738
|
obj.public_send(resolver_method)
|
@@ -711,7 +752,7 @@ module GraphQL
|
|
711
752
|
elsif inner_object.respond_to?(@method_sym)
|
712
753
|
method_to_call = @method_sym
|
713
754
|
method_receiver = obj.object
|
714
|
-
if ruby_kwargs.
|
755
|
+
if !ruby_kwargs.empty?
|
715
756
|
inner_object.public_send(@method_sym, **ruby_kwargs)
|
716
757
|
else
|
717
758
|
inner_object.public_send(@method_sym)
|
@@ -798,7 +839,7 @@ module GraphQL
|
|
798
839
|
unsatisfied_ruby_kwargs.clear
|
799
840
|
end
|
800
841
|
|
801
|
-
if unsatisfied_ruby_kwargs.
|
842
|
+
if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
|
802
843
|
raise FieldImplementationFailed.new, <<-ERR
|
803
844
|
Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
804
845
|
|
@@ -897,6 +938,20 @@ ERR
|
|
897
938
|
raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
|
898
939
|
end
|
899
940
|
end
|
941
|
+
|
942
|
+
def set_pagination_extensions(connection_extension:)
|
943
|
+
# This should run before connection extension,
|
944
|
+
# but should it run after the definition block?
|
945
|
+
if scoped?
|
946
|
+
self.extension(ScopeExtension, call_after_define: false)
|
947
|
+
end
|
948
|
+
|
949
|
+
# The problem with putting this after the definition_block
|
950
|
+
# is that it would override arguments
|
951
|
+
if connection? && connection_extension
|
952
|
+
self.extension(connection_extension, call_after_define: false)
|
953
|
+
end
|
954
|
+
end
|
900
955
|
end
|
901
956
|
end
|
902
957
|
end
|
@@ -32,7 +32,7 @@ module GraphQL
|
|
32
32
|
input_kwargs = {}
|
33
33
|
end
|
34
34
|
|
35
|
-
if input_kwargs.
|
35
|
+
if !input_kwargs.empty?
|
36
36
|
super(**input_kwargs)
|
37
37
|
else
|
38
38
|
super()
|
@@ -136,6 +136,8 @@ module GraphQL
|
|
136
136
|
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
137
137
|
end
|
138
138
|
end
|
139
|
+
# For compatibility, in case no arguments are defined:
|
140
|
+
has_no_arguments(true)
|
139
141
|
mutation(mutation_class)
|
140
142
|
# these might be inherited:
|
141
143
|
mutation_args.each do |arg|
|
@@ -149,7 +151,8 @@ module GraphQL
|
|
149
151
|
|
150
152
|
def authorize_arguments(args, values)
|
151
153
|
# remove the `input` wrapper to match values
|
152
|
-
|
154
|
+
input_type = args.find { |a| a.graphql_name == "input" }.type.unwrap
|
155
|
+
input_args = context.types.arguments(input_type)
|
153
156
|
super(input_args, values)
|
154
157
|
end
|
155
158
|
end
|