packages feed

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 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