Skip to content

Servant-mock and AuthProtect #1

@jkarni

Description

@jkarni

From @RocketPuppy on September 6, 2016 0:53

While working on generating a mock server of my API (which is really cool now that I have it working), I ran into several issues regarding AuthProtect.

The first issue is simply that there isn't an instance for HasMock covering the AuthProtect combinator. Now that I've "implemented" one I think I'm starting to see why. The second issue is that authentication using AuthProtect is still done even in the mock servers. I have something working now but I'm not confident in it. I'll try and walk through my steps below.

Suppose an API like:

type API = AuthProtect "cookie-auth" AuthCookie :> Get '[JSON] SecretSauce

In order to mock I did the following:

api = Proxy :: Proxy API
type AppContext = '[ AuthHandler Request AuthCookie ]

context :: Context AppContext
context = (myAuthHandler :: AuthHandler Request AuthCookie) :. EmptyContext

mockServer = serveWithContext api context (mock api (Proxy :: AppContext))

context is used in the canonical implementation of the API to provide the authentication handler. The first problem I ran into was the lack of a HasMock instance. I came up with the following:

instance ( HasMock rest context
         , HasServer rest context
         , HasContextEntry context (AuthHandler Request (AuthServerData (AuthProtect sym)))
         ) =>
         HasMock (AuthProtect (sym :: Symbol) :> rest) context where
    mock _ context = \_ -> mock (Proxy :: Proxy rest) context

Which I'm not very happy with because it requires UndecidableInstances. The second problem I ran into, after I had that working, was that authentication still happens on the mock server. I'm on the fence as to whether this is actually a problem, but it was undesirable in my circumstances. I came up with the following solution:

mockAuthHandler :: (Arbitrary a) => AuthHandler Request a
mockAuthHandler = mkAuthHandler $ \ _ -> liftIO $ generate arbitrary

mockContext :: Context AppContext
mockContext = (mockAuthHandler :: AuthHandler Request AuthCookie) :. Context

-- and use mockContext in the mock server
mockServer = serveWithContext api mockContext (mock api (Proxy :: AppContext))

mockAuthHandler is always successful at authenticating a user.

Altogether this solution works, in that I can run a mock server and get arbitrary values from the endpoints. But I wouldn't say that servant-mock plays well with AuthProtect.

I'd be interested in better solutions than the one I hacked up here.

Copied from original issue: haskell-servant/servant#596

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions