Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for Servant-Multipart #99

Open
rsoeldner opened this issue Apr 27, 2020 · 4 comments
Open

Support for Servant-Multipart #99

rsoeldner opened this issue Apr 27, 2020 · 4 comments

Comments

@rsoeldner
Copy link

I would like to use servant-multipart with reflex for file upload. Could this be easily added ?

@imalsogreg
Copy link
Owner

A couple things make this tricky to support

  1. servant-multipart packages both the API type and the servant-server instances - this brings wai and other server-side packages into the dependency tree of your frontend. Last time I checked, ghcjs fails to compile in this case (but maybe that's changed recently?). We would have to either overrive servant-multipart and stub out the server-side parts, or get servant-multipart maintainers to split their package into servant-multipart-types and servant-multipart, so that we could depend on the former cleanly.

  2. I'm not sure what servant-reflex should provide as an interface for MultipartForm. Looking at the source for servant-multipart, I think we would want to use their ToMultipart class and the functions that turn multipart data into a RequestBody... however RequestBody type comes from servant-client-core, which servant-reflex doesn't integrate with yet, but we could.

Are you interested in working on these?

@rsoeldner
Copy link
Author

rsoeldner commented Apr 28, 2020

This looks not so easy. I have seperated the servant-multipart package see here: https://github.com/rsoeldner/servant-multipart

I tried adding the following instance to servant-reflex as a starting point:

  instance  {-# OVERLAPPABLE #-}
    (SupportsServantReflex t m, HasClient t m sublayout tag 
    ) => HasClient t m ((MultipartForm tt (MultipartData tt)) :> sublayout ) tag where
  
    type Client t m ((MultipartForm tt (MultipartData tt)) :> sublayout) tag =
      Event t (MultipartData tt) -> Client t m sublayout tag 
  
    clientWithRouteAndResultHandler Proxy q t req baseurl opts wrap trigs =
      clientWithRouteAndResultHandler (Proxy :: Proxy sublayout) q t req baseurl opts wrap

I tried to convert MultipartData using multipartToBody boundry $ toMultipart but
multipartToBody :: forall tag. MultipartBackend tag => LBS.ByteString -> MultipartData tag -> RequestBody requiresMultipartBackend.

Additionally I'm not sure how to fill the reqBody https://hackage.haskell.org/package/servant-reflex-0.3.4/docs/src/Servant.Common.Req.html#reqBody

Can you provide guidance ?

@imalsogreg
Copy link
Owner

Ooh, that looks tough to get around. I expect that the dependency on wai comes from the need to be able to stream bytes into the http request. I see two paths forward.

  1. Give up on any POST requests that can't fit in memory; in this case toRequestBody could be written as a pure ByteString manipulation function.
  2. Replace the request body handling in servant-reflex so that it's compatible with the javascript FormData object, which I'm only now becoming aware of now that I read about multipart-form at https://stackoverflow.com/questions/35192841/fetch-post-with-multipart-form-data

@imalsogreg
Copy link
Owner

Oh also, I forgot to explain reqBody. It's type Maybe (Dynamic t (Either Text (BL.ByteString, Text))) isn't document well enough.

  • The Maybe allows a req body to optionally have no request body (for non-POST, non-PUT endpoints).
  • The Dynamic is similar to all other dynamics in the request - it gets updated along with the widgets whose values are being serialized into your request body.
  • The Either Text is used to indicate that something in the widgets was not acceptable as input in your form
  • The (BL.ByteString, Text) store the request body bytestring, and the content-type respectively

Happy to elaborate on any of those - I know the Dynamic (Either ... pattern tends to be really counter-intuitive.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants