servant-typescript-0.1.0.0: src/Servant/TypeScript/GetFunctions.hs
module Servant.TypeScript.GetFunctions (
getFunctions
) where
import Control.Lens
import Data.Maybe
import Data.String.Interpolate
import Data.Text (Text)
import qualified Data.Text as T
import Servant.Foreign.Internal as FI
-- | Default implementation of @getFunctions@.
getFunctions :: (Req Text -> Text) -> [Req Text] -> Text
getFunctions getFunctionName reqs =
[i|import queryString from "query-string";\n\n|]
<> T.intercalate "\n" (fmap (reqToFunction getFunctionName) reqs)
reqToFunction :: (Req Text -> Text) -> Req Text -> Text
reqToFunction getFunctionName req = [i|
export function #{getFunctionName req}#{getGenericBrackets req}(#{getFunctionArgs req}): Promise<#{getReturnType req}> {
let options: RequestInit = {
credentials: "same-origin" as RequestCredentials,
method: "#{req ^. reqMethod}",
headers: {"Content-Type": "application/json;charset=utf-8"}
};
#{case (req ^. reqBody) of Nothing -> ("" :: Text); Just _ -> "\n options.body = JSON.stringify(body);\n" }
let params = {#{T.intercalate ", " (getQueryParamNames req)}};
return (fetchFn || window.fetch)(`#{getPath req}` + "?" + queryString.stringify(params), options).then((response) => {
return new Promise((resolve, reject) => {
if (response.status !== 200) {
return response.text().then((text) => reject({text, status: response.status}));
} else {
#{if hasReturn req
then ("return response.json().then((json) => resolve(json));" :: Text)
else "resolve();"}
}
});
});
}|]
hasReturn :: Req Text -> Bool
hasReturn req = case req ^. reqReturnType of
Nothing -> False
Just "void" -> False
Just _ -> True
getQueryParamNames :: Req Text -> [Text]
getQueryParamNames req = [x ^. (queryArgName . argName . _PathSegment)
| x <- req ^. (reqUrl . queryStr)]
getFunctionArgs :: Req Text -> Text
getFunctionArgs req = T.intercalate ", " $ catMaybes $
maybeBodyArg
: fmap formatCaptureArg (req ^. (reqUrl . path))
<> fmap (Just . formatQueryArg) (req ^. (reqUrl . queryStr))
<> [Just [i|fetchFn?: (input: RequestInfo, init?: RequestInit) => Promise<Response>|]]
where
maybeBodyArg = case req ^. reqBody of
Nothing -> Nothing
Just x -> Just [i|body: #{x}|]
formatCaptureArg :: Segment Text -> Maybe Text
formatCaptureArg (Segment (Static {})) = Nothing
formatCaptureArg (Segment (Cap arg)) = Just [i|#{arg ^. (argName . _PathSegment)}: #{arg ^. argType}|]
formatQueryArg :: QueryArg Text -> Text
formatQueryArg arg = case arg ^. queryArgType of
Normal -> [i|#{name}?: #{typ}|]
Flag -> [i|#{name}?: boolean|]
FI.List -> [i|#{name}?: [#{typ}]|]
where
qaName = arg ^. queryArgName
name = qaName ^. (argName . _PathSegment)
typ = qaName ^. argType
getReturnType :: Req Text -> Text
getReturnType req = fromMaybe "void" (req ^. reqReturnType)
getGenericBrackets :: Req Text -> Text
getGenericBrackets _req = ""
getPath :: Req Text -> Text
getPath req = "/" <> T.intercalate "/" (fmap formatPathSegment (req ^. (reqUrl . path)))
where
formatPathSegment :: Segment Text -> Text
formatPathSegment (Segment (Static (PathSegment t))) = t
formatPathSegment (Segment (Cap ((^. argName) -> (PathSegment t)))) = [i|${#{t}}|]