graphql 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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