{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
module Network.Beeminder
( -- * API calls
user
, goal, allGoals
, createGoal
, points
, createPoint , createPointNotify
, createPoints, createPointsNotify
, updatePoint , deletePoint
-- * The Beeminder monad
, Beeminder
, Token
, runBeeminder
-- * Foo bar
, UserGoals(..)
, User(..)
, Point(..)
, Goal(..)
-- UserGoals stuff
, Burner(..), LevelOfGoalDetail(..)
-- Goal stuff
, TimeFrame(..), Aggregate(..), Direction(..), Behavior(..), Target(..), GoalType(..)
-- * Lenses
, HasID(..), HasUpdatedAt(..), HasName(..), HasTimezone(..), HasUsername(..), HasGoals(..), HasGoalsFilter(..), HasLevelOfDetail(..)
, HasPointCount(..), HasTimestamp(..), HasValue(..), HasComment(..), HasRequestID(..), HasGoal(..), HasPointRequest(..), HasPointRequests(..)
, HasGetPoints(..), HasTitle(..), HasType(..), HasTarget(..), HasBehavior(..), HasPanic(..)
-- * Utilities
, now
, gType
) where
import Control.Applicative
import Control.Monad.Base
import Control.Monad.Reader
import Control.Monad.Trans.Control
import Control.Monad.Trans.Maybe
import Control.Monad.Trans.Resource
import Data.Aeson
import Data.Conduit
import Data.Default.Class
import Network.Beeminder.Internal hiding (allGoals, createGoal,
createPoint, createPointNotify,
createPoints, createPointsNotify,
deletePoint, goal, points,
updatePoint, user)
import qualified Network.Beeminder.Internal as Internal
import Network.HTTP.Conduit
data BeeminderEnvironment = BeeminderEnvironment
{ token :: Token
, manager :: Manager
}
type Beeminder_ = MaybeT (ReaderT BeeminderEnvironment (ResourceT IO))
newtype Beeminder a = Beeminder { unBeeminder :: Beeminder_ a }
deriving (Functor, Applicative, Monad, MonadIO, MonadReader BeeminderEnvironment, MonadThrow, MonadResource, MonadBase IO)
-- The following instance (and the "deriving" clause for MonadThrow,
-- MonadUnsafeIO, MonadResource, and MonadBase IO) were copied basically
-- verbatim from the "dgs" package, and even there they were written just "by
-- typechecking" rather than with some deep understanding of what's happening.
-- So it wouldn't surprise me if there's bugs here.
instance MonadBaseControl IO Beeminder where
type StM Beeminder a = StM Beeminder_ a
liftBaseWith f = Beeminder (liftBaseWith (\g -> f (\(Beeminder m) -> g m)))
restoreM v = Beeminder (restoreM v)
-- | Run a beeminder computation with the given authentication token,
-- possibly returning a result.
runBeeminder :: Token -> Beeminder a -> IO (Maybe a)
runBeeminder t m = do
man <- newManager tlsManagerSettings
runResourceT (runReaderT (runMaybeT (unBeeminder m)) BeeminderEnvironment { token = t, manager = man })
-- | Turn a raw operation taking a token and returning a 'Request'
-- into a nicely encapsulated action in the 'Beeminder' monad.
externalize :: FromJSON a => (Token -> params -> Request) -> params -> Beeminder a
externalize f p = do
BeeminderEnvironment { token = t, manager = m } <- ask
r <- httpLbs (f t p) {responseTimeout = Nothing} m
Beeminder . MaybeT . return . decode . responseBody $ r
user :: UserParameters -> Beeminder User
goal :: GoalParameters -> Beeminder Goal
allGoals :: AllGoalsParameters -> Beeminder [Goal]
createGoal :: CreateGoalParameters -> Beeminder Goal
points :: PointsParameters -> Beeminder [Point]
createPoint , createPointNotify :: CreatePointParameters -> Beeminder Point
createPoints, createPointsNotify :: CreatePointsParameters -> Beeminder [Point]
updatePoint :: UpdatePointParameters -> Beeminder Point
deletePoint :: DeletePointParameters -> Beeminder Point
user = externalize Internal.user
goal = externalize Internal.goal
allGoals = externalize Internal.allGoals
createGoal = externalize Internal.createGoal
points = externalize Internal.points
createPoint = externalize Internal.createPoint
createPointNotify = externalize Internal.createPointNotify
createPoints = externalize Internal.createPoints
createPointsNotify = externalize Internal.createPointsNotify
updatePoint = externalize Internal.updatePoint
deletePoint = externalize Internal.deletePoint