graphql 2.3.7 → 2.4.5
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/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 +37 -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 +25 -20
- 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 +187 -0
- data/lib/graphql/schema/visibility/profile.rb +353 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +294 -0
- data/lib/graphql/schema/warden.rb +166 -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.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 +81 -45
- metadata +31 -8
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
@@ -10,6 +10,14 @@ module GraphQL
|
|
10
10
|
|
11
11
|
include GraphQL::Dig
|
12
12
|
|
13
|
+
# Raised when an InputObject doesn't have any arguments defined and hasn't explicitly opted out of this requirement
|
14
|
+
class ArgumentsAreRequiredError < GraphQL::Error
|
15
|
+
def initialize(input_object_type)
|
16
|
+
message = "Input Object types must have arguments, but #{input_object_type.graphql_name} doesn't have any. Define an argument for this type, remove it from your schema, or add `has_no_arguments(true)` to its definition."
|
17
|
+
super(message)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
13
21
|
# @return [GraphQL::Query::Context] The context for this query
|
14
22
|
attr_reader :context
|
15
23
|
# @return [GraphQL::Execution::Interpereter::Arguments] The underlying arguments instance
|
@@ -23,7 +31,8 @@ module GraphQL
|
|
23
31
|
@ruby_style_hash = ruby_kwargs
|
24
32
|
@arguments = arguments
|
25
33
|
# Apply prepares, not great to have it duplicated here.
|
26
|
-
self.class.arguments(context).each_value
|
34
|
+
arg_defns = context ? context.types.arguments(self.class) : self.class.arguments(context).each_value
|
35
|
+
arg_defns.each do |arg_defn|
|
27
36
|
ruby_kwargs_key = arg_defn.keyword
|
28
37
|
if @ruby_style_hash.key?(ruby_kwargs_key)
|
29
38
|
# Weirdly, procs are applied during coercion, but not methods.
|
@@ -44,6 +53,16 @@ module GraphQL
|
|
44
53
|
to_h
|
45
54
|
end
|
46
55
|
|
56
|
+
def deconstruct_keys(keys = nil)
|
57
|
+
if keys.nil?
|
58
|
+
@ruby_style_hash
|
59
|
+
else
|
60
|
+
new_h = {}
|
61
|
+
keys.each { |k| @ruby_style_hash.key?(k) && new_h[k] = @ruby_style_hash[k] }
|
62
|
+
new_h
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
47
66
|
def prepare
|
48
67
|
if @context
|
49
68
|
object = @context[:current_object]
|
@@ -55,33 +74,6 @@ module GraphQL
|
|
55
74
|
end
|
56
75
|
end
|
57
76
|
|
58
|
-
def self.authorized?(obj, value, ctx)
|
59
|
-
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
60
|
-
if value.respond_to?(:key?)
|
61
|
-
arguments(ctx).each do |_name, input_obj_arg|
|
62
|
-
if value.key?(input_obj_arg.keyword) &&
|
63
|
-
!input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
64
|
-
return false
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
# It didn't early-return false:
|
69
|
-
true
|
70
|
-
end
|
71
|
-
|
72
|
-
def self.one_of
|
73
|
-
if !one_of?
|
74
|
-
if all_argument_definitions.any? { |arg| arg.type.non_null? }
|
75
|
-
raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
|
76
|
-
end
|
77
|
-
directive(GraphQL::Schema::Directive::OneOf)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def self.one_of?
|
82
|
-
false # Re-defined when `OneOf` is added
|
83
|
-
end
|
84
|
-
|
85
77
|
def unwrap_value(value)
|
86
78
|
case value
|
87
79
|
when Array
|
@@ -120,6 +112,33 @@ module GraphQL
|
|
120
112
|
end
|
121
113
|
|
122
114
|
class << self
|
115
|
+
def authorized?(obj, value, ctx)
|
116
|
+
# Authorize each argument (but this doesn't apply if `prepare` is implemented):
|
117
|
+
if value.respond_to?(:key?)
|
118
|
+
ctx.types.arguments(self).each do |input_obj_arg|
|
119
|
+
if value.key?(input_obj_arg.keyword) &&
|
120
|
+
!input_obj_arg.authorized?(obj, value[input_obj_arg.keyword], ctx)
|
121
|
+
return false
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
# It didn't early-return false:
|
126
|
+
true
|
127
|
+
end
|
128
|
+
|
129
|
+
def one_of
|
130
|
+
if !one_of?
|
131
|
+
if all_argument_definitions.any? { |arg| arg.type.non_null? }
|
132
|
+
raise ArgumentError, "`one_of` may not be used with required arguments -- add `required: false` to argument definitions to use `one_of`"
|
133
|
+
end
|
134
|
+
directive(GraphQL::Schema::Directive::OneOf)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def one_of?
|
139
|
+
false # Re-defined when `OneOf` is added
|
140
|
+
end
|
141
|
+
|
123
142
|
def argument(*args, **kwargs, &block)
|
124
143
|
argument_defn = super(*args, **kwargs, &block)
|
125
144
|
if one_of?
|
@@ -132,12 +151,14 @@ module GraphQL
|
|
132
151
|
end
|
133
152
|
# Add a method access
|
134
153
|
method_name = argument_defn.keyword
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
154
|
+
suppress_redefinition_warning do
|
155
|
+
class_eval <<-RUBY, __FILE__, __LINE__
|
156
|
+
def #{method_name}
|
157
|
+
self[#{method_name.inspect}]
|
158
|
+
end
|
159
|
+
alias_method :#{method_name}, :#{method_name}
|
160
|
+
RUBY
|
161
|
+
end
|
141
162
|
argument_defn
|
142
163
|
end
|
143
164
|
|
@@ -149,7 +170,7 @@ module GraphQL
|
|
149
170
|
INVALID_OBJECT_MESSAGE = "Expected %{object} to be a key-value object."
|
150
171
|
|
151
172
|
def validate_non_null_input(input, ctx, max_errors: nil)
|
152
|
-
|
173
|
+
types = ctx.types
|
153
174
|
|
154
175
|
if input.is_a?(Array)
|
155
176
|
return GraphQL::Query::InputValidationResult.from_problem(INVALID_OBJECT_MESSAGE % { object: JSON.generate(input, quirks_mode: true) })
|
@@ -161,9 +182,9 @@ module GraphQL
|
|
161
182
|
end
|
162
183
|
|
163
184
|
# Inject missing required arguments
|
164
|
-
missing_required_inputs =
|
165
|
-
if !input.key?(
|
166
|
-
m[
|
185
|
+
missing_required_inputs = ctx.types.arguments(self).reduce({}) do |m, (argument)|
|
186
|
+
if !input.key?(argument.graphql_name) && argument.type.non_null? && !argument.default_value? && types.argument(self, argument.graphql_name)
|
187
|
+
m[argument.graphql_name] = nil
|
167
188
|
end
|
168
189
|
|
169
190
|
m
|
@@ -172,7 +193,7 @@ module GraphQL
|
|
172
193
|
result = nil
|
173
194
|
[input, missing_required_inputs].each do |args_to_validate|
|
174
195
|
args_to_validate.each do |argument_name, value|
|
175
|
-
argument =
|
196
|
+
argument = types.argument(self, argument_name)
|
176
197
|
# Items in the input that are unexpected
|
177
198
|
if argument.nil?
|
178
199
|
result ||= Query::InputValidationResult.new
|
@@ -242,6 +263,36 @@ module GraphQL
|
|
242
263
|
|
243
264
|
result
|
244
265
|
end
|
266
|
+
|
267
|
+
# @param new_has_no_arguments [Boolean] Call with `true` to make this InputObject type ignore the requirement to have any defined arguments.
|
268
|
+
# @return [void]
|
269
|
+
def has_no_arguments(new_has_no_arguments)
|
270
|
+
@has_no_arguments = new_has_no_arguments
|
271
|
+
nil
|
272
|
+
end
|
273
|
+
|
274
|
+
# @return [Boolean] `true` if `has_no_arguments(true)` was configued
|
275
|
+
def has_no_arguments?
|
276
|
+
@has_no_arguments
|
277
|
+
end
|
278
|
+
|
279
|
+
def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
|
280
|
+
if require_defined_arguments && !has_no_arguments? && !any_arguments?
|
281
|
+
warn(GraphQL::Schema::InputObject::ArgumentsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
|
282
|
+
end
|
283
|
+
super(context, false)
|
284
|
+
end
|
285
|
+
|
286
|
+
private
|
287
|
+
|
288
|
+
# Suppress redefinition warning for objectId arguments
|
289
|
+
def suppress_redefinition_warning
|
290
|
+
verbose = $VERBOSE
|
291
|
+
$VERBOSE = nil
|
292
|
+
yield
|
293
|
+
ensure
|
294
|
+
$VERBOSE = verbose
|
295
|
+
end
|
245
296
|
end
|
246
297
|
|
247
298
|
private
|
@@ -63,6 +63,7 @@ module GraphQL
|
|
63
63
|
|
64
64
|
child_class.introspection(introspection)
|
65
65
|
child_class.description(description)
|
66
|
+
child_class.comment(nil)
|
66
67
|
# If interfaces are mixed into each other, only define this class once
|
67
68
|
if !child_class.const_defined?(:UnresolvedTypeError, false)
|
68
69
|
add_unresolved_type_error(child_class)
|
@@ -82,13 +83,29 @@ module GraphQL
|
|
82
83
|
super
|
83
84
|
end
|
84
85
|
|
86
|
+
# Register other Interface or Object types as implementers of this Interface.
|
87
|
+
#
|
88
|
+
# When those Interfaces or Objects aren't used as the return values of fields,
|
89
|
+
# they may have to be registered using this method so that GraphQL-Ruby can find them.
|
90
|
+
# @param types [Class, Module]
|
91
|
+
# @return [Array<Module, Class>] Implementers of this interface, if they're registered
|
85
92
|
def orphan_types(*types)
|
86
|
-
if types.
|
87
|
-
@orphan_types
|
93
|
+
if !types.empty?
|
94
|
+
@orphan_types ||= []
|
95
|
+
@orphan_types.concat(types)
|
88
96
|
else
|
89
|
-
|
90
|
-
|
91
|
-
|
97
|
+
if defined?(@orphan_types)
|
98
|
+
all_orphan_types = @orphan_types.dup
|
99
|
+
if defined?(super)
|
100
|
+
all_orphan_types += super
|
101
|
+
all_orphan_types.uniq!
|
102
|
+
end
|
103
|
+
all_orphan_types
|
104
|
+
elsif defined?(super)
|
105
|
+
super
|
106
|
+
else
|
107
|
+
EmptyObjects::EMPTY_ARRAY
|
108
|
+
end
|
92
109
|
end
|
93
110
|
end
|
94
111
|
|
@@ -25,7 +25,7 @@ module GraphQL
|
|
25
25
|
load_constant(:DirectiveLocationEnum)
|
26
26
|
]
|
27
27
|
@types = {}
|
28
|
-
@possible_types = {}.
|
28
|
+
@possible_types = {}.compare_by_identity
|
29
29
|
type_defns.each do |t|
|
30
30
|
@types[t.graphql_name] = t
|
31
31
|
@possible_types[t] = [t]
|
@@ -69,7 +69,7 @@ module GraphQL
|
|
69
69
|
def resolve_late_bindings
|
70
70
|
@types.each do |name, t|
|
71
71
|
if t.kind.fields?
|
72
|
-
t.
|
72
|
+
t.all_field_definitions.each do |field_defn|
|
73
73
|
field_defn.type = resolve_late_binding(field_defn.type)
|
74
74
|
end
|
75
75
|
end
|
@@ -90,7 +90,8 @@ module GraphQL
|
|
90
90
|
def resolve_late_binding(late_bound_type)
|
91
91
|
case late_bound_type
|
92
92
|
when GraphQL::Schema::LateBoundType
|
93
|
-
|
93
|
+
type_name = late_bound_type.name
|
94
|
+
@types[type_name] || @schema.get_type(type_name)
|
94
95
|
when GraphQL::Schema::List
|
95
96
|
resolve_late_binding(late_bound_type.of_type).to_list_type
|
96
97
|
when GraphQL::Schema::NonNull
|
@@ -113,19 +114,7 @@ module GraphQL
|
|
113
114
|
|
114
115
|
def get_fields_from_class(class_sym:)
|
115
116
|
object_type_defn = load_constant(class_sym)
|
116
|
-
|
117
|
-
if object_type_defn.is_a?(Module)
|
118
|
-
object_type_defn.fields
|
119
|
-
else
|
120
|
-
extracted_field_defns = {}
|
121
|
-
object_class = object_type_defn.metadata[:type_class]
|
122
|
-
object_type_defn.all_fields.each do |field_defn|
|
123
|
-
inner_resolve = field_defn.resolve_proc
|
124
|
-
resolve_with_instantiate = PerFieldProxyResolve.new(object_class: object_class, inner_resolve: inner_resolve)
|
125
|
-
extracted_field_defns[field_defn.name] = field_defn.redefine(resolve: resolve_with_instantiate)
|
126
|
-
end
|
127
|
-
extracted_field_defns
|
128
|
-
end
|
117
|
+
object_type_defn.fields
|
129
118
|
end
|
130
119
|
|
131
120
|
# This is probably not 100% robust -- but it has to be good enough to avoid modifying the built-in introspection types
|
@@ -50,12 +50,27 @@ module GraphQL
|
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
+
# Call this method to provide a new comment; OR
|
54
|
+
# call it without an argument to get the comment
|
55
|
+
# @param new_comment [String]
|
56
|
+
# @return [String, nil]
|
57
|
+
def comment(new_comment = NOT_CONFIGURED)
|
58
|
+
if !NOT_CONFIGURED.equal?(new_comment)
|
59
|
+
@comment = new_comment
|
60
|
+
elsif defined?(@comment)
|
61
|
+
@comment
|
62
|
+
else
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
53
67
|
# This pushes some configurations _down_ the inheritance tree,
|
54
68
|
# in order to prevent repetitive lookups at runtime.
|
55
69
|
module ConfigurationExtension
|
56
70
|
def inherited(child_class)
|
57
71
|
child_class.introspection(introspection)
|
58
72
|
child_class.description(description)
|
73
|
+
child_class.comment(nil)
|
59
74
|
child_class.default_graphql_name = nil
|
60
75
|
|
61
76
|
if defined?(@graphql_name) && @graphql_name && (self.name.nil? || graphql_name != default_graphql_name)
|
@@ -76,8 +76,8 @@ module GraphQL
|
|
76
76
|
end
|
77
77
|
|
78
78
|
# @return [Hash<String => GraphQL::Schema::Argument] Arguments defined on this thing, keyed by name. Includes inherited definitions
|
79
|
-
def arguments(context = GraphQL::Query::NullContext.instance)
|
80
|
-
if own_arguments.
|
79
|
+
def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
|
80
|
+
if !own_arguments.empty?
|
81
81
|
own_arguments_that_apply = {}
|
82
82
|
own_arguments.each do |name, args_entry|
|
83
83
|
if (visible_defn = Warden.visible_entry?(:visible_argument?, args_entry, context))
|
@@ -90,7 +90,7 @@ module GraphQL
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def any_arguments?
|
93
|
-
own_arguments.
|
93
|
+
!own_arguments.empty?
|
94
94
|
end
|
95
95
|
|
96
96
|
module ClassConfigured
|
@@ -100,12 +100,12 @@ module GraphQL
|
|
100
100
|
end
|
101
101
|
|
102
102
|
module InheritedArguments
|
103
|
-
def arguments(context = GraphQL::Query::NullContext.instance)
|
104
|
-
own_arguments = super
|
105
|
-
inherited_arguments = superclass.arguments(context)
|
103
|
+
def arguments(context = GraphQL::Query::NullContext.instance, require_defined_arguments = true)
|
104
|
+
own_arguments = super(context, require_defined_arguments)
|
105
|
+
inherited_arguments = superclass.arguments(context, false)
|
106
106
|
|
107
|
-
if own_arguments.
|
108
|
-
if inherited_arguments.
|
107
|
+
if !own_arguments.empty?
|
108
|
+
if !inherited_arguments.empty?
|
109
109
|
# Local definitions override inherited ones
|
110
110
|
inherited_arguments.merge(own_arguments)
|
111
111
|
else
|
@@ -135,10 +135,11 @@ module GraphQL
|
|
135
135
|
|
136
136
|
def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
|
137
137
|
warden = Warden.from_context(context)
|
138
|
+
skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
138
139
|
for ancestor in ancestors
|
139
140
|
if ancestor.respond_to?(:own_arguments) &&
|
140
141
|
(a = ancestor.own_arguments[argument_name]) &&
|
141
|
-
(a = Warden.visible_entry?(:visible_argument?, a, context, warden))
|
142
|
+
(skip_visible || (a = Warden.visible_entry?(:visible_argument?, a, context, warden)))
|
142
143
|
return a
|
143
144
|
end
|
144
145
|
end
|
@@ -148,12 +149,12 @@ module GraphQL
|
|
148
149
|
end
|
149
150
|
|
150
151
|
module FieldConfigured
|
151
|
-
def arguments(context = GraphQL::Query::NullContext.instance)
|
152
|
+
def arguments(context = GraphQL::Query::NullContext.instance, _require_defined_arguments = nil)
|
152
153
|
own_arguments = super
|
153
154
|
if @resolver_class
|
154
155
|
inherited_arguments = @resolver_class.field_arguments(context)
|
155
|
-
if own_arguments.
|
156
|
-
if inherited_arguments.
|
156
|
+
if !own_arguments.empty?
|
157
|
+
if !inherited_arguments.empty?
|
157
158
|
inherited_arguments.merge(own_arguments)
|
158
159
|
else
|
159
160
|
own_arguments
|
@@ -197,16 +198,20 @@ module GraphQL
|
|
197
198
|
end
|
198
199
|
|
199
200
|
def all_argument_definitions
|
200
|
-
|
201
|
-
|
202
|
-
|
201
|
+
if !own_arguments.empty?
|
202
|
+
all_defns = own_arguments.values
|
203
|
+
all_defns.flatten!
|
204
|
+
all_defns
|
205
|
+
else
|
206
|
+
EmptyObjects::EMPTY_ARRAY
|
207
|
+
end
|
203
208
|
end
|
204
209
|
|
205
210
|
# @return [GraphQL::Schema::Argument, nil] Argument defined on this thing, fetched by name.
|
206
211
|
def get_argument(argument_name, context = GraphQL::Query::NullContext.instance)
|
207
212
|
warden = Warden.from_context(context)
|
208
|
-
if (arg_config = own_arguments[argument_name]) && (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden))
|
209
|
-
visible_arg
|
213
|
+
if (arg_config = own_arguments[argument_name]) && ((context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)) || (visible_arg = Warden.visible_entry?(:visible_argument?, arg_config, context, warden)))
|
214
|
+
visible_arg || arg_config
|
210
215
|
elsif defined?(@resolver_class) && @resolver_class
|
211
216
|
@resolver_class.get_field_argument(argument_name, context)
|
212
217
|
else
|
@@ -230,7 +235,7 @@ module GraphQL
|
|
230
235
|
# @return [Interpreter::Arguments, Execution::Lazy<Interpreter::Arguments>]
|
231
236
|
def coerce_arguments(parent_object, values, context, &block)
|
232
237
|
# Cache this hash to avoid re-merging it
|
233
|
-
arg_defns = context.
|
238
|
+
arg_defns = context.types.arguments(self)
|
234
239
|
total_args_count = arg_defns.size
|
235
240
|
|
236
241
|
finished_args = nil
|
@@ -364,8 +369,8 @@ module GraphQL
|
|
364
369
|
end
|
365
370
|
|
366
371
|
if !(
|
367
|
-
context.
|
368
|
-
context.
|
372
|
+
context.types.possible_types(argument.loads).include?(application_object_type) ||
|
373
|
+
context.types.loadable?(argument.loads, context)
|
369
374
|
)
|
370
375
|
err = GraphQL::LoadApplicationObjectFailedError.new(context: context, argument: argument, id: id, object: application_object)
|
371
376
|
application_object = load_application_object_failed(err)
|
@@ -55,14 +55,14 @@ module GraphQL
|
|
55
55
|
else
|
56
56
|
GraphQL::EmptyObjects::EMPTY_ARRAY
|
57
57
|
end
|
58
|
-
if inherited_directives.
|
58
|
+
if !inherited_directives.empty? && directives
|
59
59
|
dirs = []
|
60
60
|
merge_directives(dirs, inherited_directives)
|
61
61
|
merge_directives(dirs, directives)
|
62
62
|
dirs
|
63
63
|
elsif directives
|
64
64
|
directives
|
65
|
-
elsif inherited_directives.
|
65
|
+
elsif !inherited_directives.empty?
|
66
66
|
inherited_directives
|
67
67
|
else
|
68
68
|
GraphQL::EmptyObjects::EMPTY_ARRAY
|
@@ -71,7 +71,7 @@ module GraphQL
|
|
71
71
|
dirs = nil
|
72
72
|
schema_member.ancestors.reverse_each do |ancestor|
|
73
73
|
if ancestor.respond_to?(:own_directives) &&
|
74
|
-
(anc_dirs = ancestor.own_directives).
|
74
|
+
!(anc_dirs = ancestor.own_directives).empty?
|
75
75
|
dirs ||= []
|
76
76
|
merge_directives(dirs, anc_dirs)
|
77
77
|
end
|
@@ -79,6 +79,18 @@ module GraphQL
|
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
|
+
# @param new_has_no_fields [Boolean] Call with `true` to make this Object type ignore the requirement to have any defined fields.
|
83
|
+
# @return [void]
|
84
|
+
def has_no_fields(new_has_no_fields)
|
85
|
+
@has_no_fields = new_has_no_fields
|
86
|
+
nil
|
87
|
+
end
|
88
|
+
|
89
|
+
# @return [Boolean] `true` if `has_no_fields(true)` was configued
|
90
|
+
def has_no_fields?
|
91
|
+
@has_no_fields
|
92
|
+
end
|
93
|
+
|
82
94
|
# @return [Hash<String => GraphQL::Schema::Field, Array<GraphQL::Schema::Field>>] Fields defined on this class _specifically_, not parent classes
|
83
95
|
def own_fields
|
84
96
|
@own_fields ||= {}
|
@@ -99,11 +111,12 @@ module GraphQL
|
|
99
111
|
module InterfaceMethods
|
100
112
|
def get_field(field_name, context = GraphQL::Query::NullContext.instance)
|
101
113
|
warden = Warden.from_context(context)
|
114
|
+
skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
102
115
|
for ancestor in ancestors
|
103
116
|
if ancestor.respond_to?(:own_fields) &&
|
104
117
|
(f_entry = ancestor.own_fields[field_name]) &&
|
105
|
-
(
|
106
|
-
return
|
118
|
+
(skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden)))
|
119
|
+
return f_entry
|
107
120
|
end
|
108
121
|
end
|
109
122
|
nil
|
@@ -120,7 +133,7 @@ module GraphQL
|
|
120
133
|
# Choose the most local definition that passes `.visible?` --
|
121
134
|
# stop checking for fields by name once one has been found.
|
122
135
|
if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
|
123
|
-
visible_fields[field_name] = f
|
136
|
+
visible_fields[field_name] = f.ensure_loaded
|
124
137
|
end
|
125
138
|
end
|
126
139
|
end
|
@@ -134,13 +147,14 @@ module GraphQL
|
|
134
147
|
# Objects need to check that the interface implementation is visible, too
|
135
148
|
warden = Warden.from_context(context)
|
136
149
|
ancs = ancestors
|
150
|
+
skip_visible = context.respond_to?(:types) && context.types.is_a?(GraphQL::Schema::Visibility::Profile)
|
137
151
|
i = 0
|
138
152
|
while (ancestor = ancs[i])
|
139
153
|
if ancestor.respond_to?(:own_fields) &&
|
140
154
|
visible_interface_implementation?(ancestor, context, warden) &&
|
141
155
|
(f_entry = ancestor.own_fields[field_name]) &&
|
142
|
-
(
|
143
|
-
return
|
156
|
+
(skip_visible || (f_entry = Warden.visible_entry?(:visible_field?, f_entry, context, warden)))
|
157
|
+
return (skip_visible ? f_entry : f_entry.ensure_loaded)
|
144
158
|
end
|
145
159
|
i += 1
|
146
160
|
end
|
@@ -153,17 +167,22 @@ module GraphQL
|
|
153
167
|
warden = Warden.from_context(context)
|
154
168
|
# Local overrides take precedence over inherited fields
|
155
169
|
visible_fields = {}
|
170
|
+
had_any_fields_at_all = false
|
156
171
|
for ancestor in ancestors
|
157
172
|
if ancestor.respond_to?(:own_fields) && visible_interface_implementation?(ancestor, context, warden)
|
158
173
|
ancestor.own_fields.each do |field_name, fields_entry|
|
174
|
+
had_any_fields_at_all = true
|
159
175
|
# Choose the most local definition that passes `.visible?` --
|
160
176
|
# stop checking for fields by name once one has been found.
|
161
177
|
if !visible_fields.key?(field_name) && (f = Warden.visible_entry?(:visible_field?, fields_entry, context, warden))
|
162
|
-
visible_fields[field_name] = f
|
178
|
+
visible_fields[field_name] = f.ensure_loaded
|
163
179
|
end
|
164
180
|
end
|
165
181
|
end
|
166
182
|
end
|
183
|
+
if !had_any_fields_at_all && !has_no_fields?
|
184
|
+
warn(GraphQL::Schema::Object::FieldsAreRequiredError.new(self).message + "\n\nThis will raise an error in a future GraphQL-Ruby version.")
|
185
|
+
end
|
167
186
|
visible_fields
|
168
187
|
end
|
169
188
|
end
|
@@ -186,6 +205,7 @@ module GraphQL
|
|
186
205
|
subclass.class_eval do
|
187
206
|
@own_fields ||= nil
|
188
207
|
@field_class ||= nil
|
208
|
+
@has_no_fields ||= false
|
189
209
|
end
|
190
210
|
end
|
191
211
|
|
@@ -24,7 +24,7 @@ module GraphQL
|
|
24
24
|
implements(next_interface)
|
25
25
|
end
|
26
26
|
elsif int.is_a?(String) || int.is_a?(GraphQL::Schema::LateBoundType)
|
27
|
-
if options.
|
27
|
+
if !options.empty?
|
28
28
|
raise ArgumentError, "`implements(...)` doesn't support options with late-loaded types yet. Remove #{options} and open an issue to request this feature."
|
29
29
|
end
|
30
30
|
new_memberships << int
|
@@ -73,13 +73,13 @@ module GraphQL
|
|
73
73
|
def interfaces(context = GraphQL::Query::NullContext.instance)
|
74
74
|
visible_interfaces = super
|
75
75
|
inherited_interfaces = superclass.interfaces(context)
|
76
|
-
if visible_interfaces.
|
77
|
-
if inherited_interfaces.
|
76
|
+
if !visible_interfaces.empty?
|
77
|
+
if !inherited_interfaces.empty?
|
78
78
|
visible_interfaces.concat(inherited_interfaces)
|
79
79
|
visible_interfaces.uniq!
|
80
80
|
end
|
81
81
|
visible_interfaces
|
82
|
-
elsif inherited_interfaces.
|
82
|
+
elsif !inherited_interfaces.empty?
|
83
83
|
inherited_interfaces
|
84
84
|
else
|
85
85
|
EmptyObjects::EMPTY_ARRAY
|
@@ -7,7 +7,11 @@ module GraphQL
|
|
7
7
|
module HasUnresolvedTypeError
|
8
8
|
private
|
9
9
|
def add_unresolved_type_error(child_class)
|
10
|
-
child_class.
|
10
|
+
if child_class.name # Don't set this for anonymous classes
|
11
|
+
child_class.const_set(:UnresolvedTypeError, Class.new(GraphQL::UnresolvedTypeError))
|
12
|
+
else
|
13
|
+
child_class.const_set(:UnresolvedTypeError, UnresolvedTypeError)
|
14
|
+
end
|
11
15
|
end
|
12
16
|
end
|
13
17
|
end
|
@@ -8,6 +8,14 @@ module GraphQL
|
|
8
8
|
extend GraphQL::Schema::Member::HasFields
|
9
9
|
extend GraphQL::Schema::Member::HasInterfaces
|
10
10
|
|
11
|
+
# Raised when an Object doesn't have any field defined and hasn't explicitly opted out of this requirement
|
12
|
+
class FieldsAreRequiredError < GraphQL::Error
|
13
|
+
def initialize(object_type)
|
14
|
+
message = "Object types must have fields, but #{object_type.graphql_name} doesn't have any. Define a field for this type, remove it from your schema, or add `has_no_fields(true)` to its definition."
|
15
|
+
super(message)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
11
19
|
# @return [Object] the application object this type is wrapping
|
12
20
|
attr_reader :object
|
13
21
|
|