graphql 1.10.6 → 1.10.7

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1b006fe34754dbc2e3ee7ef459e05bdf325755538e013efd96c9eef3990af9a7
4
- data.tar.gz: b655b349e59ea3a34d6ed71fb3870af1cc91afc75bf7147cb0d9b436b78a97c8
3
+ metadata.gz: dffbc22cc92ab7e74cf26f0a6e916ae1f03014722e350318af8ee367c9eb6f40
4
+ data.tar.gz: 8b98748a98c2a7b5d471542989245a16d148b44519c69aa51cec062649357b31
5
5
  SHA512:
6
- metadata.gz: 9a07c35f18dfdb53924fc9ff8466c3255c7235f855d5ea7380738e4cde90c47c6cd12aaa505a27ee1c3ec3bee84a8a82ed9ec113cbbd362ef152a8b1ec6cdf38
7
- data.tar.gz: 23e61762fd23870cb651334aa84a9e83bb9dda486b114ea8bb2b937eeb77705bac200255b321983caf4f81d6efd0b231a992b618563b86d6446b68585ec429bc
6
+ metadata.gz: bf169d1fe41d60ad045e0faae46cfa263d4b5f2bffd56bdd527b940702e9a6e44996d7b8402a6a213a593e68adcd1b885b3ba8d7240b236db1383ae9d9073f6c
7
+ data.tar.gz: 24293245691980f79e316977fe24a6dfc6025f25d7d46806d6e46a8cc44ce9d1f84a7217487fd147209d6bef4a4ffdb0d67645fb87506850e420683a745aa90a
@@ -347,9 +347,15 @@ module GraphQL
347
347
  end
348
348
  end
349
349
  end
350
- rescue NoMethodError
351
- # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
352
- raise ListResultFailedError.new(value: value, field: field, path: path)
350
+ rescue NoMethodError => err
351
+ # Ruby 2.2 doesn't have NoMethodError#receiver, can't check that one in this case. (It's been EOL since 2017.)
352
+ if err.name == :each && (err.respond_to?(:receiver) ? err.receiver == value : true)
353
+ # This happens when the GraphQL schema doesn't match the implementation. Help the dev debug.
354
+ raise ListResultFailedError.new(value: value, field: field, path: path)
355
+ else
356
+ # This was some other NoMethodError -- let it bubble to reveal the real error.
357
+ raise
358
+ end
353
359
  end
354
360
 
355
361
  response_list
@@ -49,7 +49,7 @@ module GraphQL
49
49
  subscription: (s = warden.root_type_for_operation("subscription")) && s.graphql_name,
50
50
  # This only supports directives from parsing,
51
51
  # use a custom printer to add to this list.
52
- directives: @schema.ast_node ? @schema.ast_node.directives : [],
52
+ directives: ast_directives(@schema),
53
53
  )
54
54
  end
55
55
 
@@ -59,32 +59,26 @@ module GraphQL
59
59
  interfaces: warden.interfaces(object_type).sort_by(&:graphql_name).map { |iface| build_type_name_node(iface) },
60
60
  fields: build_field_nodes(warden.fields(object_type)),
61
61
  description: object_type.description,
62
+ directives: ast_directives(object_type),
62
63
  )
63
64
  end
64
65
 
65
66
  def build_field_node(field)
66
- field_node = GraphQL::Language::Nodes::FieldDefinition.new(
67
+ GraphQL::Language::Nodes::FieldDefinition.new(
67
68
  name: field.graphql_name,
68
69
  arguments: build_argument_nodes(warden.arguments(field)),
69
70
  type: build_type_name_node(field.type),
70
71
  description: field.description,
72
+ directives: ast_directives(field),
71
73
  )
72
-
73
- if field.deprecation_reason
74
- field_node = field_node.merge_directive(
75
- name: GraphQL::Directive::DeprecatedDirective.graphql_name,
76
- arguments: [GraphQL::Language::Nodes::Argument.new(name: "reason", value: field.deprecation_reason)]
77
- )
78
- end
79
-
80
- field_node
81
74
  end
82
75
 
83
76
  def build_union_type_node(union_type)
84
77
  GraphQL::Language::Nodes::UnionTypeDefinition.new(
85
78
  name: union_type.graphql_name,
86
79
  description: union_type.description,
87
- types: warden.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) }
80
+ types: warden.possible_types(union_type).sort_by(&:graphql_name).map { |type| build_type_name_node(type) },
81
+ directives: ast_directives(union_type),
88
82
  )
