graphql 1.8.0.pre1 → 1.8.0.pre2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/graphql/function_generator.rb +1 -1
  3. data/lib/generators/graphql/loader_generator.rb +1 -1
  4. data/lib/generators/graphql/mutation_generator.rb +6 -1
  5. data/lib/generators/graphql/templates/function.erb +2 -2
  6. data/lib/generators/graphql/templates/loader.erb +2 -2
  7. data/lib/graphql.rb +1 -0
  8. data/lib/graphql/execution.rb +1 -0
  9. data/lib/graphql/execution/instrumentation.rb +82 -0
  10. data/lib/graphql/execution/multiplex.rb +11 -28
  11. data/lib/graphql/field.rb +5 -0
  12. data/lib/graphql/internal_representation/node.rb +1 -1
  13. data/lib/graphql/language.rb +1 -0
  14. data/lib/graphql/language/document_from_schema_definition.rb +185 -0
  15. data/lib/graphql/language/lexer.rb +3 -3
  16. data/lib/graphql/language/lexer.rl +2 -2
  17. data/lib/graphql/language/token.rb +9 -2
  18. data/lib/graphql/query.rb +4 -0
  19. data/lib/graphql/railtie.rb +83 -0
  20. data/lib/graphql/relay/relation_connection.rb +13 -18
  21. data/lib/graphql/schema.rb +6 -0
  22. data/lib/graphql/schema/argument.rb +1 -1
  23. data/lib/graphql/schema/build_from_definition.rb +2 -0
  24. data/lib/graphql/schema/field.rb +5 -2
  25. data/lib/graphql/schema/input_object.rb +2 -2
  26. data/lib/graphql/schema/member.rb +10 -0
  27. data/lib/graphql/schema/member/build_type.rb +8 -0
  28. data/lib/graphql/schema/member/instrumentation.rb +3 -3
  29. data/lib/graphql/subscriptions/action_cable_subscriptions.rb +6 -4
  30. data/lib/graphql/tracing.rb +1 -0
  31. data/lib/graphql/tracing/data_dog_tracing.rb +45 -0
  32. data/lib/graphql/tracing/platform_tracing.rb +20 -7
  33. data/lib/graphql/upgrader/member.rb +111 -0
  34. data/lib/graphql/upgrader/schema.rb +37 -0
  35. data/lib/graphql/version.rb +1 -1
  36. data/readme.md +1 -1
  37. data/spec/dummy/app/channels/graphql_channel.rb +22 -1
  38. data/spec/dummy/log/development.log +239 -0
  39. data/spec/dummy/log/test.log +204 -0
  40. data/spec/dummy/test/system/action_cable_subscription_test.rb +4 -0
  41. data/spec/dummy/tmp/screenshots/failures_test_it_handles_subscriptions.png +0 -0
  42. data/spec/generators/graphql/function_generator_spec.rb +26 -0
  43. data/spec/generators/graphql/loader_generator_spec.rb +24 -0
  44. data/spec/graphql/analysis/max_query_complexity_spec.rb +3 -3
  45. data/spec/graphql/analysis/max_query_depth_spec.rb +3 -3
  46. data/spec/graphql/base_type_spec.rb +12 -0
  47. data/spec/graphql/boolean_type_spec.rb +3 -3
  48. data/spec/graphql/execution/execute_spec.rb +1 -1
  49. data/spec/graphql/execution/instrumentation_spec.rb +165 -0
  50. data/spec/graphql/execution/multiplex_spec.rb +1 -1
  51. data/spec/graphql/float_type_spec.rb +2 -2
  52. data/spec/graphql/id_type_spec.rb +1 -1
  53. data/spec/graphql/input_object_type_spec.rb +2 -2
  54. data/spec/graphql/int_type_spec.rb +2 -2
  55. data/spec/graphql/internal_representation/rewrite_spec.rb +2 -2
  56. data/spec/graphql/introspection/schema_type_spec.rb +1 -0
  57. data/spec/graphql/language/document_from_schema_definition_spec.rb +337 -0
  58. data/spec/graphql/language/lexer_spec.rb +12 -1
  59. data/spec/graphql/language/parser_spec.rb +1 -1
  60. data/spec/graphql/query/arguments_spec.rb +3 -3
  61. data/spec/graphql/query/variables_spec.rb +1 -1
  62. data/spec/graphql/query_spec.rb +4 -4
  63. data/spec/graphql/relay/base_connection_spec.rb +1 -1
  64. data/spec/graphql/relay/connection_resolve_spec.rb +1 -1
  65. data/spec/graphql/relay/connection_type_spec.rb +1 -1
  66. data/spec/graphql/relay/mutation_spec.rb +3 -3
  67. data/spec/graphql/relay/relation_connection_spec.rb +58 -0
  68. data/spec/graphql/schema/build_from_definition_spec.rb +14 -0
  69. data/spec/graphql/schema/field_spec.rb +5 -1
  70. data/spec/graphql/schema/instrumentation_spec.rb +39 -0
  71. data/spec/graphql/schema/validation_spec.rb +1 -1
  72. data/spec/graphql/schema/warden_spec.rb +11 -11
  73. data/spec/graphql/schema_spec.rb +8 -1
  74. data/spec/graphql/string_type_spec.rb +3 -3
  75. data/spec/graphql/subscriptions_spec.rb +1 -1
  76. data/spec/graphql/tracing/platform_tracing_spec.rb +59 -0
  77. data/spec/graphql/upgrader/member_spec.rb +222 -0
  78. data/spec/graphql/upgrader/schema_spec.rb +82 -0
  79. data/spec/support/dummy/schema.rb +19 -0
  80. data/spec/support/jazz.rb +14 -14
  81. data/spec/support/star_wars/data.rb +1 -2
  82. metadata +18 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 909c3eda6b9624635a5901ceddee57a8e714a96c
