packages feed

servant-openapi3 2.0.1.1 → 2.0.1.2

raw patch · 6 files changed

+380/−24 lines, 6 filesdep ~basedep ~doctestdep ~hspecPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: base, doctest, hspec, openapi3

API changes (from Hackage documentation)

- Servant.OpenApi.Internal: instance Data.OpenApi.Internal.Schema.ToSchema a => Data.OpenApi.Internal.Schema.ToSchema (Servant.API.UVerb.WithStatus s a)
- Servant.OpenApi.Internal.Orphans: instance Data.OpenApi.Internal.Schema.ToSchema a => Data.OpenApi.Internal.Schema.ToSchema (Servant.Types.SourceT.SourceT m a)
+ Servant.OpenApi.Internal: instance (Data.Typeable.Internal.Typeable (Servant.API.UVerb.WithStatus s a), Data.OpenApi.Internal.Schema.ToSchema a) => Data.OpenApi.Internal.Schema.ToSchema (Servant.API.UVerb.WithStatus s a)
+ Servant.OpenApi.Internal.Orphans: instance (Data.Typeable.Internal.Typeable (Servant.Types.SourceT.SourceT m a), Data.OpenApi.Internal.Schema.ToSchema a) => Data.OpenApi.Internal.Schema.ToSchema (Servant.Types.SourceT.SourceT m a)
+ Servant.OpenApi.Internal.Test: encodePretty :: ToJSON a => a -> ByteString

Files

