graphql 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +10 -0
  3. data/lib/graphql/base_type.rb +8 -5
  4. data/lib/graphql/compatibility.rb +3 -0
  5. data/lib/graphql/compatibility/execution_specification.rb +414 -0
  6. data/lib/graphql/compatibility/query_parser_specification.rb +117 -0
  7. data/lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb +81 -0
  8. data/lib/graphql/compatibility/query_parser_specification/query_assertions.rb +78 -0
  9. data/lib/graphql/compatibility/schema_parser_specification.rb +239 -0
  10. data/lib/graphql/define/instance_definable.rb +53 -21
  11. data/lib/graphql/directive.rb +1 -1
  12. data/lib/graphql/enum_type.rb +31 -8
  13. data/lib/graphql/execution/directive_checks.rb +0 -6
  14. data/lib/graphql/input_object_type.rb +6 -4
  15. data/lib/graphql/introspection/arguments_field.rb +3 -1
  16. data/lib/graphql/introspection/enum_values_field.rb +10 -5
  17. data/lib/graphql/introspection/fields_field.rb +1 -1
  18. data/lib/graphql/introspection/input_fields_field.rb +2 -2
  19. data/lib/graphql/introspection/interfaces_field.rb +7 -1
  20. data/lib/graphql/introspection/possible_types_field.rb +1 -1
  21. data/lib/graphql/introspection/schema_type.rb +1 -1
  22. data/lib/graphql/introspection/type_by_name_field.rb +4 -2
  23. data/lib/graphql/introspection/type_type.rb +7 -6
  24. data/lib/graphql/language/lexer.rl +0 -4
  25. data/lib/graphql/language/parser.rb +1 -1
  26. data/lib/graphql/language/parser.y +1 -1
  27. data/lib/graphql/list_type.rb +3 -4
  28. data/lib/graphql/non_null_type.rb +4 -8
  29. data/lib/graphql/object_type.rb +5 -3
  30. data/lib/graphql/query.rb +48 -12
  31. data/lib/graphql/query/context.rb +7 -1
  32. data/lib/graphql/query/serial_execution/execution_context.rb +8 -3
  33. data/lib/graphql/query/serial_execution/field_resolution.rb +8 -5
  34. data/lib/graphql/query/serial_execution/operation_resolution.rb +2 -2
  35. data/lib/graphql/query/serial_execution/selection_resolution.rb +4 -21
  36. data/lib/graphql/query/serial_execution/value_resolution.rb +59 -99
  37. data/lib/graphql/query/variables.rb +7 -2
  38. data/lib/graphql/scalar_type.rb +1 -1
  39. data/lib/graphql/schema.rb +49 -18
  40. data/lib/graphql/schema/build_from_definition.rb +248 -0
  41. data/lib/graphql/schema/instrumented_field_map.rb +23 -0
  42. data/lib/graphql/schema/loader.rb +4 -11
  43. data/lib/graphql/schema/possible_types.rb +4 -2
  44. data/lib/graphql/schema/printer.rb +1 -1
  45. data/lib/graphql/schema/type_expression.rb +4 -4
  46. data/lib/graphql/schema/type_map.rb +1 -1
  47. data/lib/graphql/schema/validation.rb +4 -0
  48. data/lib/graphql/schema/warden.rb +114 -0
  49. data/lib/graphql/static_validation/literal_validator.rb +10 -7
  50. data/lib/graphql/static_validation/rules/argument_literals_are_compatible.rb +1 -3
  51. data/lib/graphql/static_validation/rules/arguments_are_defined.rb +1 -1
  52. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +1 -1
  53. data/lib/graphql/static_validation/rules/fragment_spreads_are_possible.rb +3 -14
  54. data/lib/graphql/static_validation/rules/fragment_types_exist.rb +1 -1
  55. data/lib/graphql/static_validation/rules/fragments_are_on_composite_types.rb +1 -1
  56. data/lib/graphql/static_validation/rules/variable_default_values_are_correctly_typed.rb +3 -4
  57. data/lib/graphql/static_validation/rules/variables_are_input_types.rb +2 -1
  58. data/lib/graphql/static_validation/validation_context.rb +7 -1
  59. data/lib/graphql/union_type.rb +6 -3
  60. data/lib/graphql/unresolved_type_error.rb +1 -2
  61. data/lib/graphql/version.rb +1 -1
  62. data/readme.md +1 -5
  63. data/spec/graphql/compatibility/execution_specification_spec.rb +3 -0
  64. data/spec/graphql/compatibility/query_parser_specification_spec.rb +5 -0
  65. data/spec/graphql/compatibility/schema_parser_specification_spec.rb +5 -0
  66. data/spec/graphql/define/instance_definable_spec.rb +20 -0
  67. data/spec/graphql/directive_spec.rb +11 -0
  68. data/spec/graphql/enum_type_spec.rb +20 -1
  69. data/spec/graphql/input_object_type_spec.rb +9 -9
  70. data/spec/graphql/introspection/directive_type_spec.rb +4 -4
  71. data/spec/graphql/introspection/input_value_type_spec.rb +6 -6
  72. data/spec/graphql/introspection/type_type_spec.rb +28 -26
  73. data/spec/graphql/language/parser_spec.rb +27 -17
  74. data/spec/graphql/list_type_spec.rb +2 -2
  75. data/spec/graphql/query/variables_spec.rb +1 -0
  76. data/spec/graphql/scalar_type_spec.rb +3 -3
  77. data/spec/graphql/schema/build_from_definition_spec.rb +693 -0
  78. data/spec/graphql/schema/type_expression_spec.rb +3 -3
  79. data/spec/graphql/schema/validation_spec.rb +7 -3
  80. data/spec/graphql/schema/warden_spec.rb +510 -0
  81. data/spec/graphql/schema_spec.rb +129 -0
  82. data/spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb +1 -1
  83. data/spec/graphql/static_validation/type_stack_spec.rb +3 -3
  84. data/spec/spec_helper.rb +27 -1
  85. data/spec/support/dairy_app.rb +8 -5
  86. metadata +21 -3
  87. data/lib/graphql/language/parser_tests.rb +0 -809
