graphql 2.0.13 → 2.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of graphql might be problematic. Click here for more details.

Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/templates/schema.erb +3 -0
  3. data/lib/graphql/backtrace/table.rb +2 -2
  4. data/lib/graphql/dataloader/source.rb +9 -0
  5. data/lib/graphql/dataloader.rb +4 -1
  6. data/lib/graphql/execution/interpreter/runtime.rb +23 -10
  7. data/lib/graphql/execution/interpreter.rb +185 -59
  8. data/lib/graphql/execution/lookahead.rb +39 -28
  9. data/lib/graphql/execution/multiplex.rb +1 -116
  10. data/lib/graphql/execution.rb +0 -1
  11. data/lib/graphql/introspection/type_type.rb +7 -0
  12. data/lib/graphql/introspection.rb +3 -2
  13. data/lib/graphql/language/document_from_schema_definition.rb +18 -18
  14. data/lib/graphql/language/printer.rb +20 -11
  15. data/lib/graphql/query/context.rb +19 -5
  16. data/lib/graphql/query.rb +1 -1
  17. data/lib/graphql/schema/build_from_definition.rb +32 -17
  18. data/lib/graphql/schema/directive/one_of.rb +12 -0
  19. data/lib/graphql/schema/directive/transform.rb +1 -1
  20. data/lib/graphql/schema/field.rb +3 -3
  21. data/lib/graphql/schema/input_object.rb +35 -0
  22. data/lib/graphql/schema/late_bound_type.rb +4 -0
  23. data/lib/graphql/schema/member/build_type.rb +1 -1
  24. data/lib/graphql/schema/member/has_directives.rb +71 -56
  25. data/lib/graphql/schema/resolver/has_payload_type.rb +1 -1
  26. data/lib/graphql/schema/type_membership.rb +3 -0
  27. data/lib/graphql/schema.rb +19 -7
  28. data/lib/graphql/static_validation/all_rules.rb +1 -0
  29. data/lib/graphql/static_validation/literal_validator.rb +4 -0
  30. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb +66 -0
  31. data/lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb +29 -0
  32. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  33. data/lib/graphql/tracing/data_dog_tracing.rb +2 -0
  34. data/lib/graphql/tracing/instrumentation_tracing.rb +83 -0
  35. data/lib/graphql/types/relay/node_behaviors.rb +1 -1
  36. data/lib/graphql/version.rb +1 -1
  37. metadata +7 -4
  38. data/lib/graphql/execution/instrumentation.rb +0 -92
@@ -31,6 +31,7 @@ require "graphql/schema/union"
31
31
  require "graphql/schema/directive"
32
32
  require "graphql/schema/directive/deprecated"
33
33
  require "graphql/schema/directive/include"
34
+ require "graphql/schema/directive/one_of"
34
35
  require "graphql/schema/directive/skip"
35
36
  require "graphql/schema/directive/feature"
36
37
  require "graphql/schema/directive/flagged"
@@ -109,9 +110,10 @@ module GraphQL
109
110
  # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
110
111
  # @return [Class] the schema described by `document`
111
112
  def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
112
- # If the file ends in `.graphql`, treat it like a filepath
113
- if definition_or_path.end_with?(".graphql")
113
+ # If the file ends in `.graphql` or `.graphqls`, treat it like a filepath
114
+ if definition_or_path.end_with?(".graphql") || definition_or_path.end_with?(".graphqls")
114
115
  GraphQL::Schema::BuildFromDefinition.from_definition_path(
116
+ self,
115
117
  definition_or_path,
116
118
  default_resolve: default_resolve,
117
119
  parser: parser,
@@ -119,6 +121,7 @@ module GraphQL
119
121
  )
120
122
  else
