graphql 0.1.0 → 0.2.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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graph_ql/directive.rb +9 -5
  3. data/lib/graph_ql/directives/include_directive.rb +2 -2
  4. data/lib/graph_ql/directives/skip_directive.rb +2 -2
  5. data/lib/graph_ql/enum.rb +5 -8
  6. data/lib/graph_ql/field.rb +50 -0
  7. data/lib/graph_ql/interface.rb +4 -0
  8. data/lib/graph_ql/introspection/arguments_field.rb +2 -2
  9. data/lib/graph_ql/introspection/directive_type.rb +10 -10
  10. data/lib/graph_ql/introspection/enum_value_type.rb +11 -8
  11. data/lib/graph_ql/introspection/enum_values_field.rb +5 -5
  12. data/lib/graph_ql/introspection/field_type.rb +14 -10
  13. data/lib/graph_ql/introspection/fields_field.rb +4 -4
  14. data/lib/graph_ql/introspection/input_fields_field.rb +3 -3
  15. data/lib/graph_ql/introspection/input_value_type.rb +8 -8
  16. data/lib/graph_ql/introspection/interfaces_field.rb +5 -0
  17. data/lib/graph_ql/introspection/of_type_field.rb +3 -9
  18. data/lib/graph_ql/introspection/possible_types_field.rb +3 -10
  19. data/lib/graph_ql/introspection/schema_type.rb +8 -14
  20. data/lib/graph_ql/introspection/type_kind_enum.rb +1 -1
  21. data/lib/graph_ql/introspection/type_type.rb +17 -19
  22. data/lib/graph_ql/introspection/typename_field.rb +15 -0
  23. data/lib/graph_ql/parser.rb +9 -0
  24. data/lib/graph_ql/parser/nodes.rb +17 -7
  25. data/lib/graph_ql/parser/parser.rb +7 -7
  26. data/lib/graph_ql/parser/transform.rb +23 -20
  27. data/lib/graph_ql/parser/visitor.rb +29 -13
  28. data/lib/graph_ql/query.rb +39 -16
  29. data/lib/graph_ql/query/field_resolution_strategy.rb +15 -11
  30. data/lib/graph_ql/query/type_resolver.rb +4 -2
  31. data/lib/graph_ql/repl.rb +1 -1
  32. data/lib/graph_ql/schema.rb +19 -7
  33. data/lib/graph_ql/schema/each_item_validator.rb +12 -0
  34. data/lib/graph_ql/schema/field_validator.rb +13 -0
  35. data/lib/graph_ql/schema/implementation_validator.rb +21 -0
  36. data/lib/graph_ql/schema/schema_validator.rb +10 -0
  37. data/lib/graph_ql/schema/type_reducer.rb +6 -6
  38. data/lib/graph_ql/schema/type_validator.rb +47 -0
  39. data/lib/graph_ql/static_validation.rb +18 -0
  40. data/lib/graph_ql/static_validation/argument_literals_are_compatible.rb +13 -0
  41. data/lib/graph_ql/static_validation/arguments_are_defined.rb +10 -0
  42. data/lib/graph_ql/static_validation/arguments_validator.rb +16 -0
  43. data/lib/graph_ql/static_validation/directives_are_defined.rb +18 -0
  44. data/lib/graph_ql/static_validation/fields_are_defined_on_type.rb +29 -0
  45. data/lib/graph_ql/static_validation/fields_have_appropriate_selections.rb +31 -0
  46. data/lib/graph_ql/static_validation/fields_will_merge.rb +93 -0
  47. data/lib/graph_ql/static_validation/fragment_types_exist.rb +24 -0
  48. data/lib/graph_ql/static_validation/fragments_are_used.rb +30 -0
  49. data/lib/graph_ql/static_validation/literal_validator.rb +27 -0
  50. data/lib/graph_ql/static_validation/message.rb +29 -0
  51. data/lib/graph_ql/static_validation/required_arguments_are_present.rb +13 -0
  52. data/lib/graph_ql/static_validation/type_stack.rb +87 -0
  53. data/lib/graph_ql/static_validation/validator.rb +48 -0
  54. data/lib/graph_ql/type_kinds.rb +50 -12
  55. data/lib/graph_ql/types/argument_definer.rb +7 -0
  56. data/lib/graph_ql/types/boolean_type.rb +3 -3
  57. data/lib/graph_ql/types/field_definer.rb +19 -0
  58. data/lib/graph_ql/types/float_type.rb +3 -3
  59. data/lib/graph_ql/types/input_object_type.rb +4 -0
  60. data/lib/graph_ql/types/input_value.rb +1 -1
  61. data/lib/graph_ql/types/int_type.rb +4 -4
  62. data/lib/graph_ql/types/list_type.rb +5 -1
  63. data/lib/graph_ql/types/non_null_type.rb +4 -0
  64. data/lib/graph_ql/types/object_type.rb +9 -20
  65. data/lib/graph_ql/types/scalar_type.rb +4 -0
  66. data/lib/graph_ql/types/string_type.rb +3 -3
  67. data/lib/graph_ql/types/type_definer.rb +5 -9
  68. data/lib/graph_ql/union.rb +6 -17
  69. data/lib/graph_ql/version.rb +1 -1
  70. data/lib/graphql.rb +58 -78
  71. data/readme.md +80 -7
  72. data/spec/graph_ql/interface_spec.rb +15 -1
  73. data/spec/graph_ql/introspection/directive_type_spec.rb +2 -2
  74. data/spec/graph_ql/introspection/schema_type_spec.rb +2 -1
  75. data/spec/graph_ql/introspection/type_type_spec.rb +16 -1
  76. data/spec/graph_ql/parser/parser_spec.rb +3 -1
  77. data/spec/graph_ql/parser/transform_spec.rb +12 -2
  78. data/spec/graph_ql/parser/visitor_spec.rb +13 -5
  79. data/spec/graph_ql/query_spec.rb +25 -13
  80. data/spec/graph_ql/schema/field_validator_spec.rb +21 -0
  81. data/spec/graph_ql/schema/type_reducer_spec.rb +2 -2
  82. data/spec/graph_ql/schema/type_validator_spec.rb +54 -0
  83. data/spec/graph_ql/static_validation/argument_literals_are_compatible_spec.rb +41 -0
  84. data/spec/graph_ql/static_validation/arguments_are_defined_spec.rb +40 -0
  85. data/spec/graph_ql/static_validation/directives_are_defined_spec.rb +33 -0
  86. data/spec/graph_ql/static_validation/fields_are_defined_on_type_spec.rb +59 -0
  87. data/spec/graph_ql/static_validation/fields_have_appropriate_selections_spec.rb +30 -0
  88. data/spec/graph_ql/{validations → static_validation}/fields_will_merge_spec.rb +24 -17
  89. data/spec/graph_ql/static_validation/fragment_types_exist_spec.rb +38 -0
  90. data/spec/graph_ql/static_validation/fragments_are_used_spec.rb +24 -0
  91. data/spec/graph_ql/static_validation/required_arguments_are_present_spec.rb +41 -0
  92. data/spec/graph_ql/static_validation/type_stack_spec.rb +35 -0
  93. data/spec/graph_ql/static_validation/validator_spec.rb +28 -0
  94. data/spec/graph_ql/types/object_type_spec.rb +1 -1
  95. data/spec/graph_ql/union_spec.rb +1 -14
  96. data/spec/support/dummy_app.rb +75 -53
  97. metadata +53 -31
  98. data/lib/graph_ql/fields/abstract_field.rb +0 -37
  99. data/lib/graph_ql/fields/access_field.rb +0 -24
  100. data/lib/graph_ql/fields/field.rb +0 -34
  101. data/lib/graph_ql/types/abstract_type.rb +0 -14
  102. data/lib/graph_ql/validations/fields_are_defined_on_type.rb +0 -44
  103. data/lib/graph_ql/validations/fields_will_merge.rb +0 -80
  104. data/lib/graph_ql/validations/fragments_are_used.rb +0 -24
  105. data/lib/graph_ql/validator.rb +0 -29
  106. data/spec/graph_ql/validations/fields_are_defined_on_type_spec.rb +0 -28
  107. data/spec/graph_ql/validations/fragments_are_used_spec.rb +0 -28
  108. data/spec/graph_ql/validator_spec.rb +0 -24
