graphql 2.0.14 → 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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a3bc6610f88b6689ee6991a6ad3543580f9bdf1b3cb9f2f7e3f78862d76072f7
4
- data.tar.gz: ce52505d6c43e330aa8e5def38719f62bdfe067274a86530c889350c64994bd0
3
+ metadata.gz: 46b43157a331eae41dd54897d24dbc3adac190910237c7c1c158fc2bc32b6616
4
+ data.tar.gz: 118ac97b740ba1340d3e6b9aee5fa2f81ca25dc8769a98ec0d8e25d63e839711
5
5
  SHA512:
6
- metadata.gz: 8f3aac93aa1013c257c84521b54285719d6edb8c212a83140123902a06f3cffabc1005d2a176b723cff5677614eab7ae1617b71ce7eb403e721fcbafbe07c1e1
7
- data.tar.gz: a84bf37728b5c0ff8795070fd682ac7af83e8888d47cadff5103a76ee333788c30bf78f505528fee4f868288b99dfc11d82f7c604710e13b4a7d9690eb2efe36
6
+ metadata.gz: be38356ecdff7f8a9627bb021e83d13ea92e951b287386e76fd6e42ff05ef0a5a37ce9c98e6e4bf1834459989558f6c8b512c98e7642bef2c624578c1db24932
7
+ data.tar.gz: b0b75f342dca7a731b1b59e6399d364184152ceb0c5deb9fb4338a3582b3b6e38bf789a53839f6def9eb4b9c079c45e74675fb608a184dff4e6aeaf0ce052dfd
@@ -83,7 +83,7 @@ module GraphQL
83
83
  value = if top && @override_value
84
84
  @override_value
85
85
  else
86
- value_at(@context.query.context.namespace(:interpreter)[:runtime], context_entry.path)
86
+ value_at(@context.query.context.namespace(:interpreter_runtime)[:runtime], context_entry.path)
87
87
  end
