graphql 1.10.6 → 1.10.7

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