graphql 1.9.17 → 1.11.7
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +18 -2
- data/lib/generators/graphql/install_generator.rb +27 -0
- data/lib/generators/graphql/object_generator.rb +52 -8
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_enum.erb +2 -0
- data/lib/generators/graphql/templates/base_field.erb +2 -0
- data/lib/generators/graphql/templates/base_input_object.erb +2 -0
- data/lib/generators/graphql/templates/base_interface.erb +2 -0
- data/lib/generators/graphql/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/templates/base_object.erb +2 -0
- data/lib/generators/graphql/templates/base_scalar.erb +2 -0
- data/lib/generators/graphql/templates/base_union.erb +2 -0
- data/lib/generators/graphql/templates/enum.erb +2 -0
- data/lib/generators/graphql/templates/graphql_controller.erb +14 -10
- data/lib/generators/graphql/templates/interface.erb +2 -0
- data/lib/generators/graphql/templates/loader.erb +2 -0
- data/lib/generators/graphql/templates/mutation.erb +2 -0
- data/lib/generators/graphql/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/templates/object.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/scalar.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +10 -0
- data/lib/generators/graphql/templates/union.erb +3 -1
- data/lib/graphql/analysis/ast/field_usage.rb +1 -1
- data/lib/graphql/analysis/ast/query_complexity.rb +178 -67
- data/lib/graphql/analysis/ast/visitor.rb +3 -3
- data/lib/graphql/analysis/ast.rb +12 -11
- data/lib/graphql/argument.rb +10 -38
- data/lib/graphql/backtrace/table.rb +10 -2
- data/lib/graphql/backtrace/tracer.rb +2 -1
- data/lib/graphql/base_type.rb +4 -0
- data/lib/graphql/compatibility/execution_specification/specification_schema.rb +2 -2
- data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +5 -9
- data/lib/graphql/define/assign_enum_value.rb +1 -1
- data/lib/graphql/define/assign_global_id_field.rb +2 -2
- data/lib/graphql/define/assign_object_field.rb +3 -3
- data/lib/graphql/define/defined_object_proxy.rb +3 -0
- data/lib/graphql/define/instance_definable.rb +18 -108
- data/lib/graphql/directive/deprecated_directive.rb +1 -12
- data/lib/graphql/directive.rb +8 -1
- data/lib/graphql/enum_type.rb +5 -71
- data/lib/graphql/execution/directive_checks.rb +2 -2
- data/lib/graphql/execution/errors.rb +2 -3
- data/lib/graphql/execution/execute.rb +1 -1
- data/lib/graphql/execution/instrumentation.rb +1 -1
- data/lib/graphql/execution/interpreter/argument_value.rb +28 -0
- data/lib/graphql/execution/interpreter/arguments.rb +51 -0
- data/lib/graphql/execution/interpreter/arguments_cache.rb +79 -0
- data/lib/graphql/execution/interpreter/handles_raw_value.rb +25 -0
- data/lib/graphql/execution/interpreter/runtime.rb +227 -254
- data/lib/graphql/execution/interpreter.rb +34 -11
- data/lib/graphql/execution/lazy/lazy_method_map.rb +4 -0
- data/lib/graphql/execution/lookahead.rb +39 -114
- data/lib/graphql/execution/multiplex.rb +14 -5
- data/lib/graphql/field.rb +14 -118
- data/lib/graphql/filter.rb +1 -1
- data/lib/graphql/function.rb +1 -30
- data/lib/graphql/input_object_type.rb +6 -24
- data/lib/graphql/integer_decoding_error.rb +17 -0
- data/lib/graphql/interface_type.rb +7 -23
- data/lib/graphql/internal_representation/scope.rb +2 -2
- data/lib/graphql/internal_representation/visit.rb +2 -2
- data/lib/graphql/introspection/base_object.rb +2 -5
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +7 -7
- data/lib/graphql/introspection/field_type.rb +7 -3
- data/lib/graphql/introspection/input_value_type.rb +33 -9
- data/lib/graphql/introspection/introspection_query.rb +6 -92
- data/lib/graphql/introspection/schema_type.rb +4 -9
- data/lib/graphql/introspection/type_type.rb +11 -7
- data/lib/graphql/introspection.rb +96 -0
- data/lib/graphql/invalid_null_error.rb +18 -0
- data/lib/graphql/language/block_string.rb +24 -5
- data/lib/graphql/language/definition_slice.rb +21 -10
- data/lib/graphql/language/document_from_schema_definition.rb +89 -64
- data/lib/graphql/language/lexer.rb +7 -3
- data/lib/graphql/language/lexer.rl +7 -3
- data/lib/graphql/language/nodes.rb +52 -91
- data/lib/graphql/language/parser.rb +719 -717
- data/lib/graphql/language/parser.y +104 -98
- data/lib/graphql/language/printer.rb +1 -1
- data/lib/graphql/language/sanitized_printer.rb +222 -0
- data/lib/graphql/language/visitor.rb +2 -2
- data/lib/graphql/language.rb +2 -1
- data/lib/graphql/name_validator.rb +6 -7
- data/lib/graphql/non_null_type.rb +0 -10
- data/lib/graphql/object_type.rb +45 -56
- data/lib/graphql/pagination/active_record_relation_connection.rb +41 -0
- data/lib/graphql/pagination/array_connection.rb +77 -0
- data/lib/graphql/pagination/connection.rb +208 -0
- data/lib/graphql/pagination/connections.rb +145 -0
- data/lib/graphql/pagination/mongoid_relation_connection.rb +25 -0
- data/lib/graphql/pagination/relation_connection.rb +185 -0
- data/lib/graphql/pagination/sequel_dataset_connection.rb +28 -0
- data/lib/graphql/pagination.rb +6 -0
- data/lib/graphql/query/arguments.rb +4 -2
- data/lib/graphql/query/context.rb +36 -9
- data/lib/graphql/query/fingerprint.rb +26 -0
- data/lib/graphql/query/input_validation_result.rb +23 -6
- data/lib/graphql/query/literal_input.rb +30 -10
- data/lib/graphql/query/null_context.rb +5 -1
- data/lib/graphql/query/validation_pipeline.rb +4 -1
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +16 -7
- data/lib/graphql/query.rb +64 -15
- data/lib/graphql/rake_task/validate.rb +3 -0
- data/lib/graphql/rake_task.rb +9 -9
- data/lib/graphql/relay/array_connection.rb +10 -12
- data/lib/graphql/relay/base_connection.rb +23 -13
- data/lib/graphql/relay/connection_type.rb +2 -1
- data/lib/graphql/relay/edge_type.rb +1 -0
- data/lib/graphql/relay/edges_instrumentation.rb +1 -1
- data/lib/graphql/relay/mutation.rb +1 -86
- data/lib/graphql/relay/node.rb +2 -2
- data/lib/graphql/relay/range_add.rb +14 -5
- data/lib/graphql/relay/relation_connection.rb +8 -10
- data/lib/graphql/scalar_type.rb +15 -59
- data/lib/graphql/schema/argument.rb +113 -11
- data/lib/graphql/schema/base_64_encoder.rb +2 -0
- data/lib/graphql/schema/build_from_definition/resolve_map/default_resolve.rb +1 -1
- data/lib/graphql/schema/build_from_definition/resolve_map.rb +13 -5
- data/lib/graphql/schema/build_from_definition.rb +212 -190
- data/lib/graphql/schema/built_in_types.rb +5 -5
- data/lib/graphql/schema/default_type_error.rb +2 -0
- data/lib/graphql/schema/directive/deprecated.rb +18 -0
- data/lib/graphql/schema/directive/include.rb +1 -1
- data/lib/graphql/schema/directive/skip.rb +1 -1
- data/lib/graphql/schema/directive.rb +34 -3
- data/lib/graphql/schema/enum.rb +52 -4
- data/lib/graphql/schema/enum_value.rb +6 -1
- data/lib/graphql/schema/field/connection_extension.rb +44 -20
- data/lib/graphql/schema/field/scope_extension.rb +1 -1
- data/lib/graphql/schema/field.rb +200 -129
- data/lib/graphql/schema/find_inherited_value.rb +13 -0
- data/lib/graphql/schema/finder.rb +13 -11
- data/lib/graphql/schema/input_object.rb +131 -22
- data/lib/graphql/schema/interface.rb +26 -8
- data/lib/graphql/schema/introspection_system.rb +108 -37
- data/lib/graphql/schema/late_bound_type.rb +3 -2
- data/lib/graphql/schema/list.rb +47 -0
- data/lib/graphql/schema/loader.rb +134 -96
- data/lib/graphql/schema/member/base_dsl_methods.rb +29 -12
- data/lib/graphql/schema/member/build_type.rb +19 -5
- data/lib/graphql/schema/member/cached_graphql_definition.rb +5 -0
- data/lib/graphql/schema/member/has_arguments.rb +105 -5
- data/lib/graphql/schema/member/has_ast_node.rb +20 -0
- data/lib/graphql/schema/member/has_fields.rb +20 -10
- data/lib/graphql/schema/member/has_unresolved_type_error.rb +15 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +2 -2
- data/lib/graphql/schema/member/validates_input.rb +33 -0
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/mutation.rb +5 -1
- data/lib/graphql/schema/non_null.rb +30 -0
- data/lib/graphql/schema/object.rb +65 -12
- data/lib/graphql/schema/possible_types.rb +9 -4
- data/lib/graphql/schema/printer.rb +0 -15
- data/lib/graphql/schema/relay_classic_mutation.rb +5 -3
- data/lib/graphql/schema/resolver/has_payload_type.rb +5 -2
- data/lib/graphql/schema/resolver.rb +26 -18
- data/lib/graphql/schema/scalar.rb +27 -3
- data/lib/graphql/schema/subscription.rb +8 -18
- data/lib/graphql/schema/timeout.rb +29 -15
- data/lib/graphql/schema/traversal.rb +1 -1
- data/lib/graphql/schema/type_expression.rb +21 -13
- data/lib/graphql/schema/type_membership.rb +2 -2
- data/lib/graphql/schema/union.rb +37 -3
- data/lib/graphql/schema/unique_within_type.rb +1 -2
- data/lib/graphql/schema/validation.rb +10 -2
- data/lib/graphql/schema/warden.rb +115 -29
- data/lib/graphql/schema.rb +903 -195
- data/lib/graphql/static_validation/all_rules.rb +1 -0
- data/lib/graphql/static_validation/base_visitor.rb +10 -6
- data/lib/graphql/static_validation/literal_validator.rb +52 -27
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +43 -83
- data/lib/graphql/static_validation/rules/argument_literals_are_compatible_error.rb +17 -5
- data/lib/graphql/static_validation/rules/arguments_are_defined.rb +33 -25
- data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
- data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +4 -4
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +5 -5
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +29 -21
- data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
- data/lib/graphql/static_validation/rules/input_object_names_are_unique.rb +30 -0
- data/lib/graphql/static_validation/rules/input_object_names_are_unique_error.rb +30 -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 -5
- data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +12 -13
- data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +5 -6
- data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
- data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +5 -3
- data/lib/graphql/static_validation/type_stack.rb +2 -2
- data/lib/graphql/static_validation/validation_context.rb +1 -1
- data/lib/graphql/static_validation/validation_timeout_error.rb +25 -0
- data/lib/graphql/static_validation/validator.rb +30 -8
- data/lib/graphql/static_validation.rb +1 -0
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +89 -19
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +84 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +21 -0
- data/lib/graphql/subscriptions/event.rb +23 -5
- data/lib/graphql/subscriptions/instrumentation.rb +10 -5
- data/lib/graphql/subscriptions/serialize.rb +22 -4
- data/lib/graphql/subscriptions/subscription_root.rb +15 -5
- data/lib/graphql/subscriptions.rb +108 -35
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +14 -10
- data/lib/graphql/tracing/appoptics_tracing.rb +171 -0
- data/lib/graphql/tracing/appsignal_tracing.rb +8 -0
- data/lib/graphql/tracing/data_dog_tracing.rb +8 -0
- data/lib/graphql/tracing/new_relic_tracing.rb +9 -12
- data/lib/graphql/tracing/platform_tracing.rb +53 -9
- data/lib/graphql/tracing/prometheus_tracing/graphql_collector.rb +4 -1
- data/lib/graphql/tracing/prometheus_tracing.rb +8 -0
- data/lib/graphql/tracing/scout_tracing.rb +19 -0
- data/lib/graphql/tracing/skylight_tracing.rb +8 -0
- data/lib/graphql/tracing/statsd_tracing.rb +42 -0
- data/lib/graphql/tracing.rb +14 -34
- data/lib/graphql/types/big_int.rb +1 -1
- data/lib/graphql/types/int.rb +9 -2
- data/lib/graphql/types/iso_8601_date.rb +3 -3
- data/lib/graphql/types/iso_8601_date_time.rb +25 -10
- data/lib/graphql/types/relay/base_connection.rb +11 -7
- data/lib/graphql/types/relay/base_edge.rb +2 -1
- data/lib/graphql/types/string.rb +7 -1
- data/lib/graphql/unauthorized_error.rb +1 -1
- data/lib/graphql/union_type.rb +13 -28
- data/lib/graphql/unresolved_type_error.rb +2 -2
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +31 -6
- data/readme.md +1 -1
- metadata +34 -9
- data/lib/graphql/literal_validation_error.rb +0 -6
data/lib/graphql/schema/field.rb
CHANGED
@@ -13,8 +13,10 @@ module GraphQL
|
|
13
13
|
include GraphQL::Schema::Member::CachedGraphQLDefinition
|
14
14
|
include GraphQL::Schema::Member::AcceptsDefinition
|
15
15
|
include GraphQL::Schema::Member::HasArguments
|
16
|
+
include GraphQL::Schema::Member::HasAstNode
|
16
17
|
include GraphQL::Schema::Member::HasPath
|
17
18
|
extend GraphQL::Schema::FindInheritedValue
|
19
|
+
include GraphQL::Schema::FindInheritedValue::EmptyObjects
|
18
20
|
|
19
21
|
# @return [String] the GraphQL name for this field, camelized unless `camelize: false` is provided
|
20
22
|
attr_reader :name
|
@@ -34,10 +36,19 @@ module GraphQL
|
|
34
36
|
# @return [Symbol] The method on the type to look up
|
35
37
|
attr_reader :resolver_method
|
36
38
|
|
37
|
-
# @return [Class] The
|
38
|
-
|
39
|
+
# @return [Class] The thing this field was defined on (type, mutation, resolver)
|
40
|
+
attr_accessor :owner
|
39
41
|
|
40
|
-
# @return [
|
42
|
+
# @return [Class] The GraphQL type this field belongs to. (For fields defined on mutations, it's the payload type)
|
43
|
+
def owner_type
|
44
|
+
@owner_type ||= if owner < GraphQL::Schema::Mutation
|
45
|
+
owner.payload_type
|
46
|
+
else
|
47
|
+
owner
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [Symbol] the original name of the field, passed in by the user
|
41
52
|
attr_reader :original_name
|
42
53
|
|
43
54
|
# @return [Class, nil] The {Schema::Resolver} this field was derived from, if there is one
|
@@ -45,13 +56,18 @@ module GraphQL
|
|
45
56
|
@resolver_class
|
46
57
|
end
|
47
58
|
|
59
|
+
# @return [Boolean] Is this field a predefined introspection field?
|
60
|
+
def introspection?
|
61
|
+
@introspection
|
62
|
+
end
|
63
|
+
|
48
64
|
alias :mutation :resolver
|
49
65
|
|
50
66
|
# @return [Boolean] Apply tracing to this field? (Default: skip scalars, this is the override value)
|
51
67
|
attr_reader :trace
|
52
68
|
|
53
69
|
# @return [String, nil]
|
54
|
-
|
70
|
+
attr_accessor :subscription_scope
|
55
71
|
|
56
72
|
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
57
73
|
#
|
@@ -152,6 +168,14 @@ module GraphQL
|
|
152
168
|
end
|
153
169
|
end
|
154
170
|
|
171
|
+
# @return Boolean
|
172
|
+
attr_reader :relay_node_field
|
173
|
+
|
174
|
+
# @return [Boolean] Should we warn if this field's name conflicts with a built-in method?
|
175
|
+
def method_conflict_warning?
|
176
|
+
@method_conflict_warning
|
177
|
+
end
|
178
|
+
|
155
179
|
# @param name [Symbol] The underscore-cased version of this field name (will be camelized for the GraphQL API)
|
156
180
|
# @param type [Class, GraphQL::BaseType, Array] The return type of this field
|
157
181
|
# @param owner [Class] The type that this field belongs to
|
@@ -162,7 +186,8 @@ module GraphQL
|
|
162
186
|
# @param hash_key [String, Symbol] The hash key to lookup on the underlying object (if its a Hash) to resolve this field (defaults to `name` or `name.to_s`)
|
163
187
|
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
164
188
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
165
|
-
# @param
|
189
|
+
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
190
|
+
# @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
|
166
191
|
# @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
|
167
192
|
# @param resolve [<#call(obj, args, ctx)>] **deprecated** for compatibility with <1.8.0
|
168
193
|
# @param field [GraphQL::Field, GraphQL::Schema::Field] **deprecated** for compatibility with <1.8.0
|
@@ -175,7 +200,10 @@ module GraphQL
|
|
175
200
|
# @param subscription_scope [Symbol, String] A key in `context` which will be used to scope subscription payloads
|
176
201
|
# @param extensions [Array<Class, Hash<Class => Object>>] Named extensions to apply to this field (see also {#extension})
|
177
202
|
# @param trace [Boolean] If true, a {GraphQL::Tracing} tracer will measure this scalar field
|
178
|
-
|
203
|
+
# @param broadcastable [Boolean] Whether or not this field can be distributed in subscription broadcasts
|
204
|
+
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
205
|
+
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
206
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, field: nil, function: nil, description: nil, deprecation_reason: nil, method: nil, hash_key: nil, resolver_method: nil, resolve: nil, connection: nil, max_page_size: :not_given, scope: nil, introspection: false, camelize: true, trace: nil, complexity: 1, ast_node: nil, extras: EMPTY_ARRAY, extensions: EMPTY_ARRAY, connection_extension: self.class.connection_extension, resolver_class: nil, subscription_scope: nil, relay_node_field: false, relay_nodes_field: false, method_conflict_warning: true, broadcastable: nil, arguments: EMPTY_HASH, &definition_block)
|
179
207
|
if name.nil?
|
180
208
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
181
209
|
end
|
@@ -191,8 +219,9 @@ module GraphQL
|
|
191
219
|
raise ArgumentError, "keyword `extras:` may only be used with method-based resolve and class-based field such as mutation class, please remove `field:`, `function:` or `resolve:`"
|
192
220
|
end
|
193
221
|
@original_name = name
|
194
|
-
|
195
|
-
@
|
222
|
+
name_s = -name.to_s
|
223
|
+
@underscored_name = -Member::BuildType.underscore(name_s)
|
224
|
+
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
196
225
|
@description = description
|
197
226
|
if field.is_a?(GraphQL::Schema::Field)
|
198
227
|
raise ArgumentError, "Instead of passing a field as `field:`, use `add_field(field)` to add an already-defined field."
|
@@ -218,32 +247,34 @@ module GraphQL
|
|
218
247
|
end
|
219
248
|
|
220
249
|
# TODO: I think non-string/symbol hash keys are wrongly normalized (eg `1` will not work)
|
221
|
-
method_name = method || hash_key ||
|
222
|
-
resolver_method ||=
|
250
|
+
method_name = method || hash_key || name_s
|
251
|
+
resolver_method ||= name_s.to_sym
|
223
252
|
|
224
|
-
@method_str = method_name.to_s
|
253
|
+
@method_str = -method_name.to_s
|
225
254
|
@method_sym = method_name.to_sym
|
226
255
|
@resolver_method = resolver_method
|
227
256
|
@complexity = complexity
|
228
257
|
@return_type_expr = type
|
229
258
|
@return_type_null = null
|
230
259
|
@connection = connection
|
231
|
-
@
|
260
|
+
@has_max_page_size = max_page_size != :not_given
|
261
|
+
@max_page_size = max_page_size == :not_given ? nil : max_page_size
|
232
262
|
@introspection = introspection
|
233
263
|
@extras = extras
|
264
|
+
@broadcastable = broadcastable
|
234
265
|
@resolver_class = resolver_class
|
235
266
|
@scope = scope
|
236
267
|
@trace = trace
|
237
268
|
@relay_node_field = relay_node_field
|
238
269
|
@relay_nodes_field = relay_nodes_field
|
270
|
+
@ast_node = ast_node
|
271
|
+
@method_conflict_warning = method_conflict_warning
|
239
272
|
|
240
|
-
# Override the default from HasArguments
|
241
|
-
@own_arguments = {}
|
242
273
|
arguments.each do |name, arg|
|
243
274
|
if arg.is_a?(Hash)
|
244
275
|
argument(name: name, **arg)
|
245
276
|
else
|
246
|
-
|
277
|
+
add_argument(arg)
|
247
278
|
end
|
248
279
|
end
|
249
280
|
|
@@ -251,7 +282,7 @@ module GraphQL
|
|
251
282
|
@subscription_scope = subscription_scope
|
252
283
|
|
253
284
|
# Do this last so we have as much context as possible when initializing them:
|
254
|
-
@extensions =
|
285
|
+
@extensions = EMPTY_ARRAY
|
255
286
|
if extensions.any?
|
256
287
|
self.extensions(extensions)
|
257
288
|
end
|
@@ -262,8 +293,8 @@ module GraphQL
|
|
262
293
|
end
|
263
294
|
# The problem with putting this after the definition_block
|
264
295
|
# is that it would override arguments
|
265
|
-
if connection?
|
266
|
-
self.extension(
|
296
|
+
if connection? && connection_extension
|
297
|
+
self.extension(connection_extension)
|
267
298
|
end
|
268
299
|
|
269
300
|
if definition_block
|
@@ -275,6 +306,13 @@ module GraphQL
|
|
275
306
|
end
|
276
307
|
end
|
277
308
|
|
309
|
+
# If true, subscription updates with this field can be shared between viewers
|
310
|
+
# @return [Boolean, nil]
|
311
|
+
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
312
|
+
def broadcastable?
|
313
|
+
@broadcastable
|
314
|
+
end
|
315
|
+
|
278
316
|
# @param text [String]
|
279
317
|
# @return [String]
|
280
318
|
def description(text = nil)
|
@@ -305,6 +343,9 @@ module GraphQL
|
|
305
343
|
# Read the value
|
306
344
|
@extensions
|
307
345
|
else
|
346
|
+
if @extensions.frozen?
|
347
|
+
@extensions = @extensions.dup
|
348
|
+
end
|
308
349
|
new_extensions.each do |extension|
|
309
350
|
if extension.is_a?(Hash)
|
310
351
|
extension = extension.to_a[0]
|
@@ -342,12 +383,15 @@ module GraphQL
|
|
342
383
|
# Read the value
|
343
384
|
@extras
|
344
385
|
else
|
386
|
+
if @extras.frozen?
|
387
|
+
@extras = @extras.dup
|
388
|
+
end
|
345
389
|
# Append to the set of extras on this field
|
346
390
|
@extras.concat(new_extras)
|
347
391
|
end
|
348
392
|
end
|
349
393
|
|
350
|
-
def complexity(new_complexity)
|
394
|
+
def complexity(new_complexity = nil)
|
351
395
|
case new_complexity
|
352
396
|
when Proc
|
353
397
|
if new_complexity.parameters.size != 3
|
@@ -360,12 +404,19 @@ module GraphQL
|
|
360
404
|
end
|
361
405
|
when Numeric
|
362
406
|
@complexity = new_complexity
|
407
|
+
when nil
|
408
|
+
@complexity
|
363
409
|
else
|
364
410
|
raise("Invalid complexity: #{new_complexity.inspect} on #{@name}")
|
365
411
|
end
|
366
412
|
end
|
367
413
|
|
368
|
-
# @return [
|
414
|
+
# @return [Boolean] True if this field's {#max_page_size} should override the schema default.
|
415
|
+
def has_max_page_size?
|
416
|
+
@has_max_page_size
|
417
|
+
end
|
418
|
+
|
419
|
+
# @return [Integer, nil] Applied to connections if {#has_max_page_size?}
|
369
420
|
attr_reader :max_page_size
|
370
421
|
|
371
422
|
# @return [GraphQL::Field]
|
@@ -416,6 +467,7 @@ module GraphQL
|
|
416
467
|
field_defn.introspection = @introspection
|
417
468
|
field_defn.complexity = @complexity
|
418
469
|
field_defn.subscription_scope = @subscription_scope
|
470
|
+
field_defn.ast_node = ast_node
|
419
471
|
|
420
472
|
arguments.each do |name, defn|
|
421
473
|
arg_graphql = defn.to_graphql
|
@@ -433,14 +485,25 @@ module GraphQL
|
|
433
485
|
|
434
486
|
# Ok, `self` isn't a class, but this is for consistency with the classes
|
435
487
|
field_defn.metadata[:type_class] = self
|
436
|
-
|
488
|
+
field_defn.arguments_class = GraphQL::Query::Arguments.construct_arguments_class(field_defn)
|
437
489
|
field_defn
|
438
490
|
end
|
439
491
|
|
492
|
+
attr_writer :type
|
493
|
+
|
440
494
|
def type
|
441
|
-
@type ||=
|
442
|
-
|
443
|
-
|
495
|
+
@type ||= if @function
|
496
|
+
Member::BuildType.parse_type(@function.type, null: false)
|
497
|
+
elsif @field
|
498
|
+
Member::BuildType.parse_type(@field.type, null: false)
|
499
|
+
else
|
500
|
+
Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
501
|
+
end
|
502
|
+
rescue GraphQL::Schema::InvalidDocumentError => err
|
503
|
+
# Let this propagate up
|
504
|
+
raise err
|
505
|
+
rescue StandardError => err
|
506
|
+
raise ArgumentError, "Failed to build return type for #{@owner.graphql_name}.#{name} from #{@return_type_expr.inspect}: (#{err.class}) #{err.message}", err.backtrace
|
444
507
|
end
|
445
508
|
|
446
509
|
def visible?(context)
|
@@ -459,14 +522,14 @@ module GraphQL
|
|
459
522
|
end
|
460
523
|
end
|
461
524
|
|
462
|
-
def authorized?(object, context)
|
525
|
+
def authorized?(object, args, context)
|
463
526
|
if @resolver_class
|
464
527
|
# The resolver will check itself during `resolve()`
|
465
528
|
@resolver_class.authorized?(object, context)
|
466
529
|
else
|
467
530
|
# Faster than `.any?`
|
468
531
|
arguments.each_value do |arg|
|
469
|
-
if !arg.authorized?(object, context)
|
532
|
+
if args.key?(arg.keyword) && !arg.authorized?(object, args[arg.keyword], context)
|
470
533
|
return false
|
471
534
|
end
|
472
535
|
end
|
@@ -485,21 +548,22 @@ module GraphQL
|
|
485
548
|
# Some legacy fields can have `nil` here, not exactly sure why.
|
486
549
|
# @see https://github.com/rmosolgo/graphql-ruby/issues/1990 before removing
|
487
550
|
inner_obj = after_obj && after_obj.object
|
488
|
-
|
489
|
-
|
490
|
-
|
491
|
-
|
492
|
-
|
493
|
-
|
494
|
-
|
495
|
-
|
551
|
+
ctx.schema.after_lazy(to_ruby_args(after_obj, args, ctx)) do |ruby_args|
|
552
|
+
if authorized?(inner_obj, ruby_args, query_ctx)
|
553
|
+
# Then if it passed, resolve the field
|
554
|
+
if @resolve_proc
|
555
|
+
# Might be nil, still want to call the func in that case
|
556
|
+
with_extensions(inner_obj, ruby_args, query_ctx) do |extended_obj, extended_args|
|
557
|
+
# Pass the GraphQL args here for compatibility:
|
558
|
+
@resolve_proc.call(extended_obj, args, ctx)
|
559
|
+
end
|
560
|
+
else
|
561
|
+
public_send_field(after_obj, ruby_args, query_ctx)
|
496
562
|
end
|
497
563
|
else
|
498
|
-
|
564
|
+
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
565
|
+
query_ctx.schema.unauthorized_field(err)
|
499
566
|
end
|
500
|
-
else
|
501
|
-
err = GraphQL::UnauthorizedFieldError.new(object: inner_obj, type: obj.class, context: ctx, field: self)
|
502
|
-
query_ctx.schema.unauthorized_field(err)
|
503
567
|
end
|
504
568
|
end
|
505
569
|
end
|
@@ -516,34 +580,13 @@ module GraphQL
|
|
516
580
|
begin
|
517
581
|
# Unwrap the GraphQL object to get the application object.
|
518
582
|
application_object = object.object
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
else
|
526
|
-
extended_obj
|
527
|
-
end
|
528
|
-
@resolver_class.new(object: resolver_obj, context: ctx, field: self)
|
529
|
-
else
|
530
|
-
extended_obj
|
531
|
-
end
|
532
|
-
|
533
|
-
if field_receiver.respond_to?(@resolver_method)
|
534
|
-
# Call the method with kwargs, if there are any
|
535
|
-
if extended_args.any?
|
536
|
-
field_receiver.public_send(@resolver_method, **extended_args)
|
537
|
-
else
|
538
|
-
field_receiver.public_send(@resolver_method)
|
539
|
-
end
|
540
|
-
else
|
541
|
-
resolve_field_method(field_receiver, extended_args, ctx)
|
542
|
-
end
|
583
|
+
ctx.schema.after_lazy(self.authorized?(application_object, args, ctx)) do |is_authorized|
|
584
|
+
if is_authorized
|
585
|
+
public_send_field(object, args, ctx)
|
586
|
+
else
|
587
|
+
err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
588
|
+
ctx.schema.unauthorized_field(err)
|
543
589
|
end
|
544
|
-
else
|
545
|
-
err = GraphQL::UnauthorizedFieldError.new(object: application_object, type: object.class, context: ctx, field: self)
|
546
|
-
ctx.schema.unauthorized_field(err)
|
547
590
|
end
|
548
591
|
rescue GraphQL::UnauthorizedFieldError => err
|
549
592
|
err.field ||= self
|
@@ -555,46 +598,9 @@ module GraphQL
|
|
555
598
|
err
|
556
599
|
end
|
557
600
|
|
558
|
-
# Find a way to resolve this field, checking:
|
559
|
-
#
|
560
|
-
# - Hash keys, if the wrapped object is a hash;
|
561
|
-
# - A method on the wrapped object;
|
562
|
-
# - Or, raise not implemented.
|
563
|
-
#
|
564
|
-
# This can be overridden by defining a method on the object type.
|
565
|
-
# @param obj [GraphQL::Schema::Object]
|
566
|
-
# @param ruby_kwargs [Hash<Symbol => Object>]
|
567
|
-
# @param ctx [GraphQL::Query::Context]
|
568
|
-
def resolve_field_method(obj, ruby_kwargs, ctx)
|
569
|
-
if obj.object.is_a?(Hash)
|
570
|
-
inner_object = obj.object
|
571
|
-
if inner_object.key?(@method_sym)
|
572
|
-
inner_object[@method_sym]
|
573
|
-
else
|
574
|
-
inner_object[@method_str]
|
575
|
-
end
|
576
|
-
elsif obj.object.respond_to?(@method_sym)
|
577
|
-
if ruby_kwargs.any?
|
578
|
-
obj.object.public_send(@method_sym, **ruby_kwargs)
|
579
|
-
else
|
580
|
-
obj.object.public_send(@method_sym)
|
581
|
-
end
|
582
|
-
else
|
583
|
-
raise <<-ERR
|
584
|
-
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
585
|
-
|
586
|
-
- `#{obj.class}##{@resolver_method}`, which did not exist
|
587
|
-
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
588
|
-
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
589
|
-
|
590
|
-
To implement this field, define one of the methods above (and check for typos)
|
591
|
-
ERR
|
592
|
-
end
|
593
|
-
end
|
594
|
-
|
595
601
|
# @param ctx [GraphQL::Query::Context::FieldResolutionContext]
|
596
602
|
def fetch_extra(extra_name, ctx)
|
597
|
-
if extra_name != :path && respond_to?(extra_name)
|
603
|
+
if extra_name != :path && extra_name != :ast_node && respond_to?(extra_name)
|
598
604
|
self.public_send(extra_name)
|
599
605
|
elsif ctx.respond_to?(extra_name)
|
600
606
|
ctx.public_send(extra_name)
|
@@ -617,11 +623,36 @@ module GraphQL
|
|
617
623
|
if graphql_args.any? || @extras.any?
|
618
624
|
# Splat the GraphQL::Arguments to Ruby keyword arguments
|
619
625
|
ruby_kwargs = graphql_args.to_kwargs
|
626
|
+
maybe_lazies = []
|
620
627
|
# Apply any `prepare` methods. Not great code organization, can this go somewhere better?
|
621
628
|
arguments.each do |name, arg_defn|
|
622
629
|
ruby_kwargs_key = arg_defn.keyword
|
623
|
-
|
624
|
-
|
630
|
+
|
631
|
+
if ruby_kwargs.key?(ruby_kwargs_key)
|
632
|
+
loads = arg_defn.loads
|
633
|
+
value = ruby_kwargs[ruby_kwargs_key]
|
634
|
+
loaded_value = if loads && !arg_defn.from_resolver?
|
635
|
+
if arg_defn.type.list?
|
636
|
+
loaded_values = value.map { |val| load_application_object(arg_defn, loads, val, field_ctx.query.context) }
|
637
|
+
field_ctx.schema.after_any_lazies(loaded_values) { |result| result }
|
638
|
+
else
|
639
|
+
load_application_object(arg_defn, loads, value, field_ctx.query.context)
|
640
|
+
end
|
641
|
+
elsif arg_defn.type.list? && value.is_a?(Array)
|
642
|
+
field_ctx.schema.after_any_lazies(value, &:itself)
|
643
|
+
else
|
644
|
+
value
|
645
|
+
end
|
646
|
+
|
647
|
+
maybe_lazies << field_ctx.schema.after_lazy(loaded_value) do |loaded_value|
|
648
|
+
prepared_value = if arg_defn.prepare
|
649
|
+
arg_defn.prepare_value(obj, loaded_value)
|
650
|
+
else
|
651
|
+
loaded_value
|
652
|
+
end
|
653
|
+
|
654
|
+
ruby_kwargs[ruby_kwargs_key] = prepared_value
|
655
|
+
end
|
625
656
|
end
|
626
657
|
end
|
627
658
|
|
@@ -629,30 +660,60 @@ module GraphQL
|
|
629
660
|
ruby_kwargs[extra_arg] = fetch_extra(extra_arg, field_ctx)
|
630
661
|
end
|
631
662
|
|
632
|
-
|
663
|
+
field_ctx.schema.after_any_lazies(maybe_lazies) do
|
664
|
+
ruby_kwargs
|
665
|
+
end
|
633
666
|
else
|
634
667
|
NO_ARGS
|
635
668
|
end
|
636
669
|
end
|
637
670
|
|
638
|
-
def public_send_field(
|
639
|
-
query_ctx
|
640
|
-
with_extensions(obj, ruby_kwargs, query_ctx) do |extended_obj, extended_args|
|
671
|
+
def public_send_field(unextended_obj, unextended_ruby_kwargs, query_ctx)
|
672
|
+
with_extensions(unextended_obj, unextended_ruby_kwargs, query_ctx) do |obj, ruby_kwargs|
|
641
673
|
if @resolver_class
|
642
|
-
if
|
643
|
-
|
674
|
+
if obj.is_a?(GraphQL::Schema::Object)
|
675
|
+
obj = obj.object
|
644
676
|
end
|
645
|
-
|
677
|
+
obj = @resolver_class.new(object: obj, context: query_ctx, field: self)
|
646
678
|
end
|
647
679
|
|
648
|
-
|
649
|
-
|
650
|
-
|
680
|
+
# Find a way to resolve this field, checking:
|
681
|
+
#
|
682
|
+
# - A method on the type instance;
|
683
|
+
# - Hash keys, if the wrapped object is a hash;
|
684
|
+
# - A method on the wrapped object;
|
685
|
+
# - Or, raise not implemented.
|
686
|
+
#
|
687
|
+
if obj.respond_to?(@resolver_method)
|
688
|
+
# Call the method with kwargs, if there are any
|
689
|
+
if ruby_kwargs.any?
|
690
|
+
obj.public_send(@resolver_method, **ruby_kwargs)
|
691
|
+
else
|
692
|
+
obj.public_send(@resolver_method)
|
693
|
+
end
|
694
|
+
elsif obj.object.is_a?(Hash)
|
695
|
+
inner_object = obj.object
|
696
|
+
if inner_object.key?(@method_sym)
|
697
|
+
inner_object[@method_sym]
|
698
|
+
else
|
699
|
+
inner_object[@method_str]
|
700
|
+
end
|
701
|
+
elsif obj.object.respond_to?(@method_sym)
|
702
|
+
if ruby_kwargs.any?
|
703
|
+
obj.object.public_send(@method_sym, **ruby_kwargs)
|
651
704
|
else
|
652
|
-
|
705
|
+
obj.object.public_send(@method_sym)
|
653
706
|
end
|
654
707
|
else
|
655
|
-
|
708
|
+
raise <<-ERR
|
709
|
+
Failed to implement #{@owner.graphql_name}.#{@name}, tried:
|
710
|
+
|
711
|
+
- `#{obj.class}##{@resolver_method}`, which did not exist
|
712
|
+
- `#{obj.object.class}##{@method_sym}`, which did not exist
|
713
|
+
- Looking up hash key `#{@method_sym.inspect}` or `#{@method_str.inspect}` on `#{obj.object}`, but it wasn't a Hash
|
714
|
+
|
715
|
+
To implement this field, define one of the methods above (and check for typos)
|
716
|
+
ERR
|
656
717
|
end
|
657
718
|
end
|
658
719
|
end
|
@@ -664,32 +725,42 @@ module GraphQL
|
|
664
725
|
if @extensions.empty?
|
665
726
|
yield(obj, args)
|
666
727
|
else
|
667
|
-
#
|
668
|
-
|
669
|
-
|
670
|
-
|
671
|
-
|
672
|
-
|
673
|
-
yield(extended_obj, extended_args)
|
728
|
+
# This is a hack to get the _last_ value for extended obj and args,
|
729
|
+
# in case one of the extensions doesn't `yield`.
|
730
|
+
# (There's another implementation that uses multiple-return, but I'm wary of the perf cost of the extra arrays)
|
731
|
+
extended = { args: args, obj: obj, memos: nil }
|
732
|
+
value = run_extensions_before_resolve(obj, args, ctx, extended) do |obj, args|
|
733
|
+
yield(obj, args)
|
674
734
|
end
|
675
735
|
|
736
|
+
extended_obj = extended[:obj]
|
737
|
+
extended_args = extended[:args]
|
738
|
+
memos = extended[:memos] || EMPTY_HASH
|
739
|
+
|
676
740
|
ctx.schema.after_lazy(value) do |resolved_value|
|
677
|
-
|
741
|
+
idx = 0
|
742
|
+
@extensions.each do |ext|
|
678
743
|
memo = memos[idx]
|
679
744
|
# TODO after_lazy?
|
680
|
-
resolved_value = ext.after_resolve(object:
|
745
|
+
resolved_value = ext.after_resolve(object: extended_obj, arguments: extended_args, context: ctx, value: resolved_value, memo: memo)
|
746
|
+
idx += 1
|
681
747
|
end
|
682
748
|
resolved_value
|
683
749
|
end
|
684
750
|
end
|
685
751
|
end
|
686
752
|
|
687
|
-
def run_extensions_before_resolve(
|
753
|
+
def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
|
688
754
|
extension = @extensions[idx]
|
689
755
|
if extension
|
690
756
|
extension.resolve(object: obj, arguments: args, context: ctx) do |extended_obj, extended_args, memo|
|
691
|
-
|
692
|
-
|
757
|
+
if memo
|
758
|
+
memos = extended[:memos] ||= {}
|
759
|
+
memos[idx] = memo
|
760
|
+
end
|
761
|
+
extended[:obj] = extended_obj
|
762
|
+
extended[:args] = extended_args
|
763
|
+
run_extensions_before_resolve(extended_obj, extended_args, ctx, extended, idx: idx + 1) { |o, a| yield(o, a) }
|
693
764
|
end
|
694
765
|
else
|
695
766
|
yield(obj, args)
|
@@ -1,6 +1,19 @@
|
|
1
1
|
module GraphQL
|
2
2
|
class Schema
|
3
3
|
module FindInheritedValue
|
4
|
+
module EmptyObjects
|
5
|
+
EMPTY_HASH = {}.freeze
|
6
|
+
EMPTY_ARRAY = [].freeze
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.extended(child_cls)
|
10
|
+
child_cls.singleton_class.include(EmptyObjects)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.included(child_cls)
|
14
|
+
child_cls.include(EmptyObjects)
|
15
|
+
end
|
16
|
+
|
4
17
|
private
|
5
18
|
|
6
19
|
def find_inherited_value(method_name, default_value = nil)
|
@@ -38,7 +38,7 @@ module GraphQL
|
|
38
38
|
|
39
39
|
find_in_directive(directive, path: path)
|
40
40
|
else
|
41
|
-
type = schema.
|
41
|
+
type = schema.get_type(type_or_directive)
|
42
42
|
|
43
43
|
if type.nil?
|
44
44
|
raise MemberNotFoundError, "Could not find type `#{type_or_directive}` in schema."
|
@@ -66,22 +66,24 @@ module GraphQL
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def find_in_type(type, path:)
|
69
|
-
case type
|
70
|
-
when
|
69
|
+
case type.kind.name
|
70
|
+
when "OBJECT"
|
71
71
|
find_in_fields_type(type, kind: "object", path: path)
|
72
|
-
when
|
72
|
+
when "INTERFACE"
|
73
73
|
find_in_fields_type(type, kind: "interface", path: path)
|
74
|
-
when
|
74
|
+
when "INPUT_OBJECT"
|
75
75
|
find_in_input_object(type, path: path)
|
76
|
-
when
|
76
|
+
when "UNION"
|
77
77
|
# Error out if path that was provided is too long
|
78
78
|
# i.e UnionType.PossibleType.aField
|
79
79
|
# Use PossibleType.aField instead.
|
80
80
|
if invalid = path.first
|
81
81
|
raise MemberNotFoundError, "Cannot select union possible type `#{invalid}`. Select the type directly instead."
|
82
82
|
end
|
83
|
-
when
|
83
|
+
when "ENUM"
|
84
84
|
find_in_enum_type(type, path: path)
|
85
|
+
else
|
86
|
+
raise "Unexpected find_in_type: #{type.inspect} (#{path})"
|
85
87
|
end
|
86
88
|
end
|
87
89
|
|
@@ -90,7 +92,7 @@ module GraphQL
|
|
90
92
|
field = schema.get_field(type, field_name)
|
91
93
|
|
92
94
|
if field.nil?
|
93
|
-
raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type}`."
|
95
|
+
raise MemberNotFoundError, "Could not find field `#{field_name}` on #{kind} type `#{type.graphql_name}`."
|
94
96
|
end
|
95
97
|
|
96
98
|
return field if path.empty?
|
@@ -117,10 +119,10 @@ module GraphQL
|
|
117
119
|
|
118
120
|
def find_in_input_object(input_object, path:)
|
119
121
|
field_name = path.shift
|
120
|
-
input_field = input_object.
|
122
|
+
input_field = input_object.arguments[field_name]
|
121
123
|
|
122
124
|
if input_field.nil?
|
123
|
-
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object}`."
|
125
|
+
raise MemberNotFoundError, "Could not find input field `#{field_name}` on input object type `#{input_object.graphql_name}`."
|
124
126
|
end
|
125
127
|
|
126
128
|
# Error out if path that was provided is too long
|
@@ -137,7 +139,7 @@ module GraphQL
|
|
137
139
|
enum_value = enum_type.values[value_name]
|
138
140
|
|
139
141
|
if enum_value.nil?
|
140
|
-
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type}`."
|
142
|
+
raise MemberNotFoundError, "Could not find enum value `#{value_name}` on enum type `#{enum_type.graphql_name}`."
|
141
143
|
end
|
142
144
|
|
143
145
|
# Error out if path that was provided is too long
|