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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/detailed_trace_generator.rb +77 -0
  3. data/lib/generators/graphql/templates/create_graphql_detailed_traces.erb +10 -0
  4. data/lib/graphql/analysis/analyzer.rb +2 -1
  5. data/lib/graphql/analysis/query_complexity.rb +87 -7
  6. data/lib/graphql/analysis/visitor.rb +37 -40
  7. data/lib/graphql/analysis.rb +12 -9
  8. data/lib/graphql/autoload.rb +1 -0
  9. data/lib/graphql/backtrace/table.rb +118 -55
  10. data/lib/graphql/backtrace.rb +1 -19
  11. data/lib/graphql/current.rb +6 -1
  12. data/lib/graphql/dashboard/application_controller.rb +41 -0
  13. data/lib/graphql/dashboard/detailed_traces.rb +47 -0
  14. data/lib/graphql/dashboard/installable.rb +22 -0
  15. data/lib/graphql/dashboard/landings_controller.rb +9 -0
  16. data/lib/graphql/dashboard/limiters.rb +93 -0
  17. data/lib/graphql/dashboard/operation_store.rb +199 -0
  18. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.css +6 -0
  19. data/lib/graphql/dashboard/statics/bootstrap-5.3.3.min.js +7 -0
  20. data/lib/graphql/dashboard/statics/charts.min.css +1 -0
  21. data/lib/graphql/dashboard/statics/dashboard.css +30 -0
  22. data/lib/graphql/dashboard/statics/dashboard.js +143 -0
  23. data/lib/graphql/dashboard/statics/header-icon.png +0 -0
  24. data/lib/graphql/dashboard/statics/icon.png +0 -0
  25. data/lib/graphql/dashboard/statics_controller.rb +31 -0
  26. data/lib/graphql/dashboard/subscriptions.rb +97 -0
  27. data/lib/graphql/dashboard/views/graphql/dashboard/detailed_traces/traces/index.html.erb +45 -0
  28. data/lib/graphql/dashboard/views/graphql/dashboard/landings/show.html.erb +18 -0
  29. data/lib/graphql/dashboard/views/graphql/dashboard/limiters/limiters/show.html.erb +62 -0
  30. data/lib/graphql/dashboard/views/graphql/dashboard/not_installed.html.erb +18 -0
  31. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/_form.html.erb +24 -0
  32. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/edit.html.erb +21 -0
  33. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/index.html.erb +69 -0
  34. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/clients/new.html.erb +7 -0
  35. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/index.html.erb +39 -0
  36. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/index_entries/show.html.erb +32 -0
  37. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/index.html.erb +81 -0
  38. data/lib/graphql/dashboard/views/graphql/dashboard/operation_store/operations/show.html.erb +71 -0
  39. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/subscriptions/show.html.erb +41 -0
  40. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/index.html.erb +55 -0
  41. data/lib/graphql/dashboard/views/graphql/dashboard/subscriptions/topics/show.html.erb +40 -0
  42. data/lib/graphql/dashboard/views/layouts/graphql/dashboard/application.html.erb +108 -0
  43. data/lib/graphql/dashboard.rb +96 -0
  44. data/lib/graphql/dataloader/active_record_association_source.rb +84 -0
  45. data/lib/graphql/dataloader/active_record_source.rb +47 -0
  46. data/lib/graphql/dataloader/async_dataloader.rb +38 -15
  47. data/lib/graphql/dataloader/null_dataloader.rb +55 -10
  48. data/lib/graphql/dataloader/source.rb +18 -6
  49. data/lib/graphql/dataloader.rb +110 -26
  50. data/lib/graphql/date_encoding_error.rb +1 -1
  51. data/lib/graphql/dig.rb +2 -1
  52. data/lib/graphql/execution/interpreter/resolve.rb +10 -16
  53. data/lib/graphql/execution/interpreter/runtime/graphql_result.rb +58 -5
  54. data/lib/graphql/execution/interpreter/runtime.rb +229 -93
  55. data/lib/graphql/execution/interpreter.rb +15 -24
  56. data/lib/graphql/execution/multiplex.rb +7 -6
  57. data/lib/graphql/execution/next/field_resolve_step.rb +690 -0
  58. data/lib/graphql/execution/next/load_argument_step.rb +60 -0
  59. data/lib/graphql/execution/next/prepare_object_step.rb +129 -0
  60. data/lib/graphql/execution/next/runner.rb +389 -0
  61. data/lib/graphql/execution/next/selections_step.rb +37 -0
  62. data/lib/graphql/execution/next.rb +69 -0
  63. data/lib/graphql/execution.rb +1 -0
  64. data/lib/graphql/execution_error.rb +13 -10
  65. data/lib/graphql/introspection/directive_location_enum.rb +1 -1
  66. data/lib/graphql/introspection/directive_type.rb +7 -3
  67. data/lib/graphql/introspection/dynamic_fields.rb +5 -1
  68. data/lib/graphql/introspection/entry_points.rb +11 -3
  69. data/lib/graphql/introspection/enum_value_type.rb +5 -5
  70. data/lib/graphql/introspection/field_type.rb +13 -5
  71. data/lib/graphql/introspection/input_value_type.rb +21 -13
  72. data/lib/graphql/introspection/type_type.rb +64 -28
  73. data/lib/graphql/invalid_name_error.rb +1 -1
  74. data/lib/graphql/invalid_null_error.rb +25 -16
  75. data/lib/graphql/language/document_from_schema_definition.rb +2 -1
  76. data/lib/graphql/language/lexer.rb +16 -5
  77. data/lib/graphql/language/nodes.rb +8 -1
  78. data/lib/graphql/language/parser.rb +16 -8
  79. data/lib/graphql/language/static_visitor.rb +37 -33
  80. data/lib/graphql/language/visitor.rb +59 -55
  81. data/lib/graphql/language.rb +21 -12
  82. data/lib/graphql/pagination/connection.rb +2 -0
  83. data/lib/graphql/pagination/connections.rb +32 -0
  84. data/lib/graphql/query/context.rb +6 -10
  85. data/lib/graphql/query/null_context.rb +9 -3
  86. data/lib/graphql/query/partial.rb +179 -0
  87. data/lib/graphql/query.rb +64 -64
  88. data/lib/graphql/railtie.rb +1 -1
  89. data/lib/graphql/schema/addition.rb +3 -1
  90. data/lib/graphql/schema/always_visible.rb +1 -0
  91. data/lib/graphql/schema/argument.rb +24 -8
  92. data/lib/graphql/schema/build_from_definition.rb +113 -54
  93. data/lib/graphql/schema/directive/flagged.rb +2 -0
  94. data/lib/graphql/schema/directive.rb +52 -2
  95. data/lib/graphql/schema/enum.rb +36 -1
  96. data/lib/graphql/schema/enum_value.rb +1 -1
  97. data/lib/graphql/schema/field/connection_extension.rb +15 -35
  98. data/lib/graphql/schema/field/scope_extension.rb +22 -13
  99. data/lib/graphql/schema/field.rb +101 -51
  100. data/lib/graphql/schema/field_extension.rb +33 -0
  101. data/lib/graphql/schema/input_object.rb +45 -38
  102. data/lib/graphql/schema/interface.rb +2 -1
  103. data/lib/graphql/schema/list.rb +1 -1
  104. data/lib/graphql/schema/member/base_dsl_methods.rb +1 -1
  105. data/lib/graphql/schema/member/has_arguments.rb +56 -19
  106. data/lib/graphql/schema/member/has_authorization.rb +35 -0
  107. data/lib/graphql/schema/member/has_dataloader.rb +79 -0
  108. data/lib/graphql/schema/member/has_deprecation_reason.rb +15 -0
  109. data/lib/graphql/schema/member/has_directives.rb +1 -1
  110. data/lib/graphql/schema/member/has_fields.rb +81 -5
  111. data/lib/graphql/schema/member/has_interfaces.rb +3 -3
  112. data/lib/graphql/schema/member/scoped.rb +1 -1
  113. data/lib/graphql/schema/member/type_system_helpers.rb +17 -3
  114. data/lib/graphql/schema/member.rb +6 -0
  115. data/lib/graphql/schema/object.rb +18 -8
  116. data/lib/graphql/schema/ractor_shareable.rb +79 -0
  117. data/lib/graphql/schema/resolver.rb +52 -6
  118. data/lib/graphql/schema/scalar.rb +1 -6
  119. data/lib/graphql/schema/subscription.rb +50 -4
  120. data/lib/graphql/schema/timeout.rb +19 -2
  121. data/lib/graphql/schema/validator/required_validator.rb +71 -14
  122. data/lib/graphql/schema/visibility/migration.rb +3 -2
  123. data/lib/graphql/schema/visibility/profile.rb +115 -23
  124. data/lib/graphql/schema/visibility.rb +49 -32
  125. data/lib/graphql/schema/warden.rb +23 -2
  126. data/lib/graphql/schema.rb +333 -68
  127. data/lib/graphql/static_validation/all_rules.rb +2 -2
  128. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +47 -13
  129. data/lib/graphql/static_validation/rules/fields_will_merge.rb +79 -17
  130. data/lib/graphql/static_validation/rules/fields_will_merge_error.rb +10 -2
  131. data/lib/graphql/static_validation/rules/not_single_subscription_error.rb +25 -0
  132. data/lib/graphql/static_validation/rules/subscription_root_exists_and_single_subscription_selection.rb +26 -0
  133. data/lib/graphql/static_validation/rules/unique_directives_per_location.rb +6 -2
  134. data/lib/graphql/static_validation/validator.rb +6 -1
  135. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +1 -0
  136. data/lib/graphql/subscriptions/default_subscription_resolve_extension.rb +12 -10
  137. data/lib/graphql/subscriptions/event.rb +12 -1
  138. data/lib/graphql/subscriptions/serialize.rb +1 -1
  139. data/lib/graphql/subscriptions.rb +1 -1
  140. data/lib/graphql/testing/helpers.rb +17 -11
  141. data/lib/graphql/testing/mock_action_cable.rb +111 -0
  142. data/lib/graphql/testing.rb +1 -0
  143. data/lib/graphql/tracing/active_support_notifications_trace.rb +14 -3
  144. data/lib/graphql/tracing/active_support_notifications_tracing.rb +1 -1
  145. data/lib/graphql/tracing/appoptics_trace.rb +9 -1
  146. data/lib/graphql/tracing/appoptics_tracing.rb +7 -0
  147. data/lib/graphql/tracing/appsignal_trace.rb +32 -55
  148. data/lib/graphql/tracing/appsignal_tracing.rb +2 -0
  149. data/lib/graphql/tracing/call_legacy_tracers.rb +66 -0
  150. data/lib/graphql/tracing/data_dog_trace.rb +46 -158
  151. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  152. data/lib/graphql/tracing/detailed_trace/active_record_backend.rb +74 -0
  153. data/lib/graphql/tracing/detailed_trace/memory_backend.rb +60 -0
  154. data/lib/graphql/tracing/detailed_trace/redis_backend.rb +72 -0
  155. data/lib/graphql/tracing/detailed_trace.rb +156 -0
  156. data/lib/graphql/tracing/legacy_hooks_trace.rb +1 -0
  157. data/lib/graphql/tracing/legacy_trace.rb +4 -61
  158. data/lib/graphql/tracing/monitor_trace.rb +283 -0
  159. data/lib/graphql/tracing/new_relic_trace.rb +47 -54
  160. data/lib/graphql/tracing/new_relic_tracing.rb +2 -0
  161. data/lib/graphql/tracing/notifications_trace.rb +184 -34
  162. data/lib/graphql/tracing/notifications_tracing.rb +2 -0
  163. data/lib/graphql/tracing/null_trace.rb +9 -0
  164. data/lib/graphql/tracing/perfetto_trace/trace.proto +141 -0
  165. data/lib/graphql/tracing/perfetto_trace/trace_pb.rb +33 -0
  166. data/lib/graphql/tracing/perfetto_trace.rb +864 -0
  167. data/lib/graphql/tracing/platform_trace.rb +5 -0
  168. data/lib/graphql/tracing/prometheus_trace/graphql_collector.rb +2 -0
  169. data/lib/graphql/tracing/prometheus_trace.rb +72 -68
  170. data/lib/graphql/tracing/prometheus_tracing.rb +2 -0
  171. data/lib/graphql/tracing/scout_trace.rb +32 -55
  172. data/lib/graphql/tracing/scout_tracing.rb +2 -0
  173. data/lib/graphql/tracing/sentry_trace.rb +64 -94
  174. data/lib/graphql/tracing/statsd_trace.rb +33 -41
  175. data/lib/graphql/tracing/statsd_tracing.rb +2 -0
  176. data/lib/graphql/tracing/trace.rb +111 -1
  177. data/lib/graphql/tracing.rb +31 -30
  178. data/lib/graphql/type_kinds.rb +1 -0
  179. data/lib/graphql/types/relay/connection_behaviors.rb +9 -7
  180. data/lib/graphql/types/relay/edge_behaviors.rb +5 -4
  181. data/lib/graphql/types/relay/has_node_field.rb +13 -8
  182. data/lib/graphql/types/relay/has_nodes_field.rb +13 -8
  183. data/lib/graphql/types/relay/node_behaviors.rb +13 -2
  184. data/lib/graphql/unauthorized_error.rb +5 -1
  185. data/lib/graphql/version.rb +1 -1
  186. data/lib/graphql.rb +12 -31
  187. metadata +174 -11
  188. data/lib/graphql/backtrace/inspect_result.rb +0 -38
  189. data/lib/graphql/backtrace/trace.rb +0 -93
  190. data/lib/graphql/backtrace/tracer.rb +0 -80
  191. data/lib/graphql/schema/null_mask.rb +0 -11
  192. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +0 -17
