Propagate Maybe in the transform tree

Since the transform tree can already find some errors, it may fail here
and there. Almost all functions return a Maybe to signalize an error.
Will be replaced with an Either of course.
This commit is contained in:
Eugen Wissner 2019-11-01 14:24:21 +01:00
parent 0e3b6184be
commit 417ff5da7d
1 changed files with 52 additions and 39 deletions

View File

@ -1,5 +1,3 @@
{-# LANGUAGE OverloadedStrings #-}
-- | After the document is parsed, before getting executed the AST is
-- transformed into a similar, simpler AST. This module is responsible for
-- this transformation.
@ -12,26 +10,24 @@ import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as NonEmpty
import Data.Maybe (fromMaybe)
import qualified Language.GraphQL.AST as Full
import qualified Language.GraphQL.AST.Core as Core
import qualified Language.GraphQL.Schema as Schema
-- | Associates a fragment name with a list of 'Core.Field's.
type Fragments = HashMap Core.Name [Core.Field]
type Fragments = HashMap Core.Name (NonEmpty Core.Field)
-- | Rewrites the original syntax tree into an intermediate representation used
-- for query execution.
document :: Schema.Subs -> Full.Document -> Maybe Core.Document
document subs doc = operations subs fragments operations'
document subs doc =
case fragments of
Just fragments' -> operations subs fragments' operations'
Nothing -> Nothing
where
(fragments, operations') = foldr (defrag subs) mempty
(fragments, operations') = foldr (defrag subs) (Just HashMap.empty, [])
$ NonEmpty.toList doc
extractFragment :: Fragments -> Core.Name -> [Core.Selection]
extractFragment fragments name = Core.SelectionField
<$> fromMaybe mempty (HashMap.lookup name fragments)
-- * Operation
-- TODO: Replace Maybe by MonadThrow CustomError
@ -53,27 +49,27 @@ operation subs fragments (Full.OperationSelectionSet sels) =
operation subs fragments $ Full.OperationDefinition Full.Query mempty mempty mempty sels
-- TODO: Validate Variable definitions with substituter
operation subs fragments (Full.OperationDefinition Full.Query name _vars _dirs sels) =
pure $ Core.Query name $ appendSelection subs fragments sels
Core.Query name <$> appendSelection subs fragments sels
operation subs fragments (Full.OperationDefinition Full.Mutation name _vars _dirs sels) =
pure $ Core.Mutation name $ appendSelection subs fragments sels
Core.Mutation name <$> appendSelection subs fragments sels
selection ::
Schema.Subs ->
Fragments ->
Full.Selection ->
Either [Core.Selection] Core.Selection
Maybe (Either (NonEmpty Core.Selection) Core.Selection)
selection subs fragments (Full.SelectionField fld)
= Right $ Core.SelectionField $ field subs fragments fld
= Right . Core.SelectionField <$> field subs fragments fld
selection _ fragments (Full.SelectionFragmentSpread (Full.FragmentSpread name _))
= Left $ extractFragment fragments name
= Left . fmap Core.SelectionField <$> HashMap.lookup name fragments
selection subs fragments (Full.SelectionInlineFragment fragment)
| (Full.InlineFragment (Just typeCondition) _ selectionSet) <- fragment
= Right
$ Core.SelectionFragment
$ Core.Fragment typeCondition
$ appendSelection subs fragments selectionSet
. Core.SelectionFragment
. Core.Fragment typeCondition
<$> appendSelection subs fragments selectionSet
| (Full.InlineFragment Nothing _ selectionSet) <- fragment
= Left $ NonEmpty.toList $ appendSelection subs fragments selectionSet
= Left <$> appendSelection subs fragments selectionSet
-- * Fragment replacement
@ -81,35 +77,37 @@ selection subs fragments (Full.SelectionInlineFragment fragment)
defrag ::
Schema.Subs ->
Full.Definition ->
(Fragments, [Full.OperationDefinition]) ->
(Fragments, [Full.OperationDefinition])
(Maybe Fragments, [Full.OperationDefinition]) ->
(Maybe Fragments, [Full.OperationDefinition])
defrag _ (Full.DefinitionOperation op) (fragments, operations') =
(fragments, op : operations')
defrag subs (Full.DefinitionFragment fragDef) (fragments, operations') =
defrag subs (Full.DefinitionFragment fragDef) (Just fragments, operations') =
(fragmentDefinition subs fragments fragDef, operations')
defrag _ _ (Nothing, operations') =
(Nothing, operations')
fragmentDefinition ::
Schema.Subs ->
Fragments ->
Full.FragmentDefinition ->
Fragments
fragmentDefinition subs fragments (Full.FragmentDefinition name _tc _dirs sels) =
HashMap.insert name (extractField <$> emitValue) fragments
Maybe Fragments
fragmentDefinition subs fragments (Full.FragmentDefinition name _tc _dirs sels) = do
emitted <- emitValue
newValue <- traverse extractField emitted
Just $ HashMap.insert name newValue fragments
where
emitValue :: Maybe (NonEmpty Core.Selection)
emitValue = do
selections <- NonEmpty.toList $ selection subs mempty <$> sels
either id pure selections
extractField (Core.SelectionField field') = field'
extractField _ = error "Fragments within fragments are not supported yet"
selections <- traverse (selection subs fragments) sels
pure $ selections >>= either id pure
extractField :: Core.Selection -> Maybe Core.Field
extractField (Core.SelectionField field') = Just field'
extractField _ = Nothing -- Fragments within fragments are not supported yet
field :: Schema.Subs -> Fragments -> Full.Field -> Core.Field
field :: Schema.Subs -> Fragments -> Full.Field -> Maybe Core.Field
field subs fragments (Full.Field a n args _dirs sels) =
Core.Field a n (fold $ argument subs `traverse` args) (foldr go mempty sels)
where
go :: Full.Selection -> [Core.Selection] -> [Core.Selection]
go (Full.SelectionFragmentSpread (Full.FragmentSpread name _dirs)) =
(extractFragment fragments name <>)
go sel = (either id pure (selection subs fragments sel) <>)
Core.Field a n (fold $ argument subs `traverse` args)
<$> appendSelectionOpt subs fragments sels
argument :: Schema.Subs -> Full.Argument -> Maybe Core.Argument
argument subs (Full.Argument n v) = Core.Argument n <$> value subs v
@ -130,10 +128,25 @@ value subs (Full.ValueObject o) =
objectField :: Schema.Subs -> Full.ObjectField -> Maybe Core.ObjectField
objectField subs (Full.ObjectField n v) = Core.ObjectField n <$> value subs v
appendSelectionOpt ::
Traversable t =>
Schema.Subs ->
Fragments ->
t Full.Selection ->
Maybe [Core.Selection]
appendSelectionOpt subs fragments = foldr go (Just [])
where
go :: Full.Selection -> Maybe [Core.Selection] -> Maybe [Core.Selection]
go _ Nothing = Nothing
go sel (Just acc) = append acc <$> selection subs fragments sel
append acc (Left list) = NonEmpty.toList list <> acc
append acc (Right one) = one : acc
appendSelection ::
Schema.Subs ->
Fragments ->
NonEmpty Full.Selection ->
NonEmpty Core.Selection
appendSelection subs fragments = NonEmpty.fromList
. foldr (either (++) (:) . selection subs fragments) []
Maybe (NonEmpty Core.Selection)
appendSelection subs fragments fullSelection = do
coreSelection <-appendSelectionOpt subs fragments fullSelection
NonEmpty.nonEmpty coreSelection