graphql 2.4.5 → 2.5.21
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/detailed_trace_generator.rb +77 -0
- data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
- data/lib/graphql/analysis/analyzer.rb +2 -1
- data/lib/graphql/analysis/query_complexity.rb +87 -7
- data/lib/graphql/analysis/visitor.rb +37 -40
- data/lib/graphql/analysis.rb +12 -9
- data/lib/graphql/autoload.rb +1 -0
- data/lib/graphql/backtrace/table.rb +118 -55
- data/lib/graphql/backtrace.rb +1 -19
- data/lib/graphql/current.rb +6 -1
- data/lib/graphql/dashboard/application_controller.rb +41 -0
- data/lib/graphql/dashboard/detailed_traces.rb +47 -0
- data/lib/graphql/dashboard/installable.rb +22 -0
- data/lib/graphql/dashboard/landings_controller.rb +9 -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/statics_controller.rb +31 -0
- data/lib/graphql/dashboard/subscriptions.rb +97 -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 +96 -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 +38 -15
- data/lib/graphql/dataloader/null_dataloader.rb +55 -10
- data/lib/graphql/dataloader/source.rb +18 -6
- data/lib/graphql/dataloader.rb +110 -26
- data/lib/graphql/date_encoding_error.rb +1 -1
- data/lib/graphql/dig.rb +2 -1
- data/lib/graphql/execution/interpreter/resolve.rb +10 -16
- data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +58 -5
- data/lib/graphql/execution/interpreter/runtime.rb +229 -93
- data/lib/graphql/execution/interpreter.rb +15 -24
- data/lib/graphql/execution/multiplex.rb +7 -6
- data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
- data/lib/graphql/execution/next/load_argument_step.rb +60 -0
- data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
- data/lib/graphql/execution/next/runner.rb +389 -0
- data/lib/graphql/execution/next/selections_step.rb +37 -0
- data/lib/graphql/execution/next.rb +69 -0
- data/lib/graphql/execution.rb +1 -0
- data/lib/graphql/execution_error.rb +13 -10
- data/lib/graphql/introspection/directive_location_enum.rb +1 -1
- data/lib/graphql/introspection/directive_type.rb +7 -3
- data/lib/graphql/introspection/dynamic_fields.rb +5 -1
- data/lib/graphql/introspection/entry_points.rb +11 -3
- data/lib/graphql/introspection/enum_value_type.rb +5 -5
- data/lib/graphql/introspection/field_type.rb +13 -5
- data/lib/graphql/introspection/input_value_type.rb +21 -13
- data/lib/graphql/introspection/type_type.rb +64 -28
- data/lib/graphql/invalid_name_error.rb +1 -1
- data/lib/graphql/invalid_null_error.rb +25 -16
- data/lib/graphql/language/document_from_schema_definition.rb +2 -1
- data/lib/graphql/language/lexer.rb +16 -5
- data/lib/graphql/language/nodes.rb +8 -1
- data/lib/graphql/language/parser.rb +16 -8
- data/lib/graphql/language/static_visitor.rb +37 -33
- data/lib/graphql/language/visitor.rb +59 -55
- data/lib/graphql/language.rb +21 -12
- data/lib/graphql/pagination/connection.rb +2 -0
- data/lib/graphql/pagination/connections.rb +32 -0
- data/lib/graphql/query/context.rb +6 -10
- data/lib/graphql/query/null_context.rb +9 -3
- data/lib/graphql/query/partial.rb +179 -0
- data/lib/graphql/query.rb +64 -64
- data/lib/graphql/railtie.rb +1 -1
- data/lib/graphql/schema/addition.rb +3 -1
- data/lib/graphql/schema/always_visible.rb +1 -0
- data/lib/graphql/schema/argument.rb +24 -8
- data/lib/graphql/schema/build_from_definition.rb +113 -54
- data/lib/graphql/schema/directive/flagged.rb +2 -0
- data/lib/graphql/schema/directive.rb +52 -2
- data/lib/graphql/schema/enum.rb +36 -1
- data/lib/graphql/schema/enum_value.rb +1 -1
- data/lib/graphql/schema/field/connection_extension.rb +15 -35
- data/lib/graphql/schema/field/scope_extension.rb +22 -13
- data/lib/graphql/schema/field.rb +101 -51
- data/lib/graphql/schema/field_extension.rb +33 -0
- data/lib/graphql/schema/input_object.rb +45 -38
- data/lib/graphql/schema/interface.rb +2 -1
- data/lib/graphql/schema/list.rb +1 -1
- data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
- data/lib/graphql/schema/member/has_arguments.rb +56 -19
- data/lib/graphql/schema/member/has_authorization.rb +35 -0
- data/lib/graphql/schema/member/has_dataloader.rb +79 -0
- data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
- data/lib/graphql/schema/member/has_directives.rb +1 -1
- data/lib/graphql/schema/member/has_fields.rb +81 -5
- data/lib/graphql/schema/member/has_interfaces.rb +3 -3
- data/lib/graphql/schema/member/scoped.rb +1 -1
- data/lib/graphql/schema/member/type_system_helpers.rb +17 -3
- data/lib/graphql/schema/member.rb +6 -0
- data/lib/graphql/schema/object.rb +18 -8
- data/lib/graphql/schema/ractor_shareable.rb +79 -0
- data/lib/graphql/schema/resolver.rb +52 -6
- data/lib/graphql/schema/scalar.rb +1 -6
- data/lib/graphql/schema/subscription.rb +50 -4
- data/lib/graphql/schema/timeout.rb +19 -2
- data/lib/graphql/schema/validator/required_validator.rb +71 -14
- data/lib/graphql/schema/visibility/migration.rb +3 -2
- data/lib/graphql/schema/visibility/profile.rb +115 -23
- data/lib/graphql/schema/visibility.rb +49 -32
- data/lib/graphql/schema/warden.rb +23 -2
- data/lib/graphql/schema.rb +333 -68
- data/lib/graphql/static_validation/all_rules.rb +2 -2
- data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
- data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
- data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
- data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
- 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 +6 -2
- data/lib/graphql/static_validation/validator.rb +6 -1
- data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
- data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
- data/lib/graphql/subscriptions/event.rb +12 -1
- data/lib/graphql/subscriptions/serialize.rb +1 -1
- data/lib/graphql/subscriptions.rb +1 -1
- data/lib/graphql/testing/helpers.rb +17 -11
- data/lib/graphql/testing/mock_action_cable.rb +111 -0
- data/lib/graphql/testing.rb +1 -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 +9 -1
- data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
- 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/active_record_backend.rb +74 -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 +156 -0
- data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -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 +864 -0
- data/lib/graphql/tracing/platform_trace.rb +5 -0
- data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
- 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 +64 -94
- data/lib/graphql/tracing/statsd_trace.rb +33 -41
- data/lib/graphql/tracing/statsd_tracing.rb +2 -0
- data/lib/graphql/tracing/trace.rb +111 -1
- data/lib/graphql/tracing.rb +31 -30
- data/lib/graphql/type_kinds.rb +1 -0
- data/lib/graphql/types/relay/connection_behaviors.rb +9 -7
- data/lib/graphql/types/relay/edge_behaviors.rb +5 -4
- data/lib/graphql/types/relay/has_node_field.rb +13 -8
- data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
- data/lib/graphql/types/relay/node_behaviors.rb +13 -2
- data/lib/graphql/unauthorized_error.rb +5 -1
- data/lib/graphql/version.rb +1 -1
- data/lib/graphql.rb +12 -31
- metadata +174 -11
- data/lib/graphql/backtrace/inspect_result.rb +0 -38
- data/lib/graphql/backtrace/trace.rb +0 -93
- data/lib/graphql/backtrace/tracer.rb +0 -80
- data/lib/graphql/schema/null_mask.rb +0 -11
- data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
|
@@ -9,6 +9,7 @@ module GraphQL
|
|
|
9
9
|
class Directive < GraphQL::Schema::Member
|
|
10
10
|
extend GraphQL::Schema::Member::HasArguments
|
|
11
11
|
extend GraphQL::Schema::Member::HasArguments::HasDirectiveArguments
|
|
12
|
+
extend GraphQL::Schema::Member::HasValidators
|
|
12
13
|
|
|
13
14
|
class << self
|
|
14
15
|
# Directives aren't types, they don't have kinds.
|
|
@@ -75,6 +76,10 @@ module GraphQL
|
|
|
75
76
|
yield
|
|
76
77
|
end
|
|
77
78
|
|
|
79
|
+
def validate!(arguments, context)
|
|
80
|
+
Schema::Validator.validate!(validators, self, context, arguments)
|
|
81
|
+
end
|
|
82
|
+
|
|
78
83
|
def on_field?
|
|
79
84
|
locations.include?(FIELD)
|
|
80
85
|
end
|
|
@@ -99,7 +104,7 @@ module GraphQL
|
|
|
99
104
|
|
|
100
105
|
def inherited(subclass)
|
|
101
106
|
super
|
|
102
|
-
subclass.
|
|
107
|
+
subclass.class_exec do
|
|
103
108
|
@default_graphql_name ||= nil
|
|
104
109
|
end
|
|
105
110
|
end
|
|
@@ -111,6 +116,9 @@ module GraphQL
|
|
|
111
116
|
# @return [GraphQL::Interpreter::Arguments]
|
|
112
117
|
attr_reader :arguments
|
|
113
118
|
|
|
119
|
+
class InvalidArgumentError < GraphQL::Error
|
|
120
|
+
end
|
|
121
|
+
|
|
114
122
|
def initialize(owner, **arguments)
|
|
115
123
|
@owner = owner
|
|
116
124
|
assert_valid_owner
|
|
@@ -119,7 +127,49 @@ module GraphQL
|
|
|
119
127
|
# - lazy resolution
|
|
120
128
|
# Probably, those won't be needed here, since these are configuration arguments,
|
|
121
129
|
# not runtime arguments.
|
|
122
|
-
|
|
130
|
+
context = Query::NullContext.instance
|
|
131
|
+
self.class.all_argument_definitions.each do |arg_defn|
|
|
132
|
+
keyword = arg_defn.keyword
|
|
133
|
+
arg_type = arg_defn.type
|
|
134
|
+
if arguments.key?(keyword)
|
|
135
|
+
value = arguments[keyword]
|
|
136
|
+
# This is a Ruby-land value; convert it to graphql for validation
|
|
137
|
+
graphql_value = begin
|
|
138
|
+
coerce_value = value
|
|
139
|
+
if arg_type.list? && (!coerce_value.nil?) && (!coerce_value.is_a?(Array))
|
|
140
|
+
# When validating inputs, GraphQL accepts a single item
|
|
141
|
+
# and implicitly converts it to a one-item list.
|
|
142
|
+
# However, we're using result coercion here to go from Ruby value
|
|
143
|
+
# to GraphQL value, so it doesn't have that feature.
|
|
144
|
+
# Keep the GraphQL-type behavior but implement it manually:
|
|
145
|
+
wrap_type = arg_type
|
|
146
|
+
while wrap_type.list?
|
|
147
|
+
if wrap_type.non_null?
|
|
148
|
+
wrap_type = wrap_type.of_type
|
|
149
|
+
end
|
|
150
|
+
wrap_type = wrap_type.of_type
|
|
151
|
+
coerce_value = [coerce_value]
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
arg_type.coerce_isolated_result(coerce_value)
|
|
155
|
+
rescue GraphQL::Schema::Enum::UnresolvedValueError
|
|
156
|
+
# Let validation handle this
|
|
157
|
+
value
|
|
158
|
+
end
|
|
159
|
+
else
|
|
160
|
+
value = graphql_value = nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
result = arg_type.validate_input(graphql_value, context)
|
|
164
|
+
if !result.valid?
|
|
165
|
+
raise InvalidArgumentError, "@#{graphql_name}.#{arg_defn.graphql_name} on #{owner.path} is invalid (#{value.inspect}): #{result.problems.first["explanation"]}"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
self.class.validate!(arguments, context)
|
|
169
|
+
@arguments = self.class.coerce_arguments(nil, arguments, context)
|
|
170
|
+
if @arguments.is_a?(GraphQL::ExecutionError)
|
|
171
|
+
raise @arguments
|
|
172
|
+
end
|
|
123
173
|
end
|
|
124
174
|
|
|
125
175
|
def graphql_name
|
data/lib/graphql/schema/enum.rb
CHANGED
|
@@ -61,12 +61,19 @@ module GraphQL
|
|
|
61
61
|
# @option kwargs [String] :description, the GraphQL description for this value, present in documentation
|
|
62
62
|
# @option kwargs [String] :comment, the GraphQL comment for this value, present in documentation
|
|
63
63
|
# @option kwargs [::Object] :value the translated Ruby value for this object (defaults to `graphql_name`)
|
|
64
|
+
# @option kwargs [::Object] :value_method, the method name to fetch `graphql_name` (defaults to `graphql_name.downcase`)
|
|
64
65
|
# @option kwargs [String] :deprecation_reason if this object is deprecated, include a message here
|
|
66
|
+
# @param value_method [Symbol, false] A method to generate for this value, or `false` to skip generation
|
|
65
67
|
# @return [void]
|
|
66
68
|
# @see {Schema::EnumValue} which handles these inputs by default
|
|
67
|
-
def value(*args, **kwargs, &block)
|
|
69
|
+
def value(*args, value_method: nil, **kwargs, &block)
|
|
68
70
|
kwargs[:owner] = self
|
|
69
71
|
value = enum_value_class.new(*args, **kwargs, &block)
|
|
72
|
+
|
|
73
|
+
if value_method || (value_methods && value_method != false)
|
|
74
|
+
generate_value_method(value, value_method)
|
|
75
|
+
end
|
|
76
|
+
|
|
70
77
|
key = value.graphql_name
|
|
71
78
|
prev_value = own_values[key]
|
|
72
79
|
case prev_value
|
|
@@ -154,6 +161,18 @@ module GraphQL
|
|
|
154
161
|
end
|
|
155
162
|
end
|
|
156
163
|
|
|
164
|
+
def value_methods(new_value = NOT_CONFIGURED)
|
|
165
|
+
if NOT_CONFIGURED.equal?(new_value)
|
|
166
|
+
if @value_methods != nil
|
|
167
|
+
@value_methods
|
|
168
|
+
else
|
|
169
|
+
find_inherited_value(:value_methods, false)
|
|
170
|
+
end
|
|
171
|
+
else
|
|
172
|
+
@value_methods = new_value
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
157
176
|
def kind
|
|
158
177
|
GraphQL::TypeKinds::ENUM
|
|
159
178
|
end
|
|
@@ -215,6 +234,7 @@ module GraphQL
|
|
|
215
234
|
# because they would end up with names like `#<Class0x1234>::UnresolvedValueError` which messes up bug trackers
|
|
216
235
|
child_class.const_set(:UnresolvedValueError, Class.new(Schema::Enum::UnresolvedValueError))
|
|
217
236
|
end
|
|
237
|
+
child_class.class_exec { @value_methods = nil }
|
|
218
238
|
super
|
|
219
239
|
end
|
|
220
240
|
|
|
@@ -223,6 +243,21 @@ module GraphQL
|
|
|
223
243
|
def own_values
|
|
224
244
|
@own_values ||= {}
|
|
225
245
|
end
|
|
246
|
+
|
|
247
|
+
def generate_value_method(value, configured_value_method)
|
|
248
|
+
return if configured_value_method == false
|
|
249
|
+
|
|
250
|
+
value_method_name = configured_value_method || value.graphql_name.downcase
|
|
251
|
+
|
|
252
|
+
if respond_to?(value_method_name.to_sym)
|
|
253
|
+
warn "Failed to define value method for :#{value_method_name}, because " \
|
|
254
|
+
"#{value.owner.name || value.owner.graphql_name} already responds to that method. Use `value_method:` to override the method name " \
|
|
255
|
+
"or `value_method: false` to disable Enum value method generation."
|
|
256
|
+
return
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
define_singleton_method(value_method_name) { value.graphql_name }
|
|
260
|
+
end
|
|
226
261
|
end
|
|
227
262
|
|
|
228
263
|
enum_value_class(GraphQL::Schema::EnumValue)
|
|
@@ -21,45 +21,25 @@ module GraphQL
|
|
|
21
21
|
yield(object, next_args, arguments)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
+
def resolve_next(objects:, arguments:, context:)
|
|
25
|
+
next_args = arguments.dup
|
|
26
|
+
next_args.delete(:first)
|
|
27
|
+
next_args.delete(:last)
|
|
28
|
+
next_args.delete(:before)
|
|
29
|
+
next_args.delete(:after)
|
|
30
|
+
yield(objects, next_args, arguments)
|
|
31
|
+
end
|
|
32
|
+
|
|
24
33
|
def after_resolve(value:, object:, arguments:, context:, memo:)
|
|
25
34
|
original_arguments = memo
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
value = nil
|
|
29
|
-
context.query.after_lazy(maybe_lazy) do |resolved_value|
|
|
30
|
-
value = resolved_value
|
|
31
|
-
if value.is_a? GraphQL::ExecutionError
|
|
32
|
-
# This isn't even going to work because context doesn't have ast_node anymore
|
|
33
|
-
context.add_error(value)
|
|
34
|
-
nil
|
|
35
|
-
elsif value.nil?
|
|
36
|
-
nil
|
|
37
|
-
elsif value.is_a?(GraphQL::Pagination::Connection)
|
|
38
|
-
# update the connection with some things that may not have been provided
|
|
39
|
-
value.context ||= context
|
|
40
|
-
value.parent ||= object.object
|
|
41
|
-
value.first_value ||= original_arguments[:first]
|
|
42
|
-
value.after_value ||= original_arguments[:after]
|
|
43
|
-
value.last_value ||= original_arguments[:last]
|
|
44
|
-
value.before_value ||= original_arguments[:before]
|
|
45
|
-
value.arguments ||= original_arguments # rubocop:disable Development/ContextIsPassedCop -- unrelated .arguments method
|
|
46
|
-
value.field ||= field
|
|
47
|
-
if field.has_max_page_size? && !value.has_max_page_size_override?
|
|
48
|
-
value.max_page_size = field.max_page_size
|
|
49
|
-
end
|
|
50
|
-
if field.has_default_page_size? && !value.has_default_page_size_override?
|
|
51
|
-
value.default_page_size = field.default_page_size
|
|
52
|
-
end
|
|
53
|
-
if (custom_t = context.schema.connections.edge_class_for_field(@field))
|
|
54
|
-
value.edge_class = custom_t
|
|
55
|
-
end
|
|
56
|
-
value
|
|
57
|
-
else
|
|
58
|
-
context.namespace(:connections)[:all_wrappers] ||= context.schema.connections.all_wrappers
|
|
59
|
-
context.schema.connections.wrap(field, object.object, value, original_arguments, context)
|
|
60
|
-
end
|
|
35
|
+
context.query.after_lazy(value) do |resolved_value|
|
|
36
|
+
context.schema.connections.populate_connection(field, object.object, resolved_value, original_arguments, context)
|
|
61
37
|
end
|
|
62
38
|
end
|
|
39
|
+
|
|
40
|
+
def after_resolve_next(**kwargs)
|
|
41
|
+
raise "This should never be called -- it's hardcoded in execution instead."
|
|
42
|
+
end
|
|
63
43
|
end
|
|
64
44
|
end
|
|
65
45
|
end
|
|
@@ -5,24 +5,33 @@ module GraphQL
|
|
|
5
5
|
class Field
|
|
6
6
|
class ScopeExtension < GraphQL::Schema::FieldExtension
|
|
7
7
|
def after_resolve(object:, arguments:, context:, value:, memo:)
|
|
8
|
-
if
|
|
9
|
-
value
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
if object.is_a?(GraphQL::Schema::Object)
|
|
9
|
+
if value.nil?
|
|
10
|
+
value
|
|
11
|
+
else
|
|
12
|
+
return_type = field.type.unwrap
|
|
13
|
+
if return_type.respond_to?(:scope_items)
|
|
14
|
+
scoped_items = return_type.scope_items(value, context)
|
|
15
|
+
if !scoped_items.equal?(value) && !return_type.reauthorize_scoped_objects
|
|
16
|
+
if (current_runtime_state = Fiber[:__graphql_runtime_info]) &&
|
|
17
|
+
(query_runtime_state = current_runtime_state[context.query])
|
|
18
|
+
query_runtime_state.was_authorized_by_scope_items = true
|
|
19
|
+
end
|
|
18
20
|
end
|
|
21
|
+
scoped_items
|
|
22
|
+
else
|
|
23
|
+
value
|
|
19
24
|
end
|
|
20
|
-
scoped_items
|
|
21
|
-
else
|
|
22
|
-
value
|
|
23
25
|
end
|
|
26
|
+
else
|
|
27
|
+
# TODO skip this entirely?
|
|
28
|
+
value
|
|
24
29
|
end
|
|
25
30
|
end
|
|
31
|
+
|
|
32
|
+
def after_resolve_next(**kwargs)
|
|
33
|
+
raise "This should never be called -- it's hardcoded in execution instead."
|
|
34
|
+
end
|
|
26
35
|
end
|
|
27
36
|
end
|
|
28
37
|
end
|
data/lib/graphql/schema/field.rb
CHANGED
|
@@ -8,6 +8,7 @@ module GraphQL
|
|
|
8
8
|
include GraphQL::Schema::Member::HasArguments
|
|
9
9
|
include GraphQL::Schema::Member::HasArguments::FieldConfigured
|
|
10
10
|
include GraphQL::Schema::Member::HasAstNode
|
|
11
|
+
include GraphQL::Schema::Member::HasAuthorization
|
|
11
12
|
include GraphQL::Schema::Member::HasPath
|
|
12
13
|
include GraphQL::Schema::Member::HasValidators
|
|
13
14
|
extend GraphQL::Schema::FindInheritedValue
|
|
@@ -41,10 +42,24 @@ module GraphQL
|
|
|
41
42
|
end
|
|
42
43
|
end
|
|
43
44
|
|
|
45
|
+
# @return [String, nil]
|
|
46
|
+
def deprecation_reason
|
|
47
|
+
super || @resolver_class&.deprecation_reason
|
|
48
|
+
end
|
|
49
|
+
|
|
44
50
|
def directives
|
|
45
51
|
if @resolver_class && !(r_dirs = @resolver_class.directives).empty?
|
|
46
52
|
if !(own_dirs = super).empty?
|
|
47
|
-
|
|
53
|
+
new_dirs = own_dirs.dup
|
|
54
|
+
r_dirs.each do |r_dir|
|
|
55
|
+
if r_dir.class.repeatable? ||
|
|
56
|
+
( (r_dir_name = r_dir.graphql_name) &&
|
|
57
|
+
(!new_dirs.any? { |d| d.graphql_name == r_dir_name })
|
|
58
|
+
)
|
|
59
|
+
new_dirs << r_dir
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
new_dirs
|
|
48
63
|
else
|
|
49
64
|
r_dirs
|
|
50
65
|
end
|
|
@@ -95,52 +110,6 @@ module GraphQL
|
|
|
95
110
|
end
|
|
96
111
|
attr_writer :subscription_scope
|
|
97
112
|
|
|
98
|
-
# Create a field instance from a list of arguments, keyword arguments, and a block.
|
|
99
|
-
#
|
|
100
|
-
# This method implements prioritization between the `resolver` or `mutation` defaults
|
|
101
|
-
# and the local overrides via other keywords.
|
|
102
|
-
#
|
|
103
|
-
# It also normalizes positional arguments into keywords for {Schema::Field#initialize}.
|
|
104
|
-
# @param resolver [Class] A {GraphQL::Schema::Resolver} class to use for field configuration
|
|
105
|
-
# @param mutation [Class] A {GraphQL::Schema::Mutation} class to use for field configuration
|
|
106
|
-
# @param subscription [Class] A {GraphQL::Schema::Subscription} class to use for field configuration
|
|
107
|
-
# @return [GraphQL::Schema:Field] an instance of `self`
|
|
108
|
-
# @see {.initialize} for other options
|
|
109
|
-
def self.from_options(name = nil, type = nil, desc = nil, comment: nil, resolver: nil, mutation: nil, subscription: nil,**kwargs, &block)
|
|
110
|
-
if (resolver_class = resolver || mutation || subscription)
|
|
111
|
-
# Add a reference to that parent class
|
|
112
|
-
kwargs[:resolver_class] = resolver_class
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
if name
|
|
116
|
-
kwargs[:name] = name
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
if comment
|
|
120
|
-
kwargs[:comment] = comment
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
if !type.nil?
|
|
124
|
-
if desc
|
|
125
|
-
if kwargs[:description]
|
|
126
|
-
raise ArgumentError, "Provide description as a positional argument or `description:` keyword, but not both (#{desc.inspect}, #{kwargs[:description].inspect})"
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
kwargs[:description] = desc
|
|
130
|
-
kwargs[:type] = type
|
|
131
|
-
elsif (resolver || mutation) && type.is_a?(String)
|
|
132
|
-
# The return type should be copied from the resolver, and the second positional argument is the description
|
|
133
|
-
kwargs[:description] = type
|
|
134
|
-
else
|
|
135
|
-
kwargs[:type] = type
|
|
136
|
-
end
|
|
137
|
-
if type.is_a?(Class) && type < GraphQL::Schema::Mutation
|
|
138
|
-
raise ArgumentError, "Use `field #{name.inspect}, mutation: Mutation, ...` to provide a mutation to this field instead"
|
|
139
|
-
end
|
|
140
|
-
end
|
|
141
|
-
new(**kwargs, &block)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
113
|
# Can be set with `connection: true|false` or inferred from a type name ending in `*Connection`
|
|
145
114
|
# @return [Boolean] if true, this field will be wrapped with Relay connection behavior
|
|
146
115
|
def connection?
|
|
@@ -224,6 +193,10 @@ module GraphQL
|
|
|
224
193
|
# @param resolver_method [Symbol] The method on the type to call to resolve this field (defaults to `name`)
|
|
225
194
|
# @param connection [Boolean] `true` if this field should get automagic connection behavior; default is to infer by `*Connection` in the return type name
|
|
226
195
|
# @param connection_extension [Class] The extension to add, to implement connections. If `nil`, no extension is added.
|
|
196
|
+
# @param resolve_static [Symbol, true, nil] Used by {Schema.execute_next} to produce a single value, shared by all objects which resolve this field. Called on the owner type class with `context, **arguments`
|
|
197
|
+
# @param resolve_batch [Symbol, true, nil] Used by {Schema.execute_next} map `objects` to a same-sized Array of results. Called on the owner type class with `objects, context, **arguments`.
|
|
198
|
+
# @param resolve_each [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Called on the owner type class with `object, context, **arguments`.
|
|
199
|
+
# @param resolve_legacy_instance_method [Symbol, true, nil] Used by {Schema.execute_next} to get a value value for each item. Calls an instance method on the object type class.
|
|
227
200
|
# @param max_page_size [Integer, nil] For connections, the maximum number of items to return from this field, or `nil` to allow unlimited results.
|
|
228
201
|
# @param default_page_size [Integer, nil] For connections, the default number of items to return from this field, or `nil` to return unlimited results.
|
|
229
202
|
# @param introspection [Boolean] If true, this field will be marked as `#introspection?` and the name may begin with `__`
|
|
@@ -241,7 +214,12 @@ module GraphQL
|
|
|
241
214
|
# @param method_conflict_warning [Boolean] If false, skip the warning if this field's method conflicts with a built-in method
|
|
242
215
|
# @param validates [Array<Hash>] Configurations for validating this field
|
|
243
216
|
# @param fallback_value [Object] A fallback value if the method is not defined
|
|
244
|
-
|
|
217
|
+
# @param dynamic_introspection [Boolean] (Private, used by GraphQL-Ruby)
|
|
218
|
+
# @param relay_node_field [Boolean] (Private, used by GraphQL-Ruby)
|
|
219
|
+
# @param relay_nodes_field [Boolean] (Private, used by GraphQL-Ruby)
|
|
220
|
+
# @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
|
|
221
|
+
# @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}.
|
|
222
|
+
def initialize(type: nil, name: nil, owner: nil, null: nil, description: NOT_CONFIGURED, comment: NOT_CONFIGURED, deprecation_reason: nil, method: nil, resolve_legacy_instance_method: nil, resolve_static: nil, resolve_each: nil, resolve_batch: 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)
|
|
245
223
|
if name.nil?
|
|
246
224
|
raise ArgumentError, "missing first `name` argument or keyword `name:`"
|
|
247
225
|
end
|
|
@@ -255,7 +233,7 @@ module GraphQL
|
|
|
255
233
|
|
|
256
234
|
@underscored_name = -Member::BuildType.underscore(name_s)
|
|
257
235
|
@name = -(camelize ? Member::BuildType.camelize(name_s) : name_s)
|
|
258
|
-
|
|
236
|
+
NameValidator.validate!(@name)
|
|
259
237
|
@description = description
|
|
260
238
|
@comment = comment
|
|
261
239
|
@type = @owner_type = @own_validators = @own_directives = @own_arguments = @arguments_statically_coercible = nil # these will be prepared later if necessary
|
|
@@ -289,6 +267,33 @@ module GraphQL
|
|
|
289
267
|
@method_str = -method_name.to_s
|
|
290
268
|
@method_sym = method_name.to_sym
|
|
291
269
|
@resolver_method = (resolver_method || name_s).to_sym
|
|
270
|
+
|
|
271
|
+
if resolve_static
|
|
272
|
+
@execution_next_mode = :resolve_static
|
|
273
|
+
@execution_next_mode_key = resolve_static == true ? @method_sym : resolve_static
|
|
274
|
+
elsif resolve_batch
|
|
275
|
+
@execution_next_mode = :resolve_batch
|
|
276
|
+
@execution_next_mode_key = resolve_batch == true ? @method_sym : resolve_batch
|
|
277
|
+
elsif resolve_each
|
|
278
|
+
@execution_next_mode = :resolve_each
|
|
279
|
+
@execution_next_mode_key = resolve_each == true ? @method_sym : resolve_each
|
|
280
|
+
elsif hash_key
|
|
281
|
+
@execution_next_mode = :hash_key
|
|
282
|
+
@execution_next_mode_key = hash_key
|
|
283
|
+
elsif dig
|
|
284
|
+
@execution_next_mode = :dig
|
|
285
|
+
@execution_next_mode_key = dig
|
|
286
|
+
elsif resolver_class
|
|
287
|
+
@execution_next_mode = :resolver_class
|
|
288
|
+
@execution_next_mode_key = resolver_class
|
|
289
|
+
elsif resolve_legacy_instance_method
|
|
290
|
+
@execution_next_mode = :resolve_legacy_instance_method
|
|
291
|
+
@execution_next_mode_key = resolve_legacy_instance_method == true ? @method_sym : resolve_legacy_instance_method
|
|
292
|
+
else
|
|
293
|
+
@execution_next_mode = :direct_send
|
|
294
|
+
@execution_next_mode_key = @method_sym
|
|
295
|
+
end
|
|
296
|
+
|
|
292
297
|
@complexity = complexity
|
|
293
298
|
@dynamic_introspection = dynamic_introspection
|
|
294
299
|
@return_type_expr = type
|
|
@@ -333,7 +338,7 @@ module GraphQL
|
|
|
333
338
|
|
|
334
339
|
@extensions = EMPTY_ARRAY
|
|
335
340
|
@call_after_define = false
|
|
336
|
-
set_pagination_extensions(connection_extension: connection_extension)
|
|
341
|
+
set_pagination_extensions(connection_extension: NOT_CONFIGURED.equal?(connection_extension) ? self.class.connection_extension : connection_extension)
|
|
337
342
|
# Do this last so we have as much context as possible when initializing them:
|
|
338
343
|
if !extensions.empty?
|
|
339
344
|
self.extensions(extensions)
|
|
@@ -359,6 +364,9 @@ module GraphQL
|
|
|
359
364
|
end
|
|
360
365
|
end
|
|
361
366
|
|
|
367
|
+
# @api private
|
|
368
|
+
attr_reader :execution_next_mode_key, :execution_next_mode
|
|
369
|
+
|
|
362
370
|
# Calls the definition block, if one was given.
|
|
363
371
|
# This is deferred so that references to the return type
|
|
364
372
|
# can be lazily evaluated, reducing Rails boot time.
|
|
@@ -369,7 +377,7 @@ module GraphQL
|
|
|
369
377
|
if @definition_block.arity == 1
|
|
370
378
|
@definition_block.call(self)
|
|
371
379
|
else
|
|
372
|
-
|
|
380
|
+
instance_exec(self, &@definition_block)
|
|
373
381
|
end
|
|
374
382
|
self.extensions.each(&:after_define_apply)
|
|
375
383
|
@call_after_define = true
|
|
@@ -602,6 +610,14 @@ module GraphQL
|
|
|
602
610
|
end
|
|
603
611
|
end
|
|
604
612
|
|
|
613
|
+
def freeze
|
|
614
|
+
type
|
|
615
|
+
owner_type
|
|
616
|
+
arguments_statically_coercible?
|
|
617
|
+
connection?
|
|
618
|
+
super
|
|
619
|
+
end
|
|
620
|
+
|
|
605
621
|
class MissingReturnTypeError < GraphQL::Error; end
|
|
606
622
|
attr_writer :type
|
|
607
623
|
|
|
@@ -644,6 +660,13 @@ module GraphQL
|
|
|
644
660
|
end
|
|
645
661
|
end
|
|
646
662
|
|
|
663
|
+
def authorizes?(context)
|
|
664
|
+
method(:authorized?).owner != GraphQL::Schema::Field || (
|
|
665
|
+
(args = context.types.arguments(self)) &&
|
|
666
|
+
(args.any? { |a| a.authorizes?(context) })
|
|
667
|
+
)
|
|
668
|
+
end
|
|
669
|
+
|
|
647
670
|
def authorized?(object, args, context)
|
|
648
671
|
if @resolver_class
|
|
649
672
|
# The resolver _instance_ will check itself during `resolve()`
|
|
@@ -898,6 +921,33 @@ ERR
|
|
|
898
921
|
end
|
|
899
922
|
end
|
|
900
923
|
|
|
924
|
+
public
|
|
925
|
+
|
|
926
|
+
def run_next_extensions_before_resolve(objs, args, ctx, extended, idx: 0, &block)
|
|
927
|
+
extension = @extensions[idx]
|
|
928
|
+
if extension
|
|
929
|
+
extension.resolve_next(objects: objs, arguments: args, context: ctx) do |extended_objs, extended_args, memo|
|
|
930
|
+
if memo
|
|
931
|
+
memos = extended.memos ||= {}
|
|
932
|
+
memos[idx] = memo
|
|
933
|
+
end
|
|
934
|
+
|
|
935
|
+
if (extras = extension.added_extras)
|
|
936
|
+
ae = extended.added_extras ||= []
|
|
937
|
+
ae.concat(extras)
|
|
938
|
+
end
|
|
939
|
+
|
|
940
|
+
extended.object = extended_objs
|
|
941
|
+
extended.arguments = extended_args
|
|
942
|
+
run_next_extensions_before_resolve(extended_objs, extended_args, ctx, extended, idx: idx + 1, &block)
|
|
943
|
+
end
|
|
944
|
+
else
|
|
945
|
+
yield(objs, args)
|
|
946
|
+
end
|
|
947
|
+
end
|
|
948
|
+
|
|
949
|
+
private
|
|
950
|
+
|
|
901
951
|
def run_extensions_before_resolve(obj, args, ctx, extended, idx: 0)
|
|
902
952
|
extension = @extensions[idx]
|
|
903
953
|
if extension
|
|
@@ -134,6 +134,24 @@ module GraphQL
|
|
|
134
134
|
yield(object, arguments, nil)
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
+
# Called before batch-resolving {#field}. It should either:
|
|
138
|
+
#
|
|
139
|
+
# - `yield` values to continue execution; OR
|
|
140
|
+
# - return something else to shortcut field execution.
|
|
141
|
+
#
|
|
142
|
+
# Whatever this method returns will be used for execution.
|
|
143
|
+
#
|
|
144
|
+
# @param objects [Array<Object>] The objects the field is being resolved on
|
|
145
|
+
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
|
146
|
+
# @param context [Query::Context] the context for this query
|
|
147
|
+
# @yieldparam objects [Array<Object>] The objects to continue resolving the field on. Length must be the same as passed-in `objects:`
|
|
148
|
+
# @yieldparam arguments [Hash] The keyword arguments to continue resolving with
|
|
149
|
+
# @yieldparam memo [Object] Any extension-specific value which will be passed to {#after_resolve} later
|
|
150
|
+
# @return [Array<Object>] The return value for this field, length matching passed-in `objects:`.
|
|
151
|
+
def resolve_next(objects:, arguments:, context:)
|
|
152
|
+
yield(objects, arguments, nil)
|
|
153
|
+
end
|
|
154
|
+
|
|
137
155
|
# Called after {#field} was resolved, and after any lazy values (like `Promise`s) were synced,
|
|
138
156
|
# but before the value was added to the GraphQL response.
|
|
139
157
|
#
|
|
@@ -148,6 +166,21 @@ module GraphQL
|
|
|
148
166
|
def after_resolve(object:, arguments:, context:, value:, memo:)
|
|
149
167
|
value
|
|
150
168
|
end
|
|
169
|
+
|
|
170
|
+
# Called after {#field} was batch-resolved, and after any lazy values (like `Promise`s) were synced,
|
|
171
|
+
# but before the value was added to the GraphQL response.
|
|
172
|
+
#
|
|
173
|
+
# Whatever this hook returns will be used as the return value.
|
|
174
|
+
#
|
|
175
|
+
# @param objects [Array<Object>] The objects the field is being resolved on
|
|
176
|
+
# @param arguments [Hash] Ruby keyword arguments for resolving this field
|
|
177
|
+
# @param context [Query::Context] the context for this query
|
|
178
|
+
# @param values [Array<Object>] Whatever the field returned, one for each of `objects`
|
|
179
|
+
# @param memo [Object] The third value yielded by {#resolve}, or `nil` if there wasn't one
|
|
180
|
+
# @return [Array<Object>] The return values for this field, length matching `objects:`.
|
|
181
|
+
def after_resolve_next(objects:, arguments:, context:, values:, memo:)
|
|
182
|
+
values
|
|
183
|
+
end
|
|
151
184
|
end
|
|
152
185
|
end
|
|
153
186
|
end
|