121
123
  GraphQL::Schema::BuildFromDefinition.from_definition(
124
+ self,
122
125
  definition_or_path,
123
126
  default_resolve: default_resolve,
124
127
  parser: parser,
@@ -737,11 +740,10 @@ module GraphQL
737
740
  def handle_or_reraise(context, err)
738
741
  handler = Execution::Errors.find_handler_for(self, err.class)
739
742
  if handler
740
- runtime_info = context.namespace(:interpreter) || {}
741
- obj = runtime_info[:current_object]
742
- args = runtime_info[:current_arguments]
743
+ obj = context[:current_object]
744
+ args = context[:current_arguments]
743
745
  args = args && args.keyword_arguments
744
- field = runtime_info[:current_field]
746
+ field = context[:current_field]
745
747
  if obj.is_a?(GraphQL::Schema::Object)
746
748
  obj = obj.object
747
749
  end
@@ -815,6 +817,15 @@ module GraphQL
815
817
  member.accessible?(ctx)
816
818
  end
817
819
 
820
+ def schema_directive(dir_class, **options)
821
+ @own_schema_directives ||= []
822
+ Member::HasDirectives.add_directive(self, @own_schema_directives, dir_class, options)
823
+ end
824
+
825
+ def schema_directives
826
+ Member::HasDirectives.get_directives(self, @own_schema_directives, :schema_directives)
827
+ end
828
+
818
829
  # This hook is called when a client tries to access one or more
819
830
  # fields that fail the `accessible?` check.
820
831
  #
@@ -913,6 +924,7 @@ module GraphQL
913
924
  "include" => GraphQL::Schema::Directive::Include,
914
925
  "skip" => GraphQL::Schema::Directive::Skip,
915
926
  "deprecated" => GraphQL::Schema::Directive::Deprecated,
927
+ "oneOf" => GraphQL::Schema::Directive::OneOf,
916
928
  }.freeze
917
929
  end
918
930
 
@@ -990,7 +1002,7 @@ module GraphQL
990
1002
  # @param context [Hash] Multiplex-level context
991
1003
  # @return [Array<Hash>] One result for each query in the input
992
1004
  def multiplex(queries, **kwargs)
993
- GraphQL::Execution::Multiplex.run_all(self, queries, **kwargs)
1005
+ GraphQL::Execution::Interpreter.run_all(self, queries, **kwargs)
994
1006
  end
995
1007
 
996
1008
  def instrumenters
@@ -36,6 +36,7 @@ module GraphQL
36
36
  GraphQL::StaticValidation::QueryRootExists,
37
37
  GraphQL::StaticValidation::SubscriptionRootExists,
38
38
  GraphQL::StaticValidation::InputObjectNamesAreUnique,
39
+ GraphQL::StaticValidation::OneOfInputObjectsAreValid,
39
40
  ]
40
41
  end
41
42
  end
@@ -108,6 +108,10 @@ module GraphQL
108
108
  arg_type = @warden.get_argument(type, name).type
109
109
  recursively_validate(GraphQL::Language::Nodes::NullValue.new(name: name), arg_type)
110
110
  end
111
+
112
+ if type.one_of? && ast_node.arguments.size != 1
113
+ results << Query::InputValidationResult.from_problem("`#{type.graphql_name}` is a OneOf type, so only one argument may be given (instead of #{ast_node.arguments.size})")
114
+ end
111
115
  merge_results(results)
112
116
  end
