From 2b9c725d0bbc0e702c97161a39f449263b0d0de1 Mon Sep 17 00:00:00 2001 From: Raphael Tiersch Date: Fri, 4 Oct 2019 14:05:01 +0200 Subject: [PATCH 1/3] updated the golden files for haskell to be able to generate against those --- .../.openapi-generator/VERSION | 2 +- .../lib/OpenAPIPetstore/API.hs | 4 +-- .../lib/OpenAPIPetstore/Types.hs | 34 ------------------- 3 files changed, 3 insertions(+), 37 deletions(-) diff --git a/samples/server/petstore/haskell-servant/.openapi-generator/VERSION b/samples/server/petstore/haskell-servant/.openapi-generator/VERSION index 479c313e87b9..c3a2c7076fa8 100644 --- a/samples/server/petstore/haskell-servant/.openapi-generator/VERSION +++ b/samples/server/petstore/haskell-servant/.openapi-generator/VERSION @@ -1 +1 @@ -4.0.3-SNAPSHOT \ No newline at end of file +4.2.0-SNAPSHOT \ No newline at end of file diff --git a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs index 8a65a949ea87..959dd4ecf851 100644 --- a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs +++ b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs @@ -137,7 +137,7 @@ type OpenAPIPetstoreAPI = "pet" :> ReqBody '[JSON] Pet :> Verb 'POST 200 '[JSON] () -- 'addPet' route :<|> "pet" :> Capture "petId" Integer :> Header "api_key" Text :> Verb 'DELETE 200 '[JSON] () -- 'deletePet' route :<|> "pet" :> "findByStatus" :> QueryParam "status" (QueryList 'CommaSeparated (Text)) :> Verb 'GET 200 '[JSON] [Pet] -- 'findPetsByStatus' route - :<|> "pet" :> "findByTags" :> QueryParam "tags" (QueryList 'CommaSeparated (Text)) :> QueryParam "maxCount" Int :> Verb 'GET 200 '[JSON] [Pet] -- 'findPetsByTags' route + :<|> "pet" :> "findByTags" :> QueryParam "tags" (QueryList 'CommaSeparated (Text)) :> Verb 'GET 200 '[JSON] [Pet] -- 'findPetsByTags' route :<|> "pet" :> Capture "petId" Integer :> Verb 'GET 200 '[JSON] Pet -- 'getPetById' route :<|> "pet" :> ReqBody '[JSON] Pet :> Verb 'PUT 200 '[JSON] () -- 'updatePet' route :<|> "pet" :> Capture "petId" Integer :> ReqBody '[FormUrlEncoded] FormUpdatePetWithForm :> Verb 'POST 200 '[JSON] () -- 'updatePetWithForm' route @@ -176,7 +176,7 @@ data OpenAPIPetstoreBackend m = OpenAPIPetstoreBackend { addPet :: Pet -> m (){- ^ -} , deletePet :: Integer -> Maybe Text -> m (){- ^ -} , findPetsByStatus :: Maybe [Text] -> m [Pet]{- ^ Multiple status values can be provided with comma separated strings -} - , findPetsByTags :: Maybe [Text] -> Maybe Int -> m [Pet]{- ^ Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. -} + , findPetsByTags :: Maybe [Text] -> m [Pet]{- ^ Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. -} , getPetById :: Integer -> m Pet{- ^ Returns a single pet -} , updatePet :: Pet -> m (){- ^ -} , updatePetWithForm :: Integer -> FormUpdatePetWithForm -> m (){- ^ -} diff --git a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/Types.hs b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/Types.hs index 45355fe14ac8..d4f84e4288fc 100644 --- a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/Types.hs +++ b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/Types.hs @@ -6,8 +6,6 @@ module OpenAPIPetstore.Types ( ApiResponse (..), Category (..), - InlineObject (..), - InlineObject1 (..), Order (..), Pet (..), Tag (..), @@ -65,38 +63,6 @@ instance ToSchema Category where $ removeFieldLabelPrefix False "category" --- | -data InlineObject = InlineObject - { inlineObjectName :: Maybe Text -- ^ Updated name of the pet - , inlineObjectStatus :: Maybe Text -- ^ Updated status of the pet - } deriving (Show, Eq, Generic, Data) - -instance FromJSON InlineObject where - parseJSON = genericParseJSON (removeFieldLabelPrefix True "inlineObject") -instance ToJSON InlineObject where - toJSON = genericToJSON (removeFieldLabelPrefix False "inlineObject") -instance ToSchema InlineObject where - declareNamedSchema = Swagger.genericDeclareNamedSchema - $ Swagger.fromAesonOptions - $ removeFieldLabelPrefix False "inlineObject" - - --- | -data InlineObject1 = InlineObject1 - { inlineObject1AdditionalMetadata :: Maybe Text -- ^ Additional data to pass to server - , inlineObject1File :: Maybe FilePath -- ^ file to upload - } deriving (Show, Eq, Generic, Data) - -instance FromJSON InlineObject1 where - parseJSON = genericParseJSON (removeFieldLabelPrefix True "inlineObject1") -instance ToJSON InlineObject1 where - toJSON = genericToJSON (removeFieldLabelPrefix False "inlineObject1") -instance ToSchema InlineObject1 where - declareNamedSchema = Swagger.genericDeclareNamedSchema - $ Swagger.fromAesonOptions - $ removeFieldLabelPrefix False "inlineObject1" - - -- | An order for a pets from the pet store data Order = Order { orderId :: Maybe Integer -- ^ From 422c6a22681d5a113fcf80a5d239161cbd618e31 Mon Sep 17 00:00:00 2001 From: Raphael Tiersch Date: Fri, 4 Oct 2019 14:47:28 +0200 Subject: [PATCH 2/3] Added support for middlewares in the haskell servant generator. --- .../main/resources/haskell-servant/API.mustache | 7 ++++--- .../main/resources/haskell-servant/README.mustache | 14 +++++++++++++- .../haskell-servant-codegen.mustache | 1 + samples/server/petstore/haskell-servant/README.md | 14 +++++++++++++- .../haskell-servant/lib/OpenAPIPetstore/API.hs | 7 ++++--- .../haskell-servant/openapi-petstore.cabal | 1 + 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache b/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache index 4b02cb7f94ce..dc686f61132a 100644 --- a/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache +++ b/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache @@ -51,6 +51,7 @@ import GHC.Generics (Generic) import Network.HTTP.Client (Manager, newManager) import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Types.Method (methodOptions) +import Network.Wai (Middleware) import qualified Network.Wai.Handler.Warp as Warp import Servant (ServerError, serve) import Servant.API @@ -211,13 +212,13 @@ call{{title}} env f = do -- | Run the {{title}} server at the provided host and port. run{{title}}Server :: (MonadIO m, MonadThrow m) - => Config -> {{title}}Backend (ExceptT ServerError IO) -> m () -run{{title}}Server Config{..} backend = do + => Config -> Middleware -> {{title}}Backend (ExceptT ServerError IO) -> m () +run{{title}}Server Config{..} middleware backend = do url <- parseBaseUrl configUrl let warpSettings = Warp.defaultSettings & Warp.setPort (baseUrlPort url) & Warp.setHost (fromString $ baseUrlHost url) - liftIO $ Warp.runSettings warpSettings $ serve (Proxy :: Proxy {{title}}API) (serverFromBackend backend) + liftIO $ Warp.runSettings warpSettings $ middleware $ serve (Proxy :: Proxy {{title}}API) (serverFromBackend backend) where serverFromBackend {{title}}Backend{..} = ({{#apis}}{{#operations}}{{#operation}}coerce {{operationId}}{{#hasMore}} :<|> diff --git a/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache b/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache index 7a412893371f..91d52207a0e5 100644 --- a/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache +++ b/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache @@ -66,14 +66,26 @@ functions in `{{title}}.Handlers`, you can write: {-# LANGUAGE RecordWildCards #-} import {{title}}.API +-- required dependency: wai +import Network.Wai (Middleware) +-- required dependency: wai-extra +import Network.Wai.Middleware.RequestLogger (logStdout) -- A module you wrote yourself, containing all handlers needed for the {{title}}Backend type. import {{title}}.Handlers +-- If you would like to not use any middlewares just create an id-middleware implementation +-- requestMiddlewareId :: Application -> Application +-- requestMiddlewareId a = a + +-- Combined middlewares +requestMiddlewares :: Middleware +requestMiddlewares = logStdout + -- Run a {{title}} server on localhost:8080 main :: IO () main = do let server = {{title}}Backend{..} config = Config "http://localhost:8080/" - run{{title}}Server config server + run{{title}}Server config requestMiddlewares server ``` diff --git a/modules/openapi-generator/src/main/resources/haskell-servant/haskell-servant-codegen.mustache b/modules/openapi-generator/src/main/resources/haskell-servant/haskell-servant-codegen.mustache index 777cf33d6a2f..f41a28b41f2e 100644 --- a/modules/openapi-generator/src/main/resources/haskell-servant/haskell-servant-codegen.mustache +++ b/modules/openapi-generator/src/main/resources/haskell-servant/haskell-servant-codegen.mustache @@ -28,6 +28,7 @@ library , servant-client-core , servant-server , servant + , wai , warp , transformers , mtl diff --git a/samples/server/petstore/haskell-servant/README.md b/samples/server/petstore/haskell-servant/README.md index a2c40300b151..1853dfa57618 100644 --- a/samples/server/petstore/haskell-servant/README.md +++ b/samples/server/petstore/haskell-servant/README.md @@ -66,14 +66,26 @@ functions in `OpenAPIPetstore.Handlers`, you can write: {-# LANGUAGE RecordWildCards #-} import OpenAPIPetstore.API +-- required dependency: wai +import Network.Wai (Middleware) +-- required dependency: wai-extra +import Network.Wai.Middleware.RequestLogger (logStdout) -- A module you wrote yourself, containing all handlers needed for the OpenAPIPetstoreBackend type. import OpenAPIPetstore.Handlers +-- If you would like to not use any middlewares just create an id-middleware implementation +-- requestMiddlewareId :: Application -> Application +-- requestMiddlewareId a = a + +-- Default Requestlogging middleware. +requestMiddlewares :: Middleware +requestMiddlewares = logStdout + -- Run a OpenAPIPetstore server on localhost:8080 main :: IO () main = do let server = OpenAPIPetstoreBackend{..} config = Config "http://localhost:8080/" - runOpenAPIPetstoreServer config server + runOpenAPIPetstoreServer config requestMiddlewares server ``` diff --git a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs index 959dd4ecf851..4b1e118ee361 100644 --- a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs +++ b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs @@ -51,6 +51,7 @@ import GHC.Generics (Generic) import Network.HTTP.Client (Manager, newManager) import Network.HTTP.Client.TLS (tlsManagerSettings) import Network.HTTP.Types.Method (methodOptions) +import Network.Wai (Middleware) import qualified Network.Wai.Handler.Warp as Warp import Servant (ServerError, serve) import Servant.API @@ -263,13 +264,13 @@ callOpenAPIPetstore env f = do -- | Run the OpenAPIPetstore server at the provided host and port. runOpenAPIPetstoreServer :: (MonadIO m, MonadThrow m) - => Config -> OpenAPIPetstoreBackend (ExceptT ServerError IO) -> m () -runOpenAPIPetstoreServer Config{..} backend = do + => Config -> Middleware -> OpenAPIPetstoreBackend (ExceptT ServerError IO) -> m () +runOpenAPIPetstoreServer Config{..} middleware backend = do url <- parseBaseUrl configUrl let warpSettings = Warp.defaultSettings & Warp.setPort (baseUrlPort url) & Warp.setHost (fromString $ baseUrlHost url) - liftIO $ Warp.runSettings warpSettings $ serve (Proxy :: Proxy OpenAPIPetstoreAPI) (serverFromBackend backend) + liftIO $ Warp.runSettings warpSettings $ middleware $ serve (Proxy :: Proxy OpenAPIPetstoreAPI) (serverFromBackend backend) where serverFromBackend OpenAPIPetstoreBackend{..} = (coerce addPet :<|> diff --git a/samples/server/petstore/haskell-servant/openapi-petstore.cabal b/samples/server/petstore/haskell-servant/openapi-petstore.cabal index 93888ef6de1c..50f4dfcc2587 100644 --- a/samples/server/petstore/haskell-servant/openapi-petstore.cabal +++ b/samples/server/petstore/haskell-servant/openapi-petstore.cabal @@ -28,6 +28,7 @@ library , servant-client-core , servant-server , servant + , wai , warp , transformers , mtl From a7cb7db03128b14e26274ab0b84db291d9cb01d0 Mon Sep 17 00:00:00 2001 From: Raphael Tiersch Date: Sat, 5 Oct 2019 19:31:40 +0200 Subject: [PATCH 3/3] Renamed runOpenAPIPetstoreServer to runOpenAPIPetstoreMiddlewareServer and added a backwards compability version runOpenAPIPetstoreServer using requestMiddlewareId as middleware. --- .../main/resources/haskell-servant/API.mustache | 15 +++++++++++++-- .../resources/haskell-servant/README.mustache | 8 +++----- samples/server/petstore/haskell-servant/README.md | 10 ++++------ .../haskell-servant/lib/OpenAPIPetstore/API.hs | 15 +++++++++++++-- 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache b/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache index dc686f61132a..5816f6106714 100644 --- a/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache +++ b/modules/openapi-generator/src/main/resources/haskell-servant/API.mustache @@ -19,6 +19,7 @@ module {{title}}.API , {{title}}Backend(..) , create{{title}}Client , run{{title}}Server + , run{{title}}MiddlewareServer , run{{title}}Client , run{{title}}ClientWithManager , call{{title}} @@ -150,7 +151,7 @@ newtype {{title}}ClientError = {{title}}ClientError ClientError -- | Backend for {{title}}. -- The backend can be used both for the client and the server. The client generated from the {{title}} OpenAPI spec -- is a backend that executes actions by sending HTTP requests (see @create{{title}}Client@). Alternatively, provided --- a backend, the API can be served using @run{{title}}Server@. +-- a backend, the API can be served using @run{{title}}MiddlewareServer@. data {{title}}Backend m = {{title}}Backend { {{#apis}}{{#operations}}{{#operation}}{{operationId}} :: {{& vendorExtensions.x-clientType}}{- ^ {{& notes}} -}{{#hasMore}} , {{/hasMore}}{{/operation}}{{/operations}}{{#hasMore}} @@ -208,12 +209,22 @@ call{{title}} env f = do Right response -> pure response {{/apiInfo}} + {{#apiInfo}} +requestMiddlewareId :: Application -> Application +requestMiddlewareId a = a + -- | Run the {{title}} server at the provided host and port. run{{title}}Server +:: (MonadIO m, MonadThrow m) +=> Config -> {{title}}Backend (ExceptT ServerError IO) -> m () +run{{title}}Server config backend = run{{title}}MiddlewareServer config requestMiddlewareId backend + +-- | Run the {{title}} server at the provided host and port. +run{{title}}MiddlewareServer :: (MonadIO m, MonadThrow m) => Config -> Middleware -> {{title}}Backend (ExceptT ServerError IO) -> m () -run{{title}}Server Config{..} middleware backend = do +run{{title}}MiddlewareServer Config{..} middleware backend = do url <- parseBaseUrl configUrl let warpSettings = Warp.defaultSettings & Warp.setPort (baseUrlPort url) diff --git a/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache b/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache index 91d52207a0e5..4dac568315c3 100644 --- a/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache +++ b/modules/openapi-generator/src/main/resources/haskell-servant/README.mustache @@ -58,7 +58,7 @@ main = do ## Creating a Server -In order to create a server, you must use the `run{{title}}Server` function. However, you unlike the client, in which case you *got* a `{{title}}Backend` +In order to create a server, you must use the `run{{title}}MiddlewareServer` function. However, you unlike the client, in which case you *got* a `{{title}}Backend` from the library, you must instead *provide* a `{{title}}Backend`. For example, if you have defined handler functions for all the functions in `{{title}}.Handlers`, you can write: @@ -74,9 +74,7 @@ import Network.Wai.Middleware.RequestLogger (logStdout) -- A module you wrote yourself, containing all handlers needed for the {{title}}Backend type. import {{title}}.Handlers --- If you would like to not use any middlewares just create an id-middleware implementation --- requestMiddlewareId :: Application -> Application --- requestMiddlewareId a = a +-- If you would like to not use any middlewares you could use run{{title}}Server instead -- Combined middlewares requestMiddlewares :: Middleware @@ -87,5 +85,5 @@ main :: IO () main = do let server = {{title}}Backend{..} config = Config "http://localhost:8080/" - run{{title}}Server config requestMiddlewares server + run{{title}}MiddlewareServer config requestMiddlewares server ``` diff --git a/samples/server/petstore/haskell-servant/README.md b/samples/server/petstore/haskell-servant/README.md index 1853dfa57618..5a5258926eef 100644 --- a/samples/server/petstore/haskell-servant/README.md +++ b/samples/server/petstore/haskell-servant/README.md @@ -58,7 +58,7 @@ main = do ## Creating a Server -In order to create a server, you must use the `runOpenAPIPetstoreServer` function. However, you unlike the client, in which case you *got* a `OpenAPIPetstoreBackend` +In order to create a server, you must use the `runOpenAPIPetstoreMiddlewareServer` function. However, you unlike the client, in which case you *got* a `OpenAPIPetstoreBackend` from the library, you must instead *provide* a `OpenAPIPetstoreBackend`. For example, if you have defined handler functions for all the functions in `OpenAPIPetstore.Handlers`, you can write: @@ -74,11 +74,9 @@ import Network.Wai.Middleware.RequestLogger (logStdout) -- A module you wrote yourself, containing all handlers needed for the OpenAPIPetstoreBackend type. import OpenAPIPetstore.Handlers --- If you would like to not use any middlewares just create an id-middleware implementation --- requestMiddlewareId :: Application -> Application --- requestMiddlewareId a = a +-- If you would like to not use any middlewares you could use runOpenAPIPetstoreServer instead --- Default Requestlogging middleware. +-- Combined middlewares requestMiddlewares :: Middleware requestMiddlewares = logStdout @@ -87,5 +85,5 @@ main :: IO () main = do let server = OpenAPIPetstoreBackend{..} config = Config "http://localhost:8080/" - runOpenAPIPetstoreServer config requestMiddlewares server + runOpenAPIPetstoreMiddlewareServer config requestMiddlewares server ``` diff --git a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs index 4b1e118ee361..2f7f240a5545 100644 --- a/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs +++ b/samples/server/petstore/haskell-servant/lib/OpenAPIPetstore/API.hs @@ -19,6 +19,7 @@ module OpenAPIPetstore.API , OpenAPIPetstoreBackend(..) , createOpenAPIPetstoreClient , runOpenAPIPetstoreServer + , runOpenAPIPetstoreMiddlewareServer , runOpenAPIPetstoreClient , runOpenAPIPetstoreClientWithManager , callOpenAPIPetstore @@ -172,7 +173,7 @@ newtype OpenAPIPetstoreClientError = OpenAPIPetstoreClientError ClientError -- | Backend for OpenAPIPetstore. -- The backend can be used both for the client and the server. The client generated from the OpenAPIPetstore OpenAPI spec -- is a backend that executes actions by sending HTTP requests (see @createOpenAPIPetstoreClient@). Alternatively, provided --- a backend, the API can be served using @runOpenAPIPetstoreServer@. +-- a backend, the API can be served using @runOpenAPIPetstoreMiddlewareServer@. data OpenAPIPetstoreBackend m = OpenAPIPetstoreBackend { addPet :: Pet -> m (){- ^ -} , deletePet :: Integer -> Maybe Text -> m (){- ^ -} @@ -261,11 +262,21 @@ callOpenAPIPetstore env f = do Left err -> throwM (OpenAPIPetstoreClientError err) Right response -> pure response + +requestMiddlewareId :: Application -> Application +requestMiddlewareId a = a + -- | Run the OpenAPIPetstore server at the provided host and port. runOpenAPIPetstoreServer +:: (MonadIO m, MonadThrow m) +=> Config -> OpenAPIPetstoreBackend (ExceptT ServerError IO) -> m () +runOpenAPIPetstoreServer config backend = runOpenAPIPetstoreMiddlewareServer config requestMiddlewareId backend + +-- | Run the OpenAPIPetstore server at the provided host and port. +runOpenAPIPetstoreMiddlewareServer :: (MonadIO m, MonadThrow m) => Config -> Middleware -> OpenAPIPetstoreBackend (ExceptT ServerError IO) -> m () -runOpenAPIPetstoreServer Config{..} middleware backend = do +runOpenAPIPetstoreMiddlewareServer Config{..} middleware backend = do url <- parseBaseUrl configUrl let warpSettings = Warp.defaultSettings & Warp.setPort (baseUrlPort url)