graphql 1.9.17 → 1.11.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of graphql might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/lib/generators/graphql/core.rb +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
|