graphql 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graph_ql/directive.rb +36 -0
  3. data/lib/graph_ql/directives/directive_chain.rb +33 -0
  4. data/lib/graph_ql/directives/include_directive.rb +15 -0
  5. data/lib/graph_ql/directives/skip_directive.rb +15 -0
  6. data/lib/graph_ql/enum.rb +34 -0
  7. data/lib/graph_ql/fields/abstract_field.rb +37 -0
  8. data/lib/graph_ql/fields/access_field.rb +24 -0
  9. data/lib/graph_ql/fields/field.rb +34 -0
  10. data/lib/graph_ql/interface.rb +14 -0
  11. data/lib/graph_ql/introspection/arguments_field.rb +5 -0
  12. data/lib/graph_ql/introspection/directive_type.rb +12 -0
  13. data/lib/graph_ql/introspection/enum_value_type.rb +10 -0
  14. data/lib/graph_ql/introspection/enum_values_field.rb +15 -0
  15. data/lib/graph_ql/introspection/field_type.rb +11 -0
  16. data/lib/graph_ql/introspection/fields_field.rb +14 -0
  17. data/lib/graph_ql/introspection/input_fields_field.rb +12 -0
  18. data/lib/graph_ql/introspection/input_value_type.rb +10 -0
  19. data/lib/graph_ql/introspection/of_type_field.rb +12 -0
  20. data/lib/graph_ql/introspection/possible_types_field.rb +12 -0
  21. data/lib/graph_ql/introspection/schema_type.rb +32 -0
  22. data/lib/graph_ql/introspection/type_kind_enum.rb +7 -0
  23. data/lib/graph_ql/introspection/type_type.rb +22 -0
  24. data/lib/graph_ql/parser/nodes.rb +72 -0
  25. data/lib/graph_ql/parser/parser.rb +108 -0
  26. data/lib/graph_ql/parser/transform.rb +86 -0
  27. data/lib/graph_ql/parser/visitor.rb +47 -0
  28. data/lib/graph_ql/query.rb +50 -0
  29. data/lib/graph_ql/query/arguments.rb +25 -0
  30. data/lib/graph_ql/query/field_resolution_strategy.rb +83 -0
  31. data/lib/graph_ql/query/fragment_spread_resolution_strategy.rb +16 -0
  32. data/lib/graph_ql/query/inline_fragment_resolution_strategy.rb +14 -0
  33. data/lib/graph_ql/query/operation_resolver.rb +28 -0
  34. data/lib/graph_ql/query/selection_resolver.rb +20 -0
  35. data/lib/graph_ql/query/type_resolver.rb +19 -0
  36. data/lib/graph_ql/repl.rb +27 -0
  37. data/lib/graph_ql/schema.rb +30 -0
  38. data/lib/graph_ql/schema/type_reducer.rb +44 -0
  39. data/lib/graph_ql/type_kinds.rb +15 -0
  40. data/lib/graph_ql/types/abstract_type.rb +14 -0
  41. data/lib/graph_ql/types/boolean_type.rb +6 -0
  42. data/lib/graph_ql/types/float_type.rb +6 -0
  43. data/lib/graph_ql/types/input_object_type.rb +17 -0
  44. data/lib/graph_ql/types/input_value.rb +10 -0
  45. data/lib/graph_ql/types/int_type.rb +6 -0
  46. data/lib/graph_ql/types/list_type.rb +10 -0
  47. data/lib/graph_ql/types/non_null_type.rb +18 -0
  48. data/lib/graph_ql/types/non_null_with_bang.rb +5 -0
  49. data/lib/graph_ql/types/object_type.rb +62 -0
  50. data/lib/graph_ql/types/scalar_type.rb +5 -0
  51. data/lib/graph_ql/types/string_type.rb +6 -0
  52. data/lib/graph_ql/types/type_definer.rb +16 -0
  53. data/lib/graph_ql/union.rb +35 -0
  54. data/lib/graph_ql/validations/fields_are_defined_on_type.rb +44 -0
  55. data/lib/graph_ql/validations/fields_will_merge.rb +80 -0
  56. data/lib/graph_ql/validations/fragments_are_used.rb +24 -0
  57. data/lib/graph_ql/validator.rb +29 -0
  58. data/lib/graph_ql/version.rb +3 -0
  59. data/lib/graphql.rb +92 -99
  60. data/readme.md +17 -177
  61. data/spec/graph_ql/directive_spec.rb +81 -0
  62. data/spec/graph_ql/enum_spec.rb +5 -0
  63. data/spec/graph_ql/fields/field_spec.rb +10 -0
  64. data/spec/graph_ql/interface_spec.rb +13 -0
  65. data/spec/graph_ql/introspection/directive_type_spec.rb +40 -0
  66. data/spec/graph_ql/introspection/schema_type_spec.rb +39 -0
  67. data/spec/graph_ql/introspection/type_type_spec.rb +104 -0
  68. data/spec/graph_ql/parser/parser_spec.rb +120 -0
  69. data/spec/graph_ql/parser/transform_spec.rb +109 -0
  70. data/spec/graph_ql/parser/visitor_spec.rb +31 -0
  71. data/spec/graph_ql/query/operation_resolver_spec.rb +14 -0
  72. data/spec/graph_ql/query_spec.rb +82 -0
  73. data/spec/graph_ql/schema/type_reducer_spec.rb +24 -0
  74. data/spec/graph_ql/types/input_object_type_spec.rb +12 -0
  75. data/spec/graph_ql/types/object_type_spec.rb +35 -0
  76. data/spec/graph_ql/union_spec.rb +27 -0
  77. data/spec/graph_ql/validations/fields_are_defined_on_type_spec.rb +28 -0
  78. data/spec/graph_ql/validations/fields_will_merge_spec.rb +40 -0
  79. data/spec/graph_ql/validations/fragments_are_used_spec.rb +28 -0
  80. data/spec/graph_ql/validator_spec.rb +24 -0
  81. data/spec/spec_helper.rb +2 -2
  82. data/spec/support/dummy_app.rb +123 -63
  83. data/spec/support/dummy_data.rb +11 -0
  84. metadata +107 -59
  85. data/lib/graphql/call.rb +0 -8
  86. data/lib/graphql/connection.rb +0 -65
  87. data/lib/graphql/field.rb +0 -12
  88. data/lib/graphql/field_definer.rb +0 -25
  89. data/lib/graphql/introspection/call_type.rb +0 -13
  90. data/lib/graphql/introspection/connection.rb +0 -9
  91. data/lib/graphql/introspection/field_type.rb +0 -10
  92. data/lib/graphql/introspection/root_call_argument_node.rb +0 -5
  93. data/lib/graphql/introspection/root_call_type.rb +0 -20
  94. data/lib/graphql/introspection/schema_call.rb +0 -8
  95. data/lib/graphql/introspection/schema_type.rb +0 -17
  96. data/lib/graphql/introspection/type_call.rb +0 -8
  97. data/lib/graphql/introspection/type_type.rb +0 -18
  98. data/lib/graphql/node.rb +0 -244
  99. data/lib/graphql/parser/parser.rb +0 -39
  100. data/lib/graphql/parser/transform.rb +0 -22
  101. data/lib/graphql/query.rb +0 -109
  102. data/lib/graphql/root_call.rb +0 -202
  103. data/lib/graphql/root_call_argument.rb +0 -11
  104. data/lib/graphql/root_call_argument_definer.rb +0 -17
  105. data/lib/graphql/schema/all.rb +0 -46
  106. data/lib/graphql/schema/schema.rb +0 -87
  107. data/lib/graphql/schema/schema_validation.rb +0 -32
  108. data/lib/graphql/syntax/call.rb +0 -8
  109. data/lib/graphql/syntax/field.rb +0 -9
  110. data/lib/graphql/syntax/fragment.rb +0 -7
  111. data/lib/graphql/syntax/node.rb +0 -8
  112. data/lib/graphql/syntax/query.rb +0 -8
  113. data/lib/graphql/syntax/variable.rb +0 -7
  114. data/lib/graphql/types/boolean_type.rb +0 -3
  115. data/lib/graphql/types/number_type.rb +0 -3
  116. data/lib/graphql/types/object_type.rb +0 -6
  117. data/lib/graphql/types/string_type.rb +0 -3
  118. data/lib/graphql/version.rb +0 -3
  119. data/spec/graphql/node_spec.rb +0 -69
  120. data/spec/graphql/parser/parser_spec.rb +0 -168
  121. data/spec/graphql/parser/transform_spec.rb +0 -157
  122. data/spec/graphql/query_spec.rb +0 -274
  123. data/spec/graphql/root_call_spec.rb +0 -69
  124. data/spec/graphql/schema/schema_spec.rb +0 -93
  125. data/spec/graphql/schema/schema_validation_spec.rb +0 -48
  126. data/spec/support/nodes.rb +0 -175
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Directive do
4
+ let(:result) { GraphQL::Query.new(DummySchema, query_string, params: {"t" => true, "f" => false}).result }
5
+ describe 'on fields' do
6
+ let(:query_string) { %|query directives($t: Boolean!, $f: Boolean!) {
7
+ cheese(id: 1) {
8
+ # plain fields:
9
+ skipFlavor: flavor @skip(if: true)
10
+ dontSkipFlavor: flavor @skip(if: false)
11
+ includeFlavor: flavor @include(if: $t)
12
+ dontIncludeFlavor: flavor @include(if: $f)
13
+ # fields in fragments
14
+ ... includeIdField
15
+ ... dontIncludeIdField
16
+ ... skipIdField
17
+ ... dontSkipIdField
18
+ }
19
+ }
20
+ fragment includeIdField on Cheese { includeId: id @include(if: true) }
21
+ fragment dontIncludeIdField on Cheese { dontIncludeId: id @include(if: false) }
22
+ fragment skipIdField on Cheese { skipId: id @skip(if: true) }
23
+ fragment dontSkipIdField on Cheese { dontSkipId: id @skip(if: false) }
24
+ |}
25
+ it 'intercepts fields' do
26
+ expected = { "data" => {"directives" => {
27
+ "cheese" => {
28
+ "dontSkipFlavor" => "Brie",
29
+ "includeFlavor" => "Brie",
30
+ "includeId" => 1,
31
+ "dontSkipId" => 1,
32
+ },
33
+ }}}
34
+ assert_equal(expected, result)
35
+ end
36
+ end
37
+ describe 'on fragments' do
38
+ let(:query_string) { %|query directives {
39
+ cheese(id: 1) {
40
+ ... skipFlavorField @skip(if: true)
41
+ ... dontSkipFlavorField @skip(if: false)
42
+ ... includeFlavorField @include(if: true)
43
+ ... dontIncludeFlavorField @include(if: false)
44
+
45
+ ... includeIdField
46
+ ... dontIncludeIdField
47
+ ... skipIdField
48
+ ... dontSkipIdField
49
+
50
+ ... on Cheese @skip(if: true) { skipInlineId: id }
51
+ ... on Cheese @skip(if: false) { dontSkipInlineId: id }
52
+ ... on Cheese @include(if: true) { includeInlineId: id }
53
+ ... on Cheese @include(if: false) { dontIncludeInlineId: id }
54
+ }
55
+ }
56
+ fragment includeFlavorField on Cheese { includeFlavor: flavor }
57
+ fragment dontIncludeFlavorField on Cheese { dontIncludeFlavor: flavor }
58
+ fragment skipFlavorField on Cheese { skipFlavor: flavor }
59
+ fragment dontSkipFlavorField on Cheese { dontSkipFlavor: flavor }
60
+
61
+ fragment includeIdField on Cheese @include(if: true) { includeId: id }
62
+ fragment dontIncludeIdField on Cheese @include(if: false) { dontIncludeId: id }
63
+ fragment skipIdField on Cheese @skip(if: true) { skipId: id }
64
+ fragment dontSkipIdField on Cheese @skip(if: false) { dontSkipId: id }
65
+ |}
66
+
67
+ it 'intercepts fragment spreads' do
68
+ expected = { "data" => {"directives" => {
69
+ "cheese" => {
70
+ "dontSkipFlavor" => "Brie",
71
+ "includeFlavor" => "Brie",
72
+ "includeId" => 1,
73
+ "dontSkipId" => 1,
74
+ "dontSkipInlineId" => 1,
75
+ "includeInlineId" => 1,
76
+ },
77
+ }}}
78
+ assert_equal(expected, result)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Enum do
4
+ let(:enum) { DairyAnimalEnum }
5
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Field do
4
+ it 'accepts a proc as type' do
5
+ field = GraphQL::Field.new { |f|
6
+ f.type(-> { DairyProductUnion })
7
+ }
8
+ assert_equal(DairyProductUnion, field.type)
9
+ end
10
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Interface do
4
+ let(:interface) { Edible }
5
+ it 'has possible types' do
6
+ assert_equal([CheeseType, MilkType], interface.possible_types)
7
+ end
8
+
9
+ it 'resolves types for objects' do
10
+ assert_equal(CheeseType, interface.resolve_type(CHEESES.values.first))
11
+ assert_equal(MilkType, interface.resolve_type(MILKS.values.first))
12
+ end
13
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::DirectiveType do
4
+ let(:query_string) {%|
5
+ query getDirectives {
6
+ __schema {
7
+ directives { name, args { name, type { name, ofType { name }} }, onField, onFragment, onOperation }
8
+ }
9
+ }
10
+ |}
11
+ let(:result) { GraphQL::Query.new(DummySchema, query_string).result }
12
+
13
+ it 'shows directive info ' do
14
+ expected = { "data" => {"getDirectives" => {
15
+ "__schema" => {
16
+ "directives" => [
17
+ {
18
+ "name" => "skip",
19
+ "args" => [
20
+ {"name"=>"if", "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Boolean"}}}
21
+ ],
22
+ "onField" => true,
23
+ "onFragment" => true,
24
+ "onOperation" => false,
25
+ },
26
+ {
27
+ "name" => "include",
28
+ "args" => [
29
+ {"name"=>"if", "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Boolean"}}}
30
+ ],
31
+ "onField" => true,
32
+ "onFragment" => true,
33
+ "onOperation" => false,
34
+ },
35
+ ]
36
+ }
37
+ }}}
38
+ assert_equal(expected, result)
39
+ end
40
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::SchemaType do
4
+ let(:query_string) {%|
5
+ query getSchema {
6
+ __schema {
7
+ types { name }
8
+ queryType { fields { name }}
9
+ mutationType { fields { name }}
10
+ }
11
+ }
12
+ |}
13
+ let(:result) { GraphQL::Query.new(DummySchema, query_string).result }
14
+ it 'exposes the schema' do
15
+ expected = { "data" => { "getSchema" => {
16
+ "__schema" => {
17
+ "types" => DummySchema.types.values.map { |t| t.name.nil? ? (p t; raise("no name for #{t}")) : {"name" => t.name} },
18
+ "queryType"=>{
19
+ "fields"=>[
20
+ {"name"=>"cheese"},
21
+ {"name"=>"fromSource"},
22
+ {"name"=>"favoriteEdible"},
23
+ {"name"=>"searchDairy"},
24
+ {"name"=>"__typename"},
25
+ {"name"=>"__type"},
26
+ {"name"=>"__schema"},
27
+ ]
28
+ },
29
+ "mutationType"=> {
30
+ "fields"=>[
31
+ {"name"=>"pushValue"},
32
+ {"name"=>"__typename"}
33
+ ]
34
+ },
35
+ }
36
+ }}}
37
+ assert_equal(expected, result)
38
+ end
39
+ end
@@ -0,0 +1,104 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::TypeType do
4
+ let(:query_string) {%|
5
+ query introspectionQuery {
6
+ cheeseType: __type(name: "Cheese") { name, kind, fields { name, isDeprecated, type { name, ofType { name } } } }
7
+ dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: false) { name, isDeprecated } }
8
+ dairyProduct: __type(name: "DairyProduct") { name, kind, possibleTypes { name } }
9
+ animalProduct: __type(name: "AnimalProduct") { name, kind, possibleTypes { name }, fields { name } }
10
+ }
11
+ |}
12
+ let(:query) { GraphQL::Query.new(DummySchema, query_string, context: {}, params: {"cheeseId" => 2})}
13
+ let(:cheese_fields) {[
14
+ {"name"=>"id", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "Int"}}},
15
+ {"name"=>"flavor", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "String"}}},
16
+ {"name"=>"source", "isDeprecated" => false, "type" => { "name" => "Non-Null", "ofType" => { "name" => "DairyAnimal"}}},
17
+ {"name"=>"__typename", "isDeprecated"=>false, "type"=> { "name" => "Non-Null", "ofType" => { "name" => "String"}}},
18
+ ]}
19
+
20
+ let(:dairy_animals) {[
21
+ {"name"=>"COW", "isDeprecated"=> false },
22
+ {"name"=>"GOAT", "isDeprecated"=> false },
23
+ {"name"=>"SHEEP", "isDeprecated"=> false },
24
+ ]}
25
+ it 'exposes metadata about types' do
26
+ expected = {"data"=> { "introspectionQuery" => {
27
+ "cheeseType" => {
28
+ "name"=> "Cheese",
29
+ "kind" => "OBJECT",
30
+ "fields"=> cheese_fields
31
+ },
32
+ "dairyAnimal"=>{
33
+ "name"=>"DairyAnimal",
34
+ "kind"=>"ENUM",
35
+ "enumValues"=> dairy_animals,
36
+ },
37
+ "dairyProduct"=>{
38
+ "name"=>"DairyProduct",
39
+ "kind"=>"UNION",
40
+ "possibleTypes"=>[{"name"=>"Milk"}, {"name"=>"Cheese"}],
41
+ },
42
+ "animalProduct" => {
43
+ "name"=>"AnimalProduct",
44
+ "kind"=>"INTERFACE",
45
+ "possibleTypes"=>[{"name"=>"Cheese"}, {"name"=>"Milk"}],
46
+ "fields"=>[
47
+ {"name"=>"source"},
48
+ {"name"=>"__typename"},
49
+ ]
50
+ }
51
+ }}}
52
+ assert_equal(expected, query.result)
53
+ end
54
+
55
+ describe 'deprecated fields' do
56
+ let(:query_string) {%|
57
+ query introspectionQuery {
58
+ cheeseType: __type(name: "Cheese") { name, kind, fields(includeDeprecated: true) { name, isDeprecated, type { name, ofType { name } } } }
59
+ dairyAnimal: __type(name: "DairyAnimal") { name, kind, enumValues(includeDeprecated: true) { name, isDeprecated } }
60
+ }
61
+ |}
62
+ let(:deprecated_fields) { {"name"=>"fatContent", "isDeprecated"=>true, "type"=>{"name"=>"Non-Null", "ofType"=>{"name"=>"Float"}}} }
63
+ it 'can expose deprecated fields' do
64
+ typename = cheese_fields.pop
65
+ new_cheese_fields = cheese_fields + [deprecated_fields, typename]
66
+ expected = { "data" => { "introspectionQuery" => {
67
+ "cheeseType" => {
68
+ "name"=> "Cheese",
69
+ "kind" => "OBJECT",
70
+ "fields"=> new_cheese_fields
71
+ },
72
+ "dairyAnimal"=>{
73
+ "name"=>"DairyAnimal",
74
+ "kind"=>"ENUM",
75
+ "enumValues"=> dairy_animals + [{"name" => "YAK", "isDeprecated" => true}],
76
+ },
77
+ }}}
78
+ assert_equal(expected, query.result)
79
+ end
80
+
81
+ describe 'input objects' do
82
+ let(:query_string) {%|
83
+ query introspectionQuery {
84
+ __type(name: "DairyProductInput") { name, description, kind, inputFields { name, type { name }, defaultValue } }
85
+ }
86
+ |}
87
+
88
+ it 'exposes metadata about input objects' do
89
+ expected = { "data" => { "introspectionQuery" => {
90
+ "__type" => {
91
+ "name"=>"DairyProductInput",
92
+ "description"=>"Properties for finding a dairy product",
93
+ "kind"=>"INPUT_OBJECT",
94
+ "inputFields"=>[
95
+ {"name"=>"source", "type"=>{ "name" => "DairyAnimal"}, "defaultValue"=>nil},
96
+ {"name"=>"fatContent", "type"=>{ "name" => "Float"}, "defaultValue"=>nil}
97
+ ]
98
+ }
99
+ }}}
100
+ assert_equal(expected, query.result)
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Parser do
4
+ let(:parser) { GraphQL::PARSER }
5
+
6
+ it 'parses documents' do
7
+ assert(parser.parse_with_debug(%|
8
+ # let's make a big query:
9
+ # a read-only:
10
+ query getStuff {id, name @skip(if: true)}
11
+ # a mutation:
12
+ mutation changeStuff($override: Boolean!, $cucumbers: [Vegetable]!) @veggie, @healthy(vitamins: true) {
13
+ # change the cucumber
14
+ changeStuff(thing: $cucumbers) {
15
+ id,
16
+ name,
17
+ ... on Species { color },
18
+ ... family # background info, of course
19
+ }
20
+ }
21
+
22
+ # a fragment:
23
+ fragment family on Species {
24
+ family {
25
+ name, # name of the family
26
+ members(first: 3, query: {isPlant: true}) # some of the other examples
27
+ }
28
+ }
29
+
30
+ fragment nonsense on NonsenseType @skip(if: true) { bogus }
31
+ |), 'gets a document with lots of comments')
32
+
33
+ assert(parser.parse_with_debug("{fields, only, inThisOne}"), 'fetch-only query')
34
+ end
35
+
36
+
37
+ it 'parses operation definitions' do
38
+ assert(parser.operation_definition.parse_with_debug(%|{id, name, ...people}|), "just a selection")
39
+ assert(parser.operation_definition.parse_with_debug(%|query personStuff {id, name, ...people, ... stuff}|), "named fetch")
40
+ assert(parser.operation_definition.parse_with_debug(%|query personStuff @flagDirective {id, name, ...people}|), "with a directive")
41
+ assert(parser.operation_definition.parse_with_debug(%|mutation changeStuff($stuff: Int = 1, $things: [Boolean]!) {id, name, ...people}|), "mutation with arguments")
42
+ end
43
+
44
+ it 'parses fragment definitions' do
45
+ assert(parser.fragment_definition.parse_with_debug(%|fragment nutritionFacts on Food { fat, sodium, carbohydrates, vitamins { a, b } }|))
46
+ assert(parser.fragment_definition.parse_with_debug(%|fragment nutritionFacts on Food @directive(key: 1) { fat, sodium, carbohydrates, vitamins { a, b } }|), 'gets directives')
47
+ end
48
+
49
+ it 'parses selections' do
50
+ assert(parser.selections.parse_with_debug(%|{id, name, people { count }}|), 'gets nested fields')
51
+ assert(parser.selections.parse_with_debug(%|{id, ... myFragment }|), 'gets fragment spreads')
52
+ assert(parser.selections.parse_with_debug(%|{id, ... on User @myFlag { name, photo } }|), 'gets inline fragments')
53
+ assert(parser.selections.parse_with_debug(%|{id @skip(if: true), ... myFragment @include(if: $something)}|), 'gets directives')
54
+ end
55
+
56
+ it 'parses directives' do
57
+ assert(parser.directives.parse_with_debug("@doSomething"), 'gets without argument')
58
+ assert(parser.directives.parse_with_debug('@doSomething(why: "forSomeReason")'), 'gets with argument')
59
+ assert(parser.directives.parse_with_debug('@myFlag, @doSomething(why: "forSomeReason")'), 'gets multiple')
60
+ end
61
+
62
+ it 'parses fields' do
63
+ assert(parser.field.parse_with_debug(%|myField { name, id }|), 'gets subselections')
64
+ assert(parser.field.parse_with_debug(%{myAlias: myField}), 'gets an alias')
65
+ assert(parser.field.parse_with_debug(%{myField(intKey: 1, floatKey: 1.1e5)}), 'gets arguments')
66
+ assert(parser.field.parse_with_debug(%{myAlias: myField(stringKey: "my_string", boolKey: false, objKey: {key : true})}), 'gets alias and arguments')
67
+ assert(parser.field.parse_with_debug(%|myField @withFlag, @skip(if: true) { name, id }|), 'gets with directive')
68
+ end
69
+
70
+ it 'parses variable definitions' do
71
+ assert(parser.operation_variable_definition.parse_with_debug("$myVar: Boolean = true"), "it gets variables with defaults")
72
+ assert(parser.operation_variable_definition.parse_with_debug("$myVar: [Int]"), "it gets list variables")
73
+ assert(parser.operation_variable_definition.parse_with_debug("$myVar: Elephant!"), "it gets non-null variables")
74
+ assert(parser.operation_variable_definition.parse_with_debug("$myVar: [Food]!"), "it gets non-null list variables")
75
+ assert(parser.operation_variable_definitions.parse_with_debug(%|($myVar: Elephant!, $myList: [Float], $myString: String="Cheese")|), "it gets a list of defns")
76
+ end
77
+
78
+ describe 'value' do
79
+ it 'gets ints' do
80
+ assert(parser.value.parse_with_debug("100"), 'positive')
81
+ assert(parser.value.parse_with_debug("-9"), 'negative')
82
+ assert(parser.value.parse_with_debug("0"), 'zero')
83
+ end
84
+
85
+ it 'gets floats' do
86
+ assert(parser.value.parse_with_debug("1.14"), 'no exponent')
87
+ assert(parser.value.parse_with_debug("6.7e-9"), 'negative exponent')
88
+ assert(parser.value.parse_with_debug("0.4e12"), 'exponent')
89
+ end
90
+
91
+ it 'gets booleans' do
92
+ assert(parser.value.parse_with_debug("true"))
93
+ assert(parser.value.parse_with_debug("false"))
94
+ end
95
+
96
+ it 'gets strings' do
97
+ assert(parser.value.parse_with_debug('"my string"'))
98
+ end
99
+
100
+ it 'gets arrays' do
101
+ assert(parser.value.parse_with_debug('[true, 1, "my string", -5.123e56]'), 'array of values')
102
+ assert(parser.value.parse_with_debug('[]'), 'empty array')
103
+ assert(parser.value.parse_with_debug('[[true, 1], ["my string", -5.123e56]]'), 'array of arrays')
104
+ end
105
+
106
+ it 'gets variables' do
107
+ assert(parser.value.parse_with_debug('$myVariable'), 'gets named variables')
108
+ end
109
+
110
+ it 'gets objects' do
111
+ assert(parser.value.parse_with_debug('{name: "tomato", calories: 50}'), 'gets scalar values')
112
+ assert(parser.value.parse_with_debug('{listOfValues: [1, 2, [3]], nestedObject: {nestedKey: "nested{Value}"}}'), 'gets complex values')
113
+ assert(parser.value.parse_with_debug('{variableKey: $variableValue}'), 'gets variables')
114
+ end
115
+
116
+ it 'gets enums' do
117
+ assert(parser.value.parse_with_debug("MY_ENUM"), 'gets enums')
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Transform do
4
+ def get_result(query_string, parse: nil, debug: false)
5
+ # send parse: :value to do something less than a document
6
+ parser = parse ? GraphQL::PARSER.send(parse) : GraphQL::PARSER
7
+ raw_tree = parser.parse_with_debug(query_string)
8
+ transformed_result = GraphQL::TRANSFORM.apply(raw_tree)
9
+ # send debug: true to see parsing & transforming output
10
+ if debug
11
+ p raw_tree.inspect
12
+ p transformed_result.inspect
13
+ end
14
+ transformed_result
15
+ end
16
+
17
+ it 'transforms documents' do
18
+ query = %|
19
+ # you can retrieve data:
20
+ query someInfo {
21
+ me {
22
+ name, favorite_food,
23
+ ...personInfo
24
+ }
25
+ }
26
+
27
+ # assign fragments:
28
+ fragment personInfo on Person {
29
+ birthdate, name # with fields
30
+ }
31
+
32
+ fragment petInfo on Pet { isHousebroken, species } # all on one line
33
+
34
+ # and also mutations
35
+ mutation changePetInfo($id: Int = 5, $info: [Dog]) {
36
+ changePetName(id: $id, info: $info) {
37
+ name,
38
+ ... petInfo,
39
+ }
40
+ }
41
+ |
42
+ res = get_result(query, debug: false)
43
+ assert_equal(4, res.parts.length)
44
+
45
+ res = get_result("{ me {id, birthdate} } # query shorthand")
46
+ assert_equal(1, res.parts.length)
47
+ assert_equal("me", res.parts.first.selections.first.name)
48
+ end
49
+
50
+ it 'transforms operation definitions' do
51
+ res = get_result("query someInfo { a, b, c }", parse: :operation_definition)
52
+ assert_equal("query", res.operation_type)
53
+ assert_equal("someInfo", res.name)
54
+ assert_equal(3, res.selections.length)
55
+
56
+ res = get_result("mutation changeThings($var: Float = 4.5, $arr: [Int]!) @flag, @skip(if: 1) { changeThings(var: $var) { a,b,c }}", parse: :operation_definition)
57
+ assert_equal("mutation", res.operation_type)
58
+ assert_equal("var", res.variables.first.name)
59
+ assert_equal("Float", res.variables.first.type.name)
60
+ assert_equal(4.5, res.variables.first.default_value)
61
+ assert_equal("arr", res.variables.last.name)
62
+ assert_equal("Int", res.variables.last.type.of_type.of_type.name)
63
+ assert_equal(2, res.directives.length)
64
+ end
65
+
66
+ it 'transforms fragment definitions' do
67
+ res = get_result("fragment someFields on SomeType @flag1, @flag2 { id, name }", parse: :fragment_definition)
68
+ assert_equal("someFields", res.name)
69
+ assert_equal("SomeType", res.type)
70
+ assert_equal(2, res.directives.length)
71
+ assert_equal(2, res.selections.length)
72
+ end
73
+
74
+ it 'transforms selections' do
75
+ res = get_result("{ id, ...petStuff @flag, ... on Pet { isHousebroken }, name }", parse: :selections)
76
+ expected_classes = [GraphQL::Nodes::Field, GraphQL::Nodes::FragmentSpread, GraphQL::Nodes::InlineFragment, GraphQL::Nodes::Field]
77
+ assert_equal(expected_classes, res.map(&:class))
78
+ end
79
+
80
+ it 'transforms fields' do
81
+ res = get_result(%|best_pals: friends(first: 3, coolnessLevel: SO_COOL, query: {nice: {very: true}})|, parse: :field)
82
+ assert_equal(GraphQL::Nodes::Field, res.class)
83
+ assert_equal("friends", res.name)
84
+ assert_equal("best_pals", res.alias)
85
+ assert_equal("first", res.arguments[0].name)
86
+ assert_equal(3, res.arguments[0].value)
87
+ assert_equal("SO_COOL", res.arguments[1].value.name)
88
+ assert_equal({"nice" => {"very" => true}}, res.arguments[2].value.to_h)
89
+
90
+ res = get_result(%|me @flag, @include(if: "something") {name, id}|, parse: :field)
91
+ assert_equal("me", res.name)
92
+ assert_equal(nil, res.alias)
93
+ assert_equal(2, res.directives.length)
94
+ assert_equal("flag", res.directives.first.name)
95
+ assert_equal("something", res.directives.last.arguments.first.value)
96
+ assert_equal(2, res.selections.length)
97
+ end
98
+
99
+ it 'transforms directives' do
100
+ res = get_result("@doSomething(vigorously: true)", parse: :directive)
101
+ assert_equal("doSomething", res.name, 'gets the name without @')
102
+ assert_equal("vigorously", res.arguments.first.name)
103
+ assert_equal(true, res.arguments.first.value)
104
+
105
+ res = get_result("@someFlag", parse: :directive)
106
+ assert_equal("someFlag", res.name)
107
+ assert_equal([], res.arguments, 'gets [] if no args')
108
+ end
109
+ end