packages feed

servant-elm 0.5.0.0 → 0.6.0.0

raw patch · 28 files changed

+965/−650 lines, 28 filesdep +elm-bridgedep +servant-clientdep +typed-processdep −elm-exportnew-uploaderPVP ok

version bump matches the API change (PVP)

Dependencies added: elm-bridge, servant-client, typed-process

Dependencies removed: elm-export

API changes (from Hackage documentation)

- Servant.Elm: Spec :: [Text] -> [Text] -> Spec
- Servant.Elm: [elmExportOptions] :: ElmOptions -> Options
- Servant.Elm: class ElmType a
- Servant.Elm: data Spec :: *
- Servant.Elm: specsToDir :: [Spec] -> FilePath -> IO ()
- Servant.Elm.Internal.Foreign: instance Elm.Type.ElmType a => Servant.Foreign.Internal.HasForeignType Servant.Elm.Internal.Foreign.LangElm Elm.Type.ElmDatatype a
- Servant.Elm.Internal.Generate: [elmExportOptions] :: ElmOptions -> Options
- Servant.Elm.Internal.Orphans: instance Elm.Type.ElmType Elm.Type.ElmDatatype
- Servant.Elm.Internal.Orphans: instance Elm.Type.ElmType Servant.API.ContentTypes.NoContent
- Servant.Elm.Internal.Orphans: instance Elm.Type.ElmType a => Elm.Type.ElmType (Servant.API.ResponseHeaders.Headers ls a)
+ Servant.Elm: ETyApp :: EType -> EType -> EType
+ Servant.Elm: ETyCon :: ETCon -> EType
+ Servant.Elm: ETyTuple :: Int -> EType
+ Servant.Elm: ETyVar :: ETVar -> EType
+ Servant.Elm: [DefineElm] :: forall a. IsElmDefinition a => Proxy a -> DefineElm
+ Servant.Elm: [elmAlterations] :: ElmOptions -> ETypeDef -> ETypeDef
+ Servant.Elm: [elmTypeAlterations] :: ElmOptions -> EType -> EType
+ Servant.Elm: data DefineElm
+ Servant.Elm: data EType
+ Servant.Elm: defaultOptions :: Options
+ Servant.Elm: defaultTypeAlterations :: EType -> EType
+ Servant.Elm: deriveBoth :: Options -> Name -> Q [Dec]
+ Servant.Elm: deriveElmDef :: Options -> Name -> Q [Dec]
+ Servant.Elm: generateElmModule :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => Namespace -> Text -> FilePath -> [DefineElm] -> Proxy api -> IO ()
+ Servant.Elm: generateElmModuleWith :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => ElmOptions -> Namespace -> Text -> FilePath -> [DefineElm] -> Proxy api -> IO ()
+ Servant.Elm: toElmType :: Typeable a => Proxy a -> EType
+ Servant.Elm.Internal.Foreign: instance Data.Typeable.Internal.Typeable a => Servant.Foreign.Internal.HasForeignType Servant.Elm.Internal.Foreign.LangElm Elm.TyRep.EType (Servant.API.ResponseHeaders.Headers b a)
+ Servant.Elm.Internal.Foreign: instance Data.Typeable.Internal.Typeable a => Servant.Foreign.Internal.HasForeignType Servant.Elm.Internal.Foreign.LangElm Elm.TyRep.EType a
+ Servant.Elm.Internal.Generate: [elmAlterations] :: ElmOptions -> ETypeDef -> ETypeDef
+ Servant.Elm.Internal.Generate: [elmTypeAlterations] :: ElmOptions -> EType -> EType
+ Servant.Elm.Internal.Generate: generateElmModule :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => Namespace -> Text -> FilePath -> [DefineElm] -> Proxy api -> IO ()
+ Servant.Elm.Internal.Generate: generateElmModuleWith :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => ElmOptions -> Namespace -> Text -> FilePath -> [DefineElm] -> Proxy api -> IO ()
+ Servant.Elm.Internal.Generate: isElmListOfMaybeBoolType :: EType -> Bool
+ Servant.Elm.Internal.Generate: renderDecoderName :: EType -> Doc
+ Servant.Elm.Internal.Generate: toElmDecoderRefWith :: ElmOptions -> EType -> Text
+ Servant.Elm.Internal.Generate: toElmEncoderRefWith :: ElmOptions -> EType -> Text
+ Servant.Elm.Internal.Generate: toElmTypeRefWith :: ElmOptions -> EType -> Text
+ Servant.Elm.Internal.Generate: type Namespace = [String]
+ Servant.Elm.Internal.Options: defaultOptions :: Options
- Servant.Elm: ElmOptions :: UrlPrefix -> Options -> [ElmDatatype] -> [ElmDatatype] -> ElmOptions
+ Servant.Elm: ElmOptions :: UrlPrefix -> (EType -> EType) -> (ETypeDef -> ETypeDef) -> [EType] -> [EType] -> ElmOptions
- Servant.Elm: Proxy :: Proxy k
+ Servant.Elm: Proxy :: Proxy
- Servant.Elm: [emptyResponseElmTypes] :: ElmOptions -> [ElmDatatype]
+ Servant.Elm: [emptyResponseElmTypes] :: ElmOptions -> [EType]
- Servant.Elm: [stringElmTypes] :: ElmOptions -> [ElmDatatype]
+ Servant.Elm: [stringElmTypes] :: ElmOptions -> [EType]
- Servant.Elm: data Proxy k (t :: k) :: forall k. () => k -> *
+ Servant.Elm: data Proxy (t :: k) :: forall k. () => k -> Type
- Servant.Elm: generateElmForAPI :: (HasForeign LangElm ElmDatatype api, GenerateList ElmDatatype (Foreign ElmDatatype api)) => Proxy api -> [Text]
+ Servant.Elm: generateElmForAPI :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => Proxy api -> [Text]
- Servant.Elm: generateElmForAPIWith :: (HasForeign LangElm ElmDatatype api, GenerateList ElmDatatype (Foreign ElmDatatype api)) => ElmOptions -> Proxy api -> [Text]
+ Servant.Elm: generateElmForAPIWith :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => ElmOptions -> Proxy api -> [Text]
- Servant.Elm.Internal.Foreign: getEndpoints :: (HasForeign LangElm ElmDatatype api, GenerateList ElmDatatype (Foreign ElmDatatype api)) => Proxy api -> [Req ElmDatatype]
+ Servant.Elm.Internal.Foreign: getEndpoints :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => Proxy api -> [Req EType]
- Servant.Elm.Internal.Generate: ElmOptions :: UrlPrefix -> Options -> [ElmDatatype] -> [ElmDatatype] -> ElmOptions
+ Servant.Elm.Internal.Generate: ElmOptions :: UrlPrefix -> (EType -> EType) -> (ETypeDef -> ETypeDef) -> [EType] -> [EType] -> ElmOptions
- Servant.Elm.Internal.Generate: [emptyResponseElmTypes] :: ElmOptions -> [ElmDatatype]
+ Servant.Elm.Internal.Generate: [emptyResponseElmTypes] :: ElmOptions -> [EType]
- Servant.Elm.Internal.Generate: [stringElmTypes] :: ElmOptions -> [ElmDatatype]
+ Servant.Elm.Internal.Generate: [stringElmTypes] :: ElmOptions -> [EType]
- Servant.Elm.Internal.Generate: elmCaptureArg :: Segment ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: elmCaptureArg :: Segment EType -> Doc
- Servant.Elm.Internal.Generate: elmHeaderArg :: HeaderArg ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: elmHeaderArg :: HeaderArg EType -> Doc
- Servant.Elm.Internal.Generate: elmQueryArg :: QueryArg ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: elmQueryArg :: QueryArg EType -> Doc
- Servant.Elm.Internal.Generate: generateElmForAPI :: (HasForeign LangElm ElmDatatype api, GenerateList ElmDatatype (Foreign ElmDatatype api)) => Proxy api -> [Text]
+ Servant.Elm.Internal.Generate: generateElmForAPI :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => Proxy api -> [Text]
- Servant.Elm.Internal.Generate: generateElmForAPIWith :: (HasForeign LangElm ElmDatatype api, GenerateList ElmDatatype (Foreign ElmDatatype api)) => ElmOptions -> Proxy api -> [Text]
+ Servant.Elm.Internal.Generate: generateElmForAPIWith :: (HasForeign LangElm EType api, GenerateList EType (Foreign EType api)) => ElmOptions -> Proxy api -> [Text]
- Servant.Elm.Internal.Generate: generateElmForRequest :: ElmOptions -> Req ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: generateElmForRequest :: ElmOptions -> Req EType -> Doc
- Servant.Elm.Internal.Generate: isElmMaybeStringType :: ElmOptions -> ElmDatatype -> Bool
+ Servant.Elm.Internal.Generate: isElmMaybeStringType :: ElmOptions -> EType -> Bool
- Servant.Elm.Internal.Generate: isElmMaybeType :: ElmDatatype -> Bool
+ Servant.Elm.Internal.Generate: isElmMaybeType :: EType -> Bool
- Servant.Elm.Internal.Generate: isElmStringType :: ElmOptions -> ElmDatatype -> Bool
+ Servant.Elm.Internal.Generate: isElmStringType :: ElmOptions -> EType -> Bool
- Servant.Elm.Internal.Generate: isEmptyType :: ElmOptions -> ElmDatatype -> Bool
+ Servant.Elm.Internal.Generate: isEmptyType :: ElmOptions -> EType -> Bool
- Servant.Elm.Internal.Generate: mkArgs :: ElmOptions -> Req ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: mkArgs :: ElmOptions -> Req EType -> Doc
- Servant.Elm.Internal.Generate: mkLetParams :: ElmOptions -> Req ElmDatatype -> Maybe Doc
+ Servant.Elm.Internal.Generate: mkLetParams :: ElmOptions -> Req EType -> Maybe Doc
- Servant.Elm.Internal.Generate: mkQueryParams :: Req ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: mkQueryParams :: Req EType -> Doc
- Servant.Elm.Internal.Generate: mkRequest :: ElmOptions -> Req ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: mkRequest :: ElmOptions -> Req EType -> Doc
- Servant.Elm.Internal.Generate: mkTypeSignature :: ElmOptions -> Req ElmDatatype -> Doc
+ Servant.Elm.Internal.Generate: mkTypeSignature :: ElmOptions -> Req EType -> Doc
- Servant.Elm.Internal.Generate: mkUrl :: ElmOptions -> [Segment ElmDatatype] -> Doc
+ Servant.Elm.Internal.Generate: mkUrl :: ElmOptions -> [Segment EType] -> Doc