@@ -3,9 +3,17 @@ module GraphQL
3
3
  # If a field's resolve function returns a {ExecutionError},
4
4
  # the error will be inserted into the response's `"errors"` key
5
5
  # and the field will resolve to `nil`.
6
- class ExecutionError < GraphQL::Error
6
+ class ExecutionError < GraphQL::RuntimeError
7
7
  # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
8
- attr_accessor :ast_node
8
+ def ast_node
9
+ ast_nodes.first
10
+ end
11
+
12
+ def ast_node=(new_node)
13
+ @ast_nodes = [new_node]
14
+ end
15
+
16
+ attr_accessor :ast_nodes
9
17
 
10
18
  # @return [String] an array describing the JSON-path into the execution
11
19
  # response which corresponds to this error.
@@ -21,8 +29,8 @@ module GraphQL
21
29
  # under the `extensions` key.
22
30
  attr_accessor :extensions
23
31
 
24
- def initialize(message, ast_node: nil, options: nil, extensions: nil)
25
- @ast_node = ast_node
32
+ def initialize(message, ast_node: nil, ast_nodes: nil, options: nil, extensions: nil)
33
+ @ast_nodes = ast_nodes || [ast_node]
26
34
  @options = options
27
35
  @extensions = extensions
28
36
  super(message)
