globus 0.1.3 → 0.2.0
raw patch · 7 files changed
+317/−198 lines, 7 filesdep +exceptionsdep +filepathdep +http-clientdep −reqPVP ok
version bump matches the API change (PVP)
Dependencies added: exceptions, filepath, http-client, http-client-tls, network-uri
Dependencies removed: req
API changes (from Hackage documentation)
- Effectful.Globus: Http :: Scheme
- Effectful.Globus: Https :: Scheme
- Effectful.Globus: [SubmissionId] :: forall (a :: Type -> Type). Token 'Access -> Globus a (Tagged 'Submission Text)
- Effectful.Globus: data Scheme
- Network.Globus: Http :: Scheme
- Network.Globus: Https :: Scheme
- Network.Globus: data Scheme
- Network.Globus.Auth: State :: Text -> State
- Network.Globus.Auth: TokenItem :: Scopes -> Token 'Access -> Int -> State -> TokenItem
- Network.Globus.Auth: [access_token] :: TokenItem -> Token 'Access
- Network.Globus.Auth: [expires_in] :: TokenItem -> Int
- Network.Globus.Auth: [scope] :: TokenItem -> Scopes
- Network.Globus.Auth: [state] :: TokenItem -> State
- Network.Globus.Auth: data TokenItem
- Network.Globus.Auth: instance Data.Aeson.Types.FromJSON.FromJSON Network.Globus.Auth.State
- Network.Globus.Auth: instance Data.Aeson.Types.FromJSON.FromJSON Network.Globus.Auth.TokenItem
- Network.Globus.Auth: instance Data.String.IsString Network.Globus.Auth.State
- Network.Globus.Auth: instance GHC.Generics.Generic Network.Globus.Auth.TokenItem
- Network.Globus.Auth: instance GHC.Generics.Generic Network.Globus.Auth.UserInfo
- Network.Globus.Auth: instance GHC.Generics.Generic Network.Globus.Auth.UserProfile
- Network.Globus.Auth: instance GHC.Show.Show Network.Globus.Auth.State
- Network.Globus.Auth: instance GHC.Show.Show Network.Globus.Auth.TokenItem
- Network.Globus.Auth: instance GHC.Show.Show Network.Globus.Auth.TokenResponse
- Network.Globus.Auth: instance GHC.Show.Show Network.Globus.Auth.UserEmail
- Network.Globus.Auth: instance GHC.Show.Show Network.Globus.Auth.UserInfo
- Network.Globus.Auth: instance GHC.Show.Show Network.Globus.Auth.UserProfile
- Network.Globus.Auth: newtype State
- Network.Globus.Transfer: dataLabels :: String -> String
- Network.Globus.Transfer: dataLabelsJson :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
- Network.Globus.Transfer: instance GHC.Base.Monoid Network.Globus.Transfer.TaskFilters
- Network.Globus.Transfer: instance GHC.Base.Semigroup Network.Globus.Transfer.TaskFilters
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.IdResponse
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.Task
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.TaskList
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.TaskStatus
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.TransferItem
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.TransferRequest
- Network.Globus.Transfer: instance GHC.Generics.Generic Network.Globus.Transfer.TransferResponse
- Network.Globus.Transfer: instance GHC.Show.Show Network.Globus.Transfer.IdResponse
- Network.Globus.Transfer: instance GHC.Show.Show Network.Globus.Transfer.Task
- Network.Globus.Transfer: instance GHC.Show.Show Network.Globus.Transfer.TaskFilters
- Network.Globus.Transfer: instance GHC.Show.Show Network.Globus.Transfer.TaskList
- Network.Globus.Transfer: instance GHC.Show.Show Network.Globus.Transfer.TaskStatus
- Network.Globus.Transfer: instance GHC.Show.Show Network.Globus.Transfer.TransferResponse
- Network.Globus.Types: Query :: [(Text, Maybe Text)] -> Query
- Network.Globus.Types: Uri :: Scheme -> Text -> [Text] -> Query -> Uri (a :: Endpoint)
- Network.Globus.Types: [domain] :: Uri (a :: Endpoint) -> Text
- Network.Globus.Types: [params] :: Uri (a :: Endpoint) -> Query
- Network.Globus.Types: [path] :: Uri (a :: Endpoint) -> [Text]
- Network.Globus.Types: [scheme] :: Uri (a :: Endpoint) -> Scheme
- Network.Globus.Types: data Uri (a :: Endpoint)
- Network.Globus.Types: instance GHC.Base.Monoid Network.Globus.Types.Query
- Network.Globus.Types: instance GHC.Base.Semigroup Network.Globus.Types.Query
- Network.Globus.Types: instance GHC.IsList.IsList Network.Globus.Types.Query
- Network.Globus.Types: instance GHC.Show.Show (Network.Globus.Types.Uri a)
- Network.Globus.Types: instance GHC.Show.Show Network.Globus.Types.Query
- Network.Globus.Types: instance GHC.Show.Show Network.Globus.Types.Scope
- Network.Globus.Types: instance GHC.Show.Show Network.Globus.Types.ScopeIdentity
- Network.Globus.Types: instance GHC.Show.Show Network.Globus.Types.Scopes
- Network.Globus.Types: instance GHC.TypeLits.KnownSymbol s => Data.Aeson.Types.ToJSON.ToJSON (Network.Globus.Types.DataType s)
- Network.Globus.Types: instance Network.HTTP.Req.QueryParam Network.Globus.Types.Query
- Network.Globus.Types: newtype Query
- Network.Globus.Types: renderQuery :: Query -> Text
+ Effectful.Globus: (/:) :: forall {k} (a :: k). Uri a -> String -> Uri a
+ Effectful.Globus: Access :: Token'
+ Effectful.Globus: App :: Endpoint
+ Effectful.Globus: Authorization :: Endpoint
+ Effectful.Globus: ClientId :: Token'
+ Effectful.Globus: ClientSecret :: Token'
+ Effectful.Globus: Collection :: Id'
+ Effectful.Globus: DataKey :: DataType s -> Text -> DataKey (s :: Symbol)
+ Effectful.Globus: DataType :: DataType (s :: Symbol)
+ Effectful.Globus: Email :: ScopeIdentity
+ Effectful.Globus: Exchange :: Token'
+ Effectful.Globus: Identity :: ScopeIdentity -> Scope
+ Effectful.Globus: InvalidURI :: String -> URI -> GlobusError
+ Effectful.Globus: MissingScope :: Scope -> NonEmpty TokenItem -> GlobusError
+ Effectful.Globus: OpenId :: ScopeIdentity
+ Effectful.Globus: Profile :: ScopeIdentity
+ Effectful.Globus: Redirect :: Endpoint
+ Effectful.Globus: Request :: Id'
+ Effectful.Globus: ResponseBadJSON :: Request -> String -> ByteString -> GlobusError
+ Effectful.Globus: ResponseBadStatus :: Request -> Status -> ByteString -> GlobusError
+ Effectful.Globus: Scopes :: NonEmpty Scope -> Scopes
+ Effectful.Globus: Submission :: Id'
+ Effectful.Globus: TokenItem :: Scopes -> Token 'Access -> Int -> State -> TokenItem
+ Effectful.Globus: Tokens :: Endpoint
+ Effectful.Globus: TransferAll :: Scope
+ Effectful.Globus: Unauthorized :: Request -> ByteString -> GlobusError
+ Effectful.Globus: [GetSubmissionId] :: forall (a :: Type -> Type). Token 'Access -> Globus a (Tagged 'Submission Text)
+ Effectful.Globus: [access_token] :: TokenItem -> Token 'Access
+ Effectful.Globus: [expires_in] :: TokenItem -> Int
+ Effectful.Globus: [scope] :: TokenItem -> Scopes
+ Effectful.Globus: [state] :: TokenItem -> State
+ Effectful.Globus: [value] :: DataKey (s :: Symbol) -> Text
+ Effectful.Globus: data DataKey (s :: Symbol)
+ Effectful.Globus: data DataType (s :: Symbol)
+ Effectful.Globus: data Endpoint
+ Effectful.Globus: data GlobusError
+ Effectful.Globus: data Id'
+ Effectful.Globus: data Scope
+ Effectful.Globus: data ScopeIdentity
+ Effectful.Globus: data Token'
+ Effectful.Globus: data TokenItem
+ Effectful.Globus: dataLabels :: String -> String
+ Effectful.Globus: dataLabelsFromJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
+ Effectful.Globus: dataLabelsToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
+ Effectful.Globus: infixl 5 /:
+ Effectful.Globus: newtype Scopes
+ Effectful.Globus: renderUri :: forall {k} (a :: k). Uri a -> Text
+ Effectful.Globus: requireScopeToken :: forall {k} (es :: [Effect]) (a :: k). Error GlobusError :> es => Scope -> NonEmpty TokenItem -> Eff es (Token a)
+ Effectful.Globus: scope :: Text -> Maybe Scope
+ Effectful.Globus: scopeText :: Scope -> Text
+ Effectful.Globus: type Id (a :: k) = Tagged a Text
+ Effectful.Globus: type Token (a :: k) = Tagged a Text
+ Effectful.Globus: type Uri (a :: k) = Tagged a URI
+ Network.Globus: (/:) :: forall {k} (a :: k). Uri a -> String -> Uri a
+ Network.Globus: Access :: Token'
+ Network.Globus: App :: Endpoint
+ Network.Globus: Authorization :: Endpoint
+ Network.Globus: ClientId :: Token'
+ Network.Globus: ClientSecret :: Token'
+ Network.Globus: Collection :: Id'
+ Network.Globus: DataKey :: DataType s -> Text -> DataKey (s :: Symbol)
+ Network.Globus: DataType :: DataType (s :: Symbol)
+ Network.Globus: Email :: ScopeIdentity
+ Network.Globus: Exchange :: Token'
+ Network.Globus: Identity :: ScopeIdentity -> Scope
+ Network.Globus: InvalidURI :: String -> URI -> GlobusError
+ Network.Globus: MissingScope :: Scope -> NonEmpty TokenItem -> GlobusError
+ Network.Globus: OpenId :: ScopeIdentity
+ Network.Globus: Profile :: ScopeIdentity
+ Network.Globus: Redirect :: Endpoint
+ Network.Globus: Request :: Id'
+ Network.Globus: ResponseBadJSON :: Request -> String -> ByteString -> GlobusError
+ Network.Globus: ResponseBadStatus :: Request -> Status -> ByteString -> GlobusError
+ Network.Globus: Scopes :: NonEmpty Scope -> Scopes
+ Network.Globus: State :: Text -> State
+ Network.Globus: Submission :: Id'
+ Network.Globus: TokenItem :: Scopes -> Token 'Access -> Int -> State -> TokenItem
+ Network.Globus: Tokens :: Endpoint
+ Network.Globus: TransferAll :: Scope
+ Network.Globus: Unauthorized :: Request -> ByteString -> GlobusError
+ Network.Globus: [access_token] :: TokenItem -> Token 'Access
+ Network.Globus: [data_type] :: DataKey (s :: Symbol) -> DataType s
+ Network.Globus: [expires_in] :: TokenItem -> Int
+ Network.Globus: [scope] :: TokenItem -> Scopes
+ Network.Globus: [state] :: TokenItem -> State
+ Network.Globus: [value] :: DataKey (s :: Symbol) -> Text
+ Network.Globus: data DataKey (s :: Symbol)
+ Network.Globus: data DataType (s :: Symbol)
+ Network.Globus: data Endpoint
+ Network.Globus: data GlobusError
+ Network.Globus: data Id'
+ Network.Globus: data Scope
+ Network.Globus: data ScopeIdentity
+ Network.Globus: data Token'
+ Network.Globus: data TokenItem
+ Network.Globus: dataLabels :: String -> String
+ Network.Globus: dataLabelsFromJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
+ Network.Globus: dataLabelsToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
+ Network.Globus: infixl 5 /:
+ Network.Globus: newtype Scopes
+ Network.Globus: newtype State
+ Network.Globus: renderUri :: forall {k} (a :: k). Uri a -> Text
+ Network.Globus: scope :: Text -> Maybe Scope
+ Network.Globus: scopeText :: Scope -> Text
+ Network.Globus: type Id (a :: k) = Tagged a Text
+ Network.Globus: type Token (a :: k) = Tagged a Text
+ Network.Globus: type Uri (a :: k) = Tagged a URI
+ Network.Globus.Auth: authEndpoint :: Uri 'Authorization
+ Network.Globus.Auth: instance GHC.Internal.Generics.Generic Network.Globus.Auth.UserInfo
+ Network.Globus.Auth: instance GHC.Internal.Generics.Generic Network.Globus.Auth.UserProfile
+ Network.Globus.Auth: instance GHC.Internal.Show.Show Network.Globus.Auth.TokenResponse
+ Network.Globus.Auth: instance GHC.Internal.Show.Show Network.Globus.Auth.UserEmail
+ Network.Globus.Auth: instance GHC.Internal.Show.Show Network.Globus.Auth.UserInfo
+ Network.Globus.Auth: instance GHC.Internal.Show.Show Network.Globus.Auth.UserProfile
+ Network.Globus.Request: get :: forall {k} m (a :: k). (MonadThrow m, MonadCatch m) => Uri a -> [Header] -> m Request
+ Network.Globus.Request: oAuth2Bearer :: forall {k} (a :: k). Token a -> Header
+ Network.Globus.Request: post :: forall {k} m b (a :: k). (MonadThrow m, MonadCatch m, ToJSON b) => Uri a -> [Header] -> b -> m Request
+ Network.Globus.Request: request :: forall {k} m (a :: k). (MonadThrow m, MonadCatch m) => Method -> Uri a -> [Header] -> RequestBody -> m Request
+ Network.Globus.Request: sendJSON :: (MonadIO m, FromJSON a, MonadThrow m) => Manager -> Request -> m a
+ Network.Globus.Transfer: instance GHC.Internal.Base.Monoid Network.Globus.Transfer.TaskFilters
+ Network.Globus.Transfer: instance GHC.Internal.Base.Semigroup Network.Globus.Transfer.TaskFilters
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.IdResponse
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.Task
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.TaskList
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.TaskStatus
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.TransferItem
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.TransferRequest
+ Network.Globus.Transfer: instance GHC.Internal.Generics.Generic Network.Globus.Transfer.TransferResponse
+ Network.Globus.Transfer: instance GHC.Internal.Show.Show Network.Globus.Transfer.IdResponse
+ Network.Globus.Transfer: instance GHC.Internal.Show.Show Network.Globus.Transfer.Task
+ Network.Globus.Transfer: instance GHC.Internal.Show.Show Network.Globus.Transfer.TaskFilters
+ Network.Globus.Transfer: instance GHC.Internal.Show.Show Network.Globus.Transfer.TaskList
+ Network.Globus.Transfer: instance GHC.Internal.Show.Show Network.Globus.Transfer.TaskStatus
+ Network.Globus.Types: (/:) :: forall {k} (a :: k). Uri a -> String -> Uri a
+ Network.Globus.Types: DataKey :: DataType s -> Text -> DataKey (s :: Symbol)
+ Network.Globus.Types: InvalidURI :: String -> URI -> GlobusError
+ Network.Globus.Types: MissingScope :: Scope -> NonEmpty TokenItem -> GlobusError
+ Network.Globus.Types: ResponseBadJSON :: Request -> String -> ByteString -> GlobusError
+ Network.Globus.Types: ResponseBadStatus :: Request -> Status -> ByteString -> GlobusError
+ Network.Globus.Types: State :: Text -> State
+ Network.Globus.Types: TokenItem :: Scopes -> Token 'Access -> Int -> State -> TokenItem
+ Network.Globus.Types: Unauthorized :: Request -> ByteString -> GlobusError
+ Network.Globus.Types: [access_token] :: TokenItem -> Token 'Access
+ Network.Globus.Types: [data_type] :: DataKey (s :: Symbol) -> DataType s
+ Network.Globus.Types: [expires_in] :: TokenItem -> Int
+ Network.Globus.Types: [scope] :: TokenItem -> Scopes
+ Network.Globus.Types: [state] :: TokenItem -> State
+ Network.Globus.Types: [value] :: DataKey (s :: Symbol) -> Text
+ Network.Globus.Types: appendQuery :: Text -> Maybe Text -> String -> String
+ Network.Globus.Types: data DataKey (s :: Symbol)
+ Network.Globus.Types: data GlobusError
+ Network.Globus.Types: data TokenItem
+ Network.Globus.Types: dataLabels :: String -> String
+ Network.Globus.Types: dataLabelsFromJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a
+ Network.Globus.Types: dataLabelsToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value
+ Network.Globus.Types: infixl 5 /:
+ Network.Globus.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Network.Globus.Types.DataKey s)
+ Network.Globus.Types: instance Data.Aeson.Types.FromJSON.FromJSON (Network.Globus.Types.DataType s)
+ Network.Globus.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Globus.Types.State
+ Network.Globus.Types: instance Data.Aeson.Types.FromJSON.FromJSON Network.Globus.Types.TokenItem
+ Network.Globus.Types: instance GHC.Classes.Eq Network.Globus.Types.Scopes
+ Network.Globus.Types: instance GHC.Classes.Eq Network.Globus.Types.State
+ Network.Globus.Types: instance GHC.Classes.Eq Network.Globus.Types.TokenItem
+ Network.Globus.Types: instance GHC.Internal.Data.String.IsString Network.Globus.Types.State
+ Network.Globus.Types: instance GHC.Internal.Exception.Type.Exception Network.Globus.Types.GlobusError
+ Network.Globus.Types: instance GHC.Internal.Generics.Generic (Network.Globus.Types.DataKey s)
+ Network.Globus.Types: instance GHC.Internal.Generics.Generic Network.Globus.Types.TokenItem
+ Network.Globus.Types: instance GHC.Internal.Show.Show Network.Globus.Types.GlobusError
+ Network.Globus.Types: instance GHC.Internal.Show.Show Network.Globus.Types.Scope
+ Network.Globus.Types: instance GHC.Internal.Show.Show Network.Globus.Types.ScopeIdentity
+ Network.Globus.Types: instance GHC.Internal.Show.Show Network.Globus.Types.Scopes
+ Network.Globus.Types: instance GHC.Internal.Show.Show Network.Globus.Types.State
+ Network.Globus.Types: instance GHC.Internal.Show.Show Network.Globus.Types.TokenItem
+ Network.Globus.Types: instance GHC.Internal.TypeLits.KnownSymbol s => Data.Aeson.Types.ToJSON.ToJSON (Network.Globus.Types.DataType s)
+ Network.Globus.Types: newtype State
+ Network.Globus.Types: param :: forall {k} (a :: k). Text -> Text -> Uri a -> Uri a
+ Network.Globus.Types: type Uri (a :: k) = Tagged a URI
- Effectful.Globus: TransferResponse :: Id Task -> Token 'Submission -> Text -> Text -> Token 'Request -> TransferResponse
+ Effectful.Globus: TransferResponse :: Id Task -> Id 'Submission -> Text -> Text -> Token Request -> TransferResponse
- Effectful.Globus: [AuthUrl] :: forall (a :: Type -> Type). Uri 'Redirect -> NonEmpty Scope -> State -> Globus a (Uri 'Authorization)
+ Effectful.Globus: [AuthUrl] :: forall (a :: Type -> Type). Uri 'Redirect -> NonEmpty Scope -> State -> Globus a (Tagged 'Authorization URI)
- Effectful.Globus: [data_type] :: TransferItem -> DataType "transfer_item"
+ Effectful.Globus: [data_type] :: DataKey (s :: Symbol) -> DataType s
- Effectful.Globus: [request_id] :: TransferResponse -> Token 'Request
+ Effectful.Globus: [request_id] :: TransferResponse -> Token Request
- Effectful.Globus: [submission_id] :: TransferResponse -> Token 'Submission
+ Effectful.Globus: [submission_id] :: TransferResponse -> Id 'Submission
- Effectful.Globus: runGlobus :: forall (es :: [Effect]) a. IOE :> es => GlobusClient -> Eff (Globus ': es) a -> Eff es a
+ Effectful.Globus: runGlobus :: forall (es :: [Effect]) a. (IOE :> es, Error GlobusError :> es) => GlobusClient -> Manager -> Eff (Globus ': es) a -> Eff es a
- Network.Globus.Auth: fetchAccessTokens :: MonadIO m => Token 'ClientId -> Token 'ClientSecret -> Uri 'Redirect -> Token 'Exchange -> m (NonEmpty TokenItem)
+ Network.Globus.Auth: fetchAccessTokens :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token 'ClientId -> Token 'ClientSecret -> Uri 'Redirect -> Token 'Exchange -> m (NonEmpty TokenItem)
- Network.Globus.Auth: fetchUserInfo :: MonadIO m => Token 'OpenId -> m UserInfoResponse
+ Network.Globus.Auth: fetchUserInfo :: (MonadIO m, MonadCatch m, MonadThrow m) => Manager -> Token 'OpenId -> m UserInfoResponse
- Network.Globus.Auth: identityAuth :: Token 'OpenId -> Option 'Https
+ Network.Globus.Auth: identityAuth :: Token 'OpenId -> Header
- Network.Globus.Auth: redirectUri :: QueryParam param => Uri 'Redirect -> param
+ Network.Globus.Auth: redirectUri :: forall {k} (a :: k). Uri 'Redirect -> Uri a -> Uri a
- Network.Globus.Transfer: TransferResponse :: Id Task -> Token 'Submission -> Text -> Text -> Token 'Request -> TransferResponse
+ Network.Globus.Transfer: TransferResponse :: Id Task -> Id 'Submission -> Text -> Text -> Token Request -> TransferResponse
- Network.Globus.Transfer: [request_id] :: TransferResponse -> Token 'Request
+ Network.Globus.Transfer: [request_id] :: TransferResponse -> Token Request
- Network.Globus.Transfer: activityUrl :: Id Task -> Uri 'App
+ Network.Globus.Transfer: activityUrl :: Id Task -> Tagged 'App URI
- Network.Globus.Transfer: fetchSubmissionId :: MonadIO m => Token 'Access -> m (Id 'Submission)
+ Network.Globus.Transfer: fetchSubmissionId :: (MonadThrow m, MonadCatch m, MonadIO m) => Manager -> Token 'Access -> m (Id 'Submission)
- Network.Globus.Transfer: fetchTask :: MonadIO m => Token 'Access -> Id Task -> m Task
+ Network.Globus.Transfer: fetchTask :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token 'Access -> Id Task -> m Task
- Network.Globus.Transfer: fetchTasks :: MonadIO m => Token 'Access -> TaskFilters -> m TaskList
+ Network.Globus.Transfer: fetchTasks :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token 'Access -> TaskFilters -> m TaskList
- Network.Globus.Transfer: sendTransfer :: MonadIO m => Token 'Access -> TransferRequest -> m TransferResponse
+ Network.Globus.Transfer: sendTransfer :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token 'Access -> TransferRequest -> m TransferResponse
- Network.Globus.Transfer: transferAuth :: Token 'Access -> Option 'Https
+ Network.Globus.Transfer: transferAuth :: Token 'Access -> Header
- Network.Globus.Transfer: transferEndpoint :: Url 'Https
+ Network.Globus.Transfer: transferEndpoint :: forall {k} (a :: k). Uri a
- Network.Globus.Types: renderUri :: forall (a :: Endpoint). Uri a -> Text
+ Network.Globus.Types: renderUri :: forall {k} (a :: k). Uri a -> Text
Files
- globus.cabal +8/−2
- src/Effectful/Globus.hs +28/−13
- src/Network/Globus.hs +1/−3
- src/Network/Globus/Auth.hs +40/−63
- src/Network/Globus/Request.hs +47/−0
- src/Network/Globus/Transfer.hs +43/−53
- src/Network/Globus/Types.hs +150/−64
globus.cabal view
@@ -5,8 +5,9 @@ -- see: https://github.com/sol/hpack name: globus-version: 0.1.3+version: 0.2.0 synopsis: Globus Data Transfer+description: Haskell Client for Globus File Transfer API category: System, Data, Science homepage: https://github.com/DKISTDC/globus.hs author: Sean Hess@@ -21,6 +22,7 @@ Effectful.Globus Network.Globus Network.Globus.Auth+ Network.Globus.Request Network.Globus.Transfer Network.Globus.Types other-modules:@@ -47,9 +49,13 @@ , base >=4.16.4.0 && <5 , bytestring >=0.11 && <0.13 , effectful >=2.3 && <3+ , exceptions ==0.10.*+ , filepath >=1.5.2 && <1.6 , http-api-data ==0.6.*+ , http-client ==0.7.*+ , http-client-tls ==0.3.* , http-types ==0.12.*- , req ==3.13.*+ , network-uri ==2.6.* , tagged ==0.8.* , text ==2.* default-language: GHC2021
src/Effectful/Globus.hs view
@@ -13,17 +13,19 @@ , TaskFilters (..) , TaskList (..) , module Network.Globus.Types- , Req.Scheme (..)+ , 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-import Network.HTTP.Req as Req+import Network.Globus.Types hiding (appendQuery, param)+import Network.HTTP.Client (Manager) data GlobusClient = GlobusClient@@ -36,7 +38,7 @@ 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)- SubmissionId :: Token Access -> Globus m (Id Submission)+ 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@@ -46,22 +48,35 @@ runGlobus- :: (IOE :> es)+ :: (IOE :> es, Error GlobusError :> es) => GlobusClient+ -> Manager -> Eff (Globus : es) a -> Eff es a-runGlobus g = interpret $ \_ -> \case+runGlobus g mgr = interpret $ \_ -> \case GetAccessTokens exc red -> do- liftIO $ fetchAccessTokens g.clientId g.clientSecret red exc+ runGlobusIO $ fetchAccessTokens mgr g.clientId g.clientSecret red exc GetUserInfo ti -> do- liftIO $ fetchUserInfo ti+ runGlobusIO $ fetchUserInfo mgr ti AuthUrl red scopes state -> do pure $ authorizationUrl g.clientId red scopes state- SubmissionId access -> do- liftIO $ fetchSubmissionId access+ GetSubmissionId access -> do+ runGlobusIO $ fetchSubmissionId mgr access Transfer access request -> do- liftIO $ sendTransfer access request+ runGlobusIO $ sendTransfer mgr access request StatusTask access ti -> do- liftIO $ fetchTask access ti+ runGlobusIO $ fetchTask mgr access ti StatusTasks access tf -> do- liftIO $ fetchTasks access tf+ 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
src/Network/Globus.hs view
@@ -3,12 +3,10 @@ , module Network.Globus.Transfer , module Network.Globus.Auth , Tagged (..)- , Req.Scheme (..) ) where import Data.Tagged (Tagged (..)) import Network.Globus.Auth import Network.Globus.Transfer-import Network.Globus.Types-import Network.HTTP.Req as Req+import Network.Globus.Types hiding (appendQuery, param)
src/Network/Globus/Auth.hs view
@@ -1,68 +1,60 @@+{-# LANGUAGE QuasiQuotes #-}+ module Network.Globus.Auth where +import Control.Monad.Catch (MonadCatch, MonadThrow) import Data.Aeson import Data.Aeson.Types+import Data.Function ((&)) import Data.List qualified as L import Data.List.NonEmpty (NonEmpty (..)) import Data.List.NonEmpty qualified as NE-import Data.String (IsString) import Data.Tagged import Data.Text (Text) import Data.Text qualified as Text import Data.Text.Encoding (encodeUtf8) import Effectful (MonadIO) import GHC.Generics (Generic)+import Network.Globus.Request as Request import Network.Globus.Types-import Network.HTTP.Req as Req----- | Opaque secret identifying the user. Validate on redirect-newtype State = State Text- deriving newtype (IsString, FromJSON)- deriving (Show)+import Network.HTTP.Client (Manager, applyBasicAuth)+import Network.HTTP.Types (Header, methodPost)+import Network.URI.Static (uri) -- | The end user must visit this url authorizationUrl :: Token ClientId -> Uri Redirect -> NonEmpty Scope -> State -> Uri Authorization authorizationUrl (Tagged cid) red scopes (State st) = -- TODO: does the auth url need the security token?- authorizeEndpoint{params = query}- where- query :: Query- query =- "client_id" =: cid- <> "response_type" =: ("code" :: Text)- <> "scope" =: Text.intercalate " " (scopeText <$> NE.toList scopes)- <> "state" =: st- <> redirectUri red-- authorizeEndpoint :: Uri Authorization- authorizeEndpoint = Uri Https "auth.globus.org" ["v2", "oauth2", "authorize"] (Query [])+ authEndpoint /: "authorize"+ & param "client_id" cid+ & param "response_type" "code"+ & param "scope" (Text.intercalate " " (scopeText <$> NE.toList scopes))+ & param "state" st+ & redirectUri red -fetchAccessTokens :: (MonadIO m) => Token ClientId -> Token ClientSecret -> Uri Redirect -> Token Exchange -> m (NonEmpty TokenItem)-fetchAccessTokens (Tagged cid) (Tagged sec) red (Tagged code) = do- runReq defaultHttpConfig $ do- res <-- req POST tokenEndpoint NoReqBody jsonResponse $- "grant_type" =: ("authorization_code" :: Text)- <> "code" =: code- <> redirectUri red- -- TODO: is this the correct basic auth?- <> basicAuth (encodeUtf8 cid) (encodeUtf8 sec)-- -- liftIO $ print $ responseBody res- let TokenResponse toks = responseBody res :: TokenResponse- pure toks+fetchAccessTokens :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token ClientId -> Token ClientSecret -> Uri Redirect -> Token Exchange -> m (NonEmpty TokenItem)+fetchAccessTokens mgr (Tagged cid) (Tagged sec) red (Tagged code) = do+ req <- Request.request methodPost tokenUri [] ""+ TokenResponse toks <- sendJSON mgr (req & applyBasicAuth (encodeUtf8 cid) (encodeUtf8 sec))+ pure toks where- tokenEndpoint :: Req.Url 'Https- tokenEndpoint = https "auth.globus.org" /: "v2" /: "oauth2" /: "token"+ tokenEndpoint :: Uri Tokens+ tokenEndpoint = Tagged $ [uri|https://auth.globus.org/v2/oauth2/token|] + tokenUri :: Uri Tokens+ tokenUri =+ tokenEndpoint+ & param "grant_type" "authorization_code"+ & param "code" code+ & redirectUri red -redirectUri :: (QueryParam param) => Uri Redirect -> param-redirectUri red = "redirect_uri" =: renderUri red +redirectUri :: Uri Redirect -> Uri a -> Uri a+redirectUri red = param "redirect_uri" (renderUri red) + -- | fetchAccessTokens returns a non-empty list matching the scopes newtype TokenResponse = TokenResponse (NonEmpty TokenItem) deriving (Show)@@ -75,19 +67,6 @@ pure $ TokenResponse $ token :| other -data TokenItem = TokenItem- { scope :: Scopes- , access_token :: Token Access- , expires_in :: Int- , -- , resource_server :: Text -- "transfer.api.globus.org"- -- , tokenType :: Text -- "Bearer"- state :: State- -- , refresh_token :: Token Refresh- -- id_token :: Token Identity- }- deriving (Generic, FromJSON, Show)-- scopeToken :: Scope -> NonEmpty TokenItem -> Maybe (Token Access) scopeToken s ts = do item <- L.find (\i -> hasScope i.scope) $ NE.toList ts@@ -97,20 +76,18 @@ -- | You MUST include the OpenId Scope for this to work-fetchUserInfo :: (MonadIO m) => Token OpenId -> m UserInfoResponse-fetchUserInfo to =- runReq defaultHttpConfig $ do- res <-- req POST endpoint NoReqBody jsonResponse $- identityAuth to- pure $ responseBody res- where- endpoint :: Req.Url 'Https- endpoint = https "auth.globus.org" /: "v2" /: "oauth2" /: "userinfo"+fetchUserInfo :: (MonadIO m, MonadCatch m, MonadThrow m) => Manager -> Token OpenId -> m UserInfoResponse+fetchUserInfo mgr to = do+ req <- Request.request methodPost (authEndpoint /: "userinfo") [identityAuth to] ""+ sendJSON mgr req -identityAuth :: Token OpenId -> Option Https-identityAuth (Tagged oid) = oAuth2Bearer (encodeUtf8 oid)+authEndpoint :: Uri Authorization+authEndpoint = Tagged $ [uri|https://auth.globus.org/v2/oauth2|]+++identityAuth :: Token OpenId -> Header+identityAuth = oAuth2Bearer -- where
+ src/Network/Globus/Request.hs view
@@ -0,0 +1,47 @@+module Network.Globus.Request where++import Control.Monad (unless, when)+import Control.Monad.Catch (MonadCatch, MonadThrow, SomeException, catch, throwM)+import Control.Monad.IO.Class (MonadIO, liftIO)+import Data.Aeson (FromJSON (..), ToJSON (..), eitherDecode, encode)+import Data.Tagged+import Data.Text.Encoding (encodeUtf8)+import Network.Globus.Types+import Network.HTTP.Client as Http+import Network.HTTP.Types+++request :: (MonadThrow m, MonadCatch m) => Method -> Uri a -> [Header] -> RequestBody -> m Http.Request+request m (Tagged u) hs body = do+ req <- catch (Http.requestFromURI u) invalidUri+ pure $ req{method = m, requestHeaders = hs, requestBody = body}+ where+ invalidUri :: (MonadThrow m) => SomeException -> m a+ invalidUri e = throwM $ InvalidURI (show e) u+++get :: (MonadThrow m, MonadCatch m) => Uri a -> [Header] -> m Http.Request+get u hs = request methodGet u hs ""+++post :: (MonadThrow m, MonadCatch m, ToJSON b) => Uri a -> [Header] -> b -> m Http.Request+post u hs b = request methodPost u hs (RequestBodyLBS $ encode b)+++sendJSON :: (MonadIO m, FromJSON a, MonadThrow m) => Manager -> Request -> m a+sendJSON mgr req = do+ res <- liftIO $ Http.httpLbs req mgr++ when (responseStatus res == unauthorized401) $ do+ throwM $ Unauthorized req (responseBody res)++ unless (responseStatus res == status200) $ do+ throwM $ ResponseBadStatus req (responseStatus res) (responseBody res)++ case eitherDecode (responseBody res) of+ Left e -> throwM $ ResponseBadJSON req (show e) (responseBody res)+ Right a -> pure a+++oAuth2Bearer :: Token a -> Header+oAuth2Bearer (Tagged tok) = ("Authorization", "Bearer " <> encodeUtf8 tok)
src/Network/Globus/Transfer.hs view
@@ -1,51 +1,55 @@+{-# LANGUAGE QuasiQuotes #-}+ module Network.Globus.Transfer where +import Control.Monad.Catch (MonadCatch, MonadThrow)+import Control.Monad.IO.Class import Data.Aeson import Data.Char (toUpper)+import Data.Function ((&)) import Data.Tagged-import Data.Text (Text, pack)+import Data.Text (Text) import Data.Text qualified as T import Data.Text.Encoding (encodeUtf8)-import Effectful (MonadIO)-import GHC.Generics (Generic, Rep)+import GHC.Generics (Generic)+import Network.Globus.Request as Request import Network.Globus.Types-import Network.HTTP.Req as Req+import Network.HTTP.Client as Http+import Network.HTTP.Types (Header)+import Network.URI+import Network.URI.Static (uri) --- -----------------------------------------+-------------------------------------------- -- API--- -----------------------------------------+-------------------------------------------- -fetchSubmissionId :: (MonadIO m) => Token Access -> m (Id Submission)-fetchSubmissionId access =- runReq defaultHttpConfig $ do- res <- req GET (transferEndpoint /: "submission_id") NoReqBody jsonResponse (transferAuth access)- let idRes = responseBody res :: IdResponse- pure $ Tagged idRes.value+fetchSubmissionId :: (MonadThrow m, MonadCatch m, MonadIO m) => Manager -> Token Access -> m (Id Submission)+fetchSubmissionId mgr access = do+ req <- Request.get (transferEndpoint /: "submission_id") [transferAuth access]+ DataKey _ s <- sendJSON mgr req+ pure $ Tagged s -transferAuth :: Token Access -> Option Https-transferAuth (Tagged access) = oAuth2Bearer (encodeUtf8 access)+transferAuth :: Token Access -> Header+transferAuth (Tagged access) = ("Authorization", "Bearer " <> encodeUtf8 access) -transferEndpoint :: Req.Url 'Https-transferEndpoint = https "transfer.api.globus.org" /: "v0.10"+transferEndpoint :: Uri a+transferEndpoint = Tagged $ [uri|https://transfer.api.globus.org/v0.10|] -sendTransfer :: (MonadIO m) => Token Access -> TransferRequest -> m TransferResponse-sendTransfer access request =- runReq defaultHttpConfig $ do- res <- req POST (transferEndpoint /: "transfer") (ReqBodyJson request) jsonResponse (transferAuth access)- let tr = responseBody res :: TransferResponse- pure tr+sendTransfer :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token Access -> TransferRequest -> m TransferResponse+sendTransfer mgr access treq = do+ req <- Request.post (transferEndpoint /: "transfer") [transferAuth access] treq+ sendJSON mgr req -- https://docs.globus.org/api/transfer/task/#get_task_by_id-fetchTask :: (MonadIO m) => Token Access -> Id Task -> m Task-fetchTask access (Tagged ti) = do- runReq defaultHttpConfig $ do- res <- req GET (transferEndpoint /: "task" /: ti) NoReqBody jsonResponse (transferAuth access)- pure (responseBody res)+fetchTask :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token Access -> Id Task -> m Task+fetchTask mgr access (Tagged ti) = do+ req <- Request.get (transferEndpoint /: "task" /: T.unpack ti) [transferAuth access]+ sendJSON mgr req newtype TaskFilters = TaskFilters@@ -58,23 +62,19 @@ tf1 <> tf2 = TaskFilters{status = tf1.status <> tf2.status} -fetchTasks :: (MonadIO m) => Token Access -> TaskFilters -> m TaskList-fetchTasks access tf = do- runReq defaultHttpConfig $ do- res <-- req GET (transferEndpoint /: "task_list") NoReqBody jsonResponse $- transferAuth access- <> "filter" =: status tf.status- pure (responseBody res)+fetchTasks :: (MonadIO m, MonadThrow m, MonadCatch m) => Manager -> Token Access -> TaskFilters -> m TaskList+fetchTasks mgr access tf = do+ req <- Request.get (transferEndpoint /: "task_list" & param "filter" (status tf.status)) [transferAuth access]+ sendJSON mgr req where status :: [TaskStatus] -> Text status [] = ""- status ss = "status:" <> T.intercalate "," (map (T.toUpper . pack . show) ss)+ status ss = "status:" <> T.intercalate "," (fmap (T.toUpper . T.pack . show) ss) -activityUrl :: Id Task -> Uri App+activityUrl :: Id Task -> Tagged App URI activityUrl (Tagged t) =- Uri Https "app.globus.org" ["activity", t] (Query [])+ Tagged [uri|https://app.globus.org/activity|] /: T.unpack t taskPercentComplete :: Task -> Float@@ -134,7 +134,7 @@ instance FromJSON TaskStatus where- parseJSON = genericParseJSON defaultOptions{constructorTagModifier = map toUpper}+ parseJSON = genericParseJSON defaultOptions{constructorTagModifier = fmap toUpper} -- -----------------------------------------@@ -161,13 +161,13 @@ data TransferResponse = TransferResponse { task_id :: Id Task- , submission_id :: Token Submission+ , submission_id :: Id Submission , -- , code :: TransferCode -- Accepted, Duplicate message :: Text , resource :: Text , request_id :: Token Request }- deriving (Generic, FromJSON, Show)+ deriving (Generic, FromJSON) -- https://docs.globus.org/api/transfer/task_submit/#transfer_and_delete_documents@@ -190,7 +190,7 @@ instance ToJSON TransferRequest where- toJSON = dataLabelsJson+ toJSON = dataLabelsToJSON -- https://docs.globus.org/api/transfer/task_submit/#transfer_item_fields@@ -212,7 +212,7 @@ instance ToJSON TransferItem where- toJSON = dataLabelsJson+ toJSON = dataLabelsToJSON -- newtype FilterRule = FilterRule@@ -237,13 +237,3 @@ toInt SyncSize = 1 toInt SyncTimestamp = 2 toInt SyncChecksum = 3---dataLabelsJson :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value-dataLabelsJson = genericToJSON defaultOptions{fieldLabelModifier = dataLabels}---dataLabels :: String -> String-dataLabels "data_" = "DATA"-dataLabels "data_type" = "DATA_TYPE"-dataLabels f = f
src/Network/Globus/Types.hs view
@@ -1,22 +1,86 @@ module Network.Globus.Types where -import Data.Aeson (FromJSON (..), ToJSON (..), Value (..), withText)+import Control.Monad.Catch (Exception)+import Data.Aeson+import Data.Aeson.Types (Parser)+import Data.ByteString.Char8 qualified as BC+import Data.ByteString.Lazy (ByteString) import Data.Char (toLower) import Data.List.NonEmpty (NonEmpty (..)) import Data.Proxy (Proxy (..))+import Data.String (IsString (..)) import Data.Tagged import Data.Text (Text, pack, splitOn, unpack)-import Data.Text qualified as Text-import Data.Text.Encoding (decodeUtf8, encodeUtf8)-import GHC.IsList (IsList (..))+import Data.Text.Encoding (encodeUtf8)+import GHC.Generics (Generic, Rep) import GHC.TypeLits-import Network.HTTP.Req as Req-import Network.HTTP.Types (urlEncode)-import Web.HttpApiData (toQueryParam)+import Network.HTTP.Client (Request)+import Network.HTTP.Types (Status, urlEncode)+import Network.URI+import System.FilePath +(/:) :: Uri a -> String -> Uri a+Tagged uri /: s = Tagged $ uri{uriPath = uri.uriPath </> s}+infixl 5 /:+++-- (?:) :: Uri a -> QueryItem -> Uri a+-- Tagged uri ?: (qk, mqv) =+-- where++param :: Text -> Text -> Uri a -> Uri a+param k v (Tagged uri) = Tagged $ uri{uriQuery = appendQuery k (Just v) uri.uriQuery}+++appendQuery :: Text -> Maybe Text -> String -> String+appendQuery k mv = \case+ "" -> "?" <> keyValue+ "?" -> "?" <> keyValue+ rest -> rest <> "&" <> keyValue+ where+ keyValue =+ BC.unpack $+ (urlEncode True $ encodeUtf8 k)+ <> "="+ <> maybe "" (urlEncode True . encodeUtf8) mv+++renderUri :: Uri a -> Text+renderUri (Tagged u) = pack $ uriToString id u ""+++-- | Opaque secret identifying the user. Validate on redirect+newtype State = State Text+ deriving newtype (IsString, FromJSON, Eq)+ deriving (Show)+++data TokenItem = TokenItem+ { scope :: Scopes+ , access_token :: Token Access+ , expires_in :: Int+ , -- , resource_server :: Text -- "transfer.api.globus.org"+ -- , tokenType :: Text -- "Bearer"+ state :: State+ -- , refresh_token :: Token Refresh+ -- id_token :: Token Identity+ }+ deriving (Generic, FromJSON, Eq, Show)+++data GlobusError+ = InvalidURI String URI+ | Unauthorized Request ByteString+ | ResponseBadStatus Request Status ByteString+ | ResponseBadJSON Request String ByteString+ | MissingScope Scope (NonEmpty TokenItem)+ deriving (Show, Exception)++ type Token a = Tagged a Text type Id a = Tagged a Text+type Uri a = Tagged a URI data Token'@@ -32,77 +96,99 @@ | Collection -data DataType (s :: Symbol) = DataType---instance (KnownSymbol s) => ToJSON (DataType s) where- toJSON _ = String $ pack $ symbolVal @s Proxy---data Endpoint- = Redirect- | Authorization- | Tokens- | App----- | Simple URI Type, since all the others are obnoxious-data Uri (a :: Endpoint) = Uri- { scheme :: Scheme- , domain :: Text- , path :: [Text]- , params :: Query- }+dataLabelsToJSON :: (Generic a, GToJSON' Value Zero (Rep a)) => a -> Value+dataLabelsToJSON = genericToJSON defaultOptions{fieldLabelModifier = dataLabels} -renderUri :: Uri a -> Text-renderUri u =- scheme <> endpoint <> path <> query- where- scheme =- case u.scheme of- Http -> "http://"- Https -> "https://"- endpoint = cleanSlash u.domain- path = "/" <> Text.intercalate "/" (map cleanSlash u.path)- query =- case renderQuery u.params of- "" -> ""- q -> "?" <> q- cleanSlash = Text.dropWhileEnd (== '/') . Text.dropWhile (== '/')+dataLabelsFromJSON :: (Generic a, GFromJSON Zero (Rep a)) => Value -> Parser a+dataLabelsFromJSON = genericParseJSON defaultOptions{fieldLabelModifier = dataLabels} -instance Show (Uri a) where- show = Text.unpack . renderUri+dataLabels :: String -> String+dataLabels "data_" = "DATA"+dataLabels "data_type" = "DATA_TYPE"+dataLabels f = f -newtype Query = Query [(Text, Maybe Text)]- deriving newtype (Monoid, Semigroup)+data DataType (s :: Symbol) = DataType -instance Show Query where- show = Text.unpack . renderQuery+instance (KnownSymbol s) => ToJSON (DataType s) where+ toJSON _ = String $ pack $ symbolVal @s Proxy+instance FromJSON (DataType s) where+ parseJSON _ = pure DataType -instance IsList Query where- type Item Query = (Text, Maybe Text)- fromList = Query- toList (Query ps) = ps+data DataKey s = DataKey+ { data_type :: DataType s+ , value :: Text+ }+ deriving (Generic)+instance FromJSON (DataKey s) where+ parseJSON = dataLabelsFromJSON -instance Req.QueryParam Query where- queryParam t ma = Query [(t, toQueryParam <$> ma)]- queryParamToList (Query ps) = ps+data Endpoint+ = Redirect+ | Authorization+ | Tokens+ | App -renderQuery :: Query -> Text-renderQuery (Query ps) = Text.intercalate "&" $ map toText ps- where- toText (p, Nothing) = p- toText (p, Just v) = p <> "=" <> value v-- value = decodeUtf8 . urlEncode True . encodeUtf8+-- -- | Simple URI Type, since all the others are obnoxious+-- data Uri (a :: Endpoint) = Uri+-- { scheme :: Scheme+-- , domain :: Text+-- , path :: [Text]+-- , params :: Query+-- }+--+--+-- renderUri :: Uri a -> Text+-- renderUri u =+-- scheme <> endpoint <> path <> query+-- where+-- scheme =+-- case u.scheme of+-- Http -> "http://"+-- Https -> "https://"+-- endpoint = cleanSlash u.domain+-- path = "/" <> Text.intercalate "/" (map cleanSlash u.path)+-- query =+-- case renderQuery u.params of+-- "" -> ""+-- q -> "?" <> q+-- cleanSlash = Text.dropWhileEnd (== '/') . Text.dropWhile (== '/')+--+--+-- instance Show (Uri a) where+-- show = Text.unpack . renderUri +-- newtype Query = Query [(Text, Maybe Text)]+-- deriving newtype (Monoid, Semigroup)+--+--+-- instance Show Query where+-- show = Text.unpack . renderQuery+--+--+-- instance IsList Query where+-- type Item Query = (Text, Maybe Text)+-- fromList = Query+-- toList (Query ps) = ps+--+--+-- -- instance Req.QueryParam Query where+-- -- queryParam t ma = Query [(t, toQueryParam <$> ma)]+-- -- queryParamToList (Query ps) = ps+--+-- renderQuery :: Query -> Text+-- renderQuery (Query ps) = Text.intercalate "&" $ map toText ps+-- where+-- toText (p, Nothing) = p+-- toText (p, Just v) = p <> "=" <> value v+--+-- value = decodeUtf8 . urlEncode True . encodeUtf8 data Scope = -- TODO: figure out all scopes and hard-code@@ -137,7 +223,7 @@ newtype Scopes = Scopes (NonEmpty Scope)- deriving newtype (Show)+ deriving newtype (Show, Eq) instance FromJSON Scopes where