89
83
  end
90
84
 
@@ -92,7 +86,8 @@ module GraphQL
92
86
  GraphQL::Language::Nodes::InterfaceTypeDefinition.new(
93
87
  name: interface_type.graphql_name,
94
88
  description: interface_type.description,
95
- fields: build_field_nodes(warden.fields(interface_type))
89
+ fields: build_field_nodes(warden.fields(interface_type)),
90
+ directives: ast_directives(interface_type),
96
91
  )
97
92
  end
98
93
 
@@ -103,29 +98,23 @@ module GraphQL
103
98
  build_enum_value_node(enum_value)
104
99
  end,
105
100
  description: enum_type.description,
101
+ directives: ast_directives(enum_type),
106
102
  )
107
103
  end
108
104
 
109
105
  def build_enum_value_node(enum_value)
110
- enum_value_node = GraphQL::Language::Nodes::EnumValueDefinition.new(
106
+ GraphQL::Language::Nodes::EnumValueDefinition.new(
111
107
  name: enum_value.graphql_name,
112
108
  description: enum_value.description,
109
+ directives: ast_directives(enum_value),
113
110
  )
114
-
115
- if enum_value.deprecation_reason
116
- enum_value_node = enum_value_node.merge_directive(
117
- name: GraphQL::Directive::DeprecatedDirective.graphql_name,
118
- arguments: [GraphQL::Language::Nodes::Argument.new(name: "reason", value: enum_value.deprecation_reason)]
119
- )
120
- end
121
-
122
- enum_value_node
123
111
  end
124
112
 
125
113
  def build_scalar_type_node(scalar_type)
126
114
  GraphQL::Language::Nodes::ScalarTypeDefinition.new(
127
115
  name: scalar_type.graphql_name,
128
116
  description: scalar_type.description,
117
+ directives: ast_directives(scalar_type),
129
118
  )
130
119
  end
131
120
 
@@ -141,6 +130,7 @@ module GraphQL
141
130
  description: argument.description,
142
131
  type: build_type_name_node(argument.type),
143
132
  default_value: default_value,
133
+ directives: ast_directives(argument),
144
134
  )
145
135
 
146
136
  argument_node
@@ -151,6 +141,7 @@ module GraphQL
151
141
  name: input_object.graphql_name,
152
142
  fields: build_argument_nodes(warden.arguments(input_object)),
153
143
  description: input_object.description,
144
+ directives: ast_directives(input_object),
154
145
  )
155
146
  end
156
147
 
@@ -292,6 +283,34 @@ module GraphQL
292
283
  (schema.subscription.nil? || schema.subscription.graphql_name == 'Subscription')
293
284
  end
294
285
 
286
+ def ast_directives(member)
287
+ ast_directives = member.ast_node ? member.ast_node.directives : []
288
+
289
+ # If this schema was built from IDL, it will already have `@deprecated` in `ast_node.directives`
290
+ if member.respond_to?(:deprecation_reason) &&
291
+ (reason = member.deprecation_reason) &&
292
+ ast_directives.none? { |d| d.name == "deprecated" }
293
+
294
+ arguments = []
295
+
296
+ if reason != GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON
297
+ arguments << GraphQL::Language::Nodes::Argument.new(
298
+ name: "reason",
299
+ value: reason
300
+ )
301
+ end
302
+
303
+ ast_directives += [
304
+ GraphQL::Language::Nodes::Directive.new(
305
+ name: GraphQL::Directive::DeprecatedDirective.graphql_name,
306
+ arguments: arguments
307
+ )
308
+ ]
309
+ end
310
+
311
+ ast_directives
312
+ end
313
+
295
314
  attr_reader :schema, :warden, :always_include_schema,
