Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Don't force ReaderT Pattern #91

Merged
merged 4 commits into from
Jan 16, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion main/cli.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import Control.Monad.Reader (runReaderT)
import Text.Pretty.Simple (pPrint, pShow)

-- slack-web
import qualified Web.Slack as Slack
import qualified Web.Slack.Classy as Slack
import qualified Web.Slack.Common as Slack
import qualified Web.Slack.Conversation as SlackConversation

Expand Down
9 changes: 5 additions & 4 deletions slack-web.cabal
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: slack-web
version: 0.3.0.1
version: 0.4.0.0

build-type: Simple
cabal-version: 1.20
Expand All @@ -19,7 +19,7 @@ description: Haskell bindings for the Slack web API.

category: Web

tested-with: GHC == 8.0.2
tested-with: GHC == 8.10.7

extra-source-files:
CHANGELOG.md
Expand All @@ -34,17 +34,18 @@ library
Web.Slack.Api
Web.Slack.Auth
Web.Slack.Chat
Web.Slack.Classy
Web.Slack.Common
Web.Slack.Conversation
Web.Slack.MessageParser
Web.Slack.Pager
Web.Slack.Types
Web.Slack.User
Web.Slack.Pager
other-modules:
Web.Slack.Util
build-depends:
aeson >= 1.0 && < 1.6
, base >= 4.11 && < 4.15
, base >= 4.11 && < 4.16
, scientific
, containers
, deepseq
Expand Down
143 changes: 65 additions & 78 deletions src/Web/Slack.hs
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE CPP #-}
{-# LANGUAGE TypeOperators #-}

----------------------------------------------------------------------
-- |
-- Module: Web.Slack
-- Description:
--
--
-- Description: Provides Slack's Web API functions.
-- *Since 0.4.0.0*: The API functions is now more intuitive for newbies
-- than before. If you need compatiblity with the previous version, use
-- 'Web.Slack.Classy' instead.
--
----------------------------------------------------------------------

Expand All @@ -30,8 +31,6 @@ module Web.Slack
, authenticateReq
, Response
, LoadPage
, HasManager(..)
, HasToken(..)
)
where

Expand Down Expand Up @@ -60,7 +59,13 @@ import Servant.API hiding (addHeader)

-- servant-client
import Servant.Client hiding (Response, baseUrl)

#if MIN_VERSION_servant(0,16,0)
import Servant.Client.Core (AuthClientData, AuthenticatedRequest, Request, mkAuthenticatedRequest, addHeader)
#else
import Servant.Client.Core.Internal.Auth
import Servant.Client.Core (Request, addHeader)
#endif

-- slack-web
import qualified Web.Slack.Api as Api
Expand All @@ -79,31 +84,13 @@ mkClientEnv :: Manager -> BaseUrl -> ClientEnv
mkClientEnv = ClientEnv
#endif

#if MIN_VERSION_servant(0,16,0)
import Servant.Client.Core (AuthenticatedRequest, AuthClientData, mkAuthenticatedRequest, ClientError)
#else
import Servant.Client.Core.Internal.Auth
import Servant.Client.Core (ServantError)
type ClientError = ServantError
#endif

class HasManager a where
getManager :: a -> Manager

class HasToken a where
getToken :: a -> Text

-- | Implements the 'HasManager' and 'HasToken' typeclasses.
data SlackConfig
= SlackConfig
{ slackConfigManager :: Manager
, slackConfigToken :: Text
}

instance HasManager SlackConfig where
getManager = slackConfigManager
instance HasToken SlackConfig where
getToken = slackConfigToken

-- contains errors that can be returned by the slack API.
-- constrast with 'SlackClientError' which additionally
Expand All @@ -128,7 +115,6 @@ instance FromJSON a => FromJSON (ResponseJSON a) where
-- |
--
--

type Api =
"api.test"
:> ReqBody '[FormUrlEncoded] Api.TestReq
Expand Down Expand Up @@ -175,27 +161,28 @@ type Api =
-- <https://api.slack.com/methods/api.test>

apiTest
:: (MonadReader env m, HasManager env, MonadIO m)
=> Api.TestReq
-> m (Response Api.TestRsp)
apiTest req = run (apiTest_ req)
:: Manager
-> Api.TestReq
-> IO (Response Api.TestRsp)
apiTest mgr req = run (apiTest_ req) mgr

apiTest_
:: Api.TestReq
-> ClientM (ResponseJSON Api.TestRsp)


-- |
--
-- Check authentication and identity.
--
-- <https://api.slack.com/methods/auth.test>

authTest
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> m (Response Auth.TestRsp)
:: SlackConfig
-> IO (Response Auth.TestRsp)
authTest = do
authR <- mkSlackAuthenticateReq
run (authTest_ authR)
run (authTest_ authR) . slackConfigManager

authTest_
:: AuthenticatedRequest (AuthProtect "token")
Expand All @@ -208,12 +195,12 @@ authTest_
-- <https://api.slack.com/methods/conversations.list>

conversationsList
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> Conversation.ListReq
-> m (Response Conversation.ListRsp)
conversationsList listReq = do
:: SlackConfig
-> Conversation.ListReq
-> IO (Response Conversation.ListRsp)
conversationsList = flip $ \listReq -> do
authR <- mkSlackAuthenticateReq
run (conversationsList_ authR listReq)
run (conversationsList_ authR listReq) . slackConfigManager

conversationsList_
:: AuthenticatedRequest (AuthProtect "token")
Expand All @@ -229,12 +216,12 @@ conversationsList_
-- <https://api.slack.com/methods/conversations.history>

conversationsHistory
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> Conversation.HistoryReq
-> m (Response Conversation.HistoryRsp)
conversationsHistory histReq = do
:: SlackConfig
-> Conversation.HistoryReq
-> IO (Response Conversation.HistoryRsp)
conversationsHistory = flip $ \histReq -> do
authR <- mkSlackAuthenticateReq
run (conversationsHistory_ authR histReq)
run (conversationsHistory_ authR histReq) . slackConfigManager

conversationsHistory_
:: AuthenticatedRequest (AuthProtect "token")
Expand All @@ -251,12 +238,12 @@ conversationsHistory_
-- <https://api.slack.com/methods/conversations.replies>

conversationsReplies
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> Conversation.RepliesReq
-> m (Response Conversation.HistoryRsp)
conversationsReplies repliesReq = do
:: SlackConfig
-> Conversation.RepliesReq
-> IO (Response Conversation.HistoryRsp)
conversationsReplies = flip $ \repliesReq -> do
authR <- mkSlackAuthenticateReq
run (conversationsReplies_ authR repliesReq)
run (conversationsReplies_ authR repliesReq) . slackConfigManager

conversationsReplies_
:: AuthenticatedRequest (AuthProtect "token")
Expand All @@ -271,12 +258,12 @@ conversationsReplies_
-- <https://api.slack.com/methods/chat.postMessage>

chatPostMessage
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> Chat.PostMsgReq
-> m (Response Chat.PostMsgRsp)
chatPostMessage postReq = do
:: SlackConfig
-> Chat.PostMsgReq
-> IO (Response Chat.PostMsgRsp)
chatPostMessage = flip $ \postReq -> do
authR <- mkSlackAuthenticateReq
run (chatPostMessage_ authR postReq)
run (chatPostMessage_ authR postReq) . slackConfigManager

chatPostMessage_
:: AuthenticatedRequest (AuthProtect "token")
Expand All @@ -292,11 +279,11 @@ chatPostMessage_
-- <https://api.slack.com/methods/users.list>

usersList
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> m (Response User.ListRsp)
:: SlackConfig
-> IO (Response User.ListRsp)
usersList = do
authR <- mkSlackAuthenticateReq
run (usersList_ authR)
run (usersList_ authR) . slackConfigManager

