packages feed

servant-elm 0.4.0.1 → 0.5.0.0

raw patch · 9 files changed

+123/−36 lines, 9 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

- Servant.Elm: data Proxy k (t :: k) :: forall k. k -> *
+ Servant.Elm: data Proxy k (t :: k) :: forall k. () => k -> *
+ Servant.Elm.Internal.Generate: elmListOfMaybes :: [Doc] -> Doc
+ Servant.Elm.Internal.Generate: isElmMaybeStringType :: ElmOptions -> ElmDatatype -> Bool
+ Servant.Elm.Internal.Generate: isElmMaybeType :: ElmDatatype -> Bool
+ Servant.Elm.Internal.Generate: isNotCookie :: HeaderArg f -> Bool
+ Servant.Elm.Internal.Orphans: instance Elm.Type.ElmType a => Elm.Type.ElmType (Servant.API.ResponseHeaders.Headers ls a)

Files

CHANGELOG.md view
@@ -1,3 +1,11 @@+0.5.0.0+-------+* Fix generation for APIs with response headers.+* Support Servant's `Optional` and `Required` modifiers for+  `Header`s and `QueryParam`s (`Header` arguments are now `Maybe`s+  by default). (phaazon) (#31)+* Filter out forbidden Cookie header. (xilnocas) (#37)+ 0.4.0.1 ------- * Remove hyphens from generated function names. (servant-foreign-0.10 no longer
servant-elm.cabal view
@@ -1,5 +1,5 @@ name:                servant-elm-version:             0.4.0.1+version:             0.5.0.0 synopsis:            Automatically derive Elm functions to query servant webservices. description:         Please see README.md homepage:            http://github.com/mattjbray/servant-elm#readme
src/Servant/Elm/Internal/Generate.hs view
@@ -12,7 +12,7 @@ import qualified Data.Text                    as T import qualified Data.Text.Lazy               as L import qualified Data.Text.Encoding           as T-import           Elm                          (ElmDatatype)+import           Elm                          (ElmDatatype(..), ElmPrimitive(..)) import qualified Elm import           Servant.API                  (NoContent (..)) import           Servant.Elm.Internal.Foreign (LangElm, getEndpoints)@@ -198,6 +198,7 @@     headerTypes =       [ header ^. F.headerArg . F.argType . to elmTypeRef       | header <- request ^. F.reqHeaders+      , isNotCookie header       ]      urlCaptureTypes :: [Doc]@@ -209,15 +210,8 @@      queryTypes :: [Doc]     queryTypes =-      [ arg ^. F.queryArgName . F.argType . to (elmTypeRef . wrapper)+      [ arg ^. F.queryArgName . F.argType . to elmTypeRef       | arg <- request ^. F.reqUrl . F.queryStr-      , wrapper <- [-          case arg ^. F.queryArgType of-            F.Normal ->-              Elm.ElmPrimitive . Elm.EMaybe-            _ ->-              id-          ]       ]      bodyType :: Maybe Doc@@ -233,7 +227,7 @@ elmHeaderArg :: F.HeaderArg ElmDatatype -> Doc elmHeaderArg header =   "header_" <>-  header ^. F.headerArg . F.argName . to (stext . F.unPathSegment)+  header ^. F.headerArg . F.argName . to (stext . T.replace "-" "_" . F.unPathSegment)   elmCaptureArg :: F.Segment ElmDatatype -> Doc@@ -253,6 +247,14 @@   "body"  +isNotCookie :: F.HeaderArg f -> Bool+isNotCookie header =+   header+     ^. F.headerArg+      . F.argName+      . to ((/= "cookie") . T.toLower . F.unPathSegment)++ mkArgs   :: ElmOptions   -> F.Req ElmDatatype@@ -266,6 +268,7 @@     , -- Headers       [ elmHeaderArg header       | header <- request ^. F.reqHeaders+      , isNotCookie header       ]     , -- URL Captures       [ elmCaptureArg segment@@ -299,14 +302,16 @@       case qarg ^. F.queryArgType of         F.Normal ->           let+            argType = qarg ^. F.queryArgName . F.argType+            wrapped = isElmMaybeType argType             -- Don't use "toString" on Elm Strings, otherwise we get extraneous quotes.             toStringSrc =-              if isElmStringType opts (qarg ^. F.queryArgName . F.argType) then+              if isElmStringType opts argType || isElmMaybeStringType opts argType then                 ""               else                 "toString >> "           in-              name <$>+              (if wrapped then name else "Just" <+> name) <$>               indent 4 ("|> Maybe.map" <+> parens (toStringSrc <> "Http.encodeUri >> (++)" <+> dquotes (elmName <> equals)) <$>                         "|> Maybe.withDefault" <+> dquotes empty) @@ -334,7 +339,7 @@          indent i (dquotes method)        , "headers =" <$>          indent i-           (elmList headers)+           (elmListOfMaybes headers)        , "url =" <$>          indent i url        , "body =" <$>@@ -350,17 +355,26 @@     method =        request ^. F.reqMethod . to (stext . T.decodeUtf8) +    mkHeader header =+      let headerName = header ^. F.headerArg . F.argName . to (stext . F.unPathSegment)+          headerArgName = elmHeaderArg header+          argType = header ^. F.headerArg . F.argType+          wrapped = isElmMaybeType argType+          toStringSrc =+            if isElmMaybeStringType opts argType || isElmStringType opts argType then+              mempty+            else+              " << toString"+      in+        "Maybe.map" <+> parens (("Http.header" <+> dquotes headerName <> toStringSrc))+        <+>+        (if wrapped then headerArgName else parens ("Just" <+> headerArgName))+     headers =-        [("Http.header" <+> dquotes headerName <+>-                 (if isElmStringType opts (header ^. F.headerArg . F.argType) then-                     headerArgName-                   else-                     parens ("toString " <> headerArgName)-                  ))-        | header <- request ^. F.reqHeaders-        , headerName <- [header ^. F.headerArg . F.argName . to (stext . F.unPathSegment)]-        , headerArgName <- [elmHeaderArg header]-        ]+      [ mkHeader header+      | header <- request ^. F.reqHeaders+      , isNotCookie header+      ]      url =       mkUrl opts (request ^. F.reqUrl . F.path)@@ -454,7 +468,17 @@ 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 _ _ = False +isElmMaybeType :: ElmDatatype -> Bool+isElmMaybeType (ElmPrimitive (EMaybe _)) = True+isElmMaybeType _ = False++ -- Doc helpers  @@ -471,3 +495,7 @@ elmList :: [Doc] -> Doc elmList [] = lbracket <> rbracket elmList ds = lbracket <+> hsep (punctuate (line <> comma) ds) <$> rbracket++elmListOfMaybes :: [Doc] -> Doc+elmListOfMaybes [] = lbracket <> rbracket+elmListOfMaybes ds = "List.filterMap identity" <$> indent 4 (elmList ds)
src/Servant/Elm/Internal/Orphans.hs view
@@ -1,10 +1,11 @@ {-# LANGUAGE DeriveGeneric      #-} {-# LANGUAGE StandaloneDeriving #-}+{-# OPTIONS_GHC -fno-warn-orphans #-}  module Servant.Elm.Internal.Orphans where  import           Elm         (ElmDatatype, ElmType, toElmType)-import           Servant.API (NoContent)+import           Servant.API (NoContent, Headers, getResponse)   instance ElmType ElmDatatype where@@ -12,3 +13,9 @@   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
@@ -3,14 +3,15 @@ {-# LANGUAGE TypeOperators #-} module Common where -import           Data.Text    (Text) import           Data.Aeson   (ToJSON)-import           Data.Proxy   (Proxy(Proxy))+import           Data.Proxy   (Proxy (Proxy))+import           Data.Text    (Text) import           Elm          (ElmType) import           GHC.Generics (Generic) import           Servant.API  ((:<|>), (:>), Capture, Get, GetNoContent, Header,-                               JSON, NoContent, Post, PostNoContent, Put,-                               QueryFlag, QueryParam, QueryParams, ReqBody)+                               Header', Headers, JSON, NoContent, Post,+                               PostNoContent, Put, QueryFlag, QueryParam,+                               QueryParam', QueryParams, ReqBody, Required)  data Book = Book     { title :: String@@ -35,6 +36,7 @@          :> QueryFlag "published"          :> QueryParam "sort" String          :> QueryParam "year" Int+         :> QueryParam' '[Required] "category" String          :> QueryParams "filters" (Maybe Bool)          :> Get '[JSON] [Book]   :<|> "books"@@ -45,9 +47,14 @@   :<|> "nothing"          :> Put '[JSON] () -- old way to specify no content   :<|> "with-a-header"+         :> Header "Cookie" String          :> Header "myStringHeader" String          :> Header "MyIntHeader" Int+         :> Header' '[Required] "MyRequiredStringHeader" String+         :> Header' '[Required] "MyRequiredIntHeader" Int          :> Get '[JSON] String+  :<|> "with-a-response-header"+         :> Get '[JSON] (Headers '[Header "myResponse" String] String)  testApi :: Proxy TestApi testApi = Proxy
test/GenerateSpec.hs view
@@ -63,6 +63,10 @@                           , ( "test/elm-sources/getWithaheaderSource.elm"                             , "module GetWithAHeaderSource exposing (..)\n\n" <>                               "import Http\n" <>+                              "import Json.Decode exposing (..)\n\n\n")+                          , ( "test/elm-sources/getWitharesponseheaderSource.elm"+                            , "module GetWithAResponseHeaderSource exposing (..)\n\n" <>+                              "import Http\n" <>                               "import Json.Decode exposing (..)\n\n\n")]                   let generated = map (<> "\n") (generateElmForAPI testApi)                   generated `itemsShouldBe` expected
test/elm-sources/getBooksSource.elm view
@@ -4,8 +4,8 @@ import Json.Decode exposing (..)  -getBooks : Bool -> Maybe (String) -> Maybe (Int) -> List (Maybe (Bool)) -> Http.Request (List (Book))-getBooks query_published query_sort query_year query_filters =+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 =     let         params =             List.filter (not << String.isEmpty)@@ -18,6 +18,9 @@                     |> Maybe.withDefault ""                 , query_year                     |> Maybe.map (toString >> Http.encodeUri >> (++) "year=")+                    |> Maybe.withDefault ""+                , Just query_category+                    |> Maybe.map (Http.encodeUri >> (++) "category=")                     |> Maybe.withDefault ""                 , query_filters                     |> List.map (\val -> "query_filters[]=" ++ (val |> toString |> Http.encodeUri))
test/elm-sources/getWithaheaderSource.elm view
@@ -4,15 +4,18 @@ import Json.Decode exposing (..)  -getWithaheader : String -> Int -> Http.Request (String)-getWithaheader header_myStringHeader header_MyIntHeader =+getWithaheader : Maybe (String) -> Maybe (Int) -> String -> Int -> Http.Request (String)+getWithaheader header_myStringHeader header_MyIntHeader header_MyRequiredStringHeader header_MyRequiredIntHeader =     Http.request         { method =             "GET"         , headers =-            [ Http.header "myStringHeader" header_myStringHeader-            , Http.header "MyIntHeader" (toString header_MyIntHeader)-            ]+            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 "/"                 [ ""
+ test/elm-sources/getWitharesponseheaderSource.elm view
@@ -0,0 +1,27 @@+module GetWithAResponseHeaderSource exposing (..)++import Http+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+        }