296
315
  :include_introspection_types, :include_built_in_directives, :include_built_in_scalars
297
316
  end
@@ -555,8 +555,17 @@ module GraphQL
555
555
  # @param context [GraphQL::Query::Context] The context for the current query
556
556
  # @return [Array<GraphQL::ObjectType>] types which belong to `type_defn` in this schema
557
557
  def possible_types(type_defn, context = GraphQL::Query::NullContext)
558
- @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
559
- @possible_types.possible_types(type_defn, context)
558
+ if context == GraphQL::Query::NullContext
559
+ @possible_types ||= GraphQL::Schema::PossibleTypes.new(self)
560
+ @possible_types.possible_types(type_defn, context)
561
+ else
562
+ # Use the incoming context to cache this instance --
563
+ # if it were cached on the schema, we'd have a memory leak
564
+ # https://github.com/rmosolgo/graphql-ruby/issues/2878
565
+ ns = context.namespace(:possible_types)
566
+ per_query_possible_types = ns[:possible_types] ||= GraphQL::Schema::PossibleTypes.new(self)
567
+ per_query_possible_types.possible_types(type_defn, context)
568
+ end
560
569
  end
561
570
 
562
571
  # @see [GraphQL::Schema::Warden] Resticted access to root types
@@ -31,6 +31,11 @@ module GraphQL
31
31
  nil
32
32
  end
33
33
 
34
+ # Also for implementing introspection
35
+ def description
36
+ nil
37
+ end
38
+
34
39
  def coerce_result(value, ctx)
35
40
  value.map { |i| i.nil? ? nil : of_type.coerce_result(i, ctx) }
36
41
  end
@@ -5,18 +5,19 @@ module GraphQL
5
5
  # to make a schema. This schema is missing some important details like
6
6
  # `resolve` functions, but it does include the full type system,
7
7
  # so you can use it to validate queries.
8
+ #
9
+ # @see GraphQL::Schema.from_introspection for a public API
8
10
  module Loader
9
11
  extend self
10
12
 
11
13
  # Create schema with the result of an introspection query.
12
14
  # @param introspection_result [Hash] A response from {GraphQL::Introspection::INTROSPECTION_QUERY}
13
- # @return [GraphQL::Schema] the schema described by `input`
14
- # @deprecated Use {GraphQL::Schema.from_introspection} instead
15
+ # @return [Class] the schema described by `input`
15
16
  def load(introspection_result)
16
17
  schema = introspection_result.fetch("data").fetch("__schema")
17
18
 
18
19
  types = {}
19
- type_resolver = ->(type) { -> { resolve_type(types, type) } }
20
+ type_resolver = ->(type) { resolve_type(types, type) }
20
21
 
21
22
  schema.fetch("types").each do |type|
22
23
  next if type.fetch("name").start_with?("__")
@@ -24,18 +25,22 @@ module GraphQL
24
25
  types[type["name"]] = type_object
25
26
  end
26
27
 
27
- kargs = { orphan_types: types.values, resolve_type: NullResolveType }
28
- [:query, :mutation, :subscription].each do |root|
29
- type = schema["#{root}Type"]
30
- kargs[root] = types.fetch(type.fetch("name")) if type
31
- end
28
+ Class.new(GraphQL::Schema) do
29
+ orphan_types(types.values)
32
30
 
33
- Schema.define(**kargs, raise_definition_error: true)
34
- end
31
+ def self.resolve_type(*)
32
+ raise(GraphQL::RequiredImplementationMissingError, "This schema was loaded from string, so it can't resolve types for objects")
33
+ end
35
34
 
36
- NullResolveType = ->(type, obj, ctx) {
37
- raise(GraphQL::RequiredImplementationMissingError, "This schema was loaded from string, so it can't resolve types for objects")
38
- }
35
+ [:query, :mutation, :subscription].each do |root|
36
+ type = schema["#{root}Type"]
37
+ if type
38
+ type_defn = types.fetch(type.fetch("name"))
39
+ self.public_send(root, type_defn)
40
+ end
41
+ end
42
+ end
43
+ end
39
44
 
