packages feed

globus-0.2.0: src/Effectful/Globus.hs

module Effectful.Globus
  ( Globus (..)
  , GlobusClient (..)
  , runGlobus
  , State (..)
  , Tagged (..)
  , TransferRequest (..)
  , TransferResponse (..)
  , TransferItem (..)
  , SyncLevel (..)
  , Task (..)
  , TaskStatus (..)
  , TaskFilters (..)
  , TaskList (..)
  , module Network.Globus.Types
  , requireScopeToken
  ) where

import Control.Monad.Catch (catch)
import Data.List.NonEmpty (NonEmpty)
import Data.Tagged
import Effectful
import Effectful.Dispatch.Dynamic
import Effectful.Error.Static
import Network.Globus.Auth
import Network.Globus.Transfer
import Network.Globus.Types hiding (appendQuery, param)
import Network.HTTP.Client (Manager)


data GlobusClient = GlobusClient
  { clientId :: Token ClientId
  , clientSecret :: Token ClientSecret
  }


data Globus :: Effect where
  AuthUrl :: Uri Redirect -> NonEmpty Scope -> State -> Globus m (Uri Authorization)
  GetUserInfo :: Token OpenId -> Globus m UserInfoResponse
  GetAccessTokens :: Token Exchange -> Uri Redirect -> Globus m (NonEmpty TokenItem)
  GetSubmissionId :: Token Access -> Globus m (Id Submission)
  Transfer :: Token Access -> TransferRequest -> Globus m TransferResponse
  StatusTask :: Token Access -> Id Task -> Globus m Task
  StatusTasks :: Token Access -> TaskFilters -> Globus m TaskList


type instance DispatchOf Globus = 'Dynamic


runGlobus
  :: (IOE :> es, Error GlobusError :> es)
  => GlobusClient
  -> Manager
  -> Eff (Globus : es) a
  -> Eff es a
runGlobus g mgr = interpret $ \_ -> \case
  GetAccessTokens exc red -> do
    runGlobusIO $ fetchAccessTokens mgr g.clientId g.clientSecret red exc
  GetUserInfo ti -> do
    runGlobusIO $ fetchUserInfo mgr ti
  AuthUrl red scopes state -> do
    pure $ authorizationUrl g.clientId red scopes state
  GetSubmissionId access -> do
    runGlobusIO $ fetchSubmissionId mgr access
  Transfer access request -> do
    runGlobusIO $ sendTransfer mgr access request
  StatusTask access ti -> do
    runGlobusIO $ fetchTask mgr access ti
  StatusTasks access tf -> do
    runGlobusIO $ fetchTasks mgr access tf
 where
  onGlobusErr :: (Error GlobusError :> es) => GlobusError -> Eff es a
  onGlobusErr = throwError

  runGlobusIO :: (IOE :> es, Error GlobusError :> es) => IO a -> Eff es a
  runGlobusIO ma = catch (liftIO ma) onGlobusErr


requireScopeToken :: (Error GlobusError :> es) => Scope -> NonEmpty TokenItem -> Eff es (Token a)
requireScopeToken s tis = do
  Tagged t <- maybe (throwError $ MissingScope s tis) pure $ scopeToken s tis
  pure $ Tagged t