@@ -1,15 +1,17 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE ConstraintKinds #-}
3+{-# LANGUAGE DataKinds #-}
24{-# LANGUAGE DefaultSignatures #-}
35{-# LANGUAGE FlexibleContexts #-}
46{-# LANGUAGE FlexibleInstances #-}
57{-# LANGUAGE KindSignatures #-}
6−{-# LANGUAGE DataKinds #-}
78{-# LANGUAGE OverloadedLists #-}
89{-# LANGUAGE OverloadedStrings #-}
910{-# LANGUAGE PackageImports #-}
1011{-# LANGUAGE PolyKinds #-}
1112{-# LANGUAGE ScopedTypeVariables #-}
1213{-# LANGUAGE TupleSections #-}
14+{-# LANGUAGE TypeApplications #-}
1315{-# LANGUAGE TypeFamilies #-}
1416{-# LANGUAGE TypeOperators #-}
1517{-# LANGUAGE TypeSynonymInstances #-}
@@ -17,7 +19,7 @@
1719{-# OPTIONS_GHC -Wno-redundant-constraints #-}
1820-- For TypeErrors
1921{-# OPTIONS_GHC -Wno-unticked-promoted-constructors #-}
20−module Data.Swagger.Internal.Schema where
22+module Data.OpenApi.Internal.Schema where
2123
2224import Prelude ()
2325import Prelude.Compat
@@ -40,6 +42,7 @@ import qualified Data.HashMap.Strict.InsOrd as InsOrdHashMap
4042import Data.Int
4143import Data.IntSet (IntSet)
4244import Data.IntMap (IntMap)
45+import Data.List (sort)
4346import Data.List.NonEmpty.Compat (NonEmpty)
4447import Data.Map (Map)
4548import Data.Maybe (fromMaybe)
@@ -59,14 +62,16 @@ import Numeric.Natural.Compat (Natural)
5962import Data.Word
6063import GHC.Generics
6164import qualified Data.UUID.Types as UUID
65+import Type.Reflection (Typeable, typeRep)
6266
63−import Data.Swagger.Declare
64−import Data.Swagger.Internal
65−import Data.Swagger.Internal.ParamSchema (ToParamSchema(..))
66−import Data.Swagger.Lens hiding (name, schema)
67−import qualified Data.Swagger.Lens as Swagger
68−import Data.Swagger.SchemaOptions
69−import Data.Swagger.Internal.TypeShape
67+import Data.OpenApi.Aeson.Compat (keyToText, objectKeys, toInsOrdHashMap)
68+import Data.OpenApi.Declare
69+import Data.OpenApi.Internal
70+import Data.OpenApi.Internal.ParamSchema (ToParamSchema (..))
71+import Data.OpenApi.Internal.TypeShape
72+import Data.OpenApi.Lens hiding (name, schema)
73+import qualified Data.OpenApi.Lens as Swagger
74+import Data.OpenApi.SchemaOptions
7075
7176import qualified Data.ByteString as BS
7277import qualified Data.ByteString.Lazy.Char8 as BSL
@@ -97,7 +102,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
97102--
98103-- import Control.Lens
99104-- import Data.Proxy
100−-- import Data.Swagger
105+-- import Data.OpenApi
101106--
102107-- data Coord = Coord { x :: Double, y :: Double }
103108--
@@ -105,7 +110,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
105110-- declareNamedSchema _ = do
106111-- doubleSchema <- declareSchemaRef (Proxy :: Proxy Double)
107112-- return $ NamedSchema (Just \"Coord\") $ mempty
108−-- & type_ ?~ SwaggerObject
113+-- & type_ ?~ OpenApiObject
109114-- & properties .~
110115-- [ (\"x\", doubleSchema)
111116-- , (\"y\", doubleSchema)
@@ -131,7 +136,7 @@ rename name (NamedSchema _ schema) = NamedSchema name schema
131136--
132137-- instance ToSchema Coord
133138-- @
134−class ToSchema a where
139+class Typeable a => ToSchema a where
135140 -- | Convert a type into an optionally named schema
136141 -- together with all used definitions.
137142 -- Note that the schema itself is included in definitions
@@ -153,13 +158,19 @@ declareSchema = fmap _namedSchemaSchema . declareNamedSchema
153158--
154159-- >>> toNamedSchema (Proxy :: Proxy String) ^. name
155160-- Nothing
156−-- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy String) ^. schema)
157−-- {"type":"string"}
161+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy String) ^. schema)
162+-- {
163+-- "type": "string"
164+-- }
158165--
159166-- >>> toNamedSchema (Proxy :: Proxy Day) ^. name
160167-- Just "Day"
161−-- >>> BSL.putStrLn $ encode (toNamedSchema (Proxy :: Proxy Day) ^. schema)
162−-- {"example":"2016-07-22","format":"date","type":"string"}
168+-- >>> BSL.putStrLn $ encodePretty (toNamedSchema (Proxy :: Proxy Day) ^. schema)
169+-- {
170+-- "example": "2016-07-22",
171+-- "format": "date",
172+-- "type": "string"
173+-- }
163174toNamedSchema :: ToSchema a => Proxy a -> NamedSchema
164175toNamedSchema = undeclare . declareNamedSchema
165176
@@ -175,22 +186,35 @@ schemaName = _namedSchemaName . toNamedSchema
175186
176187-- | Convert a type into a schema.
177188--
178−-- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy Int8)
179−-- {"maximum":127,"minimum":-128,"type":"integer"}
189+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy Int8)
190+-- {
191+-- "maximum": 127,
192+-- "minimum": -128,
193+-- "type": "integer"
194+-- }
180195--
181−-- >>> BSL.putStrLn $ encode $ toSchema (Proxy :: Proxy [Day])
182−-- {"items":{"$ref":"#/components/schemas/Day"},"type":"array"}
196+-- >>> BSL.putStrLn $ encodePretty $ toSchema (Proxy :: Proxy [Day])
197+-- {
198+-- "items": {
199+-- "$ref": "#/components/schemas/Day"
200+-- },
201+-- "type": "array"
202+-- }
183203toSchema :: ToSchema a => Proxy a -> Schema
184204toSchema = _namedSchemaSchema . toNamedSchema
185205
186206-- | Convert a type into a referenced schema if possible.
187207-- Only named schemas can be referenced, nameless schemas are inlined.
188208--
189−-- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Integer)
190−-- {"type":"integer"}
209+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Integer)
210+-- {
211+-- "type": "integer"
212+-- }
191213--
192−-- >>> BSL.putStrLn $ encode $ toSchemaRef (Proxy :: Proxy Day)
193−-- {"$ref":"#/components/schemas/Day"}
214+-- >>> BSL.putStrLn $ encodePretty $ toSchemaRef (Proxy :: Proxy Day)
215+-- {
216+-- "$ref": "#/components/schemas/Day"
217+-- }
194218toSchemaRef :: ToSchema a => Proxy a -> Referenced Schema
195219toSchemaRef = undeclare . declareSchemaRef
196220
@@ -258,8 +282,15 @@ inlineAllSchemas = inlineSchemasWhen (const True)
258282
259283-- | Convert a type into a schema without references.
260284--
261−-- >>> BSL.putStrLn $ encode $ toInlinedSchema (Proxy :: Proxy [Day])
262−-- {"items":{"example":"2016-07-22","format":"date","type":"string"},"type":"array"}
285+-- >>> BSL.putStrLn $ encodePretty $ toInlinedSchema (Proxy :: Proxy [Day])
286+-- {
287+-- "items": {
288+-- "example": "2016-07-22",
289+-- "format": "date",
290+-- "type": "string"
291+-- },
292+-- "type": "array"
293+-- }
263294--
264295-- __WARNING:__ @'toInlinedSchema'@ will produce infinite schema
265296-- when inlining recursive schemas.
@@ -289,41 +320,67 @@ inlineNonRecursiveSchemas defs = inlineSchemasWhen nonRecursive defs
289320 traverse_ usedNames (InsOrdHashMap.lookup name defs)
290321 Inline subschema -> usedNames subschema
291322
292−-- | Default schema for binary data (any sequence of octets).
293−binarySchema :: Schema
294−binarySchema = mempty
295− & type_ ?~ SwaggerString
296− & format ?~ "binary"
297−
298−-- | Default schema for binary data (base64 encoded).
299−byteSchema :: Schema
300−byteSchema = mempty
301− & type_ ?~ SwaggerString
302− & format ?~ "byte"
303−
304−-- | Default schema for password string.
305−-- @"password"@ format is used to hint UIs the input needs to be obscured.
306−passwordSchema :: Schema
307−passwordSchema = mempty
308− & type_ ?~ SwaggerString
309− & format ?~ "password"
310−
311323-- | Make an unrestrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
312324-- Produced schema can be used for further refinement.
313325--
314−-- >>> BSL.putStrLn $ encode $ sketchSchema "hello"
315−-- {"example":"hello","type":"string"}
326+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema "hello"
327+-- {
328+-- "example": "hello",
329+-- "type": "string"
330+-- }
316331--
317−-- >>> BSL.putStrLn $ encode $ sketchSchema (1, 2, 3)
318−-- {"example":[1,2,3],"items":{"type":"number"},"type":"array"}
332+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (1, 2, 3)
333+-- {
334+-- "example": [
335+-- 1,
336+-- 2,
337+-- 3
338+-- ],
339+-- "items": {
340+-- "type": "number"
341+-- },
342+-- "type": "array"
343+-- }
319344--
320−-- >>> BSL.putStrLn $ encode $ sketchSchema ("Jack", 25)
321−-- {"example":["Jack",25],"items":[{"type":"string"},{"type":"number"}],"type":"array"}
345+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema ("Jack", 25)
346+-- {
347+-- "example": [
348+-- "Jack",
349+-- 25
350+-- ],
351+-- "items": [
352+-- {
353+-- "type": "string"
354+-- },
355+-- {
356+-- "type": "number"
357+-- }
358+-- ],
359+-- "type": "array"
360+-- }
322361--
323362-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
324363-- >>> instance ToJSON Person
325−-- >>> BSL.putStrLn $ encode $ sketchSchema (Person "Jack" 25)
326−-- {"required":["age","name"],"properties":{"age":{"type":"number"},"name":{"type":"string"}},"example":{"age":25,"name":"Jack"},"type":"object"}
364+-- >>> BSL.putStrLn $ encodePretty $ sketchSchema (Person "Jack" 25)
365+-- {
366+-- "example": {
367+-- "age": 25,
368+-- "name": "Jack"
369+-- },
370+-- "properties": {
371+-- "age": {
372+-- "type": "number"
373+-- },
374+-- "name": {
375+-- "type": "string"
376+-- }
377+-- },
378+-- "required": [
379+-- "age",
380+-- "name"
381+-- ],
382+-- "type": "object"
383+-- }
327384sketchSchema :: ToJSON a => a -> Schema
328385sketchSchema = sketch . toJSON
329386 where
@@ -331,15 +388,15 @@ sketchSchema = sketch . toJSON
331388 sketch js@(Bool _) = go js
332389 sketch js = go js & example ?~ js
333390
334− go Null = mempty & type_ ?~ SwaggerNull
335− go (Bool _) = mempty & type_ ?~ SwaggerBoolean
336− go (String _) = mempty & type_ ?~ SwaggerString
337− go (Number _) = mempty & type_ ?~ SwaggerNumber
391+ go Null = mempty & type_ ?~ OpenApiNull
392+ go (Bool _) = mempty & type_ ?~ OpenApiBoolean
393+ go (String _) = mempty & type_ ?~ OpenApiString
394+ go (Number _) = mempty & type_ ?~ OpenApiNumber
338395 go (Array xs) = mempty
339− & type_ ?~ SwaggerArray
396+ & type_ ?~ OpenApiArray
340397 & items ?~ case ischema of
341− Just s -> SwaggerItemsObject (Inline s)
342− _ -> SwaggerItemsArray (map Inline ys)
398+ Just s -> OpenApiItemsObject (Inline s)
399+ _ -> OpenApiItemsArray (map Inline ys)
343400 where
344401 ys = map go (V.toList xs)
345402 allSame = and ((zipWith (==)) ys (tail ys))
@@ -348,64 +405,178 @@ sketchSchema = sketch . toJSON
348405 (z:_) | allSame -> Just z
349406 _ -> Nothing
350407 go (Object o) = mempty
351− & type_ ?~ SwaggerObject
352− & required .~ HashMap.keys o
353− & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
408+ & type_ ?~ OpenApiObject
409+ & required .~ sort (objectKeys o)
410+ & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
354411
355412-- | Make a restrictive sketch of a @'Schema'@ based on a @'ToJSON'@ instance.
356413-- Produced schema uses as much constraints as possible.
357414--
358−-- >>> BSL.putStrLn $ encode $ sketchStrictSchema "hello"
359−-- {"maxLength":5,"pattern":"hello","minLength":5,"type":"string","enum":["hello"]}
415+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema "hello"
416+-- {
417+-- "enum": [
418+-- "hello"
419+-- ],
420+-- "maxLength": 5,
421+-- "minLength": 5,
422+-- "pattern": "hello",
423+-- "type": "string"
424+-- }
360425--
361−-- >>> BSL.putStrLn $ encode $ sketchStrictSchema (1, 2, 3)
362−-- {"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]]}
426+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (1, 2, 3)
427+-- {
428+-- "enum": [
429+-- [
430+-- 1,
431+-- 2,
432+-- 3
433+-- ]
434+-- ],
435+-- "items": [
436+-- {
437+-- "enum": [
438+-- 1
439+-- ],
440+-- "maximum": 1,
441+-- "minimum": 1,
442+-- "multipleOf": 1,
443+-- "type": "number"
444+-- },
445+-- {
446+-- "enum": [
447+-- 2
448+-- ],
449+-- "maximum": 2,
450+-- "minimum": 2,
451+-- "multipleOf": 2,
452+-- "type": "number"
453+-- },
454+-- {
455+-- "enum": [
456+-- 3
457+-- ],
458+-- "maximum": 3,
459+-- "minimum": 3,
460+-- "multipleOf": 3,
461+-- "type": "number"
462+-- }
463+-- ],
464+-- "maxItems": 3,
465+-- "minItems": 3,
466+-- "type": "array",
467+-- "uniqueItems": true
468+-- }
363469--
364−-- >>> BSL.putStrLn $ encode $ sketchStrictSchema ("Jack", 25)
365−-- {"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]]}
470+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema ("Jack", 25)
471+-- {
472+-- "enum": [
473+-- [
474+-- "Jack",
475+-- 25
476+-- ]
477+-- ],
478+-- "items": [
479+-- {
480+-- "enum": [
481+-- "Jack"
482+-- ],
483+-- "maxLength": 4,
484+-- "minLength": 4,
485+-- "pattern": "Jack",
486+-- "type": "string"
487+-- },
488+-- {
489+-- "enum": [
490+-- 25
491+-- ],
492+-- "maximum": 25,
493+-- "minimum": 25,
494+-- "multipleOf": 25,
495+-- "type": "number"
496+-- }
497+-- ],
498+-- "maxItems": 2,
499+-- "minItems": 2,
500+-- "type": "array",
501+-- "uniqueItems": true
502+-- }
366503--
367504-- >>> data Person = Person { name :: String, age :: Int } deriving (Generic)
368505-- >>> instance ToJSON Person
369−-- >>> BSL.putStrLn $ encode $ sketchStrictSchema (Person "Jack" 25)
370−-- {"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"}]}
506+-- >>> BSL.putStrLn $ encodePretty $ sketchStrictSchema (Person "Jack" 25)
507+-- {
508+-- "enum": [
509+-- {
510+-- "age": 25,
511+-- "name": "Jack"
512+-- }
513+-- ],
514+-- "maxProperties": 2,
515+-- "minProperties": 2,
516+-- "properties": {
517+-- "age": {
518+-- "enum": [
519+-- 25
520+-- ],
521+-- "maximum": 25,
522+-- "minimum": 25,
523+-- "multipleOf": 25,
524+-- "type": "number"
525+-- },
526+-- "name": {
527+-- "enum": [
528+-- "Jack"
529+-- ],
530+-- "maxLength": 4,
531+-- "minLength": 4,
532+-- "pattern": "Jack",
533+-- "type": "string"
534+-- }
535+-- },
536+-- "required": [
537+-- "age",
538+-- "name"
539+-- ],
540+-- "type": "object"
541+-- }
371542sketchStrictSchema :: ToJSON a => a -> Schema
372543sketchStrictSchema = go . toJSON
373544 where
374− go Null = mempty & type_ ?~ SwaggerNull
545+ go Null = mempty & type_ ?~ OpenApiNull
375546 go js@(Bool _) = mempty
376− & type_ ?~ SwaggerBoolean
547+ & type_ ?~ OpenApiBoolean
377548 & enum_ ?~ [js]
378549 go js@(String s) = mempty
379− & type_ ?~ SwaggerString
550+ & type_ ?~ OpenApiString
380551 & maxLength ?~ fromIntegral (T.length s)
381552 & minLength ?~ fromIntegral (T.length s)
382553 & pattern ?~ s
383554 & enum_ ?~ [js]
384555 go js@(Number n) = mempty
385− & type_ ?~ SwaggerNumber
556+ & type_ ?~ OpenApiNumber
386557 & maximum_ ?~ n
387558 & minimum_ ?~ n
388559 & multipleOf ?~ n
389560 & enum_ ?~ [js]
390561 go js@(Array xs) = mempty
391− & type_ ?~ SwaggerArray
562+ & type_ ?~ OpenApiArray
392563 & maxItems ?~ fromIntegral sz
393564 & minItems ?~ fromIntegral sz
394− & items ?~ SwaggerItemsArray (map (Inline . go) (V.toList xs))
565+ & items ?~ OpenApiItemsArray (map (Inline . go) (V.toList xs))
395566 & uniqueItems ?~ allUnique
396567 & enum_ ?~ [js]
397568 where
398569 sz = length xs
399570 allUnique = sz == HashSet.size (HashSet.fromList (V.toList xs))
400571 go js@(Object o) = mempty
401− & type_ ?~ SwaggerObject
402− & required .~ names
403− & properties .~ fmap (Inline . go) (InsOrdHashMap.fromHashMap o)
572+ & type_ ?~ OpenApiObject
573+ & required .~ sort names
574+ & properties .~ fmap (Inline . go) (toInsOrdHashMap o)
404575 & maxProperties ?~ fromIntegral (length names)
405576 & minProperties ?~ fromIntegral (length names)
406577 & enum_ ?~ [js]
407578 where
408− names = HashMap.keys o
579+ names = objectKeys o
409580
410581class GToSchema (f :: * -> *) where
411582 gdeclareNamedSchema :: SchemaOptions -> Proxy f -> Schema -> Declare (Definitions Schema) NamedSchema
@@ -414,8 +585,8 @@ instance {-# OVERLAPPABLE #-} ToSchema a => ToSchema [a] where
414585 declareNamedSchema _ = do
415586 ref <- declareSchemaRef (Proxy :: Proxy a)
416587 return $ unnamed $ mempty
417− & type_ ?~ SwaggerArray
418− & items ?~ SwaggerItemsObject ref
588+ & type_ ?~ OpenApiArray
589+ & items ?~ OpenApiItemsObject ref
419590
420591instance {-# OVERLAPPING #-} ToSchema String where declareNamedSchema = plain . paramSchemaToSchema
421592instance ToSchema Bool where declareNamedSchema = plain . paramSchemaToSchema
@@ -440,7 +611,7 @@ instance ToSchema Scientific where declareNamedSchema = plain . paramSchemaToSc
440611instance ToSchema Double where declareNamedSchema = plain . paramSchemaToSchema
441612instance ToSchema Float where declareNamedSchema = plain . paramSchemaToSchema
442613
443−instance HasResolution a => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
614+instance (Typeable (Fixed a), HasResolution a) => ToSchema (Fixed a) where declareNamedSchema = plain . paramSchemaToSchema
444615
445616instance ToSchema a => ToSchema (Maybe a) where
446617 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy a)
@@ -457,16 +628,22 @@ instance ToSchema UUID.UUID where
457628 declareNamedSchema p = pure $ named "UUID" $ paramSchemaToSchema p
458629 & example ?~ toJSON (UUID.toText UUID.nil)
459630
460−instance (ToSchema a, ToSchema b) => ToSchema (a, b)
461−instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c)
462−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d)
463−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e)
464−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f)
465−instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g)
631+instance (ToSchema a, ToSchema b) => ToSchema (a, b) where
632+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
633+instance (ToSchema a, ToSchema b, ToSchema c) => ToSchema (a, b, c) where
634+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
635+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d) => ToSchema (a, b, c, d) where
636+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
637+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e) => ToSchema (a, b, c, d, e) where
638+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
639+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f) => ToSchema (a, b, c, d, e, f) where
640+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
641+instance (ToSchema a, ToSchema b, ToSchema c, ToSchema d, ToSchema e, ToSchema f, ToSchema g) => ToSchema (a, b, c, d, e, f, g) where
642+ declareNamedSchema = fmap unname . genericDeclareNamedSchema defaultSchemaOptions
466643
467644timeSchema :: T.Text -> Schema
468645timeSchema fmt = mempty
469− & type_ ?~ SwaggerString
646+ & type_ ?~ OpenApiString
470647 & format ?~ fmt
471648
472649-- | Format @"date"@ corresponds to @yyyy-mm-dd@ format.
@@ -481,7 +658,7 @@ instance ToSchema LocalTime where
481658 declareNamedSchema _ = pure $ named "LocalTime" $ timeSchema "yyyy-mm-ddThh:MM:ss"
482659 & example ?~ toJSON (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0))
483660
484−-- | Format @"date"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
661+-- | Format @"date-time"@ corresponds to @yyyy-mm-ddThh:MM:ss(Z|+hh:MM)@ format.
485662instance ToSchema ZonedTime where
486663 declareNamedSchema _ = pure $ named "ZonedTime" $ timeSchema "date-time"
487664 & example ?~ toJSON (ZonedTime (LocalTime (fromGregorian 2016 7 22) (TimeOfDay 7 40 0)) (hoursToTimeZone 3))
@@ -513,7 +690,7 @@ instance ToSchemaByteStringError BSL.ByteString => ToSchema BSL.ByteString where
513690instance ToSchema IntSet where declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy (Set Int))
514691
515692-- | NOTE: This schema does not account for the uniqueness of keys.
516−instance ToSchema a => ToSchema (IntMap a) where
693+instance (ToSchema a) => ToSchema (IntMap a) where
517694 declareNamedSchema _ = declareNamedSchema (Proxy :: Proxy [(Int, a)])
518695
519696instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
@@ -524,7 +701,7 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (Map k v) where
524701 declareObjectMapSchema = do
525702 schema <- declareSchemaRef (Proxy :: Proxy v)
526703 return $ unnamed $ mempty
527− & type_ ?~ SwaggerObject
704+ & type_ ?~ OpenApiObject
528705 & additionalProperties ?~ AdditionalPropertiesSchema schema
529706
530707instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
@@ -532,7 +709,7 @@ instance (ToJSONKey k, ToSchema k, ToSchema v) => ToSchema (HashMap k v) where
532709
533710instance {-# OVERLAPPING #-} ToSchema Object where
534711 declareNamedSchema _ = pure $ NamedSchema (Just "Object") $ mempty
535− & type_ ?~ SwaggerObject
712+ & type_ ?~ OpenApiObject
536713 & description ?~ "Arbitrary JSON object."
537714 & additionalProperties ?~ AdditionalPropertiesAllowed True
538715
@@ -569,11 +746,15 @@ instance ToSchema a => ToSchema (Identity a) where declareNamedSchema _ = declar
569746
570747-- | Default schema for @'Bounded'@, @'Integral'@ types.
571748--
572−-- >>> BSL.putStrLn $ encode $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
573−-- {"maximum":32767,"minimum":-32768,"type":"integer"}
749+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedIntegral (Proxy :: Proxy Int16)
750+-- {
751+-- "maximum": 32767,
752+-- "minimum": -32768,
753+-- "type": "integer"
754+-- }
574755toSchemaBoundedIntegral :: forall a. (Bounded a, Integral a) => Proxy a -> Schema
575756toSchemaBoundedIntegral _ = mempty
576− & type_ ?~ SwaggerInteger
757+ & type_ ?~ OpenApiInteger
577758 & minimum_ ?~ fromInteger (toInteger (minBound :: a))
578759 & maximum_ ?~ fromInteger (toInteger (maxBound :: a))
579760
@@ -602,8 +783,27 @@ genericDeclareNamedSchemaNewtype opts f proxy = genericNameSchema opts proxy <$>
602783-- >>> instance ToSchema ButtonState
603784-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
604785-- >>> type ImageUrl = T.Text
605−-- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
606−-- {"properties":{"Neutral":{"type":"string"},"Focus":{"type":"string"},"Active":{"type":"string"},"Hover":{"type":"string"},"Disabled":{"type":"string"}},"type":"object"}
786+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
787+-- {
788+-- "properties": {
789+-- "Active": {
790+-- "type": "string"
791+-- },
792+-- "Disabled": {
793+-- "type": "string"
794+-- },
795+-- "Focus": {
796+-- "type": "string"
797+-- },
798+-- "Hover": {
799+-- "type": "string"
800+-- },
801+-- "Neutral": {
802+-- "type": "string"
803+-- }
804+-- },
805+-- "type": "object"
806+-- }
607807--
608808-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
609809-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -611,15 +811,15 @@ declareSchemaBoundedEnumKeyMapping :: forall map key value.
611811 (Bounded key, Enum key, ToJSONKey key, ToSchema key, ToSchema value)
612812 => Proxy (map key value) -> Declare (Definitions Schema) Schema
613813declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key of
614− ToJSONKeyText keyToText _ -> objectSchema keyToText
814+ ToJSONKeyText getKey _ -> objectSchema getKey
615815 ToJSONKeyValue _ _ -> declareSchema (Proxy :: Proxy [(key, value)])
616816 where
617− objectSchema keyToText = do
817+ objectSchema getKey = do
618818 valueRef <- declareSchemaRef (Proxy :: Proxy value)
619819 let allKeys = [minBound..maxBound :: key]
620− mkPair k = (keyToText k, valueRef)
820+ mkPair k = (keyToText $ getKey k, valueRef)
621821 return $ mempty
622− & type_ ?~ SwaggerObject
822+ & type_ ?~ OpenApiObject
623823 & properties .~ InsOrdHashMap.fromList (map mkPair allKeys)
624824
625825-- | A 'Schema' for a mapping with 'Bounded' 'Enum' keys.
@@ -630,8 +830,27 @@ declareSchemaBoundedEnumKeyMapping _ = case toJSONKey :: ToJSONKeyFunction key o
630830-- >>> instance ToSchema ButtonState
631831-- >>> instance ToJSONKey ButtonState where toJSONKey = toJSONKeyText (T.pack . show)
632832-- >>> type ImageUrl = T.Text
633−-- >>> BSL.putStrLn $ encode $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
634−-- {"properties":{"Neutral":{"type":"string"},"Focus":{"type":"string"},"Active":{"type":"string"},"Hover":{"type":"string"},"Disabled":{"type":"string"}},"type":"object"}
833+-- >>> BSL.putStrLn $ encodePretty $ toSchemaBoundedEnumKeyMapping (Proxy :: Proxy (Map ButtonState ImageUrl))
834+-- {
835+-- "properties": {
836+-- "Active": {
837+-- "type": "string"
838+-- },
839+-- "Disabled": {
840+-- "type": "string"
841+-- },
842+-- "Focus": {
843+-- "type": "string"
844+-- },
845+-- "Hover": {
846+-- "type": "string"
847+-- },
848+-- "Neutral": {
849+-- "type": "string"
850+-- }
851+-- },
852+-- "type": "object"
853+-- }
635854--
636855-- Note: this is only useful when @key@ is encoded with 'ToJSONKeyText'.
637856-- If it is encoded with 'ToJSONKeyValue' then a regular schema for @[(key, value)]@ is used.
@@ -641,7 +860,7 @@ toSchemaBoundedEnumKeyMapping :: forall map key value.
641860toSchemaBoundedEnumKeyMapping = flip evalDeclare mempty . declareSchemaBoundedEnumKeyMapping
642861
643862-- | A configurable generic @'Schema'@ creator.
644−genericDeclareSchema :: (Generic a, GToSchema (Rep a)) =>
863+genericDeclareSchema :: (Generic a, GToSchema (Rep a), Typeable a) =>
645864 SchemaOptions -> Proxy a -> Declare (Definitions Schema) Schema
646865genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSchema opts proxy
647866
@@ -649,9 +868,24 @@ genericDeclareSchema opts proxy = _namedSchemaSchema <$> genericDeclareNamedSche
649868-- This function applied to @'defaultSchemaOptions'@
650869-- is used as the default for @'declareNamedSchema'@
651870-- when the type is an instance of @'Generic'@.
652−genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a)) =>
871+--
872+-- Default implementation will use the name from 'Typeable' instance, including concrete
873+-- instantioations of type variables.
874+--
875+-- For example:
876+--
877+-- >>> _namedSchemaName $ undeclare $ genericDeclareNamedSchema defaultSchemaOptions (Proxy :: Proxy (Either Int Bool))
878+-- Just "Either_Int_Bool"
879+genericDeclareNamedSchema :: forall a. (Generic a, GToSchema (Rep a), Typeable a) =>
653880 SchemaOptions -> Proxy a -> Declare (Definitions Schema) NamedSchema
654−genericDeclareNamedSchema opts _ = gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
881+genericDeclareNamedSchema opts _ =
882+ rename (Just $ T.pack name) <$> gdeclareNamedSchema opts (Proxy :: Proxy (Rep a)) mempty
883+ where
884+ unspace ' ' = '_'
885+ unspace x = x
886+ orig = fmap unspace $ show $ typeRep @a
887+ name = datatypeNameModifier opts orig
888+
655889
656890-- | Derive a 'Generic'-based name for a datatype and assign it to a given 'Schema'.
657891genericNameSchema :: forall a d f.
@@ -667,19 +901,19 @@ gdatatypeSchemaName opts _ = case orig of
667901 orig = datatypeName (Proxy3 :: Proxy3 d f a)
668902 name = datatypeNameModifier opts orig
669903
670−-- | Lift a plain @'ParamSchema'@ into a model @'NamedSchema'@.
904+-- | Construct 'NamedSchema' usinng 'ToParamSchema'.
671905paramSchemaToNamedSchema :: (ToParamSchema a, Generic a, Rep a ~ D1 d f, Datatype d) =>
672906 SchemaOptions -> Proxy a -> NamedSchema
673907paramSchemaToNamedSchema opts proxy = genericNameSchema opts proxy (paramSchemaToSchema proxy)
674908
675−-- | Lift a plain @'ParamSchema'@ into a model @'Schema'@.
909+-- | Construct 'Schema' usinng 'ToParamSchema'.
676910paramSchemaToSchema :: ToParamSchema a => Proxy a -> Schema
677−paramSchemaToSchema proxy = mempty & paramSchema .~ toParamSchema proxy
911+paramSchemaToSchema = toParamSchema
678912
679913nullarySchema :: Schema
680914nullarySchema = mempty
681− & type_ ?~ SwaggerArray
682− & items ?~ SwaggerItemsArray []
915+ & type_ ?~ OpenApiArray
916+ & items ?~ OpenApiItemsArray []
683917
684918gtoNamedSchema :: GToSchema f => SchemaOptions -> Proxy f -> NamedSchema
685919gtoNamedSchema opts proxy = undeclare $ gdeclareNamedSchema opts proxy mempty
@@ -709,12 +943,15 @@ instance (Selector s, GToSchema f, GToSchema (S1 s f)) => GToSchema (C1 c (S1 s
709943 | unwrapUnaryRecords opts = fieldSchema
710944 | otherwise =
711945 case schema ^. items of
712− Just (SwaggerItemsArray [_]) -> fieldSchema
946+ Just (OpenApiItemsArray [_]) -> fieldSchema
713947 _ -> do
714− declare defs
715− return (unnamed schema)
948+ -- We have to run recordSchema instead of just using its defs,
949+ -- since those can be recursive and will lead to infinite loop,
950+ -- see https://github.com/biocad/openapi3/pull/37
951+ NamedSchema _ schema' <- recordSchema
952+ return (unnamed schema')
716953 where
717− (defs, NamedSchema _ schema) = runDeclare recordSchema mempty
954+ (_, NamedSchema _ schema) = runDeclare recordSchema mempty
718955 recordSchema = gdeclareNamedSchema opts (Proxy :: Proxy (S1 s f)) s
719956 fieldSchema = gdeclareNamedSchema opts (Proxy :: Proxy f) s
720957
@@ -737,10 +974,10 @@ gdeclareSchemaRef opts proxy = do
737974 return $ Ref (Reference name)
738975 _ -> Inline <$> gdeclareSchema opts proxy
739976
740−appendItem :: Referenced Schema -> Maybe SwaggerItems -> Maybe SwaggerItems
741−appendItem x Nothing = Just (SwaggerItemsArray [x])
742−appendItem x (Just (SwaggerItemsArray xs)) = Just (SwaggerItemsArray (xs ++ [x]))
743−appendItem _ _ = error "GToSchema.appendItem: cannot append to SwaggerItemsObject"
977+appendItem :: Referenced Schema -> Maybe OpenApiItems -> Maybe OpenApiItems
978+appendItem x Nothing = Just (OpenApiItemsArray [x])
979+appendItem x (Just (OpenApiItemsArray xs)) = Just (OpenApiItemsArray (xs ++ [x]))
980+appendItem _ _ = error "GToSchema.appendItem: cannot append to OpenApiItemsObject"
744981
745982withFieldSchema :: forall proxy s f. (Selector s, GToSchema f) =>
746983 SchemaOptions -> proxy s f -> Bool -> Schema -> Declare (Definitions Schema) Schema
@@ -749,12 +986,12 @@ withFieldSchema opts _ isRequiredField schema = do
749986 return $
750987 if T.null fname
751988 then schema
752− & type_ ?~ SwaggerArray
989+ & type_ ?~ OpenApiArray
753990 & items %~ appendItem ref
754991 & maxItems %~ Just . maybe 1 (+1) -- increment maxItems
755992 & minItems %~ Just . maybe 1 (+1) -- increment minItems
756993 else schema
757− & type_ ?~ SwaggerObject
994+ & type_ ?~ OpenApiObject
758995 & properties . at fname ?~ ref
759996 & if isRequiredField
760997 then required %~ (++ [fname])
@@ -789,14 +1026,14 @@ gdeclareNamedSumSchema opts proxy _
7891026 | otherwise = do
7901027 (schemas, _) <- runWriterT declareSumSchema
7911028 return $ unnamed $ mempty
792− & type_ ?~ SwaggerObject
1029+ & type_ ?~ OpenApiObject
7931030 & oneOf ?~ (snd <$> schemas)
7941031 where
7951032 declareSumSchema = gsumToSchema opts proxy
7961033 (sumSchemas, All allNullary) = undeclare (runWriterT declareSumSchema)
7971034
7981035 toStringTag schemas = mempty
799− & type_ ?~ SwaggerString
1036+ & type_ ?~ OpenApiString
8001037 & enum_ ?~ map (String . fst) sumSchemas
8011038
8021039type AllNullary = All
@@ -818,15 +1055,15 @@ gsumConToSchemaWith ref opts _ = (tag, schema)
8181055 case ref of
8191056 -- If subschema is an object and constructor is a record, we add tag directly
8201057 -- to the record, as Aeson does it.
821− Just (Inline sub) | sub ^. type_ == Just SwaggerObject && isRecord -> Inline $ sub
1058+ Just (Inline sub) | sub ^. type_ == Just OpenApiObject && isRecord -> Inline $ sub
8221059 & required <>~ [T.pack tagField]
823− & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ SwaggerString & enum_ ?~ [String tag])
1060+ & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag])
8241061
8251062 -- If it is not a record, we need to put subschema into "contents" field.
8261063 _ | not isRecord -> Inline $ mempty
827− & type_ ?~ SwaggerObject
1064+ & type_ ?~ OpenApiObject
8281065 & required .~ [T.pack tagField]
829− & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ SwaggerString & enum_ ?~ [String tag])
1066+ & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag])
8301067 -- If constructor is nullary, there is no content.
8311068 & case ref of
8321069 Just r -> (properties . at (T.pack contentsField) ?~ r) . (required <>~ [T.pack contentsField])
@@ -834,17 +1071,17 @@ gsumConToSchemaWith ref opts _ = (tag, schema)
8341071
8351072 -- In the remaining cases we combine "tag" object and "contents" object using allOf.
8361073 _ -> Inline $ mempty
837− & type_ ?~ SwaggerObject
1074+ & type_ ?~ OpenApiObject
8381075 & allOf ?~ [Inline $ mempty
839− & type_ ?~ SwaggerObject
1076+ & type_ ?~ OpenApiObject
8401077 & required .~ (T.pack tagField : if isRecord then [] else [T.pack contentsField])
841− & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ SwaggerString & enum_ ?~ [String tag])]
1078+ & properties . at (T.pack tagField) ?~ (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag])]
8421079 & if isRecord
8431080 then allOf . _Just <>~ [refOrNullary]
844− else allOf . _Just <>~ [Inline $ mempty & type_ ?~ SwaggerObject & properties . at (T.pack contentsField) ?~ refOrNullary]
1081+ else allOf . _Just <>~ [Inline $ mempty & type_ ?~ OpenApiObject & properties . at (T.pack contentsField) ?~ refOrNullary]
8451082 UntaggedValue -> refOrEnum -- Aeson encodes nullary constructors as strings in this case.
8461083 ObjectWithSingleField -> Inline $ mempty
847− & type_ ?~ SwaggerObject
1084+ & type_ ?~ OpenApiObject
8481085 & required .~ [tag]
8491086 & properties . at tag ?~ refOrNullary
8501087 TwoElemArray -> error "unrepresentable in OpenAPI 3"
@@ -852,7 +1089,7 @@ gsumConToSchemaWith ref opts _ = (tag, schema)
8521089 tag = T.pack (constructorTagModifier opts (conName (Proxy3 :: Proxy3 c f p)))
8531090 isRecord = conIsRecord (Proxy3 :: Proxy3 c f p)
8541091 refOrNullary = fromMaybe (Inline nullarySchema) ref
855− refOrEnum = fromMaybe (Inline $ mempty & type_ ?~ SwaggerString & enum_ ?~ [String tag]) ref
1092+ refOrEnum = fromMaybe (Inline $ mempty & type_ ?~ OpenApiString & enum_ ?~ [String tag]) ref
8561093
8571094gsumConToSchema :: (GToSchema (C1 c f), Constructor c) =>
8581095 SchemaOptions -> Proxy (C1 c f) -> Declare (Definitions Schema) [(T.Text, Referenced Schema)]
@@ -877,7 +1114,13 @@ data Proxy2 a b = Proxy2
8771114
8781115data Proxy3 a b c = Proxy3
8791116
880−-- $setup
881−-- >>> import Data.Swagger
882−-- >>> import Data.Aeson (encode)
883−-- >>> import Data.Aeson.Types (toJSONKeyText)
1117+{- $setup
1118+>>> import Data.OpenApi
1119+>>> import Data.Aeson (encode)
1120+>>> import Data.Aeson.Types (toJSONKeyText)
1121+>>> import Data.OpenApi.Internal.Utils
1122+>>> :set -XScopedTypeVariables
1123+>>> :set -XDeriveAnyClass
1124+>>> :set -XStandaloneDeriving
1125+>>> :set -XTypeApplications
1126+-}