4
- data.tar.gz: 7cc0f7c57d20b6979fba3156077e7989d55d6646
3
+ metadata.gz: cd8b7496d11a982cbb450b60c94ae0cfd2633892
4
+ data.tar.gz: ef5dfa73699f3e6536987f5721a609cfbf6f0dfa
5
5
  SHA512:
6
- metadata.gz: d94639f25409ba6fb4504fd9cd5fba087e717c36f88ef6329202c3f286050927ca73d67bba12313368f0aa8080af408611184b6f7bb2a4f0e24a327a76e124e7
7
- data.tar.gz: d84a84ad71225d052113c863b69ff25cb724fc97d31db680b8eee1befe7252ee94e50ff5b4cf23018fc8460c20dc80c60075dc00f42af5138c01f1f7342af9d3
6
+ metadata.gz: e31127a8a336fd40f787b9369330a6a2bc1b347ef4f7f6c83bd56cabe6290520fbd67c314023d8d95052817bd2b6668f5d094612928bada3fd9898a87f50ccf9
7
+ data.tar.gz: e0d167e65ce91ba9d19550fd9dd3b8a97150df1b9f5fec6a1b6463f00c88b30110cd982f8a3edaff4226ba3dc81d6f281f5bf5f5d9d71c1bfe4d9fa79342325e
@@ -11,7 +11,7 @@ module Graphql
11
11
  source_root File.expand_path('../templates', __FILE__)
12
12
 
13
13
  def create_function_file
14
- template "function.erb", "#{options[:directory]}/functions/#{file_name}.rb"
14
+ template "function.erb", "#{options[:directory]}/functions/#{file_path}.rb"
15
15
  end
16
16
  end
17
17
  end
@@ -13,7 +13,7 @@ module Graphql
13
13
  source_root File.expand_path('../templates', __FILE__)
14
14
 
15
15
  def create_loader_file
16
- template "loader.erb", "#{options[:directory]}/loaders/#{file_name}.rb"
16
+ template "loader.erb", "#{options[:directory]}/loaders/#{file_path}.rb"
17
17
  end
18
18
  end
19
19
  end
@@ -27,7 +27,12 @@ module Graphql
27
27
  attr_reader :file_name, :mutation_name, :field_name
28
28
 
29
29
  def create_mutation_file
30
- create_mutation_root_type
30
+ unless @behavior == :revoke
31
+ create_mutation_root_type
32
+ else
33
+ log :gsub, "#{options[:directory]}/types/mutation_type.rb"
34
+ end
35
+
31
36
  template "mutation.erb", "#{options[:directory]}/mutations/#{file_name}.rb"
32
37
 
33
38
  sentinel = /name "Mutation"\s*\n/m
@@ -1,7 +1,7 @@
1
- class Functions::<%= name %> < GraphQL::Function
1
+ class Functions::<%= class_name %> < GraphQL::Function
2
2
  # Define `initialize` to store field-level options, eg
3
3
  #
4
- # field :myField, function: Functions::<%= name %>.new(type: MyType)
4
+ # field :myField, function: Functions::<%= class_name %>.new(type: MyType)
5
5
  #