113
117
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ module OneOfInputObjectsAreValid
5
+ def on_input_object(node, parent)
6
+ return super unless parent.is_a?(GraphQL::Language::Nodes::Argument)
7
+
8
+ parent_type = get_parent_type(context, parent)
9
+ return super unless parent_type && parent_type.kind.input_object? && parent_type.one_of?
10
+
11
+ validate_one_of_input_object(node, context, parent_type)
12
+ super
13
+ end
14
+
15
+ private
16
+
17
+ def validate_one_of_input_object(ast_node, context, parent_type)
18
+ present_fields = ast_node.arguments.map(&:name)
19
+ input_object_type = parent_type.to_type_signature
20
+
21
+ if present_fields.count != 1
22
+ add_error(
23
+ OneOfInputObjectsAreValidError.new(
24
+ "OneOf Input Object '#{input_object_type}' must specify exactly one key.",
25
+ path: context.path,
26
+ nodes: ast_node,
27
+ input_object_type: input_object_type
28
+ )
29
+ )
30
+ return
31
+ end
32
+
33
+ field = present_fields.first
34
+ value = ast_node.arguments.first.value
35
+
36
+ if value.is_a?(GraphQL::Language::Nodes::NullValue)
37
+ add_error(
38
+ OneOfInputObjectsAreValidError.new(
39
+ "Argument '#{input_object_type}.#{field}' must be non-null.",
40
+ path: [*context.path, field],
41
+ nodes: ast_node.arguments.first,
42
+ input_object_type: input_object_type
43
+ )
44
+ )
45
+ return
46
+ end
47
+
48
+ if value.is_a?(GraphQL::Language::Nodes::VariableIdentifier)
49
+ variable_name = value.name
50
+ variable_type = @declared_variables[variable_name].type
51
+
52
+ unless variable_type.is_a?(GraphQL::Language::Nodes::NonNullType)
53
+ add_error(
54
+ OneOfInputObjectsAreValidError.new(
55
+ "Variable '#{variable_name}' must be non-nullable to be used for OneOf Input Object '#{input_object_type}'.",
56
+ path: [*context.path, field],
57
+ nodes: ast_node,
58
+ input_object_type: input_object_type
59
+ )
60
+ )
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module StaticValidation
4
+ class OneOfInputObjectsAreValidError < StaticValidation::Error
5
+ attr_reader :input_object_type
6
+
7
+ def initialize(message, path:, nodes:, input_object_type:)
8
+ super(message, path: path, nodes: nodes)
9
+ @input_object_type = input_object_type
10
+ end
11
+
12
+ # A hash representation of this Message
13
+ def to_h
14
+ extensions = {
15
+ "code" => code,
16
+ "inputObjectType" => input_object_type
17
+ }
18
+
19
+ super.merge({
20
+ "extensions" => extensions
21
+ })
22
+ end
23
+
24
+ def code
25
+ "invalidOneOfInputObject"
26
+ end
27
+ end
28
+ end
29
+ end
@@ -23,7 +23,7 @@ module GraphQL
23
23
  problems = validation_result.problems
24
24
  first_problem = problems && problems.first
25
25
  if first_problem
26
- error_message = first_problem["message"]
26
+ error_message = first_problem["explanation"]
27
27
  end
28
28
 
29
29
  error_message ||= "Default value for $#{node.name} doesn't match type #{type.to_type_signature}"
@@ -75,10 +75,12 @@ module GraphQL
75
75
  end
76
76
 
77
77
  def analytics_enabled?
78
+ # [Deprecated] options[:analytics_enabled] will be removed in the future
78
79
  analytics_available? && Datadog::Contrib::Analytics.enabled?(options.fetch(:analytics_enabled, false))
79
80
  end
80
81
 
81
82
  def analytics_sample_rate
83
+ # [Deprecated] options[:analytics_sample_rate] will be removed in the future
82
84
  options.fetch(:analytics_sample_rate, 1.0)
83
85
  end
84
86
 