@@ -124,4 +124,133 @@ describe GraphQL::Schema do
124
124
  end
125
125
  end
126
126
  end
127
+
128
+ describe "directives" do
129
+ describe "when directives are not overwritten" do
130
+ it "contains built-in directives" do
131
+ schema = GraphQL::Schema.define
132
+
133
+ assert_equal ['deprecated', 'include', 'skip'], schema.directives.keys.sort
134
+
135
+ assert_equal GraphQL::Directive::DeprecatedDirective, schema.directives['deprecated']
136
+ assert_equal GraphQL::Directive::IncludeDirective, schema.directives['include']
137
+ assert_equal GraphQL::Directive::SkipDirective, schema.directives['skip']
138
+ end
139
+ end
140
+
141
+ describe "when directives are overwritten" do
142
+ it "contains only specified directives" do
143
+ schema = GraphQL::Schema.define do
144
+ directives [GraphQL::Directive::DeprecatedDirective]
145
+ end
146
+
147
+ assert_equal ['deprecated'], schema.directives.keys.sort
148
+ assert_equal GraphQL::Directive::DeprecatedDirective, schema.directives['deprecated']
149
+ end
150
+ end
151
+ end
152
+
153
+ describe ".from_definition" do
154
+ it "uses BuildFromSchema to build a schema from a definition string" do
155
+ schema = <<-SCHEMA
156
+ type Query {
157
+ str: String
158
+ }
159
+ SCHEMA
160
+
161
+ built_schema = GraphQL::Schema.from_definition(schema)
162
+ assert_equal schema.chop, GraphQL::Schema::Printer.print_schema(built_schema)
163
+ end
164
+ end
165
+
166
+ describe ".from_introspection" do
167
+ let(:schema) {
168
+ query_root = GraphQL::ObjectType.define do
169
+ name 'Query'
170
+ field :str, types.String
171
+ end
172
+
173
+ GraphQL::Schema.define do
174
+ query query_root
175
+ end
176
+ }
177
+ let(:schema_json) {
178
+ schema.execute(GraphQL::Introspection::INTROSPECTION_QUERY)
179
+ }
180
+ it "uses Schema::Loader to build a schema from an introspection result" do
181
+ built_schema = GraphQL::Schema.from_introspection(schema_json)
182
+ assert_equal GraphQL::Schema::Printer.print_schema(schema), GraphQL::Schema::Printer.print_schema(built_schema)
183
+ end
184
+ end
185
+
186
+ describe "#instrument" do
187
+ class MultiplyInstrumenter
188
+ def initialize(multiplier)
189
+ @multiplier = multiplier
190
+ end
191
+
192
+ def instrument(type_defn, field_defn)
193
+ if type_defn.name == "Query" && field_defn.name == "int"
194
+ prev_proc = field_defn.resolve_proc
195
+ new_resolve_proc = ->(obj, args, ctx) {
196
+ inner_value = prev_proc.call(obj, args, ctx)
197
+ inner_value * @multiplier
198
+ }
199
+
200
+ field_defn.redefine do
201
+ resolve(new_resolve_proc)
202
+ end
203
+ else
204
+ field_defn
205
+ end
206
+ end
207
+ end
208
+
209
+ class VariableCountInstrumenter
210
+ attr_reader :counts
211
+ def initialize
212
+ @counts = []
213
+ end
214
+
215
+ def before_query(query)
216
+ @counts << query.variables.length
217
+ end
218
+
219
+ def after_query(query)
220
+ end
221
+ end
222
+
223
+ let(:variable_counter) {
224
+ VariableCountInstrumenter.new
225
+ }
226
+ let(:query_type) {
227
+ GraphQL::ObjectType.define do
228
+ name "Query"
229
+ field :int, types.Int do
230
+ argument :value, types.Int
231
+ resolve -> (obj, args, ctx) { args[:value] }
232
+ end
233
+ end
234
+ }
235
+
236
+ let(:schema) {
237
+ spec = self
238
+ GraphQL::Schema.define do
239
+ query(spec.query_type)
240
+ instrument(:field, MultiplyInstrumenter.new(3))
241
+ instrument(:query, spec.variable_counter)
242
+ end
243
+ }
244
+
245
+ it "can modify field definitions" do
246
+ res = schema.execute(" { int(value: 2) } ")
247
+ assert_equal 6, res["data"]["int"]
248
+ end
249
+
250
+ it "can wrap query execution" do
251
+ schema.execute("query getInt($val: Int = 5){ int(value: $val) } ")
252
+ schema.execute("query getInt($val: Int = 5, $val2: Int = 3){ int(value: $val) int2: int(value: $val2) } ")
253
+ assert_equal [1, 2], variable_counter.counts
254
+ end
255
+ end
127
256
  end
@@ -21,7 +21,7 @@ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
21
21
 
22
22
  it "finds undefined or missing-required arguments to fields and directives" do
23
23
  # `wacky` above is handled by ArgumentsAreDefined, so only 6 are tested below
24
- assert_equal(7, errors.length)
24
+ assert_equal(8, errors.length)
25
25
 
