{-# LANGUAGE TemplateHaskell #-}

module Hasura.GraphQL.Transport.HTTP.Protocol
  ( GQLReq (..),
    GQLBatchedReqs (..),
    GQLReqUnparsed,
    GQLReqParsed,
    GQLReqOutgoing,
    renderGQLReqOutgoing,
    SingleOperation,
    getSingleOperation,
    toParsed,
    GQLQueryText (..),
    GQLExecDoc (..),
    OperationName (..),
    VariableValues,
    encodeGQErr,
    encodeGQResp,
    decodeGQResp,
    encodeHTTPResp,
    GQResult,
    GQExecError (..),
    GQResponse,
    isExecError,
    ReqsText,
  )
where

import Data.Aeson qualified as J
import Data.Aeson.Casing qualified as J
import Data.Aeson.KeyMap qualified as KM
import Data.Aeson.TH qualified as J
import Data.ByteString.Lazy qualified as BL
import Data.Either (isLeft)
import Data.HashMap.Strict qualified as Map
import Data.Text.Extended (dquote)
import Hasura.Base.Error
import Hasura.Base.Instances ()
import Hasura.EncJSON
import Hasura.GraphQL.Execute.Inline qualified as EI
import Hasura.Prelude
import Language.GraphQL.Draft.Parser qualified as G
import Language.GraphQL.Draft.Printer qualified as G
import Language.GraphQL.Draft.Syntax qualified as G
import Language.Haskell.TH.Syntax (Lift)

-- TODO: why not just `G.ExecutableDocument G.Name`?
newtype GQLExecDoc = GQLExecDoc {GQLExecDoc -> [ExecutableDefinition Name]
unGQLExecDoc :: [G.ExecutableDefinition G.Name]}
  deriving (Eq GQLExecDoc
Eq GQLExecDoc
-> (GQLExecDoc -> GQLExecDoc -> Ordering)
-> (GQLExecDoc -> GQLExecDoc -> Bool)
-> (GQLExecDoc -> GQLExecDoc -> Bool)
-> (GQLExecDoc -> GQLExecDoc -> Bool)
-> (GQLExecDoc -> GQLExecDoc -> Bool)
-> (GQLExecDoc -> GQLExecDoc -> GQLExecDoc)
-> (GQLExecDoc -> GQLExecDoc -> GQLExecDoc)
-> Ord GQLExecDoc
GQLExecDoc -> GQLExecDoc -> Bool
GQLExecDoc -> GQLExecDoc -> Ordering
GQLExecDoc -> GQLExecDoc -> GQLExecDoc
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: GQLExecDoc -> GQLExecDoc -> GQLExecDoc
$cmin :: GQLExecDoc -> GQLExecDoc -> GQLExecDoc
max :: GQLExecDoc -> GQLExecDoc -> GQLExecDoc
$cmax :: GQLExecDoc -> GQLExecDoc -> GQLExecDoc
>= :: GQLExecDoc -> GQLExecDoc -> Bool
$c>= :: GQLExecDoc -> GQLExecDoc -> Bool
> :: GQLExecDoc -> GQLExecDoc -> Bool
$c> :: GQLExecDoc -> GQLExecDoc -> Bool
<= :: GQLExecDoc -> GQLExecDoc -> Bool
$c<= :: GQLExecDoc -> GQLExecDoc -> Bool
< :: GQLExecDoc -> GQLExecDoc -> Bool
$c< :: GQLExecDoc -> GQLExecDoc -> Bool
compare :: GQLExecDoc -> GQLExecDoc -> Ordering
$ccompare :: GQLExecDoc -> GQLExecDoc -> Ordering
$cp1Ord :: Eq GQLExecDoc
Ord, Int -> GQLExecDoc -> ShowS
[GQLExecDoc] -> ShowS
GQLExecDoc -> String
(Int -> GQLExecDoc -> ShowS)
-> (GQLExecDoc -> String)
-> ([GQLExecDoc] -> ShowS)
-> Show GQLExecDoc
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GQLExecDoc] -> ShowS
$cshowList :: [GQLExecDoc] -> ShowS
show :: GQLExecDoc -> String
$cshow :: GQLExecDoc -> String
showsPrec :: Int -> GQLExecDoc -> ShowS
$cshowsPrec :: Int -> GQLExecDoc -> ShowS
Show, GQLExecDoc -> GQLExecDoc -> Bool
(GQLExecDoc -> GQLExecDoc -> Bool)
-> (GQLExecDoc -> GQLExecDoc -> Bool) -> Eq GQLExecDoc
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GQLExecDoc -> GQLExecDoc -> Bool
$c/= :: GQLExecDoc -> GQLExecDoc -> Bool
== :: GQLExecDoc -> GQLExecDoc -> Bool
$c== :: GQLExecDoc -> GQLExecDoc -> Bool
Eq, Int -> GQLExecDoc -> Int
GQLExecDoc -> Int
(Int -> GQLExecDoc -> Int)
-> (GQLExecDoc -> Int) -> Hashable GQLExecDoc
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
hash :: GQLExecDoc -> Int
$chash :: GQLExecDoc -> Int
hashWithSalt :: Int -> GQLExecDoc -> Int
$chashWithSalt :: Int -> GQLExecDoc -> Int
Hashable, GQLExecDoc -> Q Exp
GQLExecDoc -> Q (TExp GQLExecDoc)
(GQLExecDoc -> Q Exp)
-> (GQLExecDoc -> Q (TExp GQLExecDoc)) -> Lift GQLExecDoc
forall t. (t -> Q Exp) -> (t -> Q (TExp t)) -> Lift t
liftTyped :: GQLExecDoc -> Q (TExp GQLExecDoc)
$cliftTyped :: GQLExecDoc -> Q (TExp GQLExecDoc)
lift :: GQLExecDoc -> Q Exp
$clift :: GQLExecDoc -> Q Exp
Lift)

instance J.FromJSON GQLExecDoc where
  parseJSON :: Value -> Parser GQLExecDoc
parseJSON Value
v = [ExecutableDefinition Name] -> GQLExecDoc
GQLExecDoc ([ExecutableDefinition Name] -> GQLExecDoc)
-> (ExecutableDocument Name -> [ExecutableDefinition Name])
-> ExecutableDocument Name
-> GQLExecDoc
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExecutableDocument Name -> [ExecutableDefinition Name]
forall var. ExecutableDocument var -> [ExecutableDefinition var]
G.getExecutableDefinitions (ExecutableDocument Name -> GQLExecDoc)
-> Parser (ExecutableDocument Name) -> Parser GQLExecDoc
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser (ExecutableDocument Name)
forall a. FromJSON a => Value -> Parser a
J.parseJSON Value
v

instance J.ToJSON GQLExecDoc where
  toJSON :: GQLExecDoc -> Value
toJSON = ExecutableDocument Name -> Value
forall a. ToJSON a => a -> Value
J.toJSON (ExecutableDocument Name -> Value)
-> (GQLExecDoc -> ExecutableDocument Name) -> GQLExecDoc -> Value
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [ExecutableDefinition Name] -> ExecutableDocument Name
forall var. [ExecutableDefinition var] -> ExecutableDocument var
G.ExecutableDocument ([ExecutableDefinition Name] -> ExecutableDocument Name)
-> (GQLExecDoc -> [ExecutableDefinition Name])
-> GQLExecDoc
-> ExecutableDocument Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. GQLExecDoc -> [ExecutableDefinition Name]
unGQLExecDoc

newtype OperationName = OperationName {OperationName -> Name
_unOperationName :: G.Name}
  deriving (Eq OperationName
Eq OperationName
-> (OperationName -> OperationName -> Ordering)
-> (OperationName -> OperationName -> Bool)
-> (OperationName -> OperationName -> Bool)
-> (OperationName -> OperationName -> Bool)
-> (OperationName -> OperationName -> Bool)
-> (OperationName -> OperationName -> OperationName)
-> (OperationName -> OperationName -> OperationName)
-> Ord OperationName
OperationName -> OperationName -> Bool
OperationName -> OperationName -> Ordering
OperationName -> OperationName -> OperationName
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: OperationName -> OperationName -> OperationName
$cmin :: OperationName -> OperationName -> OperationName
max :: OperationName -> OperationName -> OperationName
$cmax :: OperationName -> OperationName -> OperationName
>= :: OperationName -> OperationName -> Bool
$c>= :: OperationName -> OperationName -> Bool
> :: OperationName -> OperationName -> Bool
$c> :: OperationName -> OperationName -> Bool
<= :: OperationName -> OperationName -> Bool
$c<= :: OperationName -> OperationName -> Bool
< :: OperationName -> OperationName -> Bool
$c< :: OperationName -> OperationName -> Bool
compare :: OperationName -> OperationName -> Ordering
$ccompare :: OperationName -> OperationName -> Ordering
$cp1Ord :: Eq OperationName
Ord, Int -> OperationName -> ShowS
[OperationName] -> ShowS
OperationName -> String
(Int -> OperationName -> ShowS)
-> (OperationName -> String)
-> ([OperationName] -> ShowS)
-> Show OperationName
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [OperationName] -> ShowS
$cshowList :: [OperationName] -> ShowS
show :: OperationName -> String
$cshow :: OperationName -> String
showsPrec :: Int -> OperationName -> ShowS
$cshowsPrec :: Int -> OperationName -> ShowS
Show, OperationName -> OperationName -> Bool
(OperationName -> OperationName -> Bool)
-> (OperationName -> OperationName -> Bool) -> Eq OperationName
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: OperationName -> OperationName -> Bool
$c/= :: OperationName -> OperationName -> Bool
== :: OperationName -> OperationName -> Bool
$c== :: OperationName -> OperationName -> Bool
Eq, Int -> OperationName -> Int
OperationName -> Int
(Int -> OperationName -> Int)
-> (OperationName -> Int) -> Hashable OperationName
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
hash :: OperationName -> Int
$chash :: OperationName -> Int
hashWithSalt :: Int -> OperationName -> Int
$chashWithSalt :: Int -> OperationName -> Int
Hashable, [OperationName] -> Value
[OperationName] -> Encoding
OperationName -> Value
OperationName -> Encoding
(OperationName -> Value)
-> (OperationName -> Encoding)
-> ([OperationName] -> Value)
-> ([OperationName] -> Encoding)
-> ToJSON OperationName
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [OperationName] -> Encoding
$ctoEncodingList :: [OperationName] -> Encoding
toJSONList :: [OperationName] -> Value
$ctoJSONList :: [OperationName] -> Value
toEncoding :: OperationName -> Encoding
$ctoEncoding :: OperationName -> Encoding
toJSON :: OperationName -> Value
$ctoJSON :: OperationName -> Value
J.ToJSON, OperationName -> Q Exp
OperationName -> Q (TExp OperationName)
(OperationName -> Q Exp)
-> (OperationName -> Q (TExp OperationName)) -> Lift OperationName
forall t. (t -> Q Exp) -> (t -> Q (TExp t)) -> Lift t
liftTyped :: OperationName -> Q (TExp OperationName)
$cliftTyped :: OperationName -> Q (TExp OperationName)
lift :: OperationName -> Q Exp
$clift :: OperationName -> Q Exp
Lift)

instance J.FromJSON OperationName where
  parseJSON :: Value -> Parser OperationName
parseJSON Value
v = Name -> OperationName
OperationName (Name -> OperationName) -> Parser Name -> Parser OperationName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser Name
forall a. FromJSON a => Value -> Parser a
J.parseJSON Value
v

type VariableValues = Map.HashMap G.Name J.Value

