module Hercules.Formats.Secret where import Data.Aeson import qualified Data.Aeson.Key as AK import qualified Data.Aeson.KeyMap as AM import qualified Data.Aeson.Types as A import Data.Foldable (toList) import qualified Data.HashMap.Strict as HM import Hercules.API.Prelude import Hercules.Formats.Common ( noVersion, withKind, withVersions, ) data Condition = Or [Condition] | And [Condition] | IsDefaultBranch | IsBranch Text | IsTag | IsRepo Text | IsOwner Text | Const Bool deriving (Generic, Eq, Read, Show) instance ToJSON Condition where toJSON (Or a) = object ["or" .= a] toJSON (And a) = object ["and" .= a] toJSON IsDefaultBranch = String "isDefaultBranch" toJSON IsTag = String "isTag" toJSON (IsBranch a) = object ["isBranch" .= a] toJSON (IsRepo a) = object ["isRepo" .= a] toJSON (IsOwner a) = object ["isOwner" .= a] toJSON (Const b) = Bool b instance FromJSON Condition where parseJSON (String "isTag") = pure IsTag parseJSON (String "isDefaultBranch") = pure IsDefaultBranch parseJSON (Object o) = case AM.toList o of [] -> fail "The empty object does not represent a Condition." [(k, v)] -> case HM.lookup (AK.toText k) taggedConditionParsers of Nothing -> fail $ "The field name in a Condition object must be one of " <> show (map fst (HM.toList taggedConditionParsers)) Just p -> p v _ -> fail "A Condition object must contain a single field." parseJSON (Bool b) = pure (Const b) parseJSON _ = fail "Expected Object, String or true." taggedConditionParsers :: HM.HashMap Text (Value -> A.Parser Condition) taggedConditionParsers = HM.fromList [ ( "or", \v -> do params <- parseJSON v Or <$> traverse parseJSON params ), ( "and", \v -> do params <- parseJSON v And <$> traverse parseJSON params ), ("isBranch", fmap IsBranch . parseJSON), ("isRepo", fmap IsRepo . parseJSON), ("isOwner", fmap IsOwner . parseJSON) ] -- | Arbitrary secret like keys, tokens, passwords etc. data Secret = Secret { data_ :: Map Text Value, condition :: Maybe Condition } instance ToJSON Secret where toJSON a = object (["kind" .= String "Secret", "data" .= data_ a] ++ ["condition" .= x | x <- toList (condition a)]) toEncoding a = pairs ("kind" .= String "Secret" <> "data" .= data_ a <> foldMap ("condition" .=) (condition a)) instance FromJSON Secret where parseJSON = withKind "Secret" $ withVersions [ noVersion $ \o -> Secret <$> o .: "data" <*> o .:? "condition" ]