@@ -34,12 +42,7 @@ module GraphQL
34
42
  "message" => message,
35
43
  }
36
44
  if ast_node
37
- hash["locations"] = [
38
- {
39
- "line" => ast_node.line,
40
- "column" => ast_node.col,
41
- }
42
- ]
45
+ hash["locations"] = @ast_nodes.map { |a| { "line" => a.line, "column" => a.col } }
43
46
  end
44
47
  if path
45
48
  hash["path"] = path
@@ -7,7 +7,7 @@ module GraphQL
7
7
  "a __DirectiveLocation describes one such possible adjacencies."
8
8
 
9
9
  GraphQL::Schema::Directive::LOCATIONS.each do |location|
10
- value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location)
10
+ value(location.to_s, GraphQL::Schema::Directive::LOCATION_DESCRIPTIONS[location], value: location, value_method: false)
11
11
  end
12
12
  introspection true
13
13
  end
@@ -12,7 +12,7 @@ module GraphQL
12
12
  field :name, String, null: false, method: :graphql_name
13
13
  field :description, String
14
14
  field :locations, [GraphQL::Schema::LateBoundType.new("__DirectiveLocation")], null: false, scope: false
15
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
15
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false, resolve_each: :resolve_args do
16
16
  argument :include_deprecated, Boolean, required: false, default_value: false
