{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.Morpheus.Execution.Server.Decode
( ArgumentsConstraint
, decodeArguments
) where
import Data.Proxy (Proxy (..))
import Data.Semigroup ((<>))
import Data.Text (Text, pack)
import GHC.Generics
import Data.Morpheus.Error.Internal (internalArgumentError, internalTypeMismatch)
import Data.Morpheus.Execution.Server.Generics.EnumRep (EnumRep (..))
import Data.Morpheus.Kind (ENUM, GQL_KIND, INPUT_OBJECT, INPUT_UNION, SCALAR,
WRAPPER)
import Data.Morpheus.Types.GQLScalar (GQLScalar (..), toScalar)
import Data.Morpheus.Types.GQLType (GQLType (KIND, __typeName))
import Data.Morpheus.Types.Internal.AST.Selection (Argument (..), Arguments)
import Data.Morpheus.Types.Internal.Validation (Validation)
import Data.Morpheus.Types.Internal.Value (Value (..))
class DecodeInputUnion f where
decodeUnion :: Value -> Validation (f a)
unionTags :: Proxy f -> [Text]
instance (Datatype c, DecodeInputUnion f) => DecodeInputUnion (M1 D c f) where
decodeUnion = fmap M1 . decodeUnion
unionTags _ = unionTags (Proxy @f)
instance (Constructor c, DecodeInputUnion f) =>
DecodeInputUnion (M1 C c f) where
decodeUnion = fmap M1 . decodeUnion
unionTags _ = unionTags (Proxy @f)
instance (Selector c, DecodeInputUnion f) => DecodeInputUnion (M1 S c f) where
decodeUnion = fmap M1 . decodeUnion
unionTags _ = unionTags (Proxy @f)
instance (DecodeInputUnion a, DecodeInputUnion b) =>
DecodeInputUnion (a :+: b) where
decodeUnion (Object pairs) =
case lookup "tag" pairs of
Nothing -> internalArgumentError "tag not found on Input Union"
Just (Enum name) ->
case lookup name pairs of
Nothing ->
internalArgumentError
("type \"" <> name <> "\" was not provided on object")
Just value
| [name] == l1Tags -> L1 <$> decodeUnion value
Just value
| [name] == r1Tags -> R1 <$> decodeUnion value
Just _
| name `elem` r1Tags -> R1 <$> decodeUnion (Object pairs)
Just _ ->
internalArgumentError
("type \"" <> name <> "\" could not find in union")
where l1Tags = unionTags $ Proxy @a
r1Tags = unionTags $ Proxy @b
Just _ -> internalArgumentError "tag must be Enum"
decodeUnion _ = internalArgumentError "Expected Input Object Union!"
unionTags _ = unionTags (Proxy @a) ++ unionTags (Proxy @b)
instance (GQLType a, Decode a (KIND a)) => DecodeInputUnion (K1 i a) where
decodeUnion value = K1 <$> decode value
unionTags _ = [__typeName (Proxy @a)]
type ArgumentsConstraint a = (Generic a, DecodeInputObject (Rep a))
decodeArguments ::
(Generic p, DecodeInputObject (Rep p)) => Arguments -> Validation p
decodeArguments args =
to <$> decodeObject (Object $ fmap (\(x, y) -> (x, argumentValue y)) args)
class DecodeInputObject f where
decodeObject :: Value -> Validation (f a)
instance DecodeInputObject U1 where
decodeObject _ = pure U1
type Sel s = M1 S s
proxySelName ::
forall s. Selector s
=> Proxy (M1 S s)
-> Text
proxySelName _ = pack $ selName (undefined :: M1 S s f a)
instance (Selector s, DecodeInputObject f) => DecodeInputObject (M1 S s f) where
decodeObject (Object object) = M1 <$> selectFromObject
where
selectorName = proxySelName (Proxy @(Sel s))
selectFromObject =
case lookup selectorName object of
Nothing -> internalArgumentError ("Missing Field: " <> selectorName)
Just value -> decodeObject value
decodeObject isType = internalTypeMismatch "InputObject" isType
instance DecodeInputObject f => DecodeInputObject (M1 D c f) where
decodeObject = fmap M1 . decodeObject
instance DecodeInputObject f => DecodeInputObject (M1 C c f) where
decodeObject = fmap M1 . decodeObject
instance (DecodeInputObject f, DecodeInputObject g) =>
DecodeInputObject (f :*: g) where
decodeObject gql = (:*:) <$> decodeObject gql <*> decodeObject gql
instance (Decode a (KIND a)) => DecodeInputObject (K1 i a) where
decodeObject = fmap K1 . decode
decode ::
forall a. Decode a (KIND a)
=> Value
-> Validation a
decode = __decode (Proxy @(KIND a))
class Decode a (b :: GQL_KIND) where
__decode :: Proxy b -> Value -> Validation a
instance (GQLScalar a) => Decode a SCALAR where
__decode _ value =
case toScalar value >>= parseValue of
Right scalar -> return scalar
Left errorMessage -> internalTypeMismatch errorMessage value
instance (Generic a, EnumRep (Rep a)) => Decode a ENUM where
__decode _ (Enum value) = to <$> decodeEnum value
__decode _ isType = internalTypeMismatch "Enum" isType
instance (Generic a, DecodeInputObject (Rep a)) => Decode a INPUT_OBJECT where
__decode _ (Object x) = to <$> decodeObject (Object x)
__decode _ isType = internalTypeMismatch "InputObject" isType
instance (Generic a, DecodeInputUnion (Rep a)) => Decode a INPUT_UNION where
__decode _ (Object x) = to <$> decodeUnion (Object x)
__decode _ isType = internalTypeMismatch "InputObject" isType
instance Decode a (KIND a) => Decode (Maybe a) WRAPPER where
__decode _ Null = pure Nothing
__decode _ x = Just <$> decode x
instance Decode a (KIND a) => Decode [a] WRAPPER where
__decode _ (List li) = mapM decode li
__decode _ isType = internalTypeMismatch "List" isType