parent
def52ddc20
commit
fc9ad9c4a1
@ -6,6 +6,9 @@ All notable changes to this project will be documented in this file.
|
|||||||
- Parsing multiple string arguments, such as
|
- Parsing multiple string arguments, such as
|
||||||
`login(username: "username", password: "password")` would fail on the comma
|
`login(username: "username", password: "password")` would fail on the comma
|
||||||
due to strings not having a space consumer.
|
due to strings not having a space consumer.
|
||||||
|
- Fragment spread is evaluated based on the `__typename` resolver. If the
|
||||||
|
resolver is missing, it is assumed that the type condition is satisfied (all
|
||||||
|
fragments are included).
|
||||||
|
|
||||||
## [0.6.0.0] - 2019-11-27
|
## [0.6.0.0] - 2019-11-27
|
||||||
### Changed
|
### Changed
|
||||||
|
@ -23,7 +23,7 @@ import qualified Language.GraphQL.Schema as Schema
|
|||||||
|
|
||||||
-- | Associates a fragment name with a list of 'Core.Field's.
|
-- | Associates a fragment name with a list of 'Core.Field's.
|
||||||
data Replacement = Replacement
|
data Replacement = Replacement
|
||||||
{ fragments :: HashMap Core.Name (Seq Core.Selection)
|
{ fragments :: HashMap Core.Name Core.Fragment
|
||||||
, fragmentDefinitions :: HashMap Full.Name Full.FragmentDefinition
|
, fragmentDefinitions :: HashMap Full.Name Full.FragmentDefinition
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,15 +65,8 @@ selection ::
|
|||||||
Full.Selection ->
|
Full.Selection ->
|
||||||
TransformT (Either (Seq Core.Selection) Core.Selection)
|
TransformT (Either (Seq Core.Selection) Core.Selection)
|
||||||
selection (Full.SelectionField fld) = Right . Core.SelectionField <$> field fld
|
selection (Full.SelectionField fld) = Right . Core.SelectionField <$> field fld
|
||||||
selection (Full.SelectionFragmentSpread (Full.FragmentSpread name _)) = do
|
selection (Full.SelectionFragmentSpread fragment) =
|
||||||
fragments' <- gets fragments
|
Right . Core.SelectionFragment <$> fragmentSpread fragment
|
||||||
Left <$> maybe lookupDefinition liftJust (HashMap.lookup name fragments')
|
|
||||||
where
|
|
||||||
lookupDefinition :: TransformT (Seq Core.Selection)
|
|
||||||
lookupDefinition = do
|
|
||||||
fragmentDefinitions' <- gets fragmentDefinitions
|
|
||||||
found <- lift . lift $ HashMap.lookup name fragmentDefinitions'
|
|
||||||
fragmentDefinition found
|
|
||||||
selection (Full.SelectionInlineFragment fragment)
|
selection (Full.SelectionInlineFragment fragment)
|
||||||
| (Full.InlineFragment (Just typeCondition) _ selectionSet) <- fragment
|
| (Full.InlineFragment (Just typeCondition) _ selectionSet) <- fragment
|
||||||
= Right
|
= Right
|
||||||
@ -94,12 +87,23 @@ collectFragments = do
|
|||||||
_ <- fragmentDefinition nextValue
|
_ <- fragmentDefinition nextValue
|
||||||
collectFragments
|
collectFragments
|
||||||
|
|
||||||
|
fragmentSpread :: Full.FragmentSpread -> TransformT Core.Fragment
|
||||||
|
fragmentSpread (Full.FragmentSpread name _) = do
|
||||||
|
fragments' <- gets fragments
|
||||||
|
maybe lookupDefinition liftJust (HashMap.lookup name fragments')
|
||||||
|
where
|
||||||
|
lookupDefinition = do
|
||||||
|
fragmentDefinitions' <- gets fragmentDefinitions
|
||||||
|
found <- lift . lift $ HashMap.lookup name fragmentDefinitions'
|
||||||
|
fragmentDefinition found
|
||||||
|
|
||||||
fragmentDefinition ::
|
fragmentDefinition ::
|
||||||
Full.FragmentDefinition ->
|
Full.FragmentDefinition ->
|
||||||
TransformT (Seq Core.Selection)
|
TransformT Core.Fragment
|
||||||
fragmentDefinition (Full.FragmentDefinition name _tc _dirs selections) = do
|
fragmentDefinition (Full.FragmentDefinition name typeCondition _dirs selections) = do
|
||||||
modify deleteFragmentDefinition
|
modify deleteFragmentDefinition
|
||||||
newValue <- appendSelection selections
|
fragmentSelection <- appendSelection selections
|
||||||
|
let newValue = Core.Fragment typeCondition fragmentSelection
|
||||||
modify $ insertFragment newValue
|
modify $ insertFragment newValue
|
||||||
liftJust newValue
|
liftJust newValue
|
||||||
where
|
where
|
||||||
|
@ -131,8 +131,8 @@ resolve resolvers = fmap (Aeson.toJSON . fold) . traverse tryResolvers
|
|||||||
tryResolvers (SelectionField fld@(Field _ name _ _))
|
tryResolvers (SelectionField fld@(Field _ name _ _))
|
||||||
= maybe (errmsg fld) (tryResolver fld) $ find (compareResolvers name) resolvers
|
= maybe (errmsg fld) (tryResolver fld) $ find (compareResolvers name) resolvers
|
||||||
tryResolvers (SelectionFragment (Fragment typeCondition selections')) = do
|
tryResolvers (SelectionFragment (Fragment typeCondition selections')) = do
|
||||||
that <- maybe (return "") resolveTypeName (find (compareResolvers "__typename") resolvers)
|
that <- traverse resolveTypeName (find (compareResolvers "__typename") resolvers)
|
||||||
if Aeson.String typeCondition == that
|
if maybe True (Aeson.String typeCondition ==) that
|
||||||
then fmap fold . traverse tryResolvers $ selections'
|
then fmap fold . traverse tryResolvers $ selections'
|
||||||
else return mempty
|
else return mempty
|
||||||
compareResolvers name (Resolver name' _) = name == name'
|
compareResolvers name (Resolver name' _) = name == name'
|
||||||
|
@ -48,7 +48,8 @@ hasErrors (Object object') = HashMap.member "errors" object'
|
|||||||
hasErrors _ = True
|
hasErrors _ = True
|
||||||
|
|
||||||
spec :: Spec
|
spec :: Spec
|
||||||
spec = describe "Inline fragment executor" $ do
|
spec = do
|
||||||
|
describe "Inline fragment executor" $ do
|
||||||
it "chooses the first selection if the type matches" $ do
|
it "chooses the first selection if the type matches" $ do
|
||||||
actual <- graphql (garment "Hat" :| []) inlineQuery
|
actual <- graphql (garment "Hat" :| []) inlineQuery
|
||||||
let expected = object
|
let expected = object
|
||||||
@ -103,7 +104,8 @@ spec = describe "Inline fragment executor" $ do
|
|||||||
actual <- graphql (size :| []) query
|
actual <- graphql (size :| []) query
|
||||||
actual `shouldNotSatisfy` hasErrors
|
actual `shouldNotSatisfy` hasErrors
|
||||||
|
|
||||||
it "evaluates nested fragments" $ do
|
describe "Fragment spread executor" $ do
|
||||||
|
it "evaluates fragment spreads" $ do
|
||||||
let query = [r|
|
let query = [r|
|
||||||
{
|
{
|
||||||
...circumferenceFragment
|
...circumferenceFragment
|
||||||
@ -112,10 +114,6 @@ spec = describe "Inline fragment executor" $ do
|
|||||||
fragment circumferenceFragment on Hat {
|
fragment circumferenceFragment on Hat {
|
||||||
circumference
|
circumference
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment hatFragment on Hat {
|
|
||||||
...circumferenceFragment
|
|
||||||
}
|
|
||||||
|]
|
|]
|
||||||
|
|
||||||
actual <- graphql (circumference :| []) query
|
actual <- graphql (circumference :| []) query
|
||||||
@ -126,11 +124,13 @@ spec = describe "Inline fragment executor" $ do
|
|||||||
]
|
]
|
||||||
in actual `shouldBe` expected
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
it "evaluates fragments defined in any order" $ do
|
it "evaluates nested fragments" $ do
|
||||||
let query = [r|
|
let query = [r|
|
||||||
{
|
{
|
||||||
|
garment {
|
||||||
...circumferenceFragment
|
...circumferenceFragment
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fragment circumferenceFragment on Hat {
|
fragment circumferenceFragment on Hat {
|
||||||
...hatFragment
|
...hatFragment
|
||||||
@ -141,15 +141,17 @@ spec = describe "Inline fragment executor" $ do
|
|||||||
}
|
}
|
||||||
|]
|
|]
|
||||||
|
|
||||||
actual <- graphql (circumference :| []) query
|
actual <- graphql (garment "Hat" :| []) query
|
||||||
let expected = object
|
let expected = object
|
||||||
[ "data" .= object
|
[ "data" .= object
|
||||||
|
[ "garment" .= object
|
||||||
[ "circumference" .= (60 :: Int)
|
[ "circumference" .= (60 :: Int)
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
]
|
||||||
in actual `shouldBe` expected
|
in actual `shouldBe` expected
|
||||||
|
|
||||||
it "rejects recursive" $ do
|
it "rejects recursive fragments" $ do
|
||||||
let query = [r|
|
let query = [r|
|
||||||
{
|
{
|
||||||
...circumferenceFragment
|
...circumferenceFragment
|
||||||
@ -162,3 +164,28 @@ spec = describe "Inline fragment executor" $ do
|
|||||||
|
|
||||||
actual <- graphql (circumference :| []) query
|
actual <- graphql (circumference :| []) query
|
||||||
actual `shouldSatisfy` hasErrors
|
actual `shouldSatisfy` hasErrors
|
||||||
|
|
||||||
|
it "considers type condition" $ do
|
||||||
|
let query = [r|
|
||||||
|
{
|
||||||
|
garment {
|
||||||
|
...circumferenceFragment
|
||||||
|
...sizeFragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fragment circumferenceFragment on Hat {
|
||||||
|
circumference
|
||||||
|
}
|
||||||
|
fragment sizeFragment on Shirt {
|
||||||
|
size
|
||||||
|
}
|
||||||
|
|]
|
||||||
|
expected = object
|
||||||
|
[ "data" .= object
|
||||||
|
[ "garment" .= object
|
||||||
|
[ "circumference" .= (60 :: Int)
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
actual <- graphql (garment "Hat" :| []) query
|
||||||
|
actual `shouldBe` expected
|
||||||
|
Loading…
Reference in New Issue
Block a user