Fix singleFieldSubscriptionsRule fragment lookup

singleFieldSubscriptionsRule picks up a wrong fragment definition.
This commit is contained in:
Eugen Wissner 2020-11-06 08:33:51 +01:00
parent 7f0fb18716
commit 4a3b4cb16d
2 changed files with 52 additions and 17 deletions

View File

@ -50,6 +50,7 @@ import Data.HashSet (HashSet)
import qualified Data.HashSet as HashSet import qualified Data.HashSet as HashSet
import Data.List (groupBy, sortBy, sortOn) import Data.List (groupBy, sortBy, sortOn)
import Data.Maybe (isNothing, mapMaybe) import Data.Maybe (isNothing, mapMaybe)
import Data.List.NonEmpty (NonEmpty)
import Data.Ord (comparing) import Data.Ord (comparing)
import Data.Sequence (Seq(..), (|>)) import Data.Sequence (Seq(..), (|>))
import qualified Data.Sequence as Seq import qualified Data.Sequence as Seq
@ -127,10 +128,10 @@ singleFieldSubscriptionsRule = OperationDefinitionRule $ \case
1 -> lift mempty 1 -> lift mempty
_ _
| Just name <- name' -> pure $ Error | Just name <- name' -> pure $ Error
{ message = unwords { message = concat
[ "Subscription" [ "Subscription \""
, Text.unpack name , Text.unpack name
, "must select only one top level field." , "\" must select only one top level field."
] ]
, locations = [location'] , locations = [location']
} }
@ -172,10 +173,6 @@ singleFieldSubscriptionsRule = OperationDefinitionRule $ \case
skip (Full.Directive "include" [Full.Argument "if" (Full.Node argumentValue _) _] _) = skip (Full.Directive "include" [Full.Argument "if" (Full.Node argumentValue _) _] _) =
Full.Boolean False == argumentValue Full.Boolean False == argumentValue
skip _ = False skip _ = False
findFragmentDefinition (Full.ExecutableDefinition executableDefinition) Nothing
| Full.DefinitionFragment fragmentDefinition <- executableDefinition =
Just fragmentDefinition
findFragmentDefinition _ accumulator = accumulator
collectFromFragment typeCondition selectionSet accumulator = do collectFromFragment typeCondition selectionSet accumulator = do
types' <- lift $ asks $ Schema.types . schema types' <- lift $ asks $ Schema.types . schema
schema' <- lift $ asks schema schema' <- lift $ asks schema
@ -189,7 +186,7 @@ singleFieldSubscriptionsRule = OperationDefinitionRule $ \case
collectFromSpread fragmentName accumulator = do collectFromSpread fragmentName accumulator = do
modify $ HashSet.insert fragmentName modify $ HashSet.insert fragmentName
ast' <- lift $ asks ast ast' <- lift $ asks ast
case foldr findFragmentDefinition Nothing ast' of case findFragmentDefinition fragmentName ast' of
Nothing -> pure accumulator Nothing -> pure accumulator
Just (Full.FragmentDefinition _ typeCondition _ selectionSet _) -> Just (Full.FragmentDefinition _ typeCondition _ selectionSet _) ->
collectFromFragment typeCondition selectionSet accumulator collectFromFragment typeCondition selectionSet accumulator
@ -493,18 +490,24 @@ noFragmentCyclesRule = FragmentDefinitionRule $ \case
(accumulator <>) <$> collectFields selections (accumulator <>) <$> collectFields selections
forField accumulator (Full.Field _ _ _ _ selections _) = forField accumulator (Full.Field _ _ _ _ selections _) =
(accumulator <>) <$> collectFields selections (accumulator <>) <$> collectFields selections
findFragmentDefinition n (Full.ExecutableDefinition executableDefinition) Nothing collectFromSpread fragmentName accumulator = do
| Full.DefinitionFragment fragmentDefinition <- executableDefinition
, Full.FragmentDefinition fragmentName _ _ _ _ <- fragmentDefinition
, fragmentName == n = Just fragmentDefinition
findFragmentDefinition _ _ accumulator = accumulator
collectFromSpread _fragmentName accumulator = do
ast' <- lift $ asks ast ast' <- lift $ asks ast
case foldr (findFragmentDefinition _fragmentName) Nothing ast' of case findFragmentDefinition fragmentName ast' of
Nothing -> pure accumulator Nothing -> pure accumulator
Just (Full.FragmentDefinition _ _ _ selections _) -> Just (Full.FragmentDefinition _ _ _ selections _) ->
(accumulator <>) <$> collectFields selections (accumulator <>) <$> collectFields selections
findFragmentDefinition :: Text
-> NonEmpty Full.Definition
-> Maybe Full.FragmentDefinition
findFragmentDefinition fragmentName = foldr compareDefinition Nothing
where
compareDefinition (Full.ExecutableDefinition executableDefinition) Nothing
| Full.DefinitionFragment fragmentDefinition <- executableDefinition
, Full.FragmentDefinition anotherName _ _ _ _ <- fragmentDefinition
, anotherName == fragmentName = Just fragmentDefinition
compareDefinition _ accumulator = accumulator
-- | Fields and directives treat arguments as a mapping of argument name to -- | Fields and directives treat arguments as a mapping of argument name to
-- value. More than one argument with the same name in an argument set is -- value. More than one argument with the same name in an argument set is
-- ambiguous and invalid. -- ambiguous and invalid.

View File

@ -92,6 +92,7 @@ petType = InterfaceType "Pet" Nothing []
subscriptionType :: ObjectType IO subscriptionType :: ObjectType IO
subscriptionType = ObjectType "Subscription" Nothing [] $ HashMap.fromList subscriptionType = ObjectType "Subscription" Nothing [] $ HashMap.fromList
[ ("newMessage", newMessageResolver) [ ("newMessage", newMessageResolver)
, ("disallowedSecondRootField", newMessageResolver)
] ]
where where
newMessageField = Field Nothing (Out.NonNullObjectType messageType) mempty newMessageField = Field Nothing (Out.NonNullObjectType messageType) mempty
@ -165,7 +166,8 @@ spec =
|] |]
expected = Error expected = Error
{ message = { message =
"Subscription sub must select only one top level field." "Subscription \"sub\" must select only one top level \
\field."
, locations = [AST.Location 2 15] , locations = [AST.Location 2 15]
} }
in validate queryString `shouldContain` [expected] in validate queryString `shouldContain` [expected]
@ -186,7 +188,8 @@ spec =
|] |]
expected = Error expected = Error
{ message = { message =
"Subscription sub must select only one top level field." "Subscription \"sub\" must select only one top level \
\field."
, locations = [AST.Location 2 15] , locations = [AST.Location 2 15]
} }
in validate queryString `shouldContain` [expected] in validate queryString `shouldContain` [expected]
@ -631,3 +634,32 @@ spec =
, locations = [AST.Location 3 34] , locations = [AST.Location 3 34]
} }
in validate queryString `shouldBe` [expected] in validate queryString `shouldBe` [expected]
it "finds corresponding subscription fragment" $
let queryString = [r|
subscription sub {
...anotherSubscription
...multipleSubscriptions
}
fragment multipleSubscriptions on Subscription {
newMessage {
body
}
disallowedSecondRootField {
sender
}
}
fragment anotherSubscription on Subscription {
newMessage {
body
sender
}
}
|]
expected = Error
{ message =
"Subscription \"sub\" must select only one top level \
\field."
, locations = [AST.Location 2 15]
}
in validate queryString `shouldBe` [expected]