-- Copyright (C) 2016 Red Hat, Inc. -- -- This library is free software; you can redistribute it and/or -- modify it under the terms of the GNU Lesser General Public -- License as published by the Free Software Foundation; either -- version 2.1 of the License, or (at your option) any later version. -- -- This library is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -- Lesser General Public License for more details. -- -- You should have received a copy of the GNU Lesser General Public -- License along with this library; if not, see . {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module BDCS.KeyValue(findKeyValue, formatKeyValue, getKeyValue, insertKeyValue, keyValueListToJSON) where import Control.Monad.IO.Class(MonadIO) import Data.Aeson((.=), object, toJSON) import Data.Aeson.Types(KeyValue) import Data.List(partition) import qualified Data.Map.Strict as Map import qualified Data.Text as T import Database.Esqueleto import BDCS.DB import BDCS.KeyType {-# ANN findKeyValue ("HLint: ignore Use ." :: String) #-} findKeyValue :: MonadIO m => KeyType -> Maybe T.Text -> Maybe T.Text -> SqlPersistT m (Maybe (Key KeyVal)) findKeyValue k v e = firstKeyResult $ select $ from $ \kv -> do where_ $ kv ^. KeyValKey_value ==. val k &&. kv ^. KeyValVal_value ==? v &&. kv ^. KeyValExt_value ==? e limit 1 return $ kv ^. KeyValId formatKeyValue :: KeyVal -> T.Text formatKeyValue KeyVal{..} = let rhs = case (keyValVal_value, keyValExt_value) of (Just v, Nothing) -> T.concat [ " = ", v ] (Just v, Just e) -> T.concat [ " = (", v, ", ", e, ")" ] _ -> "" in T.concat [ T.pack $ show keyValKey_value, rhs ] getKeyValue :: MonadIO m => Key KeyVal -> SqlPersistT m (Maybe KeyVal) getKeyValue key = firstEntityResult $ select $ from $ \kv -> do where_ $ kv ^. KeyValId ==. val key limit 1 return kv insertKeyValue :: MonadIO m => KeyType -> Maybe T.Text -> Maybe T.Text -> SqlPersistT m (Key KeyVal) insertKeyValue k v e = insert (KeyVal k v e) keyValueListToJSON :: KeyValue t => [KeyVal] -> [t] keyValueListToJSON lst = let isLabelKey (LabelKey _) = True isLabelKey _ = False -- We want to handle LabelKeys differently from all other KeyTypes, so first let's sort them out. (labelKvs, otherKvs) = partition (isLabelKey . keyValKey_value) lst -- Convert LabelKeys into tuples of ("labels", json). All LabelKeys will have the same first value -- in their tuple - the string "labels". This assumes that LabelKeys do not store values or extended -- values. If they start doing that, this will have to change. labelVals = map (\kv -> (T.pack "labels", [toJSON $ keyValKey_value kv])) labelKvs -- Convert all other KeyTypes into tuples of (key, json). otherVals = map (\kv -> (asText $ keyValKey_value kv, [toJSON kv])) otherKvs -- A single list can have many KeyVals with the same key (think about rpm-provides and requires -- especially). We use an intermediate map to turn it into a list of (key, [json1, json2, ...]) tuples. -- Both types get handled the same way here. labelMap = Map.fromListWith (++) labelVals otherMap = Map.fromListWith (++) otherVals -- If there's only one KeyVal for a given key, strip the list out before converting it to a -- json list object. Otherwise, everything will end up in a list. -- -- On the other hand, we don't do anything to LabelKeys. This means labels will always end up -- in a list named "labels". pairs = map (\(k, v) -> case v of [hd] -> k .= hd _ -> k .= v) (Map.toList otherMap) ++ map (uncurry (.=)) (Map.toList labelMap) in [T.pack "keyvals" .= object pairs]