servant-elm
servant-elm copied to clipboard
Possibility to Generate For `AuthProtect` endpoints?
Hello,
I have been using this library in a personal project and it's pretty damn cool. Thank you for writing it and for releasing it.
I did have a question, though, about AuthProtect endpoints. I have been using the servant-auth-cookie library to generate sessions and enforce authentication for certain endpoints, but I have not been able to use servant-elm to generate Api calls for these endpoints.
Maybe an example would be illustrative. With an endpoint like this:
type AdminApi =
"admin" :> AuthProtect "cookie-auth" :> Get '[HTML] Html
:<|> "admin" :> "post" :> ReqBody '[JSON] Post.BlogPost :> AuthProtect "cookie-auth" :> Post '[JSON] Post.BlogPost
If I try to generate Elm for this using servant-elm, I get a compiler error like this:
client/GenerateElm.hs:21:9: error:
• No instance for (Servant.Foreign.Internal.HasForeign
servant-elm-0.1.0.2:Servant.Elm.Foreign.LangElm
elm-export-0.3.0.0:Elm.Type.ElmTypeExpr
(Servant.API.Experimental.Auth.AuthProtect "cookie-auth"
Servant.API.Sub.:> Servant.API.Verbs.Post
'[Servant.API.ContentTypes.JSON]
Post.BlogPost))
arising from a use of ‘generateElmForAPI’
• In the second argument of ‘(:)’, namely
‘generateElmForAPI adminProxyApi’
In the second argument of ‘(:)’, namely
‘defElmImports : generateElmForAPI adminProxyApi’
In the second argument of ‘(:)’, namely
‘"import Exts.Date exposing (..)"
: defElmImports : generateElmForAPI adminProxyApi’
make: *** [Api.elm] Error 1
Am I doing something wrong or must there be an instance defined to be able to handle AuthProtected endpoints?
Thanks for any suggestions.
Hi,
Thanks - glad you like the library!
We're waiting for servant-foreign to add support for the AuthProtect combinator, then we can think about how to support auth in servant-elm.
However, if your Elm requests don't need to do anything special (I'm guessing the browser automatically adds your auth cookie to the requests?), in the meantime you can just add an instance for AuthProtect that does nothing:
instance (KnownSymbol sym, HasForeign lang ftype sublayout)
=> HasForeign lang ftype (AuthProtect sym :> sublayout) where
type Foreign ftype (AuthProtect sym :> sublayout) = Foreign ftype sublayout
foreignFor lang ftype Proxy req =
foreignFor lang ftype (Proxy :: Proxy sublayout) req
Nice! I'll give it a shot and report back. Thanks for the suggestion!
Well, I got stuck on an ambiguous type variable problem and was unfortunately at a loss as to how to begin debugging it, but it's alright if this doesn't work because I can just generate the Elm by hand and return to this problem once the servant-foreign library supports the AuthProtect combinator.
I'll post my attempt and the compiler error anyway in case anyone is working on something similar and stumbles across this discussion:
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
import Data.List
import Data.Proxy
import GHC.TypeLits (KnownSymbol)
import Servant.Elm
import Servant.Foreign
import Api
instance (KnownSymbol sym, HasForeign lang ftype sublayout)
=> HasForeign lang ftype (AuthProtect sym :> sublayout) where
type Foreign ftype (AuthProtect sym :> sublayout) = Foreign ftype sublayout
foreignFor lang ftype Proxy req =
foreignFor lang ftype (Proxy :: Proxy sublayout) req
Results in:
• Couldn't match type ‘Foreign ftype api0’
with ‘Foreign ftype sublayout’
Expected type: Foreign ftype (AuthProtect sym :> sublayout)
Actual type: Foreign ftype api0
NB: ‘Foreign’ is a type function, and may not be injective
The type variable ‘api0’ is ambiguous
• In the expression:
foreignFor lang ftype (Proxy :: Proxy sublayout) req
In an equation for ‘foreignFor’:
foreignFor lang ftype Proxy req
= foreignFor lang ftype (Proxy :: Proxy sublayout) req
In the instance declaration for
‘HasForeign lang ftype (AuthProtect sym :> sublayout)’
• Relevant bindings include
req :: Req ftype (bound at client/GenerateElm.hs:19:33)
ftype :: Proxy ftype (bound at client/GenerateElm.hs:19:21)
foreignFor :: Proxy lang
-> Proxy ftype
-> Proxy (AuthProtect sym :> sublayout)
-> Req ftype
-> Foreign ftype (AuthProtect sym :> sublayout)
(bound at client/GenerateElm.hs:19:5)
I just happened to be working on this and using {-# LANGUAGE ScopedTypeVariables #-} solved the ambiguous type variable problem.
@garetht's suggestion worked for me.
Great! Sorry, it would have been helpful for me to put the language pragmas and imports in my snippet...
Oh, no worries. I should understand the language pragmas better than I do if I'm going to be using them.