40
45
  NullScalarCoerce = ->(val, _ctx) { val }
41
46
 
@@ -48,14 +53,14 @@ module GraphQL
48
53
  type_name = type.fetch("name")
49
54
  type = types[type_name] || Schema::BUILT_IN_TYPES[type_name]
50
55
  if type.nil?
51
- raise "Type not found: #{type_name.inspect} among #{types.keys.sort}"
56
+ GraphQL::Schema::LateBoundType.new(type_name)
52
57
  else
53
- type.graphql_definition
58
+ type
54
59
  end
55
60
  when "LIST"
56
- ListType.new(of_type: resolve_type(types, type.fetch("ofType")))
61
+ Schema::List.new(resolve_type(types, type.fetch("ofType")))
57
62
  when "NON_NULL"
58
- NonNullType.new(of_type: resolve_type(types, type.fetch("ofType")))
63
+ Schema::NonNull.new(resolve_type(types, type.fetch("ofType")))
59
64
  else
60
65
  fail GraphQL::RequiredImplementationMissingError, "#{kind} not implemented"
61
66
  end
@@ -83,109 +88,108 @@ module GraphQL
83
88
  end
84
89
 
85
90
  def define_type(type, type_resolver)
91
+ loader = self
86
92
  case type.fetch("kind")
87
93
  when "ENUM"
