{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE DuplicateRecordFields #-}
{-# LANGUAGE OverloadedStrings #-}

-- | View and patch types for the rate-limit management/observability API.
module Arbiter.Core.RateLimit.Stats
  ( RateLimitPolicyView (..)
  , RateLimitBucketView (..)
  , RateLimitPolicyUpdate (..)
  ) where

import Data.Aeson (FromJSON (..), ToJSON (..), object, withObject, (.:), (.:?), (.=))
import Data.Aeson qualified as Aeson
import Data.Int (Int64)
import Data.Text (Text)
import Data.Time (UTCTime)
import GHC.Generics (Generic)

import Arbiter.Core.Json (explicitOptionalField, patchOptions)

-- | A policy with its default and override params plus live bucket and throttle
-- stats. The effective param is @override@ when set, else @default@.
data RateLimitPolicyView = RateLimitPolicyView
  { RateLimitPolicyView -> Text
prefix :: Text
  , RateLimitPolicyView -> Double
defaultMaxTokens :: Double
  , RateLimitPolicyView -> Double
defaultRefillAmount :: Double
  , RateLimitPolicyView -> Double
defaultInterval :: Double
  , RateLimitPolicyView -> Maybe Double
overrideMaxTokens :: Maybe Double
  , RateLimitPolicyView -> Maybe Double
overrideRefillAmount :: Maybe Double
  , RateLimitPolicyView -> Maybe Double
overrideInterval :: Maybe Double
  , RateLimitPolicyView -> Int64
bucketCount :: Int64
  , RateLimitPolicyView -> Int64
throttledCount :: Int64
  , RateLimitPolicyView -> Maybe Double
minTokens :: Maybe Double
  , RateLimitPolicyView -> Maybe Double
avgTokens :: Maybe Double
  }
  deriving stock (RateLimitPolicyView -> RateLimitPolicyView -> Bool
(RateLimitPolicyView -> RateLimitPolicyView -> Bool)
-> (RateLimitPolicyView -> RateLimitPolicyView -> Bool)
-> Eq RateLimitPolicyView
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RateLimitPolicyView -> RateLimitPolicyView -> Bool
== :: RateLimitPolicyView -> RateLimitPolicyView -> Bool
$c/= :: RateLimitPolicyView -> RateLimitPolicyView -> Bool
/= :: RateLimitPolicyView -> RateLimitPolicyView -> Bool
Eq, (forall x. RateLimitPolicyView -> Rep RateLimitPolicyView x)
-> (forall x. Rep RateLimitPolicyView x -> RateLimitPolicyView)
-> Generic RateLimitPolicyView
forall x. Rep RateLimitPolicyView x -> RateLimitPolicyView
forall x. RateLimitPolicyView -> Rep RateLimitPolicyView x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RateLimitPolicyView -> Rep RateLimitPolicyView x
from :: forall x. RateLimitPolicyView -> Rep RateLimitPolicyView x
$cto :: forall x. Rep RateLimitPolicyView x -> RateLimitPolicyView
to :: forall x. Rep RateLimitPolicyView x -> RateLimitPolicyView
Generic, Int -> RateLimitPolicyView -> ShowS
[RateLimitPolicyView] -> ShowS
RateLimitPolicyView -> String
(Int -> RateLimitPolicyView -> ShowS)
-> (RateLimitPolicyView -> String)
-> ([RateLimitPolicyView] -> ShowS)
-> Show RateLimitPolicyView
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RateLimitPolicyView -> ShowS
showsPrec :: Int -> RateLimitPolicyView -> ShowS
$cshow :: RateLimitPolicyView -> String
show :: RateLimitPolicyView -> String
$cshowList :: [RateLimitPolicyView] -> ShowS
showList :: [RateLimitPolicyView] -> ShowS
Show)
  deriving anyclass (Maybe RateLimitPolicyView
Value -> Parser [RateLimitPolicyView]
Value -> Parser RateLimitPolicyView
(Value -> Parser RateLimitPolicyView)
-> (Value -> Parser [RateLimitPolicyView])
-> Maybe RateLimitPolicyView
-> FromJSON RateLimitPolicyView
forall a.
(Value -> Parser a)
-> (Value -> Parser [a]) -> Maybe a -> FromJSON a
$cparseJSON :: Value -> Parser RateLimitPolicyView
parseJSON :: Value -> Parser RateLimitPolicyView
$cparseJSONList :: Value -> Parser [RateLimitPolicyView]
parseJSONList :: Value -> Parser [RateLimitPolicyView]
$comittedField :: Maybe RateLimitPolicyView
omittedField :: Maybe RateLimitPolicyView
FromJSON, [RateLimitPolicyView] -> Value
[RateLimitPolicyView] -> Encoding
RateLimitPolicyView -> Bool
RateLimitPolicyView -> Value
RateLimitPolicyView -> Encoding
(RateLimitPolicyView -> Value)
-> (RateLimitPolicyView -> Encoding)
-> ([RateLimitPolicyView] -> Value)
-> ([RateLimitPolicyView] -> Encoding)
-> (RateLimitPolicyView -> Bool)
-> ToJSON RateLimitPolicyView
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> (a -> Bool)
-> ToJSON a
$ctoJSON :: RateLimitPolicyView -> Value
toJSON :: RateLimitPolicyView -> Value
$ctoEncoding :: RateLimitPolicyView -> Encoding
toEncoding :: RateLimitPolicyView -> Encoding
$ctoJSONList :: [RateLimitPolicyView] -> Value
toJSONList :: [RateLimitPolicyView] -> Value
$ctoEncodingList :: [RateLimitPolicyView] -> Encoding
toEncodingList :: [RateLimitPolicyView] -> Encoding
$comitField :: RateLimitPolicyView -> Bool
omitField :: RateLimitPolicyView -> Bool
ToJSON)

-- | A single key's bucket: current tokens, effective max, and fill fraction.
data RateLimitBucketView = RateLimitBucketView
  { RateLimitBucketView -> Text
rateLimitKey :: Text
  , RateLimitBucketView -> Text
policyPrefix :: Text
  , RateLimitBucketView -> Double
tokens :: Double
  , RateLimitBucketView -> Double
maxTokens :: Double
  , RateLimitBucketView -> Maybe Double
fillFraction :: Maybe Double
  , RateLimitBucketView -> UTCTime
lastRefill :: UTCTime
  }
  deriving stock (RateLimitBucketView -> RateLimitBucketView -> Bool
(RateLimitBucketView -> RateLimitBucketView -> Bool)
-> (RateLimitBucketView -> RateLimitBucketView -> Bool)
-> Eq RateLimitBucketView
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RateLimitBucketView -> RateLimitBucketView -> Bool
== :: RateLimitBucketView -> RateLimitBucketView -> Bool
$c/= :: RateLimitBucketView -> RateLimitBucketView -> Bool
/= :: RateLimitBucketView -> RateLimitBucketView -> Bool
Eq, (forall x. RateLimitBucketView -> Rep RateLimitBucketView x)
-> (forall x. Rep RateLimitBucketView x -> RateLimitBucketView)
-> Generic RateLimitBucketView
forall x. Rep RateLimitBucketView x -> RateLimitBucketView
forall x. RateLimitBucketView -> Rep RateLimitBucketView x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RateLimitBucketView -> Rep RateLimitBucketView x
from :: forall x. RateLimitBucketView -> Rep RateLimitBucketView x
$cto :: forall x. Rep RateLimitBucketView x -> RateLimitBucketView
to :: forall x. Rep RateLimitBucketView x -> RateLimitBucketView
Generic, Int -> RateLimitBucketView -> ShowS
[RateLimitBucketView] -> ShowS
RateLimitBucketView -> String
(Int -> RateLimitBucketView -> ShowS)
-> (RateLimitBucketView -> String)
-> ([RateLimitBucketView] -> ShowS)
-> Show RateLimitBucketView
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RateLimitBucketView -> ShowS
showsPrec :: Int -> RateLimitBucketView -> ShowS
$cshow :: RateLimitBucketView -> String
show :: RateLimitBucketView -> String
$cshowList :: [RateLimitBucketView] -> ShowS
showList :: [RateLimitBucketView] -> ShowS
Show)

instance ToJSON RateLimitBucketView where
  toJSON :: RateLimitBucketView -> Value
toJSON RateLimitBucketView
view =
    [Pair] -> Value
object
      [ Key
"key" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= RateLimitBucketView -> Text
rateLimitKey RateLimitBucketView
view
      , Key
"prefix" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= RateLimitBucketView -> Text
policyPrefix RateLimitBucketView
view
      , Key
"tokens" Key -> Double -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= RateLimitBucketView -> Double
tokens RateLimitBucketView
view
      , Key
"maxTokens" Key -> Double -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= RateLimitBucketView -> Double
maxTokens RateLimitBucketView
view
      , Key
"fillFraction" Key -> Maybe Double -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= RateLimitBucketView -> Maybe Double
fillFraction RateLimitBucketView
view
      , Key
"lastRefill" Key -> UTCTime -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= RateLimitBucketView -> UTCTime
lastRefill RateLimitBucketView
view
      ]

instance FromJSON RateLimitBucketView where
  parseJSON :: Value -> Parser RateLimitBucketView
parseJSON = String
-> (Object -> Parser RateLimitBucketView)
-> Value
-> Parser RateLimitBucketView
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"RateLimitBucketView" ((Object -> Parser RateLimitBucketView)
 -> Value -> Parser RateLimitBucketView)
-> (Object -> Parser RateLimitBucketView)
-> Value
-> Parser RateLimitBucketView
forall a b. (a -> b) -> a -> b
$ \Object
obj ->
    Text
-> Text
-> Double
-> Double
-> Maybe Double
-> UTCTime
-> RateLimitBucketView
RateLimitBucketView
      (Text
 -> Text
 -> Double
 -> Double
 -> Maybe Double
 -> UTCTime
 -> RateLimitBucketView)
-> Parser Text
-> Parser
     (Text
      -> Double
      -> Double
      -> Maybe Double
      -> UTCTime
      -> RateLimitBucketView)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
obj Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"key"
      Parser
  (Text
   -> Double
   -> Double
   -> Maybe Double
   -> UTCTime
   -> RateLimitBucketView)
-> Parser Text
-> Parser
     (Double
      -> Double -> Maybe Double -> UTCTime -> RateLimitBucketView)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
obj Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"prefix"
      Parser
  (Double
   -> Double -> Maybe Double -> UTCTime -> RateLimitBucketView)
-> Parser Double
-> Parser
     (Double -> Maybe Double -> UTCTime -> RateLimitBucketView)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
obj Object -> Key -> Parser Double
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"tokens"
      Parser (Double -> Maybe Double -> UTCTime -> RateLimitBucketView)
-> Parser Double
-> Parser (Maybe Double -> UTCTime -> RateLimitBucketView)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
obj Object -> Key -> Parser Double
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"maxTokens"
      Parser (Maybe Double -> UTCTime -> RateLimitBucketView)
-> Parser (Maybe Double) -> Parser (UTCTime -> RateLimitBucketView)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
obj Object -> Key -> Parser (Maybe Double)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"fillFraction"
      Parser (UTCTime -> RateLimitBucketView)
-> Parser UTCTime -> Parser RateLimitBucketView
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
obj Object -> Key -> Parser UTCTime
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"lastRefill"

-- | A patch over a policy's override params. Per field: 'Nothing' leaves it
-- unchanged, @Just Nothing@ clears the override (reverts to the default), and
-- @Just (Just v)@ sets it.
data RateLimitPolicyUpdate = RateLimitPolicyUpdate
  { RateLimitPolicyUpdate -> Maybe (Maybe Double)
overrideMaxTokens :: Maybe (Maybe Double)
  , RateLimitPolicyUpdate -> Maybe (Maybe Double)
overrideRefillAmount :: Maybe (Maybe Double)
  , RateLimitPolicyUpdate -> Maybe (Maybe Double)
overrideInterval :: Maybe (Maybe Double)
  }
  deriving stock (RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool
(RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool)
-> (RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool)
-> Eq RateLimitPolicyUpdate
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool
== :: RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool
$c/= :: RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool
/= :: RateLimitPolicyUpdate -> RateLimitPolicyUpdate -> Bool
Eq, (forall x. RateLimitPolicyUpdate -> Rep RateLimitPolicyUpdate x)
-> (forall x. Rep RateLimitPolicyUpdate x -> RateLimitPolicyUpdate)
-> Generic RateLimitPolicyUpdate
forall x. Rep RateLimitPolicyUpdate x -> RateLimitPolicyUpdate
forall x. RateLimitPolicyUpdate -> Rep RateLimitPolicyUpdate x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. RateLimitPolicyUpdate -> Rep RateLimitPolicyUpdate x
from :: forall x. RateLimitPolicyUpdate -> Rep RateLimitPolicyUpdate x
$cto :: forall x. Rep RateLimitPolicyUpdate x -> RateLimitPolicyUpdate
to :: forall x. Rep RateLimitPolicyUpdate x -> RateLimitPolicyUpdate
Generic, Int -> RateLimitPolicyUpdate -> ShowS
[RateLimitPolicyUpdate] -> ShowS
RateLimitPolicyUpdate -> String
(Int -> RateLimitPolicyUpdate -> ShowS)
-> (RateLimitPolicyUpdate -> String)
-> ([RateLimitPolicyUpdate] -> ShowS)
-> Show RateLimitPolicyUpdate
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> RateLimitPolicyUpdate -> ShowS
showsPrec :: Int -> RateLimitPolicyUpdate -> ShowS
$cshow :: RateLimitPolicyUpdate -> String
show :: RateLimitPolicyUpdate -> String
$cshowList :: [RateLimitPolicyUpdate] -> ShowS
showList :: [RateLimitPolicyUpdate] -> ShowS
Show)

instance ToJSON RateLimitPolicyUpdate where
  toJSON :: RateLimitPolicyUpdate -> Value
toJSON = Options -> RateLimitPolicyUpdate -> Value
forall a.
(Generic a, GToJSON' Value Zero (Rep a)) =>
Options -> a -> Value
Aeson.genericToJSON Options
patchOptions
  toEncoding :: RateLimitPolicyUpdate -> Encoding
toEncoding = Options -> RateLimitPolicyUpdate -> Encoding
forall a.
(Generic a, GToJSON' Encoding Zero (Rep a)) =>
Options -> a -> Encoding
Aeson.genericToEncoding Options
patchOptions

-- | Hand-written. A missing key leaves the field unchanged. An explicit @null@
-- clears the override.
instance FromJSON RateLimitPolicyUpdate where
  parseJSON :: Value -> Parser RateLimitPolicyUpdate
parseJSON = String
-> (Object -> Parser RateLimitPolicyUpdate)
-> Value
-> Parser RateLimitPolicyUpdate
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
"RateLimitPolicyUpdate" ((Object -> Parser RateLimitPolicyUpdate)
 -> Value -> Parser RateLimitPolicyUpdate)
-> (Object -> Parser RateLimitPolicyUpdate)
-> Value
-> Parser RateLimitPolicyUpdate
forall a b. (a -> b) -> a -> b
$ \Object
obj ->
    Maybe (Maybe Double)
-> Maybe (Maybe Double)
-> Maybe (Maybe Double)
-> RateLimitPolicyUpdate
RateLimitPolicyUpdate
      (Maybe (Maybe Double)
 -> Maybe (Maybe Double)
 -> Maybe (Maybe Double)
 -> RateLimitPolicyUpdate)
-> Parser (Maybe (Maybe Double))
-> Parser
     (Maybe (Maybe Double)
      -> Maybe (Maybe Double) -> RateLimitPolicyUpdate)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object -> Text -> Parser (Maybe (Maybe Double))
forall a. FromJSON a => Object -> Text -> Parser (Maybe (Maybe a))
explicitOptionalField Object
obj Text
"overrideMaxTokens"
      Parser
  (Maybe (Maybe Double)
   -> Maybe (Maybe Double) -> RateLimitPolicyUpdate)
-> Parser (Maybe (Maybe Double))
-> Parser (Maybe (Maybe Double) -> RateLimitPolicyUpdate)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object -> Text -> Parser (Maybe (Maybe Double))
forall a. FromJSON a => Object -> Text -> Parser (Maybe (Maybe a))
explicitOptionalField Object
obj Text
"overrideRefillAmount"
      Parser (Maybe (Maybe Double) -> RateLimitPolicyUpdate)
-> Parser (Maybe (Maybe Double)) -> Parser RateLimitPolicyUpdate
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object -> Text -> Parser (Maybe (Maybe Double))
forall a. FromJSON a => Object -> Text -> Parser (Maybe (Maybe a))
explicitOptionalField Object
obj Text
"overrideInterval"