graphql 0.19.3 → 0.19.4

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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql/analysis/max_query_complexity.rb +1 -1
  3. data/lib/graphql/analysis/max_query_depth.rb +1 -1
  4. data/lib/graphql/boolean_type.rb +2 -2
  5. data/lib/graphql/define/instance_definable.rb +2 -2
  6. data/lib/graphql/enum_type.rb +3 -3
  7. data/lib/graphql/field.rb +6 -6
  8. data/lib/graphql/float_type.rb +2 -2
  9. data/lib/graphql/id_type.rb +2 -2
  10. data/lib/graphql/input_object_type.rb +1 -1
  11. data/lib/graphql/int_type.rb +2 -2
  12. data/lib/graphql/internal_representation/rewrite.rb +12 -12
  13. data/lib/graphql/introspection/arguments_field.rb +1 -1
  14. data/lib/graphql/introspection/enum_value_type.rb +1 -1
  15. data/lib/graphql/introspection/enum_values_field.rb +1 -1
  16. data/lib/graphql/introspection/field_type.rb +1 -1
  17. data/lib/graphql/introspection/fields_field.rb +1 -1
  18. data/lib/graphql/introspection/input_fields_field.rb +1 -1
  19. data/lib/graphql/introspection/input_value_type.rb +2 -2
  20. data/lib/graphql/introspection/interfaces_field.rb +1 -1
  21. data/lib/graphql/introspection/of_type_field.rb +1 -1
  22. data/lib/graphql/introspection/possible_types_field.rb +1 -1
  23. data/lib/graphql/introspection/schema_field.rb +1 -1
  24. data/lib/graphql/introspection/schema_type.rb +5 -5
  25. data/lib/graphql/introspection/type_by_name_field.rb +1 -1
  26. data/lib/graphql/introspection/type_type.rb +6 -6
  27. data/lib/graphql/introspection/typename_field.rb +1 -1
  28. data/lib/graphql/language.rb +1 -0
  29. data/lib/graphql/language/comments.rb +44 -0
  30. data/lib/graphql/language/definition_slice.rb +1 -1
  31. data/lib/graphql/language/generation.rb +35 -12
  32. data/lib/graphql/language/lexer.rb +29 -10
  33. data/lib/graphql/language/lexer.rl +25 -6
  34. data/lib/graphql/language/nodes.rb +30 -23
  35. data/lib/graphql/language/parser.rb +33 -14
  36. data/lib/graphql/language/parser.y +33 -14
  37. data/lib/graphql/language/parser_tests.rb +86 -2
  38. data/lib/graphql/language/token.rb +3 -2
  39. data/lib/graphql/language/visitor.rb +3 -3
  40. data/lib/graphql/object_type.rb +1 -1
  41. data/lib/graphql/query/arguments.rb +23 -2
  42. data/lib/graphql/query/serial_execution/field_resolution.rb +31 -14
  43. data/lib/graphql/relay/base_connection.rb +1 -3
  44. data/lib/graphql/relay/connection_type.rb +1 -1
  45. data/lib/graphql/relay/mutation.rb +1 -1
  46. data/lib/graphql/relay/relation_connection.rb +7 -3
  47. data/lib/graphql/scalar_type.rb +1 -1
  48. data/lib/graphql/schema.rb +19 -8
  49. data/lib/graphql/schema/loader.rb +2 -2
  50. data/lib/graphql/schema/printer.rb +63 -8
  51. data/lib/graphql/schema/timeout_middleware.rb +11 -11
  52. data/lib/graphql/schema/validation.rb +12 -12
  53. data/lib/graphql/static_validation/arguments_validator.rb +1 -1
  54. data/lib/graphql/static_validation/rules/directives_are_defined.rb +1 -1
  55. data/lib/graphql/static_validation/rules/directives_are_in_valid_locations.rb +1 -1
  56. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  57. data/lib/graphql/static_validation/rules/fields_have_appropriate_selections.rb +1 -1
  58. data/lib/graphql/static_validation/rules/fields_will_merge.rb +3 -2
  59. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -3
  60. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  61. data/lib/graphql/static_validation/rules/fragments_are_finite.rb +9 -4
  62. data/lib/graphql/static_validation/rules/fragments_are_named.rb +1 -1
  63. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  64. data/lib/graphql/static_validation/rules/fragments_are_used.rb +3 -3
  65. data/lib/graphql/static_validation/rules/mutation_root_exists.rb +1 -1
  66. data/lib/graphql/static_validation/rules/required_arguments_are_present.rb +2 -2
  67. data/lib/graphql/static_validation/rules/subscription_root_exists.rb +1 -1
  68. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +1 -1
  69. data/lib/graphql/static_validation/rules/variable_usages_are_allowed.rb +2 -2
  70. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +1 -1
  71. data/lib/graphql/static_validation/rules/variables_are_used_and_defined.rb +6 -6
  72. data/lib/graphql/static_validation/type_stack.rb +2 -2
  73. data/lib/graphql/string_type.rb +2 -2
  74. data/lib/graphql/version.rb +1 -1
  75. data/readme.md +1 -1
  76. data/spec/graphql/analysis/analyze_query_spec.rb +3 -3
  77. data/spec/graphql/analysis/query_complexity_spec.rb +7 -7
  78. data/spec/graphql/introspection/directive_type_spec.rb +2 -2
  79. data/spec/graphql/introspection/introspection_query_spec.rb +26 -26
  80. data/spec/graphql/language/generation_spec.rb +137 -53
  81. data/spec/graphql/language/lexer_spec.rb +22 -0
  82. data/spec/graphql/language/visitor_spec.rb +6 -6
  83. data/spec/graphql/query/arguments_spec.rb +45 -1
  84. data/spec/graphql/query/context_spec.rb +4 -4
  85. data/spec/graphql/query/executor_spec.rb +1 -1
  86. data/spec/graphql/query/serial_execution/value_resolution_spec.rb +1 -1
  87. data/spec/graphql/relay/mutation_spec.rb +2 -2
  88. data/spec/graphql/relay/node_spec.rb +2 -2
  89. data/spec/graphql/relay/relation_connection_spec.rb +16 -0
  90. data/spec/graphql/schema/loader_spec.rb +2 -2
  91. data/spec/graphql/schema/middleware_chain_spec.rb +6 -6
  92. data/spec/graphql/schema/printer_spec.rb +268 -18
  93. data/spec/graphql/schema/rescue_middleware_spec.rb +1 -1
  94. data/spec/graphql/schema/timeout_middleware_spec.rb +2 -2
  95. data/spec/graphql/schema_spec.rb +2 -2
  96. data/spec/graphql/static_validation/rules/fragments_are_finite_spec.rb +13 -0
  97. data/spec/graphql/static_validation/rules/fragments_are_used_spec.rb +1 -1
  98. data/spec/graphql/static_validation/type_stack_spec.rb +1 -1
  99. data/spec/support/dairy_app.rb +25 -25
  100. data/spec/support/dairy_data.rb +2 -0
  101. data/spec/support/star_wars_data.rb +2 -1
  102. data/spec/support/star_wars_schema.rb +18 -18
  103. metadata +19 -2
