graphql 0.0.4 → 0.1.0

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 (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