-- | https://graphql.org/learn/serving-over-http/#post-request
--
-- See 'GQLReqParsed' for invariants.
data GQLReq a = GQLReq
  { GQLReq a -> Maybe OperationName
_grOperationName :: !(Maybe OperationName),
    GQLReq a -> a
_grQuery :: !a,
    GQLReq a -> Maybe VariableValues
_grVariables :: !(Maybe VariableValues)
  }
  deriving (Int -> GQLReq a -> ShowS
[GQLReq a] -> ShowS
GQLReq a -> String
(Int -> GQLReq a -> ShowS)
-> (GQLReq a -> String) -> ([GQLReq a] -> ShowS) -> Show (GQLReq a)
forall a. Show a => Int -> GQLReq a -> ShowS
forall a. Show a => [GQLReq a] -> ShowS
forall a. Show a => GQLReq a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GQLReq a] -> ShowS
$cshowList :: forall a. Show a => [GQLReq a] -> ShowS
show :: GQLReq a -> String
$cshow :: forall a. Show a => GQLReq a -> String
showsPrec :: Int -> GQLReq a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> GQLReq a -> ShowS
Show, GQLReq a -> GQLReq a -> Bool
(GQLReq a -> GQLReq a -> Bool)
-> (GQLReq a -> GQLReq a -> Bool) -> Eq (GQLReq a)
forall a. Eq a => GQLReq a -> GQLReq a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GQLReq a -> GQLReq a -> Bool
$c/= :: forall a. Eq a => GQLReq a -> GQLReq a -> Bool
== :: GQLReq a -> GQLReq a -> Bool
$c== :: forall a. Eq a => GQLReq a -> GQLReq a -> Bool
Eq, (forall x. GQLReq a -> Rep (GQLReq a) x)
-> (forall x. Rep (GQLReq a) x -> GQLReq a) -> Generic (GQLReq a)
forall x. Rep (GQLReq a) x -> GQLReq a
forall x. GQLReq a -> Rep (GQLReq a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (GQLReq a) x -> GQLReq a
forall a x. GQLReq a -> Rep (GQLReq a) x
$cto :: forall a x. Rep (GQLReq a) x -> GQLReq a
$cfrom :: forall a x. GQLReq a -> Rep (GQLReq a) x
Generic, a -> GQLReq b -> GQLReq a
(a -> b) -> GQLReq a -> GQLReq b
(forall a b. (a -> b) -> GQLReq a -> GQLReq b)
-> (forall a b. a -> GQLReq b -> GQLReq a) -> Functor GQLReq
forall a b. a -> GQLReq b -> GQLReq a
forall a b. (a -> b) -> GQLReq a -> GQLReq b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> GQLReq b -> GQLReq a
$c<$ :: forall a b. a -> GQLReq b -> GQLReq a
fmap :: (a -> b) -> GQLReq a -> GQLReq b
$cfmap :: forall a b. (a -> b) -> GQLReq a -> GQLReq b
Functor, GQLReq a -> Q Exp
GQLReq a -> Q (TExp (GQLReq a))
(GQLReq a -> Q Exp)
-> (GQLReq a -> Q (TExp (GQLReq a))) -> Lift (GQLReq a)
forall a. Lift a => GQLReq a -> Q Exp
forall a. Lift a => GQLReq a -> Q (TExp (GQLReq a))
forall t. (t -> Q Exp) -> (t -> Q (TExp t)) -> Lift t
liftTyped :: GQLReq a -> Q (TExp (GQLReq a))
$cliftTyped :: forall a. Lift a => GQLReq a -> Q (TExp (GQLReq a))
lift :: GQLReq a -> Q Exp
$clift :: forall a. Lift a => GQLReq a -> Q Exp
Lift)

$(J.deriveJSON (J.aesonPrefix J.camelCase) {J.omitNothingFields = True} ''GQLReq)

instance (Hashable a) => Hashable (GQLReq a)

-- | Batched queries are sent as a JSON array of
-- 'GQLReq' records. This newtype exists to support
-- the unusual JSON encoding.
--
-- See <https://github.com/hasura/graphql-engine/issues/1812>.
data GQLBatchedReqs a
  = GQLSingleRequest a
  | GQLBatchedReqs [a]
  deriving (Int -> GQLBatchedReqs a -> ShowS
[GQLBatchedReqs a] -> ShowS
GQLBatchedReqs a -> String
(Int -> GQLBatchedReqs a -> ShowS)
-> (GQLBatchedReqs a -> String)
-> ([GQLBatchedReqs a] -> ShowS)
-> Show (GQLBatchedReqs a)
forall a. Show a => Int -> GQLBatchedReqs a -> ShowS
forall a. Show a => [GQLBatchedReqs a] -> ShowS
forall a. Show a => GQLBatchedReqs a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GQLBatchedReqs a] -> ShowS
$cshowList :: forall a. Show a => [GQLBatchedReqs a] -> ShowS
show :: GQLBatchedReqs a -> String
$cshow :: forall a. Show a => GQLBatchedReqs a -> String
showsPrec :: Int -> GQLBatchedReqs a -> ShowS
$cshowsPrec :: forall a. Show a => Int -> GQLBatchedReqs a -> ShowS
Show, GQLBatchedReqs a -> GQLBatchedReqs a -> Bool
(GQLBatchedReqs a -> GQLBatchedReqs a -> Bool)
-> (GQLBatchedReqs a -> GQLBatchedReqs a -> Bool)
-> Eq (GQLBatchedReqs a)
forall a. Eq a => GQLBatchedReqs a -> GQLBatchedReqs a -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GQLBatchedReqs a -> GQLBatchedReqs a -> Bool
$c/= :: forall a. Eq a => GQLBatchedReqs a -> GQLBatchedReqs a -> Bool
== :: GQLBatchedReqs a -> GQLBatchedReqs a -> Bool
$c== :: forall a. Eq a => GQLBatchedReqs a -> GQLBatchedReqs a -> Bool
Eq, (forall x. GQLBatchedReqs a -> Rep (GQLBatchedReqs a) x)
-> (forall x. Rep (GQLBatchedReqs a) x -> GQLBatchedReqs a)
-> Generic (GQLBatchedReqs a)
forall x. Rep (GQLBatchedReqs a) x -> GQLBatchedReqs a
forall x. GQLBatchedReqs a -> Rep (GQLBatchedReqs a) x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
forall a x. Rep (GQLBatchedReqs a) x -> GQLBatchedReqs a
forall a x. GQLBatchedReqs a -> Rep (GQLBatchedReqs a) x
$cto :: forall a x. Rep (GQLBatchedReqs a) x -> GQLBatchedReqs a
$cfrom :: forall a x. GQLBatchedReqs a -> Rep (GQLBatchedReqs a) x
Generic, a -> GQLBatchedReqs b -> GQLBatchedReqs a
(a -> b) -> GQLBatchedReqs a -> GQLBatchedReqs b
(forall a b. (a -> b) -> GQLBatchedReqs a -> GQLBatchedReqs b)
-> (forall a b. a -> GQLBatchedReqs b -> GQLBatchedReqs a)
-> Functor GQLBatchedReqs
forall a b. a -> GQLBatchedReqs b -> GQLBatchedReqs a
forall a b. (a -> b) -> GQLBatchedReqs a -> GQLBatchedReqs b
forall (f :: * -> *).
(forall a b. (a -> b) -> f a -> f b)
-> (forall a b. a -> f b -> f a) -> Functor f
<$ :: a -> GQLBatchedReqs b -> GQLBatchedReqs a
$c<$ :: forall a b. a -> GQLBatchedReqs b -> GQLBatchedReqs a
fmap :: (a -> b) -> GQLBatchedReqs a -> GQLBatchedReqs b
$cfmap :: forall a b. (a -> b) -> GQLBatchedReqs a -> GQLBatchedReqs b
Functor)

