graphql 2.0.13 → 2.0.15

Sign up to get free protection for your applications and to get access to all the features.
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