2015-08-17 23:56:29 +02:00
|
|
|
{-# LANGUAGE DataKinds #-}
|
|
|
|
{-# LANGUAGE FlexibleContexts #-}
|
2014-11-25 01:36:34 +01:00
|
|
|
{-# LANGUAGE FlexibleInstances #-}
|
2015-08-17 23:56:29 +02:00
|
|
|
{-# LANGUAGE TypeOperators #-}
|
2014-11-25 01:36:34 +01:00
|
|
|
-----------------------------------------------------------------------------
|
|
|
|
-- |
|
2015-07-22 19:23:31 +02:00
|
|
|
-- Module : Servant.JS
|
2014-11-25 01:36:34 +01:00
|
|
|
-- License : BSD3
|
|
|
|
-- Maintainer : Alp Mestanogullari <alpmestan@gmail.com>
|
|
|
|
-- Stability : experimental
|
|
|
|
-- Portability : non-portable
|
2015-07-22 19:23:31 +02:00
|
|
|
--
|
|
|
|
-- Generating Javascript code to query your APIs using vanilla Javascript,
|
|
|
|
-- Angular.js or JQuery.
|
|
|
|
--
|
|
|
|
-- Using this package is very simple. Say you have this API type around:
|
|
|
|
--
|
|
|
|
-- > type API = "users" :> Get '[JSON] [Users]
|
|
|
|
-- > :<|> "messages" :> Get '[JSON] [Message]
|
|
|
|
--
|
|
|
|
-- All you need to do to generate the Javascript code is to write a 'Proxy'
|
|
|
|
-- for this API type:
|
|
|
|
--
|
|
|
|
-- > api :: Proxy API
|
|
|
|
-- > api = Proxy
|
|
|
|
--
|
|
|
|
-- And pick one of the generators:
|
|
|
|
--
|
|
|
|
-- - 'vanillaJS' and 'vanillaJSWith' generate functions that use
|
|
|
|
-- /XMLHttpRequest/ to query your endpoints. The former just calls
|
|
|
|
-- the latter with default code-generation options.
|
|
|
|
-- - 'jquery' and 'jqueryWith' follow the same pattern except that they
|
|
|
|
-- generate functions that use /jQuery/'s AJAX functions.
|
|
|
|
-- - 'angular' and 'angularWith' do the same but use /Angular.js/'s $http
|
|
|
|
-- service. In addition, we provide 'angularService' and 'angularServiceWith'
|
|
|
|
-- which produce functions under an Angular service that your controlers
|
|
|
|
-- can depend on to query the API.
|
|
|
|
--
|
|
|
|
-- Let's keep it simple and produce vanilla Javascript code with the default options.
|
|
|
|
--
|
|
|
|
-- @
|
2015-10-02 13:59:54 +02:00
|
|
|
-- jsCode :: Text
|
2015-07-22 19:23:31 +02:00
|
|
|
-- jsCode = 'jsForAPI' api 'vanillaJS'
|
|
|
|
-- @
|
|
|
|
--
|
|
|
|
-- That's it! If you want to write that code to a file:
|
|
|
|
--
|
|
|
|
-- @
|
|
|
|
-- writeJSCode :: IO ()
|
|
|
|
-- writeJSCode = 'writeJSForAPI' api 'vanillaJS' "./my_api.js"
|
|
|
|
-- @
|
|
|
|
--
|
|
|
|
-- If you want to customize the rendering options, take a look
|
|
|
|
-- at 'CommonGeneratorOptions' which are generic options common to all the
|
|
|
|
-- generators. the /xxxWith/ variants all take 'CommonGeneratorOptions' whereas
|
|
|
|
-- the /xxx/ versions use 'defCommonGeneratorOptions'. Once you have some custom
|
|
|
|
--
|
|
|
|
-- > myOptions :: 'CommonGeneratorOptions'
|
|
|
|
--
|
|
|
|
-- All you need to do to use it is to use 'vanillaJSWith' and pass it @myOptions@.
|
|
|
|
--
|
|
|
|
-- @
|
2015-10-02 13:59:54 +02:00
|
|
|
-- jsCodeWithMyOptions :: Text
|
2015-07-22 19:23:31 +02:00
|
|
|
-- jsCodeWithMyOptions = 'jsForAPI' api ('vanillaJSWith' myOptions)
|
|
|
|
-- @
|
|
|
|
--
|
|
|
|
-- Follow the same pattern for any other generator.
|
|
|
|
--
|
|
|
|
-- /Note/: The Angular generators take an additional type of options,
|
|
|
|
-- namely 'AngularOptions', to let you tweak aspects of the code generation
|
|
|
|
-- that are specific to /Angular.js/.
|
2015-07-22 12:55:44 +02:00
|
|
|
module Servant.JS
|
2015-07-22 19:23:31 +02:00
|
|
|
( -- * Generating javascript code from an API type
|
|
|
|
jsForAPI
|
|
|
|
, writeJSForAPI
|
|
|
|
, JavaScriptGenerator
|
|
|
|
|
|
|
|
, -- * Options common to all generators
|
2015-11-04 11:11:36 +01:00
|
|
|
CommonGeneratorOptions(..)
|
2015-07-22 19:23:31 +02:00
|
|
|
, defCommonGeneratorOptions
|
|
|
|
|
2015-07-27 13:55:30 +02:00
|
|
|
, -- * Function renamers
|
2015-07-28 16:41:07 +02:00
|
|
|
concatCase
|
|
|
|
, snakeCase
|
|
|
|
, camelCase
|
2015-07-27 13:55:30 +02:00
|
|
|
|
2015-07-22 19:23:31 +02:00
|
|
|
, -- * Vanilla Javascript code generation
|
|
|
|
vanillaJS
|
|
|
|
, vanillaJSWith
|
|
|
|
|
|
|
|
, -- * JQuery code generation
|
|
|
|
jquery
|
|
|
|
, jqueryWith
|
|
|
|
|
|
|
|
, -- * Angular.js code generation
|
|
|
|
angular
|
|
|
|
, angularWith
|
|
|
|
, angularService
|
|
|
|
, angularServiceWith
|
|
|
|
, AngularOptions(..)
|
|
|
|
, defAngularOptions
|
|
|
|
|
2015-07-27 16:29:08 +02:00
|
|
|
, -- * Axios code generation
|
|
|
|
axios
|
|
|
|
, axiosWith
|
2015-07-28 15:06:00 +02:00
|
|
|
, AxiosOptions(..)
|
|
|
|
, defAxiosOptions
|
2015-07-27 16:29:08 +02:00
|
|
|
|
2015-07-22 19:23:31 +02:00
|
|
|
, -- * Misc.
|
|
|
|
listFromAPI
|
|
|
|
, javascript
|
|
|
|
, GenerateList(..)
|
2014-11-25 19:42:52 +01:00
|
|
|
) where
|
2014-11-25 01:36:34 +01:00
|
|
|
|
2015-10-02 13:59:54 +02:00
|
|
|
import Prelude hiding (writeFile)
|
2015-08-17 23:56:29 +02:00
|
|
|
import Data.Proxy
|
2015-10-02 13:59:54 +02:00
|
|
|
import Data.Text
|
|
|
|
import Data.Text.IO (writeFile)
|
2015-08-17 23:56:29 +02:00
|
|
|
import Servant.JS.Angular
|
|
|
|
import Servant.JS.Axios
|
|
|
|
import Servant.JS.Internal
|
|
|
|
import Servant.JS.JQuery
|
|
|
|
import Servant.JS.Vanilla
|
2014-11-25 01:36:34 +01:00
|
|
|
|
2015-07-22 19:23:31 +02:00
|
|
|
-- | Generate the data necessary to generate javascript code
|
|
|
|
-- for all the endpoints of an API, as ':<|>'-separated values
|
|
|
|
-- of type 'AjaxReq'.
|
2015-09-21 12:31:00 +02:00
|
|
|
javascript :: HasForeign layout => Proxy layout -> Foreign layout
|
|
|
|
javascript p = foreignFor p defReq
|
2014-11-25 01:36:34 +01:00
|
|
|
|
2015-04-19 10:07:58 +02:00
|
|
|
-- | Directly generate all the javascript functions for your API
|
|
|
|
-- from a 'Proxy' for your API type. You can then write it to
|
|
|
|
-- a file or integrate it in a page, for example.
|
2015-09-21 12:31:00 +02:00
|
|
|
jsForAPI :: (HasForeign api, GenerateList (Foreign api))
|
2015-07-22 19:23:31 +02:00
|
|
|
=> Proxy api -- ^ proxy for your API type
|
|
|
|
-> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
|
2015-10-02 13:59:54 +02:00
|
|
|
-> Text -- ^ a text that you can embed in your pages or write to a file
|
2015-07-22 19:23:31 +02:00
|
|
|
jsForAPI p gen = gen (listFromAPI p)
|
|
|
|
|
|
|
|
-- | Directly generate all the javascript functions for your API
|
|
|
|
-- from a 'Proxy' for your API type using the given generator
|
|
|
|
-- and write the resulting code to a file at the given path.
|
2015-09-21 12:31:00 +02:00
|
|
|
writeJSForAPI :: (HasForeign api, GenerateList (Foreign api))
|
2015-07-22 19:23:31 +02:00
|
|
|
=> Proxy api -- ^ proxy for your API type
|
|
|
|
-> JavaScriptGenerator -- ^ js code generator to use (angular, vanilla js, jquery, others)
|
|
|
|
-> FilePath -- ^ path to the file you want to write the resulting javascript code into
|
|
|
|
-> IO ()
|
|
|
|
writeJSForAPI p gen fp = writeFile fp (jsForAPI p gen)
|
2015-07-17 23:36:38 +02:00
|
|
|
|
2015-11-28 09:24:55 +01:00
|
|
|
-- A catch all instance since JavaScript has no types.
|
|
|
|
instance HasForeignType a where
|
|
|
|
typeFor _ = empty
|
|
|
|
|
2015-07-22 19:23:31 +02:00
|
|
|
-- | Utility class used by 'jsForAPI' which computes
|
|
|
|
-- the data needed to generate a function for each endpoint
|
|
|
|
-- and hands it all back in a list.
|
2015-07-17 23:36:38 +02:00
|
|
|
class GenerateList reqs where
|
|
|
|
generateList :: reqs -> [AjaxReq]
|
|
|
|
|
|
|
|
instance GenerateList AjaxReq where
|
|
|
|
generateList r = [r]
|
|
|
|
|
2015-08-05 21:01:33 +02:00
|
|
|
instance (GenerateList start, GenerateList rest) => GenerateList (start :<|> rest) where
|
|
|
|
generateList (start :<|> rest) = (generateList start) ++ (generateList rest)
|
2015-07-17 23:36:38 +02:00
|
|
|
|
2015-07-22 19:23:31 +02:00
|
|
|
-- | Generate the necessary data for JS codegen as a list, each 'AjaxReq'
|
2015-08-17 23:56:29 +02:00
|
|
|
-- describing one endpoint from your API type.
|
2015-09-21 12:31:00 +02:00
|
|
|
listFromAPI :: (HasForeign api, GenerateList (Foreign api)) => Proxy api -> [AjaxReq]
|
2015-07-17 23:36:38 +02:00
|
|
|
listFromAPI p = generateList (javascript p)
|
2015-09-21 12:31:00 +02:00
|
|
|
|