2017-02-19 19:29:58 +01:00
|
|
|
{-# LANGUAGE OverloadedStrings #-}
|
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-10-31 07:32:51 +01:00
|
|
|
import Data.Foldable (fold)
|
|
|
|
import Data.HashMap.Strict (HashMap)
|
|
|
|
import qualified Data.HashMap.Strict as HashMap
|
2019-10-19 10:00:25 +02:00
|
|
|
import Data.List.NonEmpty (NonEmpty)
|
2017-01-29 22:44:03 +01:00
|
|
|
import qualified Data.List.NonEmpty as NonEmpty
|
2019-10-31 07:32:51 +01:00
|
|
|
import Data.Maybe (fromMaybe)
|
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.
|
|
|
|
type Fragments = HashMap Core.Name [Core.Field]
|
2017-01-29 22:44:03 +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-10-31 07:32:51 +01:00
|
|
|
document subs doc = operations subs fragments operations'
|
2017-01-29 22:44:03 +01:00
|
|
|
where
|
2019-10-31 07:32:51 +01:00
|
|
|
(fragments, operations') = foldr (defrag subs) mempty
|
|
|
|
$ NonEmpty.toList doc
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-10-31 07:32:51 +01:00
|
|
|
extractFragment :: Fragments -> Core.Name -> [Core.Selection]
|
|
|
|
extractFragment fragments name = Core.SelectionField
|
|
|
|
<$> fromMaybe mempty (HashMap.lookup name fragments)
|
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-10-31 07:32:51 +01:00
|
|
|
operations ::
|
|
|
|
Schema.Subs ->
|
|
|
|
Fragments ->
|
|
|
|
[Full.OperationDefinition] ->
|
|
|
|
Maybe Core.Document
|
|
|
|
operations subs fragments operations' = do
|
|
|
|
coreOperations <- traverse (operation subs fragments) operations'
|
|
|
|
NonEmpty.nonEmpty coreOperations
|
|
|
|
|
|
|
|
operation ::
|
|
|
|
Schema.Subs ->
|
|
|
|
Fragments ->
|
|
|
|
Full.OperationDefinition ->
|
|
|
|
Maybe Core.Operation
|
|
|
|
operation subs fragments (Full.OperationSelectionSet sels) =
|
|
|
|
operation subs fragments $ Full.OperationDefinition Full.Query mempty mempty mempty sels
|
2017-02-12 19:19:13 +01:00
|
|
|
-- TODO: Validate Variable definitions with substituter
|
2019-10-31 07:32:51 +01:00
|
|
|
operation subs fragments (Full.OperationDefinition Full.Query name _vars _dirs sels) =
|
|
|
|
pure $ 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
|
|
|
|
|
|
|
|
selection ::
|
|
|
|
Schema.Subs ->
|
|
|
|
Fragments ->
|
|
|
|
Full.Selection ->
|
|
|
|
Either [Core.Selection] Core.Selection
|
|
|
|
selection subs fragments (Full.SelectionField fld)
|
|
|
|
= Right $ Core.SelectionField $ field subs fragments fld
|
|
|
|
selection _ fragments (Full.SelectionFragmentSpread (Full.FragmentSpread name _))
|
|
|
|
= Left $ extractFragment fragments name
|
|
|
|
selection subs fragments (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
|
|
|
|
$ Core.SelectionFragment
|
|
|
|
$ Core.Fragment typeCondition
|
2019-10-31 07:32:51 +01:00
|
|
|
$ appendSelection subs fragments selectionSet
|
2019-10-11 23:28:55 +02:00
|
|
|
| (Full.InlineFragment Nothing _ selectionSet) <- fragment
|
2019-10-31 07:32:51 +01:00
|
|
|
= Left $ NonEmpty.toList $ appendSelection subs fragments selectionSet
|
2017-02-04 01:48:26 +01:00
|
|
|
|
2017-01-29 22:44:03 +01:00
|
|
|
-- * Fragment replacement
|
|
|
|
|
2019-10-31 07:32:51 +01:00
|
|
|
-- | Extract fragments into a single 'HashMap' and operation definitions.
|
|
|
|
defrag ::
|
|
|
|
Schema.Subs ->
|
|
|
|
Full.Definition ->
|
|
|
|
(Fragments, [Full.OperationDefinition]) ->
|
|
|
|
(Fragments, [Full.OperationDefinition])
|
|
|
|
defrag _ (Full.DefinitionOperation op) (fragments, operations') =
|
|
|
|
(fragments, op : operations')
|
|
|
|
defrag subs (Full.DefinitionFragment fragDef) (fragments, operations') =
|
|
|
|
(fragmentDefinition subs fragments fragDef, operations')
|
|
|
|
|
|
|
|
fragmentDefinition ::
|
|
|
|
Schema.Subs ->
|
|
|
|
Fragments ->
|
|
|
|
Full.FragmentDefinition ->
|
|
|
|
Fragments
|
|
|
|
fragmentDefinition subs fragments (Full.FragmentDefinition name _tc _dirs sels) =
|
|
|
|
HashMap.insert name (extractField <$> emitValue) fragments
|
|
|
|
where
|
|
|
|
emitValue = do
|
2019-10-07 21:03:07 +02:00
|
|
|
selections <- NonEmpty.toList $ selection subs mempty <$> sels
|
|
|
|
either id pure selections
|
2019-10-31 07:32:51 +01:00
|
|
|
extractField (Core.SelectionField field') = field'
|
|
|
|
extractField _ = error "Fragments within fragments are not supported yet"
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-10-31 07:32:51 +01:00
|
|
|
field :: Schema.Subs -> Fragments -> Full.Field -> 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)
|
2017-02-19 19:29:58 +01:00
|
|
|
where
|
2019-10-07 21:03:07 +02:00
|
|
|
go :: Full.Selection -> [Core.Selection] -> [Core.Selection]
|
2019-10-31 07:32:51 +01:00
|
|
|
go (Full.SelectionFragmentSpread (Full.FragmentSpread name _dirs)) =
|
|
|
|
(extractFragment fragments name <>)
|
|
|
|
go sel = (either id pure (selection subs fragments sel) <>)
|
2017-02-12 19:19:13 +01:00
|
|
|
|
|
|
|
argument :: Schema.Subs -> Full.Argument -> Maybe Core.Argument
|
|
|
|
argument subs (Full.Argument n v) = Core.Argument n <$> value subs v
|
|
|
|
|
|
|
|
value :: Schema.Subs -> Full.Value -> Maybe Core.Value
|
|
|
|
value subs (Full.ValueVariable n) = subs n
|
|
|
|
value _ (Full.ValueInt i) = pure $ Core.ValueInt i
|
|
|
|
value _ (Full.ValueFloat f) = pure $ Core.ValueFloat f
|
|
|
|
value _ (Full.ValueString x) = pure $ Core.ValueString x
|
|
|
|
value _ (Full.ValueBoolean b) = pure $ Core.ValueBoolean b
|
|
|
|
value _ Full.ValueNull = pure Core.ValueNull
|
|
|
|
value _ (Full.ValueEnum e) = pure $ Core.ValueEnum e
|
|
|
|
value subs (Full.ValueList l) =
|
|
|
|
Core.ValueList <$> traverse (value subs) l
|
|
|
|
value subs (Full.ValueObject o) =
|
|
|
|
Core.ValueObject <$> traverse (objectField subs) o
|
|
|
|
|
|
|
|
objectField :: Schema.Subs -> Full.ObjectField -> Maybe Core.ObjectField
|
|
|
|
objectField subs (Full.ObjectField n v) = Core.ObjectField n <$> value subs v
|
2017-01-29 22:44:03 +01:00
|
|
|
|
2019-10-19 10:00:25 +02:00
|
|
|
appendSelection ::
|
|
|
|
Schema.Subs ->
|
2019-10-31 07:32:51 +01:00
|
|
|
Fragments ->
|
2019-10-19 10:00:25 +02:00
|
|
|
NonEmpty Full.Selection ->
|
|
|
|
NonEmpty Core.Selection
|
2019-10-31 07:32:51 +01:00
|
|
|
appendSelection subs fragments = NonEmpty.fromList
|
|
|
|
. foldr (either (++) (:) . selection subs fragments) []
|