graphql 2.0.31 → 2.5.18
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.
- checksums.yaml +4 -4
- data/lib/generators/graphql/install/mutation_root_generator.rb +2 -2
- data/lib/generators/graphql/install/templates/base_mutation.erb +2 -0
- data/lib/generators/graphql/install/templates/mutation_type.erb +2 -0
- data/lib/generators/graphql/install_generator.rb +49 -0
- data/lib/generators/graphql/orm_mutations_base.rb +1 -1
- data/lib/generators/graphql/templates/base_argument.erb +2 -0
- data/lib/generators/graphql/templates/base_connection.erb +2 -0
- data/lib/generators/graphql/templates/base_edge.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_object.erb +2 -0
- data/lib/generators/graphql/templates/base_resolver.erb +8 -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/graphql_controller.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/node_type.erb +2 -0
- data/lib/generators/graphql/templates/query_type.erb +2 -0
- data/lib/generators/graphql/templates/schema.erb +5 -0
- data/lib/generators/graphql/type_generator.rb +1 -1
- data/lib/graphql/analysis/analyzer.rb +90 -0
- data/lib/graphql/analysis/field_usage.rb +82 -0
- data/lib/graphql/analysis/max_query_complexity.rb +20 -0
- data/lib/graphql/analysis/max_query_depth.rb +20 -0
- data/lib/graphql/analysis/query_complexity.rb +263 -0
- data/lib/graphql/analysis/query_depth.rb +58 -0
- data/lib/graphql/analysis/visitor.rb +280 -0
- data/lib/graphql/analysis.rb +95 -1
- data/lib/graphql/autoload.rb +38 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/coercion_error.rb +1 -9
- data/lib/graphql/current.rb +57 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/limiters.rb +93 -0
- data/lib/graphql/dashboard/operation_store.rb +199 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
- data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
- data/lib/graphql/dashboard/statics/charts.min.css +1 -0
- data/lib/graphql/dashboard/statics/dashboard.css +30 -0
- data/lib/graphql/dashboard/statics/dashboard.js +143 -0
- data/lib/graphql/dashboard/statics/header-icon.png +0 -0
- data/lib/graphql/dashboard/statics/icon.png +0 -0
- data/lib/graphql/dashboard/subscriptions.rb +96 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
- data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
- data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
- data/lib/graphql/dashboard.rb +161 -0
- data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
- data/lib/graphql/dataloader/active_record_source.rb +47 -0
- data/lib/graphql/dataloader/async_dataloader.rb +112 -0
- data/lib/graphql/dataloader/null_dataloader.rb +51 -10
- data/lib/graphql/dataloader/request.rb +5 -0
- data/lib/graphql/dataloader/source.rb +35 -12
- data/lib/graphql/dataloader.rb +224 -149
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/duration_encoding_error.rb +16 -0
- data/lib/graphql/execution/interpreter/argument_value.rb +5 -1
- data/lib/graphql/execution/interpreter/arguments_cache.rb +5 -10
- data/lib/graphql/execution/interpreter/resolve.rb +23 -25
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +228 -0
- data/lib/graphql/execution/interpreter/runtime.rb +363 -434
- data/lib/graphql/execution/interpreter.rb +91 -164
- data/lib/graphql/execution/lookahead.rb +105 -31
- data/lib/graphql/execution/multiplex.rb +6 -5
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +1 -1
- data/lib/graphql/introspection/dynamic_fields.rb +1 -1
- data/lib/graphql/introspection/entry_points.rb +11 -5
- data/lib/graphql/introspection/field_type.rb +1 -1
- data/lib/graphql/introspection/schema_type.rb +8 -11
- data/lib/graphql/introspection/type_type.rb +5 -5
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +20 -17
- data/lib/graphql/language/block_string.rb +34 -18
- data/lib/graphql/language/cache.rb +13 -0
- data/lib/graphql/language/comment.rb +18 -0
- data/lib/graphql/language/definition_slice.rb +1 -1
- data/lib/graphql/language/document_from_schema_definition.rb +90 -61
- data/lib/graphql/language/lexer.rb +319 -193
- data/lib/graphql/language/nodes.rb +139 -77
- data/lib/graphql/language/parser.rb +807 -1985
- data/lib/graphql/language/printer.rb +324 -151
- data/lib/graphql/language/sanitized_printer.rb +21 -23
- data/lib/graphql/language/static_visitor.rb +171 -0
- data/lib/graphql/language/visitor.rb +62 -119
- data/lib/graphql/language.rb +71 -1
- data/lib/graphql/load_application_object_failed_error.rb +5 -1
- data/lib/graphql/pagination/array_connection.rb +6 -6
- data/lib/graphql/pagination/connection.rb +28 -1
- data/lib/graphql/pagination/mongoid_relation_connection.rb +1 -2
- data/lib/graphql/query/context/scoped_context.rb +101 -0
- data/lib/graphql/query/context.rb +81 -144
- data/lib/graphql/query/null_context.rb +7 -16
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query/validation_pipeline.rb +4 -4
- data/lib/graphql/query/variable_validation_error.rb +1 -1
- data/lib/graphql/query/variables.rb +3 -3
- data/lib/graphql/query.rb +126 -81
- data/lib/graphql/railtie.rb +16 -6
- data/lib/graphql/rake_task.rb +3 -12
- data/lib/graphql/rubocop/graphql/base_cop.rb +1 -1
- 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 +26 -13
- data/lib/graphql/schema/always_visible.rb +7 -2
- data/lib/graphql/schema/argument.rb +73 -14
- data/lib/graphql/schema/base_64_encoder.rb +3 -5
- data/lib/graphql/schema/build_from_definition.rb +123 -66
- data/lib/graphql/schema/directive/flagged.rb +4 -2
- data/lib/graphql/schema/directive/one_of.rb +12 -0
- data/lib/graphql/schema/directive/specified_by.rb +14 -0
- data/lib/graphql/schema/directive.rb +55 -3
- data/lib/graphql/schema/enum.rb +110 -27
- data/lib/graphql/schema/enum_value.rb +11 -3
- data/lib/graphql/schema/field/connection_extension.rb +2 -16
- data/lib/graphql/schema/field/scope_extension.rb +8 -1
- data/lib/graphql/schema/field.rb +173 -119
- data/lib/graphql/schema/field_extension.rb +1 -1
- data/lib/graphql/schema/has_single_input_argument.rb +160 -0
- data/lib/graphql/schema/input_object.rb +123 -65
- data/lib/graphql/schema/interface.rb +34 -16
- data/lib/graphql/schema/introspection_system.rb +8 -17
- data/lib/graphql/schema/late_bound_type.rb +4 -0
- data/lib/graphql/schema/list.rb +3 -3
- data/lib/graphql/schema/loader.rb +3 -4
- data/lib/graphql/schema/member/base_dsl_methods.rb +17 -1
- data/lib/graphql/schema/member/has_arguments.rb +132 -100
- data/lib/graphql/schema/member/has_dataloader.rb +62 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +5 -5
- data/lib/graphql/schema/member/has_fields.rb +111 -16
- data/lib/graphql/schema/member/has_interfaces.rb +27 -13
- 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/member/relay_shortcuts.rb +1 -1
- data/lib/graphql/schema/member/scoped.rb +19 -0
- data/lib/graphql/schema/member/type_system_helpers.rb +18 -5
- data/lib/graphql/schema/member/validates_input.rb +3 -3
- data/lib/graphql/schema/member.rb +1 -0
- data/lib/graphql/schema/mutation.rb +7 -0
- data/lib/graphql/schema/object.rb +33 -8
- data/lib/graphql/schema/printer.rb +9 -7
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/relay_classic_mutation.rb +6 -129
- data/lib/graphql/schema/resolver.rb +45 -31
- data/lib/graphql/schema/scalar.rb +4 -9
- data/lib/graphql/schema/subscription.rb +63 -10
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/type_expression.rb +2 -2
- data/lib/graphql/schema/union.rb +2 -2
- data/lib/graphql/schema/unique_within_type.rb +1 -1
- data/lib/graphql/schema/validator/all_validator.rb +62 -0
- data/lib/graphql/schema/validator/required_validator.rb +92 -11
- data/lib/graphql/schema/validator.rb +3 -1
- data/lib/graphql/schema/visibility/migration.rb +188 -0
- data/lib/graphql/schema/visibility/profile.rb +445 -0
- data/lib/graphql/schema/visibility/visit.rb +190 -0
- data/lib/graphql/schema/visibility.rb +311 -0
- data/lib/graphql/schema/warden.rb +275 -103
- data/lib/graphql/schema.rb +924 -210
- data/lib/graphql/static_validation/all_rules.rb +3 -3
- data/lib/graphql/static_validation/base_visitor.rb +7 -6
- data/lib/graphql/static_validation/literal_validator.rb +6 -7
- 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 +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +88 -25
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- 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/not_single_subscription_error.rb +25 -0
- data/lib/graphql/static_validation/rules/query_root_exists.rb +1 -1
- data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/required_input_object_attributes_are_present.rb +5 -5
- data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
- data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +7 -3
- 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 +21 -5
- data/lib/graphql/static_validation/validator.rb +9 -1
- data/lib/graphql/static_validation.rb +0 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +8 -5
- data/lib/graphql/subscriptions/broadcast_analyzer.rb +11 -5
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +21 -4
- data/lib/graphql/subscriptions/serialize.rb +3 -1
- data/lib/graphql/subscriptions.rb +21 -17
- data/lib/graphql/testing/helpers.rb +161 -0
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +3 -0
- data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
- data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
- data/lib/graphql/tracing/appoptics_trace.rb +11 -3
- data/lib/graphql/tracing/appoptics_tracing.rb +9 -2
- data/lib/graphql/tracing/appsignal_trace.rb +32 -55
- data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
- data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
- data/lib/graphql/tracing/data_dog_trace.rb +46 -158
- data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
- data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
- data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
- data/lib/graphql/tracing/detailed_trace.rb +141 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +75 -0
- data/lib/graphql/tracing/legacy_trace.rb +4 -61
- data/lib/graphql/tracing/monitor_trace.rb +283 -0
- data/lib/graphql/tracing/new_relic_trace.rb +47 -54
- data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
- data/lib/graphql/tracing/notifications_trace.rb +184 -34
- data/lib/graphql/tracing/notifications_tracing.rb +2 -0
- data/lib/graphql/tracing/null_trace.rb +9 -0
- data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
- data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
- data/lib/graphql/tracing/perfetto_trace.rb +818 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/platform_tracing.rb +3 -1
- data/lib/graphql/tracing/{prometheus_tracing → prometheus_trace}/graphql_collector.rb +5 -1
- data/lib/graphql/tracing/prometheus_trace.rb +72 -68
- data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
- data/lib/graphql/tracing/scout_trace.rb +32 -55
- data/lib/graphql/tracing/scout_tracing.rb +2 -0
- data/lib/graphql/tracing/sentry_trace.rb +82 -0
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +112 -1
- data/lib/graphql/tracing.rb +31 -28
- data/lib/graphql/type_kinds.rb +2 -1
- data/lib/graphql/types/iso_8601_duration.rb +77 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +43 -3
- data/lib/graphql/types/relay/edge_behaviors.rb +18 -1
- data/lib/graphql/types/relay/page_info_behaviors.rb +4 -0
- data/lib/graphql/types.rb +18 -10
- data/lib/graphql/unauthorized_enum_value_error.rb +13 -0
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +68 -54
- data/readme.md +12 -2
- metadata +220 -40
- data/lib/graphql/analysis/ast/analyzer.rb +0 -84
- data/lib/graphql/analysis/ast/field_usage.rb +0 -57
- data/lib/graphql/analysis/ast/max_query_complexity.rb +0 -22
- data/lib/graphql/analysis/ast/max_query_depth.rb +0 -22
- data/lib/graphql/analysis/ast/query_complexity.rb +0 -230
- data/lib/graphql/analysis/ast/query_depth.rb +0 -55
- data/lib/graphql/analysis/ast/visitor.rb +0 -276
- data/lib/graphql/analysis/ast.rb +0 -81
- data/lib/graphql/backtrace/inspect_result.rb +0 -50
- data/lib/graphql/backtrace/trace.rb +0 -96
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/deprecation.rb +0 -9
- data/lib/graphql/filter.rb +0 -59
- data/lib/graphql/language/parser.y +0 -560
- data/lib/graphql/language/token.rb +0 -34
- data/lib/graphql/schema/base_64_bp.rb +0 -26
- data/lib/graphql/schema/invalid_type_error.rb +0 -7
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
- data/lib/graphql/static_validation/type_stack.rb +0 -216
- data/lib/graphql/subscriptions/instrumentation.rb +0 -28
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -41,6 +41,32 @@ module GraphQL
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
+
# @return [String, nil]
|
|
45
|
+
def deprecation_reason
|
|
46
|
+
super || @resolver_class&.deprecation_reason
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def directives
|
|
50
|
+
if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
|
|
51
|
+
if !(own_dirs = super).empty?
|
|
52
|
+
new_dirs = own_dirs.dup
|
|
53
|
+
r_dirs.each do |r_dir|
|
|
54
|
+
if r_dir.class.repeatable? ||
|
|
55
|
+
( (r_dir_name = r_dir.graphql_name) &&
|
|
56
|
+
(!new_dirs.any? { |d| d.graphql_name == r_dir_name })
|
|
57
|
+
)
|
|
58
|
+
new_dirs << r_dir
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
new_dirs
|
|
62
|
+
else
|
|
63
|
+
r_dirs
|
|
64
|
+
end
|
|
65
|
+
else
|
|
66
|
+
super
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
44
70
|
# @return [Class] The thing this field was defined on (type, mutation, resolver)
|
|
45
71
|
attr_accessor :owner
|
|
46
72
|
|
|
@@ -69,7 +95,7 @@ module GraphQL
|
|
|
69
95
|
end
|
|
70
96
|
|
|
71
97
|
def inspect
|
|
72
|
-
"#<#{self.class} #{path}#{all_argument_definitions.
|
|
98
|
+
"#<#{self.class} #{path}#{!all_argument_definitions.empty? ? "(...)" : ""}: #{type.to_type_signature}>"
|
|
73
99
|
end
|
|
74
100
|
|
|
75
101
|
alias :mutation :resolver
|
|
@@ -83,48 +109,6 @@ module GraphQL
|
|
|
83
109
|
end
|
|
84
110
|
attr_writer :subscription_scope
|
|
85
111
|
|
|
86
|
-
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
|
87
|
-
#
|
|
88
|
-
# This method implements prioritization between the `resolver` or `mutation` defaults
|
|
89
|
-
# and the local overrides via other keywords.
|
|
90
|
-
#
|
|
91
|
-
# It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
|
|
92
|
-
# @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
|
|
93
|
-
# @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
|
|
94
|
-
# @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
|
|
95
|
-
# @return [GraphQL::Schema:Field] an instance of `self`
|
|
96
|
-
# @see {.initialize} for other options
|
|
97
|
-
def self.from_options(name = nil, type = nil, desc = nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
|
98
|
-
if (resolver_class = resolver || mutation || subscription)
|
|
99
|
-
# Add a reference to that parent class
|
|
100
|
-
kwargs[:resolver_class] = resolver_class
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
if name
|
|
104
|
-
kwargs[:name] = name
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
if !type.nil?
|
|
108
|
-
if desc
|
|
109
|
-
if kwargs[:description]
|
|
110
|
-
raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
kwargs[:description] = desc
|
|
114
|
-
kwargs[:type] = type
|
|
115
|
-
elsif (resolver || mutation) && type.is_a?(String)
|
|
116
|
-
# The return type should be copied from the resolver, and the second positional argument is the description
|
|
117
|
-
kwargs[:description] = type
|
|
118
|
-
else
|
|
119
|
-
kwargs[:type] = type
|
|
120
|
-
end
|
|
121
|
-
if type.is_a?(Class) && type < GraphQL::Schema::Mutation
|
|
122
|
-
raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
|
|
123
|
-
end
|
|
124
|
-
end
|
|
125
|
-
new(**kwargs, &block)
|
|
126
|
-
end
|
|
127
|
-
|
|
128
112
|
# Can be set with `connection: true|false` or inferred from a type name ending in `*Connection`
|
|
129
113
|
# @return [Boolean] if true, this field will be wrapped with Relay connection behavior
|
|
130
114
|
def connection?
|
|
@@ -134,11 +118,16 @@ module GraphQL
|
|
|
134
118
|
Member::BuildType.to_type_name(@return_type_expr)
|
|
135
119
|
elsif @resolver_class && @resolver_class.type
|
|
136
120
|
Member::BuildType.to_type_name(@resolver_class.type)
|
|
137
|
-
|
|
121
|
+
elsif type
|
|
138
122
|
# As a last ditch, try to force loading the return type:
|
|
139
123
|
type.unwrap.name
|
|
140
124
|
end
|
|
141
|
-
|
|
125
|
+
if return_type_name
|
|
126
|
+
@connection = return_type_name.end_with?("Connection") && return_type_name != "Connection"
|
|
127
|
+
else
|
|
128
|
+
# TODO set this when type is set by method
|
|
129
|
+
false # not loaded yet?
|
|
130
|
+
end
|
|
142
131
|
else
|
|
143
132
|
@connection
|
|
144
133
|
end
|
|
@@ -195,6 +184,7 @@ module GraphQL
|
|
|
195
184
|
# @param owner [Class] The type that this field belongs to
|
|
196
185
|
# @param null [Boolean] (defaults to `true`) `true` if this field may return `null`, `false` if it is never `null`
|
|
197
186
|
# @param description [String] Field description
|
|
187
|
+
# @param comment [String] Field comment
|
|
198
188
|
# @param deprecation_reason [String] If present, the field is marked "deprecated" with this message
|
|
199
189
|
# @param method [Symbol] The method to call on the underlying object to resolve this field (defaults to `name`)
|
|
200
190
|
# @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`)
|
|
@@ -218,14 +208,19 @@ module GraphQL
|
|
|
218
208
|
# @param ast_node [Language::Nodes::FieldDefinition, nil] If this schema was parsed from definition, this AST node defined the field
|
|
219
209
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
|
220
210
|
# @param validates [Array<Hash>] Configurations for validating this field
|
|
221
|
-
# @fallback_value [Object] A fallback value if the method is not defined
|
|
222
|
-
|
|
211
|
+
# @param fallback_value [Object] A fallback value if the method is not defined
|
|
212
|
+
# @param dynamic_introspection [Boolean] (Private, used by GraphQL-Ruby)
|
|
213
|
+
# @param relay_node_field [Boolean] (Private, used by GraphQL-Ruby)
|
|
214
|
+
# @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
|
|
215
|
+
# @param extras [Array<:ast_node, :parent, :lookahead, :owner, :execution_errors, :graphql_name, :argument_details, Symbol>] Extra arguments to be injected into the resolver for this field
|
|
216
|
+
# @param definition_block [Proc] an additional block for configuring the field. Receive the field as a block param, or, if no block params are defined, then the block is `instance_eval`'d on the new {Field}.
|
|
217
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, hash_key: nil, dig: nil, resolver_method: nil, connection: nil, max_page_size: NOT_CONFIGURED, default_page_size: NOT_CONFIGURED, scope: nil, introspection: false, camelize: true, trace: nil, complexity: nil, 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: NOT_CONFIGURED, arguments: EMPTY_HASH, directives: EMPTY_HASH, validates: EMPTY_ARRAY, fallback_value: NOT_CONFIGURED, dynamic_introspection: false, &definition_block)
|
|
223
218
|
if name.nil?
|
|
224
219
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
|
225
220
|
end
|
|
226
221
|
if !(resolver_class)
|
|
227
|
-
if type.nil?
|
|
228
|
-
raise ArgumentError, "missing second `type` argument or
|
|
222
|
+
if type.nil? && !block_given?
|
|
223
|
+
raise ArgumentError, "missing second `type` argument, keyword `type:`, or a block containing `type(...)`"
|
|
229
224
|
end
|
|
230
225
|
end
|
|
231
226
|
@original_name = name
|
|
@@ -233,8 +228,9 @@ module GraphQL
|
|
|
233
228
|
|
|
234
229
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
|
235
230
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
|
236
|
-
|
|
231
|
+
NameValidator.validate!(@name)
|
|
237
232
|
@description = description
|
|
233
|
+
@comment = comment
|
|
238
234
|
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
|
|
239
235
|
|
|
240
236
|
self.deprecation_reason = deprecation_reason
|
|
@@ -267,6 +263,7 @@ module GraphQL
|
|
|
267
263
|
@method_sym = method_name.to_sym
|
|
268
264
|
@resolver_method = (resolver_method || name_s).to_sym
|
|
269
265
|
@complexity = complexity
|
|
266
|
+
@dynamic_introspection = dynamic_introspection
|
|
270
267
|
@return_type_expr = type
|
|
271
268
|
@return_type_null = if !null.nil?
|
|
272
269
|
null
|
|
@@ -289,6 +286,7 @@ module GraphQL
|
|
|
289
286
|
@ast_node = ast_node
|
|
290
287
|
@method_conflict_warning = method_conflict_warning
|
|
291
288
|
@fallback_value = fallback_value
|
|
289
|
+
@definition_block = definition_block
|
|
292
290
|
|
|
293
291
|
arguments.each do |name, arg|
|
|
294
292
|
case arg
|
|
@@ -308,28 +306,17 @@ module GraphQL
|
|
|
308
306
|
|
|
309
307
|
@extensions = EMPTY_ARRAY
|
|
310
308
|
@call_after_define = false
|
|
311
|
-
|
|
312
|
-
# but should it run after the definition block?
|
|
313
|
-
if scoped?
|
|
314
|
-
self.extension(ScopeExtension)
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
# The problem with putting this after the definition_block
|
|
318
|
-
# is that it would override arguments
|
|
319
|
-
if connection? && connection_extension
|
|
320
|
-
self.extension(connection_extension)
|
|
321
|
-
end
|
|
322
|
-
|
|
309
|
+
set_pagination_extensions(connection_extension: NOT_CONFIGURED.equal?(connection_extension) ? self.class.connection_extension : connection_extension)
|
|
323
310
|
# Do this last so we have as much context as possible when initializing them:
|
|
324
|
-
if extensions.
|
|
311
|
+
if !extensions.empty?
|
|
325
312
|
self.extensions(extensions)
|
|
326
313
|
end
|
|
327
314
|
|
|
328
|
-
if resolver_class && resolver_class.extensions.
|
|
315
|
+
if resolver_class && !resolver_class.extensions.empty?
|
|
329
316
|
self.extensions(resolver_class.extensions)
|
|
330
317
|
end
|
|
331
318
|
|
|
332
|
-
if directives.
|
|
319
|
+
if !directives.empty?
|
|
333
320
|
directives.each do |(dir_class, options)|
|
|
334
321
|
self.directive(dir_class, **options)
|
|
335
322
|
end
|
|
@@ -339,18 +326,33 @@ module GraphQL
|
|
|
339
326
|
self.validates(validates)
|
|
340
327
|
end
|
|
341
328
|
|
|
342
|
-
if
|
|
343
|
-
|
|
344
|
-
|
|
329
|
+
if @definition_block.nil?
|
|
330
|
+
self.extensions.each(&:after_define_apply)
|
|
331
|
+
@call_after_define = true
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
# Calls the definition block, if one was given.
|
|
336
|
+
# This is deferred so that references to the return type
|
|
337
|
+
# can be lazily evaluated, reducing Rails boot time.
|
|
338
|
+
# @return [self]
|
|
339
|
+
# @api private
|
|
340
|
+
def ensure_loaded
|
|
341
|
+
if @definition_block
|
|
342
|
+
if @definition_block.arity == 1
|
|
343
|
+
@definition_block.call(self)
|
|
345
344
|
else
|
|
346
|
-
|
|
345
|
+
instance_exec(self, &@definition_block)
|
|
347
346
|
end
|
|
347
|
+
self.extensions.each(&:after_define_apply)
|
|
348
|
+
@call_after_define = true
|
|
349
|
+
@definition_block = nil
|
|
348
350
|
end
|
|
349
|
-
|
|
350
|
-
self.extensions.each(&:after_define_apply)
|
|
351
|
-
@call_after_define = true
|
|
351
|
+
self
|
|
352
352
|
end
|
|
353
353
|
|
|
354
|
+
attr_accessor :dynamic_introspection
|
|
355
|
+
|
|
354
356
|
# If true, subscription updates with this field can be shared between viewers
|
|
355
357
|
# @return [Boolean, nil]
|
|
356
358
|
# @see GraphQL::Subscriptions::BroadcastAnalyzer
|
|
@@ -378,6 +380,20 @@ module GraphQL
|
|
|
378
380
|
end
|
|
379
381
|
end
|
|
380
382
|
|
|
383
|
+
# @param text [String]
|
|
384
|
+
# @return [String, nil]
|
|
385
|
+
def comment(text = nil)
|
|
386
|
+
if text
|
|
387
|
+
@comment = text
|
|
388
|
+
elsif !NOT_CONFIGURED.equal?(@comment)
|
|
389
|
+
@comment
|
|
390
|
+
elsif @resolver_class
|
|
391
|
+
@resolver_class.comment
|
|
392
|
+
else
|
|
393
|
+
nil
|
|
394
|
+
end
|
|
395
|
+
end
|
|
396
|
+
|
|
381
397
|
# Read extension instances from this field,
|
|
382
398
|
# or add new classes/options to be initialized on this field.
|
|
383
399
|
# Extensions are executed in the order they are added.
|
|
@@ -398,7 +414,7 @@ module GraphQL
|
|
|
398
414
|
new_extensions.each do |extension_config|
|
|
399
415
|
if extension_config.is_a?(Hash)
|
|
400
416
|
extension_class, options = *extension_config.to_a[0]
|
|
401
|
-
self.extension(extension_class, options)
|
|
417
|
+
self.extension(extension_class, **options)
|
|
402
418
|
else
|
|
403
419
|
self.extension(extension_config)
|
|
404
420
|
end
|
|
@@ -418,7 +434,7 @@ module GraphQL
|
|
|
418
434
|
# @param extension_class [Class] subclass of {Schema::FieldExtension}
|
|
419
435
|
# @param options [Hash] if provided, given as `options:` when initializing `extension`.
|
|
420
436
|
# @return [void]
|
|
421
|
-
def extension(extension_class, options
|
|
437
|
+
def extension(extension_class, **options)
|
|
422
438
|
extension_inst = extension_class.new(field: self, options: options)
|
|
423
439
|
if @extensions.frozen?
|
|
424
440
|
@extensions = @extensions.dup
|
|
@@ -439,7 +455,7 @@ module GraphQL
|
|
|
439
455
|
if new_extras.nil?
|
|
440
456
|
# Read the value
|
|
441
457
|
field_extras = @extras
|
|
442
|
-
if @resolver_class &&
|
|
458
|
+
if @resolver_class && !@resolver_class.extras.empty?
|
|
443
459
|
field_extras + @resolver_class.extras
|
|
444
460
|
else
|
|
445
461
|
field_extras
|
|
@@ -468,6 +484,8 @@ module GraphQL
|
|
|
468
484
|
if arguments[:last] && (max_possible_page_size.nil? || arguments[:last] > max_possible_page_size)
|
|
469
485
|
max_possible_page_size = arguments[:last]
|
|
470
486
|
end
|
|
487
|
+
elsif arguments.is_a?(GraphQL::ExecutionError) || arguments.is_a?(GraphQL::UnauthorizedError)
|
|
488
|
+
raise arguments
|
|
471
489
|
end
|
|
472
490
|
|
|
473
491
|
if max_possible_page_size.nil?
|
|
@@ -480,41 +498,24 @@ module GraphQL
|
|
|
480
498
|
metadata_complexity = 0
|
|
481
499
|
lookahead = GraphQL::Execution::Lookahead.new(query: query, field: self, ast_nodes: nodes, owner_type: owner)
|
|
482
500
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
501
|
+
lookahead.selections.each do |next_lookahead|
|
|
502
|
+
# this includes `pageInfo`, `nodes` and `edges` and any custom fields
|
|
503
|
+
# TODO this doesn't support procs yet -- unlikely to need it.
|
|
504
|
+
metadata_complexity += next_lookahead.field.complexity
|
|
505
|
+
if next_lookahead.name != :nodes && next_lookahead.name != :edges
|
|
506
|
+
# subfields, eg, for pageInfo -- assumes no subselections
|
|
507
|
+
metadata_complexity += next_lookahead.selections.size
|
|
508
|
+
end
|
|
490
509
|
end
|
|
491
510
|
|
|
492
|
-
nodes_edges_complexity = 0
|
|
493
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:edges)
|
|
494
|
-
nodes_edges_complexity += 1 if lookahead.selects?(:nodes)
|
|
495
|
-
|
|
496
511
|
# Possible bug: selections on `edges` and `nodes` are _both_ multiplied here. Should they be?
|
|
497
|
-
items_complexity = child_complexity - metadata_complexity
|
|
498
|
-
|
|
499
|
-
|
|
512
|
+
items_complexity = child_complexity - metadata_complexity
|
|
513
|
+
subfields_complexity = (max_possible_page_size * items_complexity) + metadata_complexity
|
|
514
|
+
# Apply this field's own complexity
|
|
515
|
+
apply_own_complexity_to(subfields_complexity, query, nodes)
|
|
500
516
|
end
|
|
501
517
|
else
|
|
502
|
-
|
|
503
|
-
case defined_complexity
|
|
504
|
-
when Proc
|
|
505
|
-
arguments = query.arguments_for(nodes.first, self)
|
|
506
|
-
if arguments.is_a?(GraphQL::ExecutionError)
|
|
507
|
-
return child_complexity
|
|
508
|
-
elsif arguments.respond_to?(:keyword_arguments)
|
|
509
|
-
arguments = arguments.keyword_arguments
|
|
510
|
-
end
|
|
511
|
-
|
|
512
|
-
defined_complexity.call(query.context, arguments, child_complexity)
|
|
513
|
-
when Numeric
|
|
514
|
-
defined_complexity + child_complexity
|
|
515
|
-
else
|
|
516
|
-
raise("Invalid complexity: #{defined_complexity.inspect} on #{path} (#{inspect})")
|
|
517
|
-
end
|
|
518
|
+
apply_own_complexity_to(child_complexity, query, nodes)
|
|
518
519
|
end
|
|
519
520
|
end
|
|
520
521
|
|
|
@@ -574,19 +575,40 @@ module GraphQL
|
|
|
574
575
|
end
|
|
575
576
|
end
|
|
576
577
|
|
|
578
|
+
def freeze
|
|
579
|
+
type
|
|
580
|
+
owner_type
|
|
581
|
+
arguments_statically_coercible?
|
|
582
|
+
connection?
|
|
583
|
+
super
|
|
584
|
+
end
|
|
585
|
+
|
|
577
586
|
class MissingReturnTypeError < GraphQL::Error; end
|
|
578
587
|
attr_writer :type
|
|
579
588
|
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
589
|
+
# Get or set the return type of this field.
|
|
590
|
+
#
|
|
591
|
+
# It may return nil if no type was configured or if the given definition block wasn't called yet.
|
|
592
|
+
# @param new_type [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List] A GraphQL return type
|
|
593
|
+
# @return [Module, GraphQL::Schema::NonNull, GraphQL::Schema::List, nil] the configured type for this field
|
|
594
|
+
def type(new_type = NOT_CONFIGURED)
|
|
595
|
+
if NOT_CONFIGURED.equal?(new_type)
|
|
596
|
+
if @resolver_class
|
|
597
|
+
return_type = @return_type_expr || @resolver_class.type_expr
|
|
598
|
+
if return_type.nil?
|
|
599
|
+
raise MissingReturnTypeError, "Can't determine the return type for #{self.path} (it has `resolver: #{@resolver_class}`, perhaps that class is missing a `type ...` declaration, or perhaps its type causes a cyclical loading issue)"
|
|
600
|
+
end
|
|
601
|
+
nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
|
|
602
|
+
Member::BuildType.parse_type(return_type, null: nullable)
|
|
603
|
+
elsif !@return_type_expr.nil?
|
|
604
|
+
@type ||= Member::BuildType.parse_type(@return_type_expr, null: @return_type_null)
|
|
585
605
|
end
|
|
586
|
-
nullable = @return_type_null.nil? ? @resolver_class.null : @return_type_null
|
|
587
|
-
Member::BuildType.parse_type(return_type, null: nullable)
|
|
588
606
|
else
|
|
589
|
-
@
|
|
607
|
+
@return_type_expr = new_type
|
|
608
|
+
# If `type` is set in the definition block, then the `connection_extension: ...` given as a keyword won't be used, hmm...
|
|
609
|
+
# Also, arguments added by `connection_extension` will clobber anything previously defined,
|
|
610
|
+
# so `type(...)` should go first.
|
|
611
|
+
set_pagination_extensions(connection_extension: self.class.connection_extension)
|
|
590
612
|
end
|
|
591
613
|
rescue GraphQL::Schema::InvalidDocumentError, MissingReturnTypeError => err
|
|
592
614
|
# Let this propagate up
|
|
@@ -618,7 +640,7 @@ module GraphQL
|
|
|
618
640
|
using_arg_values = false
|
|
619
641
|
end
|
|
620
642
|
|
|
621
|
-
args = context.
|
|
643
|
+
args = context.types.arguments(self)
|
|
622
644
|
args.each do |arg|
|
|
623
645
|
arg_key = arg.keyword
|
|
624
646
|
if arg_values.key?(arg_key)
|
|
@@ -659,7 +681,7 @@ module GraphQL
|
|
|
659
681
|
method_to_call = nil
|
|
660
682
|
method_args = nil
|
|
661
683
|
|
|
662
|
-
Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
|
684
|
+
@own_validators && Schema::Validator.validate!(validators, application_object, query_ctx, args)
|
|
663
685
|
|
|
664
686
|
query_ctx.query.after_lazy(self.authorized?(application_object, args, query_ctx)) do |is_authorized|
|
|
665
687
|
if is_authorized
|
|
@@ -691,7 +713,7 @@ module GraphQL
|
|
|
691
713
|
method_to_call = resolver_method
|
|
692
714
|
method_receiver = obj
|
|
693
715
|
# Call the method with kwargs, if there are any
|
|
694
|
-
if ruby_kwargs.
|
|
716
|
+
if !ruby_kwargs.empty?
|
|
695
717
|
obj.public_send(resolver_method, **ruby_kwargs)
|
|
696
718
|
else
|
|
697
719
|
obj.public_send(resolver_method)
|
|
@@ -701,7 +723,7 @@ module GraphQL
|
|
|
701
723
|
inner_object.dig(*@dig_keys)
|
|
702
724
|
elsif inner_object.key?(@method_sym)
|
|
703
725
|
inner_object[@method_sym]
|
|
704
|
-
elsif inner_object.key?(@method_str)
|
|
726
|
+
elsif inner_object.key?(@method_str) || !inner_object.default_proc.nil?
|
|
705
727
|
inner_object[@method_str]
|
|
706
728
|
elsif @fallback_value != NOT_CONFIGURED
|
|
707
729
|
@fallback_value
|
|
@@ -711,7 +733,7 @@ module GraphQL
|
|
|
711
733
|
elsif inner_object.respond_to?(@method_sym)
|
|
712
734
|
method_to_call = @method_sym
|
|
713
735
|
method_receiver = obj.object
|
|
714
|
-
if ruby_kwargs.
|
|
736
|
+
if !ruby_kwargs.empty?
|
|
715
737
|
inner_object.public_send(@method_sym, **ruby_kwargs)
|
|
716
738
|
else
|
|
717
739
|
inner_object.public_send(@method_sym)
|
|
@@ -798,7 +820,7 @@ module GraphQL
|
|
|
798
820
|
unsatisfied_ruby_kwargs.clear
|
|
799
821
|
end
|
|
800
822
|
|
|
801
|
-
if unsatisfied_ruby_kwargs.
|
|
823
|
+
if !unsatisfied_ruby_kwargs.empty? || !unsatisfied_method_params.empty?
|
|
802
824
|
raise FieldImplementationFailed.new, <<-ERR
|
|
803
825
|
Failed to call `#{method_name.inspect}` on #{receiver.inspect} because the Ruby method params were incompatible with the GraphQL arguments:
|
|
804
826
|
|
|
@@ -879,6 +901,38 @@ ERR
|
|
|
879
901
|
yield(obj, args)
|
|
880
902
|
end
|
|
881
903
|
end
|
|
904
|
+
|
|
905
|
+
def apply_own_complexity_to(child_complexity, query, nodes)
|
|
906
|
+
case (own_complexity = complexity)
|
|
907
|
+
when Numeric
|
|
908
|
+
own_complexity + child_complexity
|
|
909
|
+
when Proc
|
|
910
|
+
arguments = query.arguments_for(nodes.first, self)
|
|
911
|
+
if arguments.is_a?(GraphQL::ExecutionError)
|
|
912
|
+
return child_complexity
|
|
913
|
+
elsif arguments.respond_to?(:keyword_arguments)
|
|
914
|
+
arguments = arguments.keyword_arguments
|
|
915
|
+
end
|
|
916
|
+
|
|
917
|
+
own_complexity.call(query.context, arguments, child_complexity)
|
|
918
|
+
else
|
|
919
|
+
raise ArgumentError, "Invalid complexity for #{self.path}: #{own_complexity.inspect}"
|
|
920
|
+
end
|
|
921
|
+
end
|
|
922
|
+
|
|
923
|
+
def set_pagination_extensions(connection_extension:)
|
|
924
|
+
# This should run before connection extension,
|
|
925
|
+
# but should it run after the definition block?
|
|
926
|
+
if scoped?
|
|
927
|
+
self.extension(ScopeExtension, call_after_define: false)
|
|
928
|
+
end
|
|
929
|
+
|
|
930
|
+
# The problem with putting this after the definition_block
|
|
931
|
+
# is that it would override arguments
|
|
932
|
+
if connection? && connection_extension
|
|
933
|
+
self.extension(connection_extension, call_after_define: false)
|
|
934
|
+
end
|
|
935
|
+
end
|
|
882
936
|
end
|
|
883
937
|
end
|
|
884
938
|
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module GraphQL
|
|
4
|
+
class Schema
|
|
5
|
+
module HasSingleInputArgument
|
|
6
|
+
def resolve_with_support(**inputs)
|
|
7
|
+
if inputs[:input].is_a?(InputObject)
|
|
8
|
+
input = inputs[:input].to_kwargs
|
|
9
|
+
else
|
|
10
|
+
input = inputs[:input]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
new_extras = field ? field.extras : []
|
|
14
|
+
all_extras = self.class.extras + new_extras
|
|
15
|
+
|
|
16
|
+
# Transfer these from the top-level hash to the
|
|
17
|
+
# shortcutted `input:` object
|
|
18
|
+
all_extras.each do |ext|
|
|
19
|
+
# It's possible that the `extra` was not passed along by this point,
|
|
20
|
+
# don't re-add it if it wasn't given here.
|
|
21
|
+
if inputs.key?(ext)
|
|
22
|
+
input[ext] = inputs[ext]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
if input
|
|
27
|
+
# This is handled by Relay::Mutation::Resolve, a bit hacky, but here we are.
|
|
28
|
+
input_kwargs = input.to_h
|
|
29
|
+
else
|
|
30
|
+
# Relay Classic Mutations with no `argument`s
|
|
31
|
+
# don't require `input:`
|
|
32
|
+
input_kwargs = {}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
if !input_kwargs.empty?
|
|
36
|
+
super(**input_kwargs)
|
|
37
|
+
else
|
|
38
|
+
super()
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.included(base)
|
|
43
|
+
base.extend(ClassMethods)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
module ClassMethods
|
|
47
|
+
def dummy
|
|
48
|
+
@dummy ||= begin
|
|
49
|
+
d = Class.new(GraphQL::Schema::Resolver)
|
|
50
|
+
d.graphql_name "#{self.graphql_name}DummyResolver"
|
|
51
|
+
d.argument_class(self.argument_class)
|
|
52
|
+
# TODO make this lazier?
|
|
53
|
+
d.argument(:input, input_type, description: "Parameters for #{self.graphql_name}")
|
|
54
|
+
d
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def field_arguments(context = GraphQL::Query::NullContext.instance)
|
|
59
|
+
dummy.arguments(context)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def get_field_argument(name, context = GraphQL::Query::NullContext.instance)
|
|
63
|
+
dummy.get_argument(name, context)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def own_field_arguments
|
|
67
|
+
dummy.own_arguments
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def any_field_arguments?
|
|
71
|
+
dummy.any_arguments?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def all_field_argument_definitions
|
|
75
|
+
dummy.all_argument_definitions
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Also apply this argument to the input type:
|
|
79
|
+
def argument(*args, own_argument: false, **kwargs, &block)
|
|
80
|
+
it = input_type # make sure any inherited arguments are already added to it
|
|
81
|
+
arg = super(*args, **kwargs, &block)
|
|
82
|
+
|
|
83
|
+
# This definition might be overriding something inherited;
|
|
84
|
+
# if it is, remove the inherited definition so it's not confused at runtime as having multiple definitions
|
|
85
|
+
prev_args = it.own_arguments[arg.graphql_name]
|
|
86
|
+
case prev_args
|
|
87
|
+
when GraphQL::Schema::Argument
|
|
88
|
+
if prev_args.owner != self
|
|
89
|
+
it.own_arguments.delete(arg.graphql_name)
|
|
90
|
+
end
|
|
91
|
+
when Array
|
|
92
|
+
prev_args.reject! { |a| a.owner != self }
|
|
93
|
+
if prev_args.empty?
|
|
94
|
+
it.own_arguments.delete(arg.graphql_name)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it.add_argument(arg)
|
|
99
|
+
arg
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# The base class for generated input object types
|
|
103
|
+
# @param new_class [Class] The base class to use for generating input object definitions
|
|
104
|
+
# @return [Class] The base class for this mutation's generated input object (default is {GraphQL::Schema::InputObject})
|
|
105
|
+
def input_object_class(new_class = nil)
|
|
106
|
+
if new_class
|
|
107
|
+
@input_object_class = new_class
|
|
108
|
+
end
|
|
109
|
+
@input_object_class || (superclass.respond_to?(:input_object_class) ? superclass.input_object_class : GraphQL::Schema::InputObject)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# @param new_input_type [Class, nil] If provided, it configures this mutation to accept `new_input_type` instead of generating an input type
|
|
113
|
+
# @return [Class] The generated {Schema::InputObject} class for this mutation's `input`
|
|
114
|
+
def input_type(new_input_type = nil)
|
|
115
|
+
if new_input_type
|
|
116
|
+
@input_type = new_input_type
|
|
117
|
+
end
|
|
118
|
+
@input_type ||= generate_input_type
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
private
|
|
122
|
+
|
|
123
|
+
# Generate the input type for the `input:` argument
|
|
124
|
+
# To customize how input objects are generated, override this method
|
|
125
|
+
# @return [Class] a subclass of {.input_object_class}
|
|
126
|
+
def generate_input_type
|
|
127
|
+
mutation_args = all_argument_definitions
|
|
128
|
+
mutation_class = self
|
|
129
|
+
Class.new(input_object_class) do
|
|
130
|
+
class << self
|
|
131
|
+
def default_graphql_name
|
|
132
|
+
"#{self.mutation.graphql_name}Input"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def description(new_desc = nil)
|
|
136
|
+
super || "Autogenerated input type of #{self.mutation.graphql_name}"
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
# For compatibility, in case no arguments are defined:
|
|
140
|
+
has_no_arguments(true)
|
|
141
|
+
mutation(mutation_class)
|
|
142
|
+
# these might be inherited:
|
|
143
|
+
mutation_args.each do |arg|
|
|
144
|
+
add_argument(arg)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
def authorize_arguments(args, values)
|
|
153
|
+
# remove the `input` wrapper to match values
|
|
154
|
+
input_type = args.find { |a| a.graphql_name == "input" }.type.unwrap
|
|
155
|
+
input_args = context.types.arguments(input_type)
|
|
156
|
+
super(input_args, values)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|