@@ -19,7 +19,9 @@ describe GraphQL::Schema::Printer do
19
19
 
20
20
  sub_input_type = GraphQL::InputObjectType.define do
21
21
  name "Sub"
22
- input_field :string, types.String
22
+ description "Test"
23
+ input_field :string, types.String, 'Something'
24
+ input_field :int, types.Int, 'Something'
23
25
  end
24
26
 
25
27
  variant_input_type = GraphQL::InputObjectType.define do
@@ -51,6 +53,30 @@ describe GraphQL::Schema::Printer do
51
53
  field :comments_count, !types.Int, deprecation_reason: 'Use "comments".'
52
54
  end
53
55
 
56
+ audio_type = GraphQL::ObjectType.define do
57
+ name "Audio"
58
+
59
+ field :id, !types.ID
60
+ field :name, !types.String
61
+ field :duration, !types.Int
62
+ end
63
+
64
+ image_type = GraphQL::ObjectType.define do
65
+ name "Image"
66
+
67
+ field :id, !types.ID
68
+ field :name, !types.String
69
+ field :width, !types.Int
70
+ field :height, !types.Int
71
+ end
72
+
73
+ media_union_type = GraphQL::UnionType.define do
74
+ name "Media"
75
+ description "Media objects"
76
+
77
+ possible_types [image_type, audio_type]
78
+ end
79
+
54
80
  query_root = GraphQL::ObjectType.define do