@@ -0,0 +1,83 @@
1
+ module GraphQL
2
+ module Tracing
3
+ class InstrumentationTracing
4
+ def initialize(schema)
5
+ @schema = schema
6
+ end
7
+
8
+ def trace(event, data)
9
+ instrumenters = nil
10
+ query_instrumenters = nil
11
+ case event
12
+ when "execute_multiplex"
13
+ instrumenters = @schema.instrumenters
14
+ multiplex_instrumenters = instrumenters[:multiplex]
15
+ query_instrumenters = instrumenters[:query]
16
+ call_hooks(multiplex_instrumenters, data[:multiplex], :before_multiplex, :after_multiplex) {
17
+ each_query_call_hooks(query_instrumenters, data[:multiplex].queries) {
18
+ yield
19
+ }
20
+ }
21
+ else
22
+ yield
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ # Call the before_ hooks of each query,
29
+ # Then yield if no errors.
30
+ # `call_hooks` takes care of appropriate cleanup.
31
+ def each_query_call_hooks(instrumenters, queries, i = 0)
32
+ if i >= queries.length
33
+ yield
34
+ else
35
+ query = queries[i]
36
+ call_hooks(instrumenters, query, :before_query, :after_query) {
37
+ each_query_call_hooks(instrumenters, queries, i + 1) {
38
+ yield
39
+ }
40
+ }
41
+ end
42
+ end
43
+
44
+ # Call each before hook, and if they all succeed, yield.
45
+ # If they don't all succeed, call after_ for each one that succeeded.
46
+ def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
47
+ begin
48
+ successful = []
49
+ instrumenters.each do |instrumenter|
50
+ instrumenter.public_send(before_hook_name, object)
51
+ successful << instrumenter
52
+ end
53
+
54
+ # if any before hooks raise an exception, quit calling before hooks,
55
+ # but call the after hooks on anything that succeeded but also
56
+ # raise the exception that came from the before hook.
57
+ rescue GraphQL::ExecutionError => err
58
+ object.context.errors << err
59
+ rescue => e
60
+ raise call_after_hooks(successful, object, after_hook_name, e)
61
+ end
62
+
63
+ begin
64
+ yield # Call the user code
65
+ ensure
66
+ ex = call_after_hooks(successful, object, after_hook_name, nil)
67
+ raise ex if ex
68
+ end
69
+ end
70
+
71
+ def call_after_hooks(instrumenters, object, after_hook_name, ex)
72
+ instrumenters.reverse_each do |instrumenter|
73
+ begin
74
+ instrumenter.public_send(after_hook_name, object)
75
+ rescue => e
76
+ ex = e
77
+ end
78
+ end
79
+ ex
80
+ end
81
+ end
82
+ end
83
+ end
@@ -11,7 +11,7 @@ module GraphQL
11
11
  end
12
12
 
13
13
  def default_global_id
14
- context.schema.id_from_object(object, self, context)
14
+ context.schema.id_from_object(object, self.class, context)
15
15
  end
16
16
  end
17
17
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "2.0.13"
3
+ VERSION = "2.0.15"
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.13
4
+ version: 2.0.15
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-08-12 00:00:00.000000000 Z
11
+ date: 2022-10-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -303,7 +303,6 @@ files:
303
303
  - lib/graphql/execution.rb
304
304
  - lib/graphql/execution/directive_checks.rb
305
305
  - lib/graphql/execution/errors.rb
306
- - lib/graphql/execution/instrumentation.rb
307
306
  - lib/graphql/execution/interpreter.rb
308
307
  - lib/graphql/execution/interpreter/argument_value.rb
309
308
  - lib/graphql/execution/interpreter/arguments.rb
@@ -394,6 +393,7 @@ files:
394
393
  - lib/graphql/schema/directive/feature.rb
395
394
  - lib/graphql/schema/directive/flagged.rb
396
395
  - lib/graphql/schema/directive/include.rb
396
+ - lib/graphql/schema/directive/one_of.rb
397
397
  - lib/graphql/schema/directive/skip.rb
398
398
  - lib/graphql/schema/directive/transform.rb
399
399
  - lib/graphql/schema/enum.rb
@@ -497,6 +497,8 @@ files:
497
497
  - lib/graphql/static_validation/rules/mutation_root_exists_error.rb
498
498
  - lib/graphql/static_validation/rules/no_definitions_are_present.rb
499
499
  - lib/graphql/static_validation/rules/no_definitions_are_present_error.rb
500
+ - lib/graphql/static_validation/rules/one_of_input_objects_are_valid.rb
501
+ - lib/graphql/static_validation/rules/one_of_input_objects_are_valid_error.rb
500
502
  - lib/graphql/static_validation/rules/operation_names_are_valid.rb
501
503
  - lib/graphql/static_validation/rules/operation_names_are_valid_error.rb
502
504
  - lib/graphql/static_validation/rules/query_root_exists.rb
@@ -536,6 +538,7 @@ files:
536
538
  - lib/graphql/tracing/appoptics_tracing.rb
537
539
  - lib/graphql/tracing/appsignal_tracing.rb
538
540
  - lib/graphql/tracing/data_dog_tracing.rb
541
+ - lib/graphql/tracing/instrumentation_tracing.rb
539
542
  - lib/graphql/tracing/new_relic_tracing.rb
540
543
  - lib/graphql/tracing/notifications_tracing.rb
541
544
  - lib/graphql/tracing/platform_tracing.rb
@@ -595,7 +598,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
595
598
  - !ruby/object:Gem::Version
596
599
  version: '0'
597
600
  requirements: []
598
- rubygems_version: 3.2.22
601
+ rubygems_version: 3.2.33
599
602
  signing_key:
600
603
  specification_version: 4
601
604
  summary: A GraphQL language and runtime for Ruby
@@ -1,92 +0,0 @@
1
- # frozen_string_literal: true
2
- module GraphQL
3
- module Execution
4
- module Instrumentation
5
- # This function implements the instrumentation policy:
6
- #
7
- # - Instrumenters are a stack; the first `before_query` will have the last `after_query`
8
- # - If a `before_` hook returned without an error, its corresponding `after_` hook will run.
9
- # - If the `before_` hook did _not_ run, the `after_` hook will not be called.
10
- #
11
- # When errors are raised from `after_` hooks:
12
- # - Subsequent `after_` hooks _are_ called
13
- # - The first raised error is captured; later errors are ignored
14
- # - If an error was capture, it's re-raised after all hooks are finished
15
- #
16
- # Partial runs of instrumentation are possible:
17
- # - If a `before_multiplex` hook raises an error, no `before_query` hooks will run
18
- # - If a `before_query` hook raises an error, subsequent `before_query` hooks will not run (on any query)
19
- def self.apply_instrumenters(multiplex)
20
- schema = multiplex.schema
21
- queries = multiplex.queries
22
- query_instrumenters = schema.instrumenters[:query]
23
- multiplex_instrumenters = schema.instrumenters[:multiplex]
24
-
25
- # First, run multiplex instrumentation, then query instrumentation for each query
26
- call_hooks(multiplex_instrumenters, multiplex, :before_multiplex, :after_multiplex) do
27
- each_query_call_hooks(query_instrumenters, queries) do
28
- # Let them be executed
29
- yield
30
- end
31
- end
32
- end
33
-
34
- class << self
35
- private
36
- # Call the before_ hooks of each query,
37
- # Then yield if no errors.
38
- # `call_hooks` takes care of appropriate cleanup.
39
- def each_query_call_hooks(instrumenters, queries, i = 0)
40
- if i >= queries.length
41
- yield
42
- else
43
- query = queries[i]
44
- call_hooks(instrumenters, query, :before_query, :after_query) {
45
- each_query_call_hooks(instrumenters, queries, i + 1) {
46
- yield
47
- }
48
- }
49
- end
50
- end
51
-
52
- # Call each before hook, and if they all succeed, yield.
53
- # If they don't all succeed, call after_ for each one that succeeded.
54
- def call_hooks(instrumenters, object, before_hook_name, after_hook_name)
55
- begin
56
- successful = []
57
- instrumenters.each do |instrumenter|
58
- instrumenter.public_send(before_hook_name, object)
59
- successful << instrumenter
60
- end
61
-
62
- # if any before hooks raise an exception, quit calling before hooks,
63
- # but call the after hooks on anything that succeeded but also
64
- # raise the exception that came from the before hook.
65
- rescue GraphQL::ExecutionError => err
66
- object.context.errors << err
67
- rescue => e
68
- raise call_after_hooks(successful, object, after_hook_name, e)
69
- end
70
-
71
- begin
72
- yield # Call the user code
73
- ensure
74
- ex = call_after_hooks(successful, object, after_hook_name, nil)
75
- raise ex if ex
76
- end
77
- end
78
-
79
- def call_after_hooks(instrumenters, object, after_hook_name, ex)
80
- instrumenters.reverse_each do |instrumenter|
81
- begin
82
- instrumenter.public_send(after_hook_name, object)
83
- rescue => e
84
- ex = e
85
- end
86
- end
87
- ex
88
- end
89
- end
90
- end
91
- end
92
- end