hoauth2-1.3.0: example/Fitbit/test.hs
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Main where
import Control.Monad (mzero)
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as BL
import Data.Char (chr)
import qualified Data.Map as M
import Data.Text (Text)
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Network.HTTP.Conduit hiding (Request, queryString)
import Network.HTTP.Types (Query, status200)
import Network.Wai
import Network.Wai.Handler.Warp (run)
import URI.ByteString (serializeURIRef')
import URI.ByteString.QQ
import Keys (fitbitKey)
import Network.OAuth.OAuth2
import GHC.Generics
import Data.Aeson
import Data.Aeson.Types
data Errors =
SomeRandomError
deriving (Show, Eq, Generic)
instance FromJSON Errors where
parseJSON = genericParseJSON defaultOptions { constructorTagModifier = camelTo2 '_', allNullaryToStringTag = True }
------------------------------------------------------------------------------
data FitbitUser = FitbitUser
{ userId :: Text
, userName :: Text
, userAge :: Int
} deriving (Show, Eq)
instance FromJSON FitbitUser where
parseJSON (Object o) =
FitbitUser
<$> ((o .: "user") >>= (.: "encodedId"))
<*> ((o .: "user") >>= (.: "fullName"))
<*> ((o .: "user") >>= (.: "age"))
parseJSON _ = mzero
instance ToJSON FitbitUser where
toJSON (FitbitUser fid name age) =
object [ "id" .= fid
, "name" .= name
, "age" .= age
]
------------------------------------------------------------------------------
main :: IO ()
main = do
print $ serializeURIRef' $ appendQueryParams [("state", state), ("scope", "profile")] $ authorizationUrl fitbitKey
putStrLn "visit the url to continue"
run 9988 application
state :: B.ByteString
state = "testFitbitApi"
application :: Application
application request respond = do
response <- handleRequest requestPath request
respond $ responseLBS status200 [("Content-Type", "text/plain")] response
where
requestPath = T.intercalate "/" $ pathInfo request
handleRequest :: Text -> Request -> IO BL.ByteString
handleRequest "favicon.ico" _ = return ""
handleRequest _ request = do
mgr <- newManager tlsManagerSettings
token <- getApiToken mgr $ getApiCode request
print token
user <- getApiUser mgr (accessToken token)
print user
return $ encode user
getApiCode :: Request -> ExchangeToken
getApiCode request =
case M.lookup "code" queryMap of
Just code -> ExchangeToken $ T.decodeUtf8 code
Nothing -> Prelude.error "request doesn't include code"
where
queryMap = convertQueryToMap $ queryString request
getApiToken :: Manager -> ExchangeToken -> IO OAuth2Token
getApiToken mgr code = do
result <- doJSONPostRequest mgr fitbitKey url $ body ++ [("state", state)]
case result of
Right token -> return token
Left (e :: OAuth2Error Errors) -> Prelude.error $ show e
where
(url, body) = accessTokenUrl fitbitKey code
getApiUser :: Manager -> AccessToken -> IO FitbitUser
getApiUser mgr token = do
result <- authGetJSON mgr token [uri|https://api.fitbit.com/1/user/-/profile.json|]
case result of
Right user -> return user
Left (e :: OAuth2Error Errors) -> Prelude.error $ show e
convertQueryToMap :: Query -> M.Map B.ByteString B.ByteString
convertQueryToMap query =
M.fromList $ map normalize query
where
normalize (k, Just v) = (k, v)
normalize (k, Nothing) = (k, B.empty)
lazyBSToString :: BL.ByteString -> String
lazyBSToString s = map (chr . fromIntegral) (BL.unpack s)