88
- EnumType.define(
89
- name: type["name"],
90
- description: type["description"],
91
- values: type["enumValues"].map { |enum|
92
- EnumType::EnumValue.define(
93
- name: enum["name"],
94
- description: enum["description"],
95
- deprecation_reason: enum["deprecationReason"],
96
- value: enum["name"]
94
+ Class.new(GraphQL::Schema::Enum) do
95
+ graphql_name(type["name"])
96
+ description(type["description"])
97
+ type["enumValues"].each do |enum_value|
98
+ value(
99
+ enum_value["name"],
100
+ description: enum_value["description"],
101
+ deprecation_reason: enum_value["deprecation_reason"],
97
102
  )
98
- })
103
+ end
104
+ end
99
105
  when "INTERFACE"
100
- InterfaceType.define(
101
- name: type["name"],
102
- description: type["description"],
103
- fields: Hash[(type["fields"] || []).map { |field|
104
- [field["name"], define_type(field.merge("kind" => "FIELD"), type_resolver)]
105
- }]
106
- )
106
+ Module.new do
107
+ include GraphQL::Schema::Interface
108
+ graphql_name(type["name"])
109
+ description(type["description"])
110
+ loader.build_fields(self, type["fields"] || [], type_resolver)
111
+ end
107
112
  when "INPUT_OBJECT"
108
- InputObjectType.define(
109
- name: type["name"],
110
- description: type["description"],
111
- arguments: Hash[type["inputFields"].map { |arg|
112
- [arg["name"], define_type(arg.merge("kind" => "ARGUMENT"), type_resolver)]
113
- }]
114
- )
115
- when "OBJECT"
116
- ObjectType.define(
117
- name: type["name"],
118
- description: type["description"],
119
- interfaces: (type["interfaces"] || []).map { |interface|
120
- type_resolver.call(interface)
121
- },
122
- fields: Hash[type["fields"].map { |field|
123
- [field["name"], define_type(field.merge("kind" => "FIELD"), type_resolver)]
124
- }]
125
- )
126
- when "FIELD"
127
- defns = {
128
- name: type["name"],
129
- type: type_resolver.call(type["type"]),
130
- description: type["description"],
131
- }
132
-
133
- # Avoid passing an empty hash, which warns on Ruby 2.7
134
- if type["args"].any?
135
- defns[:arguments] = Hash[type["args"].map { |arg|
136
- [arg["name"], define_type(arg.merge("kind" => "ARGUMENT"), type_resolver)]
137
- }]
113
+ Class.new(GraphQL::Schema::InputObject) do
114
+ graphql_name(type["name"])
115
+ description(type["description"])
116
+ loader.build_arguments(self, type["inputFields"], type_resolver)
138
117
  end
139
-
140
- GraphQL::Field.define(**defns)
141
- when "ARGUMENT"
142
- kwargs = {}
143
- if type["defaultValue"]
144
- kwargs[:default_value] = begin
145
- default_value_str = type["defaultValue"]
146
-
147
- dummy_query_str = "query getStuff($var: InputObj = #{default_value_str}) { __typename }"
148
-
149
- # Returns a `GraphQL::Language::Nodes::Document`:
150
- dummy_query_ast = GraphQL.parse(dummy_query_str)
151
-
152
- # Reach into the AST for the default value:
153
- input_value_ast = dummy_query_ast.definitions.first.variables.first.default_value
154
-
155
- extract_default_value(default_value_str, input_value_ast)
118
+ when "OBJECT"
119
+ Class.new(GraphQL::Schema::Object) do
120
+ graphql_name(type["name"])
121
+ description(type["description"])
122
+ if type["interfaces"]
123
+ type["interfaces"].each do |interface_type|
124
+ implements(type_resolver.call(interface_type))
125
+ end
156
126
  end
127
+ loader.build_fields(self, type["fields"], type_resolver)
157
128
  end
158
-
159
- GraphQL::Argument.define(
160
- name: type["name"],
161
- type: type_resolver.call(type["type"]),
162
- description: type["description"],
163
- method_access: false,
164
- **kwargs
165
- )
166
129
  when "SCALAR"
167
130
  type_name = type.fetch("name")
168
- if GraphQL::Schema::BUILT_IN_TYPES[type_name]
169
- GraphQL::Schema::BUILT_IN_TYPES[type_name]
131
+ if (builtin = GraphQL::Schema::BUILT_IN_TYPES[type_name])
132
+ builtin
170
133
  else
171
- ScalarType.define(
172
- name: type["name"],
173
- description: type["description"],
174
- coerce: NullScalarCoerce,
175
- )
134
+ Class.new(GraphQL::Schema::Scalar) do
135
+ graphql_name(type["name"])
136
+ description(type["description"])
137
+ end
176
138
  end
177
139
  when "UNION"
178
- UnionType.define(
179
- name: type["name"],
180
- description: type["description"],
181
- possible_types: type["possibleTypes"].map { |possible_type|
182
- type_resolver.call(possible_type)
183
- }
184
- )
140
+ Class.new(GraphQL::Schema::Union) do
141
+ graphql_name(type["name"])
142
+ description(type["description"])
143
+ possible_types(*(type["possibleTypes"].map { |pt| type_resolver.call(pt) }))
144
+ end
185
145
  else
186
146
  fail GraphQL::RequiredImplementationMissingError, "#{type["kind"]} not implemented"
187
147
  end
188
148
  end
149
+
150
+ public
151
+
152
+ def build_fields(type_defn, fields, type_resolver)
153
+ loader = self
154
+ fields.each do |field_hash|
155
+ type_defn.field(
156
+ field_hash["name"],
157
+ type: type_resolver.call(field_hash["type"]),
158
+ description: field_hash["description"],
159
+ null: true,
160
+ ) do
161
+ if field_hash["args"].any?
162
+ loader.build_arguments(self, field_hash["args"], type_resolver)
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ def build_arguments(arg_owner, args, type_resolver)
169
+ args.each do |arg|
170
+ kwargs = {
171
+ type: type_resolver.call(arg["type"]),
172
+ description: arg["description"],
173
+ required: false,
174
+ }
175
+
176
+ if arg["defaultValue"]
177
+ default_value_str = arg["defaultValue"]
178
+
179
+ dummy_query_str = "query getStuff($var: InputObj = #{default_value_str}) { __typename }"
180
+
181
+ # Returns a `GraphQL::Language::Nodes::Document`:
182
+ dummy_query_ast = GraphQL.parse(dummy_query_str)
183
+
184
+ # Reach into the AST for the default value:
185
+ input_value_ast = dummy_query_ast.definitions.first.variables.first.default_value
186
+
187
+ kwargs[:default_value] = extract_default_value(default_value_str, input_value_ast)
188
+ end
189
+
190
+ arg_owner.argument(arg["name"], **kwargs)
191
+ end
192
+ end
189
193
  end
190
194
  end
191
195
  end
@@ -57,6 +57,11 @@ module GraphQL
57
57
  def coerce_result(value, ctx)
58
58
  of_type.coerce_result(value, ctx)
59
59
  end
60
+
61
+ # This is for implementing introspection
62
+ def description
63
+ nil
64
+ end
60
65
  end
61
66
  end
62
67
  end
@@ -162,7 +162,7 @@ module GraphQL
162
162
  obj_type = GraphQL::ObjectType.new
163
163
  obj_type.name = graphql_name
164
164
  obj_type.description = description
165
- obj_type.structural_interface_type_memberships = own_interface_type_memberships
165
+ obj_type.structural_interface_type_memberships = interface_type_memberships
166
166
  obj_type.introspection = introspection
167
167
  obj_type.mutation = mutation
168
168
  obj_type.ast_node = ast_node
@@ -94,20 +94,6 @@ module GraphQL
94
94
  print(node)
95
95
  end
96
96
 
97
- def print_directive(directive)
98
- if directive.name == "deprecated"
99
- reason = directive.arguments.find { |arg| arg.name == "reason" }
100
-
101
- if reason.value == GraphQL::Schema::Directive::DEFAULT_DEPRECATION_REASON
102
- "@deprecated"
103
- else
104
- "@deprecated(reason: #{reason.value.to_s.inspect})"
105
- end
106
- else
107
- super
108
- end
109
- end
110
-
111
97
  class IntrospectionPrinter < GraphQL::Language::Printer
112
98
  def print_schema_definition(schema)
113
99
  "schema {\n query: Root\n}"
@@ -15,10 +15,10 @@ module GraphQL
15
15
  class ISO8601Date < GraphQL::Schema::Scalar
16
16
  description "An ISO 8601-encoded date"
17
17
 
18
- # @param value [Date]
18
+ # @param value [Date,DateTime,String]
19
19
  # @return [String]
20
20
  def self.coerce_result(value, _ctx)
21
- value.iso8601
21
+ Date.parse(value.to_s).iso8601
22
22
  end
23
23
 
24
24
  # @param str_value [String]
@@ -29,12 +29,22 @@ module GraphQL
29
29
  @time_precision = value
30
30
  end
31
31
 
32
- # @param value [DateTime]
32
+ # @param value [Date,DateTime,String]
33
33
  # @return [String]
34
- def self.coerce_result(value, _ctx)
35
- value.iso8601(time_precision)
36
- rescue ArgumentError
37
- raise GraphQL::Error, "An incompatible object (#{value.class}) was given to #{self}. Make sure that only DateTimes are used with this type."
34
+ def self.coerce_result(value, _ctx)\
35
+ case value
36
+ when DateTime
37
+ return value.iso8601(time_precision)
38
+ when Date
39
+ return DateTime.parse(value.to_s).iso8601(time_precision)
40
+ when ::String
41
+ return DateTime.parse(value).iso8601(time_precision)
42
+ else
43
+ # In case some other API-compliant thing is given:
44
+ return value.iso8601(time_precision)
45
+ end
46
+ rescue StandardError => error
47
+ raise GraphQL::Error, "An incompatible object (#{value.class}) was given to #{self}. Make sure that only Dates, DateTimes, and well-formatted Strings are used with this type. (#{error.message})"
38
48
  end
39
49
 
40
50
  # @param str_value [String]
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module GraphQL
3
- VERSION = "1.10.6"
3
+ VERSION = "1.10.7"
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: 1.10.6
4
+ version: 1.10.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-04-06 00:00:00.000000000 Z
11
+ date: 2020-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips