Save fragments in a hash map

Fixes #20.
This commit is contained in:
Eugen Wissner 2019-10-31 07:32:51 +01:00
parent 51d39b69e8
commit 0e3b6184be
2 changed files with 74 additions and 71 deletions

View File

@ -7,106 +7,109 @@ module Language.GraphQL.AST.Transform
( document
) where
import Control.Applicative (empty)
import Data.Bifunctor (first)
import Data.Either (partitionEithers)
import Data.Foldable (fold, foldMap)
import Data.Foldable (fold)
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.Monoid (Alt(Alt,getAlt), (<>))
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
-- | Replaces a fragment name by a list of 'Core.Field'. If the name doesn't
-- match an empty list is returned.
type Fragmenter = Core.Name -> [Core.Field]
-- | Associates a fragment name with a list of 'Core.Field's.
type Fragments = HashMap Core.Name [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 fr ops
document subs doc = operations subs fragments operations'
where
(fr, ops) = first foldFrags
. partitionEithers
. NonEmpty.toList
$ defrag subs
<$> doc
(fragments, operations') = foldr (defrag subs) mempty
$ NonEmpty.toList doc
foldFrags :: [Fragmenter] -> Fragmenter
foldFrags fs n = getAlt $ foldMap (Alt . ($ n)) fs
extractFragment :: Fragments -> Core.Name -> [Core.Selection]
extractFragment fragments name = Core.SelectionField
<$> fromMaybe mempty (HashMap.lookup name fragments)
-- * Operation
-- TODO: Replace Maybe by MonadThrow CustomError
operations
:: Schema.Subs
-> Fragmenter
-> [Full.OperationDefinition]
-> Maybe Core.Document
operations subs fr = NonEmpty.nonEmpty . fmap (operation subs fr)
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
-> Fragmenter
-> Full.OperationDefinition
-> Core.Operation
operation subs fr (Full.OperationSelectionSet sels) =
operation subs fr $ Full.OperationDefinition Full.Query empty empty empty sels
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
-- TODO: Validate Variable definitions with substituter
operation subs fr (Full.OperationDefinition Full.Query name _vars _dirs sels) =
Core.Query name $ appendSelection subs fr sels
operation subs fr (Full.OperationDefinition Full.Mutation name _vars _dirs sels) =
Core.Mutation name $ appendSelection subs fr sels
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
-> Fragmenter
-> Full.Selection
-> Either [Core.Selection] Core.Selection
selection subs fr (Full.SelectionField fld)
= Right $ Core.SelectionField $ field subs fr fld
selection _ fr (Full.SelectionFragmentSpread (Full.FragmentSpread name _))
= Left $ Core.SelectionField <$> fr name
selection subs fr (Full.SelectionInlineFragment fragment)
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)
| (Full.InlineFragment (Just typeCondition) _ selectionSet) <- fragment
= Right
$ Core.SelectionFragment
$ Core.Fragment typeCondition
$ appendSelection subs fr selectionSet
$ appendSelection subs fragments selectionSet
| (Full.InlineFragment Nothing _ selectionSet) <- fragment
= Left $ NonEmpty.toList $ appendSelection subs fr selectionSet
= Left $ NonEmpty.toList $ appendSelection subs fragments selectionSet
-- * Fragment replacement
-- | Extract Fragments into a single Fragmenter function and a Operation
-- Definition.
defrag
:: Schema.Subs
-> Full.Definition
-> Either Fragmenter Full.OperationDefinition
defrag _ (Full.DefinitionOperation op) =
Right op
defrag subs (Full.DefinitionFragment fragDef) =
Left $ fragmentDefinition subs fragDef
-- | 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 -> Full.FragmentDefinition -> Fragmenter
fragmentDefinition subs (Full.FragmentDefinition name _tc _dirs sels) name'
| name == name' = selection' <$> do
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
selections <- NonEmpty.toList $ selection subs mempty <$> sels
either id pure selections
| otherwise = empty
where
selection' (Core.SelectionField field') = field'
selection' _ = error "Fragments within fragments are not supported yet"
extractField (Core.SelectionField field') = field'
extractField _ = error "Fragments within fragments are not supported yet"
field :: Schema.Subs -> Fragmenter -> Full.Field -> Core.Field
field subs fr (Full.Field a n args _dirs sels) =
Core.Field a n (fold $ argument subs `traverse` args) (foldr go empty sels)
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)
where
go :: Full.Selection -> [Core.Selection] -> [Core.Selection]
go (Full.SelectionFragmentSpread (Full.FragmentSpread name _dirs)) = ((Core.SelectionField <$> fr name) <>)
go sel = (either id pure (selection subs fr sel) <>)
go (Full.SelectionFragmentSpread (Full.FragmentSpread name _dirs)) =
(extractFragment fragments name <>)
go sel = (either id pure (selection subs fragments sel) <>)
argument :: Schema.Subs -> Full.Argument -> Maybe Core.Argument
argument subs (Full.Argument n v) = Core.Argument n <$> value subs v
@ -129,8 +132,8 @@ objectField subs (Full.ObjectField n v) = Core.ObjectField n <$> value subs v
appendSelection ::
Schema.Subs ->
Fragmenter ->
Fragments ->
NonEmpty Full.Selection ->
NonEmpty Core.Selection
appendSelection subs fr = NonEmpty.fromList
. foldr (either (++) (:) . selection subs fr) []
appendSelection subs fragments = NonEmpty.fromList
. foldr (either (++) (:) . selection subs fragments) []

View File

@ -1,4 +1,4 @@
resolver: lts-14.11
resolver: lts-14.12
packages:
- .