{-#LANGUAGE OverloadedStrings #-} module Servant.JS.Angular where import Prelude () import Prelude.Compat import Control.Lens import Data.Maybe (isJust) import qualified Data.Text as T import Data.Text (Text) import Data.Text.Encoding (decodeUtf8) import Servant.Foreign import Servant.JS.Internal -- | Options specific to the angular code generator data AngularOptions = AngularOptions { serviceName :: Text -- ^ When generating code with wrapInService, -- name of the service to generate , prologue :: Text -> Text -> Text -- ^ beginning of the service definition , epilogue :: Text -- ^ end of the service definition } -- | Default options for the Angular codegen. Used by 'wrapInService'. defAngularOptions :: AngularOptions defAngularOptions = AngularOptions { serviceName = "" , prologue = \svc m -> m <> "service('" <> svc <> "', function($http) {\n" <> " return ({" , epilogue = "});\n});\n" } -- | Instead of simply generating top level functions, generates a service instance -- on which your controllers can depend to access your API. -- This variant uses default 'AngularOptions'. angularService :: AngularOptions -> JavaScriptGenerator angularService ngOpts = angularServiceWith ngOpts defCommonGeneratorOptions -- | Instead of simply generating top level functions, generates a service instance -- on which your controllers can depend to access your API angularServiceWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator angularServiceWith ngOpts opts reqs = prologue ngOpts svc mName <> T.intercalate "," (map generator reqs) <> epilogue ngOpts where generator req = generateAngularJSWith ngOpts opts req svc = serviceName ngOpts mName = if moduleName opts == "" then "app." else moduleName opts <> "." -- | Generate regular javacript functions that use -- the $http service, using default values for 'CommonGeneratorOptions'. angular :: AngularOptions -> JavaScriptGenerator angular ngopts = angularWith ngopts defCommonGeneratorOptions -- | Generate regular javascript functions that use the $http service. angularWith :: AngularOptions -> CommonGeneratorOptions -> JavaScriptGenerator angularWith ngopts opts = T.intercalate "\n\n" . map (generateAngularJSWith ngopts opts) -- | js codegen using $http service from Angular using default options generateAngularJS :: AngularOptions -> AjaxReq -> Text generateAngularJS ngOpts = generateAngularJSWith ngOpts defCommonGeneratorOptions -- | js codegen using $http service from Angular generateAngularJSWith :: AngularOptions -> CommonGeneratorOptions -> AjaxReq -> Text generateAngularJSWith ngOptions opts req = "\n" <> fname <> fsep <> " function(" <> argsStr <> ")\n" <> "{\n" <> " return $http(\n" <> " { url: " <> url <> "\n" <> dataBody <> reqheaders <> " , method: '" <> decodeUtf8 method <> "'\n" <> " });\n" <> "}\n" where argsStr = T.intercalate ", " args args = http ++ captures ++ map (view $ queryArgName . argPath) queryparams ++ body ++ map ( toValidFunctionName . (<>) "header" . view (headerArg . argPath) ) hs -- If we want to generate Top Level Function, they must depend on -- the $http service, if we generate a service, the functions will -- inherit this dependency from the service http = case T.length (serviceName ngOptions) of 0 -> ["$http"] _ -> [] captures = map (view argPath . captureArg) . filter isCapture $ req ^. reqUrl . path hs = req ^. reqHeaders queryparams = req ^.. reqUrl.queryStr.traverse body = if isJust (req ^. reqBody) then [requestBody opts] else [] dataBody = if isJust (req ^. reqBody) then " , data: JSON.stringify(body)\n" <> " , contentType: 'application/json'\n" else "" reqheaders = if null hs then "" else " , headers: { " <> headersStr <> " }\n" where headersStr = T.intercalate ", " $ map headerStr hs headerStr header = "\"" <> header ^. headerArg . argPath <> "\": " <> toJSHeader header namespace = if hasService then "" else if hasNoModule then "var " else (moduleName opts) <> "." where hasNoModule = moduleName opts == "" hasService = serviceName ngOptions /= "" fsep = if hasService then ":" else " =" fname = namespace <> (toValidFunctionName (functionNameBuilder opts $ req ^. reqFuncName)) method = req ^. reqMethod url = if url' == "'" then "'/'" else url' url' = "'" <> urlPrefix opts <> urlArgs <> queryArgs urlArgs = jsSegments $ req ^.. reqUrl.path.traverse queryArgs = if null queryparams then "" else " + '?" <> jsParams queryparams