{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Arbiter.Core.Admission
(
prefixedKeyText
, prefixedKeyToJSON
, prefixedKeyParseJSON
, splitPrefixedSuffix
, AdmissionPolicy (..)
, selectNone
, selectBy
, CollectFor (..)
, RegistryPolicies (..)
, registryPolicies
, registryPolicyTables
, effectivePolicyCol
, excludedAssignment
, policyUpsertSQL
) where
import Data.Aeson (Value, object, withObject, (.:), (.=))
import Data.Aeson.Types (Parser)
import Data.Proxy (Proxy (..))
import Data.Set (Set)
import Data.Set qualified as Set
import Data.Text (Text)
import Data.Text qualified as T
import GHC.TypeLits (KnownSymbol, symbolVal)
import NeatInterpolation (text)
import Arbiter.Core.QueueRegistry (JobPayloadRegistry, SpecName, SpecPayload)
import Arbiter.Core.Selector (Selector, field, usePolicy)
prefixedKeyText :: Text -> Text -> Text
prefixedKeyText :: Text -> Text -> Text
prefixedKeyText Text
prefix Text
suffix = Text
prefix Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
":" Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
suffix
prefixedKeyToJSON :: Text -> Text -> Value
prefixedKeyToJSON :: Text -> Text -> Value
prefixedKeyToJSON Text
prefix Text
suffix = [Pair] -> Value
object [Key
"prefix" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
prefix, Key
"suffix" Key -> Text -> Pair
forall v. ToJSON v => Key -> v -> Pair
forall e kv v. (KeyValue e kv, ToJSON v) => Key -> v -> kv
.= Text
suffix]
prefixedKeyParseJSON :: String -> (Text -> Text -> a) -> Value -> Parser a
prefixedKeyParseJSON :: forall a. String -> (Text -> Text -> a) -> Value -> Parser a
prefixedKeyParseJSON String
name Text -> Text -> a
mkKey = String -> (Object -> Parser a) -> Value -> Parser a
forall a. String -> (Object -> Parser a) -> Value -> Parser a
withObject String
name ((Object -> Parser a) -> Value -> Parser a)
-> (Object -> Parser a) -> Value -> Parser a
forall a b. (a -> b) -> a -> b
$ \Object
obj -> Text -> Text -> a
mkKey (Text -> Text -> a) -> Parser Text -> Parser (Text -> a)
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
"prefix" Parser (Text -> a) -> Parser Text -> Parser a
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
"suffix"
splitPrefixedSuffix :: Text -> Text -> Text
splitPrefixedSuffix :: Text -> Text -> Text
splitPrefixedSuffix Text
prefix Text
key = Int -> Text -> Text
T.drop (Text -> Int
T.length Text
prefix Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
1) Text
key
class (Ord p) => AdmissionPolicy p where
policyPrefixOf :: p -> Text
selectNone :: Selector p payload (Maybe key)
selectNone :: forall p payload key. Selector p payload (Maybe key)
selectNone = Maybe key -> Select (Prim p payload) (Maybe key)
forall a. a -> Select (Prim p payload) a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe key
forall a. Maybe a
Nothing
selectBy
:: (AdmissionPolicy p)
=> (Text -> Text -> key)
-> p
-> (payload -> Text)
-> Selector p payload (Maybe key)
selectBy :: forall p key payload.
AdmissionPolicy p =>
(Text -> Text -> key)
-> p -> (payload -> Text) -> Selector p payload (Maybe key)
selectBy Text -> Text -> key
mkKey p
pol payload -> Text
suffix =
(\p
policy Text
suffixText -> key -> Maybe key
forall a. a -> Maybe a
Just (Text -> Text -> key
mkKey (p -> Text
forall p. AdmissionPolicy p => p -> Text
policyPrefixOf p
policy) Text
suffixText)) (p -> Text -> Maybe key)
-> Select (Prim p payload) p
-> Select (Prim p payload) (Text -> Maybe key)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> p -> Select (Prim p payload) p
forall policy payload. policy -> Selector policy payload policy
usePolicy p
pol Select (Prim p payload) (Text -> Maybe key)
-> Select (Prim p payload) Text
-> Select (Prim p payload) (Maybe key)
forall a b.
Select (Prim p payload) (a -> b)
-> Select (Prim p payload) a -> Select (Prim p payload) b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (payload -> Text) -> Select (Prim p payload) Text
forall payload a policy.
(payload -> a) -> Selector policy payload a
field payload -> Text
suffix
effectivePolicyCol :: Text -> Text -> Text
effectivePolicyCol :: Text -> Text -> Text
effectivePolicyCol Text
alias Text
name = [text|COALESCE(${alias}.override_${name}, ${alias}.default_${name})|]
excludedAssignment :: Text -> Text
excludedAssignment :: Text -> Text
excludedAssignment Text
column = [text|${column} = EXCLUDED.${column}|]
policyUpsertSQL :: Text -> Text -> [(Text, Text)] -> Text
policyUpsertSQL :: Text -> Text -> [(Text, Text)] -> Text
policyUpsertSQL Text
policiesTable Text
prefixLit [(Text, Text)]
defaults =
let names :: Text
names = Text -> [Text] -> Text
T.intercalate Text
", " (((Text, Text) -> Text) -> [(Text, Text)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text, Text) -> Text
forall a b. (a, b) -> a
fst [(Text, Text)]
defaults)
values :: Text
values = Text -> [Text] -> Text
T.intercalate Text
", " (((Text, Text) -> Text) -> [(Text, Text)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text, Text) -> Text
forall a b. (a, b) -> b
snd [(Text, Text)]
defaults)
setClause :: Text
setClause = Text -> [Text] -> Text
T.intercalate Text
", " (((Text, Text) -> Text) -> [(Text, Text)] -> [Text]
forall a b. (a -> b) -> [a] -> [b]
map (Text -> Text
excludedAssignment (Text -> Text) -> ((Text, Text) -> Text) -> (Text, Text) -> Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Text, Text) -> Text
forall a b. (a, b) -> a
fst) [(Text, Text)]
defaults)
in [text|INSERT INTO ${policiesTable} (prefix_id, ${names}) VALUES (${prefixLit}, ${values}) ON CONFLICT (prefix_id) DO UPDATE SET ${setClause};|]
class CollectFor payload p where
collectFor :: Set p
class RegistryPolicies (registry :: JobPayloadRegistry) p where
registryTablePolicies :: [(Text, Set p)]
instance RegistryPolicies '[] p where
registryTablePolicies :: [(Text, Set p)]
registryTablePolicies = []
instance
(CollectFor (SpecPayload spec) p, KnownSymbol (SpecName spec), RegistryPolicies rest p)
=> RegistryPolicies (spec ': rest) p
where
registryTablePolicies :: [(Text, Set p)]
registryTablePolicies =
(String -> Text
T.pack (Proxy (SpecName spec) -> String
forall (n :: Symbol) (proxy :: Symbol -> *).
KnownSymbol n =>
proxy n -> String
symbolVal (forall {k} (t :: k). Proxy t
forall (t :: Symbol). Proxy t
Proxy @(SpecName spec))), forall {k} (payload :: k) p. CollectFor payload p => Set p
forall payload p. CollectFor payload p => Set p
collectFor @(SpecPayload spec) @p)
(Text, Set p) -> [(Text, Set p)] -> [(Text, Set p)]
forall a. a -> [a] -> [a]
: forall (registry :: JobPayloadRegistry) p.
RegistryPolicies registry p =>
[(Text, Set p)]
registryTablePolicies @rest @p
registryPolicies :: forall registry p. (Ord p, RegistryPolicies registry p) => Set p
registryPolicies :: forall (registry :: JobPayloadRegistry) p.
(Ord p, RegistryPolicies registry p) =>
Set p
registryPolicies = [Set p] -> Set p
forall (f :: * -> *) a. (Foldable f, Ord a) => f (Set a) -> Set a
Set.unions (((Text, Set p) -> Set p) -> [(Text, Set p)] -> [Set p]
forall a b. (a -> b) -> [a] -> [b]
map (Text, Set p) -> Set p
forall a b. (a, b) -> b
snd (forall (registry :: JobPayloadRegistry) p.
RegistryPolicies registry p =>
[(Text, Set p)]
registryTablePolicies @registry @p))
registryPolicyTables :: forall registry p. (RegistryPolicies registry p) => [(Text, Bool)]
registryPolicyTables :: forall (registry :: JobPayloadRegistry) p.
RegistryPolicies registry p =>
[(Text, Bool)]
registryPolicyTables = ((Text, Set p) -> (Text, Bool))
-> [(Text, Set p)] -> [(Text, Bool)]
forall a b. (a -> b) -> [a] -> [b]
map ((Set p -> Bool) -> (Text, Set p) -> (Text, Bool)
forall a b. (a -> b) -> (Text, a) -> (Text, b)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Bool -> Bool
not (Bool -> Bool) -> (Set p -> Bool) -> Set p -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Set p -> Bool
forall a. Set a -> Bool
Set.null)) (forall (registry :: JobPayloadRegistry) p.
RegistryPolicies registry p =>
[(Text, Set p)]
registryTablePolicies @registry @p)