17
17
  end
18
18
  field :on_operation, Boolean, null: false, deprecation_reason: "Use `locations`.", method: :on_operation?
@@ -21,11 +21,15 @@ module GraphQL
21
21
 
22
22
  field :is_repeatable, Boolean, method: :repeatable?
23
23
 
24
- def args(include_deprecated:)
25
- args = @context.types.arguments(@object)
24
+ def self.resolve_args(object, context, include_deprecated:)
25
+ args = context.types.arguments(object)
26
26
  args = args.reject(&:deprecation_reason) unless include_deprecated
27
27
  args
28
28
  end
29
+
30
+ def args(include_deprecated:)
31
+ self.class.resolve_args(object, context, include_deprecated: include_deprecated)
32
+ end
29
33
  end
30
34
  end
31
35
  end
@@ -2,9 +2,13 @@
2
2
  module GraphQL
3
3
  module Introspection
4
4
  class DynamicFields < Introspection::BaseObject
5
- field :__typename, String, "The name of this type", null: false, dynamic_introspection: true
5
+ field :__typename, String, "The name of this type", null: false, dynamic_introspection: true, resolve_each: true
6
6
 
7
7
  def __typename
8
+ self.class.__typename(object, context)
9
+ end
10
+
11
+ def self.__typename(object, context)
8
12
  object.class.graphql_name
9
13
  end
10
14
  end
@@ -2,19 +2,27 @@
2
2
  module GraphQL
3
3
  module Introspection
4
4
  class EntryPoints < Introspection::BaseObject
5
- field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true
6
- field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true do
5
+ field :__schema, GraphQL::Schema::LateBoundType.new("__Schema"), "This GraphQL schema", null: false, dynamic_introspection: true, resolve_static: :__schema
6
+ field :__type, GraphQL::Schema::LateBoundType.new("__Type"), "A type in the GraphQL system", dynamic_introspection: true, resolve_static: :__type do
7
7
  argument :name, String
8
8
  end
9
9
 
10
- def __schema
10
+ def self.__schema(context)
11
11
  # Apply wrapping manually since this field isn't wrapped by instrumentation
12
12
  schema = context.schema
13
13
  schema_type = schema.introspection_system.types["__Schema"]
14
14
  schema_type.wrap(schema, context)
15
15
  end
16
16
 
17
+ def __schema
18
+ self.class.__schema(context)
19
+ end
20
+
17
21
  def __type(name:)
22
+ self.class.__type(context, name: name)
23
+ end
24
+
25
+ def self.__type(context, name:)
18
26
  if context.types.reachable_type?(name) && (type = context.types.type(name))
19
27
  type
20
28
  elsif (type = context.schema.extra_types.find { |t| t.graphql_name == name })
@@ -6,17 +6,17 @@ module GraphQL
6
6
  description "One possible value for a given Enum. Enum values are unique values, not a "\
7
7
  "placeholder for a string or numeric value. However an Enum value is returned in "\
8
8
  "a JSON response as a string."
9
- field :name, String, null: false
9
+ field :name, String, null: false, method: :graphql_name
10
10
  field :description, String
11
- field :is_deprecated, Boolean, null: false
11
+ field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
12
12
  field :deprecation_reason, String
13
13
 
14
- def name
15
- object.graphql_name
14
+ def self.resolve_is_deprecated(object, context)
15
+ !!object.deprecation_reason
16
16
  end
17
17
 