88
88
  rows << [
89
89
  "#{context_entry.ast_node ? context_entry.ast_node.position.join(":") : ""}",
@@ -112,7 +112,7 @@ module GraphQL
112
112
  if object.is_a?(GraphQL::Schema::Object)
113
113
  object = object.object
114
114
  end
115
- value = value_at(context_entry.namespace(:interpreter)[:runtime], [])
115
+ value = value_at(context_entry.namespace(:interpreter_runtime)[:runtime], [])
116
116
  rows << [
117
117
  "#{position}",
118
118
  "#{op_type}#{op_name ? " #{op_name}" : ""}",
@@ -289,7 +289,10 @@ module GraphQL
289
289
  fiber_locals = {}
290
290
 
291
291
  Thread.current.keys.each do |fiber_var_key|
292
- fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
292
+ # This variable should be fresh in each new fiber
293
+ if fiber_var_key != :__graphql_runtime_info
294
+ fiber_locals[fiber_var_key] = Thread.current[fiber_var_key]
295
+ end
293
296
  end
294
297
 
295
298
  if @nonblocking
@@ -148,13 +148,23 @@ module GraphQL
148
148
  # @return [GraphQL::Query::Context]
149
149
  attr_reader :context
150
150
 
151
+ def thread_info
152
+ info = Thread.current[:__graphql_runtime_info]
153
+ if !info
154
+ new_ti = {}
155
+ info = Thread.current[:__graphql_runtime_info] = new_ti
156
+ end
157
+ info
158
+ end
159
+
151
160
  def initialize(query:)
152
161
  @query = query
153
162
  @dataloader = query.multiplex.dataloader
154
163
  @schema = query.schema
155
164
  @context = query.context
156
165
  @multiplex_context = query.multiplex.context
157
- @interpreter_context = @context.namespace(:interpreter)
166
+ # Start this off empty:
167
+ Thread.current[:__graphql_runtime_info] = nil
158
168
  @response = GraphQLResultHash.new(nil, nil)
159
169
  # Identify runtime directives by checking which of this schema's directives have overridden `def self.resolve`
160
170
  @runtime_directive_names = []
@@ -680,7 +690,11 @@ module GraphQL
680
690
 
681
691
  case current_type.kind.name
682
692
  when "SCALAR", "ENUM"
683
- r = current_type.coerce_result(value, context)
693
+ r = begin
694
+ current_type.coerce_result(value, context)
695
+ rescue StandardError => err
696
+ schema.handle_or_reraise(context, err)
697
+ end
684
698
  set_result(selection_result, result_name, r)
685
699
  r
686
700
  when "UNION", "INTERFACE"
@@ -870,17 +884,18 @@ module GraphQL
870
884
  end
871
885
 
872
886
  def set_all_interpreter_context(object, field, arguments, path)
887
+ ti = thread_info
873
888
  if object
874
- @context[:current_object] = @interpreter_context[:current_object] = object
889
+ ti[:current_object] = object
875
890
  end
876
891
  if field
877
- @context[:current_field] = @interpreter_context[:current_field] = field
892
+ ti[:current_field] = field
878
893
  end
879
894
  if arguments
880
- @context[:current_arguments] = @interpreter_context[:current_arguments] = arguments
895
+ ti[:current_arguments] = arguments
881
896
  end
882
897
  if path
883
- @context[:current_path] = @interpreter_context[:current_path] = path
898
+ ti[:current_path] = path
884
899
  end
885
900
  end
886
901
 
@@ -940,13 +955,11 @@ module GraphQL
940
955
  # Set this pair in the Query context, but also in the interpeter namespace,
941
956
  # for compatibility.
942
957
  def set_interpreter_context(key, value)
943
- @interpreter_context[key] = value
944
- @context[key] = value
958
+ thread_info[key] = value
945
959
  end
946
960
 
947
961
  def delete_interpreter_context(key)
948
- @interpreter_context.delete(key)
949
- @context.delete(key)
962
+ (ti = thread_info) && ti.delete(key)
950
963
  end
951
964
 
952
965
  def resolve_type(type, value, path)
@@ -68,7 +68,7 @@ module GraphQL
68
68
  # they also have another item of state, which is private to that query
69
69
  # in particular, assign it here:
70
70
  runtime = Runtime.new(query: query)
71
- query.context.namespace(:interpreter)[:runtime] = runtime
71
+ query.context.namespace(:interpreter_runtime)[:runtime] = runtime
72
72
 
73
73
  query.trace("execute_query", {query: query}) do
74
74
  runtime.run_eager
@@ -90,7 +90,7 @@ module GraphQL
90
90
  query = multiplex.queries.length == 1 ? multiplex.queries[0] : nil
91
91
  queries = multiplex ? multiplex.queries : [query]
92
92
  final_values = queries.map do |query|
93
- runtime = query.context.namespace(:interpreter)[:runtime]
93
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
94
94
  # it might not be present if the query has an error
95
95
  runtime ? runtime.final_result : nil
96
96
  end
@@ -99,7 +99,7 @@ module GraphQL
99
99
  Interpreter::Resolve.resolve_all(final_values, multiplex.dataloader)
100
100
  end
101
101
  queries.each do |query|
102
- runtime = query.context.namespace(:interpreter)[:runtime]
102
+ runtime = query.context.namespace(:interpreter_runtime)[:runtime]
103
103
  if runtime
104
104
  runtime.delete_interpreter_context(:current_path)
105
105
  runtime.delete_interpreter_context(:current_field)
@@ -123,7 +123,7 @@ module GraphQL
123
123
  end
124
124
  else
125
125
  result = {
126
- "data" => query.context.namespace(:interpreter)[:runtime].final_result
126
+ "data" => query.context.namespace(:interpreter_runtime)[:runtime].final_result
127
127
  }
128
128
 
129
129
  if query.context.errors.any?
@@ -76,8 +76,8 @@ module GraphQL
76
76
  # @param field_name [String, Symbol]
77
77
  # @param arguments [Hash] Arguments which must match in the selection
78
78
  # @return [Boolean]
79
- def selects?(field_name, arguments: nil)
80
- selection(field_name, arguments: arguments).selected?
79
+ def selects?(field_name, selected_type: @selected_type, arguments: nil)
80
+ selection(field_name, selected_type: selected_type, arguments: arguments).selected?
81
81
  end
82
82
 
83
83
  # @return [Boolean] True if this lookahead represents a field that was requested
@@ -95,11 +95,22 @@ module GraphQL
95
95
  @query.get_field(selected_type, field_name)
96
96
  when Symbol
97
97
  # Try to avoid the `.to_s` below, if possible
98
- all_fields = @query.warden.fields(selected_type)
98
+ all_fields = if selected_type.kind.fields?
99
+ @query.warden.fields(selected_type)
100
+ else
101
+ # Handle unions by checking possible
102
+ @query.warden
103
+ .possible_types(selected_type)
104
+ .map { |t| @query.warden.fields(t) }
105
+ .flatten
106
+ end
107
+
99
108
  if (match_by_orig_name = all_fields.find { |f| f.original_name == field_name })
100
109
  match_by_orig_name
101
110
  else
102
- guessed_name = Schema::Member::BuildType.camelize(field_name.to_s)
111
+ # Symbol#name is only present on 3.0+
112
+ sym_s = field_name.respond_to?(:name) ? field_name.name : field_name.to_s
113
+ guessed_name = Schema::Member::BuildType.camelize(sym_s)
103
114
  @query.get_field(selected_type, guessed_name)
104
115
  end
105
116
  end
@@ -4,7 +4,7 @@ module GraphQL
4
4
  def self.query(include_deprecated_args: false, include_schema_description: false, include_is_repeatable: false, include_specified_by_url: false, include_is_one_of: false)
5
5
  # The introspection query to end all introspection queries, copied from
6
6
  # https://github.com/graphql/graphql-js/blob/master/src/utilities/introspectionQuery.js
7
- <<-QUERY
7
+ <<-QUERY.gsub(/\n{2,}/, "\n")
8
8
  query IntrospectionQuery {
9
9
  __schema {
10
10
  #{include_schema_description ? "description" : ""}
@@ -44,16 +44,18 @@ module GraphQL
44
44
  end
45
45
 
46
46
  def build_schema_node
47
- GraphQL::Language::Nodes::SchemaDefinition.new(
48
- query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
49
- mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
50
- subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
51
- # This only supports directives from parsing,
52
- # use a custom printer to add to this list.
53
- #
47
+ schema_options = {
54
48
  # `@schema.directives` is covered by `build_definition_nodes`
55
- directives: ast_directives(@schema),
56
- )
49
+ directives: definition_directives(@schema, :schema_directives),
50
+ }
51
+ if !schema_respects_root_name_conventions?(@schema)
52
+ schema_options.merge!({
53
+ query: (q = warden.root_type_for_operation("query")) && q.graphql_name,
54
+ mutation: (m = warden.root_type_for_operation("mutation")) && m.graphql_name,
55
+ subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
56
+ })
57
+ end
58
+ GraphQL::Language::Nodes::SchemaDefinition.new(schema_options)
57
59
  end
58
60
 
59
61
  def build_object_type_node(object_type)
@@ -283,7 +285,9 @@ module GraphQL
283
285
  private
284
286
 
285
287
  def include_schema_node?
286
- always_include_schema || !schema_respects_root_name_conventions?(schema)
288
+ always_include_schema ||
289
+ !schema_respects_root_name_conventions?(schema) ||
290
+ !schema.schema_directives.empty?
287
291
  end
288
292
 
289
293
  def schema_respects_root_name_conventions?(schema)
@@ -293,14 +297,14 @@ module GraphQL
293
297
  end
294
298
 
295
299
  def directives(member)
296
- definition_directives(member)
300
+ definition_directives(member, :directives)
297
301
  end
298
302
 
299
- def definition_directives(member)
300
- dirs = if !member.respond_to?(:directives) || member.directives.empty?
303
+ def definition_directives(member, directives_method)
304
+ dirs = if !member.respond_to?(directives_method) || member.directives.empty?
301
305
  []
302
306
  else
303
- member.directives.map do |dir|
307
+ member.public_send(directives_method).map do |dir|
304
308
  args = []
305
309
  dir.arguments.argument_values.each_value do |arg_value| # rubocop:disable Development/ContextIsPassedCop -- directive instance method
306
310
  arg_defn = arg_value.definition
@@ -324,10 +328,6 @@ module GraphQL
324
328
  dirs
325
329
  end
326
330
 
327
- def ast_directives(member)
328
- member.ast_node ? member.ast_node.directives : []
329
- end
330
-
331
331
  attr_reader :schema, :warden, :always_include_schema,
332
332
  :include_introspection_types, :include_built_in_directives, :include_built_in_scalars
333
333
  end
@@ -132,10 +132,11 @@ module GraphQL
132
132
  end
133
133
 
134
134
  def print_schema_definition(schema)
135
- if (schema.query.nil? || schema.query == 'Query') &&
136
- (schema.mutation.nil? || schema.mutation == 'Mutation') &&
137
- (schema.subscription.nil? || schema.subscription == 'Subscription') &&
138
- (schema.directives.empty?)
135
+ has_conventional_names = (schema.query.nil? || schema.query == 'Query') &&
136
+ (schema.mutation.nil? || schema.mutation == 'Mutation') &&
137
+ (schema.subscription.nil? || schema.subscription == 'Subscription')
138
+
139
+ if has_conventional_names && schema.directives.empty?
139
140
  return
140
141
  end
141
142
 
@@ -145,14 +146,22 @@ module GraphQL
145
146
  out << "\n "
146
147
  out << print_node(dir)
147
148
  end
148
- out << "\n{"
149
- else
150
- out << " {\n"
149
+ if !has_conventional_names
150
+ out << "\n"
151
+ end
151
152
  end
152
- out << " query: #{schema.query}\n" if schema.query
153
- out << " mutation: #{schema.mutation}\n" if schema.mutation
154
- out << " subscription: #{schema.subscription}\n" if schema.subscription
155
- out << "}"
153
+
154
+ if !has_conventional_names
155
+ if schema.directives.empty?
156
+ out << " "
157
+ end
158
+ out << "{\n"
159
+ out << " query: #{schema.query}\n" if schema.query
160
+ out << " mutation: #{schema.mutation}\n" if schema.mutation
161
+ out << " subscription: #{schema.subscription}\n" if schema.subscription
162
+ out << "}"
163
+ end
164
+ out
156
165
  end
157
166
 
158
167
  def print_scalar_type_definition(scalar_type)
@@ -111,7 +111,7 @@ module GraphQL
111
111
  end
112
112
 
113
113
  def current_path
114
- @query_context.namespace(:interpreter)[:current_path] || @no_path
114
+ @query_context[:current_path] || @no_path
115
115
  end
116
116
 
117
117
  def key?(key)
@@ -189,12 +189,16 @@ module GraphQL
189
189
 
190
190
  def_delegators :@query, :trace, :interpreter?
191
191
 
192
+ RUNTIME_METADATA_KEYS = Set.new([:current_object, :current_arguments, :current_field, :current_path])
192
193
  # @!method []=(key, value)
193
194
  # Reassign `key` to the hash passed to {Schema#execute} as `context:`
194
195
 
195
196
  # Lookup `key` from the hash passed to {Schema#execute} as `context:`
196
197
  def [](key)
197
- if @scoped_context.key?(key)
198
+ if RUNTIME_METADATA_KEYS.include?(key)
199
+ thread_info = Thread.current[:__graphql_runtime_info]
200
+ thread_info && thread_info[key]
201
+ elsif @scoped_context.key?(key)
198
202
  @scoped_context[key]
199
203
  else
200
204
  @provided_values[key]
@@ -212,7 +216,10 @@ module GraphQL
212
216
  UNSPECIFIED_FETCH_DEFAULT = Object.new
213
217
 
214
218
  def fetch(key, default = UNSPECIFIED_FETCH_DEFAULT)
215
- if @scoped_context.key?(key)
219
+ if RUNTIME_METADATA_KEYS.include?(key)
220
+ (thread_info = Thread.current[:__graphql_runtime_info]) &&
221
+ thread_info[key]
222
+ elsif @scoped_context.key?(key)
216
223
  scoped_context[key]
217
224
  elsif @provided_values.key?(key)
218
225
  @provided_values[key]
@@ -226,7 +233,10 @@ module GraphQL
226
233
  end
227
234
 
228
235
  def dig(key, *other_keys)
229
- if @scoped_context.key?(key)
236
+ if RUNTIME_METADATA_KEYS.include?(key)
237
+ (thread_info = Thread.current[:__graphql_runtime_info]).key?(key) &&
238
+ thread_info.dig(key)
239
+ elsif @scoped_context.key?(key)
230
240
  @scoped_context.dig(key, *other_keys)
231
241
  else
232
242
  @provided_values.dig(key, *other_keys)
@@ -259,7 +269,11 @@ module GraphQL
259
269
  # @param ns [Object] a usage-specific namespace identifier
260
270
  # @return [Hash] namespaced storage
261
271
  def namespace(ns)
262
- @storage[ns]
272
+ if ns == :interpreter
273
+ self
274
+ else
275
+ @storage[ns]
276
+ end
263
277
  end
264
278
 
265
279
  # @return [Boolean] true if this namespace was accessed before
@@ -6,16 +6,16 @@ module GraphQL
6
6
  module BuildFromDefinition
7
7
  class << self
8
8
  # @see {Schema.from_definition}
9
- def from_definition(definition_string, parser: GraphQL.default_parser, **kwargs)
10
- from_document(parser.parse(definition_string), **kwargs)
9
+ def from_definition(schema_superclass, definition_string, parser: GraphQL.default_parser, **kwargs)
10
+ from_document(schema_superclass, parser.parse(definition_string), **kwargs)
11
11
  end
12
12
 
13
- def from_definition_path(definition_path, parser: GraphQL.default_parser, **kwargs)
14
- from_document(parser.parse_file(definition_path), **kwargs)
13
+ def from_definition_path(schema_superclass, definition_path, parser: GraphQL.default_parser, **kwargs)
14
+ from_document(schema_superclass, parser.parse_file(definition_path), **kwargs)
15
15
  end
16
16
 
17
- def from_document(document, default_resolve:, using: {}, relay: false)
18
- Builder.build(document, default_resolve: default_resolve || {}, relay: relay, using: using)
17
+ def from_document(schema_superclass, document, default_resolve:, using: {}, relay: false)
18
+ Builder.build(schema_superclass, document, default_resolve: default_resolve || {}, relay: relay, using: using)
19
19
  end
20
20
  end
21
21
 
@@ -23,7 +23,7 @@ module GraphQL
23
23
  module Builder
24
24
  extend self
25
25
 
26
- def build(document, default_resolve:, using: {}, relay:)
26
+ def build(schema_superclass, document, default_resolve:, using: {}, relay:)
27
27
  raise InvalidDocumentError.new('Must provide a document ast.') if !document || !document.is_a?(GraphQL::Language::Nodes::Document)
28
28
 
29
29
  if default_resolve.is_a?(Hash)
@@ -36,7 +36,7 @@ module GraphQL
36
36
  end
37
37
  schema_definition = schema_defns.first
38
38
  types = {}
39
- directives = {}
39
+ directives = schema_superclass.directives.dup
40
40
  type_resolver = build_resolve_type(types, directives, ->(type_name) { types[type_name] ||= Schema::LateBoundType.new(type_name)})
41
41
  # Make a different type resolver because we need to coerce directive arguments
42
42
  # _while_ building the schema.
@@ -65,10 +65,14 @@ module GraphQL
65
65
  # In case any directives referenced built-in types for their arguments:
66
66
  replace_late_bound_types_with_built_in(types)
67
67
 
68
+ schema_extensions = nil
68
69
  document.definitions.each do |definition|
69
70
  case definition
70
71
  when GraphQL::Language::Nodes::SchemaDefinition, GraphQL::Language::Nodes::DirectiveDefinition
71
72
  nil # already handled
73
+ when GraphQL::Language::Nodes::SchemaExtension
74
+ schema_extensions ||= []
75
+ schema_extensions << definition
72
76
  else
73
77
  # It's possible that this was already loaded by the directives
74
78
  prev_type = types[definition.name]
@@ -103,7 +107,7 @@ module GraphQL
103
107
 
104
108
  raise InvalidDocumentError.new('Must provide schema definition with query type or a type named Query.') unless query_root_type
105
109
 
106
- Class.new(GraphQL::Schema) do
110
+ schema_class = Class.new(schema_superclass) do
107
111
  begin
108
112
  # Add these first so that there's some chance of resolving late-bound types
109
113
  orphan_types types.values
@@ -157,6 +161,14 @@ module GraphQL
157
161
  child_class.definition_default_resolve = self.definition_default_resolve
158
162
  end
159
163
  end
164
+
165
+ if schema_extensions
166
+ schema_extensions.each do |ext|
167
+ build_directives(schema_class, ext, type_resolver)
168
+ end
169
+ end
170
+
171
+ schema_class
160
172
  end
161
173
 
162
174
  NullResolveType = ->(type, obj, ctx) {
@@ -196,13 +208,18 @@ module GraphQL
196
208
 
197
209
  def build_directives(definition, ast_node, type_resolver)
198
210
  dirs = prepare_directives(ast_node, type_resolver)
199
- dirs.each do |dir_class, options|
200
- definition.directive(dir_class, **options)
211
+ dirs.each do |(dir_class, options)|
212
+ if definition.respond_to?(:schema_directive)
213
+ # it's a schema
214
+ definition.schema_directive(dir_class, **options)
215
+ else
216
+ definition.directive(dir_class, **options)
217
+ end
201
218
  end
202
219
  end
203
220
 
204
221
  def prepare_directives(ast_node, type_resolver)
205
- dirs = {}
222
+ dirs = []
206
223
  ast_node.directives.each do |dir_node|
207
224
  if dir_node.name == "deprecated"
208
225
  # This is handled using `deprecation_reason`
@@ -210,10 +227,10 @@ module GraphQL
210
227
  else
211
228
  dir_class = type_resolver.call(dir_node.name)
212
229
  if dir_class.nil?
213
- raise ArgumentError, "No definition for @#{dir_node.name} on #{ast_node.name} at #{ast_node.line}:#{ast_node.col}"
230
+ raise ArgumentError, "No definition for @#{dir_node.name} #{ast_node.respond_to?(:name) ? "on #{ast_node.name} " : ""}at #{ast_node.line}:#{ast_node.col}"
214
231
  end
215
232
  options = args_to_kwargs(dir_class, dir_node)
216
- dirs[dir_class] = options
233
+ dirs << [dir_class, options]
217
234
  end
218
235
  end
219
236
  dirs
@@ -389,7 +406,6 @@ module GraphQL
389
406
  graphql_name(interface_type_definition.name)
390
407
  description(interface_type_definition.description)
391
408
  interface_type_definition.interfaces.each do |interface_name|
392
- "Implements: #{interface_type_definition} -> #{interface_name}"
393
409
  interface_defn = type_resolver.call(interface_name)
394
410
  implements(interface_defn)
395
411
  end
@@ -39,7 +39,7 @@ module GraphQL
39
39
  transform_name = arguments[:by]
40
40
  if TRANSFORMS.include?(transform_name) && return_value.respond_to?(transform_name)
41
41
  return_value = return_value.public_send(transform_name)
42
- response = context.namespace(:interpreter)[:runtime].final_result
42
+ response = context.namespace(:interpreter_runtime)[:runtime].final_result
43
43
  *keys, last = path
44
44
  keys.each do |key|
45
45
  if response && (response = response[key])
@@ -129,10 +129,10 @@ module GraphQL
129
129
  def connection?
130
130
  if @connection.nil?
131
131
  # Provide default based on type name
132
- return_type_name = if @resolver_class && @resolver_class.type
133
- Member::BuildType.to_type_name(@resolver_class.type)
134
- elsif @return_type_expr
132
+ return_type_name = if @return_type_expr
135
133
  Member::BuildType.to_type_name(@return_type_expr)
134
+ elsif @resolver_class && @resolver_class.type
135
+ Member::BuildType.to_type_name(@resolver_class.type)
136
136
  else
137
137
  # As a last ditch, try to force loading the return type:
138
138
  type.unwrap.name
@@ -120,7 +120,7 @@ module GraphQL
120
120
  def camelize(string)
121
121
  return string if string == '_'
122
122
  return string unless string.include?("_")
123
- camelized = string.split('_').map(&:capitalize).join
123
+ camelized = string.split('_').each(&:capitalize!).join
124
124
  camelized[0] = camelized[0].downcase
125
125
  if (match_data = string.match(/\A(_+)/))
126
126
  camelized = "#{match_data[0]}#{camelized}"
@@ -11,8 +11,7 @@ module GraphQL
11
11
  # @return [void]
12
12
  def directive(dir_class, **options)
13
13
  @own_directives ||= []
14
- remove_directive(dir_class) unless dir_class.repeatable?
15
- @own_directives << dir_class.new(self, **options)
14
+ HasDirectives.add_directive(self, @own_directives, dir_class, options)
16
15
  nil
17
16
  end
18
17
 
@@ -20,78 +19,94 @@ module GraphQL
20
19
  # @param dir_class [Class<GraphQL::Schema::Directive>]
21
20
  # @return [viod]
22
21
  def remove_directive(dir_class)
23
- @own_directives && @own_directives.reject! { |d| d.is_a?(dir_class) }
22
+ HasDirectives.remove_directive(@own_directives, dir_class)
24
23
  nil
25
24
  end
26
25
 
27
26
  NO_DIRECTIVES = [].freeze
28
27
 
29
28
  def directives
30
- case self
31
- when Class
32
- inherited_directives = if superclass.respond_to?(:directives)
33
- superclass.directives
34
- else
35
- NO_DIRECTIVES
36
- end
37
- if inherited_directives.any? && @own_directives
38
- dirs = []
39
- merge_directives(dirs, inherited_directives)
40
- merge_directives(dirs, @own_directives)
41
- dirs
42
- elsif @own_directives
43
- @own_directives
44
- elsif inherited_directives.any?
45
- inherited_directives
46
- else
47
- NO_DIRECTIVES
48
- end
49
- when Module
50
- dirs = nil
51
- self.ancestors.reverse_each do |ancestor|
52
- if ancestor.respond_to?(:own_directives) &&
53
- (anc_dirs = ancestor.own_directives).any?
29
+ HasDirectives.get_directives(self, @own_directives, :directives)
30
+ end
31
+
32
+ class << self
33
+ def add_directive(schema_member, directives, directive_class, directive_options)
34
+ remove_directive(directives, directive_class) unless directive_class.repeatable?
35
+ directives << directive_class.new(schema_member, **directive_options)
36
+ end
37
+
38
+ def remove_directive(directives, directive_class)
39
+ directives && directives.reject! { |d| d.is_a?(directive_class) }
40
+ end
41
+
42
+ def get_directives(schema_member, directives, directives_method)
43
+ case schema_member
44
+ when Class
45
+ inherited_directives = if schema_member.superclass.respond_to?(directives_method)
46
+ get_directives(schema_member.superclass, schema_member.superclass.public_send(directives_method), directives_method)
47
+ else
48
+ NO_DIRECTIVES
49
+ end
50
+ if inherited_directives.any? && directives
51
+ dirs = []
52
+ merge_directives(dirs, inherited_directives)
53
+ merge_directives(dirs, directives)
54
+ dirs
55
+ elsif directives
56
+ directives
57
+ elsif inherited_directives.any?
58
+ inherited_directives
59
+ else
60
+ NO_DIRECTIVES
61
+ end
62
+ when Module
63
+ dirs = nil
64
+ schema_member.ancestors.reverse_each do |ancestor|
65
+ if ancestor.respond_to?(:own_directives) &&
66
+ (anc_dirs = ancestor.own_directives).any?
67
+ dirs ||= []
68
+ merge_directives(dirs, anc_dirs)
69
+ end
70
+ end
71
+ if directives
54
72
  dirs ||= []
55
- merge_directives(dirs, anc_dirs)
73
+ merge_directives(dirs, directives)
56
74
  end
75
+ dirs || NO_DIRECTIVES
76
+ when HasDirectives
77
+ directives || NO_DIRECTIVES
78
+ else
79
+ raise "Invariant: how could #{schema_member} not be a Class, Module, or instance of HasDirectives?"
57
80
  end
58
- if own_directives
59
- dirs ||= []
60
- merge_directives(dirs, own_directives)
81
+ end
82
+
83
+ private
84
+
85
+ # Modify `target` by adding items from `dirs` such that:
86
+ # - Any name conflict is overriden by the incoming member of `dirs`
87
+ # - Any other member of `dirs` is appended
88
+ # @param target [Array<GraphQL::Schema::Directive>]
89
+ # @param dirs [Array<GraphQL::Schema::Directive>]
90
+ # @return [void]
91
+ def merge_directives(target, dirs)
92
+ dirs.each do |dir|
93
+ if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
94
+ target.slice!(idx)
95
+ target.insert(idx, dir)
96
+ else
97
+ target << dir
98
+ end
61
99
  end
62
- dirs || NO_DIRECTIVES
63
- when HasDirectives
64
- @own_directives || NO_DIRECTIVES
65
- else
66
- raise "Invariant: how could #{self} not be a Class, Module, or instance of HasDirectives?"
100
+ nil
67
101
  end
68
102
  end
69
103
 
104
+
70
105
  protected
71
106
 
72
107
  def own_directives
73
108
  @own_directives
74
109
  end
75
-
76
- private
77
-
78
- # Modify `target` by adding items from `dirs` such that:
79
- # - Any name conflict is overriden by the incoming member of `dirs`
80
- # - Any other member of `dirs` is appended
81
- # @param target [Array<GraphQL::Schema::Directive>]
82
- # @param dirs [Array<GraphQL::Schema::Directive>]
83
- # @return [void]
84
- def merge_directives(target, dirs)
85
- dirs.each do |dir|
86
- if (idx = target.find_index { |d| d.graphql_name == dir.graphql_name })
87
- target.slice!(idx)
88
- target.insert(idx, dir)
89
- else
90
- target << dir
91
- end
92
- end
93
- nil
94
- end
95
110
  end
96
111
  end
97
112
  end
@@ -91,7 +91,7 @@ module GraphQL
91
91
  resolver_fields = all_field_definitions
92
92
  Class.new(object_class) do
93
93
  graphql_name("#{resolver_name}Payload")
94
- description("Autogenerated return type of #{resolver_name}")
94
+ description("Autogenerated return type of #{resolver_name}.")
95
95
  resolver_fields.each do |f|
96
96
  # Reattach the already-defined field here
97
97
  # (The field's `.owner` will still point to the mutation, not the object type, I think)
@@ -11,6 +11,9 @@ module GraphQL
11
11
  # @return [Class<GraphQL::Schema::Union>, Module<GraphQL::Schema::Interface>]
12
12
  attr_reader :abstract_type
13
13
 
14
+ # @return [Hash]
15
+ attr_reader :options
16
+
14
17
  # Called when an object is hooked up to an abstract type, such as {Schema::Union.possible_types}
15
18
  # or {Schema::Object.implements} (for interfaces).
16
19
  #
@@ -110,9 +110,10 @@ module GraphQL
110
110
  # @param using [Hash] Plugins to attach to the created schema with `use(key, value)`
111
111
  # @return [Class] the schema described by `document`
112
112
  def from_definition(definition_or_path, default_resolve: nil, parser: GraphQL.default_parser, using: {})
113
- # If the file ends in `.graphql`, treat it like a filepath
114
- 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")
115
115
  GraphQL::Schema::BuildFromDefinition.from_definition_path(
116
+ self,
116
117
  definition_or_path,
117
118
  default_resolve: default_resolve,
118
119
  parser: parser,
@@ -120,6 +121,7 @@ module GraphQL
120
121
  )
121
122
  else
122
123
  GraphQL::Schema::BuildFromDefinition.from_definition(
124
+ self,
123
125
  definition_or_path,
124
126
  default_resolve: default_resolve,
125
127
  parser: parser,
@@ -738,11 +740,10 @@ module GraphQL
738
740
  def handle_or_reraise(context, err)
739
741
  handler = Execution::Errors.find_handler_for(self, err.class)
740
742
  if handler
741
- runtime_info = context.namespace(:interpreter) || {}
742
- obj = runtime_info[:current_object]
743
- args = runtime_info[:current_arguments]
743
+ obj = context[:current_object]
744
+ args = context[:current_arguments]
744
745
  args = args && args.keyword_arguments
745
- field = runtime_info[:current_field]
746
+ field = context[:current_field]
746
747
  if obj.is_a?(GraphQL::Schema::Object)
747
748
  obj = obj.object
748
749
  end
@@ -816,6 +817,15 @@ module GraphQL
816
817
  member.accessible?(ctx)
817
818
  end
818
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
+
819
829
  # This hook is called when a client tries to access one or more
820
830
  # fields that fail the `accessible?` check.
821
831
  #
@@ -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.14"
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.14
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-09-08 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
@@ -538,6 +538,7 @@ files:
538
538
  - lib/graphql/tracing/appoptics_tracing.rb
539
539
  - lib/graphql/tracing/appsignal_tracing.rb
540
540
  - lib/graphql/tracing/data_dog_tracing.rb
541
+ - lib/graphql/tracing/instrumentation_tracing.rb
541
542
  - lib/graphql/tracing/new_relic_tracing.rb
542
543
  - lib/graphql/tracing/notifications_tracing.rb
543
544
  - lib/graphql/tracing/platform_tracing.rb