2019-11-03 10:42:10 +01:00
|
|
|
{-# LANGUAGE TupleSections #-}
|
2019-11-11 15:46:52 +01:00
|
|
|
{-# LANGUAGE ExplicitForAll #-}
|
2019-11-03 10:42:10 +01:00
|
|
|
|
2019-09-25 05:35:36 +02:00
|
|
|
-- | After the document is parsed, before getting executed the AST is
|
|
|
|
-- transformed into a similar, simpler AST. This module is responsible for
|
|
|
|
-- this transformation.
|
2019-07-14 05:58:05 +02:00
|
|
|
module Language.GraphQL.AST.Transform
|
|
|
|
( document
|
|
|
|
) where
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-11-11 15:46:52 +01:00
|
|
|
import Control.Arrow (first)
|
|
|
|
import Control.Monad (foldM, unless)
|
2019-11-06 06:34:36 +01:00
|
|
|
import Control.Monad.Trans.Class (lift)
|
2019-11-11 15:46:52 +01:00
|
|
|
import Control.Monad.Trans.Reader (ReaderT, ask, runReaderT)
|
|
|
|
import Control.Monad.Trans.State (StateT, evalStateT, gets, modify)
|
2019-10-31 07:32:51 +01:00
|
|
|
import Data.HashMap.Strict (HashMap)
|
|
|
|
import qualified Data.HashMap.Strict as HashMap
|
2017-01-29 22:44:03 +01:00
|
|
|
import qualified Data.List.NonEmpty as NonEmpty
|
2019-11-16 11:41:40 +01:00
|
|
|
import Data.Sequence (Seq, (<|), (><))
|
2019-07-07 06:31:53 +02:00
|
|
|
import qualified Language.GraphQL.AST as Full
|
|
|
|
import qualified Language.GraphQL.AST.Core as Core
|
|
|
|
import qualified Language.GraphQL.Schema as Schema
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-10-31 07:32:51 +01:00
|
|
|
-- | Associates a fragment name with a list of 'Core.Field's.
|
2019-11-06 06:34:36 +01:00
|
|
|
data Replacement = Replacement
|
2019-12-01 20:43:19 +01:00
|
|
|
{ fragments :: HashMap Core.Name Core.Fragment
|
2019-11-11 15:46:52 +01:00
|
|
|
, fragmentDefinitions :: HashMap Full.Name Full.FragmentDefinition
|
2019-11-06 06:34:36 +01:00
|
|
|
}
|
|
|
|
|
2019-11-11 15:46:52 +01:00
|
|
|
type TransformT a = StateT Replacement (ReaderT Schema.Subs Maybe) a
|
2019-11-06 06:34:36 +01:00
|
|
|
|
2019-08-29 07:40:50 +02:00
|
|
|
-- | Rewrites the original syntax tree into an intermediate representation used
|
|
|
|
-- for query execution.
|
2017-02-04 01:48:26 +01:00
|
|
|
document :: Schema.Subs -> Full.Document -> Maybe Core.Document
|
2019-11-11 15:46:52 +01:00
|
|
|
document subs document' =
|
|
|
|
flip runReaderT subs
|
|
|
|
$ evalStateT (collectFragments >> operations operationDefinitions)
|
|
|
|
$ Replacement HashMap.empty fragmentTable
|
2017-01-29 22:44:03 +01:00
|
|
|
where
|
2019-11-11 15:46:52 +01:00
|
|
|
(fragmentTable, operationDefinitions) = foldr defragment mempty document'
|
|
|
|
defragment (Full.DefinitionOperation definition) acc =
|
|
|
|
(definition :) <$> acc
|
|
|
|
defragment (Full.DefinitionFragment definition) acc =
|
|
|
|
let (Full.FragmentDefinition name _ _ _) = definition
|
|
|
|
in first (HashMap.insert name definition) acc
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2017-02-04 01:48:26 +01:00
|
|
|
-- * Operation
|
|
|
|
|
2017-02-25 20:46:51 +01:00
|
|
|
-- TODO: Replace Maybe by MonadThrow CustomError
|
2019-11-06 06:34:36 +01:00
|
|
|
operations :: [Full.OperationDefinition] -> TransformT Core.Document
|
|
|
|
operations operations' = do
|
|
|
|
coreOperations <- traverse operation operations'
|
2019-11-11 15:46:52 +01:00
|
|
|
lift . lift $ NonEmpty.nonEmpty coreOperations
|
2019-11-06 06:34:36 +01:00
|
|
|
|
|
|
|
operation :: Full.OperationDefinition -> TransformT Core.Operation
|
|
|
|
operation (Full.OperationSelectionSet sels) =
|
|
|
|
operation $ Full.OperationDefinition Full.Query mempty mempty mempty sels
|
2017-02-12 19:19:13 +01:00
|
|
|
-- TODO: Validate Variable definitions with substituter
|
2019-11-06 06:34:36 +01:00
|
|
|
operation (Full.OperationDefinition Full.Query name _vars _dirs sels) =
|
|
|
|
Core.Query name <$> appendSelection sels
|
|
|
|
operation (Full.OperationDefinition Full.Mutation name _vars _dirs sels) =
|
|
|
|
Core.Mutation name <$> appendSelection sels
|
2019-10-31 07:32:51 +01:00
|
|
|
|
|
|
|
selection ::
|
|
|
|
Full.Selection ->
|
2019-11-16 11:41:40 +01:00
|
|
|
TransformT (Either (Seq Core.Selection) Core.Selection)
|
2019-11-06 06:34:36 +01:00
|
|
|
selection (Full.SelectionField fld) = Right . Core.SelectionField <$> field fld
|
2019-12-01 20:43:19 +01:00
|
|
|
selection (Full.SelectionFragmentSpread fragment) =
|
|
|
|
Right . Core.SelectionFragment <$> fragmentSpread fragment
|
2019-11-06 06:34:36 +01:00
|
|
|
selection (Full.SelectionInlineFragment fragment)
|
2019-10-07 21:03:07 +02:00
|
|
|
| (Full.InlineFragment (Just typeCondition) _ selectionSet) <- fragment
|
2019-10-11 23:28:55 +02:00
|
|
|
= Right
|
2019-11-01 14:24:21 +01:00
|
|
|
. Core.SelectionFragment
|
|
|
|
. Core.Fragment typeCondition
|
2019-11-06 06:34:36 +01:00
|
|
|
<$> appendSelection selectionSet
|
2019-10-11 23:28:55 +02:00
|
|
|
| (Full.InlineFragment Nothing _ selectionSet) <- fragment
|
2019-11-06 06:34:36 +01:00
|
|
|
= Left <$> appendSelection selectionSet
|
2017-02-04 01:48:26 +01:00
|
|
|
|
2017-01-29 22:44:03 +01:00
|
|
|
-- * Fragment replacement
|
|
|
|
|
2019-11-11 15:46:52 +01:00
|
|
|
-- | Extract fragment definitions into a single 'HashMap'.
|
|
|
|
collectFragments :: TransformT ()
|
|
|
|
collectFragments = do
|
|
|
|
fragDefs <- gets fragmentDefinitions
|
|
|
|
let nextValue = head $ HashMap.elems fragDefs
|
|
|
|
unless (HashMap.null fragDefs) $ do
|
|
|
|
_ <- fragmentDefinition nextValue
|
|
|
|
collectFragments
|
|
|
|
|
2019-12-01 20:43:19 +01:00
|
|
|
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
|
|
|
|
|
2019-11-13 20:40:09 +01:00
|
|
|
fragmentDefinition ::
|
|
|
|
Full.FragmentDefinition ->
|
2019-12-01 20:43:19 +01:00
|
|
|
TransformT Core.Fragment
|
|
|
|
fragmentDefinition (Full.FragmentDefinition name typeCondition _dirs selections) = do
|
2019-11-13 20:40:09 +01:00
|
|
|
modify deleteFragmentDefinition
|
2019-12-01 20:43:19 +01:00
|
|
|
fragmentSelection <- appendSelection selections
|
|
|
|
let newValue = Core.Fragment typeCondition fragmentSelection
|
2019-11-13 20:40:09 +01:00
|
|
|
modify $ insertFragment newValue
|
2019-11-11 15:46:52 +01:00
|
|
|
liftJust newValue
|
2019-10-31 07:32:51 +01:00
|
|
|
where
|
2019-11-13 20:40:09 +01:00
|
|
|
deleteFragmentDefinition (Replacement fragments' fragmentDefinitions') =
|
|
|
|
Replacement fragments' $ HashMap.delete name fragmentDefinitions'
|
|
|
|
insertFragment newValue (Replacement fragments' fragmentDefinitions') =
|
|
|
|
let newFragments = HashMap.insert name newValue fragments'
|
|
|
|
in Replacement newFragments fragmentDefinitions'
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-11-06 06:34:36 +01:00
|
|
|
field :: Full.Field -> TransformT Core.Field
|
|
|
|
field (Full.Field a n args _dirs sels) = do
|
|
|
|
arguments <- traverse argument args
|
2019-11-16 11:41:40 +01:00
|
|
|
selection' <- appendSelection sels
|
2019-11-06 06:34:36 +01:00
|
|
|
return $ Core.Field a n arguments selection'
|
|
|
|
|
|
|
|
argument :: Full.Argument -> TransformT Core.Argument
|
|
|
|
argument (Full.Argument n v) = Core.Argument n <$> value v
|
|
|
|
|
|
|
|
value :: Full.Value -> TransformT Core.Value
|
|
|
|
value (Full.Variable n) = do
|
2019-11-11 15:46:52 +01:00
|
|
|
substitute' <- lift ask
|
|
|
|
lift . lift $ substitute' n
|
2019-11-06 06:34:36 +01:00
|
|
|
value (Full.Int i) = pure $ Core.Int i
|
|
|
|
value (Full.Float f) = pure $ Core.Float f
|
|
|
|
value (Full.String x) = pure $ Core.String x
|
|
|
|
value (Full.Boolean b) = pure $ Core.Boolean b
|
|
|
|
value Full.Null = pure Core.Null
|
|
|
|
value (Full.Enum e) = pure $ Core.Enum e
|
|
|
|
value (Full.List l) =
|
|
|
|
Core.List <$> traverse value l
|
|
|
|
value (Full.Object o) =
|
|
|
|
Core.Object . HashMap.fromList <$> traverse objectField o
|
|
|
|
|
|
|
|
objectField :: Full.ObjectField -> TransformT (Core.Name, Core.Value)
|
|
|
|
objectField (Full.ObjectField n v) = (n,) <$> value v
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-11-16 11:41:40 +01:00
|
|
|
appendSelection ::
|
2019-11-01 14:24:21 +01:00
|
|
|
Traversable t =>
|
|
|
|
t Full.Selection ->
|
2019-11-16 11:41:40 +01:00
|
|
|
TransformT (Seq Core.Selection)
|
|
|
|
appendSelection = foldM go mempty
|
2019-11-01 14:24:21 +01:00
|
|
|
where
|
2019-11-06 06:34:36 +01:00
|
|
|
go acc sel = append acc <$> selection sel
|
2019-11-16 11:41:40 +01:00
|
|
|
append acc (Left list) = list >< acc
|
|
|
|
append acc (Right one) = one <| acc
|
2019-11-11 15:46:52 +01:00
|
|
|
|
|
|
|
liftJust :: forall a. a -> TransformT a
|
|
|
|
liftJust = lift . lift . Just
|