18
18
  def is_deprecated
19
- !!@object.deprecation_reason
19
+ self.class.resolve_is_deprecated(object, context)
20
20
  end
21
21
  end
22
22
  end
@@ -7,22 +7,30 @@ module GraphQL
7
7
  "a name, potentially a list of arguments, and a return type."
8
8
  field :name, String, null: false
9
9
  field :description, String
10
- field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false do
10
+ field :args, [GraphQL::Schema::LateBoundType.new("__InputValue")], null: false, scope: false, resolve_each: :resolve_args do
11
11
  argument :include_deprecated, Boolean, required: false, default_value: false
12
12
  end
13
13
  field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
14
- field :is_deprecated, Boolean, null: false
14
+ field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
15
15
  field :deprecation_reason, String
16
16
 
17
+ def self.resolve_is_deprecated(object, _context)
18
+ !!object.deprecation_reason
19
+ end
20
+
17
21
  def is_deprecated
18
- !!@object.deprecation_reason
22
+ self.class.resolve_is_deprecated(object, context)
19
23
  end
20
24
 
21
- def args(include_deprecated:)
22
- args = @context.types.arguments(@object)
25
+ def self.resolve_args(object, context, include_deprecated:)
26
+ args = context.types.arguments(object)
23
27
  args = args.reject(&:deprecation_reason) unless include_deprecated
24
28
  args
25
29
  end
30
+
31
+ def args(include_deprecated:)
32
+ self.class.resolve_args(object, context, include_deprecated: include_deprecated)
33
+ end
26
34
  end
27
35
  end
28
36
  end
@@ -9,53 +9,61 @@ module GraphQL
9
9
  field :name, String, null: false
10
10
  field :description, String
11
11
  field :type, GraphQL::Schema::LateBoundType.new("__Type"), null: false
12
- field :default_value, String, "A GraphQL-formatted string representing the default value for this input value."
13
- field :is_deprecated, Boolean, null: false
12
+ field :default_value, String, "A GraphQL-formatted string representing the default value for this input value.", resolve_each: :resolve_default_value
13
+ field :is_deprecated, Boolean, null: false, resolve_each: :resolve_is_deprecated
14
14
  field :deprecation_reason, String
15
15
 
16
+ def self.resolve_is_deprecated(object, context)
17
+ !!object.deprecation_reason
18
+ end
19
+
16
20
  def is_deprecated
17
- !!@object.deprecation_reason
21
+ self.class.resolve_is_deprecated(object, context)
18
22
  end
19
23
 
20
- def default_value
21
- if @object.default_value?
22
- value = @object.default_value
24
+ def self.resolve_default_value(object, context)
25
+ if object.default_value?
26
+ value = object.default_value
23
27
  if value.nil?
24
28
  'null'
25
29
  else
26
- if (@object.type.kind.list? || (@object.type.kind.non_null? && @object.type.of_type.kind.list?)) && !value.respond_to?(:map)
30
+ if (object.type.kind.list? || (object.type.kind.non_null? && object.type.of_type.kind.list?)) && !value.respond_to?(:map)
27
31
  # This is a bit odd -- we expect the default value to be an application-style value, so we use coerce result below.
28
32
  # But coerce_result doesn't wrap single-item lists, which are valid inputs to list types.
29
33
  # So, apply that wrapper here if needed.
30
34
  value = [value]
31
35
  end
32
- coerced_default_value = @object.type.coerce_result(value, @context)
33
- serialize_default_value(coerced_default_value, @object.type)
36
+ coerced_default_value = object.type.coerce_result(value, context)
37
+ serialize_default_value(coerced_default_value, object.type, context)
34
38
  end
35
39
  else
36
40
  nil
37
41
  end
38
42
  end
39
43
 
44
+ def default_value
45
+ self.class.resolve_default_value(object, context)
46
+ end
47
+
40
48
 
41
49
  private
42
50
 
43
51
  # Recursively serialize, taking care not to add quotes to enum values
44
- def serialize_default_value(value, type)
52
+ def self.serialize_default_value(value, type, context)
45
53
  if value.nil?
46
54
  'null'
47
55
  elsif type.kind.list?
48
56
  inner_type = type.of_type
49
- "[" + value.map { |v| serialize_default_value(v, inner_type) }.join(", ") + "]"
57
+ "[" + value.map { |v| serialize_default_value(v, inner_type, context) }.join(", ") + "]"
50
58
  elsif type.kind.non_null?
51
- serialize_default_value(value, type.of_type)
59
+ serialize_default_value(value, type.of_type, context)
52
60
  elsif type.kind.enum?
53
61
  value
54
62
  elsif type.kind.input_object?
55
63
  "{" +
56
64
  value.map do |k, v|
57
65
  arg_defn = type.get_argument(k, context)
58
- "#{k}: #{serialize_default_value(v, arg_defn.type)}"
66
+ "#{k}: #{serialize_default_value(v, arg_defn.type, context)}"
59
67
  end.join(", ") +
60
68
  "}"
61
69
  else
