git.iko.soy/typeable/servant
on master help sign in
[servant-foreign] Parameterise type classes with a foreign representation type

We allow a user-specified type to represent the foreign type of haskell types encountered in the API. This lets users map Integer, Date etc. to representations other than Text, and have those representations available in the returned list of Req. For example, we might want to map a type which has an instance of Generic to both a foreign type name and a class declaration for that foreign type such that it can encode/decode itself to JSON. The previous limitation to a single Text output prevented this case.

commit5188e842a9288ac3986b30b93cb1a5d58cb2933d authorSteve Purcell <steve@sanityinc.com> dateMon 14 Mar 2016, 10:21 (11 years ago) parent207f05e
8 files changed+155−169
servant-foreign/servant-foreign.cabal−1 servant-foreign/src/Servant/Foreign.hs−3 servant-foreign/src/Servant/Foreign/Internal.hs+114−122 servant-foreign/test/Servant/ForeignSpec.hs+16−17 servant-js/src/Servant/JS.hs+5−5 servant-js/src/Servant/JS/Internal.hs+2−4 servant-js/test/Servant/JSSpec.hs+2−2 servant-js/test/Servant/JSSpec/CustomHeaders.hs+16−15
servant-foreign/servant-foreign.cabal−1
@@ -65,7 +65,6 @@ test-suite spec
6565 build-depends: base
6666 , hspec >= 2.1.8
6767 , servant-foreign
68− , text >= 1.2 && < 1.3
6968 default-language: Haskell2010
7069 default-extensions: ConstraintKinds
7170 , DataKinds
servant-foreign/src/Servant/Foreign.hs−3
@@ -10,7 +10,6 @@ module Servant.Foreign
1010 , Url(..)
1111 -- aliases
1212 , Path
13− , ForeignType(..)
1413 , Arg(..)
1514 , FunctionName(..)
1615 , PathSegment(..)
@@ -31,7 +30,6 @@ module Servant.Foreign
3130 , headerArg
3231 -- prisms
3332 , _PathSegment
34− , _ForeignType
3533 , _HeaderArg
3634 , _ReplaceHeaderArg
3735 , _Static
@@ -42,7 +40,6 @@ module Servant.Foreign
4240 -- rest of it
4341 , HasForeign(..)
4442 , HasForeignType(..)
45− , HasNoForeignType
4643 , GenerateList(..)
4744 , NoTypes
4845 , captureArg
servant-foreign/src/Servant/Foreign/Internal.hs+114−122
@@ -27,15 +27,6 @@ newtype FunctionName = FunctionName { unFunctionName :: [Text] }
2727
2828makePrisms ''FunctionName
2929
30−newtype ForeignType f = ForeignType { unForeignType :: f }
31−
32−deriving instance Show f => Show (ForeignType f)
33−deriving instance Eq f => Eq (ForeignType f)
34−deriving instance IsString f => IsString (ForeignType f)
35−deriving instance Monoid f => Monoid (ForeignType f)
36−
37−makePrisms ''ForeignType
38−
3930newtype PathSegment = PathSegment { unPathSegment :: Text }
4031 deriving (Show, Eq, IsString, Monoid)
4132
@@ -43,7 +34,7 @@ makePrisms ''PathSegment
4334
4435data Arg f = Arg
4536 { _argName :: PathSegment
46− , _argType :: ForeignType f }
37+ , _argType :: f }
4738
4839deriving instance Eq f => Eq (Arg f)
4940deriving instance Show f => Show (Arg f)
@@ -130,8 +121,8 @@ data Req f = Req
130121 { _reqUrl :: Url f
131122 , _reqMethod :: HTTP.Method
132123 , _reqHeaders :: [HeaderArg f]
133− , _reqBody :: Maybe (ForeignType f)
134− , _reqReturnType :: ForeignType f
124+ , _reqBody :: Maybe f
125+ , _reqReturnType :: Maybe f
135126 , _reqFuncName :: FunctionName
136127 }
137128
@@ -140,8 +131,8 @@ deriving instance Show f => Show (Req f)
140131
141132makeLenses ''Req
142133
143−defReq :: Req Text
144−defReq = Req defUrl "GET" [] Nothing (ForeignType "") (FunctionName [])
134+defReq :: Req ftype
135+defReq = Req defUrl "GET" [] Nothing Nothing (FunctionName [])
145136
146137-- | To be used exclusively as a "negative" return type/constraint
147138-- by @'Elem`@ type family.
@@ -154,158 +145,158 @@ type family Elem (a :: *) (ls::[*]) :: Constraint where
154145
155146-- | 'HasForeignType' maps Haskell types with types in the target
156147-- language of your backend. For example, let's say you're
157−-- implementing a backend to some language __X__:
148+-- implementing a backend to some language __X__, and you want
149+-- a Text representation of each input/output type mentioned in the API:
158150--
159151-- > -- First you need to create a dummy type to parametrize your
160152-- > -- instances.
161153-- > data LangX
162154-- >
163155-- > -- Otherwise you define instances for the types you need
164−-- > instance HasForeignType LangX Int where
165−-- > typeFor _ _ = "intX"
156+-- > instance HasForeignType LangX Text Int where
157+-- > typeFor _ _ _ = "intX"
166158-- >
167159-- > -- Or for example in case of lists
168−-- > instance HasForeignType LangX a => HasForeignType LangX [a] where
169−-- > typeFor lang _ = "listX of " <> typeFor lang (Proxy :: Proxy a)
160+-- > instance HasForeignType LangX Text a => HasForeignType LangX Text [a] where
161+-- > typeFor lang type _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a)
170162--
171163-- Finally to generate list of information about all the endpoints for
172164-- an API you create a function of a form:
173165--
174−-- > getEndpoints :: (HasForeign LangX api, GenerateList (Foreign api))
175−-- > => Proxy api -> [Req]
176−-- > getEndpoints api = listFromAPI (Proxy :: Proxy LangX) api
166+-- > getEndpoints :: (HasForeign LangX Text api, GenerateList Text (Foreign Text api))
167+-- > => Proxy api -> [Req Text]
168+-- > getEndpoints api = listFromAPI (Proxy :: Proxy LangX) (Proxy :: Proxy Text) api
177169--
178170-- > -- If language __X__ is dynamically typed then you can use
179−-- > -- a predefined NoTypes parameter
180−-- > getEndpoints :: (HasForeign NoTypes api, GenerateList (Foreign api))
181−-- > => Proxy api -> [Req]
182−-- > getEndpoints api = listFromAPI (Proxy :: Proxy NoTypes) api
171+-- > -- a predefined NoTypes parameter with the () output type:
172+--
173+-- > getEndpoints :: (HasForeign NoTypes () api, GenerateList Text (Foreign () api))
174+-- > => Proxy api -> [Req ()]
175+-- > getEndpoints api = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) api
183176-- >
184177--
185−class HasForeignType lang a where
186− typeFor :: Proxy lang -> Proxy a -> ForeignType Text
178+class HasForeignType lang ftype a where
179+ typeFor :: Proxy lang -> Proxy ftype -> Proxy a -> ftype
187180
188181data NoTypes
189182
190−instance HasForeignType NoTypes ftype where
191− typeFor _ _ = ForeignType empty
192−
193−type HasNoForeignType = HasForeignType NoTypes
183+instance HasForeignType NoTypes () ftype where
184+ typeFor _ _ _ = ()
194185
195−class HasForeign lang (layout :: *) where
196− type Foreign layout :: *
197− foreignFor :: Proxy lang -> Proxy layout -> Req Text -> Foreign layout
186+class HasForeign lang ftype (layout :: *) where
187+ type Foreign ftype layout :: *
188+ foreignFor :: Proxy lang -> Proxy ftype -> Proxy layout -> Req ftype -> Foreign ftype layout
198189
199−instance (HasForeign lang a, HasForeign lang b)
200− => HasForeign lang (a :<|> b) where
201− type Foreign (a :<|> b) = Foreign a :<|> Foreign b
190+instance (HasForeign lang ftype a, HasForeign lang ftype b)
191+ => HasForeign lang ftype (a :<|> b) where
192+ type Foreign ftype (a :<|> b) = Foreign ftype a :<|> Foreign ftype b
202193
203− foreignFor lang Proxy req =
204− foreignFor lang (Proxy :: Proxy a) req
205− :<|> foreignFor lang (Proxy :: Proxy b) req
194+ foreignFor lang ftype Proxy req =
195+ foreignFor lang ftype (Proxy :: Proxy a) req
196+ :<|> foreignFor lang ftype (Proxy :: Proxy b) req
206197
207−instance (KnownSymbol sym, HasForeignType lang ftype, HasForeign lang sublayout)
208− => HasForeign lang (Capture sym ftype :> sublayout) where
209− type Foreign (Capture sym a :> sublayout) = Foreign sublayout
198+instance (KnownSymbol sym, HasForeignType lang ftype t, HasForeign lang ftype sublayout)
199+ => HasForeign lang ftype (Capture sym t :> sublayout) where
200+ type Foreign ftype (Capture sym a :> sublayout) = Foreign ftype sublayout
210201
211− foreignFor lang Proxy req =
212− foreignFor lang (Proxy :: Proxy sublayout) $
202+ foreignFor lang Proxy Proxy req =
203+ foreignFor lang Proxy (Proxy :: Proxy sublayout) $
213204 req & reqUrl . path <>~ [Segment (Cap arg)]
214205 & reqFuncName . _FunctionName %~ (++ ["by", str])
215206 where
216207 str = pack . symbolVal $ (Proxy :: Proxy sym)
217− ftype = typeFor lang (Proxy :: Proxy ftype)
208+ ftype = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy t)
218209 arg = Arg
219210 { _argName = PathSegment str
220211 , _argType = ftype }
221212
222−instance (Elem JSON list, HasForeignType lang a, ReflectMethod method)
223− => HasForeign lang (Verb method status list a) where
224− type Foreign (Verb method status list a) = Req Text
213+instance (Elem JSON list, HasForeignType lang ftype a, ReflectMethod method)
214+ => HasForeign lang ftype (Verb method status list a) where
215+ type Foreign ftype (Verb method status list a) = Req ftype
225216
226− foreignFor lang Proxy req =
217+ foreignFor lang Proxy Proxy req =
227218 req & reqFuncName . _FunctionName %~ (methodLC :)
228219 & reqMethod .~ method
229− & reqReturnType .~ retType
220+ & reqReturnType .~ Just retType
230221 where
231− retType = typeFor lang (Proxy :: Proxy a)
222+ retType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a)
232223 method = reflectMethod (Proxy :: Proxy method)
233224 methodLC = toLower $ decodeUtf8 method
234225
235−instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
236− => HasForeign lang (Header sym a :> sublayout) where
237− type Foreign (Header sym a :> sublayout) = Foreign sublayout
226+instance (KnownSymbol sym, HasForeignType lang ftype a, HasForeign lang ftype sublayout)
227+ => HasForeign lang ftype (Header sym a :> sublayout) where
228+ type Foreign ftype (Header sym a :> sublayout) = Foreign ftype sublayout
238229
239− foreignFor lang Proxy req =
240− foreignFor lang subP $ req & reqHeaders <>~ [HeaderArg arg]
230+ foreignFor lang Proxy Proxy req =
231+ foreignFor lang Proxy subP $ req & reqHeaders <>~ [HeaderArg arg]
241232 where
242233 hname = pack . symbolVal $ (Proxy :: Proxy sym)
243234 arg = Arg
244235 { _argName = PathSegment hname
245− , _argType = typeFor lang (Proxy :: Proxy a) }
236+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) }
246237 subP = Proxy :: Proxy sublayout
247238
248−instance (KnownSymbol sym, HasForeignType lang a, HasForeign lang sublayout)
249− => HasForeign lang (QueryParam sym a :> sublayout) where
250− type Foreign (QueryParam sym a :> sublayout) = Foreign sublayout
239+instance (KnownSymbol sym, HasForeignType lang ftype a, HasForeign lang ftype sublayout)
240+ => HasForeign lang ftype (QueryParam sym a :> sublayout) where
241+ type Foreign ftype (QueryParam sym a :> sublayout) = Foreign ftype sublayout
251242
252− foreignFor lang Proxy req =
253− foreignFor lang (Proxy :: Proxy sublayout) $
243+ foreignFor lang Proxy Proxy req =
244+ foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy sublayout) $
254245 req & reqUrl.queryStr <>~ [QueryArg arg Normal]
255246 where
256247 str = pack . symbolVal $ (Proxy :: Proxy sym)
257248 arg = Arg
258249 { _argName = PathSegment str
259− , _argType = typeFor lang (Proxy :: Proxy a) }
250+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy a) }
260251
261252instance
262− (KnownSymbol sym, HasForeignType lang [a], HasForeign lang sublayout)
263− => HasForeign lang (QueryParams sym a :> sublayout) where
264− type Foreign (QueryParams sym a :> sublayout) = Foreign sublayout
265− foreignFor lang Proxy req =
266− foreignFor lang (Proxy :: Proxy sublayout) $
253+ (KnownSymbol sym, HasForeignType lang ftype [a], HasForeign lang ftype sublayout)
254+ => HasForeign lang ftype (QueryParams sym a :> sublayout) where
255+ type Foreign ftype (QueryParams sym a :> sublayout) = Foreign ftype sublayout
256+ foreignFor lang Proxy Proxy req =
257+ foreignFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy sublayout) $
267258 req & reqUrl.queryStr <>~ [QueryArg arg List]
268259 where
269260 str = pack . symbolVal $ (Proxy :: Proxy sym)
270261 arg = Arg
271262 { _argName = PathSegment str
272− , _argType = typeFor lang (Proxy :: Proxy [a]) }
263+ , _argType = typeFor lang (Proxy :: Proxy ftype) (Proxy :: Proxy [a]) }
273264
274265instance
275− (KnownSymbol sym, HasForeignType lang Bool, HasForeign lang sublayout)
276− => HasForeign lang (QueryFlag sym :> sublayout) where
277− type Foreign (QueryFlag sym :> sublayout) = Foreign sublayout
266+ (KnownSymbol sym, HasForeignType lang ftype Bool, HasForeign lang ftype sublayout)
267+ => HasForeign lang ftype (QueryFlag sym :> sublayout) where
268+ type Foreign ftype (QueryFlag sym :> sublayout) = Foreign ftype sublayout
278269
279− foreignFor lang Proxy req =
280− foreignFor lang (Proxy :: Proxy sublayout) $
270+ foreignFor lang ftype Proxy req =
271+ foreignFor lang ftype (Proxy :: Proxy sublayout) $
281272 req & reqUrl.queryStr <>~ [QueryArg arg Flag]
282273 where
283274 str = pack . symbolVal $ (Proxy :: Proxy sym)
284275 arg = Arg
285276 { _argName = PathSegment str
286− , _argType = typeFor lang (Proxy :: Proxy Bool) }
277+ , _argType = typeFor lang ftype (Proxy :: Proxy Bool) }
287278
288−instance HasForeign lang Raw where
289− type Foreign Raw = HTTP.Method -> Req Text
279+instance HasForeign lang ftype Raw where
280+ type Foreign ftype Raw = HTTP.Method -> Req ftype
290281
291− foreignFor _ Proxy req method =
282+ foreignFor _ Proxy Proxy req method =
292283 req & reqFuncName . _FunctionName %~ ((toLower $ decodeUtf8 method) :)
293284 & reqMethod .~ method
294285
295−instance (Elem JSON list, HasForeignType lang a, HasForeign lang sublayout)
296− => HasForeign lang (ReqBody list a :> sublayout) where
297− type Foreign (ReqBody list a :> sublayout) = Foreign sublayout
286+instance (Elem JSON list, HasForeignType lang ftype a, HasForeign lang ftype sublayout)
287+ => HasForeign lang ftype (ReqBody list a :> sublayout) where
288+ type Foreign ftype (ReqBody list a :> sublayout) = Foreign ftype sublayout
298289
299− foreignFor lang Proxy req =
300− foreignFor lang (Proxy :: Proxy sublayout) $
301− req & reqBody .~ (Just $ typeFor lang (Proxy :: Proxy a))
290+ foreignFor lang ftype Proxy req =
291+ foreignFor lang ftype (Proxy :: Proxy sublayout) $
292+ req & reqBody .~ (Just $ typeFor lang ftype (Proxy :: Proxy a))
302293
303−instance (KnownSymbol path, HasForeign lang sublayout)
304− => HasForeign lang (path :> sublayout) where
305− type Foreign (path :> sublayout) = Foreign sublayout
294+instance (KnownSymbol path, HasForeign lang ftype sublayout)
295+ => HasForeign lang ftype (path :> sublayout) where
296+ type Foreign ftype (path :> sublayout) = Foreign ftype sublayout
306297
307− foreignFor lang Proxy req =
308− foreignFor lang (Proxy :: Proxy sublayout) $
298+ foreignFor lang ftype Proxy req =
299+ foreignFor lang ftype (Proxy :: Proxy sublayout) $
309300 req & reqUrl . path <>~ [Segment (Static (PathSegment str))]
310301 & reqFuncName . _FunctionName %~ (++ [str])
311302 where
@@ -313,58 +304,59 @@ instance (KnownSymbol path, HasForeign lang sublayout)
313304 Data.Text.map (\c -> if c == '.' then '_' else c)
314305 . pack . symbolVal $ (Proxy :: Proxy path)
315306
316−instance HasForeign lang sublayout
317− => HasForeign lang (RemoteHost :> sublayout) where
318− type Foreign (RemoteHost :> sublayout) = Foreign sublayout
307+instance HasForeign lang ftype sublayout
308+ => HasForeign lang ftype (RemoteHost :> sublayout) where
309+ type Foreign ftype (RemoteHost :> sublayout) = Foreign ftype sublayout
319310
320− foreignFor lang Proxy req =
321− foreignFor lang (Proxy :: Proxy sublayout) req
311+ foreignFor lang ftype Proxy req =
312+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
322313
323−instance HasForeign lang sublayout
324− => HasForeign lang (IsSecure :> sublayout) where
325− type Foreign (IsSecure :> sublayout) = Foreign sublayout
314+instance HasForeign lang ftype sublayout
315+ => HasForeign lang ftype (IsSecure :> sublayout) where
316+ type Foreign ftype (IsSecure :> sublayout) = Foreign ftype sublayout
326317
327− foreignFor lang Proxy req =
328− foreignFor lang (Proxy :: Proxy sublayout) req
318+ foreignFor lang ftype Proxy req =
319+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
329320
330−instance HasForeign lang sublayout => HasForeign lang (Vault :> sublayout) where
331− type Foreign (Vault :> sublayout) = Foreign sublayout
321+instance HasForeign lang ftype sublayout => HasForeign lang ftype (Vault :> sublayout) where
322+ type Foreign ftype (Vault :> sublayout) = Foreign ftype sublayout
332323
333− foreignFor lang Proxy req =
334− foreignFor lang (Proxy :: Proxy sublayout) req
324+ foreignFor lang ftype Proxy req =
325+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
335326
336−instance HasForeign lang sublayout =>
337− HasForeign lang (WithNamedContext name context sublayout) where
327+instance HasForeign lang ftype sublayout =>
328+ HasForeign lang ftype (WithNamedContext name context sublayout) where
338329
339− type Foreign (WithNamedContext name context sublayout) = Foreign sublayout
330+ type Foreign ftype (WithNamedContext name context sublayout) = Foreign ftype sublayout
340331
341− foreignFor lang Proxy = foreignFor lang (Proxy :: Proxy sublayout)
332+ foreignFor lang ftype Proxy = foreignFor lang ftype (Proxy :: Proxy sublayout)
342333
343−instance HasForeign lang sublayout
344− => HasForeign lang (HttpVersion :> sublayout) where
345− type Foreign (HttpVersion :> sublayout) = Foreign sublayout
334+instance HasForeign lang ftype sublayout
335+ => HasForeign lang ftype (HttpVersion :> sublayout) where
336+ type Foreign ftype (HttpVersion :> sublayout) = Foreign ftype sublayout
346337
347− foreignFor lang Proxy req =
348− foreignFor lang (Proxy :: Proxy sublayout) req
338+ foreignFor lang ftype Proxy req =
339+ foreignFor lang ftype (Proxy :: Proxy sublayout) req
349340
350341-- | Utility class used by 'listFromAPI' which computes
351342-- the data needed to generate a function for each endpoint
352343-- and hands it all back in a list.
353−class GenerateList reqs where
354− generateList :: reqs -> [Req Text]
344+class GenerateList ftype reqs where
345+ generateList :: reqs -> [Req ftype]
355346
356−instance GenerateList (Req Text) where
347+instance GenerateList ftype (Req ftype) where
357348 generateList r = [r]
358349
359−instance (GenerateList start, GenerateList rest)
360− => GenerateList (start :<|> rest) where
350+instance (GenerateList ftype start, GenerateList ftype rest)
351+ => GenerateList ftype (start :<|> rest) where
361352 generateList (start :<|> rest) = (generateList start) ++ (generateList rest)
362353
363354-- | Generate the necessary data for codegen as a list, each 'Req'
364355-- describing one endpoint from your API type.
365356listFromAPI
366− :: (HasForeign lang api, GenerateList (Foreign api))
357+ :: (HasForeign lang ftype api, GenerateList ftype (Foreign ftype api))
367358 => Proxy lang
359+ -> Proxy ftype
368360 -> Proxy api
369− -> [Req Text]
370−listFromAPI lang p = generateList (foreignFor lang p defReq)
361+ -> [Req ftype]
362+listFromAPI lang ftype p = generateList (foreignFor lang ftype p defReq)
servant-foreign/test/Servant/ForeignSpec.hs+16−17
@@ -6,7 +6,6 @@ module Servant.ForeignSpec where
66import Data.Monoid ((<>))
77import Data.Proxy
88import Servant.Foreign
9−import Data.Text (Text(..))
109
1110import Test.Hspec
1211
@@ -27,20 +26,20 @@ camelCaseSpec = describe "camelCase" $ do
2726
2827data LangX
2928
30−instance HasForeignType LangX () where
31− typeFor _ _ = ForeignType "voidX"
29+instance HasForeignType LangX String () where
30+ typeFor _ _ _ = "voidX"
3231
33−instance HasForeignType LangX Int where
34− typeFor _ _ = "intX"
32+instance HasForeignType LangX String Int where
33+ typeFor _ _ _ = "intX"
3534
36−instance HasForeignType LangX Bool where
37− typeFor _ _ = "boolX"
35+instance HasForeignType LangX String Bool where
36+ typeFor _ _ _ = "boolX"
3837
39−instance OVERLAPPING_ HasForeignType LangX String where
40− typeFor _ _ = "stringX"
38+instance OVERLAPPING_ HasForeignType LangX String String where
39+ typeFor _ _ _ = "stringX"
4140
42−instance OVERLAPPABLE_ HasForeignType LangX a => HasForeignType LangX [a] where
43− typeFor lang _ = "listX of " <> typeFor lang (Proxy :: Proxy a)
41+instance OVERLAPPABLE_ HasForeignType LangX String a => HasForeignType LangX String [a] where
42+ typeFor lang ftype _ = "listX of " <> typeFor lang ftype (Proxy :: Proxy a)
4443
4544type TestApi
4645 = "test" :> Header "header" [String] :> QueryFlag "flag" :> Get '[JSON] Int
@@ -48,8 +47,8 @@ type TestApi
4847 :<|> "test" :> QueryParams "params" Int :> ReqBody '[JSON] String :> Put '[JSON] ()
4948 :<|> "test" :> Capture "id" Int :> Delete '[JSON] ()
5049
51−testApi :: [Req Text]
52−testApi = listFromAPI (Proxy :: Proxy LangX) (Proxy :: Proxy TestApi)
50+testApi :: [Req String]
51+testApi = listFromAPI (Proxy :: Proxy LangX) (Proxy :: Proxy String) (Proxy :: Proxy TestApi)
5352
5453listFromAPISpec :: Spec
5554listFromAPISpec = describe "listFromAPI" $ do
@@ -66,7 +65,7 @@ listFromAPISpec = describe "listFromAPI" $ do
6665 , _reqMethod = "GET"
6766 , _reqHeaders = [HeaderArg $ Arg "header" "listX of stringX"]
6867 , _reqBody = Nothing
69− , _reqReturnType = "intX"
68+ , _reqReturnType = Just "intX"
7069 , _reqFuncName = FunctionName ["get", "test"]
7170 }
7271
@@ -78,7 +77,7 @@ listFromAPISpec = describe "listFromAPI" $ do
7877 , _reqMethod = "POST"
7978 , _reqHeaders = []
8079 , _reqBody = Just "listX of stringX"
81− , _reqReturnType = "voidX"
80+ , _reqReturnType = Just "voidX"
8281 , _reqFuncName = FunctionName ["post", "test"]
8382 }
8483
@@ -91,7 +90,7 @@ listFromAPISpec = describe "listFromAPI" $ do
9190 , _reqMethod = "PUT"
9291 , _reqHeaders = []
9392 , _reqBody = Just "stringX"
94− , _reqReturnType = "voidX"
93+ , _reqReturnType = Just "voidX"
9594 , _reqFuncName = FunctionName ["put", "test"]
9695 }
9796
@@ -104,6 +103,6 @@ listFromAPISpec = describe "listFromAPI" $ do
104103 , _reqMethod = "DELETE"
105104 , _reqHeaders = []
106105 , _reqBody = Nothing
107− , _reqReturnType = "voidX"
106+ , _reqReturnType = Just "voidX"
108107 , _reqFuncName = FunctionName ["delete", "test", "by", "id"]
109108 }
servant-js/src/Servant/JS.hs+5−5
@@ -128,22 +128,22 @@ import Servant.Foreign (GenerateList(..), listFromAPI, NoTypes)
128128-- | Generate the data necessary to generate javascript code
129129-- for all the endpoints of an API, as ':<|>'-separated values
130130-- of type 'AjaxReq'.
131−javascript :: HasForeign NoTypes layout => Proxy layout -> Foreign layout
132−javascript p = foreignFor (Proxy :: Proxy NoTypes) p defReq
131+javascript :: HasForeign NoTypes () layout => Proxy layout -> Foreign () layout
132+javascript p = foreignFor (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) p defReq
133133
134134-- | Directly generate all the javascript functions for your API
135135-- from a 'Proxy' for your API type. You can then write it to
136136-- a file or integrate it in a page, for example.
137−jsForAPI :: (HasForeign NoTypes api, GenerateList (Foreign api))
137+jsForAPI :: (HasForeign NoTypes () api, GenerateList () (Foreign () api))
138138 => Proxy api -- ^ proxy for your API type
139139 -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
140140 -> Text -- ^ a text that you can embed in your pages or write to a file
141−jsForAPI p gen = gen (listFromAPI (Proxy :: Proxy NoTypes) p)
141+jsForAPI p gen = gen (listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) p)
142142
143143-- | Directly generate all the javascript functions for your API
144144-- from a 'Proxy' for your API type using the given generator
145145-- and write the resulting code to a file at the given path.
146−writeJSForAPI :: (HasForeign NoTypes api, GenerateList (Foreign api))
146+writeJSForAPI :: (HasForeign NoTypes () api, GenerateList () (Foreign () api))
147147 => Proxy api -- ^ proxy for your API type
148148 -> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
149149 -> FilePath -- ^ path to the file you want to write the resulting javascript code into
servant-js/src/Servant/JS/Internal.hs+2−4
@@ -21,7 +21,6 @@ module Servant.JS.Internal
2121 , reqHeaders
2222 , HasForeign(..)
2323 , HasForeignType(..)
24− , HasNoForeignType
2524 , GenerateList(..)
2625 , NoTypes
2726 , HeaderArg
@@ -33,7 +32,6 @@ module Servant.JS.Internal
3332 , SegmentType(..)
3433 , Url(..)
3534 , Path
36− , ForeignType(..)
3735 , Arg(..)
3836 , FunctionName(..)
3937 , PathSegment(..)
@@ -57,12 +55,12 @@ import qualified Data.Text as T
5755import Data.Text (Text)
5856import Servant.Foreign
5957
60−type AjaxReq = Req Text
58+type AjaxReq = Req ()
6159
6260-- A 'JavascriptGenerator' just takes the data found in the API type
6361-- for each endpoint and generates Javascript code in a Text. Several
6462-- generators are available in this package.
65−type JavaScriptGenerator = [Req Text] -> Text
63+type JavaScriptGenerator = [Req ()] -> Text
6664
6765-- | This structure is used by specific implementations to let you
6866-- customize the output
servant-js/test/Servant/JSSpec.hs+2−2
@@ -106,7 +106,7 @@ a `shouldNotContain` b = shouldNotSatisfy a (T.isInfixOf b)
106106
107107axiosSpec :: Spec
108108axiosSpec = describe specLabel $ do
109− let reqList = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy TestAPI)
109+ let reqList = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) (Proxy :: Proxy TestAPI)
110110 it "should add withCredentials when needed" $ do
111111 let jsText = genJS withCredOpts $ reqList
112112 output jsText
@@ -130,7 +130,7 @@ axiosSpec = describe specLabel $ do
130130
131131angularSpec :: TestNames -> Spec
132132angularSpec test = describe specLabel $ do
133− let reqList = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy TestAPI)
133+ let reqList = listFromAPI (Proxy :: Proxy NoTypes) (Proxy :: Proxy ()) (Proxy :: Proxy TestAPI)
134134 it "should implement a service globally" $ do
135135 let jsText = genJS reqList
136136 output jsText
servant-js/test/Servant/JSSpec/CustomHeaders.hs+16−15
@@ -1,4 +1,5 @@
11{-# LANGUAGE DataKinds #-}
2+{-# LANGUAGE FlexibleContexts #-}
23{-# LANGUAGE FlexibleInstances #-}
34{-# LANGUAGE KindSignatures #-}
45{-# LANGUAGE PolyKinds #-}
@@ -22,13 +23,13 @@ import Servant.JS.Internal
2223-- using -- Basic, Digest, whatever.
2324data Authorization (sym :: Symbol) a
2425
25−instance (KnownSymbol sym, HasForeign lang sublayout)
26− => HasForeign lang (Authorization sym a :> sublayout) where
27− type Foreign (Authorization sym a :> sublayout) = Foreign sublayout
26+instance (KnownSymbol sym, HasForeign lang () sublayout)
27+ => HasForeign lang () (Authorization sym a :> sublayout) where
28+ type Foreign () (Authorization sym a :> sublayout) = Foreign () sublayout
2829
29− foreignFor lang Proxy req = foreignFor lang (Proxy :: Proxy sublayout) $
30+ foreignFor lang ftype Proxy req = foreignFor lang ftype (Proxy :: Proxy sublayout) $
3031 req & reqHeaders <>~
31− [ ReplaceHeaderArg (Arg "Authorization" "")
32+ [ ReplaceHeaderArg (Arg "Authorization" ())
3233 $ tokenType (pack . symbolVal $ (Proxy :: Proxy sym)) ]
3334 where
3435 tokenType t = t <> " {Authorization}"
@@ -36,23 +37,23 @@ instance (KnownSymbol sym, HasForeign lang sublayout)
3637-- | This is a combinator that fetches an X-MyLovelyHorse header.
3738data MyLovelyHorse a
3839
39−instance (HasForeign lang sublayout)
40− => HasForeign lang (MyLovelyHorse a :> sublayout) where
41− type Foreign (MyLovelyHorse a :> sublayout) = Foreign sublayout
40+instance (HasForeign lang () sublayout)
41+ => HasForeign lang () (MyLovelyHorse a :> sublayout) where
42+ type Foreign () (MyLovelyHorse a :> sublayout) = Foreign () sublayout
4243
43− foreignFor lang Proxy req = foreignFor lang (Proxy :: Proxy sublayout) $
44− req & reqHeaders <>~ [ ReplaceHeaderArg (Arg "X-MyLovelyHorse" "") tpl ]
44+ foreignFor lang ftype Proxy req = foreignFor lang ftype (Proxy :: Proxy sublayout) $
45+ req & reqHeaders <>~ [ ReplaceHeaderArg (Arg "X-MyLovelyHorse" ()) tpl ]
4546 where
4647 tpl = "I am good friends with {X-MyLovelyHorse}"
4748
4849-- | This is a combinator that fetches an X-WhatsForDinner header.
4950data WhatsForDinner a
5051
51−instance (HasForeign lang sublayout)
52− => HasForeign lang (WhatsForDinner a :> sublayout) where
53− type Foreign (WhatsForDinner a :> sublayout) = Foreign sublayout
52+instance (HasForeign lang () sublayout)
53+ => HasForeign lang () (WhatsForDinner a :> sublayout) where
54+ type Foreign () (WhatsForDinner a :> sublayout) = Foreign () sublayout
5455
55− foreignFor lang Proxy req = foreignFor lang (Proxy :: Proxy sublayout) $
56− req & reqHeaders <>~ [ ReplaceHeaderArg (Arg "X-WhatsForDinner" "") tpl ]
56+ foreignFor lang ftype Proxy req = foreignFor lang ftype (Proxy :: Proxy sublayout) $
57+ req & reqHeaders <>~ [ ReplaceHeaderArg (Arg "X-WhatsForDinner" ()) tpl ]
5758 where
5859 tpl = "I would like {X-WhatsForDinner} with a cherry on top."