graphql 1.7.7 → 1.7.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/enum_type.rb +1 -1
  3. data/lib/graphql/field.rb +10 -1
  4. data/lib/graphql/input_object_type.rb +3 -1
  5. data/lib/graphql/introspection/arguments_field.rb +1 -0
  6. data/lib/graphql/introspection/enum_values_field.rb +1 -0
  7. data/lib/graphql/introspection/fields_field.rb +1 -0
  8. data/lib/graphql/introspection/input_fields_field.rb +1 -0
  9. data/lib/graphql/introspection/interfaces_field.rb +1 -0
  10. data/lib/graphql/introspection/of_type_field.rb +1 -0
  11. data/lib/graphql/introspection/possible_types_field.rb +1 -0
  12. data/lib/graphql/introspection/schema_field.rb +1 -0
  13. data/lib/graphql/introspection/type_by_name_field.rb +1 -0
  14. data/lib/graphql/introspection/typename_field.rb +1 -0
  15. data/lib/graphql/language.rb +1 -0
  16. data/lib/graphql/language/document_from_schema_definition.rb +129 -37
  17. data/lib/graphql/language/generation.rb +3 -182
  18. data/lib/graphql/language/nodes.rb +12 -2
  19. data/lib/graphql/language/parser.rb +63 -55
  20. data/lib/graphql/language/parser.y +2 -1
  21. data/lib/graphql/language/printer.rb +351 -0
  22. data/lib/graphql/object_type.rb +1 -1
  23. data/lib/graphql/query/arguments.rb +27 -9
  24. data/lib/graphql/query/literal_input.rb +4 -1
  25. data/lib/graphql/schema/printer.rb +33 -266
  26. data/lib/graphql/tracing/scout_tracing.rb +2 -2
  27. data/lib/graphql/version.rb +1 -1
  28. data/spec/graphql/language/document_from_schema_definition_spec.rb +729 -296
  29. data/spec/graphql/language/generation_spec.rb +21 -186
  30. data/spec/graphql/language/nodes_spec.rb +21 -0
  31. data/spec/graphql/language/printer_spec.rb +203 -0
  32. data/spec/graphql/query/arguments_spec.rb +33 -11
  33. data/spec/graphql/schema/build_from_definition_spec.rb +13 -4
  34. data/spec/graphql/schema/printer_spec.rb +14 -14
  35. metadata +5 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38483eef219e223ada69c9b3abf6be4b21b18631
4
- data.tar.gz: 6e7b06898fbc81c29dd817ee9817ae54e4a44ada
3
+ metadata.gz: d85dab3949103799bd681457cdb2fecd6ddcaddf
4
+ data.tar.gz: ed5808c8a3caba6bf7834c0c7e2299ad5597eab3
5
5
  SHA512:
6
- metadata.gz: 8c1813ee4f6758846e9f5620d1952517c9d7a3d714e265d18da05fcd68425f152afdae56f8c400fd55d4e455e03443f26d90e04fd69578d85c71b3130f5eaf0f
7
- data.tar.gz: 41480a626d5acf9db70ea864438e192bab03ec56ad89c137f9f5e63e9ada426c3fd812b9099e8f4c621e3705740fd7daf1d1f74019c632ebd19f54e62608eb90
6
+ metadata.gz: 19330668600890f1dd17e4231e5ded4a43b4cf35dc3c076b07a0b8d54449924c1967c8c9609c4ea9729beaf800c9d63280d48ce7ccefb18b0466db8b54494108
7
+ data.tar.gz: 80a05ac7f10d5c2a3727d12f4ca8f30cfa3634d8047e1d23cc15f66e1c63940c9d27e0d7d875e05d084897702d56794daf314c830785edae969d8fab0ab06399
@@ -121,7 +121,7 @@ module GraphQL
121
121
  if enum_value
122
122
  enum_value.name
123
123
  else
124
- raise(UnresolvedValueError, "Can't resolve enum #{name} for #{value}")
124
+ raise(UnresolvedValueError, "Can't resolve enum #{name} for #{value.inspect}")
125
125
  end
126
126
  end
127
127
 
@@ -131,6 +131,7 @@ module GraphQL
131
131
  :relay_nodes_field,
132
132
  :subscription_scope,
133
133
  :trace,