6
6
  # attr_reader :type
7
7
  # def initialize(type:)
@@ -1,7 +1,7 @@
1
- class Loaders::<%= name %> < GraphQL::Batch::Loader
1
+ class Loaders::<%= class_name %> < GraphQL::Batch::Loader
2
2
  # Define `initialize` to store grouping arguments, eg
3
3
  #
4
- # Loaders::<%= name %>.for(group).load(value)
4
+ # Loaders::<%= class_name %>.for(group).load(value)
5
5
  #
6
6
  # def initialize()
7
7
  # end
@@ -3,6 +3,7 @@ require "delegate"
3
3
  require "json"
4
4
  require "set"
5
5
  require "singleton"
6
+ require_relative "./graphql/railtie" if defined? Rails::Railtie
6
7
 
7
8
  module GraphQL
8
9
  if RUBY_VERSION == "2.4.0"
@@ -2,6 +2,7 @@
2
2
  require "graphql/execution/directive_checks"
3
3
  require "graphql/execution/execute"
4
4
  require "graphql/execution/flatten"
5
+ require "graphql/execution/instrumentation"
5
6
  require "graphql/execution/lazy"
6
7
  require "graphql/execution/multiplex"
7
8
  require "graphql/execution/typecast"
@@ -0,0 +1,82 @@
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, i = 0)
55
+ if i >= instrumenters.length
56
+ # We've reached the end of the instrumenters, so start exiting the call stack.
57
+ # (This will eventually call the originally-passed block.)
58
+ yield
59
+ else
60
+ # Get the next instrumenter and call the before_hook.
61
+ instrumenter = instrumenters[i]
62
+ instrumenter.public_send(before_hook_name, object)
63
+ # At this point, the before_hook did _not_ raise an error.
64
+ # (If it did raise an error, we wouldn't reach this point.)
65
+ # So we should guarantee that we run the after_hook.
66
+ begin
67
+ # Call the next instrumenter on the list,
68
+ # basically passing along the original block
69
+ call_hooks(instrumenters, object, before_hook_name, after_hook_name, i + 1) {
70
+ yield
71
+ }
72
+ ensure
73
+ # Regardless of how the next instrumenter in the list behaves,
74
+ # call the after_hook of this instrumenter
75
+ instrumenter.public_send(after_hook_name, object)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -60,12 +60,12 @@ module GraphQL
60
60
  if queries.length != 1
61
61
  raise ArgumentError, "Multiplexing doesn't support custom execution strategies, run one query at a time instead"
62
62
  else
63
- with_instrumentation(multiplex, max_complexity: max_complexity) do
63
+ instrument_and_analyze(multiplex, max_complexity: max_complexity) do
64
64
  [run_one_legacy(schema, queries.first)]
65
65
  end
66
66
  end
67
67
  else
68
- with_instrumentation(multiplex, max_complexity: max_complexity) do
68
+ instrument_and_analyze(multiplex, max_complexity: max_complexity) do
69
69
  run_as_multiplex(multiplex)
70
70
  end
71
71
  end
@@ -162,34 +162,17 @@ module GraphQL
162
162
  # Apply multiplex & query instrumentation to `queries`.
163
163
  #
164
164
  # It yields when the queries should be executed, then runs teardown.
165
- def with_instrumentation(multiplex, max_complexity:)
166
- schema = multiplex.schema
167
- queries = multiplex.queries
168
- query_instrumenters = schema.instrumenters[:query]
169
- multiplex_instrumenters = schema.instrumenters[:multiplex]
170
-
171
- # First, run multiplex instrumentation, then query instrumentation for each query
172
- multiplex_instrumenters.each { |i| i.before_multiplex(multiplex) }
173
- queries.each do |query|
174
- query_instrumenters.each { |i| i.before_query(query) }
175
- end
176
-
177
- multiplex_analyzers = schema.multiplex_analyzers
178
- if max_complexity
179
- multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(max_complexity)]
180
- end
181
-
182
- GraphQL::Analysis.analyze_multiplex(multiplex, multiplex_analyzers)
165
+ def instrument_and_analyze(multiplex, max_complexity:)
166
+ GraphQL::Execution::Instrumentation.apply_instrumenters(multiplex) do
167
+ schema = multiplex.schema
168
+ multiplex_analyzers = schema.multiplex_analyzers
169
+ if max_complexity
170
+ multiplex_analyzers += [GraphQL::Analysis::MaxQueryComplexity.new(max_complexity)]
171
+ end
183
172
 
