packages feed

greskell-websocket-1.0.0.6: src/Network/Greskell/WebSocket/Response.hs

{-# LANGUAGE DeriveGeneric     #-}
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module: Network.Greskell.WebSocket.Response
-- Description: Response from Gremlin Server
-- Maintainer: Toshio Ito <debug.ito@gmail.com>
--
--
module Network.Greskell.WebSocket.Response
    ( -- * ResponseMessage
      ResponseMessage (..)
      -- * ResponseStatus
    , ResponseStatus (..)
      -- * ResponseResult
    , ResponseResult (..)
      -- * ResponseCode
    , ResponseCode (..)
    , codeToInt
    , codeFromInt
    , isTerminating
    , isSuccess
    , isClientSideError
    , isServerSideError
    ) where

import           Control.Applicative           ((<$>), (<*>))
import           Data.Aeson                    (FromJSON (..), Object, ToJSON (..),
                                                Value (Number, Object), defaultOptions,
                                                genericParseJSON)
import           Data.Greskell.GraphSON        (FromGraphSON (..), GValueBody (..), gsonValue,
                                                parseUnwrapAll, (.:))
import           Data.Greskell.GraphSON.GValue (gValueBody)
import           Data.Text                     (Text)
import           Data.UUID                     (UUID)
import           GHC.Generics                  (Generic)



-- | Response status code
data ResponseCode = Success | NoContent | PartialContent | Unauthorized | Authenticate | MalformedRequest | InvalidRequestArguments | ServerError | ScriptEvaluationError | ServerTimeout | ServerSerializationError deriving
    ( Bounded
    , Enum
    , Eq
    , Ord
    , Show
    )

codeToInt :: ResponseCode -> Int
codeToInt c = case c of
  Success                  -> 200
  NoContent                -> 204
  PartialContent           -> 206
  Unauthorized             -> 401
  Authenticate             -> 407
  MalformedRequest         -> 498
  InvalidRequestArguments  -> 499
  ServerError              -> 500
  ScriptEvaluationError    -> 597
  ServerTimeout            -> 598
  ServerSerializationError -> 599

codeFromInt :: Int -> Maybe ResponseCode
codeFromInt i = case i of
  200 -> Just Success
  204 -> Just NoContent
  206 -> Just PartialContent
  401 -> Just Unauthorized
  407 -> Just Authenticate
  498 -> Just MalformedRequest
  499 -> Just InvalidRequestArguments
  500 -> Just ServerError
  597 -> Just ScriptEvaluationError
  598 -> Just ServerTimeout
  599 -> Just ServerSerializationError
  _   -> Nothing

-- | Returns 'True' if the 'ResponseCode' is a terminating code.
isTerminating :: ResponseCode -> Bool
isTerminating PartialContent = False
isTerminating _              = True

isCodeClass :: Int -> ResponseCode -> Bool
isCodeClass n c = (codeToInt c `div` 100) == n

-- | Returns 'True' if the 'ResponseCode' is a success.
isSuccess :: ResponseCode -> Bool
isSuccess = isCodeClass 2

-- | Returns 'True' if the 'ResponseCode' is a client-side failure.
isClientSideError :: ResponseCode -> Bool
isClientSideError = isCodeClass 4

-- | Returns 'True' if the 'ResponseCode' is a server-side failure.
isServerSideError :: ResponseCode -> Bool
isServerSideError = isCodeClass 5

instance FromJSON ResponseCode where
  parseJSON (Number n) = maybe err return $ codeFromInt $ floor n
    where
      err = fail ("Unknown response code: " ++ show n)
  parseJSON v = fail ("Expected Number, but got " ++ show v)

instance FromGraphSON ResponseCode where
  parseGraphSON = parseUnwrapAll

instance ToJSON ResponseCode where
  toJSON = toJSON . codeToInt

-- | \"status\" field.
data ResponseStatus
  = ResponseStatus
      { code       :: !ResponseCode
      , message    :: !Text
      , attributes :: !Object
      }
  deriving (Eq, Generic, Show)

instance FromJSON ResponseStatus where
  parseJSON v = parseGraphSON =<< parseJSON v

instance FromGraphSON ResponseStatus where
  parseGraphSON gv = case gValueBody gv of
    GObject o ->
      ResponseStatus
      <$> o .: "code"
      <*> o .: "message"
      <*> o .: "attributes"
    gb -> fail ("Expected GObject, but got " ++ show gb)


-- | \"result\" field.
data ResponseResult s
  = ResponseResult
      { resultData :: !s
        -- ^ \"data\" field.
      , meta       :: !Object
      }
  deriving (Eq, Generic, Show)

instance FromGraphSON s => FromJSON (ResponseResult s) where
  parseJSON v = parseGraphSON =<< parseJSON v

instance FromGraphSON s => FromGraphSON (ResponseResult s) where
  parseGraphSON gv = case gValueBody gv of
    GObject o ->
      ResponseResult
      <$> o .: "data"
      <*> o .: "meta"
    gb -> fail ("Expected GObject, but got " ++ show gb)

instance Functor ResponseResult where
  fmap f rr = rr { resultData = f $ resultData rr }

-- | ResponseMessage object from Gremlin Server. See
-- <http://tinkerpop.apache.org/docs/current/dev/provider/>.
--
-- Type @s@ is the type of the response data.
data ResponseMessage s
  = ResponseMessage
      { requestId :: !UUID
      , status    :: !ResponseStatus
      , result    :: !(ResponseResult s)
      }
  deriving (Eq, Generic, Show)

instance FromGraphSON s => FromJSON (ResponseMessage s) where
  parseJSON v = parseGraphSON =<< parseJSON v

instance FromGraphSON s => FromGraphSON (ResponseMessage s) where
  parseGraphSON gv = case gValueBody gv of
    GObject o ->
      ResponseMessage
      <$> (o .: "requestId")
      <*> (o .: "status")
      <*> (o .: "result")
    gb -> fail ("Expected GObject, but got " ++ show gb)

instance Functor ResponseMessage where
  fmap f rm = rm { result = fmap f $ result rm }