@@ -53,12 +53,20 @@ describe GraphQL::Transform do
53
53
  assert_equal("someInfo", res.name)
54
54
  assert_equal(3, res.selections.length)
55
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)
56
+ res = get_result(
57
+ "mutation changeThings(
58
+ $var: Float = 4.5E+6,
59
+ $arr: [Int]!
60
+ ) @flag, @skip(if: 1) {
61
+ changeThings(var: $var) { a,b,c }
62
+ }", parse: :operation_definition)
57
63
  assert_equal("mutation", res.operation_type)
58
64
  assert_equal("var", res.variables.first.name)
59
65
  assert_equal("Float", res.variables.first.type.name)
60
- assert_equal(4.5, res.variables.first.default_value)
66
+ assert_equal(4_500_000.0, res.variables.first.default_value)
61
67
  assert_equal("arr", res.variables.last.name)
68
+ assert_equal(3, res.variables.last.line)
69
+ assert_equal(10, res.variables.last.col)
62
70
  assert_equal("Int", res.variables.last.type.of_type.of_type.name)
63
71
  assert_equal(2, res.directives.length)
64
72
  end
@@ -80,6 +88,8 @@ describe GraphQL::Transform do
80
88
  it 'transforms fields' do
81
89
  res = get_result(%|best_pals: friends(first: 3, coolnessLevel: SO_COOL, query: {nice: {very: true}})|, parse: :field)