usersList_
:: AuthenticatedRequest (AuthProtect "token")
Expand All @@ -310,12 +297,12 @@ usersList_
-- <https://api.slack.com/methods/users.lookupByEmail>

userLookupByEmail
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> User.Email
-> m (Response User.UserRsp)
userLookupByEmail email = do
:: SlackConfig
-> User.Email
-> IO (Response User.UserRsp)
userLookupByEmail = flip $ \email -> do
authR <- mkSlackAuthenticateReq
run (userLookupByEmail_ authR email)
run (userLookupByEmail_ authR email) . slackConfigManager

userLookupByEmail_
:: AuthenticatedRequest (AuthProtect "token")
Expand Down Expand Up @@ -343,13 +330,13 @@ getUserDesc unknownUserFn users =
-- To fetch all messages in the conversation, run the returned 'LoadPage' action
-- repeatedly until it returns an empty list.
conversationsHistoryAll
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> Conversation.HistoryReq
:: SlackConfig
-> Conversation.HistoryReq
-- ^ The first request to send. _NOTE_: 'Conversation.historyReqCursor' is silently ignored.
-> m (LoadPage m Common.Message)
-> IO (LoadPage IO Common.Message)
-- ^ An action which returns a new page of messages every time called.
-- If there are no pages anymore, it returns an empty list.
conversationsHistoryAll = conversationsHistoryAllBy conversationsHistory
conversationsHistoryAll = conversationsHistoryAllBy . conversationsHistory


-- | Returns an action to send a request to get the replies of a conversation.
Expand All @@ -362,13 +349,14 @@ conversationsHistoryAll = conversationsHistoryAllBy conversationsHistory
-- the first message of the thread. You should drop it if you want to
-- collect messages in a thread without duplicates.
repliesFetchAll
:: (MonadReader env m, HasManager env, HasToken env, MonadIO m)
=> Conversation.RepliesReq
:: SlackConfig
-> Conversation.RepliesReq
-- ^ The first request to send. _NOTE_: 'Conversation.repliesReqCursor' is silently ignored.
-> m (LoadPage m Common.Message)
-> IO (LoadPage IO Common.Message)
-- ^ An action which returns a new page of messages every time called.
-- If there are no pages anymore, it returns an empty list.
repliesFetchAll = repliesFetchAllBy conversationsReplies
repliesFetchAll = repliesFetchAllBy . conversationsReplies


apiTest_
:<|> authTest_
Expand All @@ -393,7 +381,6 @@ type instance AuthClientData (AuthProtect "token") =
-- |
--
--

authenticateReq
:: Text
-> Request
Expand All @@ -407,17 +394,17 @@ authenticateReq token =
--

run
:: (MonadReader env m, HasManager env, MonadIO m)
=> ClientM (ResponseJSON a)
-> m (Response a)
run clientAction = do
env <- ask
:: ClientM (ResponseJSON a)
-> Manager
-> IO (Response a)
run clientAction mgr = do
let baseUrl = BaseUrl Https "slack.com" 443 "/api"
unnestErrors <$> liftIO (runClientM clientAction $ mkClientEnv (getManager env) baseUrl)
unnestErrors <$> liftIO (runClientM clientAction $ mkClientEnv mgr baseUrl)


mkSlackAuthenticateReq :: SlackConfig -> AuthenticatedRequest (AuthProtect "token")
mkSlackAuthenticateReq = (`mkAuthenticatedRequest` authenticateReq) . slackConfigToken

mkSlackAuthenticateReq :: (MonadReader env m, HasToken env)
=> m (AuthenticatedRequest (AuthProtect "token"))
mkSlackAuthenticateReq = flip mkAuthenticatedRequest authenticateReq . getToken <$> ask

unnestErrors :: Either ClientError (ResponseJSON a) -> Response a
unnestErrors (Right (ResponseJSON (Right a))) = Right a
Expand Down
Loading