55
81
  name "Query"
56
82
  description "The query root of this schema"
@@ -59,57 +85,152 @@ describe GraphQL::Schema::Printer do
59
85
  type post_type
60
86
  argument :id, !types.ID
61
87
  argument :varied, variant_input_type, default_value: { id: "123", int: 234, float: 2.3, enum: :foo, sub: [{ string: "str" }] }
62
- resolve -> (obj, args, ctx) { Post.find(args["id"]) }
88
+ resolve ->(obj, args, ctx) { Post.find(args["id"]) }
89
+ end
90
+ end
91
+
92
+ create_post_mutation = GraphQL::Relay::Mutation.define do
93
+ name "CreatePost"
94
+ description "Create a blog post"
95
+
96
+ input_field :title, !types.String
97
+ input_field :body, !types.String
98
+
99
+ return_field :post, post_type
100
+
101
+ resolve ->(_, _, _) { }
102
+ end
103
+
104
+ mutation_root = GraphQL::ObjectType.define do
105
+ name "Mutation"
106
+
107
+ field :createPost, field: create_post_mutation.field
108
+ end
109
+
110
+ subscription_root = GraphQL::ObjectType.define do
111
+ name "Subscription"
112
+
113
+ field :post do
114
+ type post_type
115
+ argument :id, !types.ID
116
+ resolve ->(_, _, _) { }
63
117
  end
64
118
  end
65
119
 
66
- GraphQL::Schema.define(query: query_root, resolve_type: :pass)
120
+ GraphQL::Schema.define(
121
+ query: query_root,
122
+ mutation: mutation_root,
123
+ subscription: subscription_root,
124
+ resolve_type: :pass,
125
+ orphan_types: [media_union_type]
126
+ )
67
127
  }
68
128
 
69
129
  describe ".print_introspection_schema" do
70
130
  it "returns the schema as a string for the introspection types" do
131
+ # From https://github.com/graphql/graphql-js/blob/6a0e00fe46951767287f2cc62e1a10b167b2eaa6/src/utilities/__tests__/schemaPrinter-test.js#L599
71
132
  expected = <<SCHEMA
72
133
  schema {
73
- query: Query
134
+ query: Root
74
135
  }
75
136
 