Files

CHANGELOG.md view
@@ -1,3 +1,8 @@+0.6.0.0+-------++* PR #49. Migrate to elm-bridge and http 2.0.0+ 0.5.0.0 ------- * Fix generation for APIs with response headers.
README.md view
@@ -16,16 +16,15 @@  ```haskell {-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveGeneric     #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-} {-# LANGUAGE TypeOperators     #-} -import           Elm          (Spec (Spec), specsToDir, toElmDecoderSource,-                               toElmTypeSource)-import           GHC.Generics (Generic)+import           Elm.Derive   (defaultOptions, deriveBoth)+ import           Servant.API  ((:>), Capture, Get, JSON)-import           Servant.Elm  (ElmType, Proxy (Proxy), defElmImports,-                               generateElmForAPI)+import           Servant.Elm  (DefineElm (DefineElm), Proxy (Proxy), defElmImports, defElmOptions,+                               generateElmModuleWith) ```  We have some Haskell-defined types and our Servant API.@@ -33,9 +32,9 @@ ```haskell data Book = Book     { name :: String-    } deriving (Generic)+    } -instance ElmType Book+deriveBoth defaultOptions ''Book  type BooksApi = "books" :> Capture "bookId" Int :> Get '[JSON] Book ```@@ -43,15 +42,18 @@ Now we can generate Elm functions to query the API:  ```haskell-spec :: Spec-spec = Spec ["Generated", "MyApi"]-            (defElmImports-             : toElmTypeSource    (Proxy :: Proxy Book)-             : toElmDecoderSource (Proxy :: Proxy Book)-             : generateElmForAPI  (Proxy :: Proxy BooksApi))- main :: IO ()-main = specsToDir [spec] "my-elm-dir"+main =+  generateElmModuleWith+    defElmOptions+    [ "Generated"+    , "MyApi"+    ]+    defElmImports+    "my-elm-dir"+    [ DefineElm (Proxy :: Proxy Book)+    ]+    (Proxy :: Proxy BooksApi) ```  Let's save this as `example.hs` and run it:@@ -65,46 +67,69 @@ Here's what was generated:  ```elm-module Generated.MyApi exposing (..)+module Generated.MyApi exposing(..) -import Json.Decode exposing (..)-import Json.Decode.Pipeline exposing (..)-import Json.Encode+import Json.Decode+import Json.Encode exposing (Value)+-- The following module comes from bartavelle/json-helpers+import Json.Helpers exposing (..)+import Dict exposing (Dict)+import Set import Http import String+import Url.Builder +maybeBoolToIntStr : Maybe Bool -> String+maybeBoolToIntStr mx =+  case mx of+    Nothing -> ""+    Just True -> "1"+    Just False -> "0" -type alias Book =-    { name : String-    }+type alias Book  =+   { name: String+   } -decodeBook : Decoder Book-decodeBook =-    decode Book-        |> required "name" string+jsonDecBook : Json.Decode.Decoder ( Book )+jsonDecBook =+   Json.Decode.succeed (\pname -> {name = pname})+   |> required "name" (Json.Decode.string) -getBooksByBookId : Int -> Http.Request (Book)+jsonEncBook : Book -> Value+jsonEncBook  val =+   Json.Encode.object+   [ ("name", Json.Encode.string val.name)+   ]+++getBooksByBookId : Int -> Http.Request Book getBooksByBookId capture_bookId =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "books"-                , capture_bookId |> toString |> Http.encodeUri-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson decodeBook-        , timeout =-            Nothing-        , withCredentials =-            False-        }+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.absolute+                    [ "books"+                    , capture_bookId |> String.fromInt+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson <| jsonDecBook+            , timeout =+                Nothing+            , withCredentials =+                False+            } ```  See [`examples`](examples) for a complete usage example, or take a look at
examples/books/generate.hs view
@@ -1,21 +1,19 @@ {-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveGeneric     #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-} {-# LANGUAGE TypeOperators     #-} -import           Elm          (Spec (Spec), specsToDir, toElmDecoderSource,-                               toElmEncoderSource, toElmTypeSource)-import           GHC.Generics (Generic) import           Servant.API  ((:<|>), (:>), Capture, Get, JSON, Post, ReqBody)-import           Servant.Elm  (ElmOptions (..), ElmType, Proxy (Proxy),-                               UrlPrefix (Static), defElmImports, defElmOptions,-                               generateElmForAPIWith)+import           Servant.Elm  (DefineElm (DefineElm), ElmOptions(urlPrefix),+                               Proxy (Proxy), UrlPrefix(Static), defaultOptions,+                               defElmImports, defElmOptions, deriveBoth,+                               generateElmModuleWith)  data Book = Book   { name :: String-  } deriving (Show, Eq, Generic)+  } deriving (Show, Eq) -instance ElmType Book+deriveBoth defaultOptions ''Book  type BooksApi = "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book            :<|> "books" :> Get '[JSON] [Book]@@ -24,13 +22,15 @@ myElmOpts :: ElmOptions myElmOpts = defElmOptions { urlPrefix = Static "http://localhost:8000" } -spec :: Spec-spec = Spec ["Generated", "BooksApi"]-            (defElmImports-             : toElmTypeSource    (Proxy :: Proxy Book)-             : toElmDecoderSource (Proxy :: Proxy Book)-             : toElmEncoderSource (Proxy :: Proxy Book)-             : generateElmForAPIWith myElmOpts (Proxy :: Proxy BooksApi))- main :: IO ()-main = specsToDir [spec] "elm"+main =+  generateElmModuleWith+    myElmOpts+    [ "Generated"+    , "BooksApi"+    ]+    defElmImports+    "elm"+    [ DefineElm (Proxy :: Proxy Book)+    ]+    (Proxy :: Proxy BooksApi)
examples/e2e-tests/generate.hs view
@@ -1,18 +1,13 @@ {-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveAnyClass    #-}-{-# LANGUAGE DeriveGeneric     #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-} {-# LANGUAGE TypeOperators     #-} -import           Elm          (Spec (Spec), specsToDir, toElmDecoderSource,-                               toElmEncoderSource, toElmTypeSource)-import           GHC.Generics (Generic) import           Servant.API  ((:<|>), (:>), Capture, Get, GetNoContent, JSON,-                               NoContent, Post, QueryParam, ReqBody)-import           Servant.Elm  (ElmOptions (..), ElmType, Proxy (Proxy),-                               UrlPrefix (Static), defElmImports, defElmOptions,-                               generateElmForAPIWith)-+                               Post, QueryParam, ReqBody)+import           Servant.Elm  (DefineElm (DefineElm), ElmOptions(..), Proxy (Proxy),+                               UrlPrefix (Static), defaultOptions, defElmImports,+                               defElmOptions, deriveBoth, generateElmModuleWith)  myElmOpts :: ElmOptions myElmOpts = defElmOptions { urlPrefix = Static "https://httpbin.org" }@@ -20,31 +15,30 @@  data MessageBody = MessageBody   { message :: String }-  deriving (Generic, ElmType)  data QueryArgs = QueryArgs   { q :: String }-  deriving (Generic, ElmType)  data Response  = Response   { origin :: String }-  deriving (Generic, ElmType)  data ResponseWithJson  = ResponseWithJson   { json :: MessageBody }-  deriving (Generic, ElmType)  data ResponseWithArgs  = ResponseWithArgs   { args :: QueryArgs }-  deriving (Generic, ElmType) +concat <$> mapM+  (deriveBoth defaultOptions)+  [''MessageBody, ''QueryArgs, ''Response, ''ResponseWithJson, ''ResponseWithArgs] + type Api      = "ip"     :> Get '[JSON] Response   :<|> "status"     :> "204"-    :> GetNoContent '[JSON] NoContent+    :> GetNoContent '[JSON] ()   :<|> "post"     :> ReqBody '[JSON] MessageBody     :> Post '[JSON] ResponseWithJson@@ -55,26 +49,19 @@     :> Get '[JSON] Response  -spec :: Spec-spec =-  Spec ["Generated", "Api"]-    (defElmImports-     : toElmTypeSource    (Proxy :: Proxy Response)-     : toElmDecoderSource (Proxy :: Proxy Response)-     : toElmTypeSource    (Proxy :: Proxy NoContent)-     : toElmTypeSource    (Proxy :: Proxy MessageBody)-     : toElmEncoderSource (Proxy :: Proxy MessageBody)-     : toElmDecoderSource (Proxy :: Proxy MessageBody)-     : toElmTypeSource    (Proxy :: Proxy ResponseWithJson)-     : toElmDecoderSource (Proxy :: Proxy ResponseWithJson)-     : toElmTypeSource    (Proxy :: Proxy QueryArgs)-     : toElmDecoderSource (Proxy :: Proxy QueryArgs)-     : toElmTypeSource    (Proxy :: Proxy ResponseWithArgs)-     : toElmDecoderSource (Proxy :: Proxy ResponseWithArgs)-     : generateElmForAPIWith-         myElmOpts-         (Proxy :: Proxy Api))-- main :: IO ()-main = specsToDir [spec] "elm"+main =+  generateElmModuleWith+    myElmOpts+    [ "Generated"+    , "Api"+    ]+    defElmImports+    "elm"+    [ DefineElm (Proxy :: Proxy MessageBody)+    , DefineElm (Proxy :: Proxy QueryArgs)+    , DefineElm (Proxy :: Proxy Response)+    , DefineElm (Proxy :: Proxy ResponseWithJson)+    , DefineElm (Proxy :: Proxy ResponseWithArgs)+    ]+    (Proxy :: Proxy Api)
examples/giphy/generate.hs view
@@ -1,62 +1,54 @@ {-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveGeneric     #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-} {-# LANGUAGE TypeOperators     #-} -import qualified Data.Text    as T-import           Elm          (Options, Spec (Spec), defaultOptions,-                               fieldLabelModifier, specsToDir,-                               toElmDecoderSourceWith, toElmTypeSourceWith)-import           GHC.Generics (Generic)+import qualified Elm.Derive as Elm+ import           Servant.API  ((:>), Get, JSON, QueryParam)-import           Servant.Elm  (ElmOptions (..), ElmType, Proxy (Proxy), UrlPrefix(Static),-                               defElmImports, defElmOptions,-                               generateElmForAPIWith)+import           Servant.Elm  (DefineElm (DefineElm), ElmOptions (urlPrefix),+                               Proxy (Proxy), UrlPrefix (Static), defElmImports,+                               defElmOptions, defaultOptions, deriveBoth,+                               generateElmModuleWith)  data GifData = GifData   { image_url :: String-  } deriving (Show, Eq, Generic)+  } deriving (Show, Eq)  data Gif = Gif   { _data :: GifData-  } deriving (Show, Eq, Generic)--instance ElmType GifData-instance ElmType Gif--stripUnderscore :: T.Text -> T.Text-stripUnderscore field =-  if T.head field == '_' then-    T.tail field-  else-    field+  } deriving (Show, Eq) -options :: Elm.Options-options =-  Elm.defaultOptions-    { Elm.fieldLabelModifier = stripUnderscore }+concat <$> mapM+  (deriveBoth defaultOptions+    { Elm.fieldLabelModifier = \ field ->+        if head field == '_' then+          tail field+        else+          field+    , Elm.unwrapUnaryRecords = False+    }+  ) [''GifData, ''Gif]  myElmOpts :: ElmOptions myElmOpts =   defElmOptions     { urlPrefix =         Static "http://api.giphy.com/v1/gifs"-    , elmExportOptions =-        options     }  type GiphyApi = "random" :> QueryParam "api_key" String :> QueryParam "tag" String :> Get '[JSON] Gif -giphySpec :: Spec-giphySpec = Spec ["Generated", "GiphyApi"]-                 (defElmImports-                  : toElmTypeSourceWith    options (Proxy :: Proxy Gif)-                  : toElmTypeSourceWith    options (Proxy :: Proxy GifData)-                  : toElmDecoderSourceWith options (Proxy :: Proxy Gif)-                  : toElmDecoderSourceWith options (Proxy :: Proxy GifData)-                  : generateElmForAPIWith-                      myElmOpts-                      (Proxy :: Proxy GiphyApi))- main :: IO ()-main = specsToDir [giphySpec] "elm"+main =+  generateElmModuleWith+    myElmOpts+    [ "Generated"+    , "GiphyApi"+    ]+    defElmImports+    "elm"+    [ DefineElm (Proxy :: Proxy Gif)+    , DefineElm (Proxy :: Proxy GifData)+    ]+    (Proxy :: Proxy GiphyApi)
examples/readme-example/generate.hs view
@@ -1,29 +1,30 @@ {-# LANGUAGE DataKinds         #-}-{-# LANGUAGE DeriveGeneric     #-} {-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE TemplateHaskell   #-} {-# LANGUAGE TypeOperators     #-} -import           Elm          (Spec (Spec), specsToDir, toElmDecoderSource,-                               toElmTypeSource)-import           GHC.Generics (Generic) import           Servant.API  ((:>), Capture, Get, JSON)-import           Servant.Elm  (ElmType, Proxy (Proxy), defElmImports,-                               generateElmForAPI)+import           Servant.Elm  (DefineElm (DefineElm), Proxy (Proxy),+                               defaultOptions, defElmImports, defElmOptions,+                               deriveBoth, generateElmModuleWith)  data Book = Book-    { name :: String-    } deriving ((Generic))+  { name :: String+  } -instance ElmType Book+deriveBoth defaultOptions ''Book  type BooksApi = "books" :> Capture "bookId" Int :> Get '[JSON] Book -spec :: Spec-spec = Spec ["Generated", "MyApi"]-            (defElmImports-             : toElmTypeSource    (Proxy :: Proxy Book)-             : toElmDecoderSource (Proxy :: Proxy Book)-             : generateElmForAPI  (Proxy :: Proxy BooksApi))- main :: IO ()-main = specsToDir [spec] "my-elm-dir"+main =+  generateElmModuleWith+    defElmOptions+    [ "Generated"+    , "MyApi"+    ]+    defElmImports+    "my-elm-dir"+    [ DefineElm (Proxy :: Proxy Book)+    ]+    (Proxy :: Proxy BooksApi)
servant-elm.cabal view
@@ -1,5 +1,5 @@ name:                servant-elm-version:             0.5.0.0+version:             0.6.0.0 synopsis:            Automatically derive Elm functions to query servant webservices. description:         Please see README.md homepage:            http://github.com/mattjbray/servant-elm#readme@@ -13,7 +13,7 @@ extra-source-files:   CHANGELOG.md   README.md-  test/elm-sources/elm-package.json+  test/elm-sources/elm.json   test/elm-sources/*.elm cabal-version:       >=1.10 @@ -30,9 +30,11 @@   exposed-modules:     Servant.Elm                      , Servant.Elm.Internal.Foreign                      , Servant.Elm.Internal.Generate-                     , Servant.Elm.Internal.Orphans+                     , Servant.Elm.Internal.Options   build-depends:       base >= 4.7 && < 5-                     , elm-export      >= 0.5+                     , aeson >= 0.9+                     , directory+                     , elm-bridge      >= 0.4                      , lens                      , servant         >= 0.8                      , servant-foreign >= 0.8@@ -51,9 +53,10 @@                      , HUnit                      , aeson >= 0.9                      , base-                     , elm-export      >= 0.5+                     , elm-bridge >= 0.4                      , hspec                      , servant+                     , servant-client                      , servant-elm                      , text   ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall@@ -69,7 +72,7 @@   build-depends:       aeson >= 0.9                       , base                       , directory-                      , elm-export      >= 0.5+                      , elm-bridge >= 0.4                       , hspec                       , interpolate                       , mockery@@ -77,6 +80,7 @@                       , servant                       , servant-elm                       , text+                      , typed-process   ghc-options:         -threaded -rtsopts -with-rtsopts=-N -Wall   default-language:    Haskell2010 @@ -89,7 +93,7 @@     Buildable:         False   main-is:             generate.hs   build-depends:       base >= 4.7 && < 5-                     , elm-export      >= 0.5+                     , elm-bridge >= 0.4                      , servant >= 0.8                      , servant-elm   hs-source-dirs:      examples/books@@ -101,7 +105,7 @@     Buildable:         False   main-is:             generate.hs   build-depends:       base >= 4.7 && < 5-                     , elm-export      >= 0.5+                     , elm-bridge >= 0.4                      , servant >= 0.8                      , servant-elm   hs-source-dirs:      examples/e2e-tests@@ -113,7 +117,7 @@     Buildable:         False   main-is:             generate.hs   build-depends:       base >= 4.7 && < 5-                     , elm-export >= 0.5+                     , elm-bridge >= 0.4                      , servant >= 0.8                      , servant-elm                      , text@@ -126,7 +130,7 @@     Buildable:         False   main-is:             generate.hs   build-depends:       base >= 4.7 && < 5-                     , elm-export >= 0.5+                     , elm-bridge >= 0.4                      , servant >= 0.8                      , servant-elm   hs-source-dirs:      examples/readme-example
src/Servant/Elm.hs view
@@ -14,14 +14,20 @@ module Servant.Elm        ( generateElmForAPI        , generateElmForAPIWith+       , generateElmModule+       , generateElmModuleWith        , ElmOptions(..)        , UrlPrefix(..)        , defElmOptions        , defElmImports+       , defaultOptions        -- * Convenience re-exports from the "Elm" module-       , Spec(Spec)-       , ElmType-       , specsToDir+       , DefineElm (..)+       , EType (..)+       , defaultTypeAlterations+       , toElmType+       , deriveBoth+       , deriveElmDef        -- * Convenience re-exports from "Data.Proxy"        , Proxy(Proxy)        ) where@@ -29,8 +35,11 @@ import           Servant.Elm.Internal.Generate (ElmOptions (..), UrlPrefix (..),                                                 defElmImports, defElmOptions,                                                 generateElmForAPI,-                                                generateElmForAPIWith)-+                                                generateElmForAPIWith,+                                                generateElmModule,+                                                generateElmModuleWith)+import           Servant.Elm.Internal.Options  (defaultOptions) import           Data.Proxy                    (Proxy (Proxy))-import           Elm                           (ElmType, Spec (Spec),-                                                specsToDir)+import           Elm.TyRep                     (EType (..), toElmType)+import           Elm.Module                    (DefineElm (..), defaultTypeAlterations)+import           Elm.Derive                    (deriveBoth, deriveElmDef)
src/Servant/Elm/Internal/Foreign.hs view
@@ -7,21 +7,27 @@ module Servant.Elm.Internal.Foreign where  import           Data.Proxy      (Proxy (Proxy))-import           Elm             (ElmDatatype, ElmType, toElmType)+import           Data.Typeable   (Typeable)+import           Elm.TyRep       (EType, toElmType)+import           Servant.API     (Headers(..)) import           Servant.Foreign (Foreign, GenerateList, HasForeign,                                   HasForeignType, Req, listFromAPI, typeFor)   data LangElm -instance (ElmType a) => HasForeignType LangElm ElmDatatype a where-  typeFor _ _ _ =-    toElmType (Proxy :: Proxy a)+--- TODO: Generate Elm functions that can handle the response headers. PRs+--- welcome!+instance {-# OVERLAPPING #-} (Typeable a) => HasForeignType LangElm EType (Headers b a) where+  typeFor _ _ _ = toElmType (Proxy :: Proxy a) +instance {-# OVERLAPPABLE #-} (Typeable a) => HasForeignType LangElm EType a where+  typeFor _ _ _ = toElmType (Proxy :: Proxy a)+ getEndpoints-  :: ( HasForeign LangElm ElmDatatype api-     , GenerateList ElmDatatype (Foreign ElmDatatype api))+  :: ( HasForeign LangElm EType api+     , GenerateList EType (Foreign EType api))   => Proxy api-  -> [Req ElmDatatype]+  -> [Req EType] getEndpoints =-  listFromAPI (Proxy :: Proxy LangElm) (Proxy :: Proxy ElmDatatype)+  listFromAPI (Proxy :: Proxy LangElm) (Proxy :: Proxy EType)
src/Servant/Elm/Internal/Generate.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE RecordWildCards       #-} {-# LANGUAGE FlexibleContexts      #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings     #-}@@ -5,22 +6,36 @@  import           Prelude                      hiding ((<$>)) import           Control.Lens                 (to, (^.))-import           Data.List                    (nub)+import           Data.List                    (intercalate, intersperse, nub) import           Data.Maybe                   (catMaybes)-import           Data.Proxy                   (Proxy)+import           Data.Proxy                   (Proxy(..)) import           Data.Text                    (Text) import qualified Data.Text                    as T import qualified Data.Text.Lazy               as L import qualified Data.Text.Encoding           as T-import           Elm                          (ElmDatatype(..), ElmPrimitive(..))-import qualified Elm-import           Servant.API                  (NoContent (..))+import           Data.Text.IO                 as TIO++import           Elm.Json (jsonParserForType, jsonSerForType)+import qualified Elm.Module                   as Elm+import           Elm.TyRep (ETCon(..), EType(..), ETypeDef(..), toElmType)+import           Elm.TyRender (renderElm)+import           Elm.Versions (ElmVersion(Elm0p18))+ import           Servant.Elm.Internal.Foreign (LangElm, getEndpoints)-import           Servant.Elm.Internal.Orphans () import qualified Servant.Foreign              as F+import           System.Directory (createDirectoryIfMissing) import           Text.PrettyPrint.Leijen.Text  +toElmTypeRefWith :: ElmOptions -> EType -> Text+toElmTypeRefWith ElmOptions{..} = T.pack . renderElm . elmTypeAlterations++toElmDecoderRefWith :: ElmOptions -> EType -> Text+toElmDecoderRefWith ElmOptions{..} = T.pack . jsonParserForType . elmTypeAlterations++toElmEncoderRefWith :: ElmOptions -> EType -> Text+toElmEncoderRefWith ElmOptions{..} = T.pack . jsonSerForType . elmTypeAlterations+ {-| Options to configure how code is generated. -}@@ -34,11 +49,13 @@     argument.     -}     urlPrefix             :: UrlPrefix-  , elmExportOptions      :: Elm.Options-    -- ^ Options to pass to elm-export-  , emptyResponseElmTypes :: [ElmDatatype]+  , elmTypeAlterations        :: (EType -> EType)+    -- ^ Alterations to perform on ETypes before code generation.+  , elmAlterations        :: (ETypeDef -> ETypeDef)+    -- ^ Alterations to perform on ETypeDefs before code generation.+  , emptyResponseElmTypes :: [EType]     -- ^ Types that represent an empty Http response.-  , stringElmTypes        :: [ElmDatatype]+  , stringElmTypes        :: [EType]     -- ^ Types that represent a String.   } @@ -47,6 +64,7 @@   = Static T.Text   | Dynamic +type Namespace = [String]  {-| Default options for generating Elm code.@@ -55,25 +73,26 @@  > { urlPrefix = >     Static ""-> , elmExportOptions =->     Elm.defaultOptions+> , elmAlterations =+>     Elm.defaultTypeAlterations > , emptyResponseElmTypes =->     [ toElmType NoContent ]+>     [ getType (Proxy :: Proxy ()) ] > , stringElmTypes =->     [ toElmType "" ]+>     [ getType (Proxy :: Proxy String)+>     , getType (Proxy :: Proxy T.Text) ] > } -} defElmOptions :: ElmOptions defElmOptions = ElmOptions   { urlPrefix = Static ""-  , elmExportOptions = Elm.defaultOptions+  , elmTypeAlterations = Elm.defaultTypeAlterations+  , elmAlterations = Elm.defaultAlterations   , emptyResponseElmTypes =-      [ Elm.toElmType NoContent-      , Elm.toElmType ()+      [ toElmType (Proxy :: Proxy ())       ]   , stringElmTypes =-      [ Elm.toElmType ("" :: String)-      , Elm.toElmType ("" :: T.Text)+      [ toElmType (Proxy :: Proxy String)+      , toElmType (Proxy :: Proxy T.Text)       ]   } @@ -85,24 +104,84 @@  The default required imports are: -> import Json.Decode exposing (..)-> import Json.Decode.Pipeline exposing (..)-> import Json.Encode+> import Json.Decode+> import Json.Encode exposing (Value)+> -- The following module comes from bartavelle/json-helpers+> import Json.Helpers exposing (..)+> import Dict exposing (Dict)+> import Set > import Http > import String+> import Url.Builder -} defElmImports :: Text defElmImports =   T.unlines-    [ "import Json.Decode exposing (..)"-    , "import Json.Decode.Pipeline exposing (..)"-    , "import Json.Encode"+    [ "import Json.Decode"+    , "import Json.Encode exposing (Value)"+    , "-- The following module comes from bartavelle/json-helpers"+    , "import Json.Helpers exposing (..)"+    , "import Dict exposing (Dict)"+    , "import Set"     , "import Http"     , "import String"+    , "import Url.Builder"+    , ""+    , "maybeBoolToIntStr : Maybe Bool -> String"+    , "maybeBoolToIntStr mx ="+    , "  case mx of"+    , "    Nothing -> \"\""+    , "    Just True -> \"1\""+    , "    Just False -> \"0\""     ] +{-|+Helper to generate a complete Elm module given a list of Elm type definitions+and an API.+-}+generateElmModuleWith ::+     ( F.HasForeign LangElm EType api+     , F.GenerateList EType (F.Foreign EType api)+     )+  => ElmOptions+  -> Namespace+  -> Text+  -> FilePath+  -> [Elm.DefineElm]+  -> Proxy api+  -> IO ()+generateElmModuleWith options namespace imports rootDir typeDefs api = do+  let out =+        T.unlines $+        [ T.pack $ Elm.moduleHeader Elm0p18 moduleName+        , ""+        , imports+        , T.pack $ Elm.makeModuleContentWithAlterations (elmAlterations options) typeDefs+        ] +++        generateElmForAPIWith options api+      moduleName = intercalate "." namespace+      filePath = intercalate "/" $ rootDir:init namespace+      fileName = intercalate "/" $ filePath:[last namespace ++ ".elm"]+  createDirectoryIfMissing True filePath+  TIO.writeFile fileName out  {-|+Calls generateElmModuleWith with @defElmOptions@.+-}+generateElmModule ::+     ( F.HasForeign LangElm EType api+     , F.GenerateList EType (F.Foreign EType api)+     )+  => Namespace+  -> Text+  -> FilePath+  -> [Elm.DefineElm]+  -> Proxy api+  -> IO ()+generateElmModule namespace imports filePath typeDefs api =+  generateElmModuleWith defElmOptions namespace imports filePath typeDefs api++{-| Generate Elm code for the API with default options.  Returns a list of Elm functions to query your Servant API from Elm.@@ -112,8 +191,8 @@ which handles the module name for you. -} generateElmForAPI-  :: ( F.HasForeign LangElm ElmDatatype api-     , F.GenerateList ElmDatatype (F.Foreign ElmDatatype api))+  :: ( F.HasForeign LangElm EType api+     , F.GenerateList EType (F.Foreign EType api))   => Proxy api   -> [Text] generateElmForAPI =@@ -124,12 +203,12 @@ Generate Elm code for the API with custom options. -} generateElmForAPIWith-  :: ( F.HasForeign LangElm ElmDatatype api-     , F.GenerateList ElmDatatype (F.Foreign ElmDatatype api))+  :: ( F.HasForeign LangElm EType api+     , F.GenerateList EType (F.Foreign EType api))   => ElmOptions   -> Proxy api   -> [Text]-generateElmForAPIWith opts =+generateElmForAPIWith opts = intersperse "" .   nub . map docToText . map (generateElmForRequest opts) . getEndpoints  i :: Int@@ -138,7 +217,7 @@ {-| Generate an Elm function for one endpoint. -}-generateElmForRequest :: ElmOptions -> F.Req ElmDatatype -> Doc+generateElmForRequest :: ElmOptions -> F.Req EType -> Doc generateElmForRequest opts request =   funcDef   where@@ -159,8 +238,10 @@         ]      fnName =-      request ^. F.reqFuncName . to (T.replace "-" "" . F.camelCase) . to stext+      request ^. F.reqFuncName . to (replace . F.camelCase) . to stext +    replace = T.replace "-" "" . T.replace "." ""+     typeSignature =       mkTypeSignature opts request @@ -174,14 +255,14 @@       mkRequest opts request  -mkTypeSignature :: ElmOptions -> F.Req ElmDatatype -> Doc+mkTypeSignature :: ElmOptions -> F.Req EType -> Doc mkTypeSignature opts request =   (hsep . punctuate " ->" . concat)     [ catMaybes [urlPrefixType]     , headerTypes     , urlCaptureTypes     , queryTypes-    , catMaybes [bodyType, returnType]+    , catMaybes [bodyType, toMsgType, returnType]     ]   where     urlPrefixType :: Maybe Doc@@ -190,9 +271,9 @@           Dynamic -> Just "String"           Static _ -> Nothing -    elmTypeRef :: ElmDatatype -> Doc+    elmTypeRef :: EType -> Doc     elmTypeRef eType =-      stext (Elm.toElmTypeRefWith (elmExportOptions opts) eType)+      stext (toElmTypeRefWith opts eType)      headerTypes :: [Doc]     headerTypes =@@ -218,25 +299,31 @@     bodyType =         fmap elmTypeRef $ request ^. F.reqBody +    toMsgType :: Maybe Doc+    toMsgType = do+      result <- fmap elmTypeRef $ request ^. F.reqReturnType+      Just ("(Result Http.Error " <+> parens result <+> " -> msg)")+     returnType :: Maybe Doc     returnType = do-      result <- fmap elmTypeRef $ request ^. F.reqReturnType-      pure ("Http.Request" <+> parens result)+      pure ("Cmd msg")  -elmHeaderArg :: F.HeaderArg ElmDatatype -> Doc+elmHeaderArg :: F.HeaderArg EType -> Doc elmHeaderArg header =   "header_" <>   header ^. F.headerArg . F.argName . to (stext . T.replace "-" "_" . F.unPathSegment)  -elmCaptureArg :: F.Segment ElmDatatype -> Doc+elmCaptureArg :: F.Segment EType -> Doc elmCaptureArg segment =   "capture_" <>-  F.captureArg segment ^. F.argName . to (stext . F.unPathSegment)+  F.captureArg segment ^. F.argName . to (stext . replace . F.unPathSegment)+  where+    replace = T.replace "-" "_"  -elmQueryArg :: F.QueryArg ElmDatatype -> Doc+elmQueryArg :: F.QueryArg EType -> Doc elmQueryArg arg =   "query_" <>   arg ^. F.queryArgName . F.argName . to (stext . F.unPathSegment)@@ -257,7 +344,7 @@  mkArgs   :: ElmOptions-  -> F.Req ElmDatatype+  -> F.Req EType   -> Doc mkArgs opts request =   (hsep . concat) $@@ -281,22 +368,21 @@       ]     , -- Request body       maybe [] (const [elmBodyArg]) (request ^. F.reqBody)+    , pure "toMsg"     ]  -mkLetParams :: ElmOptions -> F.Req ElmDatatype -> Maybe Doc+mkLetParams :: ElmOptions -> F.Req EType -> Maybe Doc mkLetParams opts request =-  if null (request ^. F.reqUrl . F.queryStr) then-    Nothing-  else     Just $ "params =" <$>-           indent i ("List.filter (not << String.isEmpty)" <$>-                      indent i (elmList params))+           indent i ("List.filterMap identity" <$>+                      parens ("List.concat" <$>+                              indent i (elmList params)))   where     params :: [Doc]     params = map paramToDoc (request ^. F.reqUrl . F.queryStr) -    paramToDoc :: F.QueryArg ElmDatatype -> Doc+    paramToDoc :: F.QueryArg EType -> Doc     paramToDoc qarg =       -- something wrong with indentation here...       case qarg ^. F.queryArgType of@@ -309,28 +395,44 @@               if isElmStringType opts argType || isElmMaybeStringType opts argType then                 ""               else-                "toString >> "+                "String.fromInt >> "           in-              (if wrapped then name else "Just" <+> name) <$>-              indent 4 ("|> Maybe.map" <+> parens (toStringSrc <> "Http.encodeUri >> (++)" <+> dquotes (elmName <> equals)) <$>-                        "|> Maybe.withDefault" <+> dquotes empty)+              "[" <+> (if wrapped then elmName else "Just" <+> elmName) <> line <>+                (indent 4 ("|> Maybe.map" <+> parens (toStringSrc <> "Url.Builder.string" <+> dquotes elmName)))+                <+> "]"+              -- (if wrapped then name else "Just" <+> name) <$>+              -- indent 4 ("|> Maybe.map" <+> parens (toStringSrc <> "Http.encodeUri >> (++)" <+> dquotes (elmName <> equals)) <$>+              --           "|> Maybe.withDefault" <+> dquotes empty)          F.Flag ->-            "if" <+> name <+> "then" <$>-            indent 4 (dquotes (name <> equals)) <$>+            "[" <+>+            ("if" <+> elmName <+> "then" <$>+            indent 4 ("Just" <+> parens ("Url.Builder.string" <+> dquotes name <+> dquotes empty)) <$>             indent 2 "else" <$>-            indent 4 (dquotes empty)+            indent 4 "Nothing")+            <+> "]"          F.List ->-            name <$>-            indent 4 ("|> List.map" <+> parens (backslash <> "val ->" <+> dquotes (name <> "[]=") <+> "++ (val |> toString |> Http.encodeUri)") <$>-                      "|> String.join" <+> dquotes "&")+            let+              argType = qarg ^. F.queryArgName . F.argType+              convertedVal =+                if isElmListOfMaybeBoolType argType then+                  parens ("maybeBoolToIntStr" <+> "val")+                else+                  "val"+            in+            elmName <$>+            indent 4 ("|> List.map"+                      <+> parens (backslash <> "val ->" <+> "Just"+                                  <+> parens ("Url.Builder.string"+                                              <+> dquotes (name <> "[]")+                                              <+> convertedVal)))       where-        name = elmQueryArg qarg-        elmName= qarg ^. F.queryArgName . F.argName . to (stext . F.unPathSegment)+        elmName = elmQueryArg qarg+        name = qarg ^. F.queryArgName . F.argName . to (stext . F.unPathSegment)  -mkRequest :: ElmOptions -> F.Req ElmDatatype -> Doc+mkRequest :: ElmOptions -> F.Req EType -> Doc mkRequest opts request =   "Http.request" <$>   indent i@@ -348,8 +450,8 @@          indent i expect        , "timeout =" <$>          indent i "Nothing"-       , "withCredentials =" <$>-         indent i "False"+       , "tracker =" <$>+         indent i "Nothing"        ])   where     method =@@ -364,7 +466,7 @@             if isElmMaybeStringType opts argType || isElmStringType opts argType then               mempty             else-              " << toString"+              " << String.fromInt"       in         "Maybe.map" <+> parens (("Http.header" <+> dquotes headerName <> toStringSrc))         <+>@@ -388,42 +490,69 @@         Just elmTypeExpr ->           let             encoderName =-              Elm.toElmEncoderRefWith (elmExportOptions opts) elmTypeExpr+              toElmEncoderRefWith opts elmTypeExpr           in             "Http.jsonBody" <+> parens (stext encoderName <+> elmBodyArg)      expect =       case request ^. F.reqReturnType of-        Just elmTypeExpr | isEmptyType opts elmTypeExpr ->-          let elmConstructor =-                Elm.toElmTypeRefWith (elmExportOptions opts) elmTypeExpr-          in-            "Http.expectStringResponse" <$>-            indent i (parens (backslash <> braces " body " <+> "->" <$>-                              indent i ("if String.isEmpty body then" <$>-                                        indent i "Ok" <+> stext elmConstructor <$>-                                        "else" <$>-                                        indent i ("Err" <+> dquotes "Expected the response body to be empty")) <> line))+        Just elmTypeExpr+          | isEmptyType opts elmTypeExpr+            -- let elmConstructor = T.pack (renderElm elmTypeExpr)+           ->+            "Http.expectString " <> line <+> indent i "(\\x -> case x of" <> line <+>+            indent i "Err e -> toMsg (Err e)" <> line <+>+            indent i "Ok _ -> toMsg (Ok ()))"+        Just elmTypeExpr ->+          "Http.expectJson toMsg" <+> renderDecoderName elmTypeExpr+        Nothing -> error "mkHttpRequest: no reqReturnType?"+      -- case request ^. F.reqReturnType of+      --   Just elmTypeExpr | isEmptyType opts elmTypeExpr ->+      --     let elmConstructor =+      --           toElmTypeRefWith opts elmTypeExpr+      --     in+      --       "Http.expectStringResponse" <$>+      --       indent i (parens (backslash <> " rsp " <+> "->" <$>+      --                         indent i ("if String.isEmpty rsp.body then" <$>+      --                                   indent i "Ok" <+> stext elmConstructor <$>+      --                                   "else" <$>+      --                                   indent i ("Err" <+> dquotes "Expected the response body to be empty")) <> line))  -        Just elmTypeExpr ->-          "Http.expectJson" <+> stext (Elm.toElmDecoderRefWith (elmExportOptions opts) elmTypeExpr)+      --   Just elmTypeExpr ->+      --     "Http.expectJson <|" <+> stext (toElmDecoderRefWith opts elmTypeExpr) -        Nothing ->-          error "mkHttpRequest: no reqReturnType?"+      --   Nothing ->+      --     error "mkHttpRequest: no reqReturnType?" +renderDecoderName :: EType -> Doc+renderDecoderName elmTypeExpr =+  case elmTypeExpr of+    ETyApp (ETyCon (ETCon "List")) t ->+      parens ("Json.Decode.list " <> parens (renderDecoderName t))+    ETyApp (ETyCon (ETCon "Maybe")) t ->+      parens ("Json.Decode.maybe " <> parens (renderDecoderName t))+    ETyCon (ETCon "Int") -> "Json.Decode.int"+    ETyCon (ETCon "String") -> "Json.Decode.string"+    _ -> ("jsonDec" <> stext (T.pack (renderElm elmTypeExpr))) -mkUrl :: ElmOptions -> [F.Segment ElmDatatype] -> Doc++mkUrl :: ElmOptions -> [F.Segment EType] -> Doc mkUrl opts segments =-  "String.join" <+> dquotes "/" <$>-  (indent i . elmList)-    ( case urlPrefix opts of-        Dynamic -> "urlBase"-        Static url -> dquotes (stext url)-      : map segmentToDoc segments)+  urlBuilder <$>+    (indent i . elmList)+    ( map segmentToDoc segments)+  -- ( case urlPrefix opts of+  --     Dynamic -> "urlBase"+  --     Static url -> dquotes (stext url)+  --   : map segmentToDoc segments)   where+    urlBuilder :: Doc+    urlBuilder = case urlPrefix opts of+      Dynamic -> "Url.Builder.absolute" :: Doc+      Static url -> "Url.Builder.crossOrigin" <+> dquotes (stext url) -    segmentToDoc :: F.Segment ElmDatatype -> Doc+    segmentToDoc :: F.Segment EType -> Doc     segmentToDoc s =       case F.unSegment s of         F.Static path ->@@ -435,28 +564,25 @@               if isElmStringType opts (arg ^. F.argType) then                 empty               else-                " |> toString"+                " |> String.fromInt"           in-            (elmCaptureArg s) <> toStringSrc <> " |> Http.encodeUri"+            (elmCaptureArg s) <> toStringSrc   mkQueryParams-  :: F.Req ElmDatatype+  :: F.Req EType   -> Doc-mkQueryParams request =-  if null (request ^. F.reqUrl . F.queryStr) then-    empty-  else-    line <> "++" <+> align ("if List.isEmpty params then" <$>-                            indent i (dquotes empty) <$>-                            "else" <$>-                            indent i (dquotes "?" <+> "++ String.join" <+> dquotes "&" <+> "params"))+mkQueryParams _request =+  -- if null (request ^. F.reqUrl . F.queryStr) then+  --   empty+  -- else+    line <> indent 4 (align "params")   {- | Determines whether we construct an Elm function that expects an empty response body. -}-isEmptyType :: ElmOptions -> ElmDatatype -> Bool+isEmptyType :: ElmOptions -> EType -> Bool isEmptyType opts elmTypeExpr =   elmTypeExpr `elem` emptyResponseElmTypes opts @@ -464,20 +590,25 @@ {- | Determines whether we call `toString` on URL captures and query params of this type in Elm. -}-isElmStringType :: ElmOptions -> ElmDatatype -> Bool+isElmStringType :: ElmOptions -> EType -> Bool isElmStringType opts elmTypeExpr =   elmTypeExpr `elem` stringElmTypes opts  {- | Determines whether a type is 'Maybe a' where 'a' is something akin to a 'String'. -}-isElmMaybeStringType :: ElmOptions -> ElmDatatype -> Bool-isElmMaybeStringType opts (ElmPrimitive (EMaybe elmTypeExpr)) = elmTypeExpr `elem` stringElmTypes opts+isElmMaybeStringType :: ElmOptions -> EType -> Bool+isElmMaybeStringType opts (ETyApp (ETyCon (ETCon "Maybe")) elmTypeExpr) = elmTypeExpr `elem` stringElmTypes opts isElmMaybeStringType _ _ = False -isElmMaybeType :: ElmDatatype -> Bool-isElmMaybeType (ElmPrimitive (EMaybe _)) = True+isElmMaybeType :: EType -> Bool+isElmMaybeType (ETyApp (ETyCon (ETCon "Maybe")) _) = True isElmMaybeType _ = False +isElmListOfMaybeBoolType :: EType -> Bool+isElmListOfMaybeBoolType t =+  case t of+    (ETyApp (ETyCon (ETCon "List")) (ETyApp (ETyCon (ETCon "Maybe")) (ETyCon (ETCon "Bool")))) -> True+    _ -> False  -- Doc helpers 
+ src/Servant/Elm/Internal/Options.hs view
@@ -0,0 +1,8 @@+module Servant.Elm.Internal.Options where++import qualified Data.Aeson as A+import qualified Elm.Derive as E++defaultOptions :: A.Options+defaultOptions = E.defaultOptions { A.unwrapUnaryRecords = False}+
− src/Servant/Elm/Internal/Orphans.hs
@@ -1,21 +0,0 @@-{-# LANGUAGE DeriveGeneric      #-}-{-# LANGUAGE StandaloneDeriving #-}-{-# OPTIONS_GHC -fno-warn-orphans #-}--module Servant.Elm.Internal.Orphans where--import           Elm         (ElmDatatype, ElmType, toElmType)-import           Servant.API (NoContent, Headers, getResponse)---instance ElmType ElmDatatype where-  toElmType = id---instance ElmType NoContent----- TODO: Generate Elm functions that can handle the response headers. PRs--- welcome!-instance (ElmType a) => ElmType (Headers ls a) where-  toElmType = toElmType . getResponse
test/Common.hs view
@@ -1,24 +1,21 @@ {-# LANGUAGE DataKinds     #-}-{-# LANGUAGE DeriveGeneric #-}+{-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE TypeOperators #-} module Common where -import           Data.Aeson   (ToJSON) import           Data.Proxy   (Proxy (Proxy)) import           Data.Text    (Text)-import           Elm          (ElmType)-import           GHC.Generics (Generic) import           Servant.API  ((:<|>), (:>), Capture, Get, GetNoContent, Header,-                               Header', Headers, JSON, NoContent, Post,+                               Header', Headers, JSON, Post,                                PostNoContent, Put, QueryFlag, QueryParam,                                QueryParam', QueryParams, ReqBody, Required)+import           Servant.Elm  (deriveBoth, defaultOptions)  data Book = Book     { title :: String-    } deriving (Generic)+    } -instance ToJSON Book-instance ElmType Book+deriveBoth defaultOptions ''Book  type TestApi =        "one"@@ -41,9 +38,9 @@          :> Get '[JSON] [Book]   :<|> "books"          :> ReqBody '[JSON] Book-         :> PostNoContent '[JSON] NoContent+         :> PostNoContent '[JSON] ()   :<|> "nothing"-         :> GetNoContent '[JSON] NoContent+         :> GetNoContent '[JSON] ()   :<|> "nothing"          :> Put '[JSON] () -- old way to specify no content   :<|> "with-a-header"
test/CompileSpec.hs view
@@ -8,23 +8,23 @@  import           Control.Exception import           Control.Monad                (when)+import           Data.List                    (intercalate) import           Data.String.Interpolate import           Data.String.Interpolate.Util-import qualified Data.Text                    as T-import qualified Data.Text.IO                 as T-import           Elm                          (toElmDecoderSource,-                                               toElmEncoderSource,-                                               toElmTypeSource)-import           Servant.API                  (NoContent)+import           Elm.Versions (ElmVersion(..)) import           Servant.Elm import           System.Directory             (canonicalizePath,                                                createDirectoryIfMissing,                                                doesDirectoryExist,                                                getCurrentDirectory, removeFile,                                                setCurrentDirectory)-import           System.Process+import           System.Process (callCommand)+import           System.Process.Typed+import qualified Data.Text as T+import qualified Elm.Module                   as Elm+import           Data.Text.IO                 as TIO -import Common (Book, testApi)+import Common (Book, TestApi)  main :: IO () main =@@ -35,23 +35,50 @@   describe "generateElmForAPI" $ do     it "creates compilable javascript" $ do       inTempElmDir $ do-        let generated =-              T.intercalate "\n\n" $-                defElmImports :-                [ toElmTypeSource (Proxy :: Proxy NoContent)-                , toElmTypeSource (Proxy :: Proxy Book)-                , toElmDecoderSource (Proxy :: Proxy Book)-                , toElmEncoderSource (Proxy :: Proxy Book)-                ] ++-                generateElmForAPI testApi-        T.writeFile "Api.elm" generated-        callCommand "elm-make Api.elm --output api.js"+        let options = defElmOptions+            namespace :: [String]+            namespace = ["Generated", "BooksApi"]+            imports = defElmImports+            rootDir = "."+            typeDefs = [ DefineElm (Proxy :: Proxy Book)+                       ]+            api = (Proxy :: Proxy TestApi)+        let out =+              T.unlines $+              [ T.pack $ Elm.moduleHeader Elm0p18 moduleName+              , ""+              , imports+              , ""+              , "type NoContent = NoContent"+              , ""+              , T.pack $ Elm.makeModuleContentWithAlterations (elmAlterations options) typeDefs+              ] +++              generateElmForAPIWith options api+            moduleName = T.unpack (T.intercalate "." (map T.pack namespace))+            filePath = intercalate "/" $ rootDir:init namespace+            fileName = intercalate "/" $ filePath : [last namespace ++ ".elm"]+        createDirectoryIfMissing True filePath+        TIO.writeFile fileName out +        -- generateElmModuleWith+        --   defElmOptions+        --   ["Generated", "BooksApi"]+        --   defElmImports+        --   "."+        --   [ DefineElm (Proxy :: Proxy Book)+        --   , DefineElm (Proxy :: Proxy NoContent)+        --   ]+        --   (Proxy :: Proxy TestApi)+        -- -- Useful for locally checking out sources in your tmp dir+        callCommand "cp -r . /home/kb/tmp/servelmtest"+        runProcess_ "elm make Generated/BooksApi.elm --output api.js"++ inTempElmDir :: IO a -> IO a inTempElmDir action = do+  callCommand "rm -rf ./_test-cache"   cacheExists <- doesDirectoryExist "_test-cache"-  when (not cacheExists)-    createCache+  when (not cacheExists) createCache   cacheDir <- canonicalizePath "_test-cache"   inTempDirectory $ do     callCommand ("cp -r " ++ cacheDir ++ "/* .")@@ -61,32 +88,44 @@ createCache = do   createDirectoryIfMissing True "_test-cache"   withCurrentDirectory "_test-cache" $ do-    writeFile "elm-package.json" $ unindent $ [i|+    TIO.writeFile "elm.json" $ T.pack $ unindent $ [i|       {-          "version": "1.0.0",-          "summary": "helpful summary of your project, less than 80 characters",-          "repository": "https://github.com/user/project.git",-          "license": "BSD3",+          "type": "application",           "source-directories": [               "."           ],-          "exposed-modules": [],+          "elm-version": "0.19.0",+          "version": "1.0.0",+          "summary": "helpful summary of your project, less than 80 characters",           "dependencies": {-              "elm-lang/core": "5.0.0 <= v < 6.0.0",-              "elm-community/json-extra": "2.0.0 <= v < 3.0.0",-              "elm-lang/http": "1.0.0 <= v < 2.0.0",-              "NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0"+              "direct": {+                "elm/core": "1.0.2",+                "elm/json": "1.1.3",+                "elm/http": "2.0.0",+                "elm/url": "1.0.0",+                "bartavelle/json-helpers": "2.0.2"+              },+              "indirect": {+                 "elm/bytes": "1.0.8",+                 "elm/file": "1.0.5",+                 "elm/time": "1.0.0"+              }           },-          "elm-version": "0.18.0 <= v < 0.19.0"+          "test-dependencies": {+            "direct": {},+            "indirect": {}+          }       }     |]-    callCommand "elm-package install --yes"-    compileDependencies+    -- callCommand "elm install"+    -- compileDependencies  compileDependencies :: IO () compileDependencies = do-  writeFile "Main.elm" "foo = 42"-  callCommand "elm-make Main.elm --output main.js"+  TIO.writeFile "Main.elm" "module Main exposing (foo)\n\nfoo : Int\nfoo = 42\n"+  callCommand "echo '>>>>>> Main.elm'"+  callCommand "cat Main.elm"+  callCommand "elm make Main.elm --output main.js"   removeFile "Main.elm"   removeFile "main.js" 
test/GenerateSpec.hs view
@@ -15,7 +15,7 @@ import           Servant.API               ((:>), Get, JSON) import           Servant.Elm import           Test.Hspec                (Spec, describe, hspec, it)-import           Test.HUnit                (Assertion, assertBool)+import           Test.HUnit                (Assertion, assertEqual)  import           Common                    (testApi) @@ -35,40 +35,78 @@                           [ ( "test/elm-sources/getOneSource.elm"                             , "module GetOneSource exposing (..)\n\n" <>                               "import Http\n" <>-                              "import Json.Decode exposing (..)\n\n\n")+                              "import Json.Decode exposing (..)\n" <>+                              "import Url.Builder\n\n\n"+                            )                           , ( "test/elm-sources/postTwoSource.elm"                             , "module PostTwoSource exposing (..)\n\n" <>                               "import Http\n" <>                               "import Json.Decode exposing (..)\n" <>-                              "import Json.Encode\n\n\n")+                              "import Json.Encode\n" <>+                              "import Url.Builder\n\n\n"+                            )                           , ( "test/elm-sources/getBooksByIdSource.elm"                             , "module GetBooksByIdSource exposing (..)\n\n" <>-                              "import Http\n\n\n")+                              "import Http\n" <>+                              "import Url.Builder\n" <>+                              "import Json.Decode\n" <>+                              "\n" <>+                              "type Book = Book\n" <>+                              "jsonDecBook : Json.Decode.Decoder Book\n" <>+                              "jsonDecBook = Debug.todo \"\"\n\n"+                            )                           , ( "test/elm-sources/getBooksByTitleSource.elm"                             , "module GetBooksByTitleSource exposing (..)\n\n" <>-                              "import Http\n\n\n")+                              "import Http\n" <>+                              "import Url.Builder\n" <>+                              "import Json.Decode as J\n\n" <>+                              "type alias Book = {}\n" <>+                              "jsonDecBook = J.succeed {}\n\n"+                            )                           , ( "test/elm-sources/getBooksSource.elm"                             , "module GetBooksSource exposing (..)\n\n" <>                               "import Http\n" <>-                              "import Json.Decode exposing (..)\n\n\n")+                              "import Json.Decode exposing (..)\n" <>+                              "import Url.Builder\n" <>+                              "import Json.Decode as J\n\n" <>+                              "type alias Book = {}\n\n" <>+                              "maybeBoolToIntStr : Maybe Bool -> String\n" <>+                              "maybeBoolToIntStr mx =\n" <>+                              "  case mx of\n" <>+                              "    Nothing -> \"\"\n" <>+                              "    Just True -> \"1\"\n" <>+                              "    Just False -> \"0\"\n\n" <>+                              "jsonDecBook = J.succeed {}\n\n"+                            )                           , ( "test/elm-sources/postBooksSource.elm"                             , "module PostBooksSource exposing (..)\n\n" <>-                              "import Http\n\n\n")+                              "import Http\n" <>+                              "import Url.Builder\n" <>+                              "import Json.Encode as Enc\n\n" <>+                              "type alias Book = {}\n" <>+                              "jsonEncBook = \\b -> Enc.object []\n\n"+                            )                           , ( "test/elm-sources/getNothingSource.elm"                             , "module GetNothingSource exposing (..)\n\n" <>-                              "import Http\n\n\n")+                              "import Http\n" <>+                              "import Url.Builder\n\n\n"+                            )                           , ( "test/elm-sources/putNothingSource.elm"                             , "module PutNothingSource exposing (..)\n\n" <>-                              "import Http\n\n\n")+                              "import Http\n" <>+                              "import Url.Builder\n\n\n"+                            )                           , ( "test/elm-sources/getWithaheaderSource.elm"                             , "module GetWithAHeaderSource exposing (..)\n\n" <>                               "import Http\n" <>+                              "import Url.Builder\n" <>                               "import Json.Decode exposing (..)\n\n\n")                           , ( "test/elm-sources/getWitharesponseheaderSource.elm"                             , "module GetWithAResponseHeaderSource exposing (..)\n\n" <>                               "import Http\n" <>+                              "import Url.Builder\n" <>                               "import Json.Decode exposing (..)\n\n\n")]-                  let generated = map (<> "\n") (generateElmForAPI testApi)+                  let generated = filter (not . T.null) (generateElmForAPI testApi)                   generated `itemsShouldBe` expected            it "with dynamic URLs" $                do expected <-@@ -79,6 +117,7 @@                           [ ( "test/elm-sources/getOneWithDynamicUrlSource.elm"                             , "module GetOneWithDynamicUrlSource exposing (..)\n\n" <>                               "import Http\n" <>+                              "import Url.Builder\n" <>                               "import Json.Decode exposing (..)\n\n\n")]                   let generated =                           map@@ -99,10 +138,13 @@  shouldBeDiff :: Text -> (String, Text, Text) -> Assertion shouldBeDiff a (fpath,header,b) =-    assertBool+    assertEqual         ("< generated\n" <> "> " <> fpath <> "\n" <>          Diff.ppDiff              (Diff.getGroupedDiff-                  (lines (T.unpack (header <> a)))-                  (lines (T.unpack b))))-        (header <> a == b)+                  (lines (T.unpack actual))+                  (lines (T.unpack expected))))+        actual expected+    where+      actual = T.strip $ header <> a+      expected = T.strip b
− test/elm-sources/elm-package.json
@@ -1,16 +0,0 @@-{-    "version": "1.0.0",-    "summary": "helpful summary of your project, less than 80 characters",-    "repository": "https://github.com/user/project.git",-    "license": "BSD3",-    "source-directories": [-        "."-    ],-    "exposed-modules": [],-    "dependencies": {-        "elm-lang/core": "5.0.0 <= v < 6.0.0",-        "elm-lang/html": "2.0.0 <= v < 3.0.0",-        "elm-lang/http": "1.0.0 <= v < 2.0.0"-    },-    "elm-version": "0.18.0 <= v < 0.19.0"-}
+ test/elm-sources/elm.json view
@@ -0,0 +1,30 @@+{+    "type": "application",+    "version": "1.0.0",+    "summary": "helpful summary of your project, less than 80 characters",+    "repository": "https://github.com/user/project.git",+    "license": "BSD3",+    "source-directories": [+        "."+    ],+    "exposed-modules": [],+    "dependencies": {+        "direct": {+          "elm/core": "1.0.2",+          "elm/json": "1.1.3",+          "elm/http": "2.0.0",+          "elm/url": "1.0.0",+          "bartavelle/json-helpers": "2.0.2"+        },+        "indirect": {+           "elm/bytes": "1.0.8",+           "elm/file": "1.0.5",+           "elm/time": "1.0.0"+        }+    },+    "test-dependencies": {+      "direct": {},+      "indirect": {}+    },+    "elm-version": "0.19.0"+}
test/elm-sources/getBooksByIdSource.elm view
@@ -1,27 +1,38 @@ module GetBooksByIdSource exposing (..)  import Http+import Url.Builder+import Json.Decode +type Book = Book+jsonDecBook : Json.Decode.Decoder Book+jsonDecBook = Debug.todo "" -getBooksById : Int -> Http.Request (Book)-getBooksById capture_id =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "books"-                , capture_id |> toString |> Http.encodeUri-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson decodeBook-        , timeout =-            Nothing-        , withCredentials =-            False-        }+getBooksById : Int -> (Result Http.Error  (Book)  -> msg) -> Cmd msg+getBooksById capture_id toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "books"+                    , capture_id |> String.fromInt+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson toMsg jsonDecBook+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/getBooksByTitleSource.elm view
@@ -1,27 +1,37 @@ module GetBooksByTitleSource exposing (..)  import Http+import Url.Builder+import Json.Decode as J +type alias Book = {}+jsonDecBook = J.succeed {} -getBooksByTitle : String -> Http.Request (Book)-getBooksByTitle capture_title =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "books"-                , capture_title |> Http.encodeUri-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson decodeBook-        , timeout =-            Nothing-        , withCredentials =-            False-        }+getBooksByTitle : String -> (Result Http.Error  (Book)  -> msg) -> Cmd msg+getBooksByTitle capture_title toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "books"+                    , capture_title+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson toMsg jsonDecBook+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/getBooksSource.elm view
@@ -2,30 +2,39 @@  import Http import Json.Decode exposing (..)+import Url.Builder+import Json.Decode as J +type alias Book = {} -getBooks : Bool -> Maybe (String) -> Maybe (Int) -> String -> List (Maybe (Bool)) -> Http.Request (List (Book))-getBooks query_published query_sort query_year query_category query_filters =+maybeBoolToIntStr : Maybe Bool -> String+maybeBoolToIntStr mx =+  case mx of+    Nothing -> ""+    Just True -> "1"+    Just False -> "0"++jsonDecBook = J.succeed {}++getBooks : Bool -> (Maybe String) -> (Maybe Int) -> String -> (List (Maybe Bool)) -> (Result Http.Error  ((List Book))  -> msg) -> Cmd msg+getBooks query_published query_sort query_year query_category query_filters toMsg =     let         params =-            List.filter (not << String.isEmpty)-                [ if query_published then-                    "query_published="+            List.filterMap identity+            (List.concat+                [ [ if query_published then+                    Just (Url.Builder.string "published" "")                   else-                    ""-                , query_sort-                    |> Maybe.map (Http.encodeUri >> (++) "sort=")-                    |> Maybe.withDefault ""-                , query_year-                    |> Maybe.map (toString >> Http.encodeUri >> (++) "year=")-                    |> Maybe.withDefault ""-                , Just query_category-                    |> Maybe.map (Http.encodeUri >> (++) "category=")-                    |> Maybe.withDefault ""+                    Nothing ]+                , [ query_sort+                    |> Maybe.map (Url.Builder.string "query_sort") ]+                , [ query_year+                    |> Maybe.map (String.fromInt >> Url.Builder.string "query_year") ]+                , [ Just query_category+                    |> Maybe.map (Url.Builder.string "query_category") ]                 , query_filters-                    |> List.map (\val -> "query_filters[]=" ++ (val |> toString |> Http.encodeUri))-                    |> String.join "&"-                ]+                    |> List.map (\val -> Just (Url.Builder.string "filters[]" (maybeBoolToIntStr val)))+                ])     in         Http.request             { method =@@ -33,20 +42,16 @@             , headers =                 []             , url =-                String.join "/"-                    [ ""-                    , "books"+                Url.Builder.crossOrigin ""+                    [ "books"                     ]-                ++ if List.isEmpty params then-                       ""-                   else-                       "?" ++ String.join "&" params+                    params             , body =                 Http.emptyBody             , expect =-                Http.expectJson (list decodeBook)+                Http.expectJson toMsg (Json.Decode.list (jsonDecBook))             , timeout =                 Nothing-            , withCredentials =-                False+            , tracker =+                Nothing             }
test/elm-sources/getNothingSource.elm view
@@ -1,32 +1,36 @@ module GetNothingSource exposing (..)  import Http+import Url.Builder  -getNothing : Http.Request (NoContent)-getNothing =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "nothing"-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectStringResponse-                (\{ body } ->-                    if String.isEmpty body then-                        Ok NoContent-                    else-                        Err "Expected the response body to be empty"-                )-        , timeout =-            Nothing-        , withCredentials =-            False-        }+getNothing : (Result Http.Error  (())  -> msg) -> Cmd msg+getNothing toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "nothing"+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectString +                     (\x -> case x of+                     Err e -> toMsg (Err e)+                     Ok _ -> toMsg (Ok ()))+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/getOneSource.elm view
@@ -2,26 +2,33 @@  import Http import Json.Decode exposing (..)+import Url.Builder  -getOne : Http.Request (Int)-getOne =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "one"-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson int-        , timeout =-            Nothing-        , withCredentials =-            False-        }+getOne : (Result Http.Error  (Int)  -> msg) -> Cmd msg+getOne toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "one"+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson toMsg Json.Decode.int+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/getOneWithDynamicUrlSource.elm view
@@ -1,27 +1,34 @@ module GetOneWithDynamicUrlSource exposing (..)  import Http+import Url.Builder import Json.Decode exposing (..)  -getOne : String -> Http.Request (Int)-getOne urlBase =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ urlBase-                , "one"-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson int-        , timeout =-            Nothing-        , withCredentials =-            False-        }+getOne : String -> (Result Http.Error  (Int)  -> msg) -> Cmd msg+getOne urlBase toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.absolute+                    [ "one"+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson toMsg Json.Decode.int+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/getWithaheaderSource.elm view
@@ -1,32 +1,39 @@ module GetWithAHeaderSource exposing (..)  import Http+import Url.Builder import Json.Decode exposing (..)  -getWithaheader : Maybe (String) -> Maybe (Int) -> String -> Int -> Http.Request (String)-getWithaheader header_myStringHeader header_MyIntHeader header_MyRequiredStringHeader header_MyRequiredIntHeader =-    Http.request-        { method =-            "GET"-        , headers =+getWithaheader : (Maybe String) -> (Maybe Int) -> String -> Int -> (Result Http.Error  (String)  -> msg) -> Cmd msg+getWithaheader header_myStringHeader header_MyIntHeader header_MyRequiredStringHeader header_MyRequiredIntHeader toMsg =+    let+        params =             List.filterMap identity-                [ Maybe.map (Http.header "myStringHeader") header_myStringHeader-                , Maybe.map (Http.header "MyIntHeader" << toString) header_MyIntHeader-                , Maybe.map (Http.header "MyRequiredStringHeader") (Just header_MyRequiredStringHeader)-                , Maybe.map (Http.header "MyRequiredIntHeader" << toString) (Just header_MyRequiredIntHeader)-                ]-        , url =-            String.join "/"-                [ ""-                , "with-a-header"-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson string-        , timeout =-            Nothing-        , withCredentials =-            False-        }+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                List.filterMap identity+                    [ Maybe.map (Http.header "myStringHeader") header_myStringHeader+                    , Maybe.map (Http.header "MyIntHeader" << String.fromInt) header_MyIntHeader+                    , Maybe.map (Http.header "MyRequiredStringHeader") (Just header_MyRequiredStringHeader)+                    , Maybe.map (Http.header "MyRequiredIntHeader" << String.fromInt) (Just header_MyRequiredIntHeader)+                    ]+            , url =+                Url.Builder.crossOrigin ""+                    [ "with-a-header"+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson toMsg Json.Decode.string+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/getWitharesponseheaderSource.elm view
@@ -1,27 +1,34 @@ module GetWithAResponseHeaderSource exposing (..)  import Http+import Url.Builder import Json.Decode exposing (..)  -getWitharesponseheader : Http.Request (String)-getWitharesponseheader =-    Http.request-        { method =-            "GET"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "with-a-response-header"-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectJson string-        , timeout =-            Nothing-        , withCredentials =-            False-        }+getWitharesponseheader : (Result Http.Error  (String)  -> msg) -> Cmd msg+getWitharesponseheader toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "GET"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "with-a-response-header"+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectJson toMsg Json.Decode.string+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/postBooksSource.elm view
@@ -1,32 +1,39 @@ module PostBooksSource exposing (..)  import Http+import Url.Builder+import Json.Encode as Enc +type alias Book = {}+jsonEncBook = \b -> Enc.object [] -postBooks : Book -> Http.Request (NoContent)-postBooks body =-    Http.request-        { method =-            "POST"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "books"-                ]-        , body =-            Http.jsonBody (encodeBook body)-        , expect =-            Http.expectStringResponse-                (\{ body } ->-                    if String.isEmpty body then-                        Ok NoContent-                    else-                        Err "Expected the response body to be empty"-                )-        , timeout =-            Nothing-        , withCredentials =-            False-        }+postBooks : Book -> (Result Http.Error  (())  -> msg) -> Cmd msg+postBooks body toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "POST"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "books"+                    ]+                    params+            , body =+                Http.jsonBody (jsonEncBook body)+            , expect =+                Http.expectString +                     (\x -> case x of+                     Err e -> toMsg (Err e)+                     Ok _ -> toMsg (Ok ()))+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/postTwoSource.elm view
@@ -3,26 +3,33 @@ import Http import Json.Decode exposing (..) import Json.Encode+import Url.Builder  -postTwo : String -> Http.Request (Maybe (Int))-postTwo body =-    Http.request-        { method =-            "POST"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "two"-                ]-        , body =-            Http.jsonBody (Json.Encode.string body)-        , expect =-            Http.expectJson (maybe int)-        , timeout =-            Nothing-        , withCredentials =-            False-        }+postTwo : String -> (Result Http.Error  ((Maybe Int))  -> msg) -> Cmd msg+postTwo body toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "POST"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "two"+                    ]+                    params+            , body =+                Http.jsonBody (Json.Encode.string body)+            , expect =+                Http.expectJson toMsg (Json.Decode.maybe (Json.Decode.int))+            , timeout =+                Nothing+            , tracker =+                Nothing+            }
test/elm-sources/putNothingSource.elm view
@@ -1,32 +1,36 @@ module PutNothingSource exposing (..)  import Http+import Url.Builder  -putNothing : Http.Request (())-putNothing =-    Http.request-        { method =-            "PUT"-        , headers =-            []-        , url =-            String.join "/"-                [ ""-                , "nothing"-                ]-        , body =-            Http.emptyBody-        , expect =-            Http.expectStringResponse-                (\{ body } ->-                    if String.isEmpty body then-                        Ok ()-                    else-                        Err "Expected the response body to be empty"-                )-        , timeout =-            Nothing-        , withCredentials =-            False-        }+putNothing : (Result Http.Error  (())  -> msg) -> Cmd msg+putNothing toMsg =+    let+        params =+            List.filterMap identity+            (List.concat+                [])+    in+        Http.request+            { method =+                "PUT"+            , headers =+                []+            , url =+                Url.Builder.crossOrigin ""+                    [ "nothing"+                    ]+                    params+            , body =+                Http.emptyBody+            , expect =+                Http.expectString +                     (\x -> case x of+                     Err e -> toMsg (Err e)+                     Ok _ -> toMsg (Ok ()))+            , timeout =+                Nothing+            , tracker =+                Nothing+            }