openapi3 3.0.2.0 → 3.1.0
raw patch · 11 files changed
+715/−184 lines, 11 filesdep ~basedep ~hashabledep ~hspec
Dependency ranges changed: base, hashable, hspec, template-haskell
Files
- CHANGELOG.md +12/−0
- openapi3.cabal +7/−8
- src/Data/OpenApi.hs +140/−20
- src/Data/OpenApi/Internal.hs +35/−12
- src/Data/OpenApi/Internal/ParamSchema.hs +29/−11
- src/Data/OpenApi/Internal/Schema.hs +301/−97
- src/Data/OpenApi/Internal/Schema/Validation.hs +15/−15
- src/Data/OpenApi/Internal/Utils.hs +5/−0
- src/Data/OpenApi/Operation.hs +117/−10
- src/Data/OpenApi/Optics.hs +54/−9
- src/Data/OpenApi/Schema.hs +0/−2
CHANGELOG.md view
@@ -1,3 +1,15 @@+Unreleased+----------++3.1.0+-----++- Use `format: date-time` for `ToParamSchema ZonedTime` instance (see+ [#20](https://github.com/biocad/openapi3/pull/20)).+- Support generating schema for any polymorphic types via `Typeable` (see+ [#19](https://github.com/biocad/openapi3/pull/19)).+- Allow `hashable-1.3.1`, prettify doctests (see [#18](https://github.com/biocad/openapi3/pull/18)).+ 3.0.2.0 -------
openapi3.cabal view
@@ -1,6 +1,6 @@ cabal-version: >=1.10 name: openapi3-version: 3.0.2.0+version: 3.1.0 synopsis: OpenAPI 3.0 data model category: Web, Swagger, OpenApi@@ -25,8 +25,9 @@ tested-with: GHC ==8.4.4 || ==8.6.5- || ==8.8.3- || ==8.10.1+ || ==8.8.4+ || ==8.10.4+ || ==9.0.1 custom-setup setup-depends:@@ -57,10 +58,10 @@ -- GHC boot libraries build-depends:- base >=4.11.1.0 && <4.15+ base >=4.11.1.0 && <4.16 , bytestring >=0.10.8.2 && <0.11 , containers >=0.5.11.0 && <0.7- , template-haskell >=2.13.0.0 && <2.17+ , template-haskell >=2.13.0.0 && <2.18 , time >=1.8.0.2 && <1.10 , transformers >=0.5.5.0 && <0.6 @@ -76,9 +77,7 @@ -- cookie 0.4.3 is needed by GHC 7.8 due to time>=1.4 constraint , cookie >=0.4.3 && <0.5 , generics-sop >=0.5.1.0 && <0.6- -- <1.3.1 is a temporary measure, until we fix doctests not to depend on key order- -- see https://github.com/haskell/aeson/issues/837- , hashable >=1.2.7.0 && <1.3.1+ , hashable >=1.2.7.0 && <1.4 , http-media >=0.8.0.0 && <0.9 , insert-ordered-containers >=0.2.3 && <0.3 , lens >=4.16.1 && <5.1
src/Data/OpenApi.hs view
@@ -137,6 +137,7 @@ -- >>> import Data.Proxy -- >>> import GHC.Generics -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL+-- >>> import Data.OpenApi.Internal.Utils -- >>> :set -XDeriveGeneric -- >>> :set -XOverloadedStrings -- >>> :set -XOverloadedLists@@ -153,8 +154,16 @@ -- -- In this library you can use @'mempty'@ for a default/empty value. For instance: ----- >>> BSL.putStrLn $ encode (mempty :: OpenApi)--- {"openapi":"3.0.0","info":{"version":"","title":""},"components":{}}+-- >>> BSL.putStrLn $ encodePretty (mempty :: OpenApi)+-- {+-- "components": {},+-- "info": {+-- "title": "",+-- "version": ""+-- },+-- "openapi": "3.0.0",+-- "paths": {}+-- } -- -- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty. -- That is because these properties are actually required ones.@@ -162,13 +171,20 @@ -- You /should/ always override the default (empty) value for these properties, -- although it is not strictly necessary: ----- >>> BSL.putStrLn $ encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }--- {"version":"1.0","title":"Todo API"}+-- >>> BSL.putStrLn $ encodePretty mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }+-- {+-- "title": "Todo API",+-- "version": "1.0"+-- } -- -- You can merge two values using @'mappend'@ or its infix version @('<>')@: ----- >>> BSL.putStrLn $ encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }--- {"version":"1.0","title":"Todo API"}+-- >>> BSL.putStrLn $ encodePretty $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }+-- {+-- "title": "Todo API",+-- "version": "1.0"+-- }+ -- -- This can be useful for combining specifications of endpoints into a whole API specification: --@@ -194,14 +210,48 @@ -- make it fairly simple to construct/modify any part of the specification: -- -- >>> :{--- BSL.putStrLn $ encode $ (mempty :: OpenApi)+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi) -- & components . schemas .~ [ ("User", mempty & type_ ?~ OpenApiString) ] -- & paths .~ -- [ ("/user", mempty & get ?~ (mempty -- & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User"))) -- & at 404 ?~ "User info not found")) ] -- :}--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"User info not found"},"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"OK"}}}}},"components":{"schemas":{"User":{"type":"string"}}}}+-- {+-- "components": {+-- "schemas": {+-- "User": {+-- "type": "string"+-- }+-- }+-- },+-- "info": {+-- "title": "",+-- "version": ""+-- },+-- "openapi": "3.0.0",+-- "paths": {+-- "/user": {+-- "get": {+-- "responses": {+-- "200": {+-- "content": {+-- "application/json": {+-- "schema": {+-- "$ref": "#/components/schemas/User"+-- }+-- }+-- },+-- "description": "OK"+-- },+-- "404": {+-- "description": "User info not found"+-- }+-- }+-- }+-- }+-- }+-- } -- -- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@ -- which produces @application/json@ output. It should respond with code @200@ and body specified@@ -213,23 +263,34 @@ -- common field is @'description'@. Many components of a Swagger specification -- can have descriptions, and you can use the same name for them: ----- >>> BSL.putStrLn $ encode $ (mempty :: Response) & description .~ "No content"--- {"description":"No content"}+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & description .~ "No content"+-- {+-- "description": "No content"+-- } -- >>> :{--- BSL.putStrLn $ encode $ (mempty :: Schema)+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema) -- & type_ ?~ OpenApiBoolean -- & description ?~ "To be or not to be" -- :}--- {"type":"boolean","description":"To be or not to be"}+-- {+-- "description": "To be or not to be",+-- "type": "boolean"+-- } -- -- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@ -- have direct access to it via @'at' code@. Example: -- -- >>> :{--- BSL.putStrLn $ encode $ (mempty :: Operation)+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation) -- & at 404 ?~ "Not found" -- :}--- {"responses":{"404":{"description":"Not found"}}}+-- {+-- "responses": {+-- "404": {+-- "description": "Not found"+-- }+-- }+-- } -- -- You might've noticed that @'type_'@ has an extra underscore in its name -- compared to, say, @'description'@ field accessor.@@ -276,10 +337,27 @@ -- >>> data Person = Person { name :: String, age :: Integer } deriving Generic -- >>> instance ToJSON Person -- >>> instance ToSchema Person--- >>> BSL.putStrLn $ encode (Person "David" 28)--- {"age":28,"name":"David"}--- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Person)--- {"required":["name","age"],"type":"object","properties":{"age":{"type":"integer"},"name":{"type":"string"}}}+-- >>> BSL.putStrLn $ encodePretty (Person "David" 28)+-- {+-- "age": 28,+-- "name": "David"+-- }+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Person)+-- {+-- "properties": {+-- "age": {+-- "type": "integer"+-- },+-- "name": {+-- "type": "string"+-- }+-- },+-- "required": [+-- "name",+-- "age"+-- ],+-- "type": "object"+-- } -- -- This package implements OpenAPI 3.0 spec, which supports @oneOf@ in schemas, allowing any sum types -- to be faithfully represented. All sum encodings supported by @aeson@ are supported here as well, with@@ -290,8 +368,50 @@ -- >>> data Error = ErrorNoUser { userId :: Int } | ErrorAccessDenied { requiredPermission :: String } deriving Generic -- >>> instance ToJSON Error -- >>> instance ToSchema Error--- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Error)--- {"oneOf":[{"required":["userId","tag"],"type":"object","properties":{"tag":{"type":"string","enum":["ErrorNoUser"]},"userId":{"maximum":9223372036854775807,"minimum":-9223372036854775808,"type":"integer"}}},{"required":["requiredPermission","tag"],"type":"object","properties":{"tag":{"type":"string","enum":["ErrorAccessDenied"]},"requiredPermission":{"type":"string"}}}],"type":"object"}+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Error)+-- {+-- "oneOf": [+-- {+-- "properties": {+-- "tag": {+-- "enum": [+-- "ErrorNoUser"+-- ],+-- "type": "string"+-- },+-- "userId": {+-- "maximum": 9223372036854775807,+-- "minimum": -9223372036854775808,+-- "type": "integer"+-- }+-- },+-- "required": [+-- "userId",+-- "tag"+-- ],+-- "type": "object"+-- },+-- {+-- "properties": {+-- "requiredPermission": {+-- "type": "string"+-- },+-- "tag": {+-- "enum": [+-- "ErrorAccessDenied"+-- ],+-- "type": "string"+-- }+-- },+-- "required": [+-- "requiredPermission",+-- "tag"+-- ],+-- "type": "object"+-- }+-- ],+-- "type": "object"+-- } -- $manipulation -- Sometimes you have to work with an imported or generated @'Swagger'@.
src/Data/OpenApi/Internal.hs view
@@ -60,6 +60,8 @@ -- $setup -- >>> :seti -XDataKinds -- >>> import Data.Aeson+-- >>> import Data.ByteString.Lazy.Char8 as BSL+-- >>> import Data.OpenApi.Internal.Utils -- | A list of definitions that can be used in references. type Definitions = InsOrdHashMap Text@@ -848,20 +850,37 @@ -- | ----- >>> encode (SecuritySchemeHttp (HttpSchemeBearer Nothing))--- "{\"scheme\":\"bearer\",\"type\":\"http\"}"+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer Nothing))+-- {+-- "scheme": "bearer",+-- "type": "http"+-- } ----- >>> encode (SecuritySchemeHttp (HttpSchemeBearer (Just "jwt")))--- "{\"scheme\":\"bearer\",\"type\":\"http\",\"bearerFormat\":\"jwt\"}"+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeBearer (Just "jwt")))+-- {+-- "bearerFormat": "jwt",+-- "scheme": "bearer",+-- "type": "http"+-- } ----- >>> encode (SecuritySchemeHttp HttpSchemeBasic)--- "{\"scheme\":\"basic\",\"type\":\"http\"}"+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp HttpSchemeBasic)+-- {+-- "scheme": "basic",+-- "type": "http"+-- } ----- >>> encode (SecuritySchemeHttp (HttpSchemeCustom "CANARY"))--- "{\"scheme\":\"CANARY\",\"type\":\"http\"}"+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeHttp (HttpSchemeCustom "CANARY"))+-- {+-- "scheme": "CANARY",+-- "type": "http"+-- } ----- >>> encode (SecuritySchemeApiKey (ApiKeyParams "id" ApiKeyCookie))--- "{\"in\":\"cookie\",\"name\":\"id\",\"type\":\"apiKey\"}"+-- >>> BSL.putStrLn $ encodePretty (SecuritySchemeApiKey (ApiKeyParams "id" ApiKeyCookie))+-- {+-- "in": "cookie",+-- "name": "id",+-- "type": "apiKey"+-- } -- data SecuritySchemeType = SecuritySchemeHttp HttpSchemeType@@ -1305,8 +1324,12 @@ -- | As for nullary schema for 0-arity type constructors, see -- <https://github.com/GetShopTV/swagger2/issues/167>. ----- >>> encode (OpenApiItemsArray [])--- "{\"example\":[],\"items\":{},\"maxItems\":0}"+-- >>> BSL.putStrLn $ encodePretty (OpenApiItemsArray [])+-- {+-- "example": [],+-- "items": {},+-- "maxItems": 0+-- } -- instance ToJSON OpenApiItems where toJSON (OpenApiItemsObject x) = object [ "items" .= x ]
src/Data/OpenApi/Internal/ParamSchema.hs view
@@ -46,7 +46,7 @@ import Data.OpenApi.SchemaOptions import qualified Data.ByteString as BS-import qualified Data.ByteString.Lazy as BSL+import qualified Data.ByteString.Lazy.Char8 as BSL import GHC.TypeLits (TypeError, ErrorMessage(..)) -- | Default schema for binary data (any sequence of octets).@@ -112,8 +112,10 @@ class ToParamSchema a where -- | Convert a type into a plain parameter schema. --- -- >>> encode $ toParamSchema (Proxy :: Proxy Integer)- -- "{\"type\":\"integer\"}"+ -- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy Integer)+ -- {+ -- "type": "integer"+ -- } toParamSchema :: Proxy a -> Schema default toParamSchema :: (Generic a, GToParamSchema (Rep a)) => Proxy a -> Schema toParamSchema = genericToParamSchema defaultSchemaOptions@@ -155,8 +157,12 @@ -- | Default plain schema for @'Bounded'@, @'Integral'@ types. ----- >>> encode $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)--- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"+-- >>> BSL.putStrLn $ encodePretty $ toParamSchemaBoundedIntegral (Proxy :: Proxy Int8)+-- {+-- "maximum": 127,+-- "minimum": -128,+-- "type": "integer"+-- } toParamSchemaBoundedIntegral :: forall a t. (Bounded a, Integral a) => Proxy a -> Schema toParamSchemaBoundedIntegral _ = mempty & type_ ?~ OpenApiInteger@@ -210,9 +216,9 @@ -- | -- >>> toParamSchema (Proxy :: Proxy ZonedTime) ^. format--- Just "yyyy-mm-ddThh:MM:ss+hhMM"+-- Just "date-time" instance ToParamSchema ZonedTime where- toParamSchema _ = timeParamSchema "yyyy-mm-ddThh:MM:ss+hhMM"+ toParamSchema _ = timeParamSchema "date-time" -- | -- >>> toParamSchema (Proxy :: Proxy UTCTime) ^. format@@ -275,8 +281,13 @@ toParamSchema _ = toParamSchema (Proxy :: Proxy (Set a)) -- |--- >>> encode $ toParamSchema (Proxy :: Proxy ())--- "{\"type\":\"string\",\"enum\":[\"_\"]}"+-- >>> BSL.putStrLn $ encodePretty $ toParamSchema (Proxy :: Proxy ())+-- {+-- "enum": [+-- "_"+-- ],+-- "type": "string"+-- } instance ToParamSchema () where toParamSchema _ = mempty & type_ ?~ OpenApiString@@ -291,8 +302,14 @@ -- -- >>> :set -XDeriveGeneric -- >>> data Color = Red | Blue deriving Generic--- >>> encode $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)--- "{\"type\":\"string\",\"enum\":[\"Red\",\"Blue\"]}"+-- >>> BSL.putStrLn $ encodePretty $ genericToParamSchema defaultSchemaOptions (Proxy :: Proxy Color)+-- {+-- "enum": [+-- "Red",+-- "Blue"+-- ],+-- "type": "string"+-- } genericToParamSchema :: forall a t. (Generic a, GToParamSchema (Rep a)) => SchemaOptions -> Proxy a -> Schema genericToParamSchema opts _ = gtoParamSchema opts (Proxy :: Proxy (Rep a)) mempty @@ -334,3 +351,4 @@ -- $setup -- >>> import Data.Aeson (encode)+-- >>> import Data.OpenApi.Internal.Utils
src/Data/OpenApi/Internal/Schema.hs view
@@ -42,6 +42,7 @@ import Data.Int import Data.IntSet (IntSet) import Data.IntMap (IntMap)+import Data.List (sort) import Data.List.NonEmpty.Compat (NonEmpty) import Data.Map (Map) import Data.Maybe (fromMaybe)@@ -134,7 +135,7 @@ -- -- instance ToSchema Coord -- @-class ToSchema a where+class Typeable a => ToSchema a where -- | Convert a type into an optionally named schema -- together with all used definitions. -- Note that the schema itself is included in definitions@@ -156,13 +157,19 @@ -- -- >>> toNamedSchema (Proxy :: Proxy String) ^. name -- Nothing--- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy String) ^. schema)--- {"type":"string"}+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy String) ^. schema)+-- {+-- "type": "string"+-- } -- -- >>> toNamedSchema (Proxy :: Proxy Day) ^. name -- Just "Day"--- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)--- {"example":"2016-07-22","format":"date","type":"string"}+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy Day) ^. schema)+-- {+-- "example": "2016-07-22",+-- "format": "date",+-- "type": "string"+-- } toNamedSchema :: ToSchema a => Proxy a -> NamedSchema toNamedSchema = undeclare . declareNamedSchema @@ -178,22 +185,35 @@ -- | Convert a type into a schema. ----- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Int8)--- {"maximum":127,"minimum":-128,"type":"integer"}+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Int8)+-- {+-- "maximum": 127,+-- "minimum": -128,+-- "type": "integer"+-- } ----- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy [Day])--- {"items":{"$ref":"#/components/schemas/Day"},"type":"array"}+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy [Day])+-- {+-- "items": {+-- "$ref": "#/components/schemas/Day"+-- },+-- "type": "array"+-- } toSchema :: ToSchema a => Proxy a -> Schema toSchema = _namedSchemaSchema . toNamedSchema -- | Convert a type into a referenced schema if possible. -- Only named schemas can be referenced, nameless schemas are inlined. ----- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Integer)--- {"type":"integer"}+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Integer)+-- {+-- "type": "integer"+-- } ----- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Day)--- {"$ref":"#/components/schemas/Day"}+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Day)+-- {+-- "$ref": "#/components/schemas/Day"+-- } toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema toSchemaRef = undeclare . declareSchemaRef @@ -261,8 +281,15 @@ -- | Convert a type into a schema without references. ----- >>> BSL.putStrLn $ encode $ toInlinedSchema (Proxy :: Proxy [Day])--- {"items":{"example":"2016-07-22","format":"date","type":"string"},"type":"array"}+-- >>> BSL.putStrLn $ encodePretty $ toInlinedSchema (Proxy :: Proxy [Day])+-- {+-- "items": {+-- "example": "2016-07-22",+-- "format": "date",+-- "type": "string"+-- },+-- "type": "array"+-- } -- -- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema -- when inlining recursive schemas.@@ -295,19 +322,64 @@ -- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance. -- Produced schema can be used for further refinement. ----- >>> BSL.putStrLn $ encode $ sketchSchema "hello"--- {"example":"hello","type":"string"}+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema "hello"+-- {+-- "example": "hello",+-- "type": "string"+-- } ----- >>> BSL.putStrLn $ encode $ sketchSchema (1, 2, 3)--- {"example":[1,2,3],"items":{"type":"number"},"type":"array"}+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (1, 2, 3)+-- {+-- "example": [+-- 1,+-- 2,+-- 3+-- ],+-- "items": {+-- "type": "number"+-- },+-- "type": "array"+-- } ----- >>> BSL.putStrLn $ encode $ sketchSchema ("Jack", 25)--- {"example":["Jack",25],"items":[{"type":"string"},{"type":"number"}],"type":"array"}+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema ("Jack", 25)+-- {+-- "example": [+-- "Jack",+-- 25+-- ],+-- "items": [+-- {+-- "type": "string"+-- },+-- {+-- "type": "number"+-- }+-- ],+-- "type": "array"+-- } -- -- >>> data Person = Person { name :: String, age :: Int } deriving (Generic) -- >>> instance ToJSON Person--- >>> BSL.putStrLn $ encode $ sketchSchema (Person "Jack" 25)--- {"example":{"age":25,"name":"Jack"},"required":["age","name"],"type":"object","properties":{"age":{"type":"number"},"name":{"type":"string"}}}+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (Person "Jack" 25)+-- {+-- "example": {+-- "age": 25,+-- "name": "Jack"+-- },+-- "properties": {+-- "age": {+-- "type": "number"+-- },+-- "name": {+-- "type": "string"+-- }+-- },+-- "required": [+-- "age",+-- "name"+-- ],+-- "type": "object"+-- } sketchSchema :: ToJSON a => a -> Schema sketchSchema = sketch . toJSON where@@ -333,25 +405,139 @@ _ -> Nothing go (Object o) = mempty & type_ ?~ OpenApiObject- & required .~ HashMap.keys o+ & required .~ sort (HashMap.keys o) & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o) -- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance. -- Produced schema uses as much constraints as possible. ----- >>> BSL.putStrLn $ encode $ sketchStrictSchema "hello"--- {"maxLength":5,"pattern":"hello","minLength":5,"type":"string","enum":["hello"]}+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema "hello"+-- {+-- "enum": [+-- "hello"+-- ],+-- "maxLength": 5,+-- "minLength": 5,+-- "pattern": "hello",+-- "type": "string"+-- } ----- >>> BSL.putStrLn $ encode $ sketchStrictSchema (1, 2, 3)--- {"minItems":3,"uniqueItems":true,"items":[{"maximum":1,"minimum":1,"multipleOf":1,"type":"number","enum":[1]},{"maximum":2,"minimum":2,"multipleOf":2,"type":"number","enum":[2]},{"maximum":3,"minimum":3,"multipleOf":3,"type":"number","enum":[3]}],"maxItems":3,"type":"array","enum":[[1,2,3]]}+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (1, 2, 3)+-- {+-- "enum": [+-- [+-- 1,+-- 2,+-- 3+-- ]+-- ],+-- "items": [+-- {+-- "enum": [+-- 1+-- ],+-- "maximum": 1,+-- "minimum": 1,+-- "multipleOf": 1,+-- "type": "number"+-- },+-- {+-- "enum": [+-- 2+-- ],+-- "maximum": 2,+-- "minimum": 2,+-- "multipleOf": 2,+-- "type": "number"+-- },+-- {+-- "enum": [+-- 3+-- ],+-- "maximum": 3,+-- "minimum": 3,+-- "multipleOf": 3,+-- "type": "number"+-- }+-- ],+-- "maxItems": 3,+-- "minItems": 3,+-- "type": "array",+-- "uniqueItems": true+-- } ----- >>> BSL.putStrLn $ encode $ sketchStrictSchema ("Jack", 25)--- {"minItems":2,"uniqueItems":true,"items":[{"maxLength":4,"pattern":"Jack","minLength":4,"type":"string","enum":["Jack"]},{"maximum":25,"minimum":25,"multipleOf":25,"type":"number","enum":[25]}],"maxItems":2,"type":"array","enum":[["Jack",25]]}+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema ("Jack", 25)+-- {+-- "enum": [+-- [+-- "Jack",+-- 25+-- ]+-- ],+-- "items": [+-- {+-- "enum": [+-- "Jack"+-- ],+-- "maxLength": 4,+-- "minLength": 4,+-- "pattern": "Jack",+-- "type": "string"+-- },+-- {+-- "enum": [+-- 25+-- ],+-- "maximum": 25,+-- "minimum": 25,+-- "multipleOf": 25,+-- "type": "number"+-- }+-- ],+-- "maxItems": 2,+-- "minItems": 2,+-- "type": "array",+-- "uniqueItems": true+-- } -- -- >>> data Person = Person { name :: String, age :: Int } deriving (Generic) -- >>> instance ToJSON Person--- >>> BSL.putStrLn $ encode $ sketchStrictSchema (Person "Jack" 25)--- {"minProperties":2,"required":["age","name"],"maxProperties":2,"type":"object","enum":[{"age":25,"name":"Jack"}],"properties":{"age":{"maximum":25,"minimum":25,"multipleOf":25,"type":"number","enum":[25]},"name":{"maxLength":4,"pattern":"Jack","minLength":4,"type":"string","enum":["Jack"]}}}+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (Person "Jack" 25)+-- {+-- "enum": [+-- {+-- "age": 25,+-- "name": "Jack"+-- }+-- ],+-- "maxProperties": 2,+-- "minProperties": 2,+-- "properties": {+-- "age": {+-- "enum": [+-- 25+-- ],+-- "maximum": 25,+-- "minimum": 25,+-- "multipleOf": 25,+-- "type": "number"+-- },+-- "name": {+-- "enum": [+-- "Jack"+-- ],+-- "maxLength": 4,+-- "minLength": 4,+-- "pattern": "Jack",+-- "type": "string"+-- }+-- },+-- "required": [+-- "age",+-- "name"+-- ],+-- "type": "object"+-- } sketchStrictSchema :: ToJSON a => a -> Schema sketchStrictSchema = go . toJSON where@@ -383,7 +569,7 @@ allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs)) go js@(Object o) = mempty & type_ ?~ OpenApiObject- & required .~ names+ & required .~ sort names & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o) & maxProperties ?~ fromIntegral (length names) & minProperties ?~ fromIntegral (length names)@@ -424,7 +610,7 @@ instance ToSchema Double where declareNamedSchema = plain . paramSchemaToSchema instance ToSchema Float where declareNamedSchema = plain . paramSchemaToSchema -instance HasResolution a => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema+instance (Typeable (Fixed a), HasResolution a) => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema instance ToSchema a => ToSchema (Maybe a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)@@ -441,12 +627,18 @@ declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p & example ?~ toJSON (UUID.toText UUID.nil) -instance (ToSchema a, ToSchema b) => ToSchema (a, b)-instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c)-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d)-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e)-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f)-instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g)+instance (ToSchema a, ToSchema b) => ToSchema (a, b) where+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions+instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c) where+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d) where+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e) where+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f) where+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g) where+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions timeSchema :: T.Text -> Schema timeSchema fmt = mempty@@ -465,7 +657,7 @@ declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss" & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) --- | Format @"date"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.+-- | Format @"date-time"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format. instance ToSchema ZonedTime where declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time" & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))@@ -497,7 +689,7 @@ instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int)) -- | NOTE: This schema does not account for the uniqueness of keys.-instance ToSchema a => ToSchema (IntMap a) where+instance (ToSchema a) => ToSchema (IntMap a) where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)]) instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where@@ -553,8 +745,12 @@ -- | Default schema for @'Bounded'@, @'Integral'@ types. ----- >>> BSL.putStrLn $ encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)--- {"maximum":32767,"minimum":-32768,"type":"integer"}+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)+-- {+-- "maximum": 32767,+-- "minimum": -32768,+-- "type": "integer"+-- } toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema toSchemaBoundedIntegral _ = mempty & type_ ?~ OpenApiInteger@@ -586,8 +782,27 @@ -- >>> instance ToSchema ButtonState -- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show) -- >>> type ImageUrl = T.Text--- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))--- {"type":"object","properties":{"Focus":{"type":"string"},"Disabled":{"type":"string"},"Active":{"type":"string"},"Neutral":{"type":"string"},"Hover":{"type":"string"}}}+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))+-- {+-- "properties": {+-- "Active": {+-- "type": "string"+-- },+-- "Disabled": {+-- "type": "string"+-- },+-- "Focus": {+-- "type": "string"+-- },+-- "Hover": {+-- "type": "string"+-- },+-- "Neutral": {+-- "type": "string"+-- }+-- },+-- "type": "object"+-- } -- -- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'. -- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.@@ -614,8 +829,27 @@ -- >>> instance ToSchema ButtonState -- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show) -- >>> type ImageUrl = T.Text--- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))--- {"type":"object","properties":{"Focus":{"type":"string"},"Disabled":{"type":"string"},"Active":{"type":"string"},"Neutral":{"type":"string"},"Hover":{"type":"string"}}}+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))+-- {+-- "properties": {+-- "Active": {+-- "type": "string"+-- },+-- "Disabled": {+-- "type": "string"+-- },+-- "Focus": {+-- "type": "string"+-- },+-- "Hover": {+-- "type": "string"+-- },+-- "Neutral": {+-- "type": "string"+-- }+-- },+-- "type": "object"+-- } -- -- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'. -- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.@@ -625,7 +859,7 @@ toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping -- | A configurable generic @'Schema'@ creator.-genericDeclareSchema :: (Generic a, GToSchema (Rep a)) =>+genericDeclareSchema :: (Generic a, GToSchema (Rep a), Typeable a) => SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchema opts proxy @@ -633,10 +867,25 @@ -- This function applied to @'defaultSchemaOptions'@ -- is used as the default for @'declareNamedSchema'@ -- when the type is an instance of @'Generic'@.-genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a)) =>+--+-- Default implementation will use the name from 'Typeable' instance, including concrete+-- instantioations of type variables.+--+-- For example:+--+-- >>> _namedSchemaName $ undeclare $ genericDeclareNamedSchema defaultSchemaOptions (Proxy :: Proxy (Either Int Bool))+-- Just "Either_Int_Bool"+genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a), Typeable a) => SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema-genericDeclareNamedSchema opts _ = gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty+genericDeclareNamedSchema opts _ =+ rename (Just $ T.pack name) <$> gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty+ where+ unspace ' ' = '_'+ unspace x = x+ orig = fmap unspace $ show $ typeRep @a+ name = datatypeNameModifier opts orig + -- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'. genericNameSchema :: forall a d f. (Generic a, Rep a ~ D1 d f, Datatype d)@@ -861,58 +1110,13 @@ data Proxy3 a b c = Proxy3 --- | This class allows to generate schemas for polymorphic types (of kind @Type -> Type@).------ Intended usage:------ >>> data Foo a = Foo { foo :: a, bar :: Int } deriving (Eq, Show, Generic, ToSchema1)--- >>> :{--- instance (ToSchema a, Typeable a) => ToSchema (Foo a) where--- declareNamedSchema _ = declareNamedSchema @(BySchema1 Foo a) Proxy--- :}------ >>> toNamedSchema @(Foo Int) Proxy ^. name--- Just "Foo_Int"--- >>> toNamedSchema @(Foo Bool) Proxy ^. name--- Just "Foo_Bool"--- >>> toNamedSchema @(Foo (Foo T.Text)) Proxy ^. name--- Just "Foo_(Foo_Text)"-class ToSchema1 (f :: * -> *) where- declareNamedSchema1 :: (Generic (f a), GToSchema (Rep (f a)), ToSchema a) => Proxy f -> Proxy a -> Declare (Definitions Schema) NamedSchema-- -- It would be cleaner to have GToSchema constraint only on default signature and not in the class method- -- above, however sadly GHC does not like it.- default declareNamedSchema1 :: forall a. (ToSchema a, Generic (f a), GToSchema (Rep (f a))) => Proxy f -> Proxy a -> Declare (Definitions Schema) NamedSchema- declareNamedSchema1 _ _ = genericDeclareNamedSchema @(f a) defaultSchemaOptions Proxy--{- | For GHC 8.6+ it's more convenient to use @DerivingVia@ to derive instances of 'ToSchema'-using 'ToSchema1' instance, like this:--#if __GLASGOW_HASKELL__ >= 806->>> data Foo a = Foo { foo :: a, bar :: Int } deriving (Eq, Show, Generic, ToSchema1)->>> deriving via BySchema1 Foo a instance (ToSchema a, Typeable a) => ToSchema (Foo a)-#else-> data Foo a = Foo { foo :: a, bar :: Int } deriving (Eq, Show, Generic, ToSchema1)-> deriving via BySchema1 Foo a instance (ToSchema a, Typeable a) => ToSchema (Foo a)-#endif--}-newtype BySchema1 f a = BySchema1 (f a)--instance (ToSchema1 f, Generic (f a), GToSchema (Rep (f a)), Typeable (f a), ToSchema a) => ToSchema (BySchema1 f a) where- declareNamedSchema _ = do- sch <- declareNamedSchema1 @f @a Proxy Proxy- let tName = T.replace " " "_" $ T.pack $ show $ typeRep @(f a)- return $ rename (Just tName) sch- {- $setup >>> import Data.OpenApi >>> import Data.Aeson (encode) >>> import Data.Aeson.Types (toJSONKeyText)+>>> import Data.OpenApi.Internal.Utils >>> :set -XScopedTypeVariables >>> :set -XDeriveAnyClass >>> :set -XStandaloneDeriving >>> :set -XTypeApplications-#if __GLASGOW_HASKELL__ >= 806->>> :set -XDerivingVia-#endif -}
src/Data/OpenApi/Internal/Schema/Validation.hs view
@@ -31,7 +31,6 @@ import Control.Monad (forM, forM_, when) import Data.Aeson hiding (Result)-import Data.Aeson.Encode.Pretty (encodePretty) import Data.Foldable (for_, sequenceA_, traverse_) import Data.HashMap.Strict (HashMap)@@ -50,6 +49,7 @@ import Data.OpenApi.Declare import Data.OpenApi.Internal+import Data.OpenApi.Internal.Utils import Data.OpenApi.Internal.Schema import Data.OpenApi.Lens @@ -103,33 +103,33 @@ -- <BLANKLINE> -- Swagger Schema: -- {--- "required": [--- "name",--- "phone"--- ],--- "type": "object", -- "properties": {--- "phone": {--- "$ref": "#/components/schemas/Phone"--- }, -- "name": { -- "type": "string"+-- },+-- "phone": {+-- "$ref": "#/components/schemas/Phone" -- }--- }+-- },+-- "required": [+-- "name",+-- "phone"+-- ],+-- "type": "object" -- } -- <BLANKLINE> -- Swagger Description Context: -- { -- "Phone": {--- "required": [--- "value"--- ],--- "type": "object", -- "properties": { -- "value": { -- "type": "string" -- }--- }+-- },+-- "required": [+-- "value"+-- ],+-- "type": "object" -- } -- } -- <BLANKLINE>
src/Data/OpenApi/Internal/Utils.hs view
@@ -13,6 +13,8 @@ import Control.Lens.TH import Data.Aeson import Data.Aeson.Types+import qualified Data.Aeson.Encode.Pretty as P+import qualified Data.ByteString.Lazy as BSL import Data.Char import Data.Data import Data.Hashable (Hashable)@@ -132,3 +134,6 @@ swaggerMempty = Nothing swaggerMappend x Nothing = x swaggerMappend _ y = y++encodePretty :: ToJSON a => a -> BSL.ByteString+encodePretty = P.encodePretty' $ P.defConfig { P.confCompare = P.compare }
src/Data/OpenApi/Operation.hs view
@@ -55,13 +55,16 @@ -- >>> import Data.Proxy -- >>> import Data.Time -- >>> import qualified Data.ByteString.Lazy.Char8 as BSL+-- >>> import Data.OpenApi.Internal.Utils -- | Prepend path piece to all operations of the spec. -- Leading and trailing slashes are trimmed/added automatically. -- -- >>> let api = (mempty :: OpenApi) & paths .~ [("/info", mempty)]--- >>> BSL.putStrLn $ encode $ prependPath "user/{user_id}" api ^. paths--- {"/user/{user_id}/info":{}}+-- >>> BSL.putStrLn $ encodePretty $ prependPath "user/{user_id}" api ^. paths+-- {+-- "/user/{user_id}/info": {}+-- } prependPath :: FilePath -> OpenApi -> OpenApi prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>) where@@ -82,10 +85,63 @@ -- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK" -- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)] -- >>> let sub = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)]--- >>> BSL.putStrLn $ encode api--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"200":{"description":"OK"}}},"post":{"responses":{"200":{"description":"OK"}}}}},"components":{}}--- >>> BSL.putStrLn $ encode $ api & operationsOf sub . at 404 ?~ "Not found"--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"Not found"},"200":{"description":"OK"}}},"post":{"responses":{"200":{"description":"OK"}}}}},"components":{}}+-- >>> BSL.putStrLn $ encodePretty api+-- {+-- "components": {},+-- "info": {+-- "title": "",+-- "version": ""+-- },+-- "openapi": "3.0.0",+-- "paths": {+-- "/user": {+-- "get": {+-- "responses": {+-- "200": {+-- "description": "OK"+-- }+-- }+-- },+-- "post": {+-- "responses": {+-- "200": {+-- "description": "OK"+-- }+-- }+-- }+-- }+-- }+-- }+-- >>> BSL.putStrLn $ encodePretty $ api & operationsOf sub . at 404 ?~ "Not found"+-- {+-- "components": {},+-- "info": {+-- "title": "",+-- "version": ""+-- },+-- "openapi": "3.0.0",+-- "paths": {+-- "/user": {+-- "get": {+-- "responses": {+-- "200": {+-- "description": "OK"+-- },+-- "404": {+-- "description": "Not found"+-- }+-- }+-- },+-- "post": {+-- "responses": {+-- "200": {+-- "description": "OK"+-- }+-- }+-- }+-- }+-- }+-- } operationsOf :: OpenApi -> Traversal' OpenApi Operation operationsOf sub = paths.itraversed.withIndex.subops where@@ -123,8 +179,26 @@ -- -- FIXME doc ----- >>> BSL.putStrLn $ encode $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty--- [{"Day":{"example":"2016-07-22","format":"date","type":"string"}},{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day"}}}}]+-- >>> BSL.putStrLn $ encodePretty $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty+-- [+-- {+-- "Day": {+-- "example": "2016-07-22",+-- "format": "date",+-- "type": "string"+-- }+-- },+-- {+-- "content": {+-- "application/json": {+-- "schema": {+-- "$ref": "#/components/schemas/Day"+-- }+-- }+-- },+-- "description": ""+-- }+-- ] declareResponse :: ToSchema a => MediaType -> Proxy a -> Declare (Definitions Schema) Response declareResponse cType proxy = do s <- declareSchemaRef proxy@@ -143,8 +217,41 @@ -- -- >>> let api = (mempty :: OpenApi) & paths .~ [("/user", mempty & get ?~ mempty)] -- >>> let res = declareResponse "application/json" (Proxy :: Proxy Day)--- >>> BSL.putStrLn $ encode $ api & setResponse 200 res--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day"}}},"description":""}}}}},"components":{"schemas":{"Day":{"example":"2016-07-22","format":"date","type":"string"}}}}+-- >>> BSL.putStrLn $ encodePretty $ api & setResponse 200 res+-- {+-- "components": {+-- "schemas": {+-- "Day": {+-- "example": "2016-07-22",+-- "format": "date",+-- "type": "string"+-- }+-- }+-- },+-- "info": {+-- "title": "",+-- "version": ""+-- },+-- "openapi": "3.0.0",+-- "paths": {+-- "/user": {+-- "get": {+-- "responses": {+-- "200": {+-- "content": {+-- "application/json": {+-- "schema": {+-- "$ref": "#/components/schemas/Day"+-- }+-- }+-- },+-- "description": ""+-- }+-- }+-- }+-- }+-- }+-- } -- -- See also @'setResponseWith'@. setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> OpenApi -> OpenApi
src/Data/OpenApi/Optics.hs view
@@ -22,43 +22,88 @@ -- Example from the "Data.OpenApi" module using @optics@: -- -- >>> :{--- BSL.putStrLn $ encode $ (mempty :: OpenApi)+-- BSL.putStrLn $ encodePretty $ (mempty :: OpenApi) -- & #components % #schemas .~ [ ("User", mempty & #type ?~ OpenApiString) ] -- & #paths .~ -- [ ("/user", mempty & #get ?~ (mempty -- & at 200 ?~ ("OK" & #_Inline % #content % at "application/json" ?~ (mempty & #schema ?~ Ref (Reference "User"))) -- & at 404 ?~ "User info not found")) ] -- :}--- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"User info not found"},"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/User"}}},"description":"OK"}}}}},"components":{"schemas":{"User":{"type":"string"}}}}+-- {+-- "components": {+-- "schemas": {+-- "User": {+-- "type": "string"+-- }+-- }+-- },+-- "info": {+-- "title": "",+-- "version": ""+-- },+-- "openapi": "3.0.0",+-- "paths": {+-- "/user": {+-- "get": {+-- "responses": {+-- "200": {+-- "content": {+-- "application/json": {+-- "schema": {+-- "$ref": "#/components/schemas/User"+-- }+-- }+-- },+-- "description": "OK"+-- },+-- "404": {+-- "description": "User info not found"+-- }+-- }+-- }+-- }+-- }+-- } -- -- For convenience optics are defined as /labels/. It means that field accessor -- names can be overloaded for different types. One such common field is -- @#description@. Many components of a Swagger specification can have -- descriptions, and you can use the same name for them: ----- >>> BSL.putStrLn $ encode $ (mempty :: Response) & #description .~ "No content"--- {"description":"No content"}+-- >>> BSL.putStrLn $ encodePretty $ (mempty :: Response) & #description .~ "No content"+-- {+-- "description": "No content"+-- } -- >>> :{--- BSL.putStrLn $ encode $ (mempty :: Schema)+-- BSL.putStrLn $ encodePretty $ (mempty :: Schema) -- & #type ?~ OpenApiBoolean -- & #description ?~ "To be or not to be" -- :}--- {"type":"boolean","description":"To be or not to be"}+-- {+-- "description": "To be or not to be",+-- "type": "boolean"+-- } -- -- Additionally, to simplify working with @'Response'@, both @'Operation'@ and -- @'Responses'@ have direct access to it via @'Optics.Core.At.at'@. Example: -- -- >>> :{--- BSL.putStrLn $ encode $ (mempty :: Operation)+-- BSL.putStrLn $ encodePretty $ (mempty :: Operation) -- & at 404 ?~ "Not found" -- :}--- {"responses":{"404":{"description":"Not found"}}}---+-- {+-- "responses": {+-- "404": {+-- "description": "Not found"+-- }+-- }+-- } module Data.OpenApi.Optics () where import Data.Aeson (Value) import Data.Scientific (Scientific) import Data.OpenApi.Internal+import Data.OpenApi.Internal.Utils import Data.Text (Text) import Optics.Core import Optics.TH
src/Data/OpenApi/Schema.hs view
@@ -13,8 +13,6 @@ toSchemaRef, schemaName, toInlinedSchema,- ToSchema1(..),- BySchema1(..), -- * Generic schema encoding genericDeclareNamedSchema,