git.iko.soy/typeable/openapi3
on master help sign in
SwaggerSpec passes
8087980Maxim Koltsov
src/Data/Swagger.hs+1
@@ -90,6 +90,7 @@ module Data.Swagger (
90 -- ** Security
90 -- ** Security
91 SecurityScheme(..),
91 SecurityScheme(..),
92 SecuritySchemeType(..),
92 SecuritySchemeType(..),
93+ SecurityDefinitions(..),
93 SecurityRequirement(..),
94 SecurityRequirement(..),
94
95
95 -- *** API key
96 -- *** API key
src/Data/Swagger/Internal.hs+56−16
@@ -684,8 +684,6 @@ data NamedSchema = NamedSchema
684-- | Regex pattern for @string@ type.
684-- | Regex pattern for @string@ type.
685type Pattern = Text
685type Pattern = Text
686
686
687−-- TODO examples for params
688−
689data ParamSchema (t :: SwaggerKind *) = ParamSchema
687data ParamSchema (t :: SwaggerKind *) = ParamSchema
690 { -- | Declares the value of the parameter that the server will use if none is provided,
688 { -- | Declares the value of the parameter that the server will use if none is provided,
691 -- for example a @"count"@ to control the number of results per page might default to @100@
689 -- for example a @"count"@ to control the number of results per page might default to @100@
@@ -785,7 +783,7 @@ data Header = Header
785 { -- | A short description of the header.
783 { -- | A short description of the header.
786 _headerDescription :: Maybe Text
784 _headerDescription :: Maybe Text
787
785
788− , _headerParamSchema :: ParamSchema ('SwaggerKindNormal Header)
786+ , _headerSchema :: ParamSchema ('SwaggerKindNormal Header)
789 } deriving (Eq, Show, Generic, Data, Typeable)
787 } deriving (Eq, Show, Generic, Data, Typeable)
790
788
791-- | The location of the API key.
789-- | The location of the API key.
@@ -867,6 +865,10 @@ data SecurityScheme = SecurityScheme
867 , _securitySchemeDescription :: Maybe Text
865 , _securitySchemeDescription :: Maybe Text
868 } deriving (Eq, Show, Generic, Data, Typeable)
866 } deriving (Eq, Show, Generic, Data, Typeable)
869
867
868+newtype SecurityDefinitions
869+ = SecurityDefinitions (Definitions SecurityScheme)
870+ deriving (Eq, Show, Generic, Data, Typeable)
871+
870-- | Lists the required security schemes to execute this operation.
872-- | Lists the required security schemes to execute this operation.
871-- The object can have multiple security schemes declared in it which are all required
873-- The object can have multiple security schemes declared in it which are all required
872-- (that is, there is a logical AND between the schemes).
874-- (that is, there is a logical AND between the schemes).
@@ -932,6 +934,7 @@ data AdditionalProperties
932-- Generic instances
934-- Generic instances
933-------------------------------------------------------------------------------
935-------------------------------------------------------------------------------
934
936
937+deriveGeneric ''Server
935deriveGeneric ''Components
938deriveGeneric ''Components
936deriveGeneric ''Header
939deriveGeneric ''Header
937deriveGeneric ''OAuth2Flow
940deriveGeneric ''OAuth2Flow
@@ -1044,12 +1047,38 @@ instance Monoid Operation where
1044 mempty = genericMempty
1047 mempty = genericMempty
1045 mappend = (<>)
1048 mappend = (<>)
1046
1049
1050+instance Semigroup (OAuth2Flow p) where
1051+ l@OAuth2Flow{ _oAath2RefreshUrl = lUrl, _oAuth2Scopes = lScopes }
1052+ <> OAuth2Flow { _oAath2RefreshUrl = rUrl, _oAuth2Scopes = rScopes } =
1053+ l { _oAath2RefreshUrl = swaggerMappend lUrl rUrl, _oAuth2Scopes = lScopes <> rScopes }
1054+
1055+-- swaggerMappend has First-like semantics, and here we need mappend'ing under Maybes.
1047instance Semigroup OAuth2Flows where
1056instance Semigroup OAuth2Flows where
1048− (<>) = genericMappend
1057+ l <> r = OAuth2Flows
1058+ { _oAuth2FlowsImplicit = _oAuth2FlowsImplicit l <> _oAuth2FlowsImplicit r
1059+ , _oAuth2FlowsPassword = _oAuth2FlowsPassword l <> _oAuth2FlowsPassword r
1060+ , _oAuth2FlowsClientCredentials = _oAuth2FlowsClientCredentials l <> _oAuth2FlowsClientCredentials r
1061+ , _oAuth2FlowsAuthorizationCode = _oAuth2FlowsAuthorizationCode l <> _oAuth2FlowsAuthorizationCode r
1062+ }
1063+
1049instance Monoid OAuth2Flows where
1064instance Monoid OAuth2Flows where
1050 mempty = genericMempty
1065 mempty = genericMempty
1051 mappend = (<>)
1066 mappend = (<>)
1052
1067
1068+instance Semigroup SecurityScheme where
1069+ SecurityScheme (SecuritySchemeOAuth2 lFlows) lDesc
1070+ <> SecurityScheme (SecuritySchemeOAuth2 rFlows) rDesc =
1071+ SecurityScheme (SecuritySchemeOAuth2 $ lFlows <> rFlows) (swaggerMappend lDesc rDesc)
1072+ l <> _ = l
1073+
1074+instance Semigroup SecurityDefinitions where
1075+ (SecurityDefinitions sd1) <> (SecurityDefinitions sd2) =
1076+ SecurityDefinitions $ InsOrdHashMap.unionWith (<>) sd1 sd2
1077+
1078+instance Monoid SecurityDefinitions where
1079+ mempty = SecurityDefinitions InsOrdHashMap.empty
1080+ mappend = (<>)
1081+
1053instance Semigroup RequestBody where
1082instance Semigroup RequestBody where
1054 (<>) = genericMappend
1083 (<>) = genericMappend
1055instance Monoid RequestBody where
1084instance Monoid RequestBody where
@@ -1108,9 +1137,6 @@ instance ToJSON Contact where
1108instance ToJSON License where
1137instance ToJSON License where
1109 toJSON = genericToJSON (jsonPrefix "License")
1138 toJSON = genericToJSON (jsonPrefix "License")
1110
1139
1111−instance ToJSON Server where
1112− toJSON = genericToJSON (jsonPrefix "Server")
1113−
1114instance ToJSON ServerVariable where
1140instance ToJSON ServerVariable where
1115 toJSON = genericToJSON (jsonPrefix "ServerVariable")
1141 toJSON = genericToJSON (jsonPrefix "ServerVariable")
1116
1142
@@ -1160,9 +1186,6 @@ instance FromJSON Contact where
1160instance FromJSON License where
1186instance FromJSON License where
1161 parseJSON = genericParseJSON (jsonPrefix "License")
1187 parseJSON = genericParseJSON (jsonPrefix "License")
1162
1188
1163−instance FromJSON Server where
1164− parseJSON = genericParseJSON (jsonPrefix "Server")
1165−
1166instance FromJSON ServerVariable where
1189instance FromJSON ServerVariable where
1167 parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
1190 parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
1168
1191
@@ -1211,9 +1234,10 @@ instance ToJSON SecuritySchemeType where
1211 toJSON (SecuritySchemeApiKey params)
1234 toJSON (SecuritySchemeApiKey params)
1212 = toJSON params
1235 = toJSON params
1213 <+> object [ "type" .= ("apiKey" :: Text) ]
1236 <+> object [ "type" .= ("apiKey" :: Text) ]
1214− toJSON (SecuritySchemeOAuth2 params)
1237+ toJSON (SecuritySchemeOAuth2 params) = object
1215− = toJSON params
1216− <+> object [ "type" .= ("oauth2" :: Text) ]
1238+ [ "type" .= ("oauth2" :: Text)
1239+ , "flows" .= toJSON params
1240+ ]
1217 toJSON (SecuritySchemeOpenIdConnect url) = object
1241 toJSON (SecuritySchemeOpenIdConnect url) = object
1218 [ "type" .= ("openIdConnect" :: Text)
1242 [ "type" .= ("openIdConnect" :: Text)
1219 , "openIdConnectUrl" .= url
1243 , "openIdConnectUrl" .= url
@@ -1226,6 +1250,10 @@ instance ToJSON Swagger where
1226 else id
1250 else id
1227 toEncoding = sopSwaggerGenericToEncoding
1251 toEncoding = sopSwaggerGenericToEncoding
1228
1252
1253+instance ToJSON Server where
1254+ toJSON = sopSwaggerGenericToJSON
1255+ toEncoding = sopSwaggerGenericToEncoding
1256+
1229instance ToJSON SecurityScheme where
1257instance ToJSON SecurityScheme where
1230 toJSON = sopSwaggerGenericToJSON
1258 toJSON = sopSwaggerGenericToJSON
1231 toEncoding = sopSwaggerGenericToEncoding
1259 toEncoding = sopSwaggerGenericToEncoding
@@ -1299,6 +1327,9 @@ instance ToJSON Encoding where
1299 toJSON = sopSwaggerGenericToJSON
1327 toJSON = sopSwaggerGenericToJSON
1300 toEncoding = sopSwaggerGenericToEncoding
1328 toEncoding = sopSwaggerGenericToEncoding
1301
1329
1330+instance ToJSON SecurityDefinitions where
1331+ toJSON (SecurityDefinitions sd) = toJSON sd
1332+
1302instance ToJSON Reference where
1333instance ToJSON Reference where
1303 toJSON (Reference ref) = object [ "$ref" .= ref ]
1334 toJSON (Reference ref) = object [ "$ref" .= ref ]
1304
1335
@@ -1355,7 +1386,7 @@ instance FromJSON SecuritySchemeType where
1355 case t of
1386 case t of
1356 "http" -> pure SecuritySchemeHttp
1387 "http" -> pure SecuritySchemeHttp
1357 "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1388 "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1358− "oauth2" -> SecuritySchemeOAuth2 <$> parseJSON js
1389+ "oauth2" -> SecuritySchemeOAuth2 <$> (o .: "flows")
1359 "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
1390 "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
1360 _ -> empty
1391 _ -> empty
1361 parseJSON _ = empty
1392 parseJSON _ = empty
@@ -1363,6 +1394,9 @@ instance FromJSON SecuritySchemeType where
1363instance FromJSON Swagger where
1394instance FromJSON Swagger where
1364 parseJSON = sopSwaggerGenericParseJSON
1395 parseJSON = sopSwaggerGenericParseJSON
1365
1396
1397+instance FromJSON Server where
1398+ parseJSON = sopSwaggerGenericParseJSON
1399+
1366instance FromJSON SecurityScheme where
1400instance FromJSON SecurityScheme where
1367 parseJSON = sopSwaggerGenericParseJSON
1401 parseJSON = sopSwaggerGenericParseJSON
1368
1402
@@ -1433,6 +1467,9 @@ instance FromJSON Operation where
1433instance FromJSON PathItem where
1467instance FromJSON PathItem where
1434 parseJSON = sopSwaggerGenericParseJSON
1468 parseJSON = sopSwaggerGenericParseJSON
1435
1469
1470+instance FromJSON SecurityDefinitions where
1471+ parseJSON js = SecurityDefinitions <$> parseJSON js
1472+
1436instance FromJSON RequestBody where
1473instance FromJSON RequestBody where
1437 parseJSON = sopSwaggerGenericParseJSON
1474 parseJSON = sopSwaggerGenericParseJSON
1438
1475
@@ -1499,10 +1536,12 @@ instance FromJSON AdditionalProperties where
1499 parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
1536 parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
1500 parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
1537 parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
1501
1538
1539+instance HasSwaggerAesonOptions Server where
1540+ swaggerAesonOptions _ = mkSwaggerAesonOptions "server"
1502instance HasSwaggerAesonOptions Components where
1541instance HasSwaggerAesonOptions Components where
1503 swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
1542 swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
1504instance HasSwaggerAesonOptions Header where
1543instance HasSwaggerAesonOptions Header where
1505− swaggerAesonOptions _ = mkSwaggerAesonOptions "header" & saoSubObject ?~ "paramSchema"
1544+ swaggerAesonOptions _ = mkSwaggerAesonOptions "header"
1506instance AesonDefaultValue p => HasSwaggerAesonOptions (OAuth2Flow p) where
1545instance AesonDefaultValue p => HasSwaggerAesonOptions (OAuth2Flow p) where
1507 swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "params"
1546 swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "params"
1508instance HasSwaggerAesonOptions OAuth2Flows where
1547instance HasSwaggerAesonOptions OAuth2Flows where
@@ -1526,7 +1565,7 @@ instance HasSwaggerAesonOptions SecurityScheme where
1526instance HasSwaggerAesonOptions Schema where
1565instance HasSwaggerAesonOptions Schema where
1527 swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1566 swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
1528instance HasSwaggerAesonOptions Swagger where
1567instance HasSwaggerAesonOptions Swagger where
1529− swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.3")]
1568+ swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.0")]
1530instance HasSwaggerAesonOptions Example where
1569instance HasSwaggerAesonOptions Example where
1531 swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
1570 swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
1532instance HasSwaggerAesonOptions Encoding where
1571instance HasSwaggerAesonOptions Encoding where
@@ -1540,6 +1579,7 @@ instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindParamOtherSchema) where
1540instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindSchema) where
1579instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindSchema) where
1541 swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema"
1580 swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema"
1542
1581
1582+instance AesonDefaultValue Server
1543instance AesonDefaultValue Components
1583instance AesonDefaultValue Components
1544instance AesonDefaultValue (ParamSchema s)
1584instance AesonDefaultValue (ParamSchema s)
1545instance AesonDefaultValue OAuth2ImplicitFlow
1585instance AesonDefaultValue OAuth2ImplicitFlow
src/Data/Swagger/Lens.hs+2−2
@@ -107,12 +107,12 @@ instance At Operation where at n = responses . at n
107instance HasParamSchema NamedSchema (ParamSchema 'SwaggerKindSchema) where paramSchema = schema.paramSchema
107instance HasParamSchema NamedSchema (ParamSchema 'SwaggerKindSchema) where paramSchema = schema.paramSchema
108
108
109-- HasType instances
109-- HasType instances
110−instance HasType Header (Maybe (SwaggerType ('SwaggerKindNormal Header))) where type_ = paramSchema.type_
110+instance HasType Header (Maybe (SwaggerType ('SwaggerKindNormal Header))) where type_ = schema.type_
111instance HasType Schema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
111instance HasType Schema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
112instance HasType NamedSchema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
112instance HasType NamedSchema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
113
113
114-- HasDefault instances
114-- HasDefault instances
115−instance HasDefault Header (Maybe Value) where default_ = paramSchema.default_
115+instance HasDefault Header (Maybe Value) where default_ = schema.default_
116instance HasDefault Schema (Maybe Value) where default_ = paramSchema.default_
116instance HasDefault Schema (Maybe Value) where default_ = paramSchema.default_
117
117
118-- OVERLAPPABLE instances
118-- OVERLAPPABLE instances
src/Data/Swagger/Optics.hs+16−16
@@ -188,7 +188,7 @@ instance
188 ( a ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
188 ( a ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
189 , b ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
189 , b ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
190 ) => LabelOptic "type" A_Lens Header Header a b where
190 ) => LabelOptic "type" A_Lens Header Header a b where
191− labelOptic = #paramSchema % #type
191+ labelOptic = #schema % #type
192 {-# INLINE labelOptic #-}
192 {-# INLINE labelOptic #-}
193
193
194instance
194instance
@@ -210,7 +210,7 @@ instance
210instance
210instance
211 ( a ~ Maybe Value, b ~ Maybe Value
211 ( a ~ Maybe Value, b ~ Maybe Value
212 ) => LabelOptic "default" A_Lens Header Header a b where
212 ) => LabelOptic "default" A_Lens Header Header a b where
213− labelOptic = #paramSchema % #default
213+ labelOptic = #schema % #default
214 {-# INLINE labelOptic #-}
214 {-# INLINE labelOptic #-}
215
215
216instance
216instance
@@ -230,7 +230,7 @@ instance
230instance
230instance
231 ( a ~ Maybe Format, b ~ Maybe Format
231 ( a ~ Maybe Format, b ~ Maybe Format
232 ) => LabelOptic "format" A_Lens Header Header a b where
232 ) => LabelOptic "format" A_Lens Header Header a b where
233− labelOptic = #paramSchema % #format
233+ labelOptic = #schema % #format
234 {-# INLINE labelOptic #-}
234 {-# INLINE labelOptic #-}
235
235
236instance
236instance
@@ -251,7 +251,7 @@ instance
251 ( a ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
251 ( a ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
252 , b ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
252 , b ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
253 ) => LabelOptic "items" A_Lens Header Header a b where
253 ) => LabelOptic "items" A_Lens Header Header a b where
254− labelOptic = #paramSchema % #items
254+ labelOptic = #schema % #items
255 {-# INLINE labelOptic #-}
255 {-# INLINE labelOptic #-}
256
256
257instance
257instance
@@ -273,7 +273,7 @@ instance
273instance
273instance
274 ( a ~ Maybe Scientific, b ~ Maybe Scientific
274 ( a ~ Maybe Scientific, b ~ Maybe Scientific
275 ) => LabelOptic "maximum" A_Lens Header Header a b where
275 ) => LabelOptic "maximum" A_Lens Header Header a b where
276− labelOptic = #paramSchema % #maximum
276+ labelOptic = #schema % #maximum
277 {-# INLINE labelOptic #-}
277 {-# INLINE labelOptic #-}
278
278
279instance
279instance
@@ -293,7 +293,7 @@ instance
293instance
293instance
294 ( a ~ Maybe Bool, b ~ Maybe Bool
294 ( a ~ Maybe Bool, b ~ Maybe Bool
295 ) => LabelOptic "exclusiveMaximum" A_Lens Header Header a b where
295 ) => LabelOptic "exclusiveMaximum" A_Lens Header Header a b where
296− labelOptic = #paramSchema % #exclusiveMaximum
296+ labelOptic = #schema % #exclusiveMaximum
297 {-# INLINE labelOptic #-}
297 {-# INLINE labelOptic #-}
298
298
299instance
299instance
@@ -313,7 +313,7 @@ instance
313instance
313instance
314 ( a ~ Maybe Scientific, b ~ Maybe Scientific
314 ( a ~ Maybe Scientific, b ~ Maybe Scientific
315 ) => LabelOptic "minimum" A_Lens Header Header a b where
315 ) => LabelOptic "minimum" A_Lens Header Header a b where
316− labelOptic = #paramSchema % #minimum
316+ labelOptic = #schema % #minimum
317 {-# INLINE labelOptic #-}
317 {-# INLINE labelOptic #-}
318
318
319instance
319instance
@@ -333,7 +333,7 @@ instance
333instance
333instance
334 ( a ~ Maybe Bool, b ~ Maybe Bool
334 ( a ~ Maybe Bool, b ~ Maybe Bool
335 ) => LabelOptic "exclusiveMinimum" A_Lens Header Header a b where
335 ) => LabelOptic "exclusiveMinimum" A_Lens Header Header a b where
336− labelOptic = #paramSchema % #exclusiveMinimum
336+ labelOptic = #schema % #exclusiveMinimum
337 {-# INLINE labelOptic #-}
337 {-# INLINE labelOptic #-}
338
338
339instance
339instance
@@ -353,7 +353,7 @@ instance
353instance
353instance
354 ( a ~ Maybe Integer, b ~ Maybe Integer
354 ( a ~ Maybe Integer, b ~ Maybe Integer
355 ) => LabelOptic "maxLength" A_Lens Header Header a b where
355 ) => LabelOptic "maxLength" A_Lens Header Header a b where
356− labelOptic = #paramSchema % #maxLength
356+ labelOptic = #schema % #maxLength
357 {-# INLINE labelOptic #-}
357 {-# INLINE labelOptic #-}
358
358
359instance
359instance
@@ -373,7 +373,7 @@ instance
373instance
373instance
374 ( a ~ Maybe Integer, b ~ Maybe Integer
374 ( a ~ Maybe Integer, b ~ Maybe Integer
375 ) => LabelOptic "minLength" A_Lens Header Header a b where
375 ) => LabelOptic "minLength" A_Lens Header Header a b where
376− labelOptic = #paramSchema % #minLength
376+ labelOptic = #schema % #minLength
377 {-# INLINE labelOptic #-}
377 {-# INLINE labelOptic #-}
378
378
379instance
379instance
@@ -393,7 +393,7 @@ instance
393instance
393instance
394 ( a ~ Maybe Text, b ~ Maybe Text
394 ( a ~ Maybe Text, b ~ Maybe Text
395 ) => LabelOptic "pattern" A_Lens Header Header a b where
395 ) => LabelOptic "pattern" A_Lens Header Header a b where
396− labelOptic = #paramSchema % #pattern
396+ labelOptic = #schema % #pattern
397 {-# INLINE labelOptic #-}
397 {-# INLINE labelOptic #-}
398
398
399instance
399instance
@@ -413,7 +413,7 @@ instance
413instance
413instance
414 ( a ~ Maybe Integer, b ~ Maybe Integer
414 ( a ~ Maybe Integer, b ~ Maybe Integer
415 ) => LabelOptic "maxItems" A_Lens Header Header a b where
415 ) => LabelOptic "maxItems" A_Lens Header Header a b where
416− labelOptic = #paramSchema % #maxItems
416+ labelOptic = #schema % #maxItems
417 {-# INLINE labelOptic #-}
417 {-# INLINE labelOptic #-}
418
418
419instance
419instance
@@ -433,7 +433,7 @@ instance
433instance
433instance
434 ( a ~ Maybe Integer, b ~ Maybe Integer
434 ( a ~ Maybe Integer, b ~ Maybe Integer
435 ) => LabelOptic "minItems" A_Lens Header Header a b where
435 ) => LabelOptic "minItems" A_Lens Header Header a b where
436− labelOptic = #paramSchema % #minItems
436+ labelOptic = #schema % #minItems
437 {-# INLINE labelOptic #-}
437 {-# INLINE labelOptic #-}
438
438
439instance
439instance
@@ -453,7 +453,7 @@ instance
453instance
453instance
454 ( a ~ Maybe Bool, b ~ Maybe Bool
454 ( a ~ Maybe Bool, b ~ Maybe Bool
455 ) => LabelOptic "uniqueItems" A_Lens Header Header a b where
455 ) => LabelOptic "uniqueItems" A_Lens Header Header a b where
456− labelOptic = #paramSchema % #uniqueItems
456+ labelOptic = #schema % #uniqueItems
457 {-# INLINE labelOptic #-}
457 {-# INLINE labelOptic #-}
458
458
459instance
459instance
@@ -473,7 +473,7 @@ instance
473instance
473instance
474 ( a ~ Maybe [Value], b ~ Maybe [Value]
474 ( a ~ Maybe [Value], b ~ Maybe [Value]
475 ) => LabelOptic "enum" A_Lens Header Header a b where
475 ) => LabelOptic "enum" A_Lens Header Header a b where
476− labelOptic = #paramSchema % #enum
476+ labelOptic = #schema % #enum
477 {-# INLINE labelOptic #-}
477 {-# INLINE labelOptic #-}
478
478
479instance
479instance
@@ -493,7 +493,7 @@ instance
493instance
493instance
494 ( a ~ Maybe Scientific, b ~ Maybe Scientific
494 ( a ~ Maybe Scientific, b ~ Maybe Scientific
495 ) => LabelOptic "multipleOf" A_Lens Header Header a b where
495 ) => LabelOptic "multipleOf" A_Lens Header Header a b where
496− labelOptic = #paramSchema % #multipleOf
496+ labelOptic = #schema % #multipleOf
497 {-# INLINE labelOptic #-}
497 {-# INLINE labelOptic #-}
498
498
499instance
499instance
test/Data/Swagger/SchemaSpec.hs+7−7
@@ -24,37 +24,37 @@ import Test.Hspec
24import qualified Data.HashMap.Strict as HM
24import qualified Data.HashMap.Strict as HM
25import Data.Time.LocalTime
25import Data.Time.LocalTime
26
26
27−checkToSchema :: ToSchema a => Proxy a -> Value -> Spec
27+checkToSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
28checkToSchema proxy js = toSchema proxy <=> js
28checkToSchema proxy js = toSchema proxy <=> js
29
29
30−checkSchemaName :: ToSchema a => Maybe String -> Proxy a -> Spec
30+checkSchemaName :: (HasCallStack, ToSchema a) => Maybe String -> Proxy a -> Spec
31checkSchemaName sname proxy =
31checkSchemaName sname proxy =
32 it ("schema name is " ++ show sname) $
32 it ("schema name is " ++ show sname) $
33 schemaName proxy `shouldBe` fmap Text.pack sname
33 schemaName proxy `shouldBe` fmap Text.pack sname
34
34
35−checkDefs :: ToSchema a => Proxy a -> [String] -> Spec
35+checkDefs :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
36checkDefs proxy names =
36checkDefs proxy names =
37 it ("uses these definitions " ++ show names) $
37 it ("uses these definitions " ++ show names) $
38 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
38 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
39 where
39 where
40 defs = execDeclare (declareNamedSchema proxy) mempty
40 defs = execDeclare (declareNamedSchema proxy) mempty
41
41
42−checkProperties :: ToSchema a => Proxy a -> [String] -> Spec
42+checkProperties :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
43checkProperties proxy names =
43checkProperties proxy names =
44 it ("has these fields in order " ++ show names) $
44 it ("has these fields in order " ++ show names) $
45 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
45 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
46 where
46 where
47 fields = toSchema proxy ^. properties
47 fields = toSchema proxy ^. properties
48
48
49−checkInlinedSchema :: ToSchema a => Proxy a -> Value -> Spec
49+checkInlinedSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
50checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
50checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
51
51
52−checkInlinedSchemas :: ToSchema a => [String] -> Proxy a -> Value -> Spec
52+checkInlinedSchemas :: (HasCallStack, ToSchema a) => [String] -> Proxy a -> Value -> Spec
53checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
53checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
54 where
54 where
55 (defs, s) = runDeclare (declareSchema proxy) mempty
55 (defs, s) = runDeclare (declareSchema proxy) mempty
56
56
57−checkInlinedRecSchema :: ToSchema a => Proxy a -> Value -> Spec
57+checkInlinedRecSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
58checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
58checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
59 where
59 where
60 (defs, s) = runDeclare (declareSchema proxy) mempty
60 (defs, s) = runDeclare (declareSchema proxy) mempty
test/Data/SwaggerSpec.hs+397−1166
@@ -21,14 +21,6 @@ import Test.Hspec hiding (example)
21
21
22spec :: Spec
22spec :: Spec
23spec = do
23spec = do
24− describe "host" $ do
25− it "can decode the host port" $ do
26− let h = Just $ Host "localhost" (Just (fromInteger 8000))
27− swagger :: Swagger
28− swagger = swaggerExample
29− & host .~ h
30− parsed :: Swagger = either error id $ eitherDecode' $ encode swagger
31− parsed ^. host `shouldBe` h
32 describe "License Object" $ licenseExample <=> licenseExampleJSON
24 describe "License Object" $ licenseExample <=> licenseExampleJSON
33 describe "Contact Object" $ contactExample <=> contactExampleJSON
25 describe "Contact Object" $ contactExample <=> contactExampleJSON
34 describe "Info Object" $ infoExample <=> infoExampleJSON
26 describe "Info Object" $ infoExample <=> infoExampleJSON
@@ -48,9 +40,11 @@ spec = do
48 describe "Swagger Object" $ do
40 describe "Swagger Object" $ do
49 context "Example with no paths" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
41 context "Example with no paths" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
50 context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
42 context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
51− context "PetStore Example" $
43+ context "PetStore Example" $ do
52 it "decodes successfully" $ do
44 it "decodes successfully" $ do
53 fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: Swagger) -> True; _ -> False)
45 fromJSON petstoreExampleJSON `shouldSatisfy` (\x -> case x of Success (_ :: Swagger) -> True; _ -> False)
46+ it "roundtrips: fmap toJSON . fromJSON" $ do
47+ (toJSON :: Swagger -> Value) <$> fromJSON petstoreExampleJSON `shouldBe` Success petstoreExampleJSON
54
48
55main :: IO ()
49main :: IO ()
56main = hspec spec
50main = hspec spec
@@ -133,34 +127,23 @@ operationExample = mempty
133 & summary ?~ "Updates a pet in the store with form data"
127 & summary ?~ "Updates a pet in the store with form data"
134 & description ?~ ""
128 & description ?~ ""
135 & operationId ?~ "updatePetWithForm"
129 & operationId ?~ "updatePetWithForm"
136− & consumes ?~ MimeList ["application/x-www-form-urlencoded"]
137− & produces ?~ MimeList ["application/json", "application/xml"]
138− & parameters .~ map Inline
130+ & parameters .~ [Inline (mempty
139− [ mempty
140− & name .~ "petId"
131+ & name .~ "petId"
141− & description ?~ "ID of pet that needs to be updated"
132+ & description ?~ "ID of pet that needs to be updated"
142− & required ?~ True
133+ & required ?~ True
143− & schema .~ ParamOther (stringSchema ParamPath)
134+ & in_ .~ ParamPath
144− , mempty
135+ & schema ?~ Inline (mempty & type_ ?~ SwaggerString))]
145− & name .~ "name"
136+ & requestBody ?~ Inline (
137+ mempty & content . at "application/x-www-form-urlencoded" ?~ (mempty & schema ?~ (Inline (mempty
138+ & properties . at "petId" ?~ Inline (mempty
146− & description ?~ "Updated name of the pet"
139+ & description ?~ "Updated name of the pet"
147− & required ?~ False
140+ & type_ ?~ SwaggerString)
148− & schema .~ ParamOther (stringSchema ParamFormData)
141+ & properties . at "status" ?~ Inline (mempty
149− , mempty
150− & name .~ "status"
151− & description ?~ "Updated status of the pet"
142+ & description ?~ "Updated status of the pet"
152− & required ?~ False
143+ & type_ ?~ SwaggerString)))))
153− & schema .~ ParamOther (stringSchema ParamFormData)
154− ]
155−
156 & at 200 ?~ "Pet updated."
144 & at 200 ?~ "Pet updated."
157 & at 405 ?~ "Invalid input"
145 & at 405 ?~ "Invalid input"
158 & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
146 & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
159− where
160− stringSchema :: ParamLocation -> ParamOtherSchema
161− stringSchema loc = mempty
162− & in_ .~ loc
163− & type_ ?~ SwaggerString
164
147
165operationExampleJSON :: Value
148operationExampleJSON :: Value
166operationExampleJSON = [aesonQQ|
149operationExampleJSON = [aesonQQ|
@@ -171,36 +154,35 @@ operationExampleJSON = [aesonQQ|
171 "summary": "Updates a pet in the store with form data",
154 "summary": "Updates a pet in the store with form data",
172 "description": "",
155 "description": "",
173 "operationId": "updatePetWithForm",
156 "operationId": "updatePetWithForm",
174− "consumes": [
175− "application/x-www-form-urlencoded"
176− ],
177− "produces": [
178− "application/json",
179− "application/xml"
180− ],
181 "parameters": [
157 "parameters": [
182 {
158 {
183− "name": "petId",
184− "in": "path",
185− "description": "ID of pet that needs to be updated",
186 "required": true,
159 "required": true,
187− "type": "string"
160+ "schema": {
188− },
189− {
190− "name": "name",
191− "in": "formData",
192− "description": "Updated name of the pet",
193− "required": false,
194− "type": "string"
161+ "type": "string"
195− },
162+ },
196− {
163+ "in": "path",
197− "name": "status",
164+ "name": "petId",
198− "in": "formData",
165+ "description": "ID of pet that needs to be updated"
199− "description": "Updated status of the pet",
200− "required": false,
201− "type": "string"
202 }
166 }
203 ],
167 ],
168+ "requestBody": {
169+ "content": {
170+ "application/x-www-form-urlencoded": {
171+ "schema": {
172+ "properties": {
173+ "petId": {
174+ "type": "string",
175+ "description": "Updated name of the pet"
176+ },
177+ "status": {
178+ "type": "string",
179+ "description": "Updated status of the pet"
180+ }
181+ }
182+ }
183+ }
184+ }
185+ },
204 "responses": {
186 "responses": {
205 "200": {
187 "200": {
206 "description": "Pet updated."
188 "description": "Pet updated."
@@ -251,24 +233,21 @@ schemaSimpleModelExample = mempty
251
233
252schemaSimpleModelExampleJSON :: Value
234schemaSimpleModelExampleJSON :: Value
253schemaSimpleModelExampleJSON = [aesonQQ|
235schemaSimpleModelExampleJSON = [aesonQQ|
254−{
255− "type": "object",
256− "required": [
236+{ "required": [ "name" ],
257− "name"
258− ],
259 "properties": {
237 "properties": {
260 "name": {
238 "name": {
261 "type": "string"
239 "type": "string"
262 },
240 },
263 "address": {
241 "address": {
264− "$ref": "#/definitions/Address"
242+ "$ref": "#/components/schemas/Address"
265 },
243 },
266 "age": {
244 "age": {
267− "type": "integer",
268 "format": "int32",
245 "format": "int32",
269− "minimum": 0
246+ "minimum": 0,
247+ "type": "integer"
270 }
248 }
271− }
249+ },
250+ "type": "object"
272}
251}
273|]
252|]
274
253
@@ -401,16 +380,16 @@ paramsDefinitionExample =
401 & name .~ "skip"
380 & name .~ "skip"
402 & description ?~ "number of items to skip"
381 & description ?~ "number of items to skip"
403 & required ?~ True
382 & required ?~ True
404− & schema .~ ParamOther (mempty
405− & in_ .~ ParamQuery
383+ & in_ .~ ParamQuery
384+ & schema ?~ Inline (mempty
406 & type_ ?~ SwaggerInteger
385 & type_ ?~ SwaggerInteger
407 & format ?~ "int32" ))
386 & format ?~ "int32" ))
408 , ("limitParam", mempty
387 , ("limitParam", mempty
409 & name .~ "limit"
388 & name .~ "limit"
410 & description ?~ "max records to return"
389 & description ?~ "max records to return"
411 & required ?~ True
390 & required ?~ True
412− & schema .~ ParamOther (mempty
413− & in_ .~ ParamQuery
391+ & in_ .~ ParamQuery
392+ & schema ?~ Inline (mempty
414 & type_ ?~ SwaggerInteger
393 & type_ ?~ SwaggerInteger
415 & format ?~ "int32" )) ]
394 & format ?~ "int32" )) ]
416
395
@@ -422,16 +401,20 @@ paramsDefinitionExampleJSON = [aesonQQ|
422 "in": "query",
401 "in": "query",
423 "description": "number of items to skip",
402 "description": "number of items to skip",
424 "required": true,
403 "required": true,
404+ "schema": {
425− "type": "integer",
405+ "type": "integer",
426− "format": "int32"
406+ "format": "int32"
407+ }
427 },
408 },
428 "limitParam": {
409 "limitParam": {
429 "name": "limit",
410 "name": "limit",
430 "in": "query",
411 "in": "query",
431 "description": "max records to return",
412 "description": "max records to return",
432 "required": true,
413 "required": true,
414+ "schema": {
433− "type": "integer",
415+ "type": "integer",
434− "format": "int32"
416+ "format": "int32"
417+ }
435 }
418 }
436}
419}
437|]
420|]
@@ -467,39 +450,45 @@ securityDefinitionsExample = SecurityDefinitions
467 { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
450 { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
468 , _securitySchemeDescription = Nothing })
451 , _securitySchemeDescription = Nothing })
469 , ("petstore_auth", SecurityScheme
452 , ("petstore_auth", SecurityScheme
470− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
453+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
471− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
454+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
472− , _oauth2Scopes =
455+ , _oAath2RefreshUrl = Nothing
456+ , _oAuth2Scopes =
473− [ ("write:pets", "modify pets in your account")
457+ [ ("write:pets", "modify pets in your account")
474− , ("read:pets", "read your pets") ] } )
458+ , ("read:pets", "read your pets") ] } )
475 , _securitySchemeDescription = Nothing }) ]
459 , _securitySchemeDescription = Nothing }) ]
476
460
477securityDefinitionsExampleJSON :: Value
461securityDefinitionsExampleJSON :: Value
478securityDefinitionsExampleJSON = [aesonQQ|
462securityDefinitionsExampleJSON = [aesonQQ|
479{
463{
480 "api_key": {
464 "api_key": {
481− "type": "apiKey",
465+ "in": "header",
482 "name": "api_key",
466 "name": "api_key",
483− "in": "header"
467+ "type": "apiKey"
484 },
468 },
485 "petstore_auth": {
469 "petstore_auth": {
486 "type": "oauth2",
470 "type": "oauth2",
471+ "flows": {
472+ "implicit": {
473+ "scopes": {
474+ "write:pets": "modify pets in your account",
475+ "read:pets": "read your pets"
476+ },
487− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
477+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
488− "flow": "implicit",
478+ }
489− "scopes": {
490− "write:pets": "modify pets in your account",
491− "read:pets": "read your pets"
492 }
479 }
493 }
480 }
494}
481}
482+
495|]
483|]
496
484
497oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
485oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
498oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
486oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
499 [ ("petstore_auth", SecurityScheme
487 [ ("petstore_auth", SecurityScheme
500− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
488+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
501− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
489+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
502− , _oauth2Scopes =
490+ , _oAath2RefreshUrl = Nothing
491+ , _oAuth2Scopes =
503 [ ("read:pets", "read your pets") ] } )
492 [ ("read:pets", "read your pets") ] } )
504 , _securitySchemeDescription = Nothing })
493 , _securitySchemeDescription = Nothing })
505 ]
494 ]
@@ -507,10 +496,11 @@ oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
507oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
496oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
508oAuth2SecurityDefinitionsWriteExample = SecurityDefinitions
497oAuth2SecurityDefinitionsWriteExample = SecurityDefinitions
509 [ ("petstore_auth", SecurityScheme
498 [ ("petstore_auth", SecurityScheme
510− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
499+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
511− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
500+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
512− , _oauth2Scopes =
501+ , _oAath2RefreshUrl = Nothing
502+ , _oAuth2Scopes =
513− [ ("write:pets", "modify pets in your account") ] } )
503+ [ ("write:pets", "modify pets in your account") ] } )
514 , _securitySchemeDescription = Nothing })
504 , _securitySchemeDescription = Nothing })
515 ]
505 ]
516
506
@@ -524,11 +514,14 @@ oAuth2SecurityDefinitionsExampleJSON = [aesonQQ|
524{
514{
525 "petstore_auth": {
515 "petstore_auth": {
526 "type": "oauth2",
516 "type": "oauth2",
517+ "flows": {
518+ "implicit": {
519+ "scopes": {
520+ "write:pets": "modify pets in your account",
521+ "read:pets": "read your pets"
522+ },
527− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
523+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
528− "flow": "implicit",
524+ }
529− "scopes": {
530− "write:pets": "modify pets in your account",
531− "read:pets": "read your pets"
532 }
525 }
533 }
526 }
534}
527}
@@ -544,57 +537,54 @@ emptyPathsFieldExample = mempty
544emptyPathsFieldExampleJSON :: Value
537emptyPathsFieldExampleJSON :: Value
545emptyPathsFieldExampleJSON = [aesonQQ|
538emptyPathsFieldExampleJSON = [aesonQQ|
546{
539{
547− "swagger": "2.0",
540+ "openapi": "3.0.0",
548 "info": {"version": "", "title": ""},
541 "info": {"version": "", "title": ""},
549− "paths": {}
542+ "paths": {},
543+ "components": {}
550}
544}
551|]
545|]
552
546
553swaggerExample :: Swagger
547swaggerExample :: Swagger
554swaggerExample = mempty
548swaggerExample = mempty
555− & basePath ?~ "/"
549+ -- & basePath ?~ "/"
556− & schemes ?~ [Http]
550+ -- & schemes ?~ [Http]
557 & info .~ (mempty
551 & info .~ (mempty
558 & version .~ "1.0"
552 & version .~ "1.0"
559 & title .~ "Todo API"
553 & title .~ "Todo API"
560 & license ?~ "MIT"
554 & license ?~ "MIT"
561 & license._Just.url ?~ URL "http://mit.com"
555 & license._Just.url ?~ URL "http://mit.com"
562− & description ?~ "This is a an API that tests servant-swagger support for a Todo API")
556+ & description ?~ "This is an API that tests servant-swagger support for a Todo API")
563 & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
557 & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
564− & at 200 ?~ Inline (mempty
558+ & responses . at 200 ?~ Inline (mempty
565 & description .~ "OK"
559 & description .~ "OK"
560+ & content . at "application/json" ?~ (mempty
566− & schema ?~ Inline (mempty
561+ & schema ?~ Inline (mempty
567− & type_ ?~ SwaggerObject
562+ & type_ ?~ SwaggerObject
568− & example ?~ [aesonQQ|
563+ & example ?~ [aesonQQ|
569− {
564+ {
570− "created": 100,
565+ "created": 100,
571− "description": "get milk"
566+ "description": "get milk"
572− } |]
567+ } |]
573− & description ?~ "This is some real Todo right here"
568+ & description ?~ "This is some real Todo right here"
574− & properties .~
569+ & properties .~
575− [ ("created", Inline $ mempty
570+ [ ("created", Inline $ mempty
576− & type_ ?~ SwaggerInteger
571+ & type_ ?~ SwaggerInteger
577− & format ?~ "int32")
572+ & format ?~ "int32")
578− , ("description", Inline (mempty & type_ ?~ SwaggerString))]))
573+ , ("description", Inline (mempty & type_ ?~ SwaggerString))])))
579− & produces ?~ MimeList [ "application/json" ]
580 & parameters .~
574 & parameters .~
581 [ Inline $ mempty
575 [ Inline $ mempty
582 & required ?~ True
576 & required ?~ True
583 & name .~ "id"
577 & name .~ "id"
584 & description ?~ "TodoId param"
578 & description ?~ "TodoId param"
579+ & in_ .~ ParamPath
585− & schema .~ ParamOther (mempty
580+ & schema ?~ Inline (mempty
586− & in_ .~ ParamPath
587 & type_ ?~ SwaggerString ) ]
581 & type_ ?~ SwaggerString ) ]
588 & tags .~ InsOrdHS.fromList [ "todo" ] ))
582 & tags .~ InsOrdHS.fromList [ "todo" ] ))
589
583
590swaggerExampleJSON :: Value
584swaggerExampleJSON :: Value
591swaggerExampleJSON = [aesonQQ|
585swaggerExampleJSON = [aesonQQ|
592{
586{
593− "swagger": "2.0",
587+ "openapi": "3.0.0",
594− "basePath": "/",
595− "schemes": [
596− "http"
597− ],
598 "info": {
588 "info": {
599 "version": "1.0",
589 "version": "1.0",
600 "title": "Todo API",
590 "title": "Todo API",
@@ -602,1085 +592,326 @@ swaggerExampleJSON = [aesonQQ|
602 "url": "http://mit.com",
592 "url": "http://mit.com",
603 "name": "MIT"
593 "name": "MIT"
604 },
594 },
605− "description": "This is a an API that tests servant-swagger support for a Todo API"
595+ "description": "This is an API that tests servant-swagger support for a Todo API"
606 },
596 },
607 "paths": {
597 "paths": {
608 "/todo/{id}": {
598 "/todo/{id}": {
609 "get": {
599 "get": {
610− "responses": {
600+ "tags": [
611− "200": {
601+ "todo"
612− "schema": {
613− "example": {
614− "created": 100,
615− "description": "get milk"
616− },
617− "type": "object",
618− "description": "This is some real Todo right here",
619− "properties": {
620− "created": {
621− "format": "int32",
622− "type": "integer"
623− },
624− "description": {
625− "type": "string"
626− }
627− }
628− },
629− "description": "OK"
630− }
631− },
632− "produces": [
633− "application/json"
634 ],
602 ],
635 "parameters": [
603 "parameters": [
636 {
604 {
637 "required": true,
605 "required": true,
606+ "schema": {
607+ "type": "string"
608+ },
638 "in": "path",
609 "in": "path",
639 "name": "id",
610 "name": "id",
640− "type": "string",
641 "description": "TodoId param"
611 "description": "TodoId param"
642 }
612 }
643 ],
613 ],
644− "tags": [
614+ "responses": {
645− "todo"
615+ "200": {
646− ]
616+ "content": {
617+ "application/json": {
618+ "schema": {
619+ "example": {
620+ "created": 100,
621+ "description": "get milk"
622+ },
623+ "type": "object",
624+ "description": "This is some real Todo right here",
625+ "properties": {
626+ "created": {
627+ "format": "int32",
628+ "type": "integer"
629+ },
630+ "description": {
631+ "type": "string"
632+ }
633+ }
634+ }
635+ }
636+ },
637+ "description": "OK"
638+ }
639+ }
647 }
640 }
648 }
641 }
649− }
642+ },
643+ "components": {}
650}
644}
651|]
645|]
652
646
653petstoreExampleJSON :: Value
647petstoreExampleJSON :: Value
654petstoreExampleJSON = [aesonQQ|
648petstoreExampleJSON = [aesonQQ|
655{
649{
656− "swagger":"2.0",
650+ "openapi": "3.0.0",
657− "info":{
651+ "info": {
658− "description":"This is a sample server Petstore server. You can find out more about Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net, #swagger](http://swagger.io/irc/). For this sample, you can use the api key `special-key` to test the authorization filters.",
659− "version":"1.0.0",
652+ "version": "1.0.0",
660− "title":"Swagger Petstore",
653+ "title": "Swagger Petstore",
661− "termsOfService":"http://swagger.io/terms/",
662− "contact":{
663− "email":"apiteam@swagger.io"
664− },
665− "license":{
654+ "license": {
666− "name":"Apache 2.0",
655+ "name": "MIT"
667− "url":"http://www.apache.org/licenses/LICENSE-2.0.html"
656+ }
668− }
657+ },
669− },
658+ "servers": [
659+ {
670− "host":"petstore.swagger.io",
660+ "url": "http://petstore.swagger.io/v1"
671− "basePath":"/v2",
672− "tags":[
673− {
674− "name":"pet",
675− "description":"Everything about your Pets",
676− "externalDocs":{
677− "description":"Find out more",
678− "url":"http://swagger.io"
679− }
680− },
681− {
682− "name":"store",
683− "description":"Access to Petstore orders"
684− },
661+ }
685− {
686− "name":"user",
687− "description":"Operations about user",
688− "externalDocs":{
689− "description":"Find out more about our store",
690− "url":"http://swagger.io"
691− }
692− }
693− ],
662+ ],
694− "schemes":[
695− "http"
696− ],
697− "paths":{
663+ "paths": {
698− "/pet":{
664+ "/pets": {
699− "post":{
700− "tags":[
701− "pet"
702− ],
703− "summary":"Add a new pet to the store",
704− "description":"",
705− "operationId":"addPet",
706− "consumes":[
707− "application/json",
708− "application/xml"
709− ],
710− "produces":[
711− "application/xml",
712− "application/json"
713− ],
714− "parameters":[
715− {
716− "in":"body",
717− "name":"body",
718− "description":"Pet object that needs to be added to the store",
719− "required":true,
720− "schema":{
721− "$ref":"#/definitions/Pet"
722− }
723− }
724− ],
725− "responses":{
726− "405":{
727− "description":"Invalid input"
728− }
729− },
730− "security":[
731− {
732− "petstore_auth":[
733− "write:pets",
734− "read:pets"
735− ]
736− }
737− ]
738− },
739− "put":{
740− "tags":[
741− "pet"
742− ],
743− "summary":"Update an existing pet",
744− "description":"",
745− "operationId":"updatePet",
746− "consumes":[
747− "application/json",
748− "application/xml"
749− ],
750− "produces":[
751− "application/xml",
752− "application/json"
753− ],
754− "parameters":[
755− {
756− "in":"body",
757− "name":"body",
758− "description":"Pet object that needs to be added to the store",
759− "required":true,
760− "schema":{
761− "$ref":"#/definitions/Pet"
762− }
763− }
764− ],
765− "responses":{
766− "400":{
767− "description":"Invalid ID supplied"
768− },
769− "404":{
770− "description":"Pet not found"
771− },
772− "405":{
773− "description":"Validation exception"
774− }
775− },
776− "security":[
777− {
778− "petstore_auth":[
779− "write:pets",
780− "read:pets"
781− ]
782− }
783− ]
784− }
785− },
786− "/pet/findByStatus":{
787− "get":{
788− "tags":[
789− "pet"
790− ],
791− "summary":"Finds Pets by status",
792− "description":"Multiple status values can be provided with comma seperated strings",
793− "operationId":"findPetsByStatus",
794− "produces":[
795− "application/xml",
796− "application/json"
797− ],
798− "parameters":[
799− {
800− "name":"status",
801− "in":"query",
802− "description":"Status values that need to be considered for filter",
803− "required":true,
804− "type":"array",
805− "items":{
806− "type":"string",
807− "enum":[
808− "available",
809− "pending",
810− "sold"
811− ],
812− "default":"available"
813− },
814− "collectionFormat":"csv"
815− }
816− ],
817− "responses":{
818− "200":{
819− "description":"successful operation",
820− "schema":{
821− "type":"array",
822− "items":{
823− "$ref":"#/definitions/Pet"
824− }
825− }
826− },
827− "400":{
828− "description":"Invalid status value"
829− }
830− },
831− "security":[
832− {
833− "petstore_auth":[
834− "write:pets",
835− "read:pets"
836− ]
837− }
838− ]
839− }
840− },
841− "/pet/findByTags":{
842− "get":{
843− "tags":[
844− "pet"
845− ],
846− "summary":"Finds Pets by tags",
847− "description":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.",
848− "operationId":"findPetsByTags",
849− "produces":[
850− "application/xml",
851− "application/json"
852− ],
853− "parameters":[
854− {
855− "name":"tags",
856− "in":"query",
857− "description":"Tags to filter by",
858− "required":true,
859− "type":"array",
860− "items":{
861− "type":"string"
862− },
863− "collectionFormat":"csv"
864− }
865− ],
866− "responses":{
867− "200":{
868− "description":"successful operation",
869− "schema":{
870− "type":"array",
871− "items":{
872− "$ref":"#/definitions/Pet"
873− }
874− }
875− },
876− "400":{
877− "description":"Invalid tag value"
878− }
879− },
880− "security":[
881− {
882− "petstore_auth":[
883− "write:pets",
884− "read:pets"
885− ]
886− }
887− ]
888− }
889− },
890− "/pet/{petId}":{
891− "get":{
665+ "get": {
892− "tags":[
666+ "summary": "List all pets",
893− "pet"
894− ],
895− "summary":"Find pet by ID",
896− "description":"Returns a single pet",
897− "operationId":"getPetById",
667+ "operationId": "listPets",
898− "produces":[
668+ "tags": [
899− "application/xml",
669+ "pets"
900− "application/json"
901− ],
670+ ],
902− "parameters":[
671+ "parameters": [
903− {
672+ {
904− "name":"petId",
673+ "name": "limit",
905− "in":"path",
674+ "in": "query",
906− "description":"ID of pet to return",
675+ "description": "How many items to return at one time (max 100)",
907− "required":true,
908− "type":"integer",
909− "format":"int64"
910− }
911− ],
912− "responses":{
913− "200":{
914− "description":"successful operation",
915− "schema":{
916− "$ref":"#/definitions/Pet"
917− }
918− },
919− "400":{
920− "description":"Invalid ID supplied"
921− },
922− "404":{
923− "description":"Pet not found"
924− }
925− },
926− "security":[
927− {
928− "api_key": []
929− }
930− ]
931− },
932− "post":{
933− "tags":[
934− "pet"
935− ],
936− "summary":"Updates a pet in the store with form data",
937− "description":"",
938− "operationId":"updatePetWithForm",
939− "consumes":[
940− "application/x-www-form-urlencoded"
941− ],
942− "produces":[
943− "application/xml",
944− "application/json"
945− ],
946− "parameters":[
947− {
948− "name":"petId",
949− "in":"path",
950− "description":"ID of pet that needs to be updated",
951− "required":true,
676+ "required": false,
952− "type":"integer",
677+ "schema": {
953− "format":"int64"
954− },
955− {
956− "name":"name",
957− "in":"formData",
958− "description":"Updated name of the pet",
959− "required":false,
960− "type":"string"
961− },
962− {
963− "name":"status",
964− "in":"formData",
965− "description":"Updated status of the pet",
966− "required":false,
967− "type":"string"
968− }
969− ],
970− "responses":{
971− "405":{
972− "description":"Invalid input"
973− }
974− },
975− "security":[
976− {
977− "petstore_auth":[
978− "write:pets",
979− "read:pets"
980− ]
981− }
982− ]
983− },
984− "delete":{
985− "tags":[
986− "pet"
987− ],
988− "summary":"Deletes a pet",
989− "description":"",
990− "operationId":"deletePet",
991− "produces":[
992− "application/xml",
993− "application/json"
994− ],
995− "parameters":[
996− {
997− "name":"api_key",
998− "in":"header",
999− "required":false,
1000− "type":"string"
1001− },
1002− {
1003− "name":"petId",
1004− "in":"path",
1005− "description":"Pet id to delete",
1006− "required":true,
1007− "type":"integer",
678+ "type": "integer",
1008− "format":"int64"
679+ "format": "int32"
1009− }
1010− ],
1011− "responses":{
1012− "400":{
1013− "description":"Invalid pet value"
1014− }
1015− },
1016− "security":[
1017− {
1018− "petstore_auth":[
1019− "write:pets",
1020− "read:pets"
1021− ]
1022− }
1023− ]
1024− }
1025− },
1026− "/pet/{petId}/uploadImage":{
1027− "post":{
1028− "tags":[
1029− "pet"
1030− ],
1031− "summary":"uploads an image",
1032− "description":"",
1033− "operationId":"uploadFile",
1034− "consumes":[
1035− "multipart/form-data"
1036− ],
1037− "produces":[
1038− "application/json"
1039− ],
1040− "parameters":[
1041− {
1042− "name":"petId",
1043− "in":"path",
1044− "description":"ID of pet to update",
1045− "required":true,
1046− "type":"integer",
1047− "format":"int64"
1048− },
1049− {
1050− "name":"additionalMetadata",
1051− "in":"formData",
1052− "description":"Additional data to pass to server",
1053− "required":false,
1054− "type":"string"
1055− },
1056− {
1057− "name":"file",
1058− "in":"formData",
1059− "description":"file to upload",
1060− "required":false,
1061− "type":"file"
1062− }
1063− ],
1064− "responses":{
1065− "200":{
1066− "description":"successful operation",
1067− "schema":{
1068− "$ref":"#/definitions/ApiResponse"
1069− }
1070− }
1071− },
1072− "security":[
1073− {
1074− "petstore_auth":[
1075− "write:pets",
1076− "read:pets"
1077− ]
1078− }
1079− ]
1080− }
1081− },
1082− "/store/inventory":{
1083− "get":{
1084− "tags":[
1085− "store"
1086− ],
1087− "summary":"Returns pet inventories by status",
1088− "description":"Returns a map of status codes to quantities",
1089− "operationId":"getInventory",
1090− "produces":[
1091− "application/json"
1092− ],
1093− "parameters": [],
1094− "responses":{
1095− "200":{
1096− "description":"successful operation",
1097− "schema":{
1098− "type":"object",
1099− "additionalProperties":{
1100− "type":"integer",
1101− "format":"int32"
1102− }
1103− }
1104− }
1105− },
1106− "security":[
1107− {
1108− "api_key": []
1109− }
1110− ]
1111− }
1112− },
1113− "/store/order":{
1114− "post":{
1115− "tags":[
1116− "store"
1117− ],
1118− "summary":"Place an order for a pet",
1119− "description":"",
1120− "operationId":"placeOrder",
1121− "produces":[
1122− "application/xml",
1123− "application/json"
1124− ],
1125− "parameters":[
1126− {
1127− "in":"body",
1128− "name":"body",
1129− "description":"order placed for purchasing the pet",
1130− "required":true,
1131− "schema":{
1132− "$ref":"#/definitions/Order"
1133− }
1134− }
1135− ],
1136− "responses":{
1137− "200":{
1138− "description":"successful operation",
1139− "schema":{
1140− "$ref":"#/definitions/Order"
1141− }
1142− },
1143− "400":{
1144− "description":"Invalid Order"
1145− }
1146− }
1147− }
1148− },
1149− "/store/order/{orderId}":{
1150− "get":{
1151− "tags":[
1152− "store"
1153− ],
1154− "summary":"Find purchase order by ID",
1155− "description":"For valid response try integer IDs with value <= 5 or > 10. Other values will generated exceptions",
1156− "operationId":"getOrderById",
1157− "produces":[
1158− "application/xml",
1159− "application/json"
1160− ],
1161− "parameters":[
1162− {
1163− "name":"orderId",
1164− "in":"path",
1165− "description":"ID of pet that needs to be fetched",
1166− "required":true,
1167− "type":"integer",
1168− "maximum":5.0,
1169− "minimum":1.0,
1170− "format":"int64"
1171− }
1172− ],
1173− "responses":{
1174− "200":{
1175− "description":"successful operation",
1176− "schema":{
1177− "$ref":"#/definitions/Order"
1178− }
1179− },
1180− "400":{
1181− "description":"Invalid ID supplied"
1182− },
1183− "404":{
1184− "description":"Order not found"
1185− }
1186− }
1187− },
1188− "delete":{
1189− "tags":[
1190− "store"
1191− ],
1192− "summary":"Delete purchase order by ID",
1193− "description":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors",
1194− "operationId":"deleteOrder",
1195− "produces":[
1196− "application/xml",
1197− "application/json"
1198− ],
1199− "parameters":[
1200− {
1201− "name":"orderId",
1202− "in":"path",
1203− "description":"ID of the order that needs to be deleted",
1204− "required":true,
1205− "type":"string",
1206− "minimum":1.0
1207− }
1208− ],
1209− "responses":{
1210− "400":{
1211− "description":"Invalid ID supplied"
1212− },
1213− "404":{
1214− "description":"Order not found"
1215− }
1216− }
1217− }
1218− },
1219− "/user":{
1220− "post":{
1221− "tags":[
1222− "user"
1223− ],
1224− "summary":"Create user",
1225− "description":"This can only be done by the logged in user.",
1226− "operationId":"createUser",
1227− "produces":[
1228− "application/xml",
1229− "application/json"
1230− ],
1231− "parameters":[
1232− {
1233− "in":"body",
1234− "name":"body",
1235− "description":"Created user object",
1236− "required":true,
1237− "schema":{
1238− "$ref":"#/definitions/User"
1239− }
1240− }
1241− ],
1242− "responses":{
1243− "default":{
1244− "description":"successful operation"
1245− }
1246 }
680 }
1247− }
681+ }
1248− },
682+ ],
1249− "/user/createWithArray":{
683+ "responses": {
1250− "post":{
684+ "200": {
1251− "tags":[
685+ "description": "A paged array of pets",
1252− "user"
686+ "headers": {
1253− ],
687+ "x-next": {
1254− "summary":"Creates list of users with given input array",
688+ "description": "A link to the next page of responses",
1255− "description":"",
689+ "schema": {
1256− "operationId":"createUsersWithArrayInput",
690+ "type": "string"
1257− "produces":[
691+ }
1258− "application/xml",
692+ }
693+ },
694+ "content": {
1259− "application/json"
695+ "application/json": {
1260− ],
696+ "schema": {
1261− "parameters":[
697+ "type": "array",
1262− {
698+ "items": {
1263− "in":"body",
699+ "type": "object",
1264− "name":"body",
1265− "description":"List of user object",
1266− "required":true,
700+ "required": [
1267− "schema":{
701+ "id",
702+ "name"
703+ ],
704+ "properties": {
705+ "id": {
1268− "type":"array",
706+ "type": "integer",
1269− "items":{
707+ "format": "int64"
1270− "$ref":"#/definitions/User"
708+ },
709+ "name": {
710+ "type": "string"
711+ },
712+ "tag": {
713+ "type": "string"
714+ }
1271− }
715+ }
1272 }
716 }
1273− }
717+ }
1274− ],
1275− "responses":{
1276− "default":{
1277− "description":"successful operation"
1278− }
718+ }
1279 }
719 }
1280− }
1281− },
720+ },
1282− "/user/createWithList":{
721+ "default": {
1283− "post":{
1284− "tags":[
1285− "user"
1286− ],
1287− "summary":"Creates list of users with given input array",
1288− "description":"",
722+ "description": "unexpected error",
1289− "operationId":"createUsersWithListInput",
723+ "content": {
1290− "produces":[
1291− "application/xml",
1292− "application/json"
724+ "application/json": {
1293− ],
725+ "schema": {
1294− "parameters":[
726+ "type": "object",
1295− {
1296− "in":"body",
1297− "name":"body",
1298− "description":"List of user object",
1299− "required":true,
727+ "required": [
1300− "schema":{
728+ "code",
729+ "message"
730+ ],
731+ "properties": {
732+ "code": {
1301− "type":"array",
733+ "type": "integer",
1302− "items":{
734+ "format": "int32"
1303− "$ref":"#/definitions/User"
735+ },
736+ "message": {
737+ "type": "string"
1304− }
738+ }
1305 }
739 }
1306− }
740+ }
1307− ],
1308− "responses":{
1309− "default":{
1310− "description":"successful operation"
1311− }
741+ }
1312 }
742 }
743+ }
1313− }
744+ }
1314 },
745 },
1315− "/user/login":{
746+ "post": {
1316− "get":{
747+ "summary": "Create a pet",
1317− "tags":[
1318− "user"
1319− ],
1320− "summary":"Logs user into the system",
1321− "description":"",
1322− "operationId":"loginUser",
748+ "operationId": "createPets",
1323− "produces":[
749+ "tags": [
1324− "application/xml",
750+ "pets"
751+ ],
752+ "responses": {
753+ "201": {
754+ "description": "Null response"
755+ },
756+ "default": {
757+ "description": "unexpected error",
758+ "content": {
1325− "application/json"
759+ "application/json": {
1326− ],
1327− "parameters":[
1328− {
1329− "name":"username",
1330− "in":"query",
1331− "description":"The user name for login",
1332− "required":true,
1333− "type":"string"
1334− },
1335− {
1336− "name":"password",
1337− "in":"query",
1338− "description":"The password for login in clear text",
1339− "required":true,
1340− "type":"string"
1341− }
1342− ],
1343− "responses":{
1344− "200":{
1345− "description":"successful operation",
1346− "schema":{
760+ "schema": {
1347− "type":"string"
761+ "type": "object",
1348− },
762+ "required": [
1349− "headers":{
763+ "code",
1350− "X-Rate-Limit":{
764+ "message"
765+ ],
766+ "properties": {
767+ "code": {
1351− "type":"integer",
768+ "type": "integer",
1352− "format":"int32",
769+ "format": "int32"
1353− "description":"calls per hour allowed by the user"
1354− },
770+ },
1355− "X-Expires-After":{
771+ "message": {
1356− "type":"string",
772+ "type": "string"
1357− "format":"date-time",
1358− "description":"date in UTC when toekn expires"
1359− }
773+ }
1360 }
774 }
1361− },
775+ }
1362− "400":{
1363− "description":"Invalid username/password supplied"
1364− }
776+ }
1365 }
777 }
778+ }
1366− }
779+ }
1367− },
780+ }
1368− "/user/logout":{
781+ },
782+ "/pets/{petId}": {
1369− "get":{
783+ "get": {
1370− "tags":[
784+ "summary": "Info for a specific pet",
1371− "user"
1372− ],
1373− "summary":"Logs out current logged in user session",
1374− "description":"",
1375− "operationId":"logoutUser",
785+ "operationId": "showPetById",
1376− "produces":[
786+ "tags": [
1377− "application/xml",
787+ "pets"
1378− "application/json"
1379− ],
788+ ],
1380− "parameters": [],
789+ "parameters": [
1381− "responses":{
790+ {
1382− "default":{
791+ "name": "petId",
1383− "description":"successful operation"
792+ "in": "path",
1384− }
793+ "required": true,
794+ "description": "The id of the pet to retrieve",
795+ "schema": {
796+ "type": "string"
1385 }
797 }
1386− }
1387− },
1388− "/user/{username}":{
1389− "get":{
1390− "tags":[
1391− "user"
1392− ],
1393− "summary":"Get user by user name",
1394− "description":"",
1395− "operationId":"getUserByName",
1396− "produces":[
1397− "application/xml",
1398− "application/json"
1399− ],
1400− "parameters":[
1401− {
1402− "name":"username",
1403− "in":"path",
1404− "description":"The name that needs to be fetched. Use user1 for testing. ",
1405− "required":true,
1406− "type":"string"
1407− }
798+ }
1408− ],
799+ ],
1409− "responses":{
800+ "responses": {
1410− "200":{
801+ "200": {
1411− "description":"successful operation",
802+ "description": "Expected response to a valid request",
803+ "content": {
804+ "application/json": {
1412− "schema":{
805+ "schema": {
1413− "$ref":"#/definitions/User"
806+ "type": "object",
807+ "required": [
808+ "id",
809+ "name"
810+ ],
811+ "properties": {
812+ "id": {
813+ "type": "integer",
814+ "format": "int64"
815+ },
816+ "name": {
817+ "type": "string"
818+ },
819+ "tag": {
820+ "type": "string"
821+ }
1414 }
822 }
1415− },
1416− "400":{
1417− "description":"Invalid username supplied"
1418− },
823+ }
1419− "404":{
1420− "description":"User not found"
1421− }
824+ }
1422 }
825 }
1423− },
826+ },
1424− "put":{
827+ "default": {
1425− "tags":[
828+ "description": "unexpected error",
1426− "user"
829+ "content": {
1427− ],
1428− "summary":"Updated user",
1429− "description":"This can only be done by the logged in user.",
1430− "operationId":"updateUser",
1431− "produces":[
1432− "application/xml",
1433− "application/json"
830+ "application/json": {
1434− ],
831+ "schema": {
1435− "parameters":[
832+ "type": "object",
1436− {
833+ "required": [
1437− "name":"username",
834+ "code",
1438− "in":"path",
835+ "message"
1439− "description":"name that need to be deleted",
836+ ],
1440− "required":true,
837+ "properties": {
838+ "code": {
839+ "type": "integer",
840+ "format": "int32"
841+ },
842+ "message": {
1441− "type":"string"
843+ "type": "string"
1442− },
844+ }
1443− {
1444− "in":"body",
1445− "name":"body",
1446− "description":"Updated user object",
1447− "required":true,
1448− "schema":{
1449− "$ref":"#/definitions/User"
1450 }
845 }
1451− }
1452− ],
1453− "responses":{
1454− "400":{
1455− "description":"Invalid user supplied"
1456− },
846+ }
1457− "404":{
1458− "description":"User not found"
1459− }
847+ }
1460 }
848 }
1461− },
1462− "delete":{
1463− "tags":[
1464− "user"
1465− ],
1466− "summary":"Delete user",
1467− "description":"This can only be done by the logged in user.",
1468− "operationId":"deleteUser",
1469− "produces":[
1470− "application/xml",
1471− "application/json"
1472− ],
1473− "parameters":[
1474− {
1475− "name":"username",
1476− "in":"path",
1477− "description":"The name that needs to be deleted",
1478− "required":true,
1479− "type":"string"
1480− }
849+ }
1481− ],
1482− "responses":{
1483− "400":{
1484− "description":"Invalid username supplied"
1485− },
850+ }
1486− "404":{
1487− "description":"User not found"
1488− }
1489− }
1490− }
1491− }
1492− },
1493− "securityDefinitions":{
1494− "petstore_auth":{
1495− "type":"oauth2",
1496− "authorizationUrl":"http://petstore.swagger.io/api/oauth/dialog",
1497− "flow":"implicit",
1498− "scopes":{
1499− "write:pets":"modify pets in your account",
1500− "read:pets":"read your pets"
1501− }
1502− },
1503− "api_key":{
1504− "type":"apiKey",
1505− "name":"api_key",
1506− "in":"header"
1507 }
851 }
1508− },
1509− "definitions":{
1510− "Order":{
1511− "type":"object",
1512− "properties":{
1513− "id":{
1514− "type":"integer",
1515− "format":"int64"
1516− },
852+ }
1517− "petId":{
1518− "type":"integer",
1519− "format":"int64"
1520− },
853+ },
1521− "quantity":{
854+ "components": {
1522− "type":"integer",
855+ "schemas": {
1523− "format":"int32"
856+ "Pet": {
1524− },
1525− "shipDate":{
1526− "type":"string",
1527− "format":"date-time"
1528− },
1529− "status":{
1530− "type":"string",
857+ "type": "object",
1531− "description":"Order Status",
858+ "required": [
1532− "enum":[
859+ "id",
1533− "placed",
860+ "name"
1534− "approved",
1535− "delivered"
1536− ]
861+ ],
1537− },
1538− "complete":{
1539− "type":"boolean",
1540− "default":false
1541− }
1542− },
1543− "xml":{
1544− "name":"Order"
1545− }
1546− },
1547− "Category":{
1548− "type":"object",
1549− "properties":{
862+ "properties": {
1550− "id":{
863+ "id": {
1551− "type":"integer",
864+ "type": "integer",
1552− "format":"int64"
865+ "format": "int64"
1553− },
866+ },
1554− "name":{
867+ "name": {
1555− "type":"string"
1556− }
1557− },
1558− "xml":{
1559− "name":"Category"
1560− }
1561− },
1562− "User":{
1563− "type":"object",
1564− "properties":{
1565− "id":{
1566− "type":"integer",
1567− "format":"int64"
1568− },
1569− "username":{
1570− "type":"string"
1571− },
1572− "firstName":{
1573− "type":"string"
868+ "type": "string"
1574− },
869+ },
1575− "lastName":{
870+ "tag": {
1576− "type":"string"
1577− },
1578− "email":{
1579− "type":"string"
1580− },
1581− "password":{
1582− "type":"string"
1583− },
1584− "phone":{
1585− "type":"string"
1586− },
1587− "userStatus":{
1588− "type":"integer",
1589− "format":"int32",
1590− "description":"User Status"
1591− }
1592− },
1593− "xml":{
1594− "name":"User"
1595− }
1596− },
1597− "Tag":{
1598− "type":"object",
1599− "properties":{
1600− "id":{
1601− "type":"integer",
1602− "format":"int64"
1603− },
1604− "name":{
1605− "type":"string"
871+ "type": "string"
1606− }
1607− },
872+ }
1608− "xml":{
1609− "name":"Tag"
1610− }
873+ }
1611 },
874 },
1612− "Pet":{
875+ "Pets": {
876+ "type": "array",
877+ "items": {
1613− "type":"object",
878+ "type": "object",
1614− "required":[
879+ "required": [
1615− "name",
880+ "id",
1616− "photoUrls"
881+ "name"
1617− ],
882+ ],
1618− "properties":{
883+ "properties": {
1619− "id":{
884+ "id": {
1620− "type":"integer",
885+ "type": "integer",
1621− "format":"int64"
886+ "format": "int64"
1622− },
1623− "category":{
1624− "$ref":"#/definitions/Category"
1625− },
1626− "name":{
1627− "type":"string",
1628− "example":"doggie"
1629 },
887 },
1630− "photoUrls":{
1631− "type":"array",
1632− "xml":{
1633− "name":"photoUrl",
888+ "name": {
1634− "wrapped":true
1635− },
1636− "items":{
1637− "type":"string"
889+ "type": "string"
1638− }
1639 },
890 },
1640− "tags":{
1641− "type":"array",
1642− "xml":{
1643− "name":"tag",
891+ "tag": {
1644− "wrapped":true
1645− },
1646− "items":{
1647− "$ref":"#/definitions/Tag"
1648− }
1649− },
1650− "status":{
1651− "type":"string",
892+ "type": "string"
1652− "description":"pet status in the store",
1653− "enum":[
1654− "available",
1655− "pending",
1656− "sold"
1657− ]
1658 }
893 }
1659− },
894+ }
1660− "xml":{
1661− "name":"Pet"
1662− }
895+ }
1663 },
896 },
1664− "ApiResponse":{
897+ "Error": {
1665− "type":"object",
898+ "type": "object",
899+ "required": [
900+ "code",
901+ "message"
902+ ],
1666− "properties":{
903+ "properties": {
1667− "code":{
904+ "code": {
1668− "type":"integer",
905+ "type": "integer",
1669− "format":"int32"
906+ "format": "int32"
1670− },
907+ },
1671− "type":{
908+ "message": {
1672− "type":"string"
1673− },
1674− "message":{
1675− "type":"string"
909+ "type": "string"
1676− }
910+ }
1677− }
911+ }
1678 }
912 }
1679− },
913+ }
1680− "externalDocs":{
1681− "description":"Find out more about Swagger",
1682− "url":"http://swagger.io"
1683− }
914+ }
1684}
915}
1685|]
916|]
1686
917
@@ -1702,7 +933,7 @@ compositionSchemaExampleJSON = [aesonQQ|
1702 "type": "object",
933 "type": "object",
1703 "allOf": [
934 "allOf": [
1704 {
935 {
1705− "$ref": "#/definitions/Other"
936+ "$ref": "#/components/schemas/Other"
1706 },
937 },
1707 {
938 {
1708 "type": "object",
939 "type": "object",
test/SpecCommon.hs+1−1
@@ -16,7 +16,7 @@ isSubJSON (Object x) (Object y) = HashMap.keys x == HashMap.keys i && F.and i
16isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)
16isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)
17isSubJSON x y = x == y
17isSubJSON x y = x == y
18
18
19−(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a) => a -> Value -> Spec
19+(<=>) :: (Eq a, Show a, ToJSON a, FromJSON a, HasCallStack) => a -> Value -> Spec
20x <=> js = do
20x <=> js = do
21 it "encodes correctly" $ do
21 it "encodes correctly" $ do
22 toJSON x `shouldBe` js
22 toJSON x `shouldBe` js