134
+ :introspection,
134
135
  argument: GraphQL::Define::AssignArgument
135
136
 
136
137
  ensure_defined(
@@ -138,7 +139,8 @@ module GraphQL
138
139
  :mutation, :arguments, :complexity, :function,
139
140
  :resolve, :resolve=, :lazy_resolve, :lazy_resolve=, :lazy_resolve_proc, :resolve_proc,
140
141
  :type, :type=, :name=, :property=, :hash_key=,
141
- :relay_node_field, :relay_nodes_field, :edges?, :edge_class, :subscription_scope
142
+ :relay_node_field, :relay_nodes_field, :edges?, :edge_class, :subscription_scope,
143
+ :introspection?
142
144
  )
143
145
 
144
146
  # @return [Boolean] True if this is the Relay find-by-id field
@@ -183,6 +185,7 @@ module GraphQL
183
185
  attr_accessor :arguments_class
184
186
 
185
187
  attr_writer :connection
188
+ attr_writer :introspection
186
189
 
187
190
  # @return [nil, String] Prefix for subscription names from this field
188
191
  attr_accessor :subscription_scope
@@ -217,6 +220,7 @@ module GraphQL
217
220
  @connection_max_page_size = nil
218
221
  @edge_class = nil
219
222
  @trace = nil
223
+ @introspection = false
220
224
  end
221
225
 
222
226
  def initialize_copy(other)
@@ -224,6 +228,11 @@ module GraphQL
224
228
  @arguments = other.arguments.dup
225
229
  end
226
230
 
231
+ # @return [Boolean] Is this field a predefined introspection field?
232
+ def introspection?
233
+ @introspection
234
+ end
235
+
227
236
  # Get a value for this field
228
237
  # @example resolving a field value
229
238
  # field.resolve(obj, args, ctx)
@@ -84,6 +84,7 @@ module GraphQL
84
84
 
85
85
  def coerce_non_null_input(value, ctx)
86
86
  input_values = {}
87
+ defaults_used = Set.new
87
88
 
88
89
  arguments.each do |input_key, input_field_defn|
89
90
  field_value = value[input_key]
@@ -93,10 +94,11 @@ module GraphQL
93
94
  input_values[input_key] = input_field_defn.prepare(coerced_value, ctx)
94
95
  elsif input_field_defn.default_value?
95
96
  input_values[input_key] = input_field_defn.default_value
97
+ defaults_used << input_key
96
98
  end
97
99
  end
98
100
 
99
- arguments_class.new(input_values)
101
+ arguments_class.new(input_values, defaults_used)
100
102
  end
101
103
 
102
104
  # @api private
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::ArgumentsField = GraphQL::Field.define do
3
3
  type !GraphQL::ListType.new(of_type: !GraphQL::Introspection::InputValueType)
4
+ introspection true
4
5
  resolve ->(obj, args, ctx) {
5
6
  ctx.warden.arguments(obj)
6
7
  }
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::EnumValuesField = GraphQL::Field.define do
3
3
  type types[!GraphQL::Introspection::EnumValueType]
4
+ introspection true
4
5
  argument :includeDeprecated, types.Boolean, default_value: false
5
6
  resolve ->(object, arguments, context) do
6
7
  if !object.kind.enum?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::FieldsField = GraphQL::Field.define do
3
3
  type -> { types[!GraphQL::Introspection::FieldType] }
4
+ introspection true
4
5
  argument :includeDeprecated, GraphQL::BOOLEAN_TYPE, default_value: false
