content: {-# LANGUAGE DeriveGeneric #-}
  {-# LANGUAGE OverloadedStrings #-}
  
  module PGIP.GraphQL (isGraphQL, processGraphQL) where
  
  import PGIP.GraphQL.Resolver
  
  import PGIP.Shared
  
  import Driver.Options
  
  import Data.Aeson as Aeson
  import qualified Data.ByteString.Lazy as LBS
  import qualified Data.Text.Lazy.Encoding as LEncoding
  import Data.Maybe
  import Data.Map (Map)
  import qualified Data.Map as Map
  import qualified Data.Text.Lazy as LText
  import Data.Text as Text
  import Network.Wai
  import GHC.Generics
  import qualified Control.Monad.Fail as Fail
  
  isGraphQL :: String -> [String] -> Bool
  isGraphQL httpVerb pathComponents =
    httpVerb == "POST" && pathComponents == ["graphql"]
  
  processGraphQL :: HetcatsOpts -> Cache -> Request -> IO String
  processGraphQL opts sessionReference request = do
    body <- receivedRequestBody request
    let bodyQueryE = Aeson.eitherDecode $ LBS.fromStrict body :: Either String QueryBodyAux
    queryBody <- case bodyQueryE of
      Left message -> Fail.fail ("bad request body: " ++ message)
      Right b -> return $ toGraphQLQueryBody b
    resolve opts sessionReference (graphQLQuery queryBody) (graphQLVariables queryBody)
  
  -- This structure contains the data that is passed to the GraphQL API
  data QueryBody = QueryBody { graphQLQuery :: Text
                             , graphQLVariables :: Map Text Text
                             } deriving (Show, Generic)
  
  -- This is an auxiliary strucutre that helps to parse the request body.
  -- It is then converted to QueryBody.
  data QueryBodyAux = QueryBodyAux { query :: Text
                                   , variables :: Maybe (Map Text Aeson.Value)
                                   } deriving (Show, Generic)
  instance FromJSON QueryBodyAux
  
  
  -- For an unknown reason, GraphQL-API requires the query to be enclosed in {}
  toGraphQLQueryBody :: QueryBodyAux -> QueryBody
  toGraphQLQueryBody QueryBodyAux { query = q, variables = aesonVariables } =
    QueryBody { graphQLQuery = q
              , graphQLVariables = Map.map convert $
                                             fromMaybe Map.empty aesonVariables
              }
    where
      convert = LText.toStrict . LEncoding.decodeUtf8 . Aeson.encode
  