@@ -11,32 +11,36 @@ module GraphQL
11
11
  "they describe. Abstract types, Union and Interface, provide the Object types "\
12
12
  "possible at runtime. List and NonNull types compose other types."
13
13
 
14
- field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false
14
+ field :kind, GraphQL::Schema::LateBoundType.new("__TypeKind"), null: false, resolve_each: :resolve_kind
15
15
  field :name, String, method: :graphql_name
16
16
  field :description, String
17
- field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false do
17
+ field :fields, [GraphQL::Schema::LateBoundType.new("__Field")], scope: false, resolve_each: :resolve_fields do
18
18
  argument :include_deprecated, Boolean, required: false, default_value: false
19
19
  end
20
- field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
21
- field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false
22
- field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false do
20
+ field :interfaces, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false, resolve_each: :resolve_interfaces
21
+ field :possible_types, [GraphQL::Schema::LateBoundType.new("__Type")], scope: false, resolve_each: :resolve_possible_types
22
+ field :enum_values, [GraphQL::Schema::LateBoundType.new("__EnumValue")], scope: false, resolve_each: :resolve_enum_values do
23
23
  argument :include_deprecated, Boolean, required: false, default_value: false
24
24
  end
25
- field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false do
25
+ field :input_fields, [GraphQL::Schema::LateBoundType.new("__InputValue")], scope: false, resolve_each: :resolve_input_fields do
26
26
  argument :include_deprecated, Boolean, required: false, default_value: false
27
27
  end
28
- field :of_type, GraphQL::Schema::LateBoundType.new("__Type")
28
+ field :of_type, GraphQL::Schema::LateBoundType.new("__Type"), resolve_each: :resolve_of_type
29
29
 
30
- field :specifiedByURL, String, resolver_method: :specified_by_url
30
+ field :specifiedByURL, String, resolve_each: :resolve_specified_by_url, resolver_method: :specified_by_url
31
31
 
32
- field :is_one_of, Boolean, null: false
32
+ field :is_one_of, Boolean, null: false, resolve_each: :resolve_is_one_of
33
33
 
34
- def is_one_of
34
+ def self.resolve_is_one_of(object, _ctx)
35
35
  object.kind.input_object? &&
36
36
  object.directives.any? { |d| d.graphql_name == "oneOf" }
37
37
  end
38
38
 
39
- def specified_by_url
39
+ def is_one_of
40
+ self.class.resolve_is_one_of(object, context)
41
+ end
42
+
43
+ def self.resolve_specified_by_url(object, _ctx)
40
44
  if object.kind.scalar?
41
45
  object.specified_by_url
42
46
  else
@@ -44,15 +48,23 @@ module GraphQL
44
48
  end
45
49
  end
46
50
 
51
+ def specified_by_url
52
+ self.class.resolve_specified_by_url(object, context)
53
+ end
54
+
55
+ def self.resolve_kind(object, context)
56
+ object.kind.name
57
+ end
58
+
47
59
  def kind
48
- @object.kind.name
60
+ self.class.resolve_kind(object, context)
49
61
  end
50
62
 
51
- def enum_values(include_deprecated:)
52
- if !@object.kind.enum?
63
+ def self.resolve_enum_values(object, context, include_deprecated:)
64
+ if !object.kind.enum?
53
65
  nil
54
66
  else
55
- enum_values = @context.types.enum_values(@object)
67
+ enum_values = context.types.enum_values(object)
56
68
 
57
69
  if !include_deprecated
58
70
  enum_values = enum_values.select {|f| !f.deprecation_reason }
@@ -62,17 +74,25 @@ module GraphQL
62
74
  end
63
75
  end
64
76
 
65
- def interfaces
66
- if @object.kind.object? || @object.kind.interface?
67
- @context.types.interfaces(@object).sort_by(&:graphql_name)
77
+ def enum_values(include_deprecated:)
78
+ self.class.resolve_enum_values(object, context, include_deprecated: include_deprecated)
79
+ end
80
+
81
+ def self.resolve_interfaces(object, context)
82
+ if object.kind.object? || object.kind.interface?
83
+ context.types.interfaces(object).sort_by(&:graphql_name)
68
84
  else
69
85
  nil
70
86
  end
71
87
  end
72
88
 
73
- def input_fields(include_deprecated:)
74
- if @object.kind.input_object?
75
- args = @context.types.arguments(@object)
89
+ def interfaces
90
+ self.class.resolve_interfaces(object, context)
91
+ end
92
+
93
+ def self.resolve_input_fields(object, context, include_deprecated:)
94
+ if object.kind.input_object?
95
+ args = context.types.arguments(object)
76
96
  args = args.reject(&:deprecation_reason) unless include_deprecated
77
97
  args
78
98
  else
@@ -80,19 +100,27 @@ module GraphQL
80
100
  end
81
101
  end
82
102
 
