git.iko.soy/typeable/openapi3
on master help sign in
Doctests pass
commit27079c750ea4521b2707f959d13b0f0793ef5089 authorMaxim Koltsov <kolmax94@gmail.com> dateSat 4 Jul 2020, 14:51 (6 years ago) parent84f3156
6 files changed+91−87
src/Data/Swagger.hs+26−23 src/Data/Swagger/Internal.hs+2−2 src/Data/Swagger/Internal/Schema.hs+37−37 src/Data/Swagger/Internal/Schema/Validation.hs+1−1 src/Data/Swagger/Operation.hs+12−11 src/Data/Swagger/Optics.hs+13−13
src/Data/Swagger.hs+26−23
@@ -134,6 +134,7 @@ import Data.Swagger.Internal
134134-- >>> import Data.Monoid
135135-- >>> import Data.Proxy
136136-- >>> import GHC.Generics
137+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
137138-- >>> :set -XDeriveGeneric
138139-- >>> :set -XOverloadedStrings
139140-- >>> :set -XOverloadedLists
@@ -150,8 +151,8 @@ import Data.Swagger.Internal
150151--
151152-- In this library you can use @'mempty'@ for a default/empty value. For instance:
152153--
153−-- >>> encode (mempty :: Swagger)
154−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"}}"
154+-- >>> BSL.putStrLn $ encode (mempty :: Swagger)
155+-- {"openapi":"3.0.0","info":{"version":"","title":""},"components":{}}
155156--
156157-- As you can see some spec properties (e.g. @"version"@) are there even when the spec is empty.
157158-- That is because these properties are actually required ones.
@@ -159,13 +160,13 @@ import Data.Swagger.Internal
159160-- You /should/ always override the default (empty) value for these properties,
160161-- although it is not strictly necessary:
161162--
162−-- >>> encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
163−-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
163+-- >>> BSL.putStrLn $ encode mempty { _infoTitle = "Todo API", _infoVersion = "1.0" }
164+-- {"version":"1.0","title":"Todo API"}
164165--
165166-- You can merge two values using @'mappend'@ or its infix version @('<>')@:
166167--
167−-- >>> encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
168−-- "{\"version\":\"1.0\",\"title\":\"Todo API\"}"
168+-- >>> BSL.putStrLn $ encode $ mempty { _infoTitle = "Todo API" } <> mempty { _infoVersion = "1.0" }
169+-- {"version":"1.0","title":"Todo API"}
169170--
170171-- This can be useful for combining specifications of endpoints into a whole API specification:
171172--
@@ -191,15 +192,14 @@ import Data.Swagger.Internal
191192-- make it fairly simple to construct/modify any part of the specification:
192193--
193194-- >>> :{
194−-- encode $ (mempty :: Swagger)
195−-- & definitions .~ [ ("User", mempty & type_ ?~ SwaggerString) ]
195+-- BSL.putStrLn $ encode $ (mempty :: Swagger)
196+-- & components . schemas .~ [ ("User", mempty & type_ ?~ SwaggerString) ]
196197-- & paths .~
197198-- [ ("/user", mempty & get ?~ (mempty
198−-- & produces ?~ MimeList ["application/json"]
199−-- & at 200 ?~ ("OK" & _Inline.schema ?~ Ref (Reference "User"))
199+-- & at 200 ?~ ("OK" & _Inline.content.at "application/json" ?~ (mempty & schema ?~ Ref (Reference "User")))
200200-- & at 404 ?~ "User info not found")) ]
201201-- :}
202−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"produces\":[\"application/json\"],\"responses\":{\"404\":{\"description\":\"User info not found\"},\"200\":{\"schema\":{\"$ref\":\"#/definitions/User\"},\"description\":\"OK\"}}}}},\"definitions\":{\"User\":{\"type\":\"string\"}}}"
202+-- {"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"}}}}
203203--
204204-- In the snippet above we declare an API with a single path @/user@. This path provides method @GET@
205205-- which produces @application/json@ output. It should respond with code @200@ and body specified
@@ -211,30 +211,33 @@ import Data.Swagger.Internal
211211-- common field is @'description'@. Many components of a Swagger specification
212212-- can have descriptions, and you can use the same name for them:
213213--
214−-- >>> encode $ (mempty :: Response) & description .~ "No content"
215−-- "{\"description\":\"No content\"}"
214+-- >>> BSL.putStrLn $ encode $ (mempty :: Response) & description .~ "No content"
215+-- {"description":"No content"}
216216-- >>> :{
217−-- encode $ (mempty :: Schema)
217+-- BSL.putStrLn $ encode $ (mempty :: Schema)
218218-- & type_ ?~ SwaggerBoolean
219219-- & description ?~ "To be or not to be"
220220-- :}
221−-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
221+-- {"description":"To be or not to be","type":"boolean"}
222222--
223223-- @'ParamSchema'@ is basically the /base schema specification/ and many types contain it (see @'HasParamSchema'@).
224224-- So for convenience, all @'ParamSchema'@ fields are transitively made fields of the type that has it.
225225-- For example, you can use @'type_'@ to access @'SwaggerType'@ of @'Header'@ schema without having to use @'paramSchema'@:
226226--
227−-- >>> encode $ (mempty :: Header) & type_ ?~ SwaggerNumber
228−-- "{\"type\":\"number\"}"
227+-- >>> BSL.putStrLn $ encode $ (mempty :: Header) & type_ ?~ SwaggerNumber
228+-- {"schema":{"type":"number"}}
229+--
230+-- TODO this is no up-to-date ^, since in openapi 3 there is no ParamSchema madness, all objects
231+-- have Schemas as fields.
229232--
230233-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and @'Responses'@
231234-- have direct access to it via @'at' code@. Example:
232235--
233236-- >>> :{
234−-- encode $ (mempty :: Operation)
237+-- BSL.putStrLn $ encode $ (mempty :: Operation)
235238-- & at 404 ?~ "Not found"
236239-- :}
237−-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
240+-- {"responses":{"404":{"description":"Not found"}}}
238241--
239242-- You might've noticed that @'type_'@ has an extra underscore in its name
240243-- compared to, say, @'description'@ field accessor.
@@ -281,10 +284,10 @@ import Data.Swagger.Internal
281284-- >>> data Person = Person { name :: String, age :: Integer } deriving Generic
282285-- >>> instance ToJSON Person
283286-- >>> instance ToSchema Person
284−-- >>> encode (Person "David" 28)
285−-- "{\"age\":28,\"name\":\"David\"}"
286−-- >>> encode $ toSchema (Proxy :: Proxy Person)
287−-- "{\"required\":[\"name\",\"age\"],\"properties\":{\"name\":{\"type\":\"string\"},\"age\":{\"type\":\"integer\"}},\"type\":\"object\"}"
287+-- >>> BSL.putStrLn $ encode (Person "David" 28)
288+-- {"age":28,"name":"David"}
289+-- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Person)
290+-- {"required":["name","age"],"properties":{"name":{"type":"string"},"age":{"type":"integer"}},"type":"object"}
288291--
289292-- Please note that not all valid Haskell data types will have a proper swagger schema. For example while we can derive a
290293-- schema for basic enums like
src/Data/Swagger/Internal.hs+2−2
@@ -1427,10 +1427,10 @@ instance FromJSON (SwaggerItems 'SwaggerKindParamOtherSchema) where
14271427-- >>> decode "{}" :: Maybe (SwaggerItems 'SwaggerKindSchema)
14281428-- Just (SwaggerItemsArray [])
14291429--
1430−-- >>> eitherDecode "{\"$ref\":\"#/definitions/example\"}" :: Either String (SwaggerItems 'SwaggerKindSchema)
1430+-- >>> eitherDecode "{\"$ref\":\"#/components/schemas/example\"}" :: Either String (SwaggerItems 'SwaggerKindSchema)
14311431-- Right (SwaggerItemsObject (Ref (Reference {getReference = "example"})))
14321432--
1433−-- >>> eitherDecode "[{\"$ref\":\"#/definitions/example\"}]" :: Either String (SwaggerItems 'SwaggerKindSchema)
1433+-- >>> eitherDecode "[{\"$ref\":\"#/components/schemas/example\"}]" :: Either String (SwaggerItems 'SwaggerKindSchema)
14341434-- Right (SwaggerItemsArray [Ref (Reference {getReference = "example"})])
14351435--
14361436instance FromJSON (SwaggerItems 'SwaggerKindSchema) where
src/Data/Swagger/Internal/Schema.hs+37−37
@@ -67,7 +67,7 @@ import Data.Swagger.SchemaOptions
6767import Data.Swagger.Internal.TypeShape
6868
6969import qualified Data.ByteString as BS
70−import qualified Data.ByteString.Lazy as BSL
70+import qualified Data.ByteString.Lazy.Char8 as BSL
7171import GHC.TypeLits (TypeError, ErrorMessage(..))
7272
7373unnamed :: Schema -> NamedSchema
@@ -151,13 +151,13 @@ declareSchema = fmap _namedSchemaSchema . declareNamedSchema
151151--
152152-- >>> toNamedSchema (Proxy :: Proxy String) ^. name
153153-- Nothing
154−-- >>> encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
155−-- "{\"type\":\"string\"}"
154+-- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
155+-- {"type":"string"}
156156--
157157-- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
158158-- Just "Day"
159−-- >>> encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
160−-- "{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}"
159+-- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
160+-- {"example":"2016-07-22","format":"date","type":"string"}
161161toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
162162toNamedSchema = undeclare . declareNamedSchema
163163
@@ -173,22 +173,22 @@ schemaName = _namedSchemaName . toNamedSchema
173173
174174-- | Convert a type into a schema.
175175--
176−-- >>> encode $ toSchema (Proxy :: Proxy Int8)
177−-- "{\"maximum\":127,\"minimum\":-128,\"type\":\"integer\"}"
176+-- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Int8)
177+-- {"maximum":127,"minimum":-128,"type":"integer"}
178178--
179−-- >>> encode $ toSchema (Proxy :: Proxy [Day])
180−-- "{\"items\":{\"$ref\":\"#/definitions/Day\"},\"type\":\"array\"}"
179+-- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy [Day])
180+-- {"items":{"$ref":"#/components/schemas/Day"},"type":"array"}
181181toSchema :: ToSchema a => Proxy a -> Schema
182182toSchema = _namedSchemaSchema . toNamedSchema
183183
184184-- | Convert a type into a referenced schema if possible.
185185-- Only named schemas can be referenced, nameless schemas are inlined.
186186--
187−-- >>> encode $ toSchemaRef (Proxy :: Proxy Integer)
188−-- "{\"type\":\"integer\"}"
187+-- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Integer)
188+-- {"type":"integer"}
189189--
190−-- >>> encode $ toSchemaRef (Proxy :: Proxy Day)
191−-- "{\"$ref\":\"#/definitions/Day\"}"
190+-- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Day)
191+-- {"$ref":"#/components/schemas/Day"}
192192toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
193193toSchemaRef = undeclare . declareSchemaRef
194194
@@ -256,8 +256,8 @@ inlineAllSchemas = inlineSchemasWhen (const True)
256256
257257-- | Convert a type into a schema without references.
258258--
259−-- >>> encode $ toInlinedSchema (Proxy :: Proxy [Day])
260−-- "{\"items\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"},\"type\":\"array\"}"
259+-- >>> BSL.putStrLn $ encode $ toInlinedSchema (Proxy :: Proxy [Day])
260+-- {"items":{"example":"2016-07-22","format":"date","type":"string"},"type":"array"}
261261--
262262-- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
263263-- when inlining recursive schemas.
@@ -309,19 +309,19 @@ passwordSchema = mempty
309309-- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
310310-- Produced schema can be used for further refinement.
311311--
312−-- >>> encode $ sketchSchema "hello"
313−-- "{\"example\":\"hello\",\"type\":\"string\"}"
312+-- >>> BSL.putStrLn $ encode $ sketchSchema "hello"
313+-- {"example":"hello","type":"string"}
314314--
315−-- >>> encode $ sketchSchema (1, 2, 3)
316−-- "{\"example\":[1,2,3],\"items\":{\"type\":\"number\"},\"type\":\"array\"}"
315+-- >>> BSL.putStrLn $ encode $ sketchSchema (1, 2, 3)
316+-- {"example":[1,2,3],"items":{"type":"number"},"type":"array"}
317317--
318−-- >>> encode $ sketchSchema ("Jack", 25)
319−-- "{\"example\":[\"Jack\",25],\"items\":[{\"type\":\"string\"},{\"type\":\"number\"}],\"type\":\"array\"}"
318+-- >>> BSL.putStrLn $ encode $ sketchSchema ("Jack", 25)
319+-- {"example":["Jack",25],"items":[{"type":"string"},{"type":"number"}],"type":"array"}
320320--
321321-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
322322-- >>> instance ToJSON Person
323−-- >>> encode $ sketchSchema (Person "Jack" 25)
324−-- "{\"required\":[\"age\",\"name\"],\"properties\":{\"age\":{\"type\":\"number\"},\"name\":{\"type\":\"string\"}},\"example\":{\"age\":25,\"name\":\"Jack\"},\"type\":\"object\"}"
323+-- >>> BSL.putStrLn $ encode $ sketchSchema (Person "Jack" 25)
324+-- {"required":["age","name"],"properties":{"age":{"type":"number"},"name":{"type":"string"}},"example":{"age":25,"name":"Jack"},"type":"object"}
325325sketchSchema :: ToJSON a => a -> Schema
326326sketchSchema = sketch . toJSON
327327 where
@@ -353,19 +353,19 @@ sketchSchema = sketch . toJSON
353353-- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
354354-- Produced schema uses as much constraints as possible.
355355--
356−-- >>> encode $ sketchStrictSchema "hello"
357−-- "{\"maxLength\":5,\"pattern\":\"hello\",\"minLength\":5,\"type\":\"string\",\"enum\":[\"hello\"]}"
356+-- >>> BSL.putStrLn $ encode $ sketchStrictSchema "hello"
357+-- {"maxLength":5,"pattern":"hello","minLength":5,"type":"string","enum":["hello"]}
358358--
359−-- >>> encode $ sketchStrictSchema (1, 2, 3)
360−-- "{\"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]]}"
359+-- >>> BSL.putStrLn $ encode $ sketchStrictSchema (1, 2, 3)
360+-- {"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]]}
361361--
362−-- >>> encode $ sketchStrictSchema ("Jack", 25)
363−-- "{\"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]]}"
362+-- >>> BSL.putStrLn $ encode $ sketchStrictSchema ("Jack", 25)
363+-- {"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]]}
364364--
365365-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
366366-- >>> instance ToJSON Person
367−-- >>> encode $ sketchStrictSchema (Person "Jack" 25)
368−-- "{\"required\":[\"age\",\"name\"],\"properties\":{\"age\":{\"maximum\":25,\"minimum\":25,\"multipleOf\":25,\"type\":\"number\",\"enum\":[25]},\"name\":{\"maxLength\":4,\"pattern\":\"Jack\",\"minLength\":4,\"type\":\"string\",\"enum\":[\"Jack\"]}},\"maxProperties\":2,\"minProperties\":2,\"type\":\"object\",\"enum\":[{\"age\":25,\"name\":\"Jack\"}]}"
367+-- >>> BSL.putStrLn $ encode $ sketchStrictSchema (Person "Jack" 25)
368+-- {"required":["age","name"],"properties":{"age":{"maximum":25,"minimum":25,"multipleOf":25,"type":"number","enum":[25]},"name":{"maxLength":4,"pattern":"Jack","minLength":4,"type":"string","enum":["Jack"]}},"maxProperties":2,"minProperties":2,"type":"object","enum":[{"age":25,"name":"Jack"}]}
369369sketchStrictSchema :: ToJSON a => a -> Schema
370370sketchStrictSchema = go . toJSON
371371 where
@@ -565,8 +565,8 @@ instance ToSchema a => ToSchema (Identity a) where declareNamedSchema _ = declar
565565
566566-- | Default schema for @'Bounded'@, @'Integral'@ types.
567567--
568−-- >>> encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
569−-- "{\"maximum\":32767,\"minimum\":-32768,\"type\":\"integer\"}"
568+-- >>> BSL.putStrLn $ encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
569+-- {"maximum":32767,"minimum":-32768,"type":"integer"}
570570toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
571571toSchemaBoundedIntegral _ = mempty
572572 & type_ ?~ SwaggerInteger
@@ -598,8 +598,8 @@ genericDeclareNamedSchemaNewtype opts f proxy = genericNameSchema opts proxy <$>
598598-- >>> instance ToSchema ButtonState
599599-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
600600-- >>> type ImageUrl = T.Text
601−-- >>> encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
602−-- "{\"properties\":{\"Neutral\":{\"type\":\"string\"},\"Focus\":{\"type\":\"string\"},\"Active\":{\"type\":\"string\"},\"Hover\":{\"type\":\"string\"},\"Disabled\":{\"type\":\"string\"}},\"type\":\"object\"}"
601+-- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
602+-- {"properties":{"Neutral":{"type":"string"},"Focus":{"type":"string"},"Active":{"type":"string"},"Hover":{"type":"string"},"Disabled":{"type":"string"}},"type":"object"}
603603--
604604-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
605605-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -626,8 +626,8 @@ declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key o
626626-- >>> instance ToSchema ButtonState
627627-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
628628-- >>> type ImageUrl = T.Text
629−-- >>> encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
630−-- "{\"properties\":{\"Neutral\":{\"type\":\"string\"},\"Focus\":{\"type\":\"string\"},\"Active\":{\"type\":\"string\"},\"Hover\":{\"type\":\"string\"},\"Disabled\":{\"type\":\"string\"}},\"type\":\"object\"}"
629+-- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
630+-- {"properties":{"Neutral":{"type":"string"},"Focus":{"type":"string"},"Active":{"type":"string"},"Hover":{"type":"string"},"Disabled":{"type":"string"}},"type":"object"}
631631--
632632-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
633633-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
src/Data/Swagger/Internal/Schema/Validation.hs+1−1
@@ -109,7 +109,7 @@ validateToJSONWithPatternChecker checker = validateJSONWithPatternChecker checke
109109-- "type": "object",
110110-- "properties": {
111111-- "phone": {
112−-- "$ref": "#/definitions/Phone"
112+-- "$ref": "#/components/schemas/Phone"
113113-- },
114114-- "name": {
115115-- "type": "string"
src/Data/Swagger/Operation.hs+12−11
@@ -53,13 +53,14 @@ import qualified Data.HashSet.InsOrd as InsOrdHS
5353-- >>> import Data.Aeson
5454-- >>> import Data.Proxy
5555-- >>> import Data.Time
56+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
5657
5758-- | Prepend path piece to all operations of the spec.
5859-- Leading and trailing slashes are trimmed/added automatically.
5960--
6061-- >>> let api = (mempty :: Swagger) & paths .~ [("/info", mempty)]
61−-- >>> encode $ prependPath "user/{user_id}" api ^. paths
62−-- "{\"/user/{user_id}/info\":{}}"
62+-- >>> BSL.putStrLn $ encode $ prependPath "user/{user_id}" api ^. paths
63+-- {"/user/{user_id}/info":{}}
6364prependPath :: FilePath -> Swagger -> Swagger
6465prependPath path = paths %~ InsOrdHashMap.mapKeys (path </>)
6566 where
@@ -80,10 +81,10 @@ allOperations = paths.traverse.template
8081-- >>> let ok = (mempty :: Operation) & at 200 ?~ "OK"
8182-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ ok & post ?~ ok)]
8283-- >>> let sub = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
83−-- >>> encode api
84−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
85−-- >>> encode $ api & operationsOf sub . at 404 ?~ "Not found"
86−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"404\":{\"description\":\"Not found\"},\"200\":{\"description\":\"OK\"}}},\"post\":{\"responses\":{\"200\":{\"description\":\"OK\"}}}}}}"
84+-- >>> BSL.putStrLn $ encode api
85+-- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"200":{"description":"OK"}}},"post":{"responses":{"200":{"description":"OK"}}}}},"components":{}}
86+-- >>> BSL.putStrLn $ encode $ api & operationsOf sub . at 404 ?~ "Not found"
87+-- {"openapi":"3.0.0","info":{"version":"","title":""},"paths":{"/user":{"get":{"responses":{"404":{"description":"Not found"},"200":{"description":"OK"}}},"post":{"responses":{"200":{"description":"OK"}}}}},"components":{}}
8788operationsOf :: Swagger -> Traversal' Swagger Operation
8889operationsOf sub = paths.itraversed.withIndex.subops
8990 where
@@ -121,8 +122,8 @@ applyTagsFor ops ts swag = swag
121122--
122123-- FIXME doc
123124--
124−-- >>> encode $ runDeclare (declareResponse (Proxy :: Proxy Day)) mempty
125−-- "[{\"Day\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}},{\"description\":\"\",\"schema\":{\"$ref\":\"#/definitions/Day\"}}]"
125+-- >>> BSL.putStrLn $ encode $ runDeclare (declareResponse "application/json" (Proxy :: Proxy Day)) mempty
126+-- [{"Day":{"example":"2016-07-22","format":"date","type":"string"}},{"description":"","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Day"}}}}]
126127declareResponse :: ToSchema a => Text -> Proxy a -> Declare (Definitions Schema) Response
127128declareResponse cType proxy = do
128129 s <- declareSchemaRef proxy
@@ -140,9 +141,9 @@ declareResponse cType proxy = do
140141-- Example:
141142--
142143-- >>> let api = (mempty :: Swagger) & paths .~ [("/user", mempty & get ?~ mempty)]
143−-- >>> let res = declareResponse (Proxy :: Proxy Day)
144−-- >>> encode $ api & setResponse 200 res
145−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"responses\":{\"200\":{\"schema\":{\"$ref\":\"#/definitions/Day\"},\"description\":\"\"}}}}},\"definitions\":{\"Day\":{\"example\":\"2016-07-22\",\"format\":\"date\",\"type\":\"string\"}}}"
144+-- >>> let res = declareResponse "application/json" (Proxy :: Proxy Day)
145+-- >>> BSL.putStrLn $ encode $ api & setResponse 200 res
146+-- {"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"}}}}
146147--
147148-- See also @'setResponseWith'@.
148149setResponse :: HttpStatusCode -> Declare (Definitions Schema) Response -> Swagger -> Swagger
src/Data/Swagger/Optics.hs+13−13
@@ -17,33 +17,33 @@
1717-- >>> import Data.Aeson
1818-- >>> import Optics.Core
1919-- >>> :set -XOverloadedLabels
20+-- >>> import qualified Data.ByteString.Lazy.Char8 as BSL
2021--
2122-- Example from the "Data.Swagger" module using @optics@:
2223--
2324-- >>> :{
24−-- encode $ (mempty :: Swagger)
25−-- & #definitions .~ [ ("User", mempty & #type ?~ SwaggerString) ]
25+-- BSL.putStrLn $ encode $ (mempty :: Swagger)
26+-- & #components % #schemas .~ [ ("User", mempty & #type ?~ SwaggerString) ]
2627-- & #paths .~
2728-- [ ("/user", mempty & #get ?~ (mempty
28−-- & #produces ?~ MimeList ["application/json"]
29−-- & at 200 ?~ ("OK" & #_Inline % #schema ?~ Ref (Reference "User"))
29+-- & at 200 ?~ ("OK" & #_Inline % #content % at "application/json" ?~ (mempty & #schema ?~ Ref (Reference "User")))
3030-- & at 404 ?~ "User info not found")) ]
3131-- :}
32−-- "{\"swagger\":\"2.0\",\"info\":{\"version\":\"\",\"title\":\"\"},\"paths\":{\"/user\":{\"get\":{\"produces\":[\"application/json\"],\"responses\":{\"404\":{\"description\":\"User info not found\"},\"200\":{\"schema\":{\"$ref\":\"#/definitions/User\"},\"description\":\"OK\"}}}}},\"definitions\":{\"User\":{\"type\":\"string\"}}}"
32+-- {"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"}}}}
3333--
3434-- For convenience optics are defined as /labels/. It means that field accessor
3535-- names can be overloaded for different types. One such common field is
3636-- @#description@. Many components of a Swagger specification can have
3737-- descriptions, and you can use the same name for them:
3838--
39−-- >>> encode $ (mempty :: Response) & #description .~ "No content"
40−-- "{\"description\":\"No content\"}"
39+-- >>> BSL.putStrLn $ encode $ (mempty :: Response) & #description .~ "No content"
40+-- {"description":"No content"}
4141-- >>> :{
42−-- encode $ (mempty :: Schema)
42+-- BSL.putStrLn $ encode $ (mempty :: Schema)
4343-- & #type ?~ SwaggerBoolean
4444-- & #description ?~ "To be or not to be"
4545-- :}
46−-- "{\"description\":\"To be or not to be\",\"type\":\"boolean\"}"
46+-- {"description":"To be or not to be","type":"boolean"}
4747--
4848-- @'ParamSchema'@ is basically the /base schema specification/ and many types
4949-- contain it. So for convenience, all @'ParamSchema'@ fields are transitively
@@ -51,17 +51,17 @@
5151-- access @'SwaggerType'@ of @'Header'@ schema without having to use
5252-- @#paramSchema@:
5353--
54−-- >>> encode $ (mempty :: Header) & #type ?~ SwaggerNumber
55−-- "{\"type\":\"number\"}"
54+-- >>> BSL.putStrLn $ encode $ (mempty :: Header) & #type ?~ SwaggerNumber
55+-- {"schema":{"type":"number"}}
5656--
5757-- Additionally, to simplify working with @'Response'@, both @'Operation'@ and
5858-- @'Responses'@ have direct access to it via @'Optics.Core.At.at'@. Example:
5959--
6060-- >>> :{
61−-- encode $ (mempty :: Operation)
61+-- BSL.putStrLn $ encode $ (mempty :: Operation)
6262-- & at 404 ?~ "Not found"
6363-- :}
64−-- "{\"responses\":{\"404\":{\"description\":\"Not found\"}}}"
64+-- {"responses":{"404":{"description":"Not found"}}}
6565--
6666module Data.Swagger.Optics () where
6767