summaryrefslogtreecommitdiff
path: root/src/Language/GraphQL/Execute/Transform.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Language/GraphQL/Execute/Transform.hs')
-rw-r--r--src/Language/GraphQL/Execute/Transform.hs93
1 files changed, 59 insertions, 34 deletions
diff --git a/src/Language/GraphQL/Execute/Transform.hs b/src/Language/GraphQL/Execute/Transform.hs
index 485bd51..df64254 100644
--- a/src/Language/GraphQL/Execute/Transform.hs
+++ b/src/Language/GraphQL/Execute/Transform.hs
@@ -15,11 +15,12 @@ module Language.GraphQL.Execute.Transform
import Control.Monad (foldM, unless)
import Control.Monad.Trans.Class (lift)
-import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT)
-import Control.Monad.Trans.State (StateT, evalStateT, gets, modify)
+import Control.Monad.Trans.State (State, evalStateT, gets, modify)
import Data.Foldable (find)
+import Data.Functor.Identity (Identity(..))
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
+import Data.Maybe (fromMaybe)
import Data.List.NonEmpty (NonEmpty(..))
import qualified Data.List.NonEmpty as NonEmpty
import Data.Sequence (Seq, (<|), (><))
@@ -37,17 +38,13 @@ import Language.GraphQL.Type.Schema
data Replacement = Replacement
{ fragments :: HashMap Core.Name Core.Fragment
, fragmentDefinitions :: HashMap Full.Name Full.FragmentDefinition
+ , variableValues :: Schema.Subs
}
-type TransformT a = StateT Replacement (ReaderT Schema.Subs Maybe) a
-
-liftJust :: forall a. a -> TransformT a
-liftJust = lift . lift . Just
+type TransformT a = State Replacement a
-- | GraphQL document is a non-empty list of operations.
-data Document = Document
- Core.Operation
- (HashMap Full.Name Full.FragmentDefinition)
+newtype Document = Document Core.Operation
data OperationDefinition = OperationDefinition
Full.OperationType
@@ -120,18 +117,44 @@ coerceVariableValues :: (Monad m, VariableValue a)
-> OperationDefinition
-> HashMap.HashMap Full.Name a
-> Either QueryError Schema.Subs
-coerceVariableValues schema (OperationDefinition _ _ variables _ _) values =
+coerceVariableValues schema operationDefinition variableValues' =
let referencedTypes = collectReferencedTypes schema
+ OperationDefinition _ _ variableDefinitions _ _ = operationDefinition
+ coerceValue' = coerceValue referencedTypes
in maybe (Left CoercionError) Right
- $ foldr (coerceValue referencedTypes) (Just HashMap.empty) variables
+ $ foldr coerceValue' (Just HashMap.empty) variableDefinitions
where
coerceValue referencedTypes variableDefinition coercedValues = do
- let Full.VariableDefinition variableName variableTypeName _defaultValue =
+ let Full.VariableDefinition variableName variableTypeName defaultValue =
variableDefinition
+ let defaultValue' = constValue <$> defaultValue
+ let value' = HashMap.lookup variableName variableValues'
+
variableType <- lookupInputType variableTypeName referencedTypes
- value' <- HashMap.lookup variableName values
- coercedValue <- coerceVariableValue variableType value'
- HashMap.insert variableName coercedValue <$> coercedValues
+ HashMap.insert variableName
+ <$> choose value' defaultValue' variableType
+ <*> coercedValues
+ choose Nothing defaultValue variableType
+ | Just _ <- defaultValue = defaultValue
+ | not (isNonNullInputType variableType) = Just Core.Null
+ choose (Just value') _ variableType
+ | Just coercedValue <- coerceVariableValue variableType value'
+ , not (isNonNullInputType variableType) || coercedValue /= Core.Null =
+ Just coercedValue
+ choose _ _ _ = Nothing
+
+constValue :: Full.ConstValue -> Core.Value
+constValue (Full.ConstInt i) = Core.Int i
+constValue (Full.ConstFloat f) = Core.Float f
+constValue (Full.ConstString x) = Core.String x
+constValue (Full.ConstBoolean b) = Core.Boolean b
+constValue Full.ConstNull = Core.Null
+constValue (Full.ConstEnum e) = Core.Enum e
+constValue (Full.ConstList l) = Core.List $ constValue <$> l
+constValue (Full.ConstObject o) =
+ Core.Object $ HashMap.fromList $ constObjectField <$> o
+ where
+ constObjectField (Full.ObjectField key value') = (key, constValue value')
-- | Rewrites the original syntax tree into an intermediate representation used
-- for query execution.
@@ -148,10 +171,8 @@ document schema operationName subs ast = do
chosenOperation <- getOperation operationName nonEmptyOperations
coercedValues <- coerceVariableValues schema chosenOperation subs
- maybe (Left TransformationError) Right
- $ Document
- <$> operation fragmentTable coercedValues chosenOperation
- <*> pure fragmentTable
+ pure $ Document
+ $ operation fragmentTable coercedValues chosenOperation
where
defragment definition (operations, fragments')
| (Full.ExecutableDefinition executable) <- definition
@@ -174,10 +195,11 @@ operation
:: HashMap Full.Name Full.FragmentDefinition
-> Schema.Subs
-> OperationDefinition
- -> Maybe Core.Operation
-operation fragmentTable subs operationDefinition = flip runReaderT subs
+ -> Core.Operation
+operation fragmentTable subs operationDefinition
+ = runIdentity
$ evalStateT (collectFragments >> transform operationDefinition)
- $ Replacement HashMap.empty fragmentTable
+ $ Replacement HashMap.empty fragmentTable subs
where
transform :: OperationDefinition -> TransformT Core.Operation
transform (OperationDefinition Full.Query name _ _ sels) =
@@ -201,13 +223,15 @@ selection (Full.FragmentSpread name directives') =
maybe (Left mempty) (Right . Core.SelectionFragment) <$> do
spreadDirectives <- Directive.selection <$> directives directives'
fragments' <- gets fragments
- fragment <- maybe lookupDefinition liftJust (HashMap.lookup name fragments')
- pure $ fragment <$ spreadDirectives
- where
- lookupDefinition = do
+
fragmentDefinitions' <- gets fragmentDefinitions
- found <- lift . lift $ HashMap.lookup name fragmentDefinitions'
- fragmentDefinition found
+ case HashMap.lookup name fragments' of
+ Just definition -> lift $ pure $ definition <$ spreadDirectives
+ Nothing -> case HashMap.lookup name fragmentDefinitions' of
+ Just definition -> do
+ fragment <- fragmentDefinition definition
+ lift $ pure $ fragment <$ spreadDirectives
+ Nothing -> lift $ pure Nothing
selection (Full.InlineFragment type' directives' selections) = do
fragmentDirectives <- Directive.selection <$> directives directives'
case fragmentDirectives of
@@ -255,13 +279,13 @@ fragmentDefinition (Full.FragmentDefinition name type' _ selections) = do
fragmentSelection <- appendSelection selections
let newValue = Core.Fragment type' fragmentSelection
modify $ insertFragment newValue
- liftJust newValue
+ lift $ pure newValue
where
- deleteFragmentDefinition (Replacement fragments' fragmentDefinitions') =
- Replacement fragments' $ HashMap.delete name fragmentDefinitions'
- insertFragment newValue (Replacement fragments' fragmentDefinitions') =
+ deleteFragmentDefinition (Replacement fragments' fragmentDefinitions' subs) =
+ Replacement fragments' (HashMap.delete name fragmentDefinitions') subs
+ insertFragment newValue (Replacement fragments' fragmentDefinitions' subs) =
let newFragments = HashMap.insert name newValue fragments'
- in Replacement newFragments fragmentDefinitions'
+ in Replacement newFragments fragmentDefinitions' subs
arguments :: [Full.Argument] -> TransformT Core.Arguments
arguments = fmap Core.Arguments . foldM go HashMap.empty
@@ -271,7 +295,8 @@ arguments = fmap Core.Arguments . foldM go HashMap.empty
return $ HashMap.insert name substitutedValue arguments'
value :: Full.Value -> TransformT Core.Value
-value (Full.Variable name) = lift (asks $ HashMap.lookup name) >>= lift . lift
+value (Full.Variable name) =
+ gets $ fromMaybe Core.Null . HashMap.lookup name . variableValues
value (Full.Int i) = pure $ Core.Int i
value (Full.Float f) = pure $ Core.Float f
value (Full.String x) = pure $ Core.String x