graphql 1.12.10 → 1.13.4
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +3 -1
- data/lib/generators/graphql/install_generator.rb +9 -2
- data/lib/generators/graphql/mutation_generator.rb +1 -1
- data/lib/generators/graphql/object_generator.rb +2 -1
- data/lib/generators/graphql/relay.rb +19 -11
- data/lib/generators/graphql/templates/schema.erb +14 -2
- data/lib/generators/graphql/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +28 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +10 -14
- data/lib/graphql/analysis/ast/visitor.rb +4 -4
- data/lib/graphql/backtrace/table.rb +15 -3
- data/lib/graphql/backtrace/tracer.rb +7 -4
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader/null_dataloader.rb +1 -0
- data/lib/graphql/dataloader/source.rb +50 -2
- data/lib/graphql/dataloader.rb +110 -41
- data/lib/graphql/define/instance_definable.rb +1 -1
- data/lib/graphql/deprecated_dsl.rb +11 -3
- data/lib/graphql/deprecation.rb +1 -5
- data/lib/graphql/directive/deprecated_directive.rb +1 -1
- data/lib/graphql/directive/include_directive.rb +1 -1
- data/lib/graphql/directive/skip_directive.rb +1 -1
- data/lib/graphql/directive.rb +0 -4
- data/lib/graphql/enum_type.rb +5 -1
- data/lib/graphql/execution/errors.rb +1 -0
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -4
- data/lib/graphql/execution/interpreter/resolve.rb +6 -2
- data/lib/graphql/execution/interpreter/runtime.rb +513 -213
- data/lib/graphql/execution/interpreter.rb +4 -8
- data/lib/graphql/execution/lazy.rb +5 -1
- data/lib/graphql/execution/lookahead.rb +2 -2
- data/lib/graphql/execution/multiplex.rb +4 -1
- data/lib/graphql/float_type.rb +1 -1
- data/lib/graphql/id_type.rb +1 -1
- data/lib/graphql/int_type.rb +1 -1
- data/lib/graphql/integer_encoding_error.rb +18 -2
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +2 -2
- data/lib/graphql/introspection/enum_value_type.rb +2 -2
- data/lib/graphql/introspection/field_type.rb +2 -2
- data/lib/graphql/introspection/input_value_type.rb +10 -4
- data/lib/graphql/introspection/schema_type.rb +3 -3
- data/lib/graphql/introspection/type_type.rb +10 -10
- data/lib/graphql/language/block_string.rb +2 -6
- data/lib/graphql/language/document_from_schema_definition.rb +10 -4
- data/lib/graphql/language/lexer.rb +0 -3
- data/lib/graphql/language/lexer.rl +0 -4
- data/lib/graphql/language/nodes.rb +13 -3
- data/lib/graphql/language/parser.rb +442 -434
- data/lib/graphql/language/parser.y +5 -4
- data/lib/graphql/language/printer.rb +6 -1
- data/lib/graphql/language/sanitized_printer.rb +5 -5
- data/lib/graphql/language/token.rb +0 -4
- data/lib/graphql/name_validator.rb +0 -4
- data/lib/graphql/pagination/active_record_relation_connection.rb +43 -6
- data/lib/graphql/pagination/connections.rb +40 -16
- data/lib/graphql/pagination/relation_connection.rb +57 -27
- data/lib/graphql/query/arguments.rb +1 -1
- data/lib/graphql/query/arguments_cache.rb +1 -1
- data/lib/graphql/query/context.rb +15 -2
- data/lib/graphql/query/literal_input.rb +1 -1
- data/lib/graphql/query/null_context.rb +12 -7
- data/lib/graphql/query/serial_execution/field_resolution.rb +1 -1
- data/lib/graphql/query/validation_pipeline.rb +1 -1
- data/lib/graphql/query/variables.rb +5 -1
- data/lib/graphql/query.rb +5 -1
- data/lib/graphql/relay/edges_instrumentation.rb +0 -1
- data/lib/graphql/relay/global_id_resolve.rb +1 -1
- data/lib/graphql/relay/page_info.rb +1 -1
- data/lib/graphql/rubocop/graphql/base_cop.rb +36 -0
- data/lib/graphql/rubocop/graphql/default_null_true.rb +43 -0
- data/lib/graphql/rubocop/graphql/default_required_true.rb +43 -0
- data/lib/graphql/rubocop.rb +4 -0
- data/lib/graphql/schema/addition.rb +247 -0
- data/lib/graphql/schema/argument.rb +103 -45
- data/lib/graphql/schema/build_from_definition.rb +13 -7
- data/lib/graphql/schema/directive/feature.rb +1 -1
- data/lib/graphql/schema/directive/flagged.rb +2 -2
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive/transform.rb +14 -2
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +70 -11
- data/lib/graphql/schema/enum_value.rb +6 -0
- data/lib/graphql/schema/field/connection_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +243 -81
- data/lib/graphql/schema/field_extension.rb +89 -2
- data/lib/graphql/schema/find_inherited_value.rb +1 -0
- data/lib/graphql/schema/finder.rb +5 -5
- data/lib/graphql/schema/input_object.rb +39 -29
- data/lib/graphql/schema/interface.rb +11 -20
- data/lib/graphql/schema/introspection_system.rb +1 -1
- data/lib/graphql/schema/list.rb +3 -1
- data/lib/graphql/schema/member/accepts_definition.rb +15 -3
- data/lib/graphql/schema/member/build_type.rb +1 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +145 -57
- data/lib/graphql/schema/member/has_deprecation_reason.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +76 -18
- data/lib/graphql/schema/member/has_interfaces.rb +90 -0
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/non_null.rb +7 -1
- data/lib/graphql/schema/object.rb +10 -75
- data/lib/graphql/schema/printer.rb +12 -17
- data/lib/graphql/schema/relay_classic_mutation.rb +37 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +27 -2
- data/lib/graphql/schema/resolver.rb +75 -65
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +36 -8
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +1 -1
- data/lib/graphql/schema/type_membership.rb +18 -4
- data/lib/graphql/schema/union.rb +8 -1
- data/lib/graphql/schema/validator/allow_blank_validator.rb +29 -0
- data/lib/graphql/schema/validator/allow_null_validator.rb +26 -0
- data/lib/graphql/schema/validator/exclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/format_validator.rb +4 -5
- data/lib/graphql/schema/validator/inclusion_validator.rb +3 -1
- data/lib/graphql/schema/validator/length_validator.rb +5 -3
- data/lib/graphql/schema/validator/numericality_validator.rb +13 -2
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +33 -25
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +162 -227
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +8 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -1
- data/lib/graphql/static_validation/error.rb +3 -1
- data/lib/graphql/static_validation/literal_validator.rb +1 -1
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +52 -26
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +25 -4
- data/lib/graphql/static_validation/rules/fragments_are_finite.rb +2 -2
- data/lib/graphql/static_validation/rules/query_root_exists.rb +17 -0
- data/lib/graphql/static_validation/rules/query_root_exists_error.rb +26 -0
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +3 -1
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +4 -4
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +13 -7
- data/lib/graphql/static_validation/validation_context.rb +8 -2
- data/lib/graphql/static_validation/validator.rb +15 -12
- data/lib/graphql/string_encoding_error.rb +13 -3
- data/lib/graphql/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +36 -6
- data/lib/graphql/subscriptions/event.rb +68 -31
- data/lib/graphql/subscriptions/serialize.rb +23 -3
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/appsignal_tracing.rb +15 -0
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- data/lib/graphql/types/big_int.rb +5 -1
- data/lib/graphql/types/int.rb +1 -1
- data/lib/graphql/types/relay/connection_behaviors.rb +26 -9
- data/lib/graphql/types/relay/default_relay.rb +5 -1
- data/lib/graphql/types/relay/edge_behaviors.rb +13 -2
- data/lib/graphql/types/relay/has_node_field.rb +2 -2
- data/lib/graphql/types/relay/has_nodes_field.rb +2 -2
- data/lib/graphql/types/relay/node_field.rb +15 -4
- data/lib/graphql/types/relay/nodes_field.rb +14 -4
- data/lib/graphql/types/string.rb +1 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -28
- data/readme.md +1 -4
- metadata +17 -21
- data/lib/graphql/execution/interpreter/hash_response.rb +0 -46
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module GraphQL
|
|
4
4
|
class Schema
|
|
5
5
|
class Member
|
|
6
|
-
# Shared code for
|
|
6
|
+
# Shared code for Objects, Interfaces, Mutations, Subscriptions
|
|
7
7
|
module HasFields
|
|
8
8
|
# Add a field to this object or interface with the given definition
|
|
9
9
|
# @see {GraphQL::Schema::Field#initialize} for method signature
|
|
@@ -15,28 +15,39 @@ module GraphQL
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
# @return [Hash<String => GraphQL::Schema::Field>] Fields on this object, keyed by name, including inherited fields
|
|
18
|
-
def fields
|
|
18
|
+
def fields(context = GraphQL::Query::NullContext)
|
|
19
|
+
warden = Warden.from_context(context)
|
|
20
|
+
is_object = self.respond_to?(:kind) && self.kind.object?
|
|
19
21
|
# Local overrides take precedence over inherited fields
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
if ancestor.respond_to?(:own_fields)
|
|
23
|
-
|
|
22
|
+
visible_fields = {}
|
|
23
|
+
for ancestor in ancestors
|
|
24
|
+
if ancestor.respond_to?(:own_fields) &&
|
|
25
|
+
(is_object ? visible_interface_implementation?(ancestor, context, warden) : true)
|
|
26
|
+
|
|
27
|
+
ancestor.own_fields.each do |field_name, fields_entry|
|
|
28
|
+
# Choose the most local definition that passes `.visible?` --
|
|
29
|
+
# stop checking for fields by name once one has been found.
|
|
30
|
+
if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
|
|
31
|
+
visible_fields[field_name] = f
|
|
32
|
+
end
|
|
33
|
+
end
|
|
24
34
|
end
|
|
25
35
|
end
|
|
26
|
-
|
|
36
|
+
visible_fields
|
|
27
37
|
end
|
|
28
38
|
|
|
29
|
-
def get_field(field_name)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
39
|
+
def get_field(field_name, context = GraphQL::Query::NullContext)
|
|
40
|
+
warden = Warden.from_context(context)
|
|
41
|
+
is_object = self.respond_to?(:kind) && self.kind.object?
|
|
42
|
+
for ancestor in ancestors
|
|
43
|
+
if ancestor.respond_to?(:own_fields) &&
|
|
44
|
+
(is_object ? visible_interface_implementation?(ancestor, context, warden) : true) &&
|
|
45
|
+
(f_entry = ancestor.own_fields[field_name]) &&
|
|
46
|
+
(f = Warden.visible_entry?(:visible_field?, f_entry, context, warden))
|
|
47
|
+
return f
|
|
37
48
|
end
|
|
38
|
-
nil
|
|
39
49
|
end
|
|
50
|
+
nil
|
|
40
51
|
end
|
|
41
52
|
|
|
42
53
|
# A list of Ruby keywords.
|
|
@@ -64,7 +75,19 @@ module GraphQL
|
|
|
64
75
|
if method_conflict_warning && CONFLICT_FIELD_NAMES.include?(field_defn.resolver_method) && field_defn.original_name == field_defn.resolver_method && field_defn.original_name == field_defn.method_sym
|
|
65
76
|
warn(conflict_field_name_warning(field_defn))
|
|
66
77
|
end
|
|
67
|
-
own_fields[field_defn.name]
|
|
78
|
+
prev_defn = own_fields[field_defn.name]
|
|
79
|
+
|
|
80
|
+
case prev_defn
|
|
81
|
+
when nil
|
|
82
|
+
own_fields[field_defn.name] = field_defn
|
|
83
|
+
when Array
|
|
84
|
+
prev_defn << field_defn
|
|
85
|
+
when GraphQL::Schema::Field
|
|
86
|
+
own_fields[field_defn.name] = [prev_defn, field_defn]
|
|
87
|
+
else
|
|
88
|
+
raise "Invariant: unexpected previous field definition for #{field_defn.name.inspect}: #{prev_defn.inspect}"
|
|
89
|
+
end
|
|
90
|
+
|
|
68
91
|
nil
|
|
69
92
|
end
|
|
70
93
|
|
|
@@ -87,13 +110,48 @@ module GraphQL
|
|
|
87
110
|
end
|
|
88
111
|
end
|
|
89
112
|
|
|
90
|
-
# @return [Array<GraphQL::Schema::Field
|
|
113
|
+
# @return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class _specifically_, not parent classes
|
|
91
114
|
def own_fields
|
|
92
115
|
@own_fields ||= {}
|
|
93
116
|
end
|
|
94
117
|
|
|
118
|
+
def all_field_definitions
|
|
119
|
+
all_fields = {}
|
|
120
|
+
ancestors.reverse_each do |ancestor|
|
|
121
|
+
if ancestor.respond_to?(:own_fields)
|
|
122
|
+
all_fields.merge!(ancestor.own_fields)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
all_fields = all_fields.values
|
|
126
|
+
all_fields.flatten!
|
|
127
|
+
all_fields
|
|
128
|
+
end
|
|
129
|
+
|
|
95
130
|
private
|
|
96
131
|
|
|
132
|
+
# If `type` is an interface, and `self` has a type membership for `type`, then make sure it's visible.
|
|
133
|
+
def visible_interface_implementation?(type, context, warden)
|
|
134
|
+
if type.respond_to?(:kind) && type.kind.interface?
|
|
135
|
+
implements_this_interface = false
|
|
136
|
+
implementation_is_visible = false
|
|
137
|
+
interface_type_memberships.each do |tm|
|
|
138
|
+
if tm.abstract_type == type
|
|
139
|
+
implements_this_interface ||= true
|
|
140
|
+
if warden.visible_type_membership?(tm, context)
|
|
141
|
+
implementation_is_visible = true
|
|
142
|
+
break
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
# It's possible this interface came by way of `include` in another interface which this
|
|
147
|
+
# object type _does_ implement, and that's ok
|
|
148
|
+
implements_this_interface ? implementation_is_visible : true
|
|
149
|
+
else
|
|
150
|
+
# If there's no implementation, then we're looking at Ruby-style inheritance instead
|
|
151
|
+
true
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
97
155
|
# @param [GraphQL::Schema::Field]
|
|
98
156
|
# @return [String] A warning to give when this field definition might conflict with a built-in method
|
|
99
157
|
def conflict_field_name_warning(field_defn)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
class Schema
|
|
5
|
+
class Member
|
|
6
|
+
module HasInterfaces
|
|
7
|
+
def implements(*new_interfaces, **options)
|
|
8
|
+
new_memberships = []
|
|
9
|
+
new_interfaces.each do |int|
|
|
10
|
+
if int.is_a?(Module)
|
|
11
|
+
unless int.include?(GraphQL::Schema::Interface)
|
|
12
|
+
raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
new_memberships << int.type_membership_class.new(int, self, **options)
|
|
16
|
+
|
|
17
|
+
# Include the methods here,
|
|
18
|
+
# `.fields` will use the inheritance chain
|
|
19
|
+
# to find inherited fields
|
|
20
|
+
include(int)
|
|
21
|
+
|
|
22
|
+
# If this interface has interfaces of its own, add those, too
|
|
23
|
+
int.interfaces.each do |next_interface|
|
|
24
|
+
implements(next_interface)
|
|
25
|
+
end
|
|
26
|
+
elsif int.is_a?(GraphQL::InterfaceType)
|
|
27
|
+
new_memberships << int.type_membership_class.new(int, self, **options)
|
|
28
|
+
elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
|
|
29
|
+
if options.any?
|
|
30
|
+
raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
|
|
31
|
+
end
|
|
32
|
+
new_memberships << int
|
|
33
|
+
else
|
|
34
|
+
raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Remove any String or late-bound interfaces which are being replaced
|
|
39
|
+
own_interface_type_memberships.reject! { |old_i_m|
|
|
40
|
+
if !(old_i_m.respond_to?(:abstract_type) && old_i_m.abstract_type.is_a?(Module))
|
|
41
|
+
old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m
|
|
42
|
+
old_name = Schema::Member::BuildType.to_type_name(old_int_type)
|
|
43
|
+
|
|
44
|
+
new_memberships.any? { |new_i_m|
|
|
45
|
+
new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m
|
|
46
|
+
new_name = Schema::Member::BuildType.to_type_name(new_int_type)
|
|
47
|
+
|
|
48
|
+
new_name == old_name
|
|
49
|
+
}
|
|
50
|
+
end
|
|
51
|
+
}
|
|
52
|
+
own_interface_type_memberships.concat(new_memberships)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def own_interface_type_memberships
|
|
56
|
+
@own_interface_type_memberships ||= []
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def interface_type_memberships
|
|
60
|
+
own_interface_type_memberships + ((self.is_a?(Class) && superclass.respond_to?(:interface_type_memberships)) ? superclass.interface_type_memberships : [])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# param context [Query::Context] If omitted, skip filtering.
|
|
64
|
+
def interfaces(context = GraphQL::Query::NullContext)
|
|
65
|
+
warden = Warden.from_context(context)
|
|
66
|
+
visible_interfaces = []
|
|
67
|
+
own_interface_type_memberships.each do |type_membership|
|
|
68
|
+
# During initialization, `type_memberships` can hold late-bound types
|
|
69
|
+
case type_membership
|
|
70
|
+
when String, Schema::LateBoundType
|
|
71
|
+
visible_interfaces << type_membership
|
|
72
|
+
when Schema::TypeMembership
|
|
73
|
+
if warden.visible_type_membership?(type_membership, context)
|
|
74
|
+
visible_interfaces << type_membership.abstract_type
|
|
75
|
+
end
|
|
76
|
+
else
|
|
77
|
+
raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
if self.is_a?(Class) && superclass <= GraphQL::Schema::Object
|
|
82
|
+
visible_interfaces.concat(superclass.interfaces(context))
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
visible_interfaces
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -6,6 +6,7 @@ require 'graphql/schema/member/graphql_type_names'
|
|
|
6
6
|
require 'graphql/schema/member/has_ast_node'
|
|
7
7
|
require 'graphql/schema/member/has_directives'
|
|
8
8
|
require 'graphql/schema/member/has_deprecation_reason'
|
|
9
|
+
require 'graphql/schema/member/has_interfaces'
|
|
9
10
|
require 'graphql/schema/member/has_path'
|
|
10
11
|
require 'graphql/schema/member/has_unresolved_type_error'
|
|
11
12
|
require 'graphql/schema/member/has_validators'
|
|
@@ -8,8 +8,10 @@ module GraphQL
|
|
|
8
8
|
class NonNull < GraphQL::Schema::Wrapper
|
|
9
9
|
include Schema::Member::ValidatesInput
|
|
10
10
|
|
|
11
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
12
|
+
|
|
11
13
|
def to_graphql
|
|
12
|
-
@of_type.graphql_definition.to_non_null_type
|
|
14
|
+
@of_type.graphql_definition(silence_deprecation_warning: true).to_non_null_type
|
|
13
15
|
end
|
|
14
16
|
|
|
15
17
|
# @return [GraphQL::TypeKinds::NON_NULL]
|
|
@@ -51,6 +53,10 @@ module GraphQL
|
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def coerce_input(value, ctx)
|
|
56
|
+
# `.validate_input` above is used for variables, but this method is used for arguments
|
|
57
|
+
if value.nil?
|
|
58
|
+
raise GraphQL::ExecutionError, "`null` is not a valid input for `#{to_type_signature}`, please provide a value for this argument."
|
|
59
|
+
end
|
|
54
60
|
of_type.coerce_input(value, ctx)
|
|
55
61
|
end
|
|
56
62
|
|
|
@@ -7,6 +7,7 @@ module GraphQL
|
|
|
7
7
|
class Object < GraphQL::Schema::Member
|
|
8
8
|
extend GraphQL::Schema::Member::AcceptsDefinition
|
|
9
9
|
extend GraphQL::Schema::Member::HasFields
|
|
10
|
+
extend GraphQL::Schema::Member::HasInterfaces
|
|
10
11
|
|
|
11
12
|
# @return [Object] the application object this type is wrapping
|
|
12
13
|
attr_reader :object
|
|
@@ -103,84 +104,16 @@ module GraphQL
|
|
|
103
104
|
super
|
|
104
105
|
end
|
|
105
106
|
|
|
106
|
-
def implements(*new_interfaces, **options)
|
|
107
|
-
new_memberships = []
|
|
108
|
-
new_interfaces.each do |int|
|
|
109
|
-
if int.is_a?(Module)
|
|
110
|
-
unless int.include?(GraphQL::Schema::Interface)
|
|
111
|
-
raise "#{int} cannot be implemented since it's not a GraphQL Interface. Use `include` for plain Ruby modules."
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
new_memberships << int.type_membership_class.new(int, self, **options)
|
|
115
|
-
|
|
116
|
-
# Include the methods here,
|
|
117
|
-
# `.fields` will use the inheritance chain
|
|
118
|
-
# to find inherited fields
|
|
119
|
-
include(int)
|
|
120
|
-
elsif int.is_a?(GraphQL::InterfaceType)
|
|
121
|
-
new_memberships << int.type_membership_class.new(int, self, **options)
|
|
122
|
-
elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
|
|
123
|
-
if options.any?
|
|
124
|
-
raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
|
|
125
|
-
end
|
|
126
|
-
new_memberships << int
|
|
127
|
-
else
|
|
128
|
-
raise ArgumentError, "Unexpected interface definition (expected module): #{int} (#{int.class})"
|
|
129
|
-
end
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# Remove any interfaces which are being replaced (late-bound types are updated in place this way)
|
|
133
|
-
own_interface_type_memberships.reject! { |old_i_m|
|
|
134
|
-
old_int_type = old_i_m.respond_to?(:abstract_type) ? old_i_m.abstract_type : old_i_m
|
|
135
|
-
old_name = Schema::Member::BuildType.to_type_name(old_int_type)
|
|
136
|
-
|
|
137
|
-
new_memberships.any? { |new_i_m|
|
|
138
|
-
new_int_type = new_i_m.respond_to?(:abstract_type) ? new_i_m.abstract_type : new_i_m
|
|
139
|
-
new_name = Schema::Member::BuildType.to_type_name(new_int_type)
|
|
140
|
-
|
|
141
|
-
new_name == old_name
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
own_interface_type_memberships.concat(new_memberships)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
def own_interface_type_memberships
|
|
148
|
-
@own_interface_type_memberships ||= []
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
def interface_type_memberships
|
|
152
|
-
own_interface_type_memberships + (superclass.respond_to?(:interface_type_memberships) ? superclass.interface_type_memberships : [])
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
# param context [Query::Context] If omitted, skip filtering.
|
|
156
|
-
def interfaces(context = GraphQL::Query::NullContext)
|
|
157
|
-
visible_interfaces = []
|
|
158
|
-
unfiltered = context == GraphQL::Query::NullContext
|
|
159
|
-
own_interface_type_memberships.each do |type_membership|
|
|
160
|
-
# During initialization, `type_memberships` can hold late-bound types
|
|
161
|
-
case type_membership
|
|
162
|
-
when String, Schema::LateBoundType
|
|
163
|
-
visible_interfaces << type_membership
|
|
164
|
-
when Schema::TypeMembership
|
|
165
|
-
if unfiltered || type_membership.visible?(context)
|
|
166
|
-
visible_interfaces << type_membership.abstract_type
|
|
167
|
-
end
|
|
168
|
-
else
|
|
169
|
-
raise "Invariant: Unexpected type_membership #{type_membership.class}: #{type_membership.inspect}"
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
visible_interfaces + (superclass <= GraphQL::Schema::Object ? superclass.interfaces(context) : [])
|
|
173
|
-
end
|
|
174
|
-
|
|
175
107
|
# @return [Hash<String => GraphQL::Schema::Field>] All of this object's fields, indexed by name
|
|
176
108
|
# @see get_field A faster way to find one field by name ({#fields} merges hashes of inherited fields; {#get_field} just looks up one field.)
|
|
177
|
-
def fields
|
|
109
|
+
def fields(context = GraphQL::Query::NullContext)
|
|
178
110
|
all_fields = super
|
|
111
|
+
# This adds fields from legacy-style interfaces only.
|
|
112
|
+
# Multi-fields are not supported here.
|
|
179
113
|
interfaces.each do |int|
|
|
180
|
-
# Include legacy-style interfaces, too
|
|
181
114
|
if int.is_a?(GraphQL::InterfaceType)
|
|
182
115
|
int_f = {}
|
|
183
|
-
int.fields.each do |name, legacy_field|
|
|
116
|
+
int.fields.each do |name, legacy_field| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
|
184
117
|
int_f[name] = field_class.from_options(name, field: legacy_field)
|
|
185
118
|
end
|
|
186
119
|
all_fields = int_f.merge(all_fields)
|
|
@@ -189,6 +122,8 @@ module GraphQL
|
|
|
189
122
|
all_fields
|
|
190
123
|
end
|
|
191
124
|
|
|
125
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
|
126
|
+
|
|
192
127
|
# @return [GraphQL::ObjectType]
|
|
193
128
|
def to_graphql
|
|
194
129
|
obj_type = GraphQL::ObjectType.new
|
|
@@ -198,9 +133,9 @@ module GraphQL
|
|
|
198
133
|
obj_type.introspection = introspection
|
|
199
134
|
obj_type.mutation = mutation
|
|
200
135
|
obj_type.ast_node = ast_node
|
|
201
|
-
fields.each do |field_name, field_inst|
|
|
202
|
-
field_defn = field_inst.to_graphql
|
|
203
|
-
obj_type.fields[field_defn.name] = field_defn
|
|
136
|
+
fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
|
137
|
+
field_defn = field_inst.to_graphql(silence_deprecation_warning: true)
|
|
138
|
+
obj_type.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
|
204
139
|
end
|
|
205
140
|
|
|
206
141
|
obj_type.metadata[:type_class] = self
|
|
@@ -4,37 +4,32 @@ module GraphQL
|
|
|
4
4
|
# Used to convert your {GraphQL::Schema} to a GraphQL schema string
|
|
5
5
|
#
|
|
6
6
|
# @example print your schema to standard output (via helper)
|
|
7
|
-
# MySchema = GraphQL::Schema.define(query: QueryType)
|
|
8
7
|
# puts GraphQL::Schema::Printer.print_schema(MySchema)
|
|
9
8
|
#
|
|
10
9
|
# @example print your schema to standard output
|
|
11
|
-
# MySchema = GraphQL::Schema.define(query: QueryType)
|
|
12
10
|
# puts GraphQL::Schema::Printer.new(MySchema).print_schema
|
|
13
11
|
#
|
|
14
12
|
# @example print a single type to standard output
|
|
15
|
-
#
|
|
16
|
-
# name "Query"
|
|
13
|
+
# class Types::Query < GraphQL::Schema::Object
|
|
17
14
|
# description "The query root of this schema"
|
|
18
15
|
#
|
|
19
|
-
# field :post
|
|
20
|
-
# type post_type
|
|
21
|
-
# resolve ->(obj, args, ctx) { Post.find(args["id"]) }
|
|
22
|
-
# end
|
|
16
|
+
# field :post, Types::Post, null: true
|
|
23
17
|
# end
|
|
24
18
|
#
|
|
25
|
-
#
|
|
26
|
-
# name "Post"
|
|
19
|
+
# class Types::Post < GraphQL::Schema::Object
|
|
27
20
|
# description "A blog post"
|
|
28
21
|
#
|
|
29
|
-
# field :id,
|
|
30
|
-
# field :title,
|
|
31
|
-
# field :body,
|
|
22
|
+
# field :id, ID, null: false
|
|
23
|
+
# field :title, String, null: false
|
|
24
|
+
# field :body, String, null: false
|
|
32
25
|
# end
|
|
33
26
|
#
|
|
34
|
-
# MySchema
|
|
27
|
+
# class MySchema < GraphQL::Schema
|
|
28
|
+
# query(Types::Query)
|
|
29
|
+
# end
|
|
35
30
|
#
|
|
36
31
|
# printer = GraphQL::Schema::Printer.new(MySchema)
|
|
37
|
-
# puts printer.print_type(
|
|
32
|
+
# puts printer.print_type(Types::Post)
|
|
38
33
|
#
|
|
39
34
|
class Printer < GraphQL::Language::Printer
|
|
40
35
|
attr_reader :schema, :warden
|
|
@@ -61,7 +56,7 @@ module GraphQL
|
|
|
61
56
|
def self.print_introspection_schema
|
|
62
57
|
query_root = Class.new(GraphQL::Schema::Object) do
|
|
63
58
|
graphql_name "Root"
|
|
64
|
-
field :throwaway_field, String
|
|
59
|
+
field :throwaway_field, String
|
|
65
60
|
end
|
|
66
61
|
schema = Class.new(GraphQL::Schema) { query(query_root) }
|
|
67
62
|
|
|
@@ -87,7 +82,7 @@ module GraphQL
|
|
|
87
82
|
|
|
88
83
|
# Return a GraphQL schema string for the defined types in the schema
|
|
89
84
|
def print_schema
|
|
90
|
-
print(@document)
|
|
85
|
+
print(@document) + "\n"
|
|
91
86
|
end
|
|
92
87
|
|
|
93
88
|
def print_type(type)
|
|
@@ -22,7 +22,7 @@ module GraphQL
|
|
|
22
22
|
#
|
|
23
23
|
class RelayClassicMutation < GraphQL::Schema::Mutation
|
|
24
24
|
# The payload should always include this field
|
|
25
|
-
field(:client_mutation_id, String, "A unique identifier for the client performing the mutation."
|
|
25
|
+
field(:client_mutation_id, String, "A unique identifier for the client performing the mutation.")
|
|
26
26
|
# Relay classic default:
|
|
27
27
|
null(true)
|
|
28
28
|
|
|
@@ -81,6 +81,31 @@ module GraphQL
|
|
|
81
81
|
end
|
|
82
82
|
|
|
83
83
|
class << self
|
|
84
|
+
|
|
85
|
+
# Also apply this argument to the input type:
|
|
86
|
+
def argument(*args, **kwargs, &block)
|
|
87
|
+
it = input_type # make sure any inherited arguments are already added to it
|
|
88
|
+
arg = super
|
|
89
|
+
|
|
90
|
+
# This definition might be overriding something inherited;
|
|
91
|
+
# if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
|
|
92
|
+
prev_args = it.own_arguments[arg.graphql_name]
|
|
93
|
+
case prev_args
|
|
94
|
+
when GraphQL::Schema::Argument
|
|
95
|
+
if prev_args.owner != self
|
|
96
|
+
it.own_arguments.delete(arg.graphql_name)
|
|
97
|
+
end
|
|
98
|
+
when Array
|
|
99
|
+
prev_args.reject! { |a| a.owner != self }
|
|
100
|
+
if prev_args.empty?
|
|
101
|
+
it.own_arguments.delete(arg.graphql_name)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it.add_argument(arg)
|
|
106
|
+
arg
|
|
107
|
+
end
|
|
108
|
+
|
|
84
109
|
# The base class for generated input object types
|
|
85
110
|
# @param new_class [Class] The base class to use for generating input object definitions
|
|
86
111
|
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
|
@@ -115,20 +140,29 @@ module GraphQL
|
|
|
115
140
|
# To customize how input objects are generated, override this method
|
|
116
141
|
# @return [Class] a subclass of {.input_object_class}
|
|
117
142
|
def generate_input_type
|
|
118
|
-
mutation_args =
|
|
143
|
+
mutation_args = all_argument_definitions
|
|
119
144
|
mutation_name = graphql_name
|
|
120
145
|
mutation_class = self
|
|
121
146
|
Class.new(input_object_class) do
|
|
122
147
|
graphql_name("#{mutation_name}Input")
|
|
123
148
|
description("Autogenerated input type of #{mutation_name}")
|
|
124
149
|
mutation(mutation_class)
|
|
125
|
-
|
|
150
|
+
# these might be inherited:
|
|
151
|
+
mutation_args.each do |arg|
|
|
126
152
|
add_argument(arg)
|
|
127
153
|
end
|
|
128
154
|
argument :client_mutation_id, String, "A unique identifier for the client performing the mutation.", required: false
|
|
129
155
|
end
|
|
130
156
|
end
|
|
131
157
|
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
|
|
161
|
+
def authorize_arguments(args, values)
|
|
162
|
+
# remove the `input` wrapper to match values
|
|
163
|
+
input_args = args["input"].type.unwrap.arguments(context)
|
|
164
|
+
super(input_args, values)
|
|
165
|
+
end
|
|
132
166
|
end
|
|
133
167
|
end
|
|
134
168
|
end
|
|
@@ -38,6 +38,9 @@ module GraphQL
|
|
|
38
38
|
# @return [Class]
|
|
39
39
|
def object_class(new_class = nil)
|
|
40
40
|
if new_class
|
|
41
|
+
if defined?(@payload_type)
|
|
42
|
+
raise "Can't configure `object_class(...)` after the payload type has already been initialized. Move this configuration higher up the class definition."
|
|
43
|
+
end
|
|
41
44
|
@object_class = new_class
|
|
42
45
|
else
|
|
43
46
|
@object_class || find_inherited_value(:object_class, GraphQL::Schema::Object)
|
|
@@ -46,6 +49,28 @@ module GraphQL
|
|
|
46
49
|
|
|
47
50
|
NO_INTERFACES = [].freeze
|
|
48
51
|
|
|
52
|
+
def field(*args, **kwargs, &block)
|
|
53
|
+
pt = payload_type # make sure it's initialized with any inherited fields
|
|
54
|
+
field_defn = super
|
|
55
|
+
|
|
56
|
+
# Remove any inherited fields to avoid false conflicts at runtime
|
|
57
|
+
prev_fields = pt.own_fields[field_defn.graphql_name]
|
|
58
|
+
case prev_fields
|
|
59
|
+
when GraphQL::Schema::Field
|
|
60
|
+
if prev_fields.owner != self
|
|
61
|
+
pt.own_fields.delete(field_defn.graphql_name)
|
|
62
|
+
end
|
|
63
|
+
when Array
|
|
64
|
+
prev_fields.reject! { |f| f.owner != self }
|
|
65
|
+
if prev_fields.empty?
|
|
66
|
+
pt.own_fields.delete(field_defn.graphql_name)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
pt.add_field(field_defn, method_conflict_warning: false)
|
|
71
|
+
field_defn
|
|
72
|
+
end
|
|
73
|
+
|
|
49
74
|
private
|
|
50
75
|
|
|
51
76
|
# Build a subclass of {.object_class} based on `self`.
|
|
@@ -53,11 +78,11 @@ module GraphQL
|
|
|
53
78
|
# Override this hook to customize return type generation.
|
|
54
79
|
def generate_payload_type
|
|
55
80
|
resolver_name = graphql_name
|
|
56
|
-
resolver_fields =
|
|
81
|
+
resolver_fields = all_field_definitions
|
|
57
82
|
Class.new(object_class) do
|
|
58
83
|
graphql_name("#{resolver_name}Payload")
|
|
59
84
|
description("Autogenerated return type of #{resolver_name}")
|
|
60
|
-
resolver_fields.each do |
|
|
85
|
+
resolver_fields.each do |f|
|
|
61
86
|
# Reattach the already-defined field here
|
|
62
87
|
# (The field's `.owner` will still point to the mutation, not the object type, I think)
|
|
63
88
|
# Don't re-warn about a method conflict. Since this type is generated, it should be fixed in the resolver instead.
|