76
- directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
77
-
78
- directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
79
-
80
- directive @deprecated(reason: String = \"No longer supported\") on FIELD_DEFINITION | ENUM_VALUE
81
-
137
+ # Directs the executor to include this field or fragment only when the \`if\` argument is true.
138
+ directive @include(
139
+ # Included when true.
140
+ if: Boolean!
141
+ ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
142
+
143
+ # Directs the executor to skip this field or fragment when the \`if\` argument is true.
144
+ directive @skip(
145
+ # Skipped when true.
146
+ if: Boolean!
147
+ ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
148
+
149
+ # Marks an element of a GraphQL schema as no longer supported.
150
+ directive @deprecated(
151
+ # Explains why this element was deprecated, usually also including a suggestion
152
+ # for how to access supported similar data. Formatted in
153
+ # [Markdown](https://daringfireball.net/projects/markdown/).
154
+ reason: String = "No longer supported"
155
+ ) on FIELD_DEFINITION | ENUM_VALUE
156
+
157
+ # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
158
+ #
159
+ # In some cases, you need to provide options to alter GraphQL's execution behavior
160
+ # in ways field arguments will not suffice, such as conditionally including or
161
+ # skipping a field. Directives provide this by describing additional information
162
+ # to the executor.
82
163
  type __Directive {
83
164
  name: String!
84
165
  description: String
85
166
  locations: [__DirectiveLocation!]!
86
167
  args: [__InputValue!]!
87
- onOperation: Boolean! @deprecated(reason: \"Use `locations`.\")
88
- onFragment: Boolean! @deprecated(reason: \"Use `locations`.\")
89
- onField: Boolean! @deprecated(reason: \"Use `locations`.\")
168
+ onOperation: Boolean! @deprecated(reason: "Use \`locations\`.")
169
+ onFragment: Boolean! @deprecated(reason: "Use \`locations\`.")
170
+ onField: Boolean! @deprecated(reason: "Use \`locations\`.")
90
171
  }
91
172
 
173
+ # A Directive can be adjacent to many parts of the GraphQL language, a
174
+ # __DirectiveLocation describes one such possible adjacencies.
92
175
  enum __DirectiveLocation {
176
+ # Location adjacent to a query operation.
93
177
  QUERY
178
+
179
+ # Location adjacent to a mutation operation.
94
180
  MUTATION
181
+
182
+ # Location adjacent to a subscription operation.
95
183
  SUBSCRIPTION
184
+
185
+ # Location adjacent to a field.
96
186
  FIELD
187
+
188
+ # Location adjacent to a fragment definition.
97
189
  FRAGMENT_DEFINITION
190
+
191
+ # Location adjacent to a fragment spread.
98
192
  FRAGMENT_SPREAD
193
+
194
+ # Location adjacent to an inline fragment.
99
195
  INLINE_FRAGMENT
196
+
197
+ # Location adjacent to a schema definition.
100
198
  SCHEMA
199
+
200
+ # Location adjacent to a scalar definition.
101
201
  SCALAR
202
+
203
+ # Location adjacent to an object type definition.
102
204
  OBJECT
205
+
206
+ # Location adjacent to a field definition.
103
207
  FIELD_DEFINITION
208
+
209
+ # Location adjacent to an argument definition.
104
210
  ARGUMENT_DEFINITION
211
+
212
+ # Location adjacent to an interface definition.
105
213
  INTERFACE
214
+
215
+ # Location adjacent to a union definition.
106
216
  UNION
217
+
218
+ # Location adjacent to an enum definition.
107
219
  ENUM
220
+
221
+ # Location adjacent to an enum value definition.
108
222
  ENUM_VALUE
223
+
224
+ # Location adjacent to an input object type definition.
109
225
  INPUT_OBJECT
226
+
227
+ # Location adjacent to an input object field definition.
110
228
  INPUT_FIELD_DEFINITION
111
229
  }
112
230
 
231
+ # One possible value for a given Enum. Enum values are unique values, not a
232
+ # placeholder for a string or numeric value. However an Enum value is returned in
233
+ # a JSON response as a string.
113
234
  type __EnumValue {
114
235
  name: String!
115
236
  description: String
@@ -117,6 +238,8 @@ type __EnumValue {
117
238
  deprecationReason: String
118
239
  }
119
240
 
241
+ # Object and Interface types are described by a list of Fields, each of which has
242
+ # a name, potentially a list of arguments, and a return type.
120
243
  type __Field {
121
244
  name: String!
122
245
  description: String
@@ -126,41 +249,82 @@ type __Field {
126
249
  deprecationReason: String
127
250
  }
128
251
 
252
+ # Arguments provided to Fields or Directives and the input fields of an
253
+ # InputObject are represented as Input Values which describe their type and
254
+ # optionally a default value.
129
255
  type __InputValue {
130
256
  name: String!
131
257
  description: String
132
258
  type: __Type!
259
+
260
+ # A GraphQL-formatted string representing the default value for this input value.
133
261
  defaultValue: String
134
262
  }
135
263
 
264
+ # A GraphQL Schema defines the capabilities of a GraphQL server. It exposes all
265
+ # available types and directives on the server, as well as the entry points for
266
+ # query, mutation, and subscription operations.
136
267
  type __Schema {
268
+ # A list of all types supported by this server.
137
269
  types: [__Type!]!
270
+
271
+ # The type that query operations will be rooted at.
138
272
  queryType: __Type!
273
+
274
+ # If this server supports mutation, the type that mutation operations will be rooted at.
139
275
  mutationType: __Type
276
+
277
+ # If this server support subscription, the type that subscription operations will be rooted at.
140
278
  subscriptionType: __Type
279
+
280
+ # A list of all directives supported by this server.
141
281
  directives: [__Directive!]!
142
282
  }
143
283
 
284
+ # The fundamental unit of any GraphQL Schema is the type. There are many kinds of
285
+ # types in GraphQL as represented by the \`__TypeKind\` enum.
286
+ #
287
+ # Depending on the kind of a type, certain fields describe information about that
288
+ # type. Scalar types provide no information beyond a name and description, while
289
+ # Enum types provide their values. Object and Interface types provide the fields
290
+ # they describe. Abstract types, Union and Interface, provide the Object types
291
+ # possible at runtime. List and NonNull types compose other types.
144
292
  type __Type {
293
+ kind: __TypeKind!
145
294
  name: String
146
295
  description: String
147
- kind: __TypeKind!
148
296
  fields(includeDeprecated: Boolean = false): [__Field!]
149
- ofType: __Type
150
- inputFields: [__InputValue!]
297
+ interfaces: [__Type!]
151
298
  possibleTypes: [__Type!]
152
299
  enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
153
- interfaces: [__Type!]
300
+ inputFields: [__InputValue!]
301
+ ofType: __Type
154
302
  }
155
303
 
304
+ # An enum describing what kind of type a given \`__Type\` is.
156
305
  enum __TypeKind {
306
+ # Indicates this type is a scalar.
157
307
  SCALAR
308
+
309
+ # Indicates this type is an object. \`fields\` and \`interfaces\` are valid fields.
158
310
  OBJECT
311
+
312
+ # Indicates this type is an interface. \`fields\` and \`possibleTypes\` are valid fields.
159
313
  INTERFACE
314
+
315
+ # Indicates this type is a union. \`possibleTypes\` is a valid field.
160
316
  UNION
317
+
318
+ # Indicates this type is an enum. \`enumValues\` is a valid field.
161
319
  ENUM
320
+
321
+ # Indicates this type is an input object. \`inputFields\` is a valid field.
162
322
  INPUT_OBJECT
323
+
324
+ # Indicates this type is a list. \`ofType\` is a valid field.
163
325
  LIST
326
+
327
+ # Indicates this type is a non-null. \`ofType\` is a valid field.
164
328
  NON_NULL
165
329
  }
166
330
  SCHEMA
@@ -169,10 +333,54 @@ SCHEMA
169
333
  end
170
334
 
171
335
  describe ".print_schema" do
172
- it "returns the schema as a string for the defined types" do
336
+ it "includes schema definition when query root name doesn't match convention" do
337
+ schema.query.name = 'MyQueryRoot'
338
+
339
+ expected = <<SCHEMA
340
+ schema {
341
+ query: MyQueryRoot
342
+ mutation: Mutation
343
+ subscription: Subscription
344
+ }
345
+ SCHEMA
346
+
347
+ assert_match expected, GraphQL::Schema::Printer.print_schema(schema)
348
+ end
349
+
350
+ it "includes schema definition when mutation root name doesn't match convention" do
351
+ schema.mutation.name = 'MyMutationRoot'
352
+
353
+ expected = <<SCHEMA
354
+ schema {
355
+ query: Query
356
+ mutation: MyMutationRoot
357
+ subscription: Subscription
358
+ }
359
+ SCHEMA
360
+
361
+ assert_match expected, GraphQL::Schema::Printer.print_schema(schema)
362
+ end
363
+
364
+ it "includes schema definition when subscription root name doesn't match convention" do
365
+ schema.subscription.name = 'MySubscriptionRoot'
366
+
173
367
  expected = <<SCHEMA
174
368
  schema {
175
369
  query: Query
370
+ mutation: Mutation
371
+ subscription: MySubscriptionRoot
372
+ }
373
+ SCHEMA
374
+
375
+ assert_match expected, GraphQL::Schema::Printer.print_schema(schema)
376
+ end
377
+
378
+ it "returns the schema as a string for the defined types" do
379
+ expected = <<SCHEMA
380
+ type Audio {
381
+ id: ID!
382
+ name: String!
383
+ duration: Int!
176
384
  }
177
385
 
178
386
  enum Choice {
@@ -182,14 +390,46 @@ enum Choice {
182
390
  WOZ @deprecated
183
391
  }
184
392
 
393
+ # A blog comment
185
394
  type Comment implements Node {
186
395
  id: ID!
187
396
  }
188
397
 
398
+ # Autogenerated input type of CreatePost
399
+ input CreatePostInput {
400
+ # A unique identifier for the client performing the mutation.
401
+ clientMutationId: String
402
+ title: String!
403
+ body: String!
404
+ }
405
+
406
+ # Autogenerated return type of CreatePost
407
+ type CreatePostPayload {
408
+ # A unique identifier for the client performing the mutation.
409
+ clientMutationId: String
410
+ post: Post
411
+ }
412
+
413
+ type Image {
414
+ id: ID!
415
+ name: String!
416
+ width: Int!
417
+ height: Int!
418
+ }
419
+
420
+ # Media objects
421
+ union Media = Image | Audio
422
+
423
+ type Mutation {
424
+ # Create a blog post
425
+ createPost(input: CreatePostInput!): CreatePostPayload
426
+ }
427
+
189
428
  interface Node {
190
429
  id: ID!
191
430
  }
192
431
 
432
+ # A blog post
193
433
  type Post {
194
434
  id: ID!
195
435
  title: String!
@@ -198,12 +438,22 @@ type Post {
198
438
  comments_count: Int! @deprecated(reason: \"Use \\\"comments\\\".\")
199
439
  }
200
440
 
441
+ # The query root of this schema
201
442
  type Query {
202
443
  post(id: ID!, varied: Varied = {id: \"123\", int: 234, float: 2.3, enum: FOO, sub: [{string: \"str\"}]}): Post
203
444
  }
204
445
 
446
+ # Test
205
447
  input Sub {
448
+ # Something
206
449
  string: String
450
+
451
+ # Something
452
+ int: Int
453
+ }
454
+
455
+ type Subscription {
456
+ post(id: ID!): Post
207
457
  }
208
458
 
209
459
  input Varied {
@@ -3,7 +3,7 @@ require "spec_helper"
3
3
  class SpecExampleError < StandardError; end
4
4
 
5
5
  describe GraphQL::Schema::RescueMiddleware do
6
- let(:error_middleware) { -> (next_middleware) { raise(error_class) } }
6
+ let(:error_middleware) { ->(next_middleware) { raise(error_class) } }
7
7
 
8
8
  let(:rescue_middleware) do
9
9
  middleware = GraphQL::Schema::RescueMiddleware.new
@@ -5,7 +5,7 @@ describe GraphQL::Schema::TimeoutMiddleware do
5
5
  let(:timeout_middleware) { GraphQL::Schema::TimeoutMiddleware.new(max_seconds: max_seconds) }
6
6
  let(:timeout_schema) {
7
7
 
8
- sleep_for_seconds_resolve = -> (obj, args, ctx) {
8
+ sleep_for_seconds_resolve = ->(obj, args, ctx) {
9
9
  sleep(args[:seconds])
10
10
  args[:seconds]
11
11
  }
@@ -13,7 +13,7 @@ describe GraphQL::Schema::TimeoutMiddleware do
13
13
  nested_sleep_type = GraphQL::ObjectType.define do
14
14
  name "NestedSleep"
15
15
  field :seconds, types.Float do
16
- resolve -> (obj, args, ctx) { obj }
16
+ resolve ->(obj, args, ctx) { obj }
17
17
  end
18
18
 
19
19
  field :nestedSleep, -> { nested_sleep_type } do