IPv6DB-0.3.3.1: app/Queries.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RecordWildCards #-}
module Queries where
import Control.Monad (zipWithM)
import Control.Monad.Reader
import Data.Aeson as A
import qualified Data.Aeson.KeyMap as KM
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BSL
import Data.HashMap.Lazy (fromList)
import Data.Maybe (fromJust)
import qualified Data.Text as T
import Data.Text.Encoding
import Database.Redis as R hiding (decode)
import Network.HTTP.Types (StdMethod (..))
import Network.IPv6DB.Types
import Text.IPv6Addr
import Types
fromEntries :: (MonadReader Env f, MonadIO f)
=> Entries
-> [Maybe BS.ByteString]
-> f BSL.ByteString
fromEntries (Entries ents) msrcs =
encode <$> zipWithM toJson ents msrcs
where
toJson Entry{..} (Just src) = do
Env{..} <- ask
liftIO (buildResource redisConn list address src)
toJson Entry{..} Nothing =
return (ResourceError list address "Resource Not Found")
fromAddresses :: (MonadReader Env f, MonadIO f)
=> T.Text
-> Addresses
-> [Maybe BS.ByteString]
-> f BSL.ByteString
fromAddresses list (Addresses addrs) msrcs =
encode <$> zipWithM toJson addrs msrcs
where
toJson addr (Just src) = do
Env{..} <- ask
liftIO (buildResource redisConn list addr src)
toJson addr Nothing =
return (ResourceError list addr "Resource Not Found")
buildResource :: Connection
-> T.Text
-> IPv6Addr
-> BS.ByteString
-> IO Resource
buildResource conn list (IPv6Addr addr) src = do
mttl <- ttlSource conn list addr
return (fromJust $ toResource list addr mttl src)
setSource ::Connection -> StdMethod -> Resource -> IO RedisResponse
setSource _ _ ResourceError{} = undefined
setSource conn mtd Resource{ttl=ttlr,..} = do
er <- runRedis conn $ setOpts
(toKey list $ unIPv6Addr address)
(BSL.toStrict $ encode source)
SetOpts
{ setSeconds = ttlr
, setMilliseconds = Nothing
, setCondition =
case mtd of
PUT -> Just Xx
POST -> Just Nx
_ -> Nothing
}
return $
case er of
Right s ->
case s of
Ok -> RedisOk
Status status -> toRedisError list address status
Pong -> toRedisError list address "Ping!"
Left r ->
case r of
R.Error err -> toRedisError list address err
R.Bulk Nothing ->
case mtd of
PUT ->
toRedisError
list
address
"The Resource Doesn't Exist Yet (Use POST To Create It)"
POST ->
toRedisError
list
address
"The Resource Already Exists (Use PUT To Replace It)"
_ ->
toRedisError
list
address
"HTTP Method Not Handled"
R.Bulk (Just bs) -> toRedisError list address bs
_ ->
toRedisError
list
address
"Undefined Redis Error"
toRedisError :: T.Text
-> IPv6Addr
-> BS.ByteString
-> RedisResponse
toRedisError list addr err =
RedisError
{ entry = toEntry list addr
, error = err
}
ttlSource :: Connection
-> T.Text
-> T.Text
-> IO (Maybe Integer)
ttlSource conn list addr = do
ettl <- R.runRedis conn (R.ttl $ toKey list addr)
return $
case ettl of
Right i ->
if i > 0
then Just i
else Nothing
Left _ -> Nothing
getSource :: RedisCtx m f
=> T.Text
-> T.Text
-> m (f (Maybe BS.ByteString))
getSource list addr = get (toKey list addr)
delSource :: RedisCtx m f
=> T.Text
-> T.Text
-> m (f Integer)
delSource list addr = del [ toKey list addr ]
toResource :: T.Text
-> T.Text
-> Maybe Integer
-> BS.ByteString
-> Maybe Resource
toResource list addr mi bs =
decode (BSL.fromStrict bs) >>= \src ->
Just
Resource
{ list = list
, address = IPv6Addr addr
, ttl = mi
, source = Source src
}
maybeResource :: Value
-> [(T.Text,Value)]
-> Maybe Resource
maybeResource v prs =
case v of
Object hm -> do
let hm' =
if KM.member "ttl" hm
then hm
else KM.insert "ttl" Null hm
case fromJSON (Object $ KM.union hm' $ KM.fromHashMapText $ fromList prs) of
A.Success r -> Just r
A.Error _ -> Nothing
_ -> Nothing
getByAddresses :: RedisCtx m f
=> T.Text
-> Addresses
-> m (f [Maybe BS.ByteString])
getByAddresses list addrs =
mget (addressesToKeys list addrs)
getByEntries :: RedisCtx m f
=> Entries
-> m (f [Maybe BS.ByteString])
getByEntries ents = mget (fromEnts ents)
delByAddresses :: RedisCtx m f
=> T.Text
-> Addresses -> m (f Integer)
delByAddresses list addrs =
del (addressesToKeys list addrs)
delByEntries :: RedisCtx m f
=> Entries
-> m (f Integer)
delByEntries ents = del (fromEnts ents)
addressesToKeys :: T.Text
-> Addresses
-> [BS.ByteString]
addressesToKeys list (Addresses addrs) =
toKey list . unIPv6Addr <$> addrs
fromEnts :: Entries -> [BS.ByteString]
fromEnts (Entries ents) =
(\Entry{..} -> toKey list (unIPv6Addr address)) <$> ents
toEntry :: T.Text -> IPv6Addr -> Entry
toEntry list address = Entry { list = list, address = address }
toKey :: T.Text -> T.Text -> BS.ByteString
toKey list addr =
encodeUtf8 (list <> listAddressSeparator <> addr)
listAddressSeparator :: T.Text
listAddressSeparator = "/"