{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE LambdaCase #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} module Network.Mail.Mailgun.List where import Control.Lens import Control.Monad.Catch import Control.Monad.Reader import Data.Aeson ((.:)) import qualified Data.Aeson as JS import Data.Aeson.Lens import Data.Aeson.Filthy import Data.Machine import qualified Data.Proxy as Proxy import Data.Text import Data.Time import Network.Mail.Mailgun.API import Network.Mail.Mailgun.Config import Network.Wreq import Text.Printf data AccessLevel = AccessReadonly | AccessMembers | AccessEveryone deriving (Eq, Ord, Show) encodeAccessLevel :: AccessLevel -> Text encodeAccessLevel AccessReadonly = "readonly" encodeAccessLevel AccessMembers = "members" encodeAccessLevel AccessEveryone = "everyone" instance JS.FromJSON AccessLevel where parseJSON = JS.withText "AccessLevel" $ \case "readonly" -> pure AccessReadonly "members" -> pure AccessMembers "everyone" -> pure AccessEveryone t -> fail $ "Unknown AccessLevel "++show t instance JS.ToJSON AccessLevel where toJSON = JS.String . encodeAccessLevel data MailingList f = MailingList { _listAccessLevel :: AccessLevel , _listAddress :: Text , _listName :: Text , _listDescription :: Text , _listCreated :: f UTCTime , _listMemberCount :: f Integer } makeLenses ''MailingList instance Show (MailingList Identity) where show (MailingList al a n d c mc) = printf "MailingList %s %s %s %s %s %s" (show al) a n d (show c) (show mc) instance Applicative f => JS.FromJSON (MailingList f) where parseJSON = JS.withObject "MailingList" $ \v -> MailingList <$> v .: "access_level" <*> v .: "address" <*> v .: "name" <*> v .: "description" <*> (pure . fromRFC2822Time <$> v .: "created_at") <*> (pure <$> v .: "members_count") instance JS.ToJSON (MailingList Identity) where toJSON ml = JS.object [ ("access_level", ml^.listAccessLevel.to JS.toJSON) , ("address", ml^.listAddress.to JS.toJSON) , ("name", ml^.listName.to JS.toJSON) , ("description", ml^.listDescription.to JS.toJSON) , ("created_at", ml^.listCreated.to (RFC2822Time . runIdentity).to JS.toJSON) , ("members_count", ml^.listMemberCount.to runIdentity.to JS.toJSON) ] instance JS.ToJSON (MailingList Proxy.Proxy) where toJSON ml = JS.object [ ("access_level", ml^.listAccessLevel.to JS.toJSON) , ("address", ml^.listAddress.to JS.toJSON) , ("name", ml^.listName.to JS.toJSON) , ("description", ml^.listDescription.to JS.toJSON) ] data ListMember v = ListMember { _lmName :: Text , _lmAddress :: Text , _lmSubscribed :: Bool , _lmExtra :: v } deriving (Eq, Ord, Show) makeLenses ''ListMember instance JS.FromJSON v => JS.FromJSON (ListMember v) where parseJSON = JS.withObject "MailingList" $ \v -> ListMember <$> v .: "name" <*> v .: "address" <*> v .: "subscribed" <*> v .: "vars" instance JS.ToJSON v => JS.ToJSON (ListMember v) where toJSON lm = JS.object [ ("name", lm^.lmName.to JS.toJSON) , ("address", lm^.lmAddress.to JS.toJSON) , ("subscribed", lm^.lmSubscribed.to JS.toJSON) , ("vars", lm^.lmExtra.to JS.toJSON) ] createList :: (HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => MailingList Proxy.Proxy -> m () createList ml = call (MGPost (const "/v3/lists") [] [ partText "address" (ml^.listAddress) , partText "name" (ml^.listName) , partText "description" (ml^.listDescription) , partText "access_level" (ml^.listAccessLevel.to encodeAccessLevel) ]) (const $ Just ()) getLists :: (HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => SourceT m (MailingList Identity) getLists = paginatedStream (MGGet (const "/v3/lists/pages") []) (^.key "items"._JSON) getList :: (HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => Text -> m (MailingList Identity) getList addr = call (MGGet (const $ printf "/v3/lists/%s" addr) []) (^?key "list"._JSON) removeList :: (HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => Text -> m () removeList ml = call (MGDelete (const $ printf "/v3/lists/%s" ml) []) (const $ Just ()) listMembers :: (JS.ToJSON v, JS.FromJSON v ,HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => Maybe Bool -> Text -> SourceT m (ListMember v) listMembers msubbed ml = paginatedStream (MGGet (const $ printf "/v3/lists/%s/members/pages" ml) . mconcat $ [ maybe [] (\s -> [("subscribed", if s then "yes" else "no")]) msubbed , [ ("limit", "1000") ] ]) (^.key "items"._JSON) getMember :: (JS.ToJSON v, JS.FromJSON v ,HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => Text -> Text -> m (ListMember v) getMember ml mbr = call (MGGet (const $ printf "/v3/lists/%s/members/%s" ml mbr) []) (^?key "member"._JSON) removeMember :: (HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => Text -> Text -> m () removeMember ml mbr = call (MGDelete (const $ printf "/v3/lists/%s/members/%s" ml mbr) []) (const $ Just ()) addMembers :: (JS.ToJSON v, JS.FromJSON v ,HasMailgunConfig c, MonadReader c m, MonadIO m, MonadThrow m) => Bool -> Text -> ProcessT m (ListMember v) (MailingList Identity) addMembers upsert ml = buffered 1000 ~> addMemberBatch where addMemberBatch = autoM $ \batch -> do call (MGPost (const $ printf "/v3/lists/%s/members.json" ml) [] [ yesNo "upsert" upsert , partLBS "members" $ JS.encode batch ]) (^?key "list"._JSON)