82
90
  assert_equal(GraphQL::Nodes::Field, res.class)
91
+ assert_equal(1, res.line)
92
+ assert_equal(1, res.col)
83
93
  assert_equal("friends", res.name)
84
94
  assert_equal("best_pals", res.alias)
85
95
  assert_equal("first", res.arguments[0].name)
@@ -9,13 +9,13 @@ describe GraphQL::Visitor do
9
9
 
10
10
  let(:visitor) do
11
11
  v = GraphQL::Visitor.new
12
- v[GraphQL::Nodes::Field] << -> (node) { counts[:fields_entered] += 1 }
12
+ v[GraphQL::Nodes::Field] << -> (node, parent) { counts[:fields_entered] += 1 }
13
13
  # two ways to set up enter hooks:
14
- v[GraphQL::Nodes::Argument] << -> (node) { counts[:argument_names] << node.name }
15
- v[GraphQL::Nodes::Argument].enter << -> (node) { counts[:arguments_entered] += 1}
16
- v[GraphQL::Nodes::Argument].leave << -> (node) { counts[:arguments_left] += 1 }
14
+ v[GraphQL::Nodes::Argument] << -> (node, parent) { counts[:argument_names] << node.name }
15
+ v[GraphQL::Nodes::Argument].enter << -> (node, parent) { counts[:arguments_entered] += 1}
16
+ v[GraphQL::Nodes::Argument].leave << -> (node, parent) { counts[:arguments_left] += 1 }
17
17
 
18
- v[GraphQL::Nodes::Document].leave << -> (node) { counts[:finished] = true }
18
+ v[GraphQL::Nodes::Document].leave << -> (node, parent) { counts[:finished] = true }
19
19
  v
20
20
  end
21
21
 
@@ -28,4 +28,12 @@ describe GraphQL::Visitor do
28
28
  assert_equal(["id", "first"], counts[:argument_names])
29
29
  assert(counts[:finished])
30
30
  end
31
+
32
+ describe 'Visitor::SKIP' do
33
+ it 'skips the rest of the node' do
34
+ visitor[GraphQL::Nodes::Document] << -> (node, parent) { GraphQL::Visitor::SKIP }
35
+ visitor.visit(document)
36
+ assert_equal(0, counts[:fields_entered])
37
+ end
38
+ end
31
39
  end
@@ -4,14 +4,14 @@ describe GraphQL::Query do
4
4
  describe '#execute' do