83
- def possible_types
84
- if @object.kind.abstract?
85
- @context.types.possible_types(@object).sort_by(&:graphql_name)
103
+ def input_fields(include_deprecated:)
104
+ self.class.resolve_input_fields(object, context, include_deprecated: include_deprecated)
105
+ end
106
+
107
+ def self.resolve_possible_types(object, context)
108
+ if object.kind.abstract?
109
+ context.types.possible_types(object).sort_by(&:graphql_name)
86
110
  else
87
111
  nil
88
112
  end
89
113
  end
90
114
 
91
- def fields(include_deprecated:)
92
- if !@object.kind.fields?
115
+ def possible_types
116
+ self.class.resolve_possible_types(object, context)
117
+ end
118
+
119
+ def self.resolve_fields(object, context, include_deprecated:)
120
+ if !object.kind.fields?
93
121
  nil
94
122
  else
95
- fields = @context.types.fields(@object)
123
+ fields = context.types.fields(object)
96
124
  if !include_deprecated
97
125
  fields = fields.select {|f| !f.deprecation_reason }
98
126
  end
@@ -100,8 +128,16 @@ module GraphQL
100
128
  end
101
129
  end
102
130
 
131
+ def fields(include_deprecated:)
132
+ self.class.resolve_fields(object, context, include_deprecated: include_deprecated)
133
+ end
134
+
135
+ def self.resolve_of_type(object, _ctx)
136
+ object.kind.wraps? ? object.of_type : nil
137
+ end
138
+
103
139
  def of_type
104
- @object.kind.wraps? ? @object.of_type : nil
140
+ self.class.resolve_of_type(object, context)
105
141
  end
106
142
  end
107
143
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- class InvalidNameError < GraphQL::ExecutionError
3
+ class InvalidNameError < GraphQL::Error
4
4
  attr_reader :name, :valid_regex
5
5
  def initialize(name, valid_regex)
6
6
  @name = name
@@ -2,31 +2,40 @@
2
2
  module GraphQL
3
3
  # Raised automatically when a field's resolve function returns `nil`
4
4
  # for a non-null field.
5
- class InvalidNullError < GraphQL::RuntimeTypeError
5
+ class InvalidNullError < GraphQL::RuntimeError
6
6
  # @return [GraphQL::BaseType] The owner of {#field}
7
7
  attr_reader :parent_type
8
8
 
9
9
  # @return [GraphQL::Field] The field which failed to return a value
10
10
  attr_reader :field
11
11
 
12
- # @return [nil, GraphQL::ExecutionError] The invalid value for this field
13
- attr_reader :value
14
-
15
- def initialize(parent_type, field, value)
16
- @parent_type = parent_type
17
- @field = field
18
- @value = value
19
- super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
12
+ # @return [GraphQL::Language::Nodes::Field] the field where the error occurred
13
+ def ast_node
14
+ @ast_nodes.first
20
15
  end
21
16
 
22
- # @return [Hash] An entry for the response's "errors" key
23
- def to_h
24
- { "message" => message }
25
- end
17
+ attr_reader :ast_nodes
18
+
19
+ # @return [Boolean] indicates an array result caused the error
20
+ attr_reader :is_from_array
26
21
 
27
- # @deprecated always false
28
- def parent_error?
29
- false
22
+ attr_accessor :path
23
+
24
+ def initialize(parent_type, field, ast_node_or_nodes, is_from_array: false, path: nil)
25
+ @parent_type = parent_type
26
+ @field = field
27
+ @ast_nodes = Array(ast_node_or_nodes)
28
+ @is_from_array = is_from_array
29
+ @path = path
30
+ # For List elements, identify the non-null error is for an
31
+ # element and the required element type so it's not ambiguous
32
+ # whether it was caused by a null instead of the list or a
33
+ # null element.
34
+ if @is_from_array
35
+ super("Cannot return null for non-nullable element of type '#{@field.type.of_type.of_type.to_type_signature}' for #{@parent_type.graphql_name}.#{@field.graphql_name}")
36
+ else
37
+ super("Cannot return null for non-nullable field #{@parent_type.graphql_name}.#{@field.graphql_name}")
38
+ end
30
39
  end
31
40
 
32
41
  class << self
@@ -52,8 +52,9 @@ module GraphQL
52
52
 
53
53
  def build_object_type_node(object_type)
54
54
  ints = @types.interfaces(object_type)
55
+
55
56
  if !ints.empty?
56
- ints.sort_by!(&:graphql_name)
57
+ ints = ints.sort_by(&:graphql_name)
57
58
  ints.map! { |iface| build_type_name_node(iface) }
58
59
  end
59
60
 
@@ -13,17 +13,26 @@ module GraphQL
13
13
  @pos = nil
14
14
  @max_tokens = max_tokens || Float::INFINITY
15
15
  @tokens_count = 0
16
+ @finished = false
16
17
  end
17
18
 
18
- def eos?
19
- @scanner.eos?
19
+ def finished?
20
+ @finished
21
+ end
22
+
23
+ def freeze
24
+ @scanner = nil
25
+ super
20
26
  end
21
27
 
22
28
  attr_reader :pos, :tokens_count
23
29
 
24
30
  def advance
