-- | -- Module : Web.Harvest.API -- Copyright : © 2016 Stack Builders -- License : MIT -- -- Maintainer : Mark Karpov -- Stability : experimental -- Portability : portable -- -- High-level bindings to the Harvest web API. {-# LANGUAGE DataKinds #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE TypeOperators #-} module Web.Harvest.API ( -- * Types module Web.Harvest.API.Type -- * Users , getUsers -- * Timesheets , getTimeEntries ) where import Control.Monad.Except import Data.Monoid (Sum (..)) import Data.Proxy import Data.Time (Day) import Data.Void import Network.HTTP.Client (Manager) import Servant.API import Servant.Client import Web.Harvest.API.Type import qualified Data.ByteString.Char8 as BC8 import qualified Data.Time as Time -- | Type representation of Basic Auth. We don't serve the API, so user type -- is just 'Void'. type Auth = BasicAuth "" Void -- | Entire Harvest API as a type. type HarvestAPI = "people" :> Auth :> Get '[JSON] [User] :<|> "daily" :> Capture "day" Word :> Capture "year" Word :> QueryParam "of_user" UserId :> Auth :> Get '[JSON] TimeEntries -- | A shortcut for the boilerplate arguments. type Query a = BasicAuthData -> Manager -> BaseUrl -> ClientM a getUsers_ :: Query [User] getTimeEntries_ :: Word -> Word -> Maybe UserId -> Query TimeEntries getUsers_ :<|> getTimeEntries_ = client (Proxy :: Proxy HarvestAPI) -- | Get list of all users for specific account. getUsers :: MonadIO m => Manager -- ^ HTTPS manager -> Credentials -- ^ Credentials -> m (Either ServantError [User]) -- ^ Result of request getUsers = runHarvestQuery getUsers_ -- | Get time entries for specific date and user. getTimeEntries :: MonadIO m => Manager -- ^ HTTPS manager -> Credentials -- ^ Credentials -> Day -- ^ Date of interest -> UserId -- ^ User id -> m (Either ServantError TimeEntries) getTimeEntries manager creds date uid = runHarvestQuery (getTimeEntries_ day year (Just uid)) manager creds where (day, year) = getDayAndYear date -- | A helper to run a query against Harvest API. runHarvestQuery :: MonadIO m => (BasicAuthData -> Manager -> BaseUrl -> ClientM a) -- ^ Query function -> Manager -- ^ HTTPS Manager -> Credentials -- ^ Credentials -> m (Either ServantError a) -- ^ The result runHarvestQuery action manager Credentials {..} = liftIO $ do let host = BC8.unpack credentialsAccount ++ ".harvestapp.com" authData = BasicAuthData credentialsUsername credentialsPassword runExceptT (action authData manager (BaseUrl Https host 443 "")) -- | Extract day and year from given date. Days are in the range from 1 to -- 366. getDayAndYear :: Day -> (Word, Word) getDayAndYear date = (day, fromIntegral year) where (year, month, day') = Time.toGregorian date day = fromIntegral . (+ day') . getSum . foldMap (Sum . Time.gregorianMonthLength year) $ [1..pred month]