5
5
  let(:query_string) { %|
6
6
  query getFlavor($cheeseId: Int!) {
7
- brie: cheese(id: 1) { ...cheeseFields, ... meatFields, taste: flavor },
7
+ brie: cheese(id: 1) { ...cheeseFields, ... milkFields, taste: flavor },
8
8
  cheese(id: $cheeseId) {
9
9
  __typename,
10
10
  id,
11
11
  ...cheeseFields,
12
12
  ... edibleFields,
13
13
  ... on Cheese { cheeseKind: flavor },
14
- ... on Meat { cut }
14
+ ... on Milk { source }
15
15
  }
16
16
  fromSource(source: COW) { id }
17
17
  firstSheep: searchDairy(product: {source: SHEEP}) { ... dairyFields }
@@ -19,27 +19,29 @@ describe GraphQL::Query do
19
19
  }
20
20
  fragment cheeseFields on Cheese { flavor }
21
21
  fragment edibleFields on Edible { fatContent }
22
- fragment meatFields on Meat { cut }
23
- fragment dairyFields on DairyProduct {
22
+ fragment milkFields on Milk { source }
23
+ fragment dairyFields on AnimalProduct {
24
24
  ... on Cheese { flavor }
25
25
  ... on Milk { source }
26
26
  }
27
27
  |}
28
- let(:query) { GraphQL::Query.new(DummySchema, query_string, context: {}, params: {"cheeseId" => 2})}
28
+ let(:debug) { false }
29
+ let(:query) { GraphQL::Query.new(DummySchema, query_string, context: {}, params: {"cheeseId" => 2}, debug: debug)}
29
30
  let(:result) { query.result }
31
+
30
32
  it 'returns fields on objects' do
31
33
  expected = {"data"=> { "getFlavor" => {
32
34
  "brie" => { "flavor" => "Brie", "taste" => "Brie" },
33
35
  "cheese" => {
34
36
  "__typename" => "Cheese",
35
37
  "id" => 2,
36
- "fatContent" => 0.3,
37
38
  "flavor" => "Gouda",
39
+ "fatContent" => 0.3,
38
40
  "cheeseKind" => "Gouda",
39
41
  },
40
42
  "fromSource" => [{ "id" => 1 }, {"id" => 2}],
41
43
  "firstSheep" => { "flavor" => "Manchego" },
42
- "favoriteEdible"=>{"__typename"=>"Edible", "fatContent"=>0.04},
44
+ "favoriteEdible"=>{"__typename"=>"Milk", "fatContent"=>0.04},
43
45
  }}}
44
46
  assert_equal(expected, result)
45
47
  end
@@ -49,12 +51,22 @@ describe GraphQL::Query do
49
51
  end
50
52
 
51
53
  describe 'runtime errors' do
52
- let(:query_string) {%| query noMilk { milk(id: 1000) { name } }|}
53
- it 'turns into error messages' do
54
- expected = {"errors"=>[
55
- {"message"=>"Something went wrong during query execution: No field found on Query 'Query' for 'milk'"}
56
- ]}
57
- assert_equal(expected, result)
54
+ let(:query_string) {%| query noMilk { error }|}
55
+ describe 'if debug: false' do
56
+ let(:debug) { false }
57
+ it 'turns into error messages' do
58
+ expected = {"errors"=>[
59
+ {"message"=>"Something went wrong during query execution: This error was raised on purpose"}
60
+ ]}
61
+ assert_equal(expected, result)
62
+ end
63
+ end
64
+
65
+ describe 'if debug: true' do
66
+ let(:debug) { true }
67
+ it 'raises error' do
68
+ assert_raises(RuntimeError) { result }
69
+ end
58
70
  end
59
71
  end
60
72
 
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Schema::FieldValidator do
4
+ let(:field_defn) {{
5
+ name: "Field",
6
+ description: "Invalid field",
7
+ deprecation_reason: nil,
8
+ arguments: {symbol_arg: nil},
9
+ type: DairyAnimalEnum,
10
+ }}
11
+ let(:field) {
12
+ f = OpenStruct.new(field_defn)
13
+ def f.to_s; f.name; end
14
+ f
15
+ }
16
+ let(:errors) { e = []; GraphQL::Schema::FieldValidator.new.validate(field, e); e }
17
+ it "requires argument names to be strings" do
18
+ expected = ["Field.arguments keys must be Strings, but some aren't: symbol_arg"]
19
+ assert_equal(expected, errors)
20
+ end
21
+ end
@@ -9,9 +9,9 @@ describe GraphQL::Schema::TypeReducer do
9
9
  "String" => GraphQL::STRING_TYPE,
10
10
  "DairyAnimal" => DairyAnimalEnum,
11
11
  "Float" => GraphQL::FLOAT_TYPE,
12
- "Edible" => Edible,
12
+ "Edible" => EdibleInterface,
13
13
  "Milk" => MilkType,
14
- "AnimalProduct" => AnimalProduct,
14
+ "AnimalProduct" => AnimalProductInterface,
15
15
  }
16
16
  assert_equal(expected.keys, reducer.result.keys)
