agrafix / Spock

Another Haskell web framework for rapid development
https://www.spock.li
678 stars 56 forks source link

Type Issue with Contexts and defEndpoint #106

Closed elfeck closed 7 years ago

elfeck commented 7 years ago

Hello, I have an issue with the new types, specifically for defEndpoint

type Action ctx a = SpockActionCtx ctx SqlBackend SessionVal AppState a

app :: App ()
app = do
  -- ...
  prehook adminHook $ do
    -- ...
    handleEditEndpoints

handleEditEndpoints :: ListContains n IsAdmin xs => Action (HVect xs) ()
handleEditEndpoints = do
   -- ...
  defEndpoint E.editLoad editLoadHandler

editLoadHandler :: E.EditLoadReq -> Action ctx E.EditLoadRsp
editLoadHandler (E.EditLoadReq pid) = do

is giving me this cryptic type error message (at the line with defEndpoint):


• Couldn't match type ‘Spock-core-0.11.0.0:Web.Spock.Core.SpockCtxT
                                 ctx0
                                 (WebStateM
                                    Database.Persist.Sql.Types.Internal.SqlBackend SessionVal AppState)
                                 ()’
                         with ‘ActionCtxT
                                 (HVect xs)
                                 (WebStateM
                                    Database.Persist.Sql.Types.Internal.SqlBackend SessionVal AppState)
                                 ()’
          Expected type: Action (HVect xs) ()
            Actual type: Spock-core-0.11.0.0:Web.Spock.Core.SpockCtxT
                           ctx0
                           (WebStateM
                              Database.Persist.Sql.Types.Internal.SqlBackend SessionVal AppState)
                           ()
        • In a stmt of a 'do' block: defEndpoint E.editLoad editLoadHandler
          In the expression: do { defEndpoint E.editLoad editLoadHandler }
          In an equation for ‘handleEditEndpoints’:
              handleEditEndpoints = do { defEndpoint E.editLoad editLoadHandler }
        • Relevant bindings include
            handleEditEndpoints :: Action (HVect xs) ()
              (bound at src/Endpoint/Edit.hs:23:1)

Can someone see what the issue is and help? Thanks!

agrafix commented 7 years ago

You are trying to hook a route in the Action monad, specifically handleEditEndpoints is running in Action, but should be running in App.

elfeck commented 7 years ago

Ah, okay I see, thanks. Works now in the App monad, but I get a redundant type constraint warning for ListContains n IsAdmin xs. Why is that?

agrafix commented 7 years ago

Probably because you are not using the IsAdmin from the HVect anywhere in your code. You can either explicitly use it or disable this specific warning.

elfeck commented 7 years ago

Hm okay. But I need this type constraint even if I don't use it to ensure that it can only be used in preHook adminHook $ do ..., right?
One more question: Do you know an easy way to get the client-ip-address? There is getPeerName but for that I need access to the socket and it seems really low-level.

Thanks for you help!

agrafix commented 7 years ago

Ideally you should put something into the context that the admin-actions actually need to enforce the constraint. For client ip-address you could use https://hackage.haskell.org/package/wai-3.2.1.1/docs/Network-Wai.html#v:remoteHost .

elfeck commented 7 years ago

Okay. Thanks again. Can you give an example of what you put into contexts for users/admins etc.? I can't really think of anything, but that might be because my application structure currently uses other (older) means.

agrafix commented 7 years ago

You could separate your storage handler by splitting it into user and admin actions and put that into the context for example. Generally, a natural resource that is actually required by the class you are trying to select is a good choice. Another alternative would be dependently typed roles, but I have not played with that yet...

elfeck commented 7 years ago

Intersting. Thanks for the suggestion. Is there a way to lift an Action into the App monad (as request lives in the Action monad?
For instance, when a request comes in I want to store the IP of that request and then continue matching for routes. Should I hookAnyand after I am done jumpNext?

agrafix commented 7 years ago

I'd suggest using a prehook for that.

elfeck commented 7 years ago

Ah of course! Thank you for your help.

agrafix commented 7 years ago

No worries