graphql 1.10.0.pre1 → 1.10.0.pre2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +1 -0
- data/lib/generators/graphql/install_generator.rb +1 -0
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/templates/base_field.erb +0 -4
- data/lib/generators/graphql/templates/base_mutation.erb +8 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +5 -0
- data/lib/generators/graphql/templates/mutation.erb +1 -1
- data/lib/generators/graphql/templates/schema.erb +1 -1
- data/lib/graphql.rb +4 -1
- data/lib/graphql/analysis/ast.rb +14 -13
- data/lib/graphql/analysis/ast/analyzer.rb +23 -4
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/max_query_complexity.rb +3 -3
- data/lib/graphql/analysis/ast/max_query_depth.rb +7 -3
- data/lib/graphql/analysis/ast/query_complexity.rb +2 -2
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/base_type.rb +1 -1
- data/lib/graphql/directive.rb +0 -1
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/execution/errors.rb +4 -8
- data/lib/graphql/execution/interpreter.rb +5 -11
- data/lib/graphql/execution/interpreter/runtime.rb +56 -48
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +5 -5
- data/lib/graphql/execution/multiplex.rb +10 -0
- data/lib/graphql/function.rb +1 -1
- data/lib/graphql/input_object_type.rb +3 -2
- data/lib/graphql/interface_type.rb +1 -1
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +6 -6
- data/lib/graphql/introspection/schema_type.rb +1 -6
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/language.rb +1 -1
- data/lib/graphql/language/block_string.rb +2 -2
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +42 -42
- data/lib/graphql/language/lexer.rb +49 -48
- data/lib/graphql/language/lexer.rl +49 -48
- data/lib/graphql/language/nodes.rb +11 -8
- data/lib/graphql/language/parser.rb +4 -1
- data/lib/graphql/language/parser.y +4 -1
- data/lib/graphql/language/token.rb +1 -1
- data/lib/graphql/pagination/array_connection.rb +0 -1
- data/lib/graphql/pagination/connection.rb +31 -10
- data/lib/graphql/pagination/connections.rb +7 -2
- data/lib/graphql/pagination/relation_connection.rb +1 -7
- data/lib/graphql/query.rb +9 -4
- data/lib/graphql/query/arguments.rb +8 -1
- data/lib/graphql/query/literal_input.rb +2 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/relay/base_connection.rb +3 -3
- data/lib/graphql/relay/relation_connection.rb +9 -5
- data/lib/graphql/schema.rb +699 -153
- data/lib/graphql/schema/argument.rb +20 -4
- data/lib/graphql/schema/build_from_definition.rb +64 -31
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/directive.rb +16 -1
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/enum.rb +39 -3
- data/lib/graphql/schema/field.rb +39 -9
- data/lib/graphql/schema/field/connection_extension.rb +4 -4
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +109 -1
- data/lib/graphql/schema/interface.rb +8 -7
- data/lib/graphql/schema/introspection_system.rb +104 -36
- data/lib/graphql/schema/late_bound_type.rb +1 -0
- data/lib/graphql/schema/list.rb +26 -0
- data/lib/graphql/schema/loader.rb +10 -4
- data/lib/graphql/schema/member.rb +3 -0
- data/lib/graphql/schema/member/base_dsl_methods.rb +23 -13
- data/lib/graphql/schema/member/build_type.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +2 -2
- data/lib/graphql/schema/member/has_fields.rb +15 -6
- data/lib/graphql/schema/member/instrumentation.rb +6 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +1 -1
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/non_null.rb +25 -0
- data/lib/graphql/schema/object.rb +14 -1
- data/lib/graphql/schema/printer.rb +4 -3
- data/lib/graphql/schema/relay_classic_mutation.rb +5 -1
- data/lib/graphql/schema/resolver.rb +20 -2
- data/lib/graphql/schema/scalar.rb +18 -3
- data/lib/graphql/schema/subscription.rb +1 -1
- data/lib/graphql/schema/timeout_middleware.rb +3 -2
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +22 -24
- data/lib/graphql/schema/validation.rb +17 -1
- data/lib/graphql/schema/warden.rb +46 -17
- data/lib/graphql/schema/wrapper.rb +1 -1
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/definition_dependencies.rb +21 -12
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validator.rb +1 -1
- data/lib/graphql/subscriptions.rb +38 -13
- data/lib/graphql/subscriptions/event.rb +24 -7
- data/lib/graphql/subscriptions/instrumentation.rb +1 -1
- data/lib/graphql/subscriptions/subscription_root.rb +0 -1
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +10 -10
- data/lib/graphql/tracing/platform_tracing.rb +1 -2
- data/lib/graphql/tracing/skylight_tracing.rb +1 -0
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/upgrader/member.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- metadata +5 -2
@@ -116,7 +116,9 @@ module GraphQL
|
|
116
116
|
elsif as_type.kind.input_object?
|
117
117
|
as_type.arguments.each do |_name, input_obj_arg|
|
118
118
|
input_obj_arg = input_obj_arg.type_class
|
119
|
-
|
119
|
+
# TODO: this skips input objects whose values were alread replaced with application objects.
|
120
|
+
# See: https://github.com/rmosolgo/graphql-ruby/issues/2633
|
121
|
+
if value.respond_to?(:key?) && value.key?(input_obj_arg.keyword) && !input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
120
122
|
return false
|
121
123
|
end
|
122
124
|
end
|
@@ -141,6 +143,8 @@ module GraphQL
|
|
141
143
|
argument
|
142
144
|
end
|
143
145
|
|
146
|
+
attr_writer :type
|
147
|
+
|
144
148
|
def type
|
145
149
|
@type ||= Member::BuildType.parse_type(@type_expr, null: @null)
|
146
150
|
rescue StandardError => err
|
@@ -150,13 +154,25 @@ module GraphQL
|
|
150
154
|
# Apply the {prepare} configuration to `value`, using methods from `obj`.
|
151
155
|
# Used by the runtime.
|
152
156
|
# @api private
|
153
|
-
def prepare_value(obj, value)
|
157
|
+
def prepare_value(obj, value, context: nil)
|
158
|
+
if value.is_a?(GraphQL::Schema::InputObject)
|
159
|
+
value = value.prepare
|
160
|
+
end
|
161
|
+
|
154
162
|
if @prepare.nil?
|
155
163
|
value
|
156
164
|
elsif @prepare.is_a?(String) || @prepare.is_a?(Symbol)
|
157
|
-
obj.
|
165
|
+
if obj.nil?
|
166
|
+
# The problem here is, we _used to_ prepare while building variables.
|
167
|
+
# But now we don't have the runtime object there.
|
168
|
+
#
|
169
|
+
# This will have to be called later, when the runtime object _is_ available.
|
170
|
+
value
|
171
|
+
else
|
172
|
+
obj.public_send(@prepare, value)
|
173
|
+
end
|
158
174
|
elsif @prepare.respond_to?(:call)
|
159
|
-
@prepare.call(value, obj.context)
|
175
|
+
@prepare.call(value, context || obj.context)
|
160
176
|
else
|
161
177
|
raise "Invalid prepare for #{@owner.name}.name: #{@prepare.inspect}"
|
162
178
|
end
|
@@ -5,9 +5,9 @@ module GraphQL
|
|
5
5
|
class Schema
|
6
6
|
module BuildFromDefinition
|
7
7
|
class << self
|
8
|
-
def from_definition(definition_string, default_resolve:, parser: DefaultParser)
|
8
|
+
def from_definition(definition_string, default_resolve:, using: {}, interpreter: true, parser: DefaultParser)
|
9
9
|
document = parser.parse(definition_string)
|
10
|
-
Builder.build(document, default_resolve: default_resolve)
|
10
|
+
Builder.build(document, default_resolve: default_resolve, using: using, interpreter: interpreter)
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
@@ -29,34 +29,39 @@ module GraphQL
|
|
29
29
|
module Builder
|
30
30
|
extend self
|
31
31
|
|
32
|
-
def build(document, default_resolve: DefaultResolve)
|
32
|
+
def build(document, default_resolve: DefaultResolve, using: {}, interpreter: true)
|
33
33
|
raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
|
34
34
|
|
35
35
|
if default_resolve.is_a?(Hash)
|
36
36
|
default_resolve = ResolveMap.new(default_resolve)
|
37
37
|
end
|
38
38
|
|
39
|
-
|
39
|
+
schema_defns = document.definitions.select { |d| d.is_a?(GraphQL::Language::Nodes::SchemaDefinition) }
|
40
|
+
if schema_defns.size > 1
|
41
|
+
raise InvalidDocumentError.new('Must provide only one schema definition.')
|
42
|
+
end
|
43
|
+
schema_definition = schema_defns.first
|
40
44
|
types = {}
|
41
45
|
types.merge!(GraphQL::Schema::BUILT_IN_TYPES)
|
42
46
|
directives = {}
|
43
|
-
type_resolver = ->(type) {
|
47
|
+
type_resolver = ->(type) { resolve_type(types, type) }
|
44
48
|
|
45
49
|
document.definitions.each do |definition|
|
46
50
|
case definition
|
47
51
|
when GraphQL::Language::Nodes::SchemaDefinition
|
48
|
-
|
49
|
-
schema_definition = definition
|
52
|
+
nil # already handled
|
50
53
|
when GraphQL::Language::Nodes::EnumTypeDefinition
|
51
|
-
types[definition.name] = build_enum_type(definition, type_resolver)
|
54
|
+
types[definition.name] = build_enum_type(definition, type_resolver)
|
52
55
|
when GraphQL::Language::Nodes::ObjectTypeDefinition
|
53
|
-
|
56
|
+
is_subscription_root = (definition.name == "Subscription" && (schema_definition.nil? || schema_definition.subscription.nil?)) || (schema_definition && (definition.name == schema_definition.subscription))
|
57
|
+
should_extend_subscription_root = is_subscription_root && interpreter
|
58
|
+
types[definition.name] = build_object_type(definition, type_resolver, default_resolve: default_resolve, extend_subscription_root: should_extend_subscription_root)
|
54
59
|
when GraphQL::Language::Nodes::InterfaceTypeDefinition
|
55
|
-
types[definition.name] = build_interface_type(definition, type_resolver)
|
60
|
+
types[definition.name] = build_interface_type(definition, type_resolver)
|
56
61
|
when GraphQL::Language::Nodes::UnionTypeDefinition
|
57
|
-
types[definition.name] = build_union_type(definition, type_resolver)
|
62
|
+
types[definition.name] = build_union_type(definition, type_resolver)
|
58
63
|
when GraphQL::Language::Nodes::ScalarTypeDefinition
|
59
|
-
types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
|
64
|
+
types[definition.name] = build_scalar_type(definition, type_resolver, default_resolve: default_resolve)
|
60
65
|
when GraphQL::Language::Nodes::InputObjectTypeDefinition
|
61
66
|
types[definition.name] = build_input_object_type(definition, type_resolver)
|
62
67
|
when GraphQL::Language::Nodes::DirectiveDefinition
|
@@ -90,10 +95,18 @@ module GraphQL
|
|
90
95
|
raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
|
91
96
|
|
92
97
|
Class.new(GraphQL::Schema) do
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
98
|
+
begin
|
99
|
+
# Add these first so that there's some chance of resolving late-bound types
|
100
|
+
orphan_types types.values
|
101
|
+
query query_root_type
|
102
|
+
mutation mutation_root_type
|
103
|
+
subscription subscription_root_type
|
104
|
+
rescue Schema::UnresolvedLateBoundTypeError => err
|
105
|
+
type_name = err.type.name
|
106
|
+
err_backtrace = err.backtrace
|
107
|
+
raise InvalidDocumentError, "Type \"#{type_name}\" not found in document.", err_backtrace
|
108
|
+
end
|
109
|
+
|
97
110
|
if default_resolve.respond_to?(:resolve_type)
|
98
111
|
define_singleton_method(:resolve_type) do |*args|
|
99
112
|
default_resolve.resolve_type(*args)
|
@@ -110,13 +123,19 @@ module GraphQL
|
|
110
123
|
ast_node(schema_definition)
|
111
124
|
end
|
112
125
|
|
113
|
-
|
114
|
-
|
126
|
+
if interpreter
|
127
|
+
use GraphQL::Execution::Interpreter
|
128
|
+
use GraphQL::Analysis::AST
|
129
|
+
end
|
130
|
+
|
131
|
+
using.each do |plugin, options|
|
132
|
+
use(plugin, options)
|
133
|
+
end
|
115
134
|
end
|
116
135
|
end
|
117
136
|
|
118
137
|
NullResolveType = ->(type, obj, ctx) {
|
119
|
-
raise(
|
138
|
+
raise(GraphQL::RequiredImplementationMissingError, "Generated Schema cannot use Interface or Union types for execution. Implement resolve_type on your resolver.")
|
120
139
|
}
|
121
140
|
|
122
141
|
def build_enum_type(enum_type_definition, type_resolver)
|
@@ -141,7 +160,7 @@ module GraphQL
|
|
141
160
|
return unless deprecated_directive
|
142
161
|
|
143
162
|
reason = deprecated_directive.arguments.find{ |a| a.name == 'reason' }
|
144
|
-
return GraphQL::Directive::DEFAULT_DEPRECATION_REASON unless reason
|
163
|
+
return GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON unless reason
|
145
164
|
|
146
165
|
reason.value
|
147
166
|
end
|
@@ -173,15 +192,19 @@ module GraphQL
|
|
173
192
|
end
|
174
193
|
end
|
175
194
|
|
176
|
-
def build_object_type(object_type_definition, type_resolver, default_resolve:)
|
195
|
+
def build_object_type(object_type_definition, type_resolver, default_resolve:, extend_subscription_root:)
|
177
196
|
builder = self
|
178
197
|
type_def = nil
|
179
|
-
typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def
|
198
|
+
typed_resolve_fn = ->(field, obj, args, ctx) { default_resolve.call(type_def, field, obj, args, ctx) }
|
180
199
|
Class.new(GraphQL::Schema::Object) do
|
181
200
|
type_def = self
|
182
201
|
graphql_name(object_type_definition.name)
|
183
202
|
description(object_type_definition.description)
|
184
203
|
ast_node(object_type_definition)
|
204
|
+
if extend_subscription_root
|
205
|
+
# This has to come before `field ...` configurations since it modifies them
|
206
|
+
extend Subscriptions::SubscriptionRoot
|
207
|
+
end
|
185
208
|
|
186
209
|
object_type_definition.interfaces.each do |interface_name|
|
187
210
|
interface_defn = type_resolver.call(interface_name)
|
@@ -268,32 +291,42 @@ module GraphQL
|
|
268
291
|
field_definitions.map do |field_definition|
|
269
292
|
type_name = resolve_type_name(field_definition.type)
|
270
293
|
|
271
|
-
|
294
|
+
owner.field(
|
272
295
|
field_definition.name,
|
273
296
|
description: field_definition.description,
|
274
297
|
type: type_resolver.call(field_definition.type),
|
275
298
|
null: true,
|
276
299
|
connection: type_name.end_with?("Connection"),
|
277
|
-
resolve: ->(obj, args, ctx) { default_resolve.call(field.graphql_definition, obj, args, ctx) },
|
278
300
|
deprecation_reason: build_deprecation_reason(field_definition.directives),
|
279
301
|
ast_node: field_definition,
|
280
302
|
method_conflict_warning: false,
|
281
303
|
camelize: false,
|
282
304
|
) do
|
283
305
|
builder.build_arguments(self, field_definition.arguments, type_resolver)
|
306
|
+
|
307
|
+
# Don't do this for interfaces
|
308
|
+
if default_resolve
|
309
|
+
# TODO fragile hack. formalize this API?
|
310
|
+
define_singleton_method :resolve_field_method do |obj, args, ctx|
|
311
|
+
default_resolve.call(self, obj.object, args, ctx)
|
312
|
+
end
|
313
|
+
end
|
284
314
|
end
|
285
315
|
end
|
286
316
|
end
|
287
317
|
|
288
318
|
def resolve_type(types, ast_node)
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
319
|
+
case ast_node
|
320
|
+
when GraphQL::Language::Nodes::TypeName
|
321
|
+
type_name = ast_node.name
|
322
|
+
types[type_name] ||= GraphQL::Schema::LateBoundType.new(type_name)
|
323
|
+
when GraphQL::Language::Nodes::NonNullType
|
324
|
+
resolve_type(types, ast_node.of_type).to_non_null_type
|
325
|
+
when GraphQL::Language::Nodes::ListType
|
326
|
+
resolve_type(types, ast_node.of_type).to_list_type
|
327
|
+
else
|
328
|
+
raise "Unexpected ast_node: #{ast_node.inspect}"
|
295
329
|
end
|
296
|
-
type
|
297
330
|
end
|
298
331
|
|
299
332
|
def resolve_type_name(type)
|
@@ -2,11 +2,11 @@
|
|
2
2
|
module GraphQL
|
3
3
|
class Schema
|
4
4
|
BUILT_IN_TYPES = {
|
5
|
-
"Int" =>
|
6
|
-
"String" =>
|
7
|
-
"Float" =>
|
8
|
-
"Boolean" =>
|
9
|
-
"ID" =>
|
5
|
+
"Int" => GraphQL::Types::Int,
|
6
|
+
"String" => GraphQL::Types::String,
|
7
|
+
"Float" => GraphQL::Types::Float,
|
8
|
+
"Boolean" => GraphQL::Types::Boolean,
|
9
|
+
"ID" => GraphQL::Types::ID,
|
10
10
|
}
|
11
11
|
end
|
12
12
|
end
|
@@ -27,7 +27,7 @@ module GraphQL
|
|
27
27
|
elsif @default_directive.nil?
|
28
28
|
@default_directive = (superclass.respond_to?(:default_directive) ? superclass.default_directive : false)
|
29
29
|
else
|
30
|
-
|
30
|
+
!!@default_directive
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -47,6 +47,9 @@ module GraphQL
|
|
47
47
|
arg_graphql = arg_defn.to_graphql
|
48
48
|
defn.arguments[arg_graphql.name] = arg_graphql
|
49
49
|
end
|
50
|
+
# Make a reference to a classic-style Arguments class
|
51
|
+
defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(defn)
|
52
|
+
|
50
53
|
defn
|
51
54
|
end
|
52
55
|
|
@@ -59,6 +62,18 @@ module GraphQL
|
|
59
62
|
def resolve(object, arguments, context)
|
60
63
|
yield
|
61
64
|
end
|
65
|
+
|
66
|
+
def on_field?
|
67
|
+
locations.include?(FIELD)
|
68
|
+
end
|
69
|
+
|
70
|
+
def on_fragment?
|
71
|
+
locations.include?(FRAGMENT_SPREAD) && locations.include?(INLINE_FRAGMENT)
|
72
|
+
end
|
73
|
+
|
74
|
+
def on_operation?
|
75
|
+
locations.include?(QUERY) && locations.include?(MUTATION) && locations.include?(SUBSCRIPTION)
|
76
|
+
end
|
62
77
|
end
|
63
78
|
|
64
79
|
LOCATIONS = [
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module GraphQL
|
3
|
+
class Schema
|
4
|
+
class Directive < GraphQL::Schema::Member
|
5
|
+
class Deprecated < GraphQL::Schema::Directive
|
6
|
+
description "Marks an element of a GraphQL schema as no longer supported."
|
7
|
+
locations(GraphQL::Schema::Directive::FIELD_DEFINITION, GraphQL::Schema::Directive::ENUM_VALUE)
|
8
|
+
|
9
|
+
reason_description = "Explains why this element was deprecated, usually also including a "\
|
10
|
+
"suggestion for how to access supported similar data. Formatted "\
|
11
|
+
"in [Markdown](https://daringfireball.net/projects/markdown/)."
|
12
|
+
|
13
|
+
argument :reason, String, reason_description, default_value: Directive::DEFAULT_DEPRECATION_REASON, required: false
|
14
|
+
default_directive true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -58,7 +58,7 @@ module GraphQL
|
|
58
58
|
# @param context [GraphQL::Query::Context]
|
59
59
|
# @return [Boolean] If truthy, execution will continue
|
60
60
|
def self.enabled?(flag_name, object, context)
|
61
|
-
raise
|
61
|
+
raise GraphQL::RequiredImplementationMissingError, "Implement `.enabled?(flag_name, object, context)` to return true or false for the feature flag (#{flag_name.inspect})"
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
data/lib/graphql/schema/enum.rb
CHANGED
@@ -21,11 +21,9 @@ module GraphQL
|
|
21
21
|
class Schema
|
22
22
|
class Enum < GraphQL::Schema::Member
|
23
23
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
24
|
+
extend GraphQL::Schema::Member::ValidatesInput
|
24
25
|
|
25
26
|
class << self
|
26
|
-
extend Forwardable
|
27
|
-
def_delegators :graphql_definition, :coerce_isolated_input, :coerce_isolated_result, :coerce_input, :coerce_result
|
28
|
-
|
29
27
|
# Define a value for this enum
|
30
28
|
# @param graphql_name [String, Symbol] the GraphQL value for this, usually `SCREAMING_CASE`
|
31
29
|
# @param description [String], the GraphQL description for this value, present in documentation
|
@@ -73,6 +71,44 @@ module GraphQL
|
|
73
71
|
GraphQL::TypeKinds::ENUM
|
74
72
|
end
|
75
73
|
|
74
|
+
def validate_non_null_input(value_name, ctx)
|
75
|
+
result = GraphQL::Query::InputValidationResult.new
|
76
|
+
|
77
|
+
allowed_values = ctx.warden.enum_values(self)
|
78
|
+
matching_value = allowed_values.find { |v| v.graphql_name == value_name }
|
79
|
+
|
80
|
+
if matching_value.nil?
|
81
|
+
result.add_problem("Expected #{GraphQL::Language.serialize(value_name)} to be one of: #{allowed_values.map(&:graphql_name).join(', ')}")
|
82
|
+
end
|
83
|
+
|
84
|
+
result
|
85
|
+
end
|
86
|
+
|
87
|
+
def coerce_result(value, ctx)
|
88
|
+
warden = ctx.warden
|
89
|
+
all_values = warden ? warden.enum_values(self) : values.each_value
|
90
|
+
enum_value = all_values.find { |val| val.value == value }
|
91
|
+
if enum_value
|
92
|
+
enum_value.graphql_name
|
93
|
+
else
|
94
|
+
raise(GraphQL::EnumType::UnresolvedValueError, "Can't resolve enum #{graphql_name} for #{value.inspect}")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def coerce_input(value_name, ctx)
|
99
|
+
all_values = ctx.warden ? ctx.warden.enum_values(self) : values.each_value
|
100
|
+
|
101
|
+
if v = all_values.find { |val| val.graphql_name == value_name }
|
102
|
+
v.value
|
103
|
+
elsif v = all_values.find { |val| val.value == value_name }
|
104
|
+
# this is for matching default values, which are "inputs", but they're
|
105
|
+
# the Ruby value, not the GraphQL string.
|
106
|
+
v.value
|
107
|
+
else
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
76
112
|
private
|
77
113
|
|
78
114
|
def own_values
|
data/lib/graphql/schema/field.rb
CHANGED
@@ -15,6 +15,7 @@ module GraphQL
|
|
15
15
|
include GraphQL::Schema::Member::HasArguments
|
16
16
|
include GraphQL::Schema::Member::HasAstNode
|
17
17
|
include GraphQL::Schema::Member::HasPath
|
18
|
+
extend GraphQL::Schema::FindInheritedValue
|
18
19
|
|
19
20
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
20
21
|
attr_reader :name
|
@@ -35,7 +36,7 @@ module GraphQL
|
|
35
36
|
attr_reader :resolver_method
|
36
37
|
|
37
38
|
# @return [Class] The type that this field belongs to
|
38
|
-
|
39
|
+
attr_accessor :owner
|
39
40
|
|
40
41
|
# @return [Symbol] the original name of the field, passed in by the user
|
41
42
|
attr_reader :original_name
|
@@ -51,7 +52,7 @@ module GraphQL
|
|
51
52
|
attr_reader :trace
|
52
53
|
|
53
54
|
# @return [String, nil]
|
54
|
-
|
55
|
+
attr_accessor :subscription_scope
|
55
56
|
|
56
57
|
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
57
58
|
#
|
@@ -136,6 +137,25 @@ module GraphQL
|
|
136
137
|
end
|
137
138
|
end
|
138
139
|
|
140
|
+
# This extension is applied to fields when {#connection?} is true.
|
141
|
+
#
|
142
|
+
# You can override it in your base field definition.
|
143
|
+
# @return [Class] A {FieldExtension} subclass for implementing pagination behavior.
|
144
|
+
# @example Configuring a custom extension
|
145
|
+
# class Types::BaseField < GraphQL::Schema::Field
|
146
|
+
# connection_extension(MyCustomExtension)
|
147
|
+
# end
|
148
|
+
def self.connection_extension(new_extension_class = nil)
|
149
|
+
if new_extension_class
|
150
|
+
@connection_extension = new_extension_class
|
151
|
+
else
|
152
|
+
@connection_extension ||= find_inherited_value(:connection_extension, ConnectionExtension)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# @return Boolean
|
157
|
+
attr_reader :relay_node_field
|
158
|
+
|
139
159
|
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
140
160
|
def method_conflict_warning?
|
141
161
|
@method_conflict_warning
|
@@ -256,7 +276,7 @@ module GraphQL
|
|
256
276
|
# The problem with putting this after the definition_block
|
257
277
|
# is that it would override arguments
|
258
278
|
if connection?
|
259
|
-
self.extension(
|
279
|
+
self.extension(self.class.connection_extension)
|
260
280
|
end
|
261
281
|
|
262
282
|
if definition_block
|
@@ -340,7 +360,7 @@ module GraphQL
|
|
340
360
|
end
|
341
361
|
end
|
342
362
|
|
343
|
-
def complexity(new_complexity)
|
363
|
+
def complexity(new_complexity = nil)
|
344
364
|
case new_complexity
|
345
365
|
when Proc
|
346
366
|
if new_complexity.parameters.size != 3
|
@@ -353,6 +373,8 @@ module GraphQL
|
|
353
373
|
end
|
354
374
|
when Numeric
|
355
375
|
@complexity = new_complexity
|
376
|
+
when nil
|
377
|
+
@complexity
|
356
378
|
else
|
357
379
|
raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
|
358
380
|
end
|
@@ -427,12 +449,20 @@ module GraphQL
|
|
427
449
|
|
428
450
|
# Ok, `self` isn't a class, but this is for consistency with the classes
|
429
451
|
field_defn.metadata[:type_class] = self
|
430
|
-
|
452
|
+
field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
|
431
453
|
field_defn
|
432
454
|
end
|
433
455
|
|
456
|
+
attr_writer :type
|
457
|
+
|
434
458
|
def type
|
435
|
-
@type ||=
|
459
|
+
@type ||= if @function
|
460
|
+
Member::BuildType.parse_type(@function.type, null: false)
|
461
|
+
elsif @field
|
462
|
+
Member::BuildType.parse_type(@field.type, null: false)
|
463
|
+
else
|
464
|
+
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
465
|
+
end
|
436
466
|
rescue GraphQL::Schema::InvalidDocumentError => err
|
437
467
|
# Let this propagate up
|
438
468
|
raise err
|
@@ -523,7 +553,7 @@ module GraphQL
|
|
523
553
|
else
|
524
554
|
extended_obj
|
525
555
|
end
|
526
|
-
@resolver_class.new(object: resolver_obj, context: ctx)
|
556
|
+
@resolver_class.new(object: resolver_obj, context: ctx, field: self)
|
527
557
|
else
|
528
558
|
extended_obj
|
529
559
|
end
|
@@ -597,7 +627,7 @@ module GraphQL
|
|
597
627
|
elsif ctx.respond_to?(extra_name)
|
598
628
|
ctx.public_send(extra_name)
|
599
629
|
else
|
600
|
-
raise
|
630
|
+
raise GraphQL::RequiredImplementationMissingError, "Unknown field extra for #{self.path}: #{extra_name.inspect}"
|
601
631
|
end
|
602
632
|
end
|
603
633
|
|
@@ -640,7 +670,7 @@ module GraphQL
|
|
640
670
|
if extended_obj.is_a?(GraphQL::Schema::Object)
|
641
671
|
extended_obj = extended_obj.object
|
642
672
|
end
|
643
|
-
extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx)
|
673
|
+
extended_obj = @resolver_class.new(object: extended_obj, context: query_ctx, field: self)
|
644
674
|
end
|
645
675
|
|
646
676
|
if extended_obj.respond_to?(@resolver_method)
|