17
17
  assert_equal(expected, reducer.result)
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Schema::TypeValidator do
4
+ let(:base_type_defn) {
5
+ {
6
+ name: "InvalidType",
7
+ description: "...",
8
+ deprecation_reason: nil,
9
+ kind: GraphQL::TypeKinds::OBJECT,
10
+ interfaces: [],
11
+ fields: {},
12
+ }
13
+ }
14
+ let(:object) {
15
+ o = OpenStruct.new(type_defn)
16
+ def o.to_s; "InvalidType"; end
17
+ o
18
+ }
19
+ let(:validator) { GraphQL::Schema::TypeValidator.new }
20
+ let(:errors) { e = []; validator.validate(object, e); e;}
21
+ describe 'when name isnt defined' do
22
+ let(:type_defn) { base_type_defn.delete_if {|k,v| k == :name }}
23
+ it 'requires name' do
24
+ assert_equal(
25
+ ["InvalidType must respond to #name() to be a Type"],
26
+ errors
27
+ )
28
+ end
29
+ end
30
+
31
+ describe "when a field name isnt a string" do
32
+ let(:type_defn) { base_type_defn.merge(fields: {symbol_field: (GraphQL::Field.new {|f|}) }) }
33
+ it "requires string names" do
34
+ assert_equal(
35
+ ["InvalidType.fields keys must be Strings, but some aren't: symbol_field"],
36
+ errors
37
+ )
38
+ end
39
+ end
40
+
41
+ describe "when a Union isnt valid" do
42
+ let(:object) {
43
+ GraphQL::Union.new("Something", "some union", [DairyProductInputType])
44
+ }
45
+ let(:errors) { e = []; GraphQL::Schema::TypeValidator.new.validate(object, e); e;}
46
+ it 'must be 2+ types, must be only object types' do
47
+ expected = [
48
+ "Something.possible_types must be objects, but some aren't: <GraphQL::InputObjectType DairyProductInput>",
49
+ "Union Something must be defined with 2 or more types, not 1",
50
+ ]
51
+ assert_equal(expected, errors)
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::StaticValidation::ArgumentLiteralsAreCompatible do
4
+ let(:document) { GraphQL.parse(%|
5
+ query getCheese {
6
+ cheese(id: "aasdlkfj") { source }
7
+ cheese(id: 1) { source @skip(if: {id: 1})}
8
+ yakSource: searchDairy(product: {source: YAK, fatContent: 1.1}) { source }
9
+ badSource: searchDairy(product: {source: 1.1}) { source }
10
+ }
11
+
12
+ fragment cheeseFields on Cheese {
13
+ similarCheeses(source: 4.5)
14
+ }
15
+ |)}
16
+
17
+ let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, validators: [GraphQL::StaticValidation::ArgumentLiteralsAreCompatible]) }
18
+ let(:errors) { validator.validate(document) }
19
+
20
+ it 'finds undefined arguments to fields and directives' do
21
+ assert_equal(3, errors.length)
22
+
23
+ query_root_error = {
24
+ "message"=>"id on Field 'cheese' has an invalid value",
25
+ "locations"=>[{"line"=>3, "column"=>7}]
26
+ }
27
+ assert_includes(errors, query_root_error)
28
+
29
+ input_object_error = {
30
+ "message"=>"product on Field 'searchDairy' has an invalid value",
31
+ "locations"=>[{"line"=>6, "column"=>7}]
32
+ }
33
+ assert_includes(errors, input_object_error)
34
+
35
+ fragment_error = {
36
+ "message"=>"source on Field 'similarCheeses' has an invalid value",
37
+ "locations"=>[{"line"=>10, "column"=>7}]
38
+ }
39
+ assert_includes(errors, fragment_error)
40
+ end
41
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::StaticValidation::ArgumentsAreDefined do
4
+ let(:document) { GraphQL.parse("
5
+ query getCheese {
6
+ cheese(id: 1) { source }
7
+ cheese(silly: false) { source }
8
+ }
9
+
10
+ fragment cheeseFields on Cheese {
11
+ similarCheeses(source: SHEEP, nonsense: 1)
12
+ id @skip(something: 3.4)
13
+ }
14
+ ")}
15
+
16
+ let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, validators: [GraphQL::StaticValidation::ArgumentsAreDefined]) }
17
+ let(:errors) { validator.validate(document) }
18
+
19
+ it 'finds undefined arguments to fields and directives' do
20
+ assert_equal(3, errors.length)
21
+
22
+ query_root_error = {
23
+ "message"=>"Field 'cheese' doesn't accept argument silly",
24
+ "locations"=>[{"line"=>4, "column"=>7}]
25
+ }
26
+ assert_includes(errors, query_root_error)
27
+
28
+ fragment_error = {
29
+ "message"=>"Field 'similarCheeses' doesn't accept argument nonsense",
30
+ "locations"=>[{"line"=>8, "column"=>7}]
31
+ }
32
+ assert_includes(errors, fragment_error)
33
+
34
+ directive_error = {
35
+ "message"=>"Directive 'skip' doesn't accept argument something",
36
+ "locations"=>[{"line"=>9, "column"=>11}]
37
+ }
38
+ assert_includes(errors, directive_error)
39
+ end
40
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::StaticValidation::DirectivesAreDefined do
4
+ let(:document) { GraphQL.parse("
5
+ query getCheese {
6
+ okCheese: cheese(id: 1) {
7
+ id @skip(if: true),
8
+ source @nonsense(if: false)
9
+ ... on Cheese {
10
+ flavor @moreNonsense
11
+ }
12
+ }
13
+ }
14
+ ")}
15
+
16
+ let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, validators: [GraphQL::StaticValidation::DirectivesAreDefined]) }
17
+ let(:errors) { validator.validate(document) }
18
+
19
+ describe 'non-existent directives' do
20
+ it 'makes errors for them' do
21
+ expected = [
22
+ {
23
+ "message"=>"Directive @nonsense is not defined",
24
+ "locations"=>[{"line"=>5, "column"=>17}]
25
+ }, {
26
+ "message"=>"Directive @moreNonsense is not defined",
27
+ "locations"=>[{"line"=>7, "column"=>19}]
28
+ }
29
+ ]
30
+ assert_equal(expected, errors)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::StaticValidation::FieldsAreDefinedOnType do
4
+ let(:query_string) { "
5
+ query getCheese($sourceVar: DairyAnimal!) {
6
+ notDefinedField { name }
7
+ cheese(id: 1) { nonsenseField, flavor }
8
+ fromSource(source: COW) { bogusField }
9
+ }
10
+
11
+ fragment cheeseFields on Cheese { fatContent, hogwashField }
12
+ "}
13
+
14
+ let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, validators: [GraphQL::StaticValidation::FieldsAreDefinedOnType]) }
15
+ let(:errors) { validator.validate(GraphQL.parse(query_string)) }
16
+ let(:error_messages) { errors.map { |e| e["message" ] }}
17
+
18
+ it "finds fields that are requested on types that don't have that field" do
19
+ expected_errors = [
20
+ "Field 'notDefinedField' doesn't exist on type 'Query'", # from query root
21
+ "Field 'nonsenseField' doesn't exist on type 'Cheese'", # from another field
22
+ "Field 'bogusField' doesn't exist on type 'Cheese'", # from a list
23
+ "Field 'hogwashField' doesn't exist on type 'Cheese'", # from a fragment
24
+ ]
25
+ assert_equal(expected_errors, error_messages)
26
+ end
27
+
28
+ describe 'on interfaces' do
29
+ let(:query_string) { "query getStuff { favoriteEdible { amountThatILikeIt } }"}
30
+
31
+ it 'finds invalid fields' do
32
+ expected_errors = [
33
+ {"message"=>"Field 'amountThatILikeIt' doesn't exist on type 'Edible'", "locations"=>[{"line"=>1, "column"=>18}]}
34
+ ]
35
+ assert_equal(expected_errors, errors)
36
+ end
37
+ end
38
+
39
+ describe 'on unions' do
40
+ let(:query_string) { "
41
+ query notOnUnion { favoriteEdible { ...dpFields } }
42
+ fragment dbFields on DairyProduct { source }
43
+ fragment dbIndirectFields on DairyProduct { ... on Cheese { source } }
44
+ "}
45
+
46
+
47
+ it 'doesnt allow selections on unions' do
48
+ expected_errors = [
49
+ {
50
+ "message"=>"Selections can't be made directly on unions (see selections on DairyProduct)",
51
+ "locations"=>[
52
+ {"line"=>3, "column"=>7}
53
+ ]
54
+ }
55
+ ]
56
+ assert_equal(expected_errors, errors)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::StaticValidation::FieldsHaveAppropriateSelections do
4
+ let(:document) { GraphQL.parse("
5
+ query getCheese {
6
+ okCheese: cheese(id: 1) { fatContent, similarCheeses(source: YAK) { source } }
7
+ missingFieldsCheese: cheese(id: 1)
8
+ illegalSelectionCheese: cheese(id: 1) { id { something, ... someFields } }
9
+ }
10
+ ")}
11
+
12
+ let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, validators: [GraphQL::StaticValidation::FieldsHaveAppropriateSelections]) }
13
+ let(:errors) { validator.validate(document) }
14
+
15
+ it 'adds errors for selections on scalars' do
16
+ assert_equal(2, errors.length)
17
+
18
+ illegal_selection_error = {
19
+ "message"=>"Selections can't be made on scalars (field 'id' returns Int but has selections [something, someFields])",
20
+ "locations"=>[{"line"=>5, "column"=>47}]
21
+ }
22
+ assert_includes(errors, illegal_selection_error, 'finds illegal selections on scalarss')
23
+
24
+ selection_required_error = {
25
+ "message"=>"Objects must have selections (field 'cheese' returns Cheese but has no selections)",
26
+ "locations"=>[{"line"=>4, "column"=>7}]
27
+ }
28
+ assert_includes(errors, selection_required_error, 'finds objects without selections')
29
+ end
30
+ end
@@ -1,40 +1,47 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe GraphQL::Validations::FieldsWillMerge do
3
+ describe GraphQL::StaticValidation::FieldsWillMerge do
4
4
  let(:document) { GraphQL.parse("
5
5
  query getCheese($sourceVar: DairyAnimal!) {
6
- id
7
- nickname: name,
8
- nickname: fatContent,
9
- fatContent
6
+ cheese(id: 1) {
7
+ id,
8
+ nickname: name,
9
+ nickname: fatContent,
10
+ fatContent
11
+ differentLevel: fatContent
12
+ similarCheeses(source: $sourceVar)
10
13
 
11
- similarCheeses(source: $sourceVar)
12
-
13
- origin { originName: id },
14
- ...cheeseFields
15
- ... on Cheese {
16
- fatContent: name
17
- similarCheeses(source: SHEEP)
14
+ similarCow: similarCheeses(source: COW) {
15
+ similarCowSource: source,
16
+ differentLevel: fatContent
17
+ }
18
+ ...cheeseFields
19
+ ... on Cheese {
20
+ fatContent: name
21
+ similarCheeses(source: SHEEP)
22
+ }
18
23
  }
19
24
  }
20
25
  fragment cheeseFields on Cheese {
21
26
  fatContent,
22
- origin { originName: name }
27
+ similarCow: similarCheeses(source: COW) { similarCowSource: id, id }
23
28
  id @someFlag
24
29
  }
25
30
  ")}
26
31
 
27
- let(:validator) { GraphQL::Validator.new(schema: nil, validators: [GraphQL::Validations::FieldsWillMerge]) }
32
+ let(:validator) { GraphQL::StaticValidation::Validator.new(schema: DummySchema, validators: [GraphQL::StaticValidation::FieldsWillMerge]) }
28
33
  let(:errors) { validator.validate(document) }
34
+ let(:error_messages) { errors.map { |e| e["message" ] }}
35
+
29
36
  it 'finds field naming conflicts' do
30
37
  expected_errors = [
31
- "Field 'nickname' has a field conflict: name or fatContent?", # alias conflict in query
32
- "Field 'originName' has a field conflict: id or name?", # nested conflict
33
38
  "Field 'id' has a directive conflict: [] or [someFlag]?", # different directives
34
39
  "Field 'id' has a directive argument conflict: [] or [{}]?", # not sure this is a great way to handle it but here we are!
40
+ "Field 'nickname' has a field conflict: name or fatContent?", # alias conflict in query
35
41
  "Field 'fatContent' has a field conflict: fatContent or name?", # alias/name conflict in query and fragment
36
42
  "Field 'similarCheeses' has an argument conflict: {\"source\":\"sourceVar\"} or {\"source\":\"SHEEP\"}?", # different arguments
43
+ "Field 'similarCowSource' has a field conflict: source or id?", # nested conflict
37
44
  ]
38
- assert_equal(expected_errors, errors)
45
+ assert_equal(expected_errors, error_messages)
39
46
  end
40
47
  end