on master help sign in
abject madness
commit202827f04a23103f9241adc771c9462e12e8300f authorIsaac Shapira <fresheyeball@gmail.com> dateThu 1 Mar 2018, 16:49 (9 years ago) parentc6e25e9
2 files changed+108−51
src/Servant/Common/Req.hs+32−30 src/Servant/Reflex.hs+76−21
src/Servant/Common/Req.hs+32−30
@@ -7,10 +7,12 @@
77{-# LANGUAGE GADTs #-}
88{-# LANGUAGE LambdaCase #-}
99{-# LANGUAGE MultiParamTypeClasses #-}
10+{-# LANGUAGE NamedFieldPuns #-}
1011{-# LANGUAGE OverloadedStrings #-}
1112{-# LANGUAGE RankNTypes #-}
1213{-# LANGUAGE ScopedTypeVariables #-}
1314{-# LANGUAGE TupleSections #-}
15+{-# OPTIONS_GHC -fno-warn-orphans #-}
1416
1517module Servant.Common.Req where
1618
@@ -36,7 +38,6 @@ import qualified Data.Text as T
3638import qualified Data.Text.Encoding as T
3739import qualified Data.Text.Lazy as TL
3840import Data.Traversable (forM)
39−import GHCJS.DOM.Types (Blob, Document, FormData)
4041import qualified Language.Javascript.JSaddle as JS
4142import Language.Javascript.JSaddle.Monad (JSM, MonadJSM, liftJSM)
4243import qualified Network.URI as N
@@ -148,7 +149,7 @@ data QueryPart t = QueryPartParam (Dynamic t (Either Text (Maybe Text)))
148149-------------------------------------------------------------------------------
149150-- The data structure used to build up request information while traversing
150151-- the shape of a servant API
151−data Req t o = Req
152+data Req t = forall o. (IsXhrPayload o, Show o) => Req
152153 { reqMethod :: Text
153154 , reqPathParts :: [Dynamic t (Either Text Text)]
154155 , qParams :: [(Text, QueryPart t)]
@@ -158,14 +159,14 @@ data Req t o = Req
158159 , authData :: Maybe (Dynamic t (Maybe BasicAuthData))
159160 }
160161
161−defReq :: IsXhrPayload o => Req t o
162−defReq = Req "GET" [] [] Nothing [] def Nothing
162+defReq :: Req t
163+defReq = Req "GET" [] [] (Nothing :: Maybe (Dynamic t (Either Text ((), Text)))) [] def Nothing
163164
164−prependToPathParts :: Dynamic t (Either Text Text) -> Req t o -> Req t o
165+prependToPathParts :: Dynamic t (Either Text Text) -> Req t -> Req t
165166prependToPathParts p req =
166167 req { reqPathParts = p : reqPathParts req }
167168
168−addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t o -> Req t o
169+addHeader :: (ToHttpApiData a, Reflex t) => Text -> Dynamic t (Either Text a) -> Req t -> Req t
169170addHeader name val req = req { headers = (name, (fmap . fmap) (T.decodeUtf8 . toHeader) val) : headers req }
170171
171172
@@ -175,13 +176,15 @@ addHeader name val req = req { headers = (name, (fmap . fmap) (T.decodeUtf8 . to
175176-- instance ReflexDom'MimeRender BL.ByteString Blob where
176177-- ghcjsMimerender _ = id
177178
179+data SomeXhrRequest = forall o. (IsXhrPayload o, Show o) => SomeXhrRequest (XhrRequest (Maybe o))
180+
178181reqToReflexRequest
179− :: forall t o. Reflex t
182+ :: forall t. Reflex t
180183 => Text
181184 -> Dynamic t BaseUrl
182− -> Req t o
183− -> Dynamic t (Either Text (XhrRequest (Maybe o)))
184−reqToReflexRequest reqMeth reqHost req =
185+ -> Req t
186+ -> Dynamic t (Either Text SomeXhrRequest)
187+reqToReflexRequest reqMeth reqHost req@(Req _ _ _ (reqBody :: Maybe (Dynamic t (Either Text (o, Text)))) _ _ _) =
185188 let t :: Dynamic t [Either Text Text]
186189 t = sequence $ reverse $ reqPathParts req
187190
@@ -251,7 +254,7 @@ reqToReflexRequest reqMeth reqHost req =
251254 }
252255
253256 xhrOpts :: Dynamic t (Either Text (XhrRequestConfig (Maybe o)))
254− xhrOpts = case reqBody req of
257+ xhrOpts = case reqBody of
255258 Nothing -> ffor xhrHeaders $ \case
256259 Left e -> Left e
257260 Right hs -> Right $ def { _xhrRequestConfig_headers = Map.fromList hs
@@ -276,7 +279,7 @@ reqToReflexRequest reqMeth reqHost req =
276279 Nothing -> xhr
277280 Just auth -> liftA2 mkAuth auth xhr
278281
279− xhrReq = (liftA2 . liftA2) (\p opt -> XhrRequest reqMeth p opt) xhrUrl (addAuth xhrOpts)
282+ xhrReq = (liftA2 . liftA2) (\p opt -> SomeXhrRequest $ XhrRequest reqMeth p opt) xhrUrl (addAuth xhrOpts)
280283
281284 in xhrReq
282285
@@ -289,19 +292,20 @@ instance IsXhrPayload o => IsXhrPayload (Maybe o) where
289292 sendXhrPayload xhr = maybe (sendXhrPayload xhr ()) (sendXhrPayload xhr)
290293
291294-- | This function performs the request
292−performRequests :: forall t o m f tag. (SupportsServantReflex t m, Traversable f, IsXhrPayload o, Show o)
295+performRequests :: forall t m f tag. (SupportsServantReflex t m, Traversable f)
293296 => Text
294− -> Dynamic t (f (Req t o))
297+ -> Dynamic t (f (Req t))
295298 -> Dynamic t BaseUrl
296299 -> ClientOptions
297300 -> Event t tag
298301 -> m (Event t (tag, f (Either Text XhrResponse)))
299302performRequests reqMeth rs reqHost opts trigger = do
300303 let xhrReqs =
301− join $ (\(fxhr :: f (Req t o)) -> sequence $
304+ join $ (\(fxhr :: f (Req t)) -> sequence $
302305 reqToReflexRequest reqMeth reqHost <$> fxhr) <$> rs
303306
304307 -- xhrReqs = fmap snd <$> xhrReqsAndDebugs
308+ reqs :: Event t (Compose ((,) tag) f (Either Text SomeXhrRequest))
305309 reqs = attachPromptlyDynWith
306310 (\fxhr t -> Compose (t, fxhr)) xhrReqs trigger
307311
@@ -317,32 +321,31 @@ performSomeRequestsAsync
317321 HasWebView (Performable m),
318322 PerformEvent t m,
319323 TriggerEvent t m,
320− Traversable f,
321− IsXhrPayload a,
322− Show a
324+ Traversable f
323325 )
324326 => ClientOptions
325− -> Event t (f (Either Text (XhrRequest a)))
327+ -> Event t (f (Either Text SomeXhrRequest))
326328 -> m (Event t (f (Either Text XhrResponse)))
327−performSomeRequestsAsync opts =
328− performSomeRequestsAsync' opts newXMLHttpRequest . fmap return
329+performSomeRequestsAsync opts sxhr =
330+ performSomeRequestsAsync' opts newXMLHttpRequest $ return <$> sxhr
329331
330332
331333------------------------------------------------------------------------------
332334-- | A modified version or Reflex.Dom.Xhr.performRequestsAsync
333335-- that accepts 'f (Either e (XhrRequestb))' events
334336performSomeRequestsAsync'
335− :: (MonadJSM (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f, Show b)
337+ :: (MonadJSM (Performable m), PerformEvent t m, TriggerEvent t m, Traversable f)
336338 => ClientOptions
337− -> (XhrRequest b -> (a -> JSM ()) -> Performable m XMLHttpRequest)
338− -> Event t (Performable m (f (Either Text (XhrRequest b)))) -> m (Event t (f (Either Text a)))
339+ -> (forall b. IsXhrPayload b => XhrRequest b -> (a -> JSM ()) -> Performable m XMLHttpRequest)
340+ -> Event t (Performable m (f (Either Text SomeXhrRequest)))
341+ -> m (Event t (f (Either Text a)))
339342performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs cb -> do
340343 rs <- hrs
341344 resps <- forM rs $ \r -> case r of
342345 Left e -> do
343346 resp <- liftIO $ newMVar (Left e)
344347 return resp
345− Right r' -> do
348+ Right (SomeXhrRequest r') -> do
346349 resp <- liftIO newEmptyMVar
347350 r'' <- liftJSM $ (optsRequestFixup opts) r'
348351 _ <- newXhr r'' $ liftIO . putMVar resp . Right
@@ -353,10 +356,10 @@ performSomeRequestsAsync' opts newXhr req = performEventAsync $ ffor req $ \hrs
353356
354357performRequestsCT
355358 :: (SupportsServantReflex t m,
356− MimeUnrender ct a, Traversable f, IsXhrPayload o, Show o)
359+ MimeUnrender ct a, Traversable f)
357360 => Proxy ct
358361 -> Text
359− -> Dynamic t (f (Req t o))
362+ -> Dynamic t (f (Req t))
360363 -> Dynamic t BaseUrl
361364 -> ClientOptions
362365 -> Event t tag
@@ -377,10 +380,9 @@ performRequestsCT ct reqMeth reqs reqHost opts trigger = do
377380
378381performRequestsNoBody
379382 :: (SupportsServantReflex t m,
380− Traversable f,
381− IsXhrPayload o, Show o)
383+ Traversable f)
382384 => Text
383− -> Dynamic t (f (Req t o))
385+ -> Dynamic t (f (Req t))
384386 -> Dynamic t BaseUrl
385387 -> ClientOptions
386388 -> Event t tag
src/Servant/Reflex.hs+76−21
@@ -1,13 +1,17 @@
11{-# LANGUAGE AllowAmbiguousTypes #-}
22{-# LANGUAGE CPP #-}
33{-# LANGUAGE DataKinds #-}
4+{-# LANGUAGE DefaultSignatures #-}
5+{-# LANGUAGE ExtendedDefaultRules #-}
46{-# LANGUAGE FlexibleContexts #-}
57{-# LANGUAGE FlexibleInstances #-}
8+{-# LANGUAGE FunctionalDependencies #-}
69{-# LANGUAGE GeneralizedNewtypeDeriving #-}
710{-# LANGUAGE InstanceSigs #-}
811{-# LANGUAGE MultiParamTypeClasses #-}
912{-# LANGUAGE OverloadedStrings #-}
1013{-# LANGUAGE PolyKinds #-}
14+{-# LANGUAGE RecordWildCards #-}
1115{-# LANGUAGE ScopedTypeVariables #-}
1216{-# LANGUAGE TupleSections #-}
1317{-# LANGUAGE TypeFamilies #-}
@@ -36,8 +40,11 @@ module Servant.Reflex
3640
3741------------------------------------------------------------------------------
3842import Control.Applicative
43+import qualified Data.ByteString as BS
44+import qualified Data.ByteString.Lazy as BL
3945import Data.CaseInsensitive (mk)
4046import Data.Functor.Identity
47+import Data.Kind (Type)
4148import qualified Data.Map as Map
4249import Data.Monoid ((<>))
4350import Data.Proxy (Proxy (..))
@@ -45,17 +52,21 @@ import qualified Data.Set as Set
4552import Data.Text (Text)
4653import qualified Data.Text as T
4754import qualified Data.Text.Encoding as E
55+import qualified Data.Text.Lazy as TL
4856import GHC.Exts (Constraint)
4957import GHC.TypeLits (KnownSymbol, symbolVal)
50−import Servant.API ((:<|>) (..), (:>), BasicAuth,
51− BasicAuthData, BuildHeadersTo (..),
52− Capture, Header, Headers (..),
53− HttpVersion, IsSecure, MimeRender (..),
54− NoContent, QueryFlag, QueryParam,
55− QueryParams, Raw, ReflectMethod (..),
56− RemoteHost, ReqBody,
57− ToHttpApiData (..), Vault, Verb,
58− contentType)
58+import GHCJS.DOM.Types (Blob, Document, FormData)
59+import Servant.API ((:<|>) (..), (:>), Accept (..),
60+ BasicAuth, BasicAuthData,
61+ BuildHeadersTo (..), Capture,
62+ FormUrlEncoded, Header, Headers (..),
63+ HttpVersion, IsSecure, JSON,
64+ MimeRender (..), NoContent,
65+ OctetStream, PlainText, QueryFlag,
66+ QueryParam, QueryParams, Raw,
67+ ReflectMethod (..), RemoteHost,
68+ ReqBody, ToHttpApiData (..), Vault,
69+ Verb, contentType)
5970import qualified Servant.Auth as Auth
6071
6172import Reflex.Dom.Core (Dynamic, Event, IsXhrPayload, Reflex,
@@ -69,7 +80,7 @@ import Servant.Common.BaseUrl (BaseUrl (..), Scheme (..),
6980 SupportsServantReflex, baseUrlWidget,
7081 showBaseUrl)
7182import Servant.Common.Req (ClientOptions (..), MimeUnrender (..),
72− QParam (..), QueryPart (..), Req,
83+ QParam (..), QueryPart (..), Req (..),
7384 ReqResult (..), addHeader, authData,
7485 defReq, defaultClientOptions,
7586 evalResponse, performRequestsCT,
@@ -119,9 +130,9 @@ clientWithOpts p q t baseurl = clientWithRoute p q t defReq baseurl
119130-- | This class lets us define how each API combinator
120131-- influences the creation of an HTTP request. It's mostly
121132-- an internal class, you can just use 'client'.
122−class HasClient t m layout (tag :: *) where
123− type Client t m layout tag :: *
124− clientWithRoute :: IsXhrPayload o => Proxy layout -> Proxy m -> Proxy tag -> Req t o -> Dynamic t BaseUrl -> ClientOptions -> Client t m layout tag
133+class HasClient t m layout (tag :: Type) where
134+ type Client t m layout tag :: Type
135+ clientWithRoute :: Proxy layout -> Proxy m -> Proxy tag -> Req t -> Dynamic t BaseUrl -> ClientOptions -> Client t m layout tag
125136
126137
127138instance (HasClient t m a tag, HasClient t m b tag) => HasClient t m (a :<|> b) tag where
@@ -456,22 +467,66 @@ instance SupportsServantReflex t m => HasClient t m Raw tag where
456467-- > where host = BaseUrl Http "localhost" 8080
457468-- > -- then you can just use "addBook" to query that endpoint
458469
459−instance (MimeRender ct a, HasClient t m sublayout tag, Reflex t)
460− => HasClient t m (ReqBody (ct ': cts) a :> sublayout) tag where
470+instance (GHCJS'MimeRender ct a, IsXhrPayload (ToSend ct a), Show (ToSend ct a), HasClient t m sublayout tag, Reflex t)
471+ => HasClient t m (ReqBody (ct ': cts) (a :: Type) :> sublayout) tag where
461472
462473 type Client t m (ReqBody (ct ': cts) a :> sublayout) tag =
463− Dynamic t (Either Text a) -> Client t m sublayout tag
474+ Dynamic t (Either Text (ToConvert ct a)) -> Client t m sublayout tag
464475
465− clientWithRoute Proxy q t req baseurl opts body =
476+ clientWithRoute Proxy q t Req{..} baseurl opts body =
466477 clientWithRoute (Proxy :: Proxy sublayout) q t req' baseurl opts
467− where req' = req { reqBody = bodyBytesCT }
478+ where req' = Req { reqBody = Just $ (fmap . fmap)
479+ (\b -> (ghcjsMimeRender ctProxy atProxy b, ctString)) body
480+ , ..}
468481 ctProxy = Proxy :: Proxy ct
482+ atProxy = Proxy :: Proxy a
469483 ctString = T.pack $ show $ contentType ctProxy
470− bodyBytesCT = Just $ (fmap . fmap)
471− (\b -> (mimeRender ctProxy b, ctString))
472− body
473484
485+class Accept ctype => GHCJS'MimeRender (ctype :: Type) (a :: Type) where
486+ type ToConvert ctype a :: Type
487+ type ToConvert ctype a = a
488+
489+ type ToSend ctype a :: Type
490+ type ToSend ctype a = BS.ByteString
491+
492+ ghcjsMimeRender
493+ :: IsXhrPayload (ToSend ctype a)
494+ => Proxy ctype -> Proxy a -> ToConvert ctype a -> ToSend ctype a
495+ default ghcjsMimeRender
496+ :: ( IsXhrPayload (ToSend ctype a)
497+ , MimeRender ctype a
498+ , MimeRender ctype (ToConvert ctype a)
499+ , ToSend ctype a ~ BS.ByteString)
500+ => Proxy ctype -> Proxy a -> ToConvert ctype a -> ToSend ctype a
501+ ghcjsMimeRender ctype _ = BL.toStrict . mimeRender ctype
502+
503+instance MimeRender JSON x => GHCJS'MimeRender JSON x
504+
505+instance MimeRender FormUrlEncoded x => GHCJS'MimeRender FormUrlEncoded x
506+
507+instance GHCJS'MimeRender OctetStream BL.ByteString where
508+ type ToConvert OctetStream BL.ByteString = Blob
509+ type ToSend OctetStream BL.ByteString = Blob
510+ ghcjsMimeRender _ _ = id
511+
512+instance GHCJS'MimeRender PlainText String where
513+ type ToSend PlainText String = Text
514+ ghcjsMimeRender _ _ = T.pack
515+
516+instance GHCJS'MimeRender PlainText Text where
517+ type ToSend PlainText Text = Text
518+ ghcjsMimeRender _ _ = id
519+
520+instance GHCJS'MimeRender PlainText TL.Text where
521+ type ToSend PlainText TL.Text = Text
522+ ghcjsMimeRender _ _ = TL.toStrict
523+
524+instance GHCJS'MimeRender PlainText BL.ByteString where
525+ type ToSend PlainText BL.ByteString = BS.ByteString
526+ ghcjsMimeRender _ _ = BL.toStrict
474527
528+instance GHCJS'MimeRender PlainText BS.ByteString where
529+ ghcjsMimeRender _ _ = id
475530
476531-- | Make the querying function append @path@ to the request path.
477532instance (KnownSymbol path, HasClient t m sublayout tag, Reflex t) => HasClient t m (path :> sublayout) tag where