Skip to content

Commit 2442e6f

Browse files
committed
Add fragments on composite types validation rule
1 parent 35642be commit 2442e6f

File tree

7 files changed

+189
-1
lines changed

7 files changed

+189
-1
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ Console.Writeline(result);
147147
- [x] Arguments of correct type
148148
- [x] Default values of correct type
149149
- [x] Fields on correct type
150-
- [ ] Fragments on composite type
150+
- [x] Fragments on composite types
151151
- [x] Known argument names
152152
- [x] Known directives
153153
- [x] Known fragment names

src/GraphQL.Tests/GraphQL.Tests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@
7979
<Compile Include="Types\QueryArgumentTests.cs" />
8080
<Compile Include="Utilities\AstPrinterTests.cs" />
8181
<Compile Include="Validation\FieldsOnCorrectTypeTests.cs" />
82+
<Compile Include="Validation\FragmentsOnCompositeTypesTests.cs" />
8283
<Compile Include="Validation\KnownDirectivesTests.cs" />
8384
<Compile Include="Validation\NoUnusedFragmentsTests.cs" />
8485
<Compile Include="Utilities\SchemaPrinterTests.cs" />
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
using GraphQL.Validation.Rules;
2+
3+
namespace GraphQL.Tests.Validation
4+
{
5+
public class FragmentsOnCompositeTypesTests : ValidationTestBase<FragmentsOnCompositeTypes, ValidationSchema>
6+
{
7+
[Fact]
8+
public void object_is_valid_fragment_type()
9+
{
10+
ShouldPassRule(@"
11+
fragment validFragment on Dog {
12+
barks
13+
}
14+
");
15+
}
16+
17+
[Fact]
18+
public void interface_is_valid_fragment_type()
19+
{
20+
ShouldPassRule(@"
21+
fragment validFragment on Pet {
22+
name
23+
}
24+
");
25+
}
26+
27+
[Fact]
28+
public void object_is_valid_inline_fragment_type()
29+
{
30+
ShouldPassRule(@"
31+
fragment validFragment on Pet {
32+
... on Dog {
33+
barks
34+
}
35+
}
36+
");
37+
}
38+
39+
[Fact]
40+
public void inline_fragment_without_type_is_valid()
41+
{
42+
ShouldPassRule(@"
43+
fragment validFragment on Pet {
44+
... {
45+
name
46+
}
47+
}
48+
");
49+
}
50+
51+
[Fact]
52+
public void union_is_valid_fragment_type()
53+
{
54+
ShouldPassRule(@"
55+
fragment validFragment on CatOrDog {
56+
__typename
57+
}
58+
");
59+
}
60+
61+
[Fact]
62+
public void scalar_is_invalid_fragment_type()
63+
{
64+
ShouldFailRule(_ =>
65+
{
66+
_.Query = @"
67+
fragment scalarFragment on Boolean {
68+
bad
69+
}
70+
";
71+
error(_, "scalarFragment", "Boolean", 2, 46);
72+
});
73+
}
74+
75+
[Fact]
76+
public void enum_is_invalid_fragment_type()
77+
{
78+
ShouldFailRule(_ =>
79+
{
80+
_.Query = @"
81+
fragment scalarFragment on FurColor {
82+
bad
83+
}
84+
";
85+
error(_, "scalarFragment", "FurColor", 2, 46);
86+
});
87+
}
88+
89+
[Fact]
90+
public void input_object_is_invalid_fragment_type()
91+
{
92+
ShouldFailRule(_ =>
93+
{
94+
_.Query = @"
95+
fragment inputFragment on ComplexInput {
96+
stringField
97+
}
98+
";
99+
error(_, "inputFragment", "ComplexInput", 2, 45);
100+
});
101+
}
102+
103+
[Fact]
104+
public void scalar_is_invalid_inline_fragment_type()
105+
{
106+
ShouldFailRule(_ =>
107+
{
108+
_.Query = @"
109+
fragment invalidFragment on Pet {
110+
... on String {
111+
barks
112+
}
113+
}
114+
";
115+
_.Error(Rule.InlineFragmentOnNonCompositeErrorMessage("String"), 3, 28);
116+
});
117+
}
118+
119+
private void error(ValidationTestConfig _, string fragName, string typeName, int line, int column)
120+
{
121+
_.Error(Rule.FragmentOnNonCompositeErrorMessage(fragName, typeName), line, column);
122+
}
123+
}
124+
}

src/GraphQL/GraphQL.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@
161161
<Compile Include="Validation\Rules\ArgumentsOfCorrectType.cs" />
162162
<Compile Include="Validation\Rules\DefaultValuesOfCorrectType.cs" />
163163
<Compile Include="Validation\Rules\FieldsOnCorrectType.cs" />
164+
<Compile Include="Validation\Rules\FragmentsOnCompositeTypes.cs" />
164165
<Compile Include="Validation\Rules\KnownArgumentNames.cs" />
165166
<Compile Include="Validation\Rules\KnownFragmentNames.cs" />
166167
<Compile Include="Validation\Rules\KnownDirectives.cs" />

src/GraphQL/GraphQLExtensions.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ public static string TrimGraphQLTypes(this string name)
1616
return Regex.Replace(name, "[\\[!\\]]", "").Trim();
1717
}
1818

19+
public static bool IsCompositeType(this GraphType type)
20+
{
21+
return type is ObjectGraphType ||
22+
type is InterfaceGraphType ||
23+
type is UnionGraphType;
24+
}
25+
1926
public static bool IsLeafType(this GraphType type, ISchema schema)
2027
{
2128
var namedType = type.GetNamedType(schema);

src/GraphQL/Validation/DocumentValidator.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public static List<IValidationRule> CoreRules()
5656
new UniqueOperationNames(),
5757
new LoneAnonymousOperation(),
5858
new KnownTypeNames(),
59+
new FragmentsOnCompositeTypes(),
5960
new VariablesAreInputTypes(),
6061
new ScalarLeafs(),
6162
new FieldsOnCorrectType(),
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
using GraphQL.Language;
2+
3+
namespace GraphQL.Validation.Rules
4+
{
5+
/// <summary>
6+
/// Fragments on composite type
7+
///
8+
/// Fragments use a type condition to determine if they apply, since fragments
9+
/// can only be spread into a composite type (object, interface, or union), the
10+
/// type condition must also be a composite type.
11+
/// </summary>
12+
public class FragmentsOnCompositeTypes : IValidationRule
13+
{
14+
public string InlineFragmentOnNonCompositeErrorMessage(string type)
15+
{
16+
return $"Fragment cannot condition on non composite type \"{type}\".";
17+
}
18+
19+
public string FragmentOnNonCompositeErrorMessage(string fragName, string type)
20+
{
21+
return $"Fragment \"{fragName}\" cannot condition on non composite type \"{type}\".";
22+
}
23+
24+
public INodeVisitor Validate(ValidationContext context)
25+
{
26+
return new EnterLeaveListener(_ =>
27+
{
28+
_.Match<InlineFragment>(node =>
29+
{
30+
var type = context.TypeInfo.GetLastType();
31+
if (node.Type != null && type != null && !type.IsCompositeType())
32+
{
33+
context.ReportError(new ValidationError(
34+
"5.4.1.3",
35+
InlineFragmentOnNonCompositeErrorMessage(context.Print(node.Type)),
36+
node.Type));
37+
}
38+
});
39+
40+
_.Match<FragmentDefinition>(node =>
41+
{
42+
var type = context.TypeInfo.GetLastType();
43+
if (type != null && !type.IsCompositeType())
44+
{
45+
context.ReportError(new ValidationError(
46+
"5.4.1.3",
47+
FragmentOnNonCompositeErrorMessage(node.Name, context.Print(node.Type)),
48+
node.Type));
49+
}
50+
});
51+
});
52+
}
53+
}
54+
}

0 commit comments

Comments
 (0)