git.iko.soy/typeable/mandrill
on master help sign in
Wrapped users calls
commitf3ef3d0597e979d0bac2df76952e065a231c63e1 authorAlfredo Di Napoli <alfredo.dinapoli@gmail.com> dateWed 20 Aug 2014, 09:20 (12 years ago) parent304852a
9 files changed+221−26
README.md+6−2 src/Network/API/Mandrill/Messages.hs+1−1 src/Network/API/Mandrill/Types.hs+7−7 src/Network/API/Mandrill/Users.hs+5−5 src/Network/API/Mandrill/Users/Types.hs+68−4 test/Main.hs+9−5 test/Online.hs+28 test/RawData.hs+86 test/Tests.hs+11−2
README.md+6−2
@@ -8,9 +8,13 @@ the [Mandrill](http://mandrillapp.com) transactional email service.
88# Supported API versions
99
1010* 1.0 (partially)
11− - [Users call](https://mandrillapp.com/api/docs/users.JSON.html)
11+ - [Users call](https://mandrillapp.com/api/docs/users.JSON.html) - 100%
12+ + info.json
13+ + ~~ping.json~~ (as **doesn't return valid json!**)
14+ + ping2.json
15+ + senders.json
1216 - [Messages call](https://mandrillapp.com/api/docs/messages.JSON.html)
13− + send
17+ + send.json
1418
1519# Testing online API
1620
src/Network/API/Mandrill/Messages.hs+1−1
@@ -20,6 +20,6 @@ send :: MandrillKey
2020 -- ^ ip_pool
2121 -> Maybe UTCTime
2222 -- ^ send_at
23− -> IO (MandrillResponse MessagesResponse)
23+ -> IO (MandrillResponse [MessagesResponse])
2424send k msg async ip_pool send_at =
2525 toMandrillResponse MessagesSend (MessagesSendRq k msg async ip_pool send_at)
src/Network/API/Mandrill/Types.hs+7−7
@@ -64,17 +64,17 @@ deriveJSON defaultOptions {
6464-- | The main datatypes which models the response from the Mandrill API,
6565-- which can be either a success or a failure.
6666data MandrillResponse k =
67− MandrillSuccess [k]
67+ MandrillSuccess k
6868 | MandrillFailure MandrillError deriving Show
6969
7070instance FromJSON k => FromJSON (MandrillResponse k) where
71− parseJSON a@(Array _) = case (parseMaybe parseJSON a) :: Maybe [k] of
71+ parseJSON v = case (parseMaybe parseJSON v) :: Maybe k of
7272 Just r -> return $ MandrillSuccess r
73− Nothing -> fail $ show a <> " is not a valid MandrillSuccess"
74− parseJSON o@(Object _) = case (parseMaybe parseJSON o) :: Maybe MandrillError of
75− Just e -> return $ MandrillFailure e
76− Nothing -> fail $ show o <> " is not a valid MandrillError"
77− parseJSON v = typeMismatch "Expected an Object or an Array for MandrillResponse" v
73+ Nothing -> do
74+ -- try to parse it as an error
75+ case (parseMaybe parseJSON v) :: Maybe MandrillError of
76+ Just e -> return $ MandrillFailure e
77+ Nothing -> fail $ show v <> " is neither a MandrillSuccess or a MandrillError."
7878
7979
8080--------------------------------------------------------------------------------
src/Network/API/Mandrill/Users.hs+5−5
@@ -10,23 +10,23 @@ import Data.Aeson
1010
1111--------------------------------------------------------------------------------
1212-- | Return the information about the API-connected user
13−info :: MandrillKey -> IO (MandrillResponse UsersResponse)
13+info :: MandrillKey -> IO (MandrillResponse UsersInfoResponse)
1414info key = toMandrillResponse UsersInfo (UsersRq key)
1515
1616
1717--------------------------------------------------------------------------------
1818-- | Validate an API key and respond to a ping
19−ping :: MandrillKey -> IO (MandrillResponse UsersResponse)
20−ping key = toMandrillResponse UsersPing (UsersRq key)
19+ping :: MandrillKey -> IO (MandrillResponse UsersPingResponse)
20+ping _ = fail "users/ping.json doesn't return valid JSON, thus is not implemented yet."
2121
2222
2323--------------------------------------------------------------------------------
2424-- | Validate an API key and respond to a ping (anal JSON parser version)
25−ping2 :: MandrillKey -> IO (MandrillResponse UsersResponse)
25+ping2 :: MandrillKey -> IO (MandrillResponse UsersPing2Response)
2626ping2 key = toMandrillResponse UsersPing2 (UsersRq key)
2727
2828
2929--------------------------------------------------------------------------------
3030-- | Return the senders that have tried to use this account, both verified and unverified
31−senders :: MandrillKey -> IO (MandrillResponse UsersResponse)
31+senders :: MandrillKey -> IO (MandrillResponse [UsersSendersResponse])
3232senders key = toMandrillResponse UsersSenders (UsersRq key)
src/Network/API/Mandrill/Users/Types.hs+68−4
@@ -14,16 +14,80 @@ import Data.Aeson.TH
1414
1515import Network.API.Mandrill.Types
1616
17+
1718--------------------------------------------------------------------------------
1819data UsersRq = UsersRq {
19− _ureq_key :: MandrillKey
20+ _ureq_key :: !MandrillKey
2021 } deriving Show
2122
2223makeLenses ''UsersRq
2324deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''UsersRq
2425
2526--------------------------------------------------------------------------------
26−data UsersResponse = UsersResponse {
27+data MandrillStats = MandrillStats {
28+ _msts_sent :: Int
29+ , _msts_hard_bounces :: Int
30+ , _msts_soft_bounces :: Int
31+ , _msts_rejects :: Int
32+ , _msts_complaints :: Int
33+ , _msts_unsubs :: Int
34+ , _msts_opens :: Int
35+ , _msts_unique_opens :: Int
36+ , _msts_clicks :: Int
37+ , _msts_unique_clicks :: Int
38+ } deriving Show
39+
40+makeLenses ''MandrillStats
41+deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''MandrillStats
42+
43+--------------------------------------------------------------------------------
44+data UserStats = UserStats {
45+ _usts_today :: MandrillStats
46+ , _usts_last_7_days :: MandrillStats
47+ , _usts_last_30_days :: MandrillStats
48+ , _usts_last_60_days :: MandrillStats
49+ , _usts_last_90_days :: MandrillStats
50+ , _usts_all_time :: MandrillStats
51+ } deriving Show
52+
53+makeLenses ''UserStats
54+deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''UserStats
55+
56+--------------------------------------------------------------------------------
57+data UsersInfoResponse = UsersInfoResponse {
58+ _usir_username :: !T.Text
59+ , _usir_created_at :: MandrillDate
60+ , _usir_public_id :: !T.Text
61+ , _usir_reputation :: !Int
62+ , _usir_hourly_quota :: !Int
63+ , _usir_backlog :: !Int
64+ , _usir_stats :: UserStats
65+ } deriving Show
66+
67+makeLenses ''UsersInfoResponse
68+deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''UsersInfoResponse
69+
70+
71+--------------------------------------------------------------------------------
72+newtype UsersPingResponse = UsersPingResponse T.Text deriving Show
73+
74+deriveFromJSON defaultOptions ''UsersPingResponse
75+
76+instance ToJSON UsersPingResponse where
77+ toJSON (UsersPingResponse t) = String t
78+
79+
80+--------------------------------------------------------------------------------
81+data UsersPing2Response = UsersPing2Response {
82+ _usrr_PING :: T.Text
83+ } deriving Show
84+
85+deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''UsersPing2Response
86+
87+
88+
89+--------------------------------------------------------------------------------
90+data UsersSendersResponse = UsersResponse {
2791 _usrr_address :: !T.Text
2892 -- ^ The sender's email address
2993 , _usrr_created_at :: MandrillDate
@@ -54,5 +118,5 @@ data UsersResponse = UsersResponse {
54118 -- ^ The number of unique clicks for emails sent for this sender
55119 } deriving Show
56120
57−makeLenses ''UsersResponse
58−deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''UsersResponse
121+makeLenses ''UsersSendersResponse
122+deriveJSON defaultOptions { fieldLabelModifier = drop 6 } ''UsersSendersResponse
test/Main.hs+9−5
@@ -23,7 +23,10 @@ onlineTestsIfEnabled = do
2323 Nothing -> return []
2424 Just k -> return [
2525 testGroup "Mandrill online tests" [
26− testCase "messages/send.json" (testOnlineMessagesSend k)
26+ testCase "users/info.json" (testOnlineUsersInfo k)
27+ , testCase "users/ping2.json" (testOnlineUsersPing2 k)
28+ , testCase "users/senders.json" (testOnlineUsersSenders k)
29+ , testCase "messages/send.json" (testOnlineMessagesSend k)
2730 ]]
2831
2932----------------------------------------------------------------------
@@ -33,7 +36,8 @@ main = do
3336 defaultMainWithIngredients defaultIngredients $
3437 testGroup "Mandrill tests" $ onlineTests <> [
3538 testGroup "Mandrill offline tests" [
36− testCase "users/info.json API parsing" testUsersInfo
37− , testCase "messages/send.json API parsing" testMessagesSend
38− ]
39− ]
39+ testCase "users/info.json API parsing" testUsersInfo
40+ , testCase "users/senders.json API parsing" testUsersSenders
41+ , testCase "messages/send.json API parsing" testMessagesSend
42+ ]
43+ ]
test/Online.hs+28
@@ -13,8 +13,36 @@ import RawData
1313
1414import Network.API.Mandrill.Types
1515import qualified Network.API.Mandrill.Messages as API
16+import qualified Network.API.Mandrill.Users as API
17+
18+--
19+-- Users calls
20+--
21+testOnlineUsersInfo :: MandrillKey -> Assertion
22+testOnlineUsersInfo k = do
23+ res <- API.info k
24+ case res of
25+ MandrillSuccess _ -> return ()
26+ MandrillFailure e -> fail $ "users/info.json " ++ show e
27+
28+testOnlineUsersPing2 :: MandrillKey -> Assertion
29+testOnlineUsersPing2 k = do
30+ res <- API.ping2 k
31+ case res of
32+ MandrillSuccess _ -> return ()
33+ MandrillFailure e -> fail $ "users/ping2.json " ++ show e
34+
35+testOnlineUsersSenders :: MandrillKey -> Assertion
36+testOnlineUsersSenders k = do
37+ res <- API.senders k
38+ case res of
39+ MandrillSuccess _ -> return ()
40+ MandrillFailure e -> fail $ "users/senders.json " ++ show e
1641
1742
43+--
44+-- Messages calls
45+--
1846testOnlineMessagesSend :: MandrillKey -> Assertion
1947testOnlineMessagesSend k = do
2048 msg <- generate arbitrary
test/RawData.hs+86
@@ -8,6 +8,92 @@ import Data.Either
88
99usersInfoData :: String
1010usersInfoData = [r|
11+{
12+ "username": "foo@bar.com",
13+ "created_at": "2014-03-27 09:42:20.62599",
14+ "public_id": "barbaz",
15+ "reputation": 99,
16+ "hourly_quota": 1094,
17+ "backlog": 0,
18+ "stats": {
19+ "today": {
20+ "sent": 102,
21+ "hard_bounces": 0,
22+ "soft_bounces": 0,
23+ "rejects": 0,
24+ "complaints": 0,
25+ "unsubs": 0,
26+ "opens": 71,
27+ "unique_opens": 52,
28+ "clicks": 25,
29+ "unique_clicks": 19
30+ },
31+ "last_7_days": {
32+ "sent": 396,
33+ "hard_bounces": 0,
34+ "soft_bounces": 1,
35+ "rejects": 0,
36+ "complaints": 0,
37+ "unsubs": 0,
38+ "opens": 321,
39+ "unique_opens": 209,
40+ "clicks": 147,
41+ "unique_clicks": 84
42+ },
43+ "last_30_days": {
44+ "sent": 3022,
45+ "hard_bounces": 3,
46+ "soft_bounces": 4,
47+ "rejects": 0,
48+ "complaints": 0,
49+ "unsubs": 0,
50+ "opens": 2408,
51+ "unique_opens": 1540,
52+ "clicks": 958,
53+ "unique_clicks": 578
54+ },
55+ "last_60_days": {
56+ "sent": 6120,
57+ "hard_bounces": 5,
58+ "soft_bounces": 4,
59+ "rejects": 0,
60+ "complaints": 0,
61+ "unsubs": 0,
62+ "opens": 4869,
63+ "unique_opens": 3112,
64+ "clicks": 2004,
65+ "unique_clicks": 1223
66+ },
67+ "last_90_days": {
68+ "sent": 9267,
69+ "hard_bounces": 6,
70+ "soft_bounces": 4,
71+ "rejects": 1,
72+ "complaints": 1,
73+ "unsubs": 0,
74+ "opens": 7428,
75+ "unique_opens": 4772,
76+ "clicks": 2943,
77+ "unique_clicks": 1802
78+ },
79+ "all_time": {
80+ "sent": 15278,
81+ "hard_bounces": 7,
82+ "soft_bounces": 7,
83+ "rejects": 1,
84+ "complaints": 1,
85+ "unsubs": 0,
86+ "opens": 12782,
87+ "unique_opens": 7926,
88+ "clicks": 5336,
89+ "unique_clicks": 3341
90+ }
91+ }
92+}
93+|]
94+
95+usersSendersData :: String
96+usersSendersData = [r|
1197{
1298 "address": "sender.example@mandrillapp.com",
1399 "created_at": "2014-08-12 20:49:42.8344",
test/Tests.hs+11−2
@@ -26,8 +26,17 @@ testMessagesSend = assertBool "send.json: Parsing failed!" (isRight parsePayload
2626testUsersInfo :: Assertion
2727testUsersInfo = do
2828 let res = parsePayload
29− assertBool ("users.json (response): Parsing failed: " ++ show res)
29+ assertBool ("users/info.json (response): Parsing failed: " ++ show res)
3030 (isRight parsePayload)
3131 where
32− parsePayload :: Either String UsersResponse
32+ parsePayload :: Either String UsersInfoResponse
3333 parsePayload = eitherDecodeStrict . C8.pack $ usersInfoData
34+
35+testUsersSenders :: Assertion
36+testUsersSenders = do
37+ let res = parsePayload
38+ assertBool ("users/senders.json (response): Parsing failed: " ++ show res)
39+ (isRight parsePayload)
40+ where
41+ parsePayload :: Either String UsersSendersResponse
42+ parsePayload = eitherDecodeStrict . C8.pack $ usersSendersData