instance J.ToJSON a => J.ToJSON (GQLBatchedReqs a) where
  toJSON :: GQLBatchedReqs a -> Value
toJSON (GQLSingleRequest a
q) = a -> Value
forall a. ToJSON a => a -> Value
J.toJSON a
q
  toJSON (GQLBatchedReqs [a]
qs) = [a] -> Value
forall a. ToJSON a => a -> Value
J.toJSON [a]
qs

instance J.FromJSON a => J.FromJSON (GQLBatchedReqs a) where
  parseJSON :: Value -> Parser (GQLBatchedReqs a)
parseJSON arr :: Value
arr@J.Array {} = [a] -> GQLBatchedReqs a
forall a. [a] -> GQLBatchedReqs a
GQLBatchedReqs ([a] -> GQLBatchedReqs a)
-> Parser [a] -> Parser (GQLBatchedReqs a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser [a]
forall a. FromJSON a => Value -> Parser a
J.parseJSON Value
arr
  parseJSON Value
other = a -> GQLBatchedReqs a
forall a. a -> GQLBatchedReqs a
GQLSingleRequest (a -> GQLBatchedReqs a) -> Parser a -> Parser (GQLBatchedReqs a)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Value -> Parser a
forall a. FromJSON a => Value -> Parser a
J.parseJSON Value
other

newtype GQLQueryText = GQLQueryText
  { GQLQueryText -> Text
_unGQLQueryText :: Text
  }
  deriving (Int -> GQLQueryText -> ShowS
[GQLQueryText] -> ShowS
GQLQueryText -> String
(Int -> GQLQueryText -> ShowS)
-> (GQLQueryText -> String)
-> ([GQLQueryText] -> ShowS)
-> Show GQLQueryText
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GQLQueryText] -> ShowS
$cshowList :: [GQLQueryText] -> ShowS
show :: GQLQueryText -> String
$cshow :: GQLQueryText -> String
showsPrec :: Int -> GQLQueryText -> ShowS
$cshowsPrec :: Int -> GQLQueryText -> ShowS
Show, GQLQueryText -> GQLQueryText -> Bool
(GQLQueryText -> GQLQueryText -> Bool)
-> (GQLQueryText -> GQLQueryText -> Bool) -> Eq GQLQueryText
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GQLQueryText -> GQLQueryText -> Bool
$c/= :: GQLQueryText -> GQLQueryText -> Bool
== :: GQLQueryText -> GQLQueryText -> Bool
$c== :: GQLQueryText -> GQLQueryText -> Bool
Eq, Eq GQLQueryText
Eq GQLQueryText
-> (GQLQueryText -> GQLQueryText -> Ordering)
-> (GQLQueryText -> GQLQueryText -> Bool)
-> (GQLQueryText -> GQLQueryText -> Bool)
-> (GQLQueryText -> GQLQueryText -> Bool)
-> (GQLQueryText -> GQLQueryText -> Bool)
-> (GQLQueryText -> GQLQueryText -> GQLQueryText)
-> (GQLQueryText -> GQLQueryText -> GQLQueryText)
-> Ord GQLQueryText
GQLQueryText -> GQLQueryText -> Bool
GQLQueryText -> GQLQueryText -> Ordering
GQLQueryText -> GQLQueryText -> GQLQueryText
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: GQLQueryText -> GQLQueryText -> GQLQueryText
$cmin :: GQLQueryText -> GQLQueryText -> GQLQueryText
max :: GQLQueryText -> GQLQueryText -> GQLQueryText
$cmax :: GQLQueryText -> GQLQueryText -> GQLQueryText
>= :: GQLQueryText -> GQLQueryText -> Bool
$c>= :: GQLQueryText -> GQLQueryText -> Bool
> :: GQLQueryText -> GQLQueryText -> Bool
$c> :: GQLQueryText -> GQLQueryText -> Bool
<= :: GQLQueryText -> GQLQueryText -> Bool
$c<= :: GQLQueryText -> GQLQueryText -> Bool
< :: GQLQueryText -> GQLQueryText -> Bool
$c< :: GQLQueryText -> GQLQueryText -> Bool
compare :: GQLQueryText -> GQLQueryText -> Ordering
$ccompare :: GQLQueryText -> GQLQueryText -> Ordering
$cp1Ord :: Eq GQLQueryText
Ord, Int -> GQLQueryText -> Int
GQLQueryText -> Int
(Int -> GQLQueryText -> Int)
-> (GQLQueryText -> Int) -> Hashable GQLQueryText
forall a. (Int -> a -> Int) -> (a -> Int) -> Hashable a
hash :: GQLQueryText -> Int
$chash :: GQLQueryText -> Int
hashWithSalt :: Int -> GQLQueryText -> Int
$chashWithSalt :: Int -> GQLQueryText -> Int
Hashable, String -> GQLQueryText
(String -> GQLQueryText) -> IsString GQLQueryText
forall a. (String -> a) -> IsString a
fromString :: String -> GQLQueryText
$cfromString :: String -> GQLQueryText
IsString)
  deriving newtype (Value -> Parser [GQLQueryText]
Value -> Parser GQLQueryText
(Value -> Parser GQLQueryText)
-> (Value -> Parser [GQLQueryText]) -> FromJSON GQLQueryText
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [GQLQueryText]
$cparseJSONList :: Value -> Parser [GQLQueryText]
parseJSON :: Value -> Parser GQLQueryText
$cparseJSON :: Value -> Parser GQLQueryText
J.FromJSON, [GQLQueryText] -> Value
[GQLQueryText] -> Encoding
GQLQueryText -> Value
GQLQueryText -> Encoding
(GQLQueryText -> Value)
-> (GQLQueryText -> Encoding)
-> ([GQLQueryText] -> Value)
-> ([GQLQueryText] -> Encoding)
-> ToJSON GQLQueryText
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [GQLQueryText] -> Encoding
$ctoEncodingList :: [GQLQueryText] -> Encoding
toJSONList :: [GQLQueryText] -> Value
$ctoJSONList :: [GQLQueryText] -> Value
toEncoding :: GQLQueryText -> Encoding
$ctoEncoding :: GQLQueryText -> Encoding
toJSON :: GQLQueryText -> Value
$ctoJSON :: GQLQueryText -> Value
J.ToJSON)