26
26
  query_root_error = {
27
27
  "message"=>"Argument 'id' on Field 'stringCheese' has an invalid value. Expected type 'Int!'.",
@@ -8,7 +8,7 @@ class TypeCheckValidator
8
8
  def validate(context)
9
9
  self.class.checks.clear
10
10
  context.visitor[GraphQL::Language::Nodes::Field] << ->(node, parent) {
11
- self.class.checks << context.object_types.map(&:name)
11
+ self.class.checks << context.object_types.map {|t| t.name || t.kind.name }
12
12
  }
13
13
  end
14
14
  end
@@ -29,8 +29,8 @@ describe GraphQL::StaticValidation::TypeStack do
29
29
  validator.validate(query)
30
30
  expected = [
31
31
  ["Query", "Cheese"],
32
- ["Query", "Cheese", "Non-Null"],
33
- ["Edible", "Non-Null"]
32
+ ["Query", "Cheese", "NON_NULL"],
33
+ ["Edible", "NON_NULL"]
34
34
  ]
35
35
  assert_equal(expected, TypeCheckValidator.checks)
36
36
  end
@@ -16,7 +16,33 @@ Minitest::Spec.make_my_diffs_pretty!
16
16
  # to be shown.
17
17
  Minitest.backtrace_filter = Minitest::BacktraceFilter.new
18
18
 
19
- # # Load support files
19
+
20
+ # This is for convenient access to metadata in test definitions
21
+ assign_metadata_key = -> (target, key, value) { target.metadata[key] = value }
22
+ GraphQL::BaseType.accepts_definitions(metadata: assign_metadata_key)
23
+ GraphQL::Field.accepts_definitions(metadata: assign_metadata_key)
24
+ GraphQL::Argument.accepts_definitions(metadata: assign_metadata_key)
25
+ GraphQL::EnumType::EnumValue.accepts_definitions(metadata: assign_metadata_key)
26
+
27
+ # Can be used as a GraphQL::Schema::Warden for some purposes, but allows anything
28
+ module PermissiveWarden
29
+ def self.input_fields(input_obj)
30
+ input_obj.arguments.values
31
+ end
32
+
33
+ def self.enum_values(enum_type)
34
+ enum_type.values.values
35
+ end
36
+ end
37
+
38
+ # Can be used as a GraphQL::Schema::Warden for some purposes, but allows nothing
39
+ module NothingWarden
40
+ def self.enum_values(enum_type)
41
+ []
42
+ end
43
+ end
44
+
45
+ # Load support files
20
46
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
21
47
 
22
48
  def star_wars_query(string, variables={})
@@ -33,10 +33,12 @@ end
33
33
  DairyAnimalEnum = GraphQL::EnumType.define do
34
34
  name "DairyAnimal"
35
35
  description "An animal which can yield milk"
36
- value("COW", "Animal with black and white spots", value: 1)
37
- value("GOAT", "Animal with horns")
38
- value("SHEEP", "Animal with wool")
39
- value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
36
+ value("COW", "Animal with black and white spots", value: 1)
37
+ value("DONKEY", "Animal with fur", value: :donkey)
38
+ value("GOAT", "Animal with horns")
39
+ value("REINDEER", "Animal with horns", value: 'reindeer')
40
+ value("SHEEP", "Animal with wool")
41
+ value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
40
42
  end
41
43
 
42
44
  CheeseType = GraphQL::ObjectType.define do
@@ -57,7 +59,8 @@ CheeseType = GraphQL::ObjectType.define do
57
59
  field :similarCheese, CheeseType, "Cheeses like this one", property: :this_should_be_overriden do
58
60
  # metadata test
59
61
  joins [:cheeses, :milks]
60
- argument :source, !types[!DairyAnimalEnum], default_value: [1]
62
+ argument :source, !types[!DairyAnimalEnum]
63
+ argument :nullableSource, types[!DairyAnimalEnum], default_value: [1]
61
64
  resolve ->(t, a, c) {
62
65
  # get the strings out:
63
66
  sources = a["source"]
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.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Mosolgo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-10-25 00:00:00.000000000 Z
11
+ date: 2016-11-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: codeclimate-test-reporter
@@ -271,6 +271,12 @@ files:
271
271
  - lib/graphql/argument.rb
272
272
  - lib/graphql/base_type.rb
273
273
  - lib/graphql/boolean_type.rb
274
+ - lib/graphql/compatibility.rb
275
+ - lib/graphql/compatibility/execution_specification.rb
276
+ - lib/graphql/compatibility/query_parser_specification.rb
277
+ - lib/graphql/compatibility/query_parser_specification/parse_error_specification.rb
278
+ - lib/graphql/compatibility/query_parser_specification/query_assertions.rb
279
+ - lib/graphql/compatibility/schema_parser_specification.rb
274
280
  - lib/graphql/define.rb
275
281
  - lib/graphql/define/assign_argument.rb
276
282
  - lib/graphql/define/assign_connection.rb
@@ -330,7 +336,6 @@ files:
330
336
  - lib/graphql/language/nodes.rb
331
337
  - lib/graphql/language/parser.rb
332
338
  - lib/graphql/language/parser.y
333
- - lib/graphql/language/parser_tests.rb
334
339
  - lib/graphql/language/token.rb
335
340
  - lib/graphql/language/visitor.rb
336
341
  - lib/graphql/list_type.rb
@@ -365,7 +370,9 @@ files:
365
370
  - lib/graphql/relay/relation_connection.rb
366
371
  - lib/graphql/scalar_type.rb
367
372
  - lib/graphql/schema.rb
373
+ - lib/graphql/schema/build_from_definition.rb
368
374
  - lib/graphql/schema/catchall_middleware.rb
375
+ - lib/graphql/schema/instrumented_field_map.rb
369
376
  - lib/graphql/schema/invalid_type_error.rb
370
377
  - lib/graphql/schema/loader.rb
371
378
  - lib/graphql/schema/middleware_chain.rb
@@ -378,6 +385,7 @@ files:
378
385
  - lib/graphql/schema/type_map.rb
379
386
  - lib/graphql/schema/unique_within_type.rb
380
387
  - lib/graphql/schema/validation.rb
388
+ - lib/graphql/schema/warden.rb
381
389
  - lib/graphql/static_validation.rb
382
390
  - lib/graphql/static_validation/all_rules.rb
383
391
  - lib/graphql/static_validation/arguments_validator.rb
@@ -421,6 +429,9 @@ files:
421
429
  - spec/graphql/argument_spec.rb
422
430
  - spec/graphql/base_type_spec.rb
423
431
  - spec/graphql/boolean_type_spec.rb
432
+ - spec/graphql/compatibility/execution_specification_spec.rb
433
+ - spec/graphql/compatibility/query_parser_specification_spec.rb
434
+ - spec/graphql/compatibility/schema_parser_specification_spec.rb
424
435
  - spec/graphql/define/instance_definable_spec.rb
425
436
  - spec/graphql/directive_spec.rb
426
437
  - spec/graphql/enum_type_spec.rb
@@ -464,6 +475,7 @@ files:
464
475
  - spec/graphql/relay/page_info_spec.rb
465
476
  - spec/graphql/relay/relation_connection_spec.rb
466
477
  - spec/graphql/scalar_type_spec.rb
478
+ - spec/graphql/schema/build_from_definition_spec.rb
467
479
  - spec/graphql/schema/catchall_middleware_spec.rb
468
480
  - spec/graphql/schema/loader_spec.rb
469
481
  - spec/graphql/schema/middleware_chain_spec.rb
@@ -474,6 +486,7 @@ files:
474
486
  - spec/graphql/schema/type_expression_spec.rb
475
487
  - spec/graphql/schema/unique_within_type_spec.rb
476
488
  - spec/graphql/schema/validation_spec.rb
489
+ - spec/graphql/schema/warden_spec.rb
477
490
  - spec/graphql/schema_spec.rb
478
491
  - spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
479
492
  - spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
@@ -540,6 +553,9 @@ test_files:
540
553
  - spec/graphql/argument_spec.rb
541
554
  - spec/graphql/base_type_spec.rb
542
555
  - spec/graphql/boolean_type_spec.rb
556
+ - spec/graphql/compatibility/execution_specification_spec.rb
557
+ - spec/graphql/compatibility/query_parser_specification_spec.rb
558
+ - spec/graphql/compatibility/schema_parser_specification_spec.rb
543
559
  - spec/graphql/define/instance_definable_spec.rb
544
560
  - spec/graphql/directive_spec.rb
545
561
  - spec/graphql/enum_type_spec.rb
@@ -583,6 +599,7 @@ test_files:
583
599
  - spec/graphql/relay/page_info_spec.rb
584
600
  - spec/graphql/relay/relation_connection_spec.rb
585
601
  - spec/graphql/scalar_type_spec.rb
602
+ - spec/graphql/schema/build_from_definition_spec.rb
586
603
  - spec/graphql/schema/catchall_middleware_spec.rb
587
604
  - spec/graphql/schema/loader_spec.rb
588
605
  - spec/graphql/schema/middleware_chain_spec.rb
@@ -593,6 +610,7 @@ test_files:
593
610
  - spec/graphql/schema/type_expression_spec.rb
594
611
  - spec/graphql/schema/unique_within_type_spec.rb
595
612
  - spec/graphql/schema/validation_spec.rb
613
+ - spec/graphql/schema/warden_spec.rb
596
614
  - spec/graphql/schema_spec.rb
597
615
  - spec/graphql/static_validation/rules/argument_literals_are_compatible_spec.rb
598
616
  - spec/graphql/static_validation/rules/arguments_are_defined_spec.rb
@@ -1,809 +0,0 @@
1
- module GraphQL
2
- module Language
3
- # If you create your own GraphQL parser, can verify it using these tests.
4
- #
5
- # @example Include these tests in a Minitest suite
6
- # require 'graphql/language/parser_tests'
7
- #
8
- # describe MyParser do
9
- # include GraphQL::Language::ParserTests
10
- # subject { MyParser }
11
- # end
12
- module ParserTests
13
- def self.included(test)
14
- test.send(:describe, "Parser Tests") do
15
- let(:document) { subject.parse(query_string) }
16
- let(:query_string) {%|
17
- query getStuff($someVar: Int = 1, $anotherVar: [String!] ) @skip(if: false) {
18
- myField: someField(someArg: $someVar, ok: 1.4) @skip(if: $anotherVar) @thing(or: "Whatever")
19
-
20
- anotherField(someArg: [1,2,3]) {
21
- nestedField
22
- ... moreNestedFields @skip(if: true)
23
- }
24
-
25
- ... on OtherType @include(unless: false){
26
- field(arg: [{key: "value", anotherKey: 0.9, anotherAnotherKey: WHATEVER}])
27
- anotherField
28
- }
29
-
30
- ... {
31
- id
32
- }
33
- }
34
-
35
- fragment moreNestedFields on NestedType @or(something: "ok") {
36
- anotherNestedField @enum(directive: true)
37
- }
38
- |}
39
-
40
- describe ".parse" do
41
- it "parses queries" do
42
- assert document
43
- end
44
-
45
- describe "visited nodes" do
46
- let(:query) { document.definitions.first }
47
- let(:fragment_def) { document.definitions.last }
48
-
49
- it "creates a valid document" do
50
- assert document.is_a?(GraphQL::Language::Nodes::Document)
51
- assert_equal 2, document.definitions.length
52
- end
53
-
54
- it "creates a valid operation" do
55
- assert query.is_a?(GraphQL::Language::Nodes::OperationDefinition)
56
- assert_equal "getStuff", query.name
57
- assert_equal "query", query.operation_type
58
- assert_equal 2, query.variables.length
59
- assert_equal 4, query.selections.length
60
- assert_equal 1, query.directives.length
61
- assert_equal [2, 13], [query.line, query.col]
62
- end
63
-
64
- it "creates a valid fragment definition" do
65
- assert fragment_def.is_a?(GraphQL::Language::Nodes::FragmentDefinition)
66
- assert_equal "moreNestedFields", fragment_def.name
67
- assert_equal 1, fragment_def.selections.length
68
- assert_equal "NestedType", fragment_def.type.name
69
- assert_equal 1, fragment_def.directives.length
70
- assert_equal [20, 13], fragment_def.position
71
- end
72
-
73
- describe "variable definitions" do
74
- let(:optional_var) { query.variables.first }
75
- it "gets name and type" do
76
- assert_equal "someVar", optional_var.name
77
- assert_equal "Int", optional_var.type.name
78
- end
79
-
80
- it "gets default value" do
81
- assert_equal 1, optional_var.default_value
82
- end
83
-
84
- it "gets position info" do
85
- assert_equal [2, 28], optional_var.position
86
- end
87
- end
88
-
89
- describe "fields" do
90
- let(:leaf_field) { query.selections.first }
91
- let(:parent_field) { query.selections[1] }
92
-
93
- it "gets name, alias, arguments and directives" do
94
- assert_equal "someField", leaf_field.name
95
- assert_equal "myField", leaf_field.alias
96
- assert_equal 2, leaf_field.directives.length
97
- assert_equal 2, leaf_field.arguments.length
98
- end
99
-
100
- it "gets nested fields" do
101
- assert_equal 2, parent_field.selections.length
102
- end
103
-
104
- it "gets location info" do
105
- assert_equal [3 ,15], leaf_field.position
106
- end
107
-
108
- describe "when the arguments list is empty" do
109
- let(:query_string) { "{ field() }"}
110
- let(:field) { query.selections.first }
111
- it "has zero arguments" do
112
- assert_equal 0, field.arguments.length
113
- end
114
- end
115
-
116
- describe "when selections are empty" do
117
- let(:query_string) { "{ field { } }"}
118
- let(:field) { query.selections.first }
119
- it "has zero selections" do
120
- assert_equal 0, field.selections.length
121
- end
122
- end
123
- end
124
-
125
- describe "arguments" do
126
- let(:literal_argument) { query.selections.first.arguments.last }
127
- let(:variable_argument) { query.selections.first.arguments.first }
128
-
129
- it "gets name and literal value" do
130
- assert_equal "ok", literal_argument.name
131
- assert_equal 1.4, literal_argument.value
132
- end
133
-
134
- it "gets name and variable value" do
135
- assert_equal "someArg", variable_argument.name
136
- assert_equal "someVar", variable_argument.value.name
137
- end
138
-
139
-
140
- it "gets position info" do
141
- assert_equal [3, 34], variable_argument.position
142
- end
143
- end
144
-
145
- describe "fragment spreads" do
146
- let(:fragment_spread) { query.selections[1].selections.last }
147
- it "gets the name and directives" do
148
- assert_equal "moreNestedFields", fragment_spread.name
149
- assert_equal 1, fragment_spread.directives.length
150
- end
151
-
152
- it "gets position info" do
153
- assert_equal [7, 17], fragment_spread.position
154
- end
155
- end
156
-
157
- describe "directives" do
158
- let(:variable_directive) { query.selections.first.directives.first }
159
-
160
- it "gets the name and arguments" do
161
- assert_equal "skip", variable_directive.name
162
- assert_equal "if", variable_directive.arguments.first.name
163
- assert_equal 1, variable_directive.arguments.length
164
- end
165
-
166
- it "gets position info" do
167
- assert_equal [3, 62], variable_directive.position
168
- end
169
- end
170
-
171
- describe "inline fragments" do
172
- let(:inline_fragment) { query.selections[2] }
173
- let(:typeless_inline_fragment) { query.selections[3] }
174
-
175
- it "gets the type and directives" do
176
- assert_equal "OtherType", inline_fragment.type.name
177
- assert_equal 2, inline_fragment.selections.length
178
- assert_equal 1, inline_fragment.directives.length
179
- end
180
-
181
- it "gets inline fragments without type conditions" do
182
- assert_equal nil, typeless_inline_fragment.type
183
- assert_equal 1, typeless_inline_fragment.selections.length
184
- assert_equal 0, typeless_inline_fragment.directives.length
185
- end
186
-
187
- it "gets position info" do
188
- assert_equal [10, 15], inline_fragment.position
189
- end
190
- end
191
-
192
- describe "inputs" do
193
- let(:query_string) {%|
194
- {
195
- field(
196
- int: 3,
197
- float: 4.7e-24,
198
- bool: false,
199
- string: "☀︎🏆\\n escaped \\" unicode \\u00b6 /",
200
- enum: ENUM_NAME,
201
- array: [7, 8, 9]
202
- object: {a: [1,2,3], b: {c: "4"}}
203
- unicode_bom: "\xef\xbb\xbfquery"
204
- keywordEnum: on
205
- )
206
- }
207
- |}
208
-
209
- let(:inputs) { document.definitions.first.selections.first.arguments }
210
-
211
- it "parses ints" do
212
- assert_equal 3, inputs[0].value
213
- end
214
-
215
- it "parses floats" do
216
- assert_equal 0.47e-23, inputs[1].value
217
- end
218
-
219
- it "parses booleans" do
220
- assert_equal false, inputs[2].value
221
- end
222
-
223
- it "parses UTF-8 strings" do
224
- assert_equal %|☀︎🏆\n escaped " unicode ¶ /|, inputs[3].value
225
- end
226
-
227
- it "parses enums" do
228
- assert_instance_of GraphQL::Language::Nodes::Enum, inputs[4].value
229
- assert_equal "ENUM_NAME", inputs[4].value.name
230
- end
231
-
232
- it "parses arrays" do
233
- assert_equal [7,8,9], inputs[5].value
234
- end
235
-
236
- it "parses objects" do
237
- obj = inputs[6].value
238
- assert_equal "a", obj.arguments[0].name
239
- assert_equal [1,2,3], obj.arguments[0].value
240
- assert_equal "b", obj.arguments[1].name
241
- assert_equal "c", obj.arguments[1].value.arguments[0].name
242
- assert_equal "4", obj.arguments[1].value.arguments[0].value
243
- end
244
-
245
- it "parses unicode bom" do
246
- obj = inputs[7].value
247
- assert_equal %|\xef\xbb\xbfquery|, inputs[7].value
248
- end
249
-
250
- it "parses enum 'on''" do
251
- assert_equal "on", inputs[8].value.name
252
- end
253
- end
254
- end
255
-
256
- describe "unnamed queries" do
257
- let(:query_string) {%|
258
- { name, age, height }
259
- |}
260
- let(:operation) { document.definitions.first }
261
-
262
- it "parses unnamed queries" do
263
- assert_equal 1, document.definitions.length
264
- assert_equal "query", operation.operation_type
265
- assert_equal nil, operation.name
266
- assert_equal 3, operation.selections.length
267
- end
268
- end
269
-
270
- describe "introspection query" do
271
- let(:query_string) { GraphQL::Introspection::INTROSPECTION_QUERY }
272
-
273
- it "parses a big ol' query" do
274
- assert(document)
275
- end
276
- end
277
-
278
- describe "schema with comments" do
279
- let(:query_string) {%|
280
- # Schema at beginning of file
281
-
282
- schema {
283
- query: Hello
284
- }
285
-
286
- # Comment between two definitions are omitted
287
-
288
- # This is a directive
289
- directive @foo(
290
- # It has an argument
291
- arg: Int
292
- ) on FIELD
293
-
294
- # Multiline comment
295
- #
296
- # With an enum
297
- enum Color {
298
- RED
299
-
300
- # Not a creative color
301
- GREEN
302
- BLUE
303
- }
304
-
305
- #Comment without preceding space
306
- type Hello {
307
- # And a field to boot
308
- str: String
309
- }
310
-
311
- # Comment for input object types
312
- input Car {
313
- # Color of the car
314
- color: String!
315
- }
316
-
317
- # Comment for interface definitions
318
- interface Vehicle {
319
- # Amount of wheels
320
- wheels: Int!
321
- }
322
-
323
- # Comment at the end of schema
324
- |}
325
-
326
- it "parses successfully" do
327
- document = subject.parse(query_string)
328
-
329
- assert_equal 6, document.definitions.size
330
-
331
- schema_definition = document.definitions.shift
332
- assert_equal GraphQL::Language::Nodes::SchemaDefinition, schema_definition.class
333
-
334
- directive_definition = document.definitions.shift
335
- assert_equal GraphQL::Language::Nodes::DirectiveDefinition, directive_definition.class
336
- assert_equal 'This is a directive', directive_definition.description
337
-
338
- enum_type_definition = document.definitions.shift
339
- assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, enum_type_definition.class
340
- assert_equal "Multiline comment\n\nWith an enum", enum_type_definition.description
341
-
342
- assert_nil enum_type_definition.values[0].description
343
- assert_equal 'Not a creative color', enum_type_definition.values[1].description
344
-
345
- object_type_definition = document.definitions.shift
346
- assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, object_type_definition.class
347
- assert_equal 'Comment without preceding space', object_type_definition.description
348
- assert_equal 'And a field to boot', object_type_definition.fields[0].description
349
-
350
- input_object_type_definition = document.definitions.shift
351
- assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, input_object_type_definition.class
352
- assert_equal 'Comment for input object types', input_object_type_definition.description
353
- assert_equal 'Color of the car', input_object_type_definition.fields[0].description
354
-
355
- interface_type_definition = document.definitions.shift
356
- assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, interface_type_definition.class
357
- assert_equal 'Comment for interface definitions', interface_type_definition.description
358
- assert_equal 'Amount of wheels', interface_type_definition.fields[0].description
359
- end
360
- end
361
-
362
- describe "schema" do
363
- it "parses the test schema" do
364
- schema = DummySchema
365
- schema_string = GraphQL::Schema::Printer.print_schema(schema)
366
-
367
- document = subject.parse(schema_string)
368
-
369
- assert_equal schema_string, document.to_query_string
370
- end
371
-
372
- it "parses mimal schema definition" do
373
- document = subject.parse('schema { query: QueryRoot }')
374
-
375
- schema = document.definitions.first
376
- assert_equal 'QueryRoot', schema.query
377
- assert_equal nil, schema.mutation
378
- assert_equal nil, schema.subscription
379
- end
380
-
381
- it "parses full schema definitions" do
382
- document = subject.parse('
383
- schema {
384
- query: QueryRoot
385
- mutation: MutationRoot
386
- subscription: SubscriptionRoot
387
- }
388
- ')
389
-
390
- schema = document.definitions.first
391
- assert_equal 'QueryRoot', schema.query
392
- assert_equal 'MutationRoot', schema.mutation
393
- assert_equal 'SubscriptionRoot', schema.subscription
394
- end
395
-
396
- it "parses object types" do
397
- document = subject.parse('
398
- type Comment implements Node {
399
- id: ID!
400
- }
401
- ')
402
-
403
- type = document.definitions.first
404
- assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
405
- assert_equal 'Comment', type.name
406
- assert_equal ['Node'], type.interfaces.map(&:name)
407
- assert_equal ['id'], type.fields.map(&:name)
408
- assert_equal [], type.fields[0].arguments
409
- assert_equal 'ID', type.fields[0].type.of_type.name
410
- end
411
-
412
- it "parses object types with directives" do
413
- document = subject.parse('
414
- type Comment implements Node @deprecated(reason: "No longer supported") {
415
- id: ID!
416
- }
417
- ')
418
-
419
- type = document.definitions.first
420
- assert_equal GraphQL::Language::Nodes::ObjectTypeDefinition, type.class
421
- assert_equal 'Comment', type.name
422
- assert_equal ['Node'], type.interfaces.map(&:name)
423
- assert_equal ['id'], type.fields.map(&:name)
424
- assert_equal [], type.fields[0].arguments
425
- assert_equal 'ID', type.fields[0].type.of_type.name
426
- assert_equal 1, type.directives.length
427
-
428
- deprecated_directive = type.directives[0]
429
- assert_equal 'deprecated', deprecated_directive.name
430
- assert_equal 'reason', deprecated_directive.arguments[0].name
431
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
432
- end
433
-
434
- it "parses field arguments" do
435
- document = subject.parse('
436
- type Mutation {
437
- post(id: ID!, data: PostData = { message: "First!1!", type: BLOG, tags: ["Test", "Annoying"] }): Post
438
- }
439
- ')
440
-
441
- field = document.definitions.first.fields.first
442
- assert_equal ['id', 'data'], field.arguments.map(&:name)
443
- data_arg = field.arguments[1]
444
- assert_equal 'PostData', data_arg.type.name
445
- assert_equal ['message', 'type', 'tags'], data_arg.default_value.arguments.map(&:name)
446
- tags_arg = data_arg.default_value.arguments[2]
447
- assert_equal ['Test', 'Annoying'], tags_arg.value
448
- end
449
-
450
- it "parses field arguments with directives" do
451
- document = subject.parse('
452
- type Mutation {
453
- post(id: ID! @deprecated(reason: "No longer supported"), data: String): Post
454
- }
455
- ')
456
-
457
- field = document.definitions.first.fields.first
458
- assert_equal ['id', 'data'], field.arguments.map(&:name)
459
- id_arg = field.arguments[0]
460
-
461
- deprecated_directive = id_arg.directives[0]
462
- assert_equal 'deprecated', deprecated_directive.name
463
- assert_equal 'reason', deprecated_directive.arguments[0].name
464
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
465
- end
466
-
467
- it "parses directive definition" do
468
- document = subject.parse('
469
- directive @include(if: Boolean!)
470
- on FIELD
471
- | FRAGMENT_SPREAD
472
- | INLINE_FRAGMENT
473
- ')
474
-
475
- type = document.definitions.first
476
- assert_equal GraphQL::Language::Nodes::DirectiveDefinition, type.class
477
- assert_equal 'include', type.name
478
-
479
- assert_equal 1, type.arguments.length
480
- assert_equal 'if', type.arguments[0].name
481
- assert_equal 'Boolean', type.arguments[0].type.of_type.name
482
-
483
- assert_equal ['FIELD', 'FRAGMENT_SPREAD', 'INLINE_FRAGMENT'], type.locations
484
- end
485
-
486
- it "parses scalar types" do
487
- document = subject.parse('scalar DateTime')
488
-
489
- type = document.definitions.first
490
- assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
491
- assert_equal 'DateTime', type.name
492
- end
493
-
494
- it "parses scalar types with directives" do
495
- document = subject.parse('scalar DateTime @deprecated(reason: "No longer supported")')
496
-
497
- type = document.definitions.first
498
- assert_equal GraphQL::Language::Nodes::ScalarTypeDefinition, type.class
499
- assert_equal 'DateTime', type.name
500
- assert_equal 1, type.directives.length
501
-
502
- deprecated_directive = type.directives[0]
503
- assert_equal 'deprecated', deprecated_directive.name
504
- assert_equal 'reason', deprecated_directive.arguments[0].name
505
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
506
- end
507
-
508
- it "parses interface types" do
509
- document = subject.parse('
510
- interface Node {
511
- id: ID!
512
- }
513
- ')
514
-
515
- type = document.definitions.first
516
- assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
517
- assert_equal 'Node', type.name
518
- assert_equal ['id'], type.fields.map(&:name)
519
- assert_equal [], type.fields[0].arguments
520
- assert_equal 'ID', type.fields[0].type.of_type.name
521
- end
522
-
523
- it "parses interface types with directives" do
524
- document = subject.parse('
525
- interface Node @deprecated(reason: "No longer supported") {
526
- id: ID!
527
- }
528
- ')
529
-
530
- type = document.definitions.first
531
- assert_equal GraphQL::Language::Nodes::InterfaceTypeDefinition, type.class
532
- assert_equal 'Node', type.name
533
- assert_equal 1, type.directives.length
534
-
535
- deprecated_directive = type.directives[0]
536
- assert_equal 'deprecated', deprecated_directive.name
537
- assert_equal 'reason', deprecated_directive.arguments[0].name
538
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
539
- end
540
-
541
- it "parses enum types" do
542
- document = subject.parse('
543
- enum DogCommand {
544
- SIT
545
- DOWN @deprecated(reason: "No longer supported")
546
- HEEL
547
- }
548
- ')
549
-
550
- type = document.definitions.first
551
- assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
552
- assert_equal 'DogCommand', type.name
553
- assert_equal 3, type.values.length
554
-
555
- assert_equal 'SIT', type.values[0].name
556
- assert_equal [], type.values[0].directives
557
-
558
- assert_equal 'DOWN', type.values[1].name
559
- assert_equal 1, type.values[1].directives.length
560
- deprecated_directive = type.values[1].directives[0]
561
- assert_equal 'deprecated', deprecated_directive.name
562
- assert_equal 'reason', deprecated_directive.arguments[0].name
563
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
564
-
565
- assert_equal 'HEEL', type.values[2].name
566
- assert_equal [], type.values[2].directives
567
- end
568
-
569
- it "parses enum types with directives" do
570
- document = subject.parse('
571
- enum DogCommand @deprecated(reason: "No longer supported") {
572
- SIT
573
- }
574
- ')
575
-
576
- type = document.definitions.first
577
- assert_equal GraphQL::Language::Nodes::EnumTypeDefinition, type.class
578
- assert_equal 'DogCommand', type.name
579
- assert_equal 1, type.directives.length
580
-
581
- deprecated_directive = type.directives[0]
582
- assert_equal 'deprecated', deprecated_directive.name
583
- assert_equal 'reason', deprecated_directive.arguments[0].name
584
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
585
- end
586
-
587
- it "parses input object types" do
588
- document = subject.parse('
589
- input EmptyMutationInput {
590
- clientMutationId: String
591
- }
592
- ')
593
-
594
- type = document.definitions.first
595
- assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
596
- assert_equal 'EmptyMutationInput', type.name
597
- assert_equal ['clientMutationId'], type.fields.map(&:name)
598
- assert_equal 'String', type.fields[0].type.name
599
- assert_equal nil, type.fields[0].default_value
600
- end
601
-
602
- it "parses input object types with directives" do
603
- document = subject.parse('
604
- input EmptyMutationInput @deprecated(reason: "No longer supported") {
605
- clientMutationId: String
606
- }
607
- ')
608
-
609
- type = document.definitions.first
610
- assert_equal GraphQL::Language::Nodes::InputObjectTypeDefinition, type.class
611
- assert_equal 'EmptyMutationInput', type.name
612
- assert_equal ['clientMutationId'], type.fields.map(&:name)
613
- assert_equal 'String', type.fields[0].type.name
614
- assert_equal nil, type.fields[0].default_value
615
- assert_equal 1, type.directives.length
616
-
617
- deprecated_directive = type.directives[0]
618
- assert_equal 'deprecated', deprecated_directive.name
619
- assert_equal 'reason', deprecated_directive.arguments[0].name
620
- assert_equal 'No longer supported', deprecated_directive.arguments[0].value
621
-
622
- end
623
- end
624
- end
625
-
626
- describe "errors" do
627
- let(:query_string) {%| query doSomething { bogus { } |}
628
- it "raises a parse error" do
629
- err = assert_raises(GraphQL::ParseError) { document }
630
- end
631
-
632
- it "correctly identifies parse error location and content" do
633
- e = assert_raises(GraphQL::ParseError) do
634
- GraphQL.parse("
635
- query getCoupons {
636
- allCoupons: {data{id}}
637
- }
638
- ")
639
- end
640
- assert_includes(e.message, '"{"')
641
- assert_includes(e.message, "LCURLY")
642
- assert_equal(3, e.line)
643
- assert_equal(33, e.col)
644
- end
645
-
646
- it "handles unexpected ends" do
647
- err = assert_raises(GraphQL::ParseError) { GraphQL.parse("{ ") }
648
- assert_equal "Unexpected end of document", err.message
649
- end
650
-
651
- it "rejects unsupported characters" do
652
- e = assert_raises(GraphQL::ParseError) do
653
- GraphQL.parse("{ field; }")
654
- end
655
-
656
- assert_includes(e.message, "Parse error on \";\"")
657
- end
658
-
659
- it "rejects control characters" do
660
- e = assert_raises(GraphQL::ParseError) do
661
- GraphQL.parse("{ \afield }")
662
- end
663
-
664
- assert_includes(e.message, "Parse error on \"\\a\"")
665
- end
666
-
667
- it "rejects partial BOM" do
668
- e = assert_raises(GraphQL::ParseError) do
669
- GraphQL.parse("{ \xeffield }")
670
- end
671
-
672
- assert_includes(e.message, "Parse error on \"\\xEF\"")
673
- end
674
-
675
- it "rejects vertical tabs" do
676
- e = assert_raises(GraphQL::ParseError) do
677
- GraphQL.parse("{ \vfield }")
678
- end
679
-
680
- assert_includes(e.message, "Parse error on \"\\v\"")
681
- end
682
-
683
- it "rejects form feed" do
684
- e = assert_raises(GraphQL::ParseError) do
685
- GraphQL.parse("{ \ffield }")
686
- end
687
-
688
- assert_includes(e.message, "Parse error on \"\\f\"")
689
- end
690
-
691
- it "rejects no break space" do
692
- e = assert_raises(GraphQL::ParseError) do
693
- GraphQL.parse("{ \xa0field }")
694
- end
695
-
696
- assert_includes(e.message, "Parse error on \"\\xA0\"")
697
- end
698
-
699
- it "rejects unterminated strings" do
700
- e = assert_raises(GraphQL::ParseError) do
701
- GraphQL.parse("\"")
702
- end
703
-
704
- assert_includes(e.message, "Parse error on \"\\\"\"")
705
-
706
- e = assert_raises(GraphQL::ParseError) do
707
- GraphQL.parse("\"\n\"")
708
- end
709
-
710
- assert_includes(e.message, "Parse error on \"\\n\"")
711
- end
712
-
713
- it "rejects bad escape sequence in strings" do
714
- e = assert_raises(GraphQL::ParseError) do
715
- GraphQL.parse("{ field(arg:\"\\x\") }")
716
- end
717
-
718
- assert_includes(e.message, "Parse error on bad Unicode escape sequence")
719
- end
720
-
721
- it "rejects incomplete escape sequence in strings" do
722
- e = assert_raises(GraphQL::ParseError) do
723
- GraphQL.parse("{ field(arg:\"\\u1\") }")
724
- end
725
-
726
- assert_includes(e.message, "bad Unicode escape sequence")
727
- end
728
-
729
- it "rejects unicode escape with bad chars" do
730
- e = assert_raises(GraphQL::ParseError) do
731
- GraphQL.parse("{ field(arg:\"\\u0XX1\") }")
732
- end
733
-
734
- assert_includes(e.message, "bad Unicode escape sequence")
735
-
736
- e = assert_raises(GraphQL::ParseError) do
737
- GraphQL.parse("{ field(arg:\"\\uXXXX\") }")
738
- end
739
-
740
- assert_includes(e.message, "bad Unicode escape sequence")
741
-
742
-
743
- e = assert_raises(GraphQL::ParseError) do
744
- GraphQL.parse("{ field(arg:\"\\uFXXX\") }")
745
- end
746
-
747
- assert_includes(e.message, "bad Unicode escape sequence")
748
-
749
-
750
- e = assert_raises(GraphQL::ParseError) do
751
- GraphQL.parse("{ field(arg:\"\\uXXXF\") }")
752
- end
753
-
754
- assert_includes(e.message, "bad Unicode escape sequence")
755
- end
756
-
757
- it "rejects fragments named 'on'" do
758
- e = assert_raises(GraphQL::ParseError) do
759
- GraphQL.parse("fragment on on on { on }")
760
- end
761
-
762
- assert_includes(e.message, "Parse error on \"on\"")
763
- end
764
-
765
- it "rejects fragment spread of 'on'" do
766
- e = assert_raises(GraphQL::ParseError) do
767
- GraphQL.parse("{ ...on }")
768
- end
769
-
770
- assert_includes(e.message, "Parse error on \"}\"")
771
- end
772
-
773
- it "rejects null value" do
774
- e = assert_raises(GraphQL::ParseError) do
775
- GraphQL.parse("{ fieldWithNullableStringInput(input: null) }")
776
- end
777
-
778
- assert_includes(e.message, "Parse error on \"null\"")
779
- end
780
- end
781
-
782
-
783
- describe "whitespace" do
784
- describe "whitespace-only queries" do
785
- let(:query_string) { " " }
786
- it "doesn't blow up" do
787
- assert_equal [], document.definitions
788
- end
789
- end
790
-
791
- describe "empty string queries" do
792
- let(:query_string) { "" }
793
- it "doesn't blow up" do
794
- assert_equal [], document.definitions
795
- end
796
- end
797
-
798
- describe "using tabs as whitespace" do
799
- let(:query_string) { "\t{\t\tid, \tname}"}
800
- it "parses the query" do
801
- assert_equal 1, document.definitions.length
802
- end
803
- end
804
- end
805
- end
806
- end
807
- end
808
- end
809
- end