example/example.cabal view
@@ -17,7 +17,7 @@   GHC ==8.4.4    || ==8.6.5    || ==8.8.4-   || ==8.10.2+   || ==8.10.4  library   ghc-options:      -Wall
servant-openapi3.cabal view
@@ -1,5 +1,5 @@ name:                servant-openapi3-version:             2.0.1.1+version:             2.0.1.2 synopsis:            Generate a Swagger/OpenAPI/OAS 3.0 specification for your servant API. description:   Swagger is a project used to describe and document RESTful APIs. The core of the @@ -32,7 +32,7 @@   GHC ==8.4.4    || ==8.6.5    || ==8.8.4-   || ==8.10.2+   || ==8.10.4  extra-source-files:     README.md@@ -80,10 +80,10 @@                      , bytestring                >=0.10.8.1 && <0.11                      , http-media                >=0.7.1.3  && <0.9                      , insert-ordered-containers >=0.2.1.0  && <0.3-                     , lens                      >=4.17     && <4.20+                     , lens                      >=4.17     && <5.1                      , servant                   >=0.17     && <0.19                      , singleton-bool            >=0.1.4    && <0.2-                     , openapi3                  >=3.0.0    && <4.0+                     , openapi3                  >=3.0.0    && <3.2                      , text                      >=1.2.3.0  && <1.3                      , unordered-containers      >=0.2.9.0  && <0.3 @@ -120,7 +120,8 @@                   , lens-aeson >=1.0.2    && <1.2                   , servant                   , servant-openapi3-                  , openapi3+                    -- openapi3 3.1.0 fixes some ordering-related issues, making tests stable+                  , openapi3 >= 3.1.0                   , text                   , template-haskell                   , utf8-string >=1.0.1.1 && <1.1
src/Servant/OpenApi.hs view
@@ -57,6 +57,7 @@ -- >>> import Test.Hspec -- >>> import Test.QuickCheck -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL8+-- >>> import Servant.OpenApi.Internal.Test -- >>> :set -XDataKinds -- >>> :set -XDeriveDataTypeable -- >>> :set -XDeriveGeneric@@ -98,8 +99,114 @@ -- $generate -- In order to generate @'OpenApi'@ specification for a servant API, just use @'toOpenApi'@: ----- >>> BSL8.putStrLn $ encode $ toOpenApi (Proxy :: Proxy UserAPI)--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/":{"get":{"responses":{"200":{"content":{"application/json;charset=utf-8":{"schema":{"items":{"$ref":"#/components/schemas/User"},"type":"array"}}},"description":""}}},"post":{"requestBody":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"400":{"description":"Invalid `body`"},"200":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/UserId"}}},"description":""}}}},"/{user_id}":{"get":{"parameters":[{"required":true,"schema":{"type":"integer"},"in":"path","name":"user_id"}],"responses":{"404":{"description":"`user_id` not found"},"200":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/User"}}},"description":""}}}}},"components":{"schemas":{"User":{"required":["name","age"],"type":"object","properties":{"age":{"maximum":9223372036854775807,"minimum":-9223372036854775808,"type":"integer"},"name":{"type":"string"}}},"UserId":{"type":"integer"}}}}+-- >>> BSL8.putStrLn $ encodePretty $ toOpenApi (Proxy :: Proxy UserAPI)+-- {+--     "components": {+--         "schemas": {+--             "User": {+--                 "properties": {+--                     "age": {+--                         "maximum": 9223372036854775807,+--                         "minimum": -9223372036854775808,+--                         "type": "integer"+--                     },+--                     "name": {+--                         "type": "string"+--                     }+--                 },+--                 "required": [+--                     "name",+--                     "age"+--                 ],+--                 "type": "object"+--             },+--             "UserId": {+--                 "type": "integer"+--             }+--         }+--     },+--     "info": {+--         "title": "",+--         "version": ""+--     },+--     "openapi": "3.0.0",+--     "paths": {+--         "/": {+--             "get": {+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "items": {+--                                         "$ref": "#/components/schemas/User"+--                                     },+--                                     "type": "array"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     }+--                 }+--             },+--             "post": {+--                 "requestBody": {+--                     "content": {+--                         "application/json;charset=utf-8": {+--                             "schema": {+--                                 "$ref": "#/components/schemas/User"+--                             }+--                         }+--                     }+--                 },+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "$ref": "#/components/schemas/UserId"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     },+--                     "400": {+--                         "description": "Invalid `body`"+--                     }+--                 }+--             }+--         },+--         "/{user_id}": {+--             "get": {+--                 "parameters": [+--                     {+--                         "in": "path",+--                         "name": "user_id",+--                         "required": true,+--                         "schema": {+--                             "type": "integer"+--                         }+--                     }+--                 ],+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "$ref": "#/components/schemas/User"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     },+--                     "404": {+--                         "description": "`user_id` not found"+--                     }+--                 }+--             }+--         }+--     }+-- } -- -- By default @'toOpenApi'@ will generate specification for all API routes, parameters, headers, responses and data schemas. --@@ -113,14 +220,129 @@ -- We can add this information using field lenses from @"Data.OpenApi"@: -- -- >>> :{--- BSL8.putStrLn $ encode $ toOpenApi (Proxy :: Proxy UserAPI)+-- BSL8.putStrLn $ encodePretty $ toOpenApi (Proxy :: Proxy UserAPI) --   & info.title        .~ "User API" --   & info.version      .~ "1.0" --   & info.description  ?~ "This is an API for the Users service" --   & info.license      ?~ "MIT" --   & servers           .~ ["https://example.com"] -- :}--- {"openapi":"3.0.0","info":{"version":"1.0","title":"User API","license":{"name":"MIT"},"description":"This is an API for the Users service"},"servers":[{"url":"https://example.com"}],"paths":{"/":{"get":{"responses":{"200":{"content":{"application/json;charset=utf-8":{"schema":{"items":{"$ref":"#/components/schemas/User"},"type":"array"}}},"description":""}}},"post":{"requestBody":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"400":{"description":"Invalid `body`"},"200":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/UserId"}}},"description":""}}}},"/{user_id}":{"get":{"parameters":[{"required":true,"schema":{"type":"integer"},"in":"path","name":"user_id"}],"responses":{"404":{"description":"`user_id` not found"},"200":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/User"}}},"description":""}}}}},"components":{"schemas":{"User":{"required":["name","age"],"type":"object","properties":{"age":{"maximum":9223372036854775807,"minimum":-9223372036854775808,"type":"integer"},"name":{"type":"string"}}},"UserId":{"type":"integer"}}}}+-- {+--     "components": {+--         "schemas": {+--             "User": {+--                 "properties": {+--                     "age": {+--                         "maximum": 9223372036854775807,+--                         "minimum": -9223372036854775808,+--                         "type": "integer"+--                     },+--                     "name": {+--                         "type": "string"+--                     }+--                 },+--                 "required": [+--                     "name",+--                     "age"+--                 ],+--                 "type": "object"+--             },+--             "UserId": {+--                 "type": "integer"+--             }+--         }+--     },+--     "info": {+--         "description": "This is an API for the Users service",+--         "license": {+--             "name": "MIT"+--         },+--         "title": "User API",+--         "version": "1.0"+--     },+--     "openapi": "3.0.0",+--     "paths": {+--         "/": {+--             "get": {+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "items": {+--                                         "$ref": "#/components/schemas/User"+--                                     },+--                                     "type": "array"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     }+--                 }+--             },+--             "post": {+--                 "requestBody": {+--                     "content": {+--                         "application/json;charset=utf-8": {+--                             "schema": {+--                                 "$ref": "#/components/schemas/User"+--                             }+--                         }+--                     }+--                 },+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "$ref": "#/components/schemas/UserId"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     },+--                     "400": {+--                         "description": "Invalid `body`"+--                     }+--                 }+--             }+--         },+--         "/{user_id}": {+--             "get": {+--                 "parameters": [+--                     {+--                         "in": "path",+--                         "name": "user_id",+--                         "required": true,+--                         "schema": {+--                             "type": "integer"+--                         }+--                     }+--                 ],+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "$ref": "#/components/schemas/User"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     },+--                     "404": {+--                         "description": "`user_id` not found"+--                     }+--                 }+--             }+--         }+--     },+--     "servers": [+--         {+--             "url": "https://example.com"+--         }+--     ]+-- } -- -- It is also useful to annotate or modify certain endpoints. -- @'subOperations'@ provides a convenient way to zoom into a part of an API.@@ -134,11 +356,136 @@ -- >>> let getOps  = subOperations (Proxy :: Proxy (GetUsers :<|> GetUser)) (Proxy :: Proxy UserAPI) -- >>> let postOps = subOperations (Proxy :: Proxy PostUser) (Proxy :: Proxy UserAPI) -- >>> :{--- BSL8.putStrLn $ encode $ toOpenApi (Proxy :: Proxy UserAPI)+-- BSL8.putStrLn $ encodePretty $ toOpenApi (Proxy :: Proxy UserAPI) --   & applyTagsFor getOps  ["get"  & description ?~ "GET operations"] --   & applyTagsFor postOps ["post" & description ?~ "POST operations"] -- :}--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/":{"get":{"tags":["get"],"responses":{"200":{"content":{"application/json;charset=utf-8":{"schema":{"items":{"$ref":"#/components/schemas/User"},"type":"array"}}},"description":""}}},"post":{"tags":["post"],"requestBody":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/User"}}}},"responses":{"400":{"description":"Invalid `body`"},"200":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/UserId"}}},"description":""}}}},"/{user_id}":{"get":{"tags":["get"],"parameters":[{"required":true,"schema":{"type":"integer"},"in":"path","name":"user_id"}],"responses":{"404":{"description":"`user_id` not found"},"200":{"content":{"application/json;charset=utf-8":{"schema":{"$ref":"#/components/schemas/User"}}},"description":""}}}}},"components":{"schemas":{"User":{"required":["name","age"],"type":"object","properties":{"age":{"maximum":9223372036854775807,"minimum":-9223372036854775808,"type":"integer"},"name":{"type":"string"}}},"UserId":{"type":"integer"}}},"tags":[{"name":"get","description":"GET operations"},{"name":"post","description":"POST operations"}]}+-- {+--     "components": {+--         "schemas": {+--             "User": {+--                 "properties": {+--                     "age": {+--                         "maximum": 9223372036854775807,+--                         "minimum": -9223372036854775808,+--                         "type": "integer"+--                     },+--                     "name": {+--                         "type": "string"+--                     }+--                 },+--                 "required": [+--                     "name",+--                     "age"+--                 ],+--                 "type": "object"+--             },+--             "UserId": {+--                 "type": "integer"+--             }+--         }+--     },+--     "info": {+--         "title": "",+--         "version": ""+--     },+--     "openapi": "3.0.0",+--     "paths": {+--         "/": {+--             "get": {+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "items": {+--                                         "$ref": "#/components/schemas/User"+--                                     },+--                                     "type": "array"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     }+--                 },+--                 "tags": [+--                     "get"+--                 ]+--             },+--             "post": {+--                 "requestBody": {+--                     "content": {+--                         "application/json;charset=utf-8": {+--                             "schema": {+--                                 "$ref": "#/components/schemas/User"+--                             }+--                         }+--                     }+--                 },+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "$ref": "#/components/schemas/UserId"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     },+--                     "400": {+--                         "description": "Invalid `body`"+--                     }+--                 },+--                 "tags": [+--                     "post"+--                 ]+--             }+--         },+--         "/{user_id}": {+--             "get": {+--                 "parameters": [+--                     {+--                         "in": "path",+--                         "name": "user_id",+--                         "required": true,+--                         "schema": {+--                             "type": "integer"+--                         }+--                     }+--                 ],+--                 "responses": {+--                     "200": {+--                         "content": {+--                             "application/json;charset=utf-8": {+--                                 "schema": {+--                                     "$ref": "#/components/schemas/User"+--                                 }+--                             }+--                         },+--                         "description": ""+--                     },+--                     "404": {+--                         "description": "`user_id` not found"+--                     }+--                 },+--                 "tags": [+--                     "get"+--                 ]+--             }+--         }+--     },+--     "tags": [+--         {+--             "description": "GET operations",+--             "name": "get"+--         },+--         {+--             "description": "POST operations",+--             "name": "post"+--         }+--     ]+-- } -- -- This applies @\"get\"@ tag to the @GET@ endpoints and @\"post\"@ tag to the @POST@ endpoint of the User API. 
src/Servant/OpenApi/Internal.hs view
@@ -32,6 +32,7 @@ import           Data.Singletons.Bool import           Data.Text                  (Text) import qualified Data.Text                  as Text+import           Data.Typeable              (Typeable) import           GHC.TypeLits import           Network.HTTP.Media         (MediaType) import           Servant.API@@ -234,7 +235,7 @@         , _openApiExternalDocs = _openApiExternalDocs s <|> _openApiExternalDocs t         } -instance ToSchema a => ToSchema (WithStatus s a) where+instance (Typeable (WithStatus s a), ToSchema a) => ToSchema (WithStatus s a) where   declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a) #endif 
src/Servant/OpenApi/Internal/Orphans.hs view
@@ -1,14 +1,17 @@ {-# LANGUAGE ScopedTypeVariables #-}+{-# LANGUAGE FlexibleContexts #-}+ {-# OPTIONS_GHC -fno-warn-orphans #-} module Servant.OpenApi.Internal.Orphans where  import Data.OpenApi import Data.Proxy            (Proxy (..))+import Data.Typeable         (Typeable) import Servant.Types.SourceT (SourceT)  -- | Pretend that 'SourceT m a' is '[a]'. -- -- @since 1.1.7 ---instance ToSchema a => ToSchema (SourceT m a) where+instance (Typeable (SourceT m a), ToSchema a) => ToSchema (SourceT m a) where     declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [a])
src/Servant/OpenApi/Internal/Test.hs view
@@ -7,7 +7,8 @@ module Servant.OpenApi.Internal.Test where  import           Data.Aeson                     (ToJSON (..))-import           Data.Aeson.Encode.Pretty       (encodePretty)+import qualified Data.Aeson.Encode.Pretty       as P+import qualified Data.ByteString.Lazy           as BSL import           Data.OpenApi                   (Pattern, ToSchema, toSchema) import           Data.OpenApi.Schema.Validation import           Data.Text                      (Text)@@ -156,19 +157,19 @@ -- <BLANKLINE> -- OpenApi Schema: -- {---     "required": [---         "name",---         "phone"---     ],---     "type": "object", --     "properties": {---         "phone": {---             "type": "integer"---         }, --         "name": { --             "type": "string"+--         },+--         "phone": {+--             "type": "integer" --         }---     }+--     },+--     "required": [+--         "name",+--         "phone"+--     ],+--     "type": "object" -- } -- <BLANKLINE> --@@ -198,3 +199,6 @@ maybeCounterExample :: Maybe String -> Property maybeCounterExample Nothing  = property True maybeCounterExample (Just s) = counterexample s (property False)++encodePretty :: ToJSON a => a -> BSL.ByteString+encodePretty = P.encodePretty' $ P.defConfig { P.confCompare = P.compare }