graphql 1.12.23 → 1.13.3
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/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/type_generator.rb +0 -1
- data/lib/graphql/analysis/ast/field_usage.rb +2 -2
- 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 +1 -1
- data/lib/graphql/base_type.rb +4 -2
- data/lib/graphql/boolean_type.rb +1 -1
- data/lib/graphql/dataloader.rb +55 -22
- 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/interpreter/arguments.rb +1 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +2 -2
- data/lib/graphql/execution/interpreter/runtime.rb +31 -19
- 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/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 +4 -4
- data/lib/graphql/introspection/schema_type.rb +2 -2
- 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 +3 -2
- 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/relation_connection.rb +55 -28
- 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/variables.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 +37 -28
- data/lib/graphql/schema/argument.rb +13 -15
- data/lib/graphql/schema/build_from_definition.rb +5 -5
- 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 +1 -1
- data/lib/graphql/schema/directive.rb +7 -3
- data/lib/graphql/schema/enum.rb +60 -10
- 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 +229 -77
- 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 +23 -5
- 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 +0 -4
- data/lib/graphql/schema/member/cached_graphql_definition.rb +29 -2
- data/lib/graphql/schema/member/has_arguments.rb +55 -13
- 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 +1 -1
- 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 +37 -17
- data/lib/graphql/schema/scalar.rb +2 -0
- data/lib/graphql/schema/subscription.rb +11 -1
- 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/format_validator.rb +0 -4
- data/lib/graphql/schema/validator/numericality_validator.rb +1 -0
- data/lib/graphql/schema/validator/required_validator.rb +29 -15
- data/lib/graphql/schema/validator.rb +4 -7
- data/lib/graphql/schema/warden.rb +116 -52
- data/lib/graphql/schema.rb +111 -23
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +5 -5
- data/lib/graphql/static_validation/definition_dependencies.rb +0 -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 +1 -1
- 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 +2 -2
- 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/string_type.rb +1 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -4
- data/lib/graphql/subscriptions/event.rb +20 -12
- data/lib/graphql/subscriptions/serialize.rb +22 -2
- data/lib/graphql/subscriptions.rb +17 -19
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +6 -20
- data/lib/graphql/tracing/notifications_tracing.rb +59 -0
- 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 +1 -1
- data/lib/graphql/types/relay/has_nodes_field.rb +1 -1
- data/lib/graphql/types/relay/node_field.rb +14 -3
- data/lib/graphql/types/relay/nodes_field.rb +13 -3
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +10 -32
- metadata +13 -5
@@ -16,6 +16,7 @@ module GraphQL
|
|
16
16
|
include GraphQL::Schema::Member::HasAstNode
|
17
17
|
include GraphQL::Schema::Member::HasUnresolvedTypeError
|
18
18
|
include GraphQL::Schema::Member::HasDirectives
|
19
|
+
include GraphQL::Schema::Member::HasInterfaces
|
19
20
|
|
20
21
|
# Methods defined in this block will be:
|
21
22
|
# - Added as class methods to this interface
|
@@ -57,9 +58,10 @@ module GraphQL
|
|
57
58
|
child_class.extend(Schema::Interface::DefinitionMethods)
|
58
59
|
|
59
60
|
child_class.type_membership_class(self.type_membership_class)
|
60
|
-
child_class.
|
61
|
-
|
62
|
-
|
61
|
+
child_class.ancestors.reverse_each do |ancestor|
|
62
|
+
if ancestor.const_defined?(:DefinitionMethods)
|
63
|
+
child_class.extend(ancestor::DefinitionMethods)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
|
65
67
|
# Use an instance variable to tell whether it's been included previously or not;
|
@@ -73,16 +75,13 @@ module GraphQL
|
|
73
75
|
end
|
74
76
|
child_class.introspection(introspection)
|
75
77
|
child_class.description(description)
|
76
|
-
if overridden_graphql_name
|
77
|
-
child_class.graphql_name(overridden_graphql_name)
|
78
|
-
end
|
79
78
|
# If interfaces are mixed into each other, only define this class once
|
80
79
|
if !child_class.const_defined?(:UnresolvedTypeError, false)
|
81
80
|
add_unresolved_type_error(child_class)
|
82
81
|
end
|
83
82
|
elsif child_class < GraphQL::Schema::Object
|
84
83
|
# This is being included into an object type, make sure it's using `implements(...)`
|
85
|
-
backtrace_line = caller(0, 10).find { |line| line.include?("schema/
|
84
|
+
backtrace_line = caller(0, 10).find { |line| line.include?("schema/member/has_interfaces.rb") && line.include?("in `implements'")}
|
86
85
|
if !backtrace_line
|
87
86
|
raise "Attach interfaces using `implements(#{self})`, not `include(#{self})`"
|
88
87
|
end
|
@@ -101,6 +100,8 @@ module GraphQL
|
|
101
100
|
end
|
102
101
|
end
|
103
102
|
|
103
|
+
prepend Schema::Member::CachedGraphQLDefinition::DeprecatedToGraphQL
|
104
|
+
|
104
105
|
def to_graphql
|
105
106
|
type_defn = GraphQL::InterfaceType.new
|
106
107
|
type_defn.name = graphql_name
|
@@ -108,9 +109,9 @@ module GraphQL
|
|
108
109
|
type_defn.orphan_types = orphan_types
|
109
110
|
type_defn.type_membership_class = self.type_membership_class
|
110
111
|
type_defn.ast_node = ast_node
|
111
|
-
fields.each do |field_name, field_inst|
|
112
|
-
field_defn = field_inst.graphql_definition
|
113
|
-
type_defn.fields[field_defn.name] = field_defn
|
112
|
+
fields.each do |field_name, field_inst| # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
113
|
+
field_defn = field_inst.graphql_definition(silence_deprecation_warning: true)
|
114
|
+
type_defn.fields[field_defn.name] = field_defn # rubocop:disable Development/ContextIsPassedCop -- legacy-related
|
114
115
|
end
|
115
116
|
type_defn.metadata[:type_class] = self
|
116
117
|
if respond_to?(:resolve_type)
|
@@ -122,16 +123,6 @@ module GraphQL
|
|
122
123
|
def kind
|
123
124
|
GraphQL::TypeKinds::INTERFACE
|
124
125
|
end
|
125
|
-
|
126
|
-
protected
|
127
|
-
|
128
|
-
def own_interfaces
|
129
|
-
@own_interfaces ||= []
|
130
|
-
end
|
131
|
-
|
132
|
-
def interfaces
|
133
|
-
own_interfaces + (own_interfaces.map { |i| i.own_interfaces }).flatten
|
134
|
-
end
|
135
126
|
end
|
136
127
|
|
137
128
|
# Extend this _after_ `DefinitionMethods` is defined, so it will be used
|
@@ -107,7 +107,7 @@ module GraphQL
|
|
107
107
|
dup_type_class(const)
|
108
108
|
else
|
109
109
|
# Use `.to_graphql` to get a freshly-made version, not shared between schemas
|
110
|
-
const.
|
110
|
+
const.deprecated_to_graphql
|
111
111
|
end
|
112
112
|
rescue NameError
|
113
113
|
# Dup the built-in so that the cached fields aren't shared
|
data/lib/graphql/schema/list.rb
CHANGED
@@ -8,8 +8,10 @@ module GraphQL
|
|
8
8
|
class List < 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_list_type
|
14
|
+
@of_type.graphql_definition(silence_deprecation_warning: true).to_list_type
|
13
15
|
end
|
14
16
|
|
15
17
|
# @return [GraphQL::TypeKinds::LIST]
|
@@ -85,8 +85,15 @@ module GraphQL
|
|
85
85
|
define_method(name) do |*args|
|
86
86
|
if args.any?
|
87
87
|
instance_variable_set(ivar_name, args)
|
88
|
+
else
|
89
|
+
if (v = instance_variable_get(ivar_name))
|
90
|
+
v
|
91
|
+
elsif (ancestor = ancestors.find { |i| i.respond_to?(name) && i != self })
|
92
|
+
ancestor.public_send(name)
|
93
|
+
else
|
94
|
+
nil
|
95
|
+
end
|
88
96
|
end
|
89
|
-
instance_variable_get(ivar_name) || ((int = interfaces.first { |i| i.respond_to?()}) && int.public_send(name))
|
90
97
|
end
|
91
98
|
end
|
92
99
|
end
|
@@ -116,8 +123,13 @@ module GraphQL
|
|
116
123
|
end
|
117
124
|
|
118
125
|
module ToGraphQLExtension
|
119
|
-
def to_graphql
|
120
|
-
|
126
|
+
def to_graphql(*args, **kwargs)
|
127
|
+
|
128
|
+
defn = if args.empty? && kwargs.empty?
|
129
|
+
super()
|
130
|
+
else
|
131
|
+
super
|
132
|
+
end
|
121
133
|
accepts_definition_methods.each do |method_name|
|
122
134
|
value = public_send(method_name)
|
123
135
|
if !value.nil?
|
@@ -4,10 +4,6 @@ module GraphQL
|
|
4
4
|
class Member
|
5
5
|
# @api private
|
6
6
|
module BuildType
|
7
|
-
if !String.method_defined?(:match?)
|
8
|
-
using GraphQL::StringMatchBackport
|
9
|
-
end
|
10
|
-
|
11
7
|
LIST_TYPE_ERROR = "Use an array of [T] or [T, null: true] for list types; other arrays are not supported"
|
12
8
|
|
13
9
|
module_function
|
@@ -11,8 +11,24 @@ module GraphQL
|
|
11
11
|
# A cached result of {.to_graphql}.
|
12
12
|
# It's cached here so that user-overridden {.to_graphql} implementations
|
13
13
|
# are also cached
|
14
|
-
def graphql_definition
|
15
|
-
@graphql_definition ||=
|
14
|
+
def graphql_definition(silence_deprecation_warning: false)
|
15
|
+
@graphql_definition ||= begin
|
16
|
+
unless silence_deprecation_warning
|
17
|
+
message = "Legacy `.graphql_definition` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.graphql_definition` to use a class-based definition instead."
|
18
|
+
caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
|
19
|
+
GraphQL::Deprecation.warn(message + caller_message)
|
20
|
+
end
|
21
|
+
deprecated_to_graphql
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def deprecated_to_graphql
|
26
|
+
case method(:to_graphql).arity
|
27
|
+
when 0
|
28
|
+
to_graphql
|
29
|
+
else
|
30
|
+
to_graphql(silence_deprecation_warning: true)
|
31
|
+
end
|
16
32
|
end
|
17
33
|
|
18
34
|
# This is for a common interface with .define-based types
|
@@ -25,6 +41,17 @@ module GraphQL
|
|
25
41
|
super
|
26
42
|
@graphql_definition = nil
|
27
43
|
end
|
44
|
+
|
45
|
+
module DeprecatedToGraphQL
|
46
|
+
def to_graphql(silence_deprecation_warning: false)
|
47
|
+
unless silence_deprecation_warning
|
48
|
+
message = "Legacy `.to_graphql` objects are deprecated and will be removed in GraphQL-Ruby 2.0. Remove `.to_graphql` to use a class-based definition instead."
|
49
|
+
caller_message = "\n\nCalled on #{self.inspect} from:\n #{caller(1, 25).map { |l| " #{l}" }.join("\n")}"
|
50
|
+
GraphQL::Deprecation.warn(message + caller_message)
|
51
|
+
end
|
52
|
+
super()
|
53
|
+
end
|
54
|
+
end
|
28
55
|
end
|
29
56
|
end
|
30
57
|
end
|
@@ -78,30 +78,72 @@ module GraphQL
|
|
78
78
|
# @return [GraphQL::Schema::Argument]
|
79
79
|
def add_argument(arg_defn)
|
80
80
|
@own_arguments ||= {}
|
81
|
-
own_arguments[arg_defn.name]
|
81
|
+
prev_defn = own_arguments[arg_defn.name]
|
82
|
+
case prev_defn
|
83
|
+
when nil
|
84
|
+
own_arguments[arg_defn.name] = arg_defn
|
85
|
+
when Array
|
86
|
+
prev_defn << arg_defn
|
87
|
+
when GraphQL::Schema::Argument
|
88
|
+
own_arguments[arg_defn.name] = [prev_defn, arg_defn]
|
89
|
+
else
|
90
|
+
raise "Invariant: unexpected `@own_arguments[#{arg_defn.name.inspect}]`: #{prev_defn.inspect}"
|
91
|
+
end
|
82
92
|
arg_defn
|
83
93
|
end
|
84
94
|
|
85
95
|
# @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
|
86
|
-
def arguments
|
87
|
-
inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments : nil)
|
96
|
+
def arguments(context = GraphQL::Query::NullContext)
|
97
|
+
inherited_arguments = ((self.is_a?(Class) && superclass.respond_to?(:arguments)) ? superclass.arguments(context) : nil)
|
88
98
|
# Local definitions override inherited ones
|
99
|
+
if own_arguments.any?
|
100
|
+
own_arguments_that_apply = {}
|
101
|
+
own_arguments.each do |name, args_entry|
|
102
|
+
if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
|
103
|
+
own_arguments_that_apply[visible_defn.graphql_name] = visible_defn
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
89
108
|
if inherited_arguments
|
90
|
-
|
109
|
+
if own_arguments_that_apply
|
110
|
+
inherited_arguments.merge(own_arguments_that_apply)
|
111
|
+
else
|
112
|
+
inherited_arguments
|
113
|
+
end
|
91
114
|
else
|
92
|
-
|
115
|
+
# might be nil if there are actually no arguments
|
116
|
+
own_arguments_that_apply || own_arguments
|
93
117
|
end
|
94
118
|
end
|
95
119
|
|
96
|
-
|
97
|
-
|
98
|
-
|
120
|
+
def all_argument_definitions
|
121
|
+
if self.is_a?(Class)
|
122
|
+
all_defns = {}
|
123
|
+
ancestors.reverse_each do |ancestor|
|
124
|
+
if ancestor.respond_to?(:own_arguments)
|
125
|
+
all_defns.merge!(ancestor.own_arguments)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
else
|
129
|
+
all_defns = own_arguments
|
130
|
+
end
|
131
|
+
all_defns = all_defns.values
|
132
|
+
all_defns.flatten!
|
133
|
+
all_defns
|
134
|
+
end
|
99
135
|
|
100
|
-
|
101
|
-
|
136
|
+
# @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
|
137
|
+
def get_argument(argument_name, context = GraphQL::Query::NullContext)
|
138
|
+
warden = Warden.from_context(context)
|
139
|
+
if !self.is_a?(Class)
|
140
|
+
a = own_arguments[argument_name]
|
141
|
+
a && Warden.visible_entry?(:visible_argument?, a, context, warden)
|
102
142
|
else
|
103
143
|
for ancestor in ancestors
|
104
|
-
if ancestor.respond_to?(:own_arguments) &&
|
144
|
+
if ancestor.respond_to?(:own_arguments) &&
|
145
|
+
(a = ancestor.own_arguments[argument_name]) &&
|
146
|
+
(a = Warden.visible_entry?(:visible_argument?, a, context, warden))
|
105
147
|
return a
|
106
148
|
end
|
107
149
|
end
|
@@ -125,7 +167,7 @@ module GraphQL
|
|
125
167
|
# @return [Interpreter::Arguments, Execution::Lazy<Interpeter::Arguments>]
|
126
168
|
def coerce_arguments(parent_object, values, context, &block)
|
127
169
|
# Cache this hash to avoid re-merging it
|
128
|
-
arg_defns = self.arguments
|
170
|
+
arg_defns = self.arguments(context)
|
129
171
|
total_args_count = arg_defns.size
|
130
172
|
|
131
173
|
finished_args = nil
|
@@ -191,7 +233,7 @@ module GraphQL
|
|
191
233
|
def arguments_statically_coercible?
|
192
234
|
return @arguments_statically_coercible if defined?(@arguments_statically_coercible)
|
193
235
|
|
194
|
-
@arguments_statically_coercible =
|
236
|
+
@arguments_statically_coercible = all_argument_definitions.all?(&:statically_coercible?)
|
195
237
|
end
|
196
238
|
|
197
239
|
module ArgumentClassAccessor
|
@@ -7,7 +7,7 @@ module GraphQL
|
|
7
7
|
# @return [String, nil] Explains why this member was deprecated (if present, this will be marked deprecated in introspection)
|
8
8
|
def deprecation_reason
|
9
9
|
dir = self.directives.find { |d| d.is_a?(GraphQL::Schema::Directive::Deprecated) }
|
10
|
-
dir && dir.arguments[:reason]
|
10
|
+
dir && dir.arguments[:reason] # rubocop:disable Development/ContextIsPassedCop -- definition-related
|
11
11
|
end
|
12
12
|
|
13
13
|
# Set the deprecation reason for this member, or remove it by assigning `nil`
|
@@ -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
|
|