Skip to content

Commit

Permalink
UniqueFragmentNames and UniqueOperationNames rules. closes #40
Browse files Browse the repository at this point in the history
  • Loading branch information
OlegIlyenko committed Aug 7, 2015
1 parent cf23a34 commit f568967
Show file tree
Hide file tree
Showing 5 changed files with 252 additions and 0 deletions.
8 changes: 8 additions & 0 deletions src/main/scala/sangria/validation/Violation.scala
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,11 @@ case class FieldsConflictViolation(outputName: String, reason: Either[String, Ve
case class AnonOperationNotAloneViolation(sourceMapper: Option[SourceMapper], positions: List[Position]) extends AstNodeViolation {
lazy val errorMessage = s"This anonymous operation must be the only defined operation.$astLocation"
}

case class DuplicateFragmentNameViolation(fragName: String, sourceMapper: Option[SourceMapper], positions: List[Position]) extends AstNodeViolation {
lazy val errorMessage = s"There can only be one fragment named '$fragName'.$astLocation"
}

case class DuplicateOperationNameViolation(opName: String, sourceMapper: Option[SourceMapper], positions: List[Position]) extends AstNodeViolation {
lazy val errorMessage = s"There can only be one operation named '$opName'.$astLocation"
}
28 changes: 28 additions & 0 deletions src/main/scala/sangria/validation/rules/UniqueFragmentNames.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package sangria.validation.rules

import sangria.ast
import sangria.ast.AstVisitorCommand._
import sangria.validation._

import scala.collection.mutable.{Set => MutableSet}

/**
* Unique fragment names
*
* A GraphQL document is only valid if all defined fragments have unique names.
*/
class UniqueFragmentNames extends ValidationRule {
override def visitor(ctx: ValidationContext) = new AstValidatingVisitor {
val knownFragmentNames = MutableSet[String]()

override val onEnter: ValidationVisit = {
case fragDef: ast.FragmentDefinition =>
if (knownFragmentNames contains fragDef.name)
Left(Vector(DuplicateFragmentNameViolation(fragDef.name, ctx.sourceMapper, fragDef.position.toList)))
else {
knownFragmentNames += fragDef.name
Right(Continue)
}
}
}
}
28 changes: 28 additions & 0 deletions src/main/scala/sangria/validation/rules/UniqueOperationNames.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package sangria.validation.rules

import sangria.ast
import sangria.ast.AstVisitorCommand._
import sangria.validation._

import scala.collection.mutable.{Set => MutableSet}

/**
* Unique operation names
*
* A GraphQL document is only valid if all defined operations have unique names.
*/
class UniqueOperationNames extends ValidationRule {
override def visitor(ctx: ValidationContext) = new AstValidatingVisitor {
val knownOpNames = MutableSet[String]()

override val onEnter: ValidationVisit = {
case ast.OperationDefinition(_, Some(name), _, _, _, pos) =>
if (knownOpNames contains name)
Left(Vector(DuplicateOperationNameViolation(name, ctx.sourceMapper, pos.toList)))
else {
knownOpNames += name
Right(Continue)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package sangria.validation.rules

import org.scalatest.WordSpec
import sangria.util.{Pos, ValidationSupport}

class UniqueFragmentNamesSpec extends WordSpec with ValidationSupport {

override val defaultRule = Some(new UniqueFragmentNames)

"Validate: Unique fragment names" should {
"no fragments" in expectPasses(
"""
{
field
}
""")

"one fragment" in expectPasses(
"""
{
...fragA
}
fragment fragA on Type {
field
}
""")

"many fragments" in expectPasses(
"""
{
...fragA
...fragB
...fragC
}
fragment fragA on Type {
fieldA
}
fragment fragB on Type {
fieldB
}
fragment fragC on Type {
fieldC
}
""")

"inline fragments are always unique" in expectPasses(
"""
{
...on Type {
fieldA
}
...on Type {
fieldB
}
}
""")

"fragment and operation named the same" in expectPasses(
"""
query Foo {
...Foo
}
fragment Foo on Type {
field
}
""")

"fragments named the same" in expectFails(
"""
{
...fragA
}
fragment fragA on Type {
fieldA
}
fragment fragA on Type {
fieldB
}
""",
List(
"There can only be one fragment named 'fragA'." -> Some(Pos(8, 9))
))

"fragments named the same without being referenced" in expectFails(
"""
fragment fragA on Type {
fieldA
}
fragment fragA on Type {
fieldB
}
""",
List(
"There can only be one fragment named 'fragA'." -> Some(Pos(5, 9))
))
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package sangria.validation.rules

import org.scalatest.WordSpec
import sangria.util.{Pos, ValidationSupport}

class UniqueOperationNamesSpec extends WordSpec with ValidationSupport {

override val defaultRule = Some(new UniqueOperationNames)

"Validate: Unique operation names" should {
"no operations" in expectPasses(
"""
fragment fragA on Type {
field
}
""")

"one anon operation" in expectPasses(
"""
{
field
}
""")

"one named operation" in expectPasses(
"""
query Foo {
field
}
""")

"multiple operations" in expectPasses(
"""
query Foo {
field
}
query Bar {
field
}
""")

"multiple operations of different types" in expectPasses(
"""
query Foo {
field
}
mutation Bar {
field
}
""")

"fragment and operation named the same" in expectPasses(
"""
query Foo {
...Foo
}
fragment Foo on Type {
field
}
""")

"multiple operations of same name" in expectFails(
"""
query Foo {
fieldA
}
query Foo {
fieldB
}
""",
List(
"There can only be one operation named 'Foo'." -> Some(Pos(5, 9))
))

"multiple operations of same name of different types" in expectFails(
"""
query Foo {
fieldA
}
mutation Foo {
fieldB
}
""",
List(
"There can only be one operation named 'Foo'." -> Some(Pos(5, 9))
))
}
}

0 comments on commit f568967

Please sign in to comment.