25
31
  @scanner.skip(IGNORE_REGEXP)
26
- return false if @scanner.eos?
32
+ if @scanner.eos?
33
+ @finished = true
34
+ return false
35
+ end
27
36
  @tokens_count += 1
28
37
  if @tokens_count > @max_tokens
29
38
  raise_parse_error("This query is too large to execute.")
@@ -238,7 +247,7 @@ module GraphQL
238
247
  :SCALAR,
239
248
  nil,
240
249
  :FRAGMENT
241
- ]
250
+ ].freeze
242
251
 
243
252
  # This produces a unique integer for bytes 2 and 3 of each keyword string
244
253
  # See https://tenderlovemaking.com/2023/09/02/fast-tokenizers-with-stringscanner.html
@@ -267,7 +276,8 @@ module GraphQL
267
276
  PUNCTUATION_NAME_FOR_BYTE = Punctuation.constants.each_with_object([]) { |name, arr|
268
277
  punct = Punctuation.const_get(name)
269
278
  arr[punct.ord] = name
270
- }
279
+ }.freeze
280
+
271
281
 
272
282
  QUOTE = '"'
273
283
  UNICODE_DIGIT = /[0-9A-Za-z]/
@@ -317,6 +327,7 @@ module GraphQL
317
327
  punct = Punctuation.const_get(punct_name)
318
328
  FIRST_BYTES[punct.ord] = ByteFor::PUNCTUATION
319
329
  end
330
+ FIRST_BYTES.freeze
320
331
 
321
332
 
322
333
  # Replace any escaped unicode or whitespace with the _actual_ characters
@@ -83,7 +83,11 @@ module GraphQL
83
83
 
84
84
  def to_query_string(printer: GraphQL::Language::Printer.new)
85
85
  if printer.is_a?(GraphQL::Language::Printer)
86
- @query_string ||= printer.print(self)
86
+ if frozen?
87
+ @query_string || printer.print(self)
88
+ else
89
+ @query_string ||= printer.print(self)
90
+ end
87
91
  else
88
92
  printer.print(self)
89
93
  end
@@ -141,6 +145,8 @@ module GraphQL
141
145
  end
142
146
 
143
147
  class << self
148
+ # rubocop:disable Development/NoEvalCop This eval takes static inputs at load-time
149
+
144
150
  # Add a default `#visit_method` and `#children_method_name` using the class name
145
151
  def inherited(child_class)
146
152
  super
@@ -343,6 +349,7 @@ module GraphQL
343
349
  RUBY
344
350
  end
345
351
  end
352
+ # rubocop:enable Development/NoEvalCop
346
353
  end
347
354
  end
348
355
 
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "strscan"
4
4
  require "graphql/language/nodes"
5
+ require "graphql/tracing/null_trace"
5
6
 
6
7
  module GraphQL
7
8
  module Language
@@ -110,7 +111,7 @@ module GraphQL
110
111
  # Only ignored characters is not a valid document
111
112
  raise GraphQL::ParseError.new("Unexpected end of document", nil, nil, @graphql_str)
112
113
  end
113
- while !@lexer.eos?
114
+ while !@lexer.finished?
114
115
  defns << definition
115
116
  end
116
117
  Document.new(pos: 0, definitions: defns, filename: @filename, source: self)
@@ -161,7 +162,7 @@ module GraphQL
161
162
  expect_token(:VAR_SIGN)
162
163
  var_name = parse_name
163
164
  expect_token(:COLON)
164
- var_type = self.type
165
+ var_type = self.type || raise_parse_error("Missing type definition for variable: $#{var_name}")
165
166
  default_value = if at?(:EQUALS)
166
167
  advance_token
167
168
  value
@@ -488,26 +489,33 @@ module GraphQL
488
489
  end
489
490
 
490
491
  def type
491
- type = case token_name
492
+ parsed_type = case token_name
492
493
  when :IDENTIFIER
493
494
  parse_type_name
494
495
  when :LBRACKET
495
496
  list_type
497
+ else
498
+ nil
496
499
  end
497
500
 
498
- if at?(:BANG)
499
- type = Nodes::NonNullType.new(pos: pos, of_type: type, source: self)
501
+ if at?(:BANG) && parsed_type
502
+ parsed_type = Nodes::NonNullType.new(pos: pos, of_type: parsed_type, source: self)
500
503
  expect_token(:BANG)
501
504
  end
502
- type
505
+ parsed_type
503
506
  end
504
507
 
505
508
  def list_type
506
509
  loc = pos
507
510
  expect_token(:LBRACKET)
508
- type = Nodes::ListType.new(pos: loc, of_type: self.type, source: self)
511
+ inner_type = self.type
512
+ parsed_list_type = if inner_type
513
+ Nodes::ListType.new(pos: loc, of_type: inner_type, source: self)
514
+ else
515
+ nil
516
+ end
509
517
  expect_token(:RBRACKET)
510
- type
518
+ parsed_list_type
511
519
  end
512
520
 
513
521
  def parse_operation_type