{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE CPP #-}
module Web.Finger.Client
( Account (..)
, Resource (..)
, Auth (..)
, Query (..)
, Link (..)
, Description (..)
, Result (..)
, newManager
, webfinger
, Language
)
where
#if !MIN_VERSION_base(4,8,0)
import Control.Applicative
#endif
import Control.Exception
import Data.Aeson hiding (Result, Success)
import Data.Aeson.Types (typeMismatch)
import Data.ByteString (ByteString)
import Data.Default.Class
import Data.Hashable
import Data.HashMap.Lazy (HashMap)
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import GHC.Generics (Generic)
import Web.LinkRelations (LinkRelation, fromByteString, toByteString)
import qualified Data.ByteString as B
import qualified Data.ByteString.Char8 as BC
import qualified Data.HashMap.Lazy as M
import qualified Network.HTTP.Client as H
import qualified Network.HTTP.Client.TLS as H
import qualified Network.HTTP.Types as HT
import qualified URI.ByteString as U
data Account = Account
{ Account -> ByteString
acctUser :: ByteString
, Account -> ByteString
acctHost :: ByteString
}
data Resource
= ResAccount Account
| ResUri U.URI
| ResUriStr ByteString
data Auth = Auth
{ Auth -> ByteString
authUser :: ByteString
, Auth -> ByteString
authPassword :: ByteString
}
data Query = Query
{
Query -> Resource
qryTarget :: Resource
, Query -> [Either ByteString LinkRelation]
qryLinkRels :: [Either ByteString LinkRelation]
, Query -> Maybe ByteString
qryHost :: Maybe ByteString
, Query -> Maybe Auth
qryAuth :: Maybe Auth
}
instance Default Query where
def :: Query
def = Query
{ qryTarget :: Resource
qryTarget = ByteString -> Resource
ResUriStr ByteString
B.empty
, qryLinkRels :: [Either ByteString LinkRelation]
qryLinkRels = []
, qryHost :: Maybe ByteString
qryHost = Maybe ByteString
forall a. Maybe a
Nothing
, qryAuth :: Maybe Auth
qryAuth = Maybe Auth
forall a. Maybe a
Nothing
}
data Language
= LanguageCode Text
| LanguageUndefined
deriving (Language -> Language -> Bool
(Language -> Language -> Bool)
-> (Language -> Language -> Bool) -> Eq Language
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Language -> Language -> Bool
== :: Language -> Language -> Bool
$c/= :: Language -> Language -> Bool
/= :: Language -> Language -> Bool
Eq, (forall x. Language -> Rep Language x)
-> (forall x. Rep Language x -> Language) -> Generic Language
forall x. Rep Language x -> Language
forall x. Language -> Rep Language x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cfrom :: forall x. Language -> Rep Language x
from :: forall x. Language -> Rep Language x
$cto :: forall x. Rep Language x -> Language
to :: forall x. Rep Language x -> Language
Generic, Int -> Language -> ShowS
[Language] -> ShowS
Language -> String
(Int -> Language -> ShowS)
-> (Language -> String) -> ([Language] -> ShowS) -> Show Language
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Language -> ShowS
showsPrec :: Int -> Language -> ShowS
$cshow :: Language -> String
show :: Language -> String
$cshowList :: [Language] -> ShowS
showList :: [Language] -> ShowS
Show)
instance Hashable Language
instance FromJSON Language where
parseJSON :: Value -> Parser Language
parseJSON (String Text
t) = Language -> Parser Language
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Text -> Language
toLang Text
t)
parseJSON Value
v = String -> Value -> Parser Language
forall a. String -> Value -> Parser a
typeMismatch String
"Language" Value
v
instance FromJSONKey Language where
fromJSONKey :: FromJSONKeyFunction Language
fromJSONKey = (Text -> Language) -> FromJSONKeyFunction Language
forall a. (Text -> a) -> FromJSONKeyFunction a
FromJSONKeyText Text -> Language
toLang
toLang :: Text -> Language
toLang :: Text -> Language
toLang Text
t =
if Text
t Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
"und"
then Language
LanguageUndefined
else Text -> Language
LanguageCode Text
t
data Link = Link
{
Link -> Either Text LinkRelation
lnkRelation :: Either Text LinkRelation
, Link -> Maybe Text
lnkMediaType :: Maybe Text
, Link -> Maybe Text
lnkAddress :: Maybe Text
, Link -> HashMap Language Text
lnkTitles :: HashMap Language Text
, Link -> HashMap Text (Maybe Text)
lnkProperties :: HashMap Text (Maybe Text)
}
deriving (Int -> Link -> ShowS
[Link] -> ShowS
Link -> String
(Int -> Link -> ShowS)
-> (Link -> String) -> ([Link] -> ShowS) -> Show Link
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Link -> ShowS
showsPrec :: Int -> Link -> ShowS
$cshow :: Link -> String
show :: Link -> String
$cshowList :: [Link] -> ShowS
showList :: [Link] -> ShowS
Show)
parseRel :: Text -> Either Text LinkRelation
parseRel :: Text -> Either Text LinkRelation
parseRel Text
t =
case ByteString -> Maybe LinkRelation
fromByteString (ByteString -> Maybe LinkRelation)
-> ByteString -> Maybe LinkRelation
forall a b. (a -> b) -> a -> b
$ Text -> ByteString
encodeUtf8 Text
t of
Maybe LinkRelation
Nothing -> Text -> Either Text LinkRelation
forall a b. a -> Either a b
Left Text
t
Just LinkRelation
lr -> LinkRelation -> Either Text LinkRelation
forall a b. b -> Either a b
Right LinkRelation
lr
forF :: Functor f => f a -> (a -> b) -> f b
forF :: forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
forF = ((a -> b) -> f a -> f b) -> f a -> (a -> b) -> f b
forall a b c. (a -> b -> c) -> b -> a -> c
flip (a -> b) -> f a -> f b
forall a b. (a -> b) -> f a -> f b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap
instance FromJSON Link where
parseJSON :: Value -> Parser Link
parseJSON (Object Object
o) =
Either Text LinkRelation
-> Maybe Text
-> Maybe Text
-> HashMap Language Text
-> HashMap Text (Maybe Text)
-> Link
Link (Either Text LinkRelation
-> Maybe Text
-> Maybe Text
-> HashMap Language Text
-> HashMap Text (Maybe Text)
-> Link)
-> Parser (Either Text LinkRelation)
-> Parser
(Maybe Text
-> Maybe Text
-> HashMap Language Text
-> HashMap Text (Maybe Text)
-> Link)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"rel" Parser Text
-> (Text -> Either Text LinkRelation)
-> Parser (Either Text LinkRelation)
forall (f :: * -> *) a b. Functor f => f a -> (a -> b) -> f b
`forF` Text -> Either Text LinkRelation
parseRel Parser
(Maybe Text
-> Maybe Text
-> HashMap Language Text
-> HashMap Text (Maybe Text)
-> Link)
-> Parser (Maybe Text)
-> Parser
(Maybe Text
-> HashMap Language Text -> HashMap Text (Maybe Text) -> Link)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"type" Parser
(Maybe Text
-> HashMap Language Text -> HashMap Text (Maybe Text) -> Link)
-> Parser (Maybe Text)
-> Parser
(HashMap Language Text -> HashMap Text (Maybe Text) -> Link)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"href" Parser (HashMap Language Text -> HashMap Text (Maybe Text) -> Link)
-> Parser (HashMap Language Text)
-> Parser (HashMap Text (Maybe Text) -> Link)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe (HashMap Language Text))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"titles" Parser (Maybe (HashMap Language Text))
-> HashMap Language Text -> Parser (HashMap Language Text)
forall a. Parser (Maybe a) -> a -> Parser a
.!= HashMap Language Text
forall k v. HashMap k v
M.empty Parser (HashMap Text (Maybe Text) -> Link)
-> Parser (HashMap Text (Maybe Text)) -> Parser Link
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe (HashMap Text (Maybe Text)))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"properties" Parser (Maybe (HashMap Text (Maybe Text)))
-> HashMap Text (Maybe Text) -> Parser (HashMap Text (Maybe Text))
forall a. Parser (Maybe a) -> a -> Parser a
.!= HashMap Text (Maybe Text)
forall k v. HashMap k v
M.empty
parseJSON Value
v = String -> Value -> Parser Link
forall a. String -> Value -> Parser a
typeMismatch String
"Link" Value
v
data Description = Description
{
Description -> Maybe Text
desSubject :: Maybe Text
, Description -> [Text]
desAliases :: [Text]
, Description -> HashMap Text (Maybe Text)
desProperties :: HashMap Text (Maybe Text)
, Description -> [Link]
desLinks :: [Link]
}
deriving (Int -> Description -> ShowS
[Description] -> ShowS
Description -> String
(Int -> Description -> ShowS)
-> (Description -> String)
-> ([Description] -> ShowS)
-> Show Description
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Description -> ShowS
showsPrec :: Int -> Description -> ShowS
$cshow :: Description -> String
show :: Description -> String
$cshowList :: [Description] -> ShowS
showList :: [Description] -> ShowS
Show)
instance FromJSON Description where
parseJSON :: Value -> Parser Description
parseJSON (Object Object
o) =
Maybe Text
-> [Text] -> HashMap Text (Maybe Text) -> [Link] -> Description
Description (Maybe Text
-> [Text] -> HashMap Text (Maybe Text) -> [Link] -> Description)
-> Parser (Maybe Text)
-> Parser
([Text] -> HashMap Text (Maybe Text) -> [Link] -> Description)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"subject" Parser
([Text] -> HashMap Text (Maybe Text) -> [Link] -> Description)
-> Parser [Text]
-> Parser (HashMap Text (Maybe Text) -> [Link] -> Description)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe [Text])
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"aliases" Parser (Maybe [Text]) -> [Text] -> Parser [Text]
forall a. Parser (Maybe a) -> a -> Parser a
.!= [] Parser (HashMap Text (Maybe Text) -> [Link] -> Description)
-> Parser (HashMap Text (Maybe Text))
-> Parser ([Link] -> Description)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe (HashMap Text (Maybe Text)))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"properties" Parser (Maybe (HashMap Text (Maybe Text)))
-> HashMap Text (Maybe Text) -> Parser (HashMap Text (Maybe Text))
forall a. Parser (Maybe a) -> a -> Parser a
.!= HashMap Text (Maybe Text)
forall k v. HashMap k v
M.empty Parser ([Link] -> Description)
-> Parser [Link] -> Parser Description
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*>
Object
o Object -> Key -> Parser (Maybe [Link])
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"links" Parser (Maybe [Link]) -> [Link] -> Parser [Link]
forall a. Parser (Maybe a) -> a -> Parser a
.!= []
parseJSON Value
v = String -> Value -> Parser Description
forall a. String -> Value -> Parser a
typeMismatch String
"Description" Value
v
data Result
= Success Description
| InvalidDesc String
| NoInfoFound
| TargetMalformed
| HostNotDetected String
deriving (Int -> Result -> ShowS
[Result] -> ShowS
Result -> String
(Int -> Result -> ShowS)
-> (Result -> String) -> ([Result] -> ShowS) -> Show Result
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Result -> ShowS
showsPrec :: Int -> Result -> ShowS
$cshow :: Result -> String
show :: Result -> String
$cshowList :: [Result] -> ShowS
showList :: [Result] -> ShowS
Show)
newManager :: IO H.Manager
newManager :: IO Manager
newManager = ManagerSettings -> IO Manager
H.newManager ManagerSettings
H.tlsManagerSettings
getHost :: U.URI -> Either String ByteString
getHost :: URI -> Either String ByteString
getHost URI
uri =
case URI -> Maybe Authority
U.uriAuthority URI
uri of
Maybe Authority
Nothing -> String -> Either String ByteString
forall a b. a -> Either a b
Left String
"Resource URI has no authority part"
Just Authority
au -> ByteString -> Either String ByteString
forall a b. b -> Either a b
Right (ByteString -> Either String ByteString)
-> ByteString -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ Host -> ByteString
U.hostBS (Host -> ByteString) -> Host -> ByteString
forall a b. (a -> b) -> a -> b
$ Authority -> Host
U.authorityHost Authority
au
parseResource :: Resource -> (ByteString, Either String ByteString)
parseResource :: Resource -> (ByteString, Either String ByteString)
parseResource (ResAccount Account
a) =
( ByteString
"acct:" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Account -> ByteString
acctUser Account
a ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"@" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> Account -> ByteString
acctHost Account
a
, ByteString -> Either String ByteString
forall a b. b -> Either a b
Right (ByteString -> Either String ByteString)
-> ByteString -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ Account -> ByteString
acctHost Account
a
)
parseResource (ResUri URI
u) =
( URI -> ByteString
forall a. URIRef a -> ByteString
U.serializeURIRef' URI
u
, URI -> Either String ByteString
getHost URI
u
)
parseResource (ResUriStr ByteString
s) =
let prefix :: ByteString
prefix = ByteString
"acct:"
s' :: ByteString
s' = if Char
':' Char -> ByteString -> Bool
`BC.notElem` ByteString
s Bool -> Bool -> Bool
&& Char
'@' Char -> ByteString -> Bool
`BC.elem` ByteString
s
then ByteString
prefix ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
s
else ByteString
s
rest :: ByteString
rest = Int -> ByteString -> ByteString
B.drop (ByteString -> Int
B.length ByteString
prefix) ByteString
s'
needSlash :: Bool
needSlash = Bool -> Bool
not (ByteString -> Bool
B.null ByteString
rest) Bool -> Bool -> Bool
&& ByteString -> Char
BC.head ByteString
rest Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'/'
s'' :: ByteString
s'' = if ByteString
prefix ByteString -> ByteString -> Bool
`B.isPrefixOf` ByteString
s' Bool -> Bool -> Bool
&& Bool
needSlash
then ByteString
prefix ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
"//" ByteString -> ByteString -> ByteString
forall a. Semigroup a => a -> a -> a
<> ByteString
rest
else ByteString
s'
in ( ByteString
s'
, case URIParserOptions -> ByteString -> Either URIParseError URI
U.parseURI URIParserOptions
U.laxURIParserOptions ByteString
s'' of
Left URIParseError
e -> String -> Either String ByteString
forall a b. a -> Either a b
Left (String -> Either String ByteString)
-> String -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ URIParseError -> String
forall a. Show a => a -> String
show URIParseError
e
Right URI
uri -> URI -> Either String ByteString
getHost URI
uri
)
webfinger :: H.Manager
-> Query
-> IO Result
webfinger :: Manager -> Query -> IO Result
webfinger Manager
manager Query
q =
let (ByteString
uri, Either String ByteString
eith) = Resource -> (ByteString, Either String ByteString)
parseResource (Resource -> (ByteString, Either String ByteString))
-> Resource -> (ByteString, Either String ByteString)
forall a b. (a -> b) -> a -> b
$ Query -> Resource
qryTarget Query
q
eith' :: Either String ByteString
eith' = Either String ByteString
-> (ByteString -> Either String ByteString)
-> Maybe ByteString
-> Either String ByteString
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Either String ByteString
eith ByteString -> Either String ByteString
forall a b. b -> Either a b
Right (Maybe ByteString -> Either String ByteString)
-> Maybe ByteString -> Either String ByteString
forall a b. (a -> b) -> a -> b
$ Query -> Maybe ByteString
qryHost Query
q
in case Either String ByteString
eith' of
Left String
err -> Result -> IO Result
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Result -> IO Result) -> Result -> IO Result
forall a b. (a -> b) -> a -> b
$ String -> Result
HostNotDetected String
err
Right ByteString
host -> do
let req :: Request
req = Request
H.defaultRequest
{ method :: ByteString
H.method = ByteString
HT.methodGet
, secure :: Bool
H.secure = Bool
True
, host :: ByteString
H.host = ByteString
host
, port :: Int
H.port = Int
443
, path :: ByteString
H.path = ByteString
"/.well-known/webfinger"
, requestHeaders :: RequestHeaders
H.requestHeaders =
[(HeaderName
HT.hAccept, ByteString
"application/jrd+json")]
}
req' :: Request
req' = case Query -> Maybe Auth
qryAuth Query
q of
Maybe Auth
Nothing -> Request
req
Just (Auth ByteString
user ByteString
pass) -> ByteString -> ByteString -> Request -> Request
H.applyBasicAuth ByteString
user ByteString
pass Request
req
res :: (ByteString, Maybe ByteString)
res = (ByteString
"resource", ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just ByteString
uri)
toBS :: Either ByteString LinkRelation -> ByteString
toBS = (ByteString -> ByteString)
-> (LinkRelation -> ByteString)
-> Either ByteString LinkRelation
-> ByteString
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either ByteString -> ByteString
forall a. a -> a
id LinkRelation -> ByteString
toByteString
rels :: [(ByteString, Maybe ByteString)]
rels = (Either ByteString LinkRelation -> (ByteString, Maybe ByteString))
-> [Either ByteString LinkRelation]
-> [(ByteString, Maybe ByteString)]
forall a b. (a -> b) -> [a] -> [b]
map ((,) ByteString
"rel" (Maybe ByteString -> (ByteString, Maybe ByteString))
-> (Either ByteString LinkRelation -> Maybe ByteString)
-> Either ByteString LinkRelation
-> (ByteString, Maybe ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString)
-> (Either ByteString LinkRelation -> ByteString)
-> Either ByteString LinkRelation
-> Maybe ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Either ByteString LinkRelation -> ByteString
toBS) ([Either ByteString LinkRelation]
-> [(ByteString, Maybe ByteString)])
-> [Either ByteString LinkRelation]
-> [(ByteString, Maybe ByteString)]
forall a b. (a -> b) -> a -> b
$ Query -> [Either ByteString LinkRelation]
qryLinkRels Query
q
params :: [(ByteString, Maybe ByteString)]
params = (ByteString, Maybe ByteString)
res (ByteString, Maybe ByteString)
-> [(ByteString, Maybe ByteString)]
-> [(ByteString, Maybe ByteString)]
forall a. a -> [a] -> [a]
: [(ByteString, Maybe ByteString)]
rels
req'' :: Request
req'' = [(ByteString, Maybe ByteString)] -> Request -> Request
H.setQueryString [(ByteString, Maybe ByteString)]
params Request
req'
Either HttpException (Response ByteString)
eresp <- IO (Response ByteString)
-> IO (Either HttpException (Response ByteString))
forall e a. Exception e => IO a -> IO (Either e a)
try (Request -> Manager -> IO (Response ByteString)
H.httpLbs Request
req'' Manager
manager)
case Either HttpException (Response ByteString)
eresp of
Left HttpException
e ->
case HttpException
e :: H.HttpException of
H.HttpExceptionRequest Request
_ (H.StatusCodeException Response ()
resp ByteString
_)
| Response () -> Status
forall body. Response body -> Status
H.responseStatus Response ()
resp Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
HT.badRequest400 -> Result -> IO Result
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Result
TargetMalformed
| Response () -> Status
forall body. Response body -> Status
H.responseStatus Response ()
resp Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
HT.notFound404 -> Result -> IO Result
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return Result
NoInfoFound
| Bool
otherwise -> HttpException -> IO Result
forall e a. Exception e => e -> IO a
throwIO HttpException
e
HttpException
_ -> HttpException -> IO Result
forall e a. Exception e => e -> IO a
throwIO HttpException
e
Right Response ByteString
resp ->
Result -> IO Result
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Result -> IO Result) -> Result -> IO Result
forall a b. (a -> b) -> a -> b
$ case ByteString -> Either String Description
forall a. FromJSON a => ByteString -> Either String a
eitherDecode (ByteString -> Either String Description)
-> ByteString -> Either String Description
forall a b. (a -> b) -> a -> b
$ Response ByteString -> ByteString
forall body. Response body -> body
H.responseBody Response ByteString
resp of
Left String
err -> String -> Result
InvalidDesc String
err
Right Description
desc -> Description -> Result
Success Description
desc