-- | We've not yet parsed the graphql query string parameter of the POST.
type GQLReqUnparsed = GQLReq GQLQueryText

-- | Invariants:
--
--    - when '_grOperationName' is @Nothing@, '_grQuery' contains exactly one
--      'ExecutableDefinitionOperation' (and zero or more 'ExecutableDefinitionFragment')
--
--    - when '_grOperationName' is present, there is a corresponding
--      'ExecutableDefinitionOperation' in '_grQuery'
type GQLReqParsed = GQLReq GQLExecDoc

type ReqsText = GQLBatchedReqs (GQLReq GQLQueryText)

-- | A simplified form of 'GQLReqParsed' which is more ergonomic in particular
-- for APIs that act as graphql /clients/ (e.g. in remote relationship
-- execution). This is a "desugared" request in which fragments have been
-- inlined (see 'inlineSelectionSet'), and the operation ('_grOperationName')
-- to be executed is the only payload (in contrast to a 'G.ExecutableDocument'
-- with possibly many named operations).
--
-- '_grOperationName' is essentially ignored here, but should correspond with
-- '_todName' if present.
--
-- These could maybe benefit from an HKD refactoring.
type GQLReqOutgoing = GQLReq SingleOperation

-- | A single graphql operation to be executed, with fragment definitions
-- inlined. This is the simplified form of 'GQLExecDoc' or
-- 'G.ExecutableDocument':
type SingleOperation = G.TypedOperationDefinition G.NoFragments G.Name

renderGQLReqOutgoing :: GQLReqOutgoing -> GQLReqUnparsed
renderGQLReqOutgoing :: GQLReqOutgoing -> GQLReqUnparsed
renderGQLReqOutgoing = (TypedOperationDefinition NoFragments Name -> GQLQueryText)
-> GQLReqOutgoing -> GQLReqUnparsed
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Text -> GQLQueryText
GQLQueryText (Text -> GQLQueryText)
-> (TypedOperationDefinition NoFragments Name -> Text)
-> TypedOperationDefinition NoFragments Name
-> GQLQueryText
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExecutableDocument Name -> Text
G.renderExecutableDoc (ExecutableDocument Name -> Text)
-> (TypedOperationDefinition NoFragments Name
    -> ExecutableDocument Name)
-> TypedOperationDefinition NoFragments Name
-> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TypedOperationDefinition FragmentSpread Name
-> ExecutableDocument Name
forall var.
TypedOperationDefinition FragmentSpread var
-> ExecutableDocument var
toExecDoc (TypedOperationDefinition FragmentSpread Name
 -> ExecutableDocument Name)
-> (TypedOperationDefinition NoFragments Name
    -> TypedOperationDefinition FragmentSpread Name)
-> TypedOperationDefinition NoFragments Name
-> ExecutableDocument Name
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TypedOperationDefinition NoFragments Name
-> TypedOperationDefinition FragmentSpread Name
forall var.
TypedOperationDefinition NoFragments var
-> TypedOperationDefinition FragmentSpread var
inlineFrags)
  where
    -- This is essentially a 'coerce' (TODO unsafeCoerce optimization possible)?
    inlineFrags ::
      G.TypedOperationDefinition G.NoFragments var ->
      G.TypedOperationDefinition G.FragmentSpread var
    inlineFrags :: TypedOperationDefinition NoFragments var
-> TypedOperationDefinition FragmentSpread var
inlineFrags TypedOperationDefinition NoFragments var
opDef =
      TypedOperationDefinition NoFragments var