184
- # Let them be executed
185
- yield
186
- ensure
187
- # Finally, run teardown instrumentation for each query + the multiplex
188
- # Use `reverse_each` so instrumenters are treated like a stack
189
- queries.each do |query|
190
- query_instrumenters.reverse_each { |i| i.after_query(query) }
173
+ GraphQL::Analysis.analyze_multiplex(multiplex, multiplex_analyzers)
174
+ yield
191
175
  end
192
- multiplex_instrumenters.reverse_each { |i| i.after_multiplex(multiplex) }
193
176
  end
194
177
  end
195
178
  end
@@ -130,6 +130,7 @@ module GraphQL
130
130
  :relay_node_field,
131
131
  :relay_nodes_field,
132
132
  :subscription_scope,
133
+ :trace,
133
134
  argument: GraphQL::Define::AssignArgument
134
135
 
135
136
  ensure_defined(
@@ -186,6 +187,9 @@ module GraphQL
186
187
  # @return [nil, String] Prefix for subscription names from this field
187
188
  attr_accessor :subscription_scope
188
189
 
190
+ # @return [Boolean] True if this field should be traced. By default, fields are only traced if they are not a ScalarType or EnumType.
191
+ attr_accessor :trace
192
+
189
193
  # @return [Boolean]
190
194
  def connection?
191
195
  @connection
@@ -212,6 +216,7 @@ module GraphQL
212
216
  @connection = false
213
217
  @connection_max_page_size = nil
214
218
  @edge_class = nil
219
+ @trace = nil
215
220
  end
216
221
 
217
222
  def initialize_copy(other)
@@ -16,7 +16,7 @@ module GraphQL
16
16
  # This value is derived from {#scoped_children} after the rewrite is finished.
17
17
  # @return [Hash<GraphQL::ObjectType, Hash<String => Node>>]
18
18
  def typed_children
19
- @typed_childen ||= begin
19
+ @typed_children ||= begin
20
20
  new_tc = Hash.new(&DEFAULT_TYPED_CHILDREN)
21
21
  if @scoped_children.any?
22
22
  all_object_types = Set.new
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  require "graphql/language/definition_slice"
3
+ require "graphql/language/document_from_schema_definition"
3
4
  require "graphql/language/generation"
4
5
  require "graphql/language/lexer"
5
6
  require "graphql/language/nodes"
@@ -0,0 +1,185 @@
1
+ # frozen_string_literal: true
2
+ module GraphQL
3
+ module Language
4
+ # @api private
5
+ #
6
+ # {GraphQL::Language::DocumentFromSchemaDefinition} is used to convert a {GraphQL::Schema} object
7
+ # To a {GraphQL::Language::Document} AST node.
8
+ #
9
+ class DocumentFromSchemaDefinition
10
+ def initialize(schema)
11
+ @schema = schema
12
+ @types = GraphQL::Schema::Traversal.new(schema, introspection: true).type_map.values
13
+ end
14
+
15
+ def document
16
+ GraphQL::Language::Nodes::Document.new(
17
+ definitions: build_definition_nodes
18
+ )
19
+ end
20
+
21
+ protected
22
+
23
+ def build_schema_node(schema)
24
+ schema_node = GraphQL::Language::Nodes::SchemaDefinition.new(
25
+ query: schema.query.name
26
+ )
27
+
28
+ if schema.mutation
29
+ schema_node.mutation = schema.mutation.name
30
+ end
31
+
32
+ if schema.subscription
33
+ schema_node.subscription = schema.subscription.name
34
+ end
35
+
36
+ schema_node
37
+ end
38
+
39
+ def build_object_type_node(object_type)
40
+ GraphQL::Language::Nodes::ObjectTypeDefinition.new(
41
+ name: object_type.name,
42
+ interfaces: object_type.interfaces.map { |iface| build_type_name_node(iface) },
43
+ fields: build_field_nodes(object_type.fields.values),
44
+ description: object_type.description,
45
+ )
46
+ end
47
+
48
+ def build_field_node(field)
49
+ GraphQL::Language::Nodes::FieldDefinition.new(
50
+ name: field.name,
51
+ arguments: build_argument_nodes(field.arguments.values),
52
+ type: build_type_name_node(field.type),
53
+ description: field.description,
54
+ )
55
+ end
56
+
57
+ def build_union_type_node(union_type)
58
+ GraphQL::Language::Nodes::UnionTypeDefinition.new(
59
+ name: union_type.name,
60
+ description: union_type.description,
61
+ types: union_type.possible_types.map { |type| build_type_name_node(type) }
62
+ )
63
+ end
64
+
65
+ def build_interface_type_node(interface_type)
66
+ GraphQL::Language::Nodes::InterfaceTypeDefinition.new(
67
+ name: interface_type.name,
68
+ description: interface_type.description,
69
+ fields: build_field_nodes(interface_type.fields.values)
70
+ )
71
+ end
72
+
73
+ def build_enum_type_node(enum_type)
74
+ GraphQL::Language::Nodes::EnumTypeDefinition.new(
75
+ name: enum_type.name,
76
+ values: enum_type.values.values.map do |enum_value|
77
+ build_enum_value_node(enum_value)
78
+ end,
79
+ description: enum_type.description,
80
+ )
81
+ end
82
+
83
+ def build_enum_value_node(enum_value)
84
+ GraphQL::Language::Nodes::EnumValueDefinition.new(
85
+ name: enum_value.name,
86
+ description: enum_value.description,
87
+ )
88
+ end
89
+
90
+ def build_scalar_type_node(scalar_type)
91
+ GraphQL::Language::Nodes::ScalarTypeDefinition.new(
92
+ name: scalar_type.name,
93
+ description: scalar_type.description,
94
+ )
95
+ end
96
+
97
+ def build_argument_node(argument)
98
+ GraphQL::Language::Nodes::InputValueDefinition.new(
99
+ name: argument.name,
100
+ description: argument.description,
101
+ type: build_type_name_node(argument.type),
102
+ default_value: argument.default_value,
103
+ )
104
+ end
105
+
106
+ def build_input_object_node(input_object)
107
+ GraphQL::Language::Nodes::InputObjectTypeDefinition.new(
108
+ name: input_object.name,
109
+ fields: build_argument_nodes(input_object.arguments.values),
110
+ description: input_object.description,
111
+ )
112
+ end
113
+
114
+ def build_directive_node(directive)
115
+ GraphQL::Language::Nodes::DirectiveDefinition.new(
116
+ name: directive.name,
117
+ arguments: build_argument_nodes(directive.arguments.values),
118
+ locations: directive.locations.map(&:to_s),
119
+ description: directive.description,
120
+ )
121
+ end
122
+
123
+ def build_type_name_node(type)
124
+ case type
125
+ when GraphQL::ListType
126
+ GraphQL::Language::Nodes::ListType.new(
127
+ of_type: build_type_name_node(type.of_type)
128
+ )
129
+ when GraphQL::NonNullType
130
+ GraphQL::Language::Nodes::NonNullType.new(
131
+ of_type: build_type_name_node(type.of_type)
132
+ )
133
+ else
134
+ GraphQL::Language::Nodes::TypeName.new(name: type.name)
135
+ end
136
+ end
137
+
138
+ def build_type_definition_node(type)
139
+ case type
140
+ when GraphQL::ObjectType
141
+ build_object_type_node(type)
142
+ when GraphQL::UnionType
143
+ build_union_type_node(type)
144
+ when GraphQL::InterfaceType
145
+ build_interface_type_node(type)
146
+ when GraphQL::ScalarType
147
+ build_scalar_type_node(type)
148
+ when GraphQL::EnumType
149
+ build_enum_type_node(type)
150
+ when GraphQL::InputObjectType
151
+ build_input_object_node(type)
152
+ else
153
+ raise TypeError
154
+ end
155
+ end
156
+
157
+ def build_argument_nodes(arguments)
158
+ arguments.map { |arg| build_argument_node(arg) }
159
+ end
160
+
161
+ def build_directive_nodes(directives)
162
+ directives.map { |directive| build_directive_node(directive) }
163
+ end
164
+
165
+ def build_definition_nodes
166
+ definitions = build_type_definition_nodes(types)
167
+ definitions += build_directive_nodes(schema.directives.values)
168
+ definitions << build_schema_node(schema)
169
+ definitions
170
+ end
171
+
172
+ def build_type_definition_nodes(types)
173
+ types.map { |type| build_type_definition_node(type) }
174
+ end
175
+
176
+ def build_field_nodes(fields)
177
+ fields.map { |field| build_field_node(field) }
178
+ end
179
+
180
+ private
181
+
182
+ attr_reader :schema, :types
183
+ end
184
+ end
185
+ end