dynamodb-simple 0.5.0.0 → 0.6.0.0
raw patch · 8 files changed
+117/−89 lines, 8 filesdep ~amazonka-dynamodbdep ~basePVP ok
version bump matches the API change (PVP)
Dependency ranges changed: amazonka-dynamodb, base
API changes (from Hackage documentation)
- Database.DynamoDB.Types: instance (GHC.Classes.Ord a, Database.DynamoDB.Types.DynamoScalar v a) => Database.DynamoDB.Types.DynamoEncodable (Data.Set.Base.Set a)
+ Database.DynamoDB.Types: dDecodeEither :: DynamoEncodable a => Maybe AttributeValue -> Either Text a
+ Database.DynamoDB.Types: instance (GHC.Classes.Ord a, Database.DynamoDB.Types.DynamoScalar v a) => Database.DynamoDB.Types.DynamoEncodable (Data.Set.Internal.Set a)
- Database.DynamoDB: (<!:>) :: IsText key => Column (HashMap key typ) TypColumn col -> key -> Column typ TypColumn col
+ Database.DynamoDB: (<!:>) :: IsText key => Column (HashMap key typ) 'TypColumn col -> key -> Column typ 'TypColumn col
- Database.DynamoDB: (<!>) :: Column [typ] TypColumn col -> Int -> Column typ TypColumn col
+ Database.DynamoDB: (<!>) :: Column [typ] 'TypColumn col -> Int -> Column typ 'TypColumn col
- Database.DynamoDB: (<.>) :: (InCollection col2 (UnMaybe typ) NestedPath) => Column typ TypColumn col1 -> Column typ2 TypColumn col2 -> Column typ2 TypColumn col1
+ Database.DynamoDB: (<.>) :: (InCollection col2 (UnMaybe typ) 'NestedPath) => Column typ 'TypColumn col1 -> Column typ2 'TypColumn col2 -> Column typ2 'TypColumn col1
- Database.DynamoDB: class (DynamoCollection a WithRange t, HasPrimaryKey a WithRange t) => CanQuery a (t :: TableType) hash range | a -> hash range
+ Database.DynamoDB: class (DynamoCollection a 'WithRange t, HasPrimaryKey a 'WithRange t) => CanQuery a (t :: TableType) hash range | a -> hash range
- Database.DynamoDB: class DynamoCollection a r IsIndex => DynamoIndex a parent (r :: RangeType) | a -> parent r
+ Database.DynamoDB: class DynamoCollection a r 'IsIndex => DynamoIndex a parent (r :: RangeType) | a -> parent r
- Database.DynamoDB: class (DynamoCollection a r IsTable, HasPrimaryKey a r IsTable) => DynamoTable a (r :: RangeType) | a -> r where dPutItem = defaultPutItem
+ Database.DynamoDB: class (DynamoCollection a r 'IsTable, HasPrimaryKey a r 'IsTable) => DynamoTable a (r :: RangeType) | a -> r
- Database.DynamoDB: qConsistentRead :: forall a_a18KQ hash_a18KR range_a18KS. Lens' (QueryOpts a_a18KQ hash_a18KR range_a18KS) Consistency
+ Database.DynamoDB: qConsistentRead :: forall a_a16xD hash_a16xE range_a16xF. Lens' (QueryOpts a_a16xD hash_a16xE range_a16xF) Consistency
- Database.DynamoDB: qDirection :: forall a_a18KQ hash_a18KR range_a18KS. Lens' (QueryOpts a_a18KQ hash_a18KR range_a18KS) Direction
+ Database.DynamoDB: qDirection :: forall a_a16xD hash_a16xE range_a16xF. Lens' (QueryOpts a_a16xD hash_a16xE range_a16xF) Direction
- Database.DynamoDB: qFilterCondition :: forall a_a18KQ hash_a18KR range_a18KS a_a18Nt. Lens (QueryOpts a_a18KQ hash_a18KR range_a18KS) (QueryOpts a_a18Nt hash_a18KR range_a18KS) (Maybe (FilterCondition a_a18KQ)) (Maybe (FilterCondition a_a18Nt))
+ Database.DynamoDB: qFilterCondition :: forall a_a16xD hash_a16xE range_a16xF a_a16An. Lens (QueryOpts a_a16xD hash_a16xE range_a16xF) (QueryOpts a_a16An hash_a16xE range_a16xF) (Maybe (FilterCondition a_a16xD)) (Maybe (FilterCondition a_a16An))
- Database.DynamoDB: qHashKey :: forall a_a18KQ hash_a18KR range_a18KS. Lens' (QueryOpts a_a18KQ hash_a18KR range_a18KS) hash_a18KR
+ Database.DynamoDB: qHashKey :: forall a_a16xD hash_a16xE range_a16xF. Lens' (QueryOpts a_a16xD hash_a16xE range_a16xF) hash_a16xE
- Database.DynamoDB: qLimit :: forall a_a18KQ hash_a18KR range_a18KS. Lens' (QueryOpts a_a18KQ hash_a18KR range_a18KS) (Maybe Natural)
+ Database.DynamoDB: qLimit :: forall a_a16xD hash_a16xE range_a16xF. Lens' (QueryOpts a_a16xD hash_a16xE range_a16xF) (Maybe Natural)
- Database.DynamoDB: qRangeCondition :: forall a_a18KQ hash_a18KR range_a18KS. Lens' (QueryOpts a_a18KQ hash_a18KR range_a18KS) (Maybe (RangeOper range_a18KS))
+ Database.DynamoDB: qRangeCondition :: forall a_a16xD hash_a16xE range_a16xF. Lens' (QueryOpts a_a16xD hash_a16xE range_a16xF) (Maybe (RangeOper range_a16xF))
- Database.DynamoDB: qStartKey :: forall a_a18KQ hash_a18KR range_a18KS. Lens' (QueryOpts a_a18KQ hash_a18KR range_a18KS) (Maybe (hash_a18KR, range_a18KS))
+ Database.DynamoDB: qStartKey :: forall a_a16xD hash_a16xE range_a16xF. Lens' (QueryOpts a_a16xD hash_a16xE range_a16xF) (Maybe (hash_a16xE, range_a16xF))
- Database.DynamoDB: query :: forall a t m range hash. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Int -> m ([a], Maybe (PrimaryKey a WithRange))
+ Database.DynamoDB: query :: forall a t m range hash. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Int -> m ([a], Maybe (PrimaryKey a 'WithRange))
- Database.DynamoDB: queryOverIndex :: forall a t m v1 v2 hash r2 range rest parent. (CanQuery a t hash range, MonadAWS m, Code a ~ '[hash : (range : rest)], DynamoIndex a parent WithRange, ContainsTableKey a parent (PrimaryKey parent r2), DynamoTable parent r2, DynamoScalar v1 hash, DynamoScalar v2 range) => Proxy a -> QueryOpts a hash range -> Source m parent
+ Database.DynamoDB: queryOverIndex :: forall a t m v1 v2 hash r2 range rest parent. (CanQuery a t hash range, MonadAWS m, Code a ~ '[hash : (range : rest)], DynamoIndex a parent 'WithRange, ContainsTableKey a parent (PrimaryKey parent r2), DynamoTable parent r2, DynamoScalar v1 hash, DynamoScalar v2 range) => Proxy a -> QueryOpts a hash range -> Source m parent
- Database.DynamoDB: querySourceByKey :: forall a parent hash rest v1 m r. (DynamoIndex a parent NoRange, Code a ~ '[hash : rest], DynamoScalar v1 hash, MonadAWS m, DynamoTable parent r) => Proxy a -> hash -> Source m a
+ Database.DynamoDB: querySourceByKey :: forall a parent hash rest v1 m r. (DynamoIndex a parent 'NoRange, Code a ~ '[hash : rest], DynamoScalar v1 hash, MonadAWS m, DynamoTable parent r) => Proxy a -> hash -> Source m a
- Database.DynamoDB: sConsistentRead :: forall a_a18OZ r_a18P0. Lens' (ScanOpts a_a18OZ r_a18P0) Consistency
+ Database.DynamoDB: sConsistentRead :: forall a_a16BP r_a16BQ. Lens' (ScanOpts a_a16BP r_a16BQ) Consistency
- Database.DynamoDB: sFilterCondition :: forall a_a18OZ r_a18P0. Lens' (ScanOpts a_a18OZ r_a18P0) (Maybe (FilterCondition a_a18OZ))
+ Database.DynamoDB: sFilterCondition :: forall a_a16BP r_a16BQ. Lens' (ScanOpts a_a16BP r_a16BQ) (Maybe (FilterCondition a_a16BP))
- Database.DynamoDB: sLimit :: forall a_a18OZ r_a18P0. Lens' (ScanOpts a_a18OZ r_a18P0) (Maybe Natural)
+ Database.DynamoDB: sLimit :: forall a_a16BP r_a16BQ. Lens' (ScanOpts a_a16BP r_a16BQ) (Maybe Natural)
- Database.DynamoDB: sParallel :: forall a_a18OZ r_a18P0. Lens' (ScanOpts a_a18OZ r_a18P0) (Maybe (Natural, Natural))
+ Database.DynamoDB: sParallel :: forall a_a16BP r_a16BQ. Lens' (ScanOpts a_a16BP r_a16BQ) (Maybe (Natural, Natural))
- Database.DynamoDB: sStartKey :: forall a_a18OZ r_a18P0 r_a19yR. Lens (ScanOpts a_a18OZ r_a18P0) (ScanOpts a_a18OZ r_a19yR) (Maybe (PrimaryKey a_a18OZ r_a18P0)) (Maybe (PrimaryKey a_a18OZ r_a19yR))
+ Database.DynamoDB: sStartKey :: forall a_a16BP r_a16BQ r_a17i6. Lens (ScanOpts a_a16BP r_a16BQ) (ScanOpts a_a16BP r_a17i6) (Maybe (PrimaryKey a_a16BP r_a16BQ)) (Maybe (PrimaryKey a_a16BP r_a17i6))
- Database.DynamoDB.Filter: (/=.) :: (InCollection col tbl FullPath, DynamoEncodable typ) => Column typ ctyp col -> typ -> FilterCondition tbl
+ Database.DynamoDB.Filter: (/=.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ) => Column typ ctyp col -> typ -> FilterCondition tbl
- Database.DynamoDB.Filter: (<.) :: (InCollection col tbl FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
+ Database.DynamoDB.Filter: (<.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
- Database.DynamoDB.Filter: (<=.) :: (InCollection col tbl FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
+ Database.DynamoDB.Filter: (<=.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
- Database.DynamoDB.Filter: (==.) :: (InCollection col tbl FullPath, DynamoEncodable typ) => Column typ ctyp col -> typ -> FilterCondition tbl
+ Database.DynamoDB.Filter: (==.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ) => Column typ ctyp col -> typ -> FilterCondition tbl
- Database.DynamoDB.Filter: (>.) :: (InCollection col tbl FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
+ Database.DynamoDB.Filter: (>.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
- Database.DynamoDB.Filter: (>=.) :: (InCollection col tbl FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
+ Database.DynamoDB.Filter: (>=.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ, Ord typ) => Column typ ctyp col -> typ -> FilterCondition tbl
- Database.DynamoDB.Filter: attrExists :: (InCollection col tbl FullPath) => Column typ TypColumn col -> FilterCondition tbl
+ Database.DynamoDB.Filter: attrExists :: (InCollection col tbl 'FullPath) => Column typ 'TypColumn col -> FilterCondition tbl
- Database.DynamoDB.Filter: attrMissing :: (InCollection col tbl FullPath) => Column typ TypColumn col -> FilterCondition tbl
+ Database.DynamoDB.Filter: attrMissing :: (InCollection col tbl 'FullPath) => Column typ 'TypColumn col -> FilterCondition tbl
- Database.DynamoDB.Filter: beginsWith :: (InCollection col tbl FullPath, IsText typ) => Column typ TypColumn col -> Text -> FilterCondition tbl
+ Database.DynamoDB.Filter: beginsWith :: (InCollection col tbl 'FullPath, IsText typ) => Column typ 'TypColumn col -> Text -> FilterCondition tbl
- Database.DynamoDB.Filter: between :: (Ord typ, InCollection col tbl FullPath, DynamoScalar v typ) => Column typ ctyp col -> (typ, typ) -> FilterCondition tbl
+ Database.DynamoDB.Filter: between :: (Ord typ, InCollection col tbl 'FullPath, DynamoScalar v typ) => Column typ ctyp col -> (typ, typ) -> FilterCondition tbl
- Database.DynamoDB.Filter: contains :: (InCollection col tbl FullPath, IsText typ) => Column typ TypColumn col -> Text -> FilterCondition tbl
+ Database.DynamoDB.Filter: contains :: (InCollection col tbl 'FullPath, IsText typ) => Column typ 'TypColumn col -> Text -> FilterCondition tbl
- Database.DynamoDB.Filter: setContains :: (InCollection col tbl FullPath, DynamoScalar v a) => Column (Set a) TypColumn col -> a -> FilterCondition tbl
+ Database.DynamoDB.Filter: setContains :: (InCollection col tbl 'FullPath, DynamoScalar v a) => Column (Set a) 'TypColumn col -> a -> FilterCondition tbl
- Database.DynamoDB.Filter: size :: Column typ TypColumn col -> Column Int TypSize col
+ Database.DynamoDB.Filter: size :: Column typ 'TypColumn col -> Column Int 'TypSize col
- Database.DynamoDB.Filter: valIn :: (InCollection col tbl FullPath, DynamoScalar v typ) => Column typ ctyp col -> [typ] -> FilterCondition tbl
+ Database.DynamoDB.Filter: valIn :: (InCollection col tbl 'FullPath, DynamoScalar v typ) => Column typ ctyp col -> [typ] -> FilterCondition tbl
- Database.DynamoDB.Types: [ScB] :: !ByteString -> ScalarValue B
+ Database.DynamoDB.Types: [ScB] :: !ByteString -> ScalarValue 'B
- Database.DynamoDB.Types: [ScN] :: !Scientific -> ScalarValue N
+ Database.DynamoDB.Types: [ScN] :: !Scientific -> ScalarValue 'N
- Database.DynamoDB.Types: [ScS] :: !Text -> ScalarValue S
+ Database.DynamoDB.Types: [ScS] :: !Text -> ScalarValue 'S
- Database.DynamoDB.Types: class DynamoEncodable a where dEncode val = Just $ attributeValue & avS .~ (Just $ pack $ show val) dDecode (Just attr) = attr ^. avS >>= (readMaybe . unpack) dDecode Nothing = Nothing dIsMissing _ = False
+ Database.DynamoDB.Types: class DynamoEncodable a
- Database.DynamoDB.Types: class ScalarAuto v => DynamoScalar (v :: ScalarAttributeType) a | a -> v where scalarEncode = ScS . pack . show scalarDecode (ScS txt) = readMaybe (unpack txt)
+ Database.DynamoDB.Types: class ScalarAuto v => DynamoScalar (v :: ScalarAttributeType) a | a -> v
- Database.DynamoDB.Types: scalarDecode :: (DynamoScalar v a, Show a, Read a, v ~ S) => ScalarValue v -> Maybe a
+ Database.DynamoDB.Types: scalarDecode :: (DynamoScalar v a, Show a, Read a, v ~ 'S) => ScalarValue v -> Maybe a
- Database.DynamoDB.Types: scalarEncode :: (DynamoScalar v a, Show a, Read a, v ~ S) => a -> ScalarValue v
+ Database.DynamoDB.Types: scalarEncode :: (DynamoScalar v a, Show a, Read a, v ~ 'S) => a -> ScalarValue v
- Database.DynamoDB.Update: (+=.) :: (InCollection col tbl FullPath, DynamoScalar v typ, IsNumber typ) => Column typ TypColumn col -> typ -> Action tbl
+ Database.DynamoDB.Update: (+=.) :: (InCollection col tbl 'FullPath, DynamoScalar v typ, IsNumber typ) => Column typ 'TypColumn col -> typ -> Action tbl
- Database.DynamoDB.Update: (-=.) :: (InCollection col tbl FullPath, DynamoScalar v typ, IsNumber typ) => Column typ TypColumn col -> typ -> Action tbl
+ Database.DynamoDB.Update: (-=.) :: (InCollection col tbl 'FullPath, DynamoScalar v typ, IsNumber typ) => Column typ 'TypColumn col -> typ -> Action tbl
- Database.DynamoDB.Update: (=.) :: (InCollection col tbl FullPath, DynamoEncodable typ) => Column typ TypColumn col -> typ -> Action tbl
+ Database.DynamoDB.Update: (=.) :: (InCollection col tbl 'FullPath, DynamoEncodable typ) => Column typ 'TypColumn col -> typ -> Action tbl
- Database.DynamoDB.Update: add :: (InCollection col tbl FullPath, DynamoEncodable (Set typ)) => Column (Set typ) TypColumn col -> Set typ -> Action tbl
+ Database.DynamoDB.Update: add :: (InCollection col tbl 'FullPath, DynamoEncodable (Set typ)) => Column (Set typ) 'TypColumn col -> Set typ -> Action tbl
- Database.DynamoDB.Update: append :: (InCollection col tbl FullPath, DynamoEncodable typ) => Column [typ] TypColumn col -> [typ] -> Action tbl
+ Database.DynamoDB.Update: append :: (InCollection col tbl 'FullPath, DynamoEncodable typ) => Column [typ] 'TypColumn col -> [typ] -> Action tbl
- Database.DynamoDB.Update: delHashKey :: (InCollection col tbl FullPath, IsText key) => Column (HashMap key typ) TypColumn col -> key -> Action tbl
+ Database.DynamoDB.Update: delHashKey :: (InCollection col tbl 'FullPath, IsText key) => Column (HashMap key typ) 'TypColumn col -> key -> Action tbl
- Database.DynamoDB.Update: delListItem :: InCollection col tbl FullPath => Column [typ] TypColumn col -> Int -> Action tbl
+ Database.DynamoDB.Update: delListItem :: InCollection col tbl 'FullPath => Column [typ] 'TypColumn col -> Int -> Action tbl
- Database.DynamoDB.Update: delete :: (InCollection col tbl FullPath, DynamoEncodable (Set typ)) => Column (Set typ) TypColumn col -> Set typ -> Action tbl
+ Database.DynamoDB.Update: delete :: (InCollection col tbl 'FullPath, DynamoEncodable (Set typ)) => Column (Set typ) 'TypColumn col -> Set typ -> Action tbl
- Database.DynamoDB.Update: prepend :: (InCollection col tbl FullPath, DynamoEncodable typ) => Column [typ] TypColumn col -> [typ] -> Action tbl
+ Database.DynamoDB.Update: prepend :: (InCollection col tbl 'FullPath, DynamoEncodable typ) => Column [typ] 'TypColumn col -> [typ] -> Action tbl
- Database.DynamoDB.Update: setIfNothing :: (InCollection col tbl FullPath, DynamoEncodable typ) => Column (Maybe typ) TypColumn col -> typ -> Action tbl
+ Database.DynamoDB.Update: setIfNothing :: (InCollection col tbl 'FullPath, DynamoEncodable typ) => Column (Maybe typ) 'TypColumn col -> typ -> Action tbl
Files
- changelog.md +9/−0
- dynamodb-simple.cabal +2/−2
- src/Database/DynamoDB.hs +4/−5
- src/Database/DynamoDB/BatchRequest.hs +2/−2
- src/Database/DynamoDB/Class.hs +12/−8
- src/Database/DynamoDB/QueryRequest.hs +5/−33
- src/Database/DynamoDB/TH.hs +3/−2
- src/Database/DynamoDB/Types.hs +80/−37
changelog.md view
@@ -1,3 +1,12 @@+# 0.6.0.0++- add dDecodeEither method to DynamoEncodable for better error reporting+- removed hack for faulty AWSpager form 1.4.5 amazonka-dynamodb++# 0.5.0.0++- Added UUID DynamoEncodable instance+ # 0.4.0.1 - Fixed default signatures to compile with GHC 8.2
dynamodb-simple.cabal view
@@ -1,5 +1,5 @@ name: dynamodb-simple-version: 0.5.0.0+version: 0.6.0.0 synopsis: Typesafe library for working with DynamoDB database description: Framework for accessing DynamoDB database. The majority of AWS API is available to the user in a convenient, simple and typesafe manner.@@ -27,7 +27,7 @@ Database.DynamoDB.Internal, Database.DynamoDB.BatchRequest, Database.DynamoDB.QueryRequest, Database.DynamoDB.THLens, Database.DynamoDB.THContains, Database.DynamoDB.THConvert- build-depends: base >=4.8 && <5, amazonka-dynamodb >= 1.4.5, generics-sop,+ build-depends: base >=4.9 && <5, amazonka-dynamodb >= 1.5.0, generics-sop, unordered-containers, text, lens, double-conversion, semigroups, bytestring >= 0.10.8.0, containers, monad-supply, template-haskell, transformers, exceptions,
src/Database/DynamoDB.hs view
@@ -95,7 +95,6 @@ import Data.Function ((&)) import Data.Proxy import Data.Semigroup ((<>))-import qualified Data.Text as T import Network.AWS import qualified Network.AWS.DynamoDB.DeleteItem as D import qualified Network.AWS.DynamoDB.GetItem as D@@ -145,8 +144,8 @@ if | null result -> return Nothing | otherwise -> case dGsDecode result of- Just res -> return (Just res)- Nothing -> throwM (DynamoException $ "Cannot decode item: " <> T.pack (show result))+ Right res -> return (Just res)+ Left err -> throwM (DynamoException $ "Cannot decode item: " <> err) -- | Delete item from the database by specifying the primary key. deleteItemByKey :: forall m a r. (MonadAWS m, DynamoTable a r) => Proxy a -> PrimaryKey a r -> m ()@@ -207,8 +206,8 @@ | Just cmd <- dUpdateItem p pkey actions Nothing = do rs <- send (cmd & D.uiReturnValues .~ Just D.AllNew) case dGsDecode (rs ^. D.uirsAttributes) of- Just res -> return res- Nothing -> throwM (DynamoException $ "Cannot decode item: " <> T.pack (show rs))+ Right res -> return res+ Left err -> throwM (DynamoException $ "Cannot decode item: " <> err) | otherwise = do rs <- getItem Strongly p pkey case rs of
src/Database/DynamoDB/BatchRequest.hs view
@@ -98,8 +98,8 @@ mapM decoder (tbls ^.. ix tblname . traverse) decoder item = case dGsDecode item of- Just res -> return res- Nothing -> throwM (DynamoException $ "Error decoding item: " <> T.pack (show item))+ Right res -> return res+ Left err -> throwM (DynamoException $ "Error decoding item: " <> err ) dDeleteRequest :: DynamoTable a r => Proxy a -> PrimaryKey a r -> D.DeleteRequest dDeleteRequest p pkey = D.deleteRequest & D.drKey .~ dKeyToAttr p pkey
src/Database/DynamoDB/Class.hs view
@@ -14,7 +14,6 @@ {-# LANGUAGE PolyKinds #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE ScopedTypeVariables #-}-{-# LANGUAGE TupleSections #-} {-# LANGUAGE TypeFamilies #-} {-# LANGUAGE TypeOperators #-} {-# LANGUAGE UndecidableInstances #-}@@ -45,6 +44,7 @@ ) where import Control.Lens ((.~), sequenceOf, _2)+import Data.Bifunctor (first) import Data.Function ((&)) import qualified Data.HashMap.Strict as HMap import Data.List.NonEmpty (NonEmpty ((:|)), nonEmpty)@@ -79,8 +79,8 @@ => DynamoCollection a (r :: RangeType) (t :: TableType) | a -> r t where allFieldNames :: Proxy a -> [T.Text] primaryFields :: Proxy a -> [T.Text]- dGsDecode :: HashMap T.Text AttributeValue -> Maybe a- default dGsDecode :: (Code a ~ '[ xs ]) => HashMap T.Text AttributeValue -> Maybe a+ dGsDecode :: HashMap T.Text AttributeValue -> Either T.Text a+ default dGsDecode :: (Code a ~ '[ xs ]) => HashMap T.Text AttributeValue -> Either T.Text a dGsDecode = gsDecode class DynamoCollection a r t => HasPrimaryKey a (r :: RangeType) (t :: TableType) where@@ -235,19 +235,23 @@ gsDecode :: forall a r t xs. (DynamoCollection a r t, Code a ~ '[ xs ])- => HashMap T.Text AttributeValue -> Maybe a+ => HashMap T.Text AttributeValue -> Either T.Text a gsDecode = gsDecodeG (allFieldNames (Proxy :: Proxy a)) -- | Decode hashmap to a record using generic-sop. gsDecodeG :: forall a xs. (Generic a, All2 DynamoEncodable (Code a), Code a ~ '[ xs ])- => [T.Text] -> HMap.HashMap T.Text AttributeValue -> Maybe a+ => [T.Text] -> HMap.HashMap T.Text AttributeValue -> Either T.Text a gsDecodeG names attrs =- let Just vals = fromList $ map (`HMap.lookup` attrs) names- in to . SOP . Z <$> hsequence (hcliftA dproxy (dDecode . unK) vals)+ let Just pairs = fromList $ map (\k -> (k, k `HMap.lookup` attrs)) names+ in to . SOP . Z <$> hsequence (hcliftA dproxy (decodeWithErr . unK) pairs) where dproxy = Proxy :: Proxy DynamoEncodable-+ decodeWithErr (k, Nothing) = first (("Error decoding empty attr: " <> k <> " -> ") <>) (dDecodeEither Nothing)+ decodeWithErr (k, Just attr) = + first+ (\err -> "Error decoding attr: " <> k <> " -> " <> err <> " from: " <> T.pack (show attr))+ (dDecodeEither (Just attr)) defaultPutItem :: forall a r. DynamoTable a r => a -> D.PutItem defaultPutItem item = D.putItem tblname & D.piItem .~ gsEncode item
src/Database/DynamoDB/QueryRequest.hs view
@@ -48,7 +48,6 @@ import Control.Monad ((>=>)) import Control.Monad.Catch (throwM) import Data.Bool (bool)-import Data.Coerce (coerce) import Data.Conduit (Conduit, Source, (=$=)) import qualified Data.Conduit.List as CL import Data.Foldable (toList)@@ -66,7 +65,6 @@ import qualified Network.AWS.DynamoDB.Query as D import qualified Network.AWS.DynamoDB.Scan as D import qualified Network.AWS.DynamoDB.Types as D-import Network.AWS.Pager (AWSPager (..)) import Numeric.Natural (Natural) import qualified Data.HashMap.Strict as HMap @@ -81,8 +79,8 @@ => HashMap T.Text D.AttributeValue -> m a rsDecoder item = case dGsDecode item of- Just res -> return res- Nothing -> throwM (DynamoException $ "Error decoding item: " <> T.pack (show item))+ Right res -> return res+ Left err -> throwM (DynamoException $ "Error decoding item: " <> err) -- | Options for a generic query. data QueryOpts a hash range = QueryOpts {@@ -122,36 +120,10 @@ addStartKey (Just (key, range)) = D.qExclusiveStartKey .~ dQueryKeyToAttr (Proxy :: Proxy a) (key, range) --- | When https://github.com/brendanhay/amazonka/issues/340 is fixed, remove-newtype FixedQuery = FixedQuery D.Query-instance AWSRequest FixedQuery where- type Rs FixedQuery = D.QueryResponse- request (FixedQuery a) = coerce (request a)- response lgr svc _ = response lgr svc (Proxy :: Proxy D.Query)-instance AWSPager FixedQuery where- page (FixedQuery dq) resp- | null lastkey = Nothing- | otherwise = Just $ FixedQuery (dq & D.qExclusiveStartKey .~ lastkey)- where- lastkey = resp ^. D.qrsLastEvaluatedKey--newtype FixedScan = FixedScan D.Scan-instance AWSRequest FixedScan where- type Rs FixedScan = D.ScanResponse- request (FixedScan a) = coerce (request a)- response lgr svc _ = response lgr svc (Proxy :: Proxy D.Scan)-instance AWSPager FixedScan where- page (FixedScan dq) resp- | null lastkey = Nothing- | otherwise = Just $ FixedScan (dq & D.sExclusiveStartKey .~ lastkey)- where- lastkey = resp ^. D.srsLastEvaluatedKey-- -- | Same as 'querySource', but return data in original chunks. querySourceChunks :: forall a t m hash range. (CanQuery a t hash range, MonadAWS m) => Proxy a -> QueryOpts a hash range -> Source m [a]-querySourceChunks _ q = paginate (FixedQuery (queryCmd q)) =$= CL.mapM (\res -> mapM rsDecoder (res ^. D.qrsItems))+querySourceChunks _ q = paginate (queryCmd q) =$= CL.mapM (\res -> mapM rsDecoder (res ^. D.qrsItems)) -- | Generic query function. You can query table or indexes that have -- a range key defined. The filter condition cannot access the hash and range keys.@@ -164,7 +136,7 @@ (DynamoIndex a parent 'NoRange, Code a ~ '[ hash ': rest ], DynamoScalar v1 hash, MonadAWS m, DynamoTable parent r) => Proxy a -> hash -> Source m [a]-querySourceChunksByKey p key = paginate (FixedQuery sQuery) =$= CL.mapM (\res -> mapM rsDecoder (res ^. D.qrsItems))+querySourceChunksByKey p key = paginate sQuery =$= CL.mapM (\res -> mapM rsDecoder (res ^. D.qrsItems)) where sQuery = D.query (tableName (Proxy :: Proxy parent)) & D.qKeyConditionExpression .~ Just "#K = :key"@@ -304,7 +276,7 @@ -- | Conduit source for running scan; the same as 'scanSource', but return results in chunks as they come. scanSourceChunks :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Source m [a]-scanSourceChunks _ q = paginate (FixedScan (scanCmd q)) =$= CL.mapM (\res -> mapM rsDecoder (res ^. D.srsItems))+scanSourceChunks _ q = paginate (scanCmd q) =$= CL.mapM (\res -> mapM rsDecoder (res ^. D.srsItems)) -- | Conduit source for running a scan. scanSource :: (MonadAWS m, TableScan a r t) => Proxy a -> ScanOpts a r -> Source m a
src/Database/DynamoDB/TH.hs view
@@ -288,8 +288,9 @@ lift [d| instance DynamoEncodable $(conT table) where dEncode val = Just (attributeValue & avM .~ gsEncodeG $(fieldList) val)- dDecode (Just attr) = gsDecodeG $(fieldList) (attr ^. avM)- dDecode Nothing = Nothing+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = gsDecodeG $(fieldList) (attr ^. avM)+ dDecodeEither Nothing = Left "Missing value" |] >>= tell let constrs = mkConstrNames tblFieldNames forM_ constrs $ \constr ->
src/Database/DynamoDB/Types.hs view
@@ -36,6 +36,7 @@ import Control.Exception (Exception) import Control.Lens ((.~), (^.)) import qualified Data.Aeson as AE+import Data.Bifunctor (first) import qualified Data.ByteString as BS import Data.ByteString.Lazy (toStrict) import Data.Double.Conversion.Text (toShortest)@@ -45,6 +46,7 @@ import Data.HashMap.Strict (HashMap) import qualified Data.HashMap.Strict as HMap import Data.Maybe (mapMaybe)+import Data.Monoid ((<>)) import Data.Proxy import Data.UUID.Types (UUID) import qualified Data.UUID.Types as UUID@@ -184,6 +186,13 @@ default dDecode :: (Show a, Read a) => Maybe AttributeValue -> Maybe a dDecode (Just attr) = attr ^. D.avS >>= (readMaybe . T.unpack) dDecode Nothing = Nothing++ -- | Decode data. Return (Left err) on parsing error, gets+ -- 'Nothing' on input if the attribute was missing in the database.+ -- The default instance uses dDecode, define this just for better errors+ dDecodeEither :: Maybe AttributeValue -> Either T.Text a+ dDecodeEither = maybe (Left "Decoding error") Right . dDecode+ -- | Aid for searching for empty list and hashmap; these can be represented -- both by empty list and by missing value, if this returns true, enhance search. -- Also used by joins to weed out empty foreign keys@@ -192,40 +201,66 @@ instance DynamoEncodable Scientific where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Integer where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Int where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Word where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Int16 where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Int32 where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Int64 where dEncode = Just . dScalarEncode- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Double where dEncode num = Just $ attributeValue & D.avN .~ (Just $ toShortest num)- dDecode (Just attr) = attr ^. D.avN >>= AE.decodeStrict . encodeUtf8- dDecode Nothing = Nothing -- Fail on missing attr+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = + maybe (Left "Missing value") Right (attr ^. D.avN)+ >>= first T.pack . AE.eitherDecodeStrict . encodeUtf8+ dDecodeEither Nothing = Left "Missing attr" instance DynamoEncodable Bool where dEncode b = Just $ attributeValue & D.avBOOL .~ Just b- dDecode (Just attr) = attr ^. D.avBOOL- dDecode Nothing = Nothing+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = maybe (Left "Missing value") Right (attr ^. D.avBOOL)+ dDecodeEither Nothing = Left "Missing attr"+ instance DynamoEncodable T.Text where dEncode "" = Nothing dEncode txt = Just (dScalarEncode txt)@@ -255,8 +290,9 @@ instance DynamoEncodable a => DynamoEncodable (Maybe a) where dEncode Nothing = Nothing dEncode (Just key) = dEncode key- dDecode Nothing = Just Nothing- dDecode (Just attr) = Just <$> dDecode (Just attr)+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither Nothing = Right Nothing+ dDecodeEither (Just attr) = Just <$> dDecodeEither (Just attr) dIsMissing Nothing = True dIsMissing _ = False instance (Ord a, DynamoScalar v a) => DynamoEncodable (Set.Set a) where@@ -264,25 +300,30 @@ dEncode dta = Just $ dSetEncode dta dDecode (Just attr) = dSetDecode attr dDecode Nothing = Just Set.empty+ dDecodeEither (Just attr) = maybe (Left "Error decoding set") Right (dSetDecode attr)+ dDecodeEither Nothing = Right Set.empty instance (IsText t, DynamoEncodable a) => DynamoEncodable (HashMap t a) where dEncode dta = let textmap = HMap.fromList $ mapMaybe (\(key, val) -> (toText key,) <$> dEncode val) $ HMap.toList dta in Just $ attributeValue & D.avM .~ textmap- dDecode (Just attr) =- let attrlist = traverse (\(key, val) -> (fromText key,) <$> dDecode (Just val)) $ HMap.toList (attr ^. D.avM)- in HMap.fromList <$> attrlist- dDecode Nothing = Just mempty+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) =+ let attrlist = traverse (\(key, val) -> (fromText key,) <$> dDecodeEither (Just val)) $ HMap.toList (attr ^. D.avM)+ in HMap.fromList <$> attrlist+ dDecodeEither Nothing = Right mempty dIsMissing = null -- | DynamoDB cannot represent empty items; ['Maybe' a] will lose Nothings. instance DynamoEncodable a => DynamoEncodable [a] where dEncode lst = Just $ attributeValue & D.avL .~ mapMaybe dEncode lst- dDecode (Just attr) = traverse (dDecode . Just) (attr ^. D.avL)- dDecode Nothing = Just mempty+ dDecode = either (const Nothing) Just . dDecodeEither+ dDecodeEither (Just attr) = traverse (dDecodeEither . Just) (attr ^. D.avL)+ dDecodeEither Nothing = Right mempty dIsMissing = null instance {-# OVERLAPPABLE #-} DynamoEncodable a => DynamoEncodable (Tagged v a) where dEncode = dEncode . unTagged dDecode a = Tagged <$> dDecode a+ dDecodeEither a = Tagged <$> dDecodeEither a dIsMissing = dIsMissing . unTagged -- | Partial encoding/decoding Aeson values. Empty strings get converted to NULL.@@ -294,19 +335,21 @@ dEncode (AE.Bool b) = dEncode b dEncode AE.Null = Just $ attributeValue & D.avNULL .~ Just True --- dDecode Nothing = Just AE.Null- dDecode (Just attr) = -- Ok, this is going to be very hacky...+ dDecode = either (const Nothing) Just . dDecodeEither++ dDecodeEither Nothing = Right AE.Null+ dDecodeEither (Just attr) = -- Ok, this is going to be very hacky... case AE.toJSON attr of AE.Object obj -> case HMap.toList obj of- [("BOOL", AE.Bool val)] -> Just (AE.Bool val)- [("L", _)] -> (AE.Array .V.fromList) <$> mapM (dDecode . Just) (attr ^. D.avL)- [("M", _)] -> AE.Object <$> mapM (dDecode . Just) (attr ^. D.avM)- [("N", AE.String num)] -> AE.decodeStrict (encodeUtf8 num)- [("N", num@(AE.Number _))] -> Just num -- Just in case, this is usually not returned- [("S", AE.String val)] -> Just (AE.String val)- [("NULL", _)] -> Just AE.Null- _ -> Nothing- _ -> Nothing -- This shouldn't happen+ [("BOOL", AE.Bool val)] -> Right (AE.Bool val)+ [("L", _)] -> (AE.Array .V.fromList) <$> mapM (dDecodeEither . Just) (attr ^. D.avL)+ [("M", _)] -> AE.Object <$> mapM (dDecodeEither . Just) (attr ^. D.avM)+ [("N", AE.String num)] -> first T.pack (AE.eitherDecodeStrict (encodeUtf8 num))+ [("N", num@(AE.Number _))] -> Right num -- Just in case, this is usually not returned+ [("S", AE.String val)] -> Right (AE.String val)+ [("NULL", _)] -> Right AE.Null+ _ -> Left ("Undecodable json value: " <> decodeUtf8 (toStrict (AE.encode obj)))+ _ -> Left "Wrong dynamo data" -- This shouldn't happen -- dIsMissing AE.Null = True dIsMissing _ = False