5
6
  resolve ->(object, arguments, context) {
6
7
  return nil if !object.kind.fields?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::InputFieldsField = GraphQL::Field.define do
3
3
  name "inputFields"
4
+ introspection true
4
5
  type types[!GraphQL::Introspection::InputValueType]
5
6
  resolve ->(target, a, ctx) {
6
7
  if target.kind.input_object?
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::InterfacesField = GraphQL::Field.define do
3
3
  type -> { types[!GraphQL::Introspection::TypeType] }
4
+ introspection true
4
5
  resolve ->(target, a, ctx) {
5
6
  if target.kind == GraphQL::TypeKinds::OBJECT
6
7
  ctx.warden.interfaces(target)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::OfTypeField = GraphQL::Field.define do
3
3
  name "ofType"
4
+ introspection true
4
5
  type -> { GraphQL::Introspection::TypeType }
5
6
  resolve ->(obj, args, ctx) { obj.kind.wraps? ? obj.of_type : nil }
6
7
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  GraphQL::Introspection::PossibleTypesField = GraphQL::Field.define do
3
3
  type -> { types[!GraphQL::Introspection::TypeType] }
4
+ introspection true
4
5
  resolve ->(target, args, ctx) {
5
6
  if target.kind.resolves?
6
7
  ctx.warden.possible_types(target)
@@ -4,6 +4,7 @@ module GraphQL
4
4
  SchemaField = GraphQL::Field.define do
5
5
  name("__schema")
6
6
  description("This GraphQL schema")
7
+ introspection true
7
8
  type(!GraphQL::Introspection::SchemaType)
8
9
  resolve ->(o, a, ctx) { ctx.query.schema }
9
10
  end
@@ -5,6 +5,7 @@ module GraphQL
5
5
  name("__type")
6
6
  description("A type in the GraphQL system")
7
7
  type(GraphQL::Introspection::TypeType)
8
+ introspection true
8
9
  argument :name, !types.String
9
10
  resolve ->(o, args, ctx) {
10
11
  ctx.warden.get_type(args["name"])
@@ -5,6 +5,7 @@ module GraphQL
5
5
  name "__typename"
6
6
  description "The name of this type"
7
7
  type -> { !GraphQL::STRING_TYPE }
8
+ introspection true
8
9
  resolve ->(obj, a, ctx) { ctx.irep_node.owner_type }
9
10
  end
10
11
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require "graphql/language/printer"
2
3
  require "graphql/language/definition_slice"
3
4
  require "graphql/language/document_from_schema_definition"
4
5
  require "graphql/language/generation"
@@ -6,10 +6,28 @@ module GraphQL
6
6
  # {GraphQL::Language::DocumentFromSchemaDefinition} is used to convert a {GraphQL::Schema} object
7
7
  # To a {GraphQL::Language::Document} AST node.
8
8
  #
9
+ # @param context [Hash]
10
+ # @param only [<#call(member, ctx)>]
11
+ # @param except [<#call(member, ctx)>]
12
+ # @param include_introspection_types [Boolean] Whether or not to include introspection types in the AST
13
+ # @param include_built_in_scalars [Boolean] Whether or not to include built in scalars in the AST
14
+ # @param include_built_in_directives [Boolean] Whether or not to include built in diirectives in the AST
9
15
  class DocumentFromSchemaDefinition
10
- def initialize(schema)
16
+ def initialize(
17
+ schema, context: nil, only: nil, except: nil, include_introspection_types: false,
18
+ include_built_in_directives: false, include_built_in_scalars: false, always_include_schema: false
19
+ )
11
20
  @schema = schema
12
- @types = GraphQL::Schema::Traversal.new(schema, introspection: true).type_map.values
21
+ @always_include_schema = always_include_schema
22
+ @include_introspection_types = include_introspection_types
23
+ @include_built_in_scalars = include_built_in_scalars
24
+ @include_built_in_directives = include_built_in_directives
25
+
26
+ @warden = GraphQL::Schema::Warden.new(
27
+ GraphQL::Filter.new(only: only, except: except),
28
+ schema: @schema,
29
+ context: context,
30
+ )
13
31
  end
14
32
 
15
33
  def document
@@ -18,47 +36,46 @@ module GraphQL
18
36
  )
19
37
  end
20
38
 
21
- protected
22
-
23
- def build_schema_node(schema)
24
- schema_node = GraphQL::Language::Nodes::SchemaDefinition.new(
25
- query: schema.query.name
39
+ def build_schema_node
40
+ GraphQL::Language::Nodes::SchemaDefinition.new(
41
+ query: warden.root_type_for_operation("query"),
42
+ mutation: warden.root_type_for_operation("mutation"),
43
+ subscription: warden.root_type_for_operation("subscription")
26
44
  )
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
45
  end
38
46
 
39
47
  def build_object_type_node(object_type)
40
48
  GraphQL::Language::Nodes::ObjectTypeDefinition.new(
41
49
  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),
50
+ interfaces: warden.interfaces(object_type).sort_by(&:name).map { |iface| build_type_name_node(iface) },
51
+ fields: build_field_nodes(warden.fields(object_type)),
44
52
  description: object_type.description,
45
53
  )
46
54
  end
47
55
 
48
56
  def build_field_node(field)
49
- GraphQL::Language::Nodes::FieldDefinition.new(
57
+ field_node = GraphQL::Language::Nodes::FieldDefinition.new(
50
58
  name: field.name,
51
- arguments: build_argument_nodes(field.arguments.values),
59
+ arguments: build_argument_nodes(warden.arguments(field)),
52
60
  type: build_type_name_node(field.type),
53
61
  description: field.description,
54
62
  )
63
+
64
+ if field.deprecation_reason
65
+ field_node.directives << GraphQL::Language::Nodes::Directive.new(
66
+ name: GraphQL::Directive::DeprecatedDirective.name,
67
+ arguments: [GraphQL::Language::Nodes::Argument.new(name: "reason", value: field.deprecation_reason)]
68
+ )
69
+ end
70
+
71
+ field_node
55
72
  end
56
73
 
57
74
  def build_union_type_node(union_type)
58
75
  GraphQL::Language::Nodes::UnionTypeDefinition.new(
59
76
  name: union_type.name,
60
77
  description: union_type.description,
61
- types: union_type.possible_types.map { |type| build_type_name_node(type) }
78
+ types: warden.possible_types(union_type).sort_by(&:name).map { |type| build_type_name_node(type) }
62
79
  )
63
80
  end
64
81
 
@@ -66,14 +83,14 @@ module GraphQL
66
83
  GraphQL::Language::Nodes::InterfaceTypeDefinition.new(
67
84
  name: interface_type.name,
68
85
  description: interface_type.description,
69
- fields: build_field_nodes(interface_type.fields.values)
86
+ fields: build_field_nodes(warden.fields(interface_type))
70
87
  )
71
88
  end
72
89
 
73
90
  def build_enum_type_node(enum_type)
74
91
  GraphQL::Language::Nodes::EnumTypeDefinition.new(
75
92
  name: enum_type.name,
76
- values: enum_type.values.values.map do |enum_value|
93
+ values: warden.enum_values(enum_type).sort_by(&:name).map do |enum_value|
77
94
  build_enum_value_node(enum_value)
78
95
  end,
79
96
  description: enum_type.description,
@@ -81,10 +98,19 @@ module GraphQL
81
98
  end
82
99
 
83
100
  def build_enum_value_node(enum_value)
84
- GraphQL::Language::Nodes::EnumValueDefinition.new(
101
+ enum_value_node = GraphQL::Language::Nodes::EnumValueDefinition.new(
85
102
  name: enum_value.name,
86
103
  description: enum_value.description,
87
104
  )
105
+
106
+ if enum_value.deprecation_reason
107
+ enum_value_node.directives << GraphQL::Language::Nodes::Directive.new(
108
+ name: GraphQL::Directive::DeprecatedDirective.name,
109
+ arguments: [GraphQL::Language::Nodes::Argument.new(name: "reason", value: enum_value.deprecation_reason)]
110
+ )
111
+ end
112
+
113
+ enum_value_node
88
114
  end
89
115
 
90
116
  def build_scalar_type_node(scalar_type)
@@ -95,18 +121,23 @@ module GraphQL
95
121
  end
96
122
 
97
123
  def build_argument_node(argument)
98
- GraphQL::Language::Nodes::InputValueDefinition.new(
124
+ argument_node = GraphQL::Language::Nodes::InputValueDefinition.new(
99
125
  name: argument.name,
100
126
  description: argument.description,
101
127
  type: build_type_name_node(argument.type),
102
- default_value: argument.default_value,
103
128
  )
129
+
130
+ if argument.default_value?
131
+ argument_node.default_value = build_default_value(argument.default_value, argument.type)
132
+ end
133
+
134
+ argument_node
104
135
  end
105
136
 
106
137
  def build_input_object_node(input_object)
107
138
  GraphQL::Language::Nodes::InputObjectTypeDefinition.new(
108
139
  name: input_object.name,
109
- fields: build_argument_nodes(input_object.arguments.values),
140
+ fields: build_argument_nodes(warden.arguments(input_object)),
110
141
  description: input_object.description,
111
142
  )
112
143
  end
@@ -114,7 +145,7 @@ module GraphQL
114
145
  def build_directive_node(directive)
115
146
  GraphQL::Language::Nodes::DirectiveDefinition.new(
116
147
  name: directive.name,
117
- arguments: build_argument_nodes(directive.arguments.values),
148
+ arguments: build_argument_nodes(warden.arguments(directive)),
118
149
  locations: directive.locations.map(&:to_s),
119
150
  description: directive.description,
120
151
  )
@@ -135,6 +166,35 @@ module GraphQL
135
166
  end
136
167
  end
137
168
 
169
+ def build_default_value(default_value, type)
170
+ if default_value.nil?
171
+ return GraphQL::Language::Nodes::NullValue.new(name: "null")
172
+ end
173
+
174
+ case type
175
+ when GraphQL::ScalarType
176
+ default_value
177
+ when EnumType
178
+ GraphQL::Language::Nodes::Enum.new(name: type.coerce_isolated_result(default_value))
179
+ when InputObjectType
180
+ GraphQL::Language::Nodes::InputObject.new(
181
+ arguments: default_value.to_h.map do |arg_name, arg_value|
182
+ arg_type = type.input_fields.fetch(arg_name.to_s).type
183
+ GraphQL::Language::Nodes::Argument.new(
184
+ name: arg_name,
185
+ value: build_default_value(arg_value, arg_type)
186
+ )
187
+ end
188
+ )
189
+ when NonNullType
190
+ build_default_value(default_value, type.of_type)
191
+ when ListType
192
+ default_value.to_a.map { |v| build_default_value(v, type.of_type) }
193
+ else
194
+ raise NotImplementedError, "Unexpected default value type #{type.inspect}"
195
+ end
196
+ end
197
+
138
198
  def build_type_definition_node(type)
139
199
  case type
140
200
  when GraphQL::ObjectType
@@ -155,31 +215,63 @@ module GraphQL
155
215
  end
156
216
 
157
217
  def build_argument_nodes(arguments)
158
- arguments.map { |arg| build_argument_node(arg) }
218
+ arguments
219
+ .map { |arg| build_argument_node(arg) }
220
+ .sort_by(&:name)
159
221
  end
160
222
 
161
223
  def build_directive_nodes(directives)
162
- directives.map { |directive| build_directive_node(directive) }
224
+ if !include_built_in_directives
225
+ directives = directives.reject { |directive| directive.default_directive? }
226
+ end
227
+
228
+ directives
229
+ .map { |directive| build_directive_node(directive) }
230
+ .sort_by(&:name)
163
231
  end
164
232
 
165
233
  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)
234
+ definitions = []
235
+ definitions << build_schema_node if include_schema_node?
236
+ definitions += build_directive_nodes(warden.directives)
237
+ definitions += build_type_definition_nodes(warden.types)
169
238
  definitions
170
239
  end
171
240
 
172
241
  def build_type_definition_nodes(types)
173
- types.map { |type| build_type_definition_node(type) }
242
+ if !include_introspection_types
243
+ types = types.reject { |type| type.introspection? }
244
+ end
245
+
246
+ if !include_built_in_scalars
247
+ types = types.reject { |type| type.default_scalar? }
248
+ end
249
+
250
+ types
251
+ .map { |type| build_type_definition_node(type) }
252
+ .sort_by(&:name)
174
253
  end
175
254
 
176
255
  def build_field_nodes(fields)
177
- fields.map { |field| build_field_node(field) }
256
+ fields
257
+ .map { |field| build_field_node(field) }
258
+ .sort_by(&:name)
178
259
  end
179
260
 
180
261
  private
181
262
 
182
- attr_reader :schema, :types
263
+ def include_schema_node?
264
+ always_include_schema || !schema_respects_root_name_conventions?(schema)
265
+ end
266
+
267
+ def schema_respects_root_name_conventions?(schema)
268
+ (schema.query.nil? || schema.query.name == 'Query') &&
269
+ (schema.mutation.nil? || schema.mutation.name == 'Mutation') &&
270
+ (schema.subscription.nil? || schema.subscription.name == 'Subscription')
271
+ end
272
+
273
+ attr_reader :schema, :warden, :always_include_schema,
274
+ :include_introspection_types, :include_built_in_directives, :include_built_in_scalars
183
275
  end
184
276
  end
185
277
  end