@@ -56,14 +56,12 @@ import qualified Network.HTTP.Types as HTTP
56import Servant.API
56import Servant.API
57import Web.HttpApiData
57import Web.HttpApiData
59−data AltJSON = AltJSON deriving (Eq, Ord, Show, Read, Generic, Typeable)
59+data AltJSON = AltJSON deriving (Eq, Ord, Show, Read, Generic, Typeable)
60−data AltMedia = AltMedia deriving (Eq, Ord, Show, Read, Generic, Typeable)
60+data AltMedia = AltMedia deriving (Eq, Ord, Show, Read, Generic, Typeable)
62−instance ToHttpApiData AltJSON where toQueryParam = const "json"
63−instance ToHttpApiData AltMedia where toQueryParam = const "media"
65data Multipart = Multipart deriving (Eq, Ord, Show, Read, Generic, Typeable)
61data Multipart = Multipart deriving (Eq, Ord, Show, Read, Generic, Typeable)
63+instance ToHttpApiData AltJSON where toQueryParam = const "json"
64+instance ToHttpApiData AltMedia where toQueryParam = const "media"
67instance ToHttpApiData Multipart where toQueryParam = const "multipart"
65instance ToHttpApiData Multipart where toQueryParam = const "multipart"
69newtype OAuthScope = OAuthScope Text
67newtype OAuthScope = OAuthScope Text
@@ -157,7 +155,7 @@ newtype ServiceId = ServiceId Text
159newtype MediaDownload a = MediaDownload a
157newtype MediaDownload a = MediaDownload a
160−data MediaUpload a = MediaUpload a Part
158+data MediaUpload a = MediaUpload a Body
162_Coerce :: (Coercible a b, Coercible b a) => Iso' a b
160_Coerce :: (Coercible a b, Coercible b a) => Iso' a b
163_Coerce = iso coerce coerce
161_Coerce = iso coerce coerce
@@ -265,28 +263,27 @@ serviceSecure = lens _svcSecure (\s a -> s { _svcSecure = a })
265serviceTimeout :: Lens' ServiceConfig (Maybe Seconds)
263serviceTimeout :: Lens' ServiceConfig (Maybe Seconds)
266serviceTimeout = lens _svcTimeout (\s a -> s { _svcTimeout = a })
264serviceTimeout = lens _svcTimeout (\s a -> s { _svcTimeout = a })
268−-- | A single part of a multipart message.
266+-- | A single part of a (potentially multipart) request body.
269−data Part = Part MediaType [(HeaderName, ByteString)] RequestBody
267+data Body = Body !MediaType !RequestBody
271−data Payload
269+setMIMEType :: MediaType -> Body -> Body
272− = Body !MediaType !RequestBody
270+setMIMEType m (Body _ b) = Body m b
275-- | An intermediary request builder.
272-- | An intermediary request builder.
276data Request = Request
273data Request = Request
277 { _rqPath :: !Builder
274 { _rqPath :: !Builder
278 , _rqQuery :: !(DList (ByteString, Maybe ByteString))
275 , _rqQuery :: !(DList (ByteString, Maybe ByteString))
279 , _rqHeaders :: !(DList (HeaderName, ByteString))
276 , _rqHeaders :: !(DList (HeaderName, ByteString))
280− , _rqBody :: !(Maybe Payload)
277+ , _rqBody :: ![Body]
283instance Monoid Request where
280instance Monoid Request where
284− mempty = Request mempty mempty mempty Nothing
281+ mempty = Request mempty mempty mempty mempty
285 mappend a b = Request
282 mappend a b = Request
286− (_rqPath a <> "/" <> _rqPath b)
283+ (_rqPath a <> "/" <> _rqPath b)
287− (_rqQuery a <> _rqQuery b)
284+ (_rqQuery a <> _rqQuery b)
288− (_rqHeaders a <> _rqHeaders b)
285+ (_rqHeaders a <> _rqHeaders b)
289− (_rqBody b <|> _rqBody a)
286+ (_rqBody b <> _rqBody a)
291appendPath :: Request -> Builder -> Request
288appendPath :: Request -> Builder -> Request
292appendPath rq x = rq { _rqPath = _rqPath rq <> "/" <> x }
289appendPath rq x = rq { _rqPath = _rqPath rq <> "/" <> x }
@@ -305,11 +302,8 @@ appendHeader rq k (Just v) = rq
305 { _rqHeaders = DList.snoc (_rqHeaders rq) (k, Text.encodeUtf8 v)
302 { _rqHeaders = DList.snoc (_rqHeaders rq) (k, Text.encodeUtf8 v)
308−setBody :: Request -> MediaType -> RequestBody -> Request
305+setBody :: Request -> [Body] -> Request
309−setBody rq c x = rq { _rqBody = Just (Body c x) }
306+setBody rq bs = rq { _rqBody = bs }
311−setRelated :: Request -> [Part] -> Request
312−setRelated rq ps = rq { _rqBody = Just (Related ps) }
314-- | A materialised 'http-client' request and associated response parser.
308-- | A materialised 'http-client' request and associated response parser.
315data Client a = Client
309data Client a = Client
@@ -357,25 +351,22 @@ client f cs m ns rq s = Client
359class Accept c => ToBody c a where
353class Accept c => ToBody c a where
360− toBody :: Proxy c -> a -> RequestBody
354+ toBody :: Proxy c -> a -> Body
362−instance ToBody OctetStream RequestBody where
365instance ToBody OctetStream ByteString where
356instance ToBody OctetStream ByteString where
366− toBody Proxy = RequestBodyBS
357+ toBody p = Body (contentType p) . RequestBodyBS
368instance ToBody OctetStream LBS.ByteString where
359instance ToBody OctetStream LBS.ByteString where
369− toBody Proxy = RequestBodyLBS
360+ toBody p = Body (contentType p) . RequestBodyLBS
371instance ToBody PlainText ByteString where
362instance ToBody PlainText ByteString where
372− toBody Proxy = RequestBodyBS
363+ toBody p = Body (contentType p) . RequestBodyBS
374instance ToBody PlainText LBS.ByteString where
365instance ToBody PlainText LBS.ByteString where
375− toBody Proxy = RequestBodyLBS
366+ toBody p = Body (contentType p) . RequestBodyLBS
377instance ToJSON a => ToBody JSON a where
368instance ToJSON a => ToBody JSON a where
378− toBody Proxy = RequestBodyLBS . encode
369+ toBody p = Body (contentType p) . RequestBodyLBS . encode
380class Accept c => FromStream c a where
371class Accept c => FromStream c a where
381 fromStream :: Proxy c
372 fromStream :: Proxy c
@@ -417,13 +408,18 @@ data MultipartRelated (cs :: [*]) m
417instance ( ToBody c m
408instance ( ToBody c m
418 , GoogleClient fn
409 , GoogleClient fn
419 ) => GoogleClient (MultipartRelated (c ': cs) m :> fn) where
410 ) => GoogleClient (MultipartRelated (c ': cs) m :> fn) where
420− type Fn (MultipartRelated (c ': cs) m :> fn) = m -> Part -> Fn fn
411+ type Fn (MultipartRelated (c ': cs) m :> fn) = m -> Body -> Fn fn
422 buildClient Proxy rq m b =
413 buildClient Proxy rq m b =
423 buildClient (Proxy :: Proxy fn) $
414 buildClient (Proxy :: Proxy fn) $
424− setRelated rq [Part (contentType mc) [] (toBody mc m), b]
415+ setBody rq [toBody (Proxy :: Proxy c) m, b]
417+instance GoogleClient fn => GoogleClient (AltMedia :> fn) where
418+ type Fn (AltMedia :> fn) = Body -> Fn fn
420+ buildClient Proxy rq b =
426− mc = Proxy :: Proxy c
421+ buildClient (Proxy :: Proxy fn) $
428instance (KnownSymbol s, GoogleClient fn) => GoogleClient (s :> fn) where
424instance (KnownSymbol s, GoogleClient fn) => GoogleClient (s :> fn) where
429 type Fn (s :> fn) = Fn fn
425 type Fn (s :> fn) = Fn fn
@@ -513,11 +509,9 @@ instance ( ToBody c a
513 ) => GoogleClient (ReqBody (c ': cs) a :> fn) where
509 ) => GoogleClient (ReqBody (c ': cs) a :> fn) where
514 type Fn (ReqBody (c ': cs) a :> fn) = a -> Fn fn
510 type Fn (ReqBody (c ': cs) a :> fn) = a -> Fn fn
512+ buildClient Proxy rq x =
516− buildClient Proxy rq = buildClient (Proxy :: Proxy fn)
513+ buildClient (Proxy :: Proxy fn) $
517− . setBody rq (contentType p)
520− p = Proxy :: Proxy c
514+ setBody rq [toBody (Proxy :: Proxy c) x]
522instance {-# OVERLAPPABLE #-}
516instance {-# OVERLAPPABLE #-}
523 FromStream c a => GoogleClient (Get (c ': cs) a) where
517 FromStream c a => GoogleClient (Get (c ': cs) a) where