Rename to SqueakCtx, add stubs for authentication
This commit is contained in:
parent
43ca3dfbf6
commit
ace0bee9d6
@ -9,41 +9,50 @@ import Control.Applicative ((<|>))
|
|||||||
import Control.Monad.IO.Class (MonadIO, liftIO)
|
import Control.Monad.IO.Class (MonadIO, liftIO)
|
||||||
import Data.Aeson (FromJSON(parseJSON), Options(fieldLabelModifier), ToJSON(toJSON), (.:), (.=), defaultOptions, genericParseJSON, genericToJSON, object, withObject)
|
import Data.Aeson (FromJSON(parseJSON), Options(fieldLabelModifier), ToJSON(toJSON), (.:), (.=), defaultOptions, genericParseJSON, genericToJSON, object, withObject)
|
||||||
import Data.Text (Text)
|
import Data.Text (Text)
|
||||||
|
import Data.Text.Encoding (encodeUtf8)
|
||||||
import Data.Time.Calendar (Day)
|
import Data.Time.Calendar (Day)
|
||||||
import GHC.Generics (Generic)
|
import GHC.Generics (Generic)
|
||||||
import Network.HTTP.Req (Option, Scheme(..), POST(..), ReqBodyJson(..), Url, defaultHttpConfig, http, port, jsonResponse, responseBody, req, runReq)
|
import Network.HTTP.Req (Option, Scheme(..), POST(..), ReqBodyJson(..), Url, defaultHttpConfig, header, http, port, jsonResponse, responseBody, req, runReq)
|
||||||
|
|
||||||
import qualified Data.Char as Char
|
import qualified Data.Char as Char
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
|
||||||
-- | Dependency Inversion for monads that can run requests.
|
-- | Dependency Inversion for monads that can run requests.
|
||||||
-- By requiring 'HasEndpointInfo' and 'MonadIO' for functions that perform requests we don't need to import anything from Scotty.
|
-- By requiring 'SqueakCtx' and 'MonadIO' for functions that perform requests we don't need to import anything from Scotty.
|
||||||
class HasEndpointInfo m where
|
class SqueakCtx m where
|
||||||
getEndpointInfo :: m (Either (Url 'Http, Option scheme0) (Url 'Https, Option scheme1))
|
-- | 'Url' and possibly other 'Option's regarding the Squeak API server.
|
||||||
|
getLocationInfo :: m (Either (Url 'Http, Option scheme0) (Url 'Https, Option scheme1))
|
||||||
|
-- | If available, an authentication token obtained by 'login'.
|
||||||
|
getAuthToken :: m (Maybe Text)
|
||||||
|
|
||||||
-- | Debugging instance. Makes it possible to just 'getAllLectures' etc. from GHCi.
|
-- | Debugging instance. Makes it possible to just 'getAllLectures' etc. from GHCi.
|
||||||
instance HasEndpointInfo IO where
|
instance SqueakCtx IO where
|
||||||
-- getEndpointInfo = pure $ Right (https "api.squeak-test.fsmi.uni-karlsruhe.de", mempty)
|
-- getEndpointInfo = pure $ Right (https "api.squeak-test.fsmi.uni-karlsruhe.de", mempty)
|
||||||
getEndpointInfo = pure $ Left (http "localhost", port 8000)
|
getLocationInfo = pure $ Left (http "localhost", port 8000)
|
||||||
|
getAuthToken = Just . Text.pack <$> getLine
|
||||||
|
|
||||||
-- | Construct a GraphQL query that runs in 'm' and returns a 'result'.
|
-- | Construct a GraphQL query that runs in 'm' and returns a 'result'.
|
||||||
mkQueryWithVars
|
mkQueryWithVars
|
||||||
:: (HasEndpointInfo m, MonadIO m, ToJSON v, FromJSON result)
|
:: (SqueakCtx m, MonadIO m, ToJSON v, FromJSON result)
|
||||||
=> v -- ^ This field is sent as JSON in the @variables@ key.
|
=> v -- ^ This field is sent as JSON in the @variables@ key.
|
||||||
-> Text
|
-> Text
|
||||||
-> m (Either [GQLError] result)
|
-> m (Either [GQLError] result)
|
||||||
mkQueryWithVars vars query = do
|
mkQueryWithVars vars query = do
|
||||||
endpointInfo <- getEndpointInfo
|
locationInfo <- getLocationInfo
|
||||||
let request = (case endpointInfo of
|
authToken <- getAuthToken
|
||||||
Left (httpUrl, httpOptions) -> req POST httpUrl payload jsonResponse httpOptions
|
let request = (case locationInfo of
|
||||||
Right (httpsUrl, httpsOptions) -> req POST httpsUrl payload jsonResponse httpsOptions)
|
Left (httpUrl, httpOptions) -> req POST httpUrl payload jsonResponse (httpOptions <> mkAuthHeader authToken)
|
||||||
|
Right (httpsUrl, httpsOptions) -> req POST httpsUrl payload jsonResponse (httpsOptions <> mkAuthHeader authToken))
|
||||||
response <- liftIO $ runReq defaultHttpConfig request
|
response <- liftIO $ runReq defaultHttpConfig request
|
||||||
pure $ replyToEither $ responseBody response
|
pure $ replyToEither $ responseBody response
|
||||||
where
|
where
|
||||||
payload = ReqBodyJson $ GQLQuery query vars
|
payload = ReqBodyJson $ GQLQuery query vars
|
||||||
|
-- If no token is provided, no options are added ('mempty'). Otherwise, add an @Authorization: Bearer <JWT>@ header.
|
||||||
|
mkAuthHeader = maybe mempty $ header "Authorization" . ("Bearer " <>) . encodeUtf8
|
||||||
|
|
||||||
-- | Construct a GraphQL query that runs in 'm' and returns a 'result' or a 'SqueakError'.
|
-- | Construct a GraphQL query that runs in 'm' and returns a 'result' or a 'SqueakError'.
|
||||||
mkQuery
|
mkQuery
|
||||||
:: (HasEndpointInfo m, MonadIO m, FromJSON result)
|
:: (SqueakCtx m, MonadIO m, FromJSON result)
|
||||||
=> Text
|
=> Text
|
||||||
-> m (Either [GQLError] result)
|
-> m (Either [GQLError] result)
|
||||||
mkQuery = mkQueryWithVars $ object []
|
mkQuery = mkQueryWithVars $ object []
|
||||||
@ -152,13 +161,13 @@ data Lecture = Lecture
|
|||||||
instance FromJSON Lecture where
|
instance FromJSON Lecture where
|
||||||
parseJSON = genericParseJSON $ removePrefixOpts "lecture"
|
parseJSON = genericParseJSON $ removePrefixOpts "lecture"
|
||||||
|
|
||||||
getAllLectures :: (HasEndpointInfo m, MonadIO m) => m (Either SqueakError [Lecture])
|
getAllLectures :: (SqueakCtx m, MonadIO m) => m (Either SqueakError [Lecture])
|
||||||
getAllLectures = fmap unLectures <$> joinErrors <$> mkQuery
|
getAllLectures = fmap unLectures <$> joinErrors <$> mkQuery
|
||||||
"{ lectures \
|
"{ lectures \
|
||||||
\ { id displayName aliases } \
|
\ { id displayName aliases } \
|
||||||
\}"
|
\}"
|
||||||
|
|
||||||
getAllDocuments :: (HasEndpointInfo m, MonadIO m) => m (Either SqueakError [Document])
|
getAllDocuments :: (SqueakCtx m, MonadIO m) => m (Either SqueakError [Document])
|
||||||
getAllDocuments = fmap unDocuments <$> joinErrors <$> mkQuery
|
getAllDocuments = fmap unDocuments <$> joinErrors <$> mkQuery
|
||||||
"{ documents(filters: []) \
|
"{ documents(filters: []) \
|
||||||
\ { results \
|
\ { results \
|
||||||
@ -191,17 +200,7 @@ data User = User
|
|||||||
instance FromJSON User where
|
instance FromJSON User where
|
||||||
parseJSON = genericParseJSON $ removePrefixOpts "user"
|
parseJSON = genericParseJSON $ removePrefixOpts "user"
|
||||||
|
|
||||||
{-
|
login :: (SqueakCtx m, MonadIO m) => Text -> Text -> m (Either SqueakError Credentials)
|
||||||
login :: Text -> Text -> Req (Either [GQLError] Credentials)
|
|
||||||
login username password = fmap unLoginResult <$> replyToEither <$> responseBody <$> req POST serverUrl (ReqBodyJson $ GQLQueryWithVars q vars) jsonResponse mempty
|
|
||||||
where q = "mutation LoginUser($username: String!, $password: String!) { login(username: $username, password: $password) { ... on Credentials { token user { username displayName } } } }"
|
|
||||||
vars = object
|
|
||||||
[ "username" .= username
|
|
||||||
, "password" .= password
|
|
||||||
]
|
|
||||||
-}
|
|
||||||
|
|
||||||
login :: (HasEndpointInfo m, MonadIO m) => Text -> Text -> m (Either SqueakError Credentials)
|
|
||||||
login username password = joinErrors <$> fmap unLoginResult <$> mkQueryWithVars
|
login username password = joinErrors <$> fmap unLoginResult <$> mkQueryWithVars
|
||||||
(object ["username" .= username, "password" .= password])
|
(object ["username" .= username, "password" .= password])
|
||||||
"mutation LoginUser($username: String!, $password: String!) \
|
"mutation LoginUser($username: String!, $password: String!) \
|
||||||
@ -251,7 +250,7 @@ data Order = Order
|
|||||||
instance FromJSON Order where
|
instance FromJSON Order where
|
||||||
parseJSON = genericParseJSON $ removePrefixOpts "order"
|
parseJSON = genericParseJSON $ removePrefixOpts "order"
|
||||||
|
|
||||||
createOrder :: (HasEndpointInfo m, MonadIO m) => Text -> [Text] -> m (Either SqueakError Order)
|
createOrder :: (SqueakCtx m, MonadIO m) => Text -> [Text] -> m (Either SqueakError Order)
|
||||||
createOrder tag documents = joinErrors <$> fmap unCreateOrderResult <$> mkQueryWithVars
|
createOrder tag documents = joinErrors <$> fmap unCreateOrderResult <$> mkQueryWithVars
|
||||||
(object ["tag" .= tag, "documents" .= documents])
|
(object ["tag" .= tag, "documents" .= documents])
|
||||||
"mutation CreateOrder($tag: String!, $documents: [DocumentId!]!) \
|
"mutation CreateOrder($tag: String!, $documents: [DocumentId!]!) \
|
||||||
|
Loading…
x
Reference in New Issue
Block a user