graphql 2.3.7 → 2.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install_generator.rb +46 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_resolver.erb +2 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/field_usage.rb +1 -1
- data/lib/graphql/analysis/query_complexity.rb +3 -3
- data/lib/graphql/analysis/visitor.rb +8 -7
- data/lib/graphql/analysis.rb +4 -4
- data/lib/graphql/autoload.rb +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
|
|