opDef {_todSelectionSet :: SelectionSet FragmentSpread var
G._todSelectionSet = (NoFragments var -> FragmentSpread var)
-> SelectionSet NoFragments var -> SelectionSet FragmentSpread var
forall (frag :: * -> *) var (frag' :: * -> *).
(frag var -> frag' var)
-> SelectionSet frag var -> SelectionSet frag' var
G.fmapSelectionSetFragment NoFragments var -> FragmentSpread var
forall var. NoFragments var -> FragmentSpread var
G.inline (SelectionSet NoFragments var -> SelectionSet FragmentSpread var)
-> SelectionSet NoFragments var -> SelectionSet FragmentSpread var
forall a b. (a -> b) -> a -> b
$ TypedOperationDefinition NoFragments var
-> SelectionSet NoFragments var
forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> SelectionSet frag var
G._todSelectionSet TypedOperationDefinition NoFragments var
opDef}
    toExecDoc :: TypedOperationDefinition FragmentSpread var
-> ExecutableDocument var
toExecDoc =
      [ExecutableDefinition var] -> ExecutableDocument var
forall var. [ExecutableDefinition var] -> ExecutableDocument var
G.ExecutableDocument ([ExecutableDefinition var] -> ExecutableDocument var)
-> (TypedOperationDefinition FragmentSpread var
    -> [ExecutableDefinition var])
-> TypedOperationDefinition FragmentSpread var
-> ExecutableDocument var
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ExecutableDefinition var -> [ExecutableDefinition var]
forall (f :: * -> *) a. Applicative f => a -> f a
pure (ExecutableDefinition var -> [ExecutableDefinition var])
-> (TypedOperationDefinition FragmentSpread var
    -> ExecutableDefinition var)
-> TypedOperationDefinition FragmentSpread var
-> [ExecutableDefinition var]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OperationDefinition FragmentSpread var -> ExecutableDefinition var
forall var.
OperationDefinition FragmentSpread var -> ExecutableDefinition var
G.ExecutableDefinitionOperation (OperationDefinition FragmentSpread var
 -> ExecutableDefinition var)
-> (TypedOperationDefinition FragmentSpread var
    -> OperationDefinition FragmentSpread var)
-> TypedOperationDefinition FragmentSpread var
-> ExecutableDefinition var
forall b c a. (b -> c) -> (a -> b) -> a -> c
. TypedOperationDefinition FragmentSpread var
-> OperationDefinition FragmentSpread var
forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> OperationDefinition frag var
G.OperationDefinitionTyped

-- | Obtain the actual single operation to be executed, from the possibly-
-- multi-operation document, validating per the spec and inlining any
-- fragment definitions (pre-defined parts of a graphql query) at fragment
-- spreads (locations where fragments are "spliced"). See:
--
--     https://spec.graphql.org/June2018/#sec-Executable-Definitions  and...
--     https://graphql.org/learn/serving-over-http/
{-# INLINEABLE getSingleOperation #-}
getSingleOperation ::
  MonadError QErr m =>
  GQLReqParsed ->
  m SingleOperation
getSingleOperation :: GQLReqParsed -> m (TypedOperationDefinition NoFragments Name)
getSingleOperation (GQLReq Maybe OperationName
opNameM GQLExecDoc
q Maybe VariableValues
_varValsM) = do
  let ([SelectionSet FragmentSpread Name]
selSets, [TypedOperationDefinition FragmentSpread Name]
opDefs, [FragmentDefinition]
fragments) = [ExecutableDefinition Name]
-> ([SelectionSet FragmentSpread Name],
    [TypedOperationDefinition FragmentSpread Name],
    [FragmentDefinition])
forall var.
[ExecutableDefinition var]
-> ([SelectionSet FragmentSpread var],
    [TypedOperationDefinition FragmentSpread var],
    [FragmentDefinition])
G.partitionExDefs ([ExecutableDefinition Name]
 -> ([SelectionSet FragmentSpread Name],
     [TypedOperationDefinition FragmentSpread Name],
     [FragmentDefinition]))
-> [ExecutableDefinition Name]
-> ([SelectionSet FragmentSpread Name],
    [TypedOperationDefinition FragmentSpread Name],
    [FragmentDefinition])
forall a b. (a -> b) -> a -> b
$ GQLExecDoc -> [ExecutableDefinition Name]
unGQLExecDoc GQLExecDoc
q
  G.TypedOperationDefinition {[Directive Name]
SelectionSet FragmentSpread Name
[VariableDefinition]
Maybe Name
OperationType
_todVariableDefinitions :: forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> [VariableDefinition]
_todType :: forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> OperationType
_todName :: forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> Maybe Name
_todDirectives :: forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> [Directive var]
_todSelectionSet :: SelectionSet FragmentSpread Name
_todDirectives :: [Directive Name]
_todVariableDefinitions :: [VariableDefinition]
_todName :: Maybe Name
_todType :: OperationType
_todSelectionSet :: forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> SelectionSet frag var
..} <-
    case (Maybe OperationName
opNameM, [SelectionSet FragmentSpread Name]
selSets, [TypedOperationDefinition FragmentSpread Name]
opDefs) of
      (Just OperationName
opName, [], [TypedOperationDefinition FragmentSpread Name]
_) -> do
        let n :: Name
n = OperationName -> Name
_unOperationName OperationName
opName
            opDefM :: Maybe (TypedOperationDefinition FragmentSpread Name)
opDefM = (TypedOperationDefinition FragmentSpread Name -> Bool)
-> [TypedOperationDefinition FragmentSpread Name]
-> Maybe (TypedOperationDefinition FragmentSpread Name)
forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Maybe a
find (\TypedOperationDefinition FragmentSpread Name
opDef -> TypedOperationDefinition FragmentSpread Name -> Maybe Name
forall (frag :: * -> *) var.
TypedOperationDefinition frag var -> Maybe Name
G._todName TypedOperationDefinition FragmentSpread Name
opDef Maybe Name -> Maybe Name -> Bool
forall a. Eq a => a -> a -> Bool
== Name -> Maybe Name
forall a. a -> Maybe a
Just Name
n) [TypedOperationDefinition FragmentSpread Name]
opDefs
        Maybe (TypedOperationDefinition FragmentSpread Name)
-> m (TypedOperationDefinition FragmentSpread Name)
-> m (TypedOperationDefinition FragmentSpread Name)
forall (m :: * -> *) a. Applicative m => Maybe a -> m a -> m a
onNothing Maybe (TypedOperationDefinition FragmentSpread Name)
opDefM (m (TypedOperationDefinition FragmentSpread Name)
 -> m (TypedOperationDefinition FragmentSpread Name))
-> m (TypedOperationDefinition FragmentSpread Name)
-> m (TypedOperationDefinition FragmentSpread Name)
forall a b. (a -> b) -> a -> b
$
          Code -> Text -> m (TypedOperationDefinition FragmentSpread Name)
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
ValidationFailed (Text -> m (TypedOperationDefinition FragmentSpread Name))
-> Text -> m (TypedOperationDefinition FragmentSpread Name)
forall a b. (a -> b) -> a -> b
$
            Text
"no such operation found in the document: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Name -> Text
forall t. ToTxt t => t -> Text
dquote Name
n
      (Just OperationName
_, [SelectionSet FragmentSpread Name]
_, [TypedOperationDefinition FragmentSpread Name]
_) ->
        Code -> Text -> m (TypedOperationDefinition FragmentSpread Name)
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
ValidationFailed (Text -> m (TypedOperationDefinition FragmentSpread Name))
-> Text -> m (TypedOperationDefinition FragmentSpread Name)
forall a b. (a -> b) -> a -> b
$
          Text
"operationName cannot be used when "
            Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"an anonymous operation exists in the document"
      (Maybe OperationName
Nothing, [SelectionSet FragmentSpread Name
selSet], []) ->
        TypedOperationDefinition FragmentSpread Name
-> m (TypedOperationDefinition FragmentSpread Name)
forall (m :: * -> *) a. Monad m => a -> m a
return (TypedOperationDefinition FragmentSpread Name
 -> m (TypedOperationDefinition FragmentSpread Name))
-> TypedOperationDefinition FragmentSpread Name
-> m (TypedOperationDefinition FragmentSpread Name)
forall a b. (a -> b) -> a -> b
$ OperationType
-> Maybe Name
-> [VariableDefinition]
-> [Directive Name]
-> SelectionSet FragmentSpread Name
-> TypedOperationDefinition FragmentSpread Name
forall (frag :: * -> *) var.
OperationType
-> Maybe Name
-> [VariableDefinition]
-> [Directive var]
-> SelectionSet frag var
-> TypedOperationDefinition frag var
G.TypedOperationDefinition OperationType
G.OperationTypeQuery Maybe Name
forall a. Maybe a
Nothing [] [] SelectionSet FragmentSpread Name
selSet
      (Maybe OperationName
Nothing, [], [TypedOperationDefinition FragmentSpread Name
opDef]) ->
        TypedOperationDefinition FragmentSpread Name
-> m (TypedOperationDefinition FragmentSpread Name)
forall (m :: * -> *) a. Monad m => a -> m a
return TypedOperationDefinition FragmentSpread Name
opDef
      (Maybe OperationName
Nothing, [SelectionSet FragmentSpread Name]
_, [TypedOperationDefinition FragmentSpread Name]
_) ->
        Code -> Text -> m (TypedOperationDefinition FragmentSpread Name)
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
ValidationFailed (Text -> m (TypedOperationDefinition FragmentSpread Name))
-> Text -> m (TypedOperationDefinition FragmentSpread Name)
forall a b. (a -> b) -> a -> b
$
          Text
"exactly one operation has to be present "
            Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
"in the document when operationName is not specified"

  SelectionSet NoFragments Name
inlinedSelSet <- [FragmentDefinition]
-> SelectionSet FragmentSpread Name
-> m (SelectionSet NoFragments Name)
forall (m :: * -> *) (t :: * -> *).
(MonadError QErr m, Foldable t) =>
t FragmentDefinition
-> SelectionSet FragmentSpread Name
-> m (SelectionSet NoFragments Name)
EI.inlineSelectionSet [FragmentDefinition]
fragments SelectionSet FragmentSpread Name
_todSelectionSet
  TypedOperationDefinition NoFragments Name
-> m (TypedOperationDefinition NoFragments Name)
forall (f :: * -> *) a. Applicative f => a -> f a
pure (TypedOperationDefinition NoFragments Name
 -> m (TypedOperationDefinition NoFragments Name))
-> TypedOperationDefinition NoFragments Name
-> m (TypedOperationDefinition NoFragments Name)
forall a b. (a -> b) -> a -> b
$ TypedOperationDefinition :: forall (frag :: * -> *) var.
OperationType
-> Maybe Name
-> [VariableDefinition]
-> [Directive var]
-> SelectionSet frag var
-> TypedOperationDefinition frag var
G.TypedOperationDefinition {_todSelectionSet :: SelectionSet NoFragments Name
_todSelectionSet = SelectionSet NoFragments Name
inlinedSelSet, [Directive Name]
[VariableDefinition]
Maybe Name
OperationType
_todVariableDefinitions :: [VariableDefinition]
_todType :: OperationType
_todName :: Maybe Name
_todDirectives :: [Directive Name]
_todDirectives :: [Directive Name]
_todVariableDefinitions :: [VariableDefinition]
_todName :: Maybe Name
_todType :: OperationType
..}

toParsed :: (MonadError QErr m) => GQLReqUnparsed -> m GQLReqParsed
toParsed :: GQLReqUnparsed -> m GQLReqParsed
toParsed GQLReqUnparsed
req = case Text -> Either Text (ExecutableDocument Name)
G.parseExecutableDoc Text
gqlText of
  Left Text
_ -> Text -> m GQLReqParsed -> m GQLReqParsed
forall (m :: * -> *) a. QErrM m => Text -> m a -> m a
withPathK Text
"query" (m GQLReqParsed -> m GQLReqParsed)
-> m GQLReqParsed -> m GQLReqParsed
forall a b. (a -> b) -> a -> b
$ Code -> Text -> m GQLReqParsed
forall (m :: * -> *) a. QErrM m => Code -> Text -> m a
throw400 Code
ValidationFailed Text
"not a valid graphql query"
  Right ExecutableDocument Name
a -> GQLReqParsed -> m GQLReqParsed
forall (m :: * -> *) a. Monad m => a -> m a
return (GQLReqParsed -> m GQLReqParsed) -> GQLReqParsed -> m GQLReqParsed
forall a b. (a -> b) -> a -> b
$ GQLReqUnparsed
req {_grQuery :: GQLExecDoc
_grQuery = [ExecutableDefinition Name] -> GQLExecDoc
GQLExecDoc ([ExecutableDefinition Name] -> GQLExecDoc)
-> [ExecutableDefinition Name] -> GQLExecDoc
forall a b. (a -> b) -> a -> b
$ ExecutableDocument Name -> [ExecutableDefinition Name]
forall var. ExecutableDocument var -> [ExecutableDefinition var]
G.getExecutableDefinitions ExecutableDocument Name
a}
  where
    gqlText :: Text
gqlText = GQLQueryText -> Text
_unGQLQueryText (GQLQueryText -> Text) -> GQLQueryText -> Text
forall a b. (a -> b) -> a -> b
$ GQLReqUnparsed -> GQLQueryText
forall a. GQLReq a -> a
_grQuery GQLReqUnparsed
req

encodeGQErr :: Bool -> QErr -> J.Value
encodeGQErr :: Bool -> QErr -> Value
encodeGQErr Bool
includeInternal QErr
qErr =
  [Pair] -> Value
J.object [Key
"errors" Key -> [Value] -> Pair
forall kv v. (KeyValue kv, ToJSON v) => Key -> v -> kv
J..= [Bool -> QErr -> Value
encodeGQLErr Bool
includeInternal QErr
qErr]]

type GQResult a = Either GQExecError a

newtype GQExecError = GQExecError [J.Value]
  deriving (Int -> GQExecError -> ShowS
[GQExecError] -> ShowS
GQExecError -> String
(Int -> GQExecError -> ShowS)
-> (GQExecError -> String)
-> ([GQExecError] -> ShowS)
-> Show GQExecError
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GQExecError] -> ShowS
$cshowList :: [GQExecError] -> ShowS
show :: GQExecError -> String
$cshow :: GQExecError -> String
showsPrec :: Int -> GQExecError -> ShowS
$cshowsPrec :: Int -> GQExecError -> ShowS
Show, GQExecError -> GQExecError -> Bool
(GQExecError -> GQExecError -> Bool)
-> (GQExecError -> GQExecError -> Bool) -> Eq GQExecError
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: GQExecError -> GQExecError -> Bool
$c/= :: GQExecError -> GQExecError -> Bool
== :: GQExecError -> GQExecError -> Bool
$c== :: GQExecError -> GQExecError -> Bool
Eq, [GQExecError] -> Value
[GQExecError] -> Encoding
GQExecError -> Value
GQExecError -> Encoding
(GQExecError -> Value)
-> (GQExecError -> Encoding)
-> ([GQExecError] -> Value)
-> ([GQExecError] -> Encoding)
-> ToJSON GQExecError
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [GQExecError] -> Encoding
$ctoEncodingList :: [GQExecError] -> Encoding
toJSONList :: [GQExecError] -> Value
$ctoJSONList :: [GQExecError] -> Value
toEncoding :: GQExecError -> Encoding
$ctoEncoding :: GQExecError -> Encoding
toJSON :: GQExecError -> Value
$ctoJSON :: GQExecError -> Value
J.ToJSON)

type GQResponse = GQResult BL.ByteString

isExecError :: GQResult a -> Bool
isExecError :: GQResult a -> Bool
isExecError = GQResult a -> Bool
forall a b. Either a b -> Bool
isLeft

encodeGQResp :: GQResponse -> EncJSON
encodeGQResp :: GQResponse -> EncJSON
encodeGQResp GQResponse
gqResp =
  [(Text, EncJSON)] -> EncJSON
encJFromAssocList ([(Text, EncJSON)] -> EncJSON) -> [(Text, EncJSON)] -> EncJSON
forall a b. (a -> b) -> a -> b
$ case GQResponse
gqResp of
    Right ByteString
r -> [(Text
"data", ByteString -> EncJSON
encJFromLBS ByteString
r)]
    Left GQExecError
e -> [(Text
"data", Builder -> EncJSON
encJFromBuilder Builder
"null"), (Text
"errors", GQExecError -> EncJSON
forall a. ToJSON a => a -> EncJSON
encJFromJValue GQExecError
e)]

-- We don't want to force the `Maybe GQResponse` unless absolutely necessary
-- Decode EncJSON from Cache for HTTP endpoints
decodeGQResp :: EncJSON -> (Maybe GQResponse, EncJSON)
decodeGQResp :: EncJSON -> (Maybe GQResponse, EncJSON)
decodeGQResp EncJSON
encJson =
  let gqResp :: Maybe GQResponse
gqResp =
        case ByteString -> Maybe Value
forall a. FromJSON a => ByteString -> Maybe a
J.decode @J.Value (EncJSON -> ByteString
encJToLBS EncJSON
encJson) of
          Just (J.Object Object
v) ->
            case Key -> Object -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KM.lookup Key
"error" Object
v of
              Just Value
err -> GQResponse -> Maybe GQResponse
forall a. a -> Maybe a
Just (ByteString -> GQResponse
forall a b. b -> Either a b
Right (ByteString -> GQResponse) -> ByteString -> GQResponse
forall a b. (a -> b) -> a -> b
$ Value -> ByteString
forall a. ToJSON a => a -> ByteString
J.encode Value
err)
              Maybe Value
Nothing -> ByteString -> GQResponse
forall a b. b -> Either a b
Right (ByteString -> GQResponse)
-> (Value -> ByteString) -> Value -> GQResponse
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Value -> ByteString
forall a. ToJSON a => a -> ByteString
J.encode (Value -> GQResponse) -> Maybe Value -> Maybe GQResponse
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Key -> Object -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KM.lookup Key
"data" Object
v
          Maybe Value
_ -> Maybe GQResponse
forall a. Maybe a
Nothing
   in (Maybe GQResponse
gqResp, EncJSON
encJson)

-- Encode for HTTP Response without `data` envelope
encodeHTTPResp :: GQResponse -> EncJSON
encodeHTTPResp :: GQResponse -> EncJSON
encodeHTTPResp = \case
  Right ByteString
r -> ByteString -> EncJSON
encJFromLBS ByteString
r
  Left GQExecError
e -> GQExecError -> EncJSON
forall a. ToJSON a => a -> EncJSON
encJFromJValue GQExecError
e