git.iko.soy/typeable/openapi3
on master help sign in
SwaggerSpec passes
commit808798032902e909c6a8c32fe65c34021ea369b3 authorMaxim Koltsov <kolmax94@gmail.com> dateFri 26 Jun 2020, 16:59 (6 years ago) parent0bf745e
7 files changed+480−1208
src/Data/Swagger.hs+1 src/Data/Swagger/Internal.hs+56−16 src/Data/Swagger/Lens.hs+2−2 src/Data/Swagger/Optics.hs+16−16 test/Data/Swagger/SchemaSpec.hs+7−7 test/Data/SwaggerSpec.hs+397−1166 test/SpecCommon.hs+1−1
src/Data/Swagger.hs+1
@@ -90,6 +90,7 @@ module Data.Swagger (
9090 -- ** Security
9191 SecurityScheme(..),
9292 SecuritySchemeType(..),
93+ SecurityDefinitions(..),
9394 SecurityRequirement(..),
9495
9596 -- *** API key
src/Data/Swagger/Internal.hs+56−16
@@ -684,8 +684,6 @@ data NamedSchema = NamedSchema
684684-- | Regex pattern for @string@ type.
685685type Pattern = Text
686686
687−-- TODO examples for params
688−
689687data ParamSchema (t :: SwaggerKind *) = ParamSchema
690688 { -- | Declares the value of the parameter that the server will use if none is provided,
691689 -- for example a @"count"@ to control the number of results per page might default to @100@
@@ -785,7 +783,7 @@ data Header = Header
785783 { -- | A short description of the header.
786784 _headerDescription :: Maybe Text
787785
788− , _headerParamSchema :: ParamSchema ('SwaggerKindNormal Header)
786+ , _headerSchema :: ParamSchema ('SwaggerKindNormal Header)
789787 } deriving (Eq, Show, Generic, Data, Typeable)
790788
791789-- | The location of the API key.
@@ -867,6 +865,10 @@ data SecurityScheme = SecurityScheme
867865 , _securitySchemeDescription :: Maybe Text
868866 } deriving (Eq, Show, Generic, Data, Typeable)
869867
868+newtype SecurityDefinitions
869+ = SecurityDefinitions (Definitions SecurityScheme)
870+ deriving (Eq, Show, Generic, Data, Typeable)
871+
870872-- | Lists the required security schemes to execute this operation.
871873-- The object can have multiple security schemes declared in it which are all required
872874-- (that is, there is a logical AND between the schemes).
@@ -932,6 +934,7 @@ data AdditionalProperties
932934-- Generic instances
933935-------------------------------------------------------------------------------
934936
937+deriveGeneric ''Server
935938deriveGeneric ''Components
936939deriveGeneric ''Header
937940deriveGeneric ''OAuth2Flow
@@ -1044,12 +1047,38 @@ instance Monoid Operation where
10441047 mempty = genericMempty
10451048 mappend = (<>)
10461049
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.
10471056instance 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+
10491064instance Monoid OAuth2Flows where
10501065 mempty = genericMempty
10511066 mappend = (<>)
10521067
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+
10531082instance Semigroup RequestBody where
10541083 (<>) = genericMappend
10551084instance Monoid RequestBody where
@@ -1108,9 +1137,6 @@ instance ToJSON Contact where
11081137instance ToJSON License where
11091138 toJSON = genericToJSON (jsonPrefix "License")
11101139
1111−instance ToJSON Server where
1112− toJSON = genericToJSON (jsonPrefix "Server")
1113−
11141140instance ToJSON ServerVariable where
11151141 toJSON = genericToJSON (jsonPrefix "ServerVariable")
11161142
@@ -1160,9 +1186,6 @@ instance FromJSON Contact where
11601186instance FromJSON License where
11611187 parseJSON = genericParseJSON (jsonPrefix "License")
11621188
1163−instance FromJSON Server where
1164− parseJSON = genericParseJSON (jsonPrefix "Server")
1165−
11661189instance FromJSON ServerVariable where
11671190 parseJSON = genericParseJSON (jsonPrefix "ServerVariable")
11681191
@@ -1211,9 +1234,10 @@ instance ToJSON SecuritySchemeType where
12111234 toJSON (SecuritySchemeApiKey params)
12121235 = toJSON params
12131236 <+> object [ "type" .= ("apiKey" :: Text) ]
1214− toJSON (SecuritySchemeOAuth2 params)
1215− = toJSON params
1216− <+> object [ "type" .= ("oauth2" :: Text) ]
1237+ toJSON (SecuritySchemeOAuth2 params) = object
1238+ [ "type" .= ("oauth2" :: Text)
1239+ , "flows" .= toJSON params
1240+ ]
12171241 toJSON (SecuritySchemeOpenIdConnect url) = object
12181242 [ "type" .= ("openIdConnect" :: Text)
12191243 , "openIdConnectUrl" .= url
@@ -1226,6 +1250,10 @@ instance ToJSON Swagger where
12261250 else id
12271251 toEncoding = sopSwaggerGenericToEncoding
12281252
1253+instance ToJSON Server where
1254+ toJSON = sopSwaggerGenericToJSON
1255+ toEncoding = sopSwaggerGenericToEncoding
1256+
12291257instance ToJSON SecurityScheme where
12301258 toJSON = sopSwaggerGenericToJSON
12311259 toEncoding = sopSwaggerGenericToEncoding
@@ -1299,6 +1327,9 @@ instance ToJSON Encoding where
12991327 toJSON = sopSwaggerGenericToJSON
13001328 toEncoding = sopSwaggerGenericToEncoding
13011329
1330+instance ToJSON SecurityDefinitions where
1331+ toJSON (SecurityDefinitions sd) = toJSON sd
1332+
13021333instance ToJSON Reference where
13031334 toJSON (Reference ref) = object [ "$ref" .= ref ]
13041335
@@ -1355,7 +1386,7 @@ instance FromJSON SecuritySchemeType where
13551386 case t of
13561387 "http" -> pure SecuritySchemeHttp
13571388 "apiKey" -> SecuritySchemeApiKey <$> parseJSON js
1358− "oauth2" -> SecuritySchemeOAuth2 <$> parseJSON js
1389+ "oauth2" -> SecuritySchemeOAuth2 <$> (o .: "flows")
13591390 "openIdConnect" -> SecuritySchemeOpenIdConnect <$> (o .: "openIdConnectUrl")
13601391 _ -> empty
13611392 parseJSON _ = empty
@@ -1363,6 +1394,9 @@ instance FromJSON SecuritySchemeType where
13631394instance FromJSON Swagger where
13641395 parseJSON = sopSwaggerGenericParseJSON
13651396
1397+instance FromJSON Server where
1398+ parseJSON = sopSwaggerGenericParseJSON
1399+
13661400instance FromJSON SecurityScheme where
13671401 parseJSON = sopSwaggerGenericParseJSON
13681402
@@ -1433,6 +1467,9 @@ instance FromJSON Operation where
14331467instance FromJSON PathItem where
14341468 parseJSON = sopSwaggerGenericParseJSON
14351469
1470+instance FromJSON SecurityDefinitions where
1471+ parseJSON js = SecurityDefinitions <$> parseJSON js
1472+
14361473instance FromJSON RequestBody where
14371474 parseJSON = sopSwaggerGenericParseJSON
14381475
@@ -1499,10 +1536,12 @@ instance FromJSON AdditionalProperties where
14991536 parseJSON (Bool b) = pure $ AdditionalPropertiesAllowed b
15001537 parseJSON js = AdditionalPropertiesSchema <$> parseJSON js
15011538
1539+instance HasSwaggerAesonOptions Server where
1540+ swaggerAesonOptions _ = mkSwaggerAesonOptions "server"
15021541instance HasSwaggerAesonOptions Components where
15031542 swaggerAesonOptions _ = mkSwaggerAesonOptions "components"
15041543instance HasSwaggerAesonOptions Header where
1505− swaggerAesonOptions _ = mkSwaggerAesonOptions "header" & saoSubObject ?~ "paramSchema"
1544+ swaggerAesonOptions _ = mkSwaggerAesonOptions "header"
15061545instance AesonDefaultValue p => HasSwaggerAesonOptions (OAuth2Flow p) where
15071546 swaggerAesonOptions _ = mkSwaggerAesonOptions "oauth2" & saoSubObject ?~ "params"
15081547instance HasSwaggerAesonOptions OAuth2Flows where
@@ -1526,7 +1565,7 @@ instance HasSwaggerAesonOptions SecurityScheme where
15261565instance HasSwaggerAesonOptions Schema where
15271566 swaggerAesonOptions _ = mkSwaggerAesonOptions "schema" & saoSubObject ?~ "paramSchema"
15281567instance HasSwaggerAesonOptions Swagger where
1529− swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.3")]
1568+ swaggerAesonOptions _ = mkSwaggerAesonOptions "swagger" & saoAdditionalPairs .~ [("openapi", "3.0.0")]
15301569instance HasSwaggerAesonOptions Example where
15311570 swaggerAesonOptions _ = mkSwaggerAesonOptions "example"
15321571instance HasSwaggerAesonOptions Encoding where
@@ -1540,6 +1579,7 @@ instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindParamOtherSchema) where
15401579instance HasSwaggerAesonOptions (ParamSchema 'SwaggerKindSchema) where
15411580 swaggerAesonOptions _ = mkSwaggerAesonOptions "paramSchema"
15421581
1582+instance AesonDefaultValue Server
15431583instance AesonDefaultValue Components
15441584instance AesonDefaultValue (ParamSchema s)
15451585instance AesonDefaultValue OAuth2ImplicitFlow
src/Data/Swagger/Lens.hs+2−2
@@ -107,12 +107,12 @@ instance At Operation where at n = responses . at n
107107instance HasParamSchema NamedSchema (ParamSchema 'SwaggerKindSchema) where paramSchema = schema.paramSchema
108108
109109-- 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_
111111instance HasType Schema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
112112instance HasType NamedSchema (Maybe (SwaggerType 'SwaggerKindSchema)) where type_ = paramSchema.type_
113113
114114-- HasDefault instances
115−instance HasDefault Header (Maybe Value) where default_ = paramSchema.default_
115+instance HasDefault Header (Maybe Value) where default_ = schema.default_
116116instance HasDefault Schema (Maybe Value) where default_ = paramSchema.default_
117117
118118-- OVERLAPPABLE instances
src/Data/Swagger/Optics.hs+16−16
@@ -188,7 +188,7 @@ instance
188188 ( a ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
189189 , b ~ Maybe (SwaggerType ('SwaggerKindNormal Header))
190190 ) => LabelOptic "type" A_Lens Header Header a b where
191− labelOptic = #paramSchema % #type
191+ labelOptic = #schema % #type
192192 {-# INLINE labelOptic #-}
193193
194194instance
@@ -210,7 +210,7 @@ instance
210210instance
211211 ( a ~ Maybe Value, b ~ Maybe Value
212212 ) => LabelOptic "default" A_Lens Header Header a b where
213− labelOptic = #paramSchema % #default
213+ labelOptic = #schema % #default
214214 {-# INLINE labelOptic #-}
215215
216216instance
@@ -230,7 +230,7 @@ instance
230230instance
231231 ( a ~ Maybe Format, b ~ Maybe Format
232232 ) => LabelOptic "format" A_Lens Header Header a b where
233− labelOptic = #paramSchema % #format
233+ labelOptic = #schema % #format
234234 {-# INLINE labelOptic #-}
235235
236236instance
@@ -251,7 +251,7 @@ instance
251251 ( a ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
252252 , b ~ Maybe (SwaggerItems ('SwaggerKindNormal Header))
253253 ) => LabelOptic "items" A_Lens Header Header a b where
254− labelOptic = #paramSchema % #items
254+ labelOptic = #schema % #items
255255 {-# INLINE labelOptic #-}
256256
257257instance
@@ -273,7 +273,7 @@ instance
273273instance
274274 ( a ~ Maybe Scientific, b ~ Maybe Scientific
275275 ) => LabelOptic "maximum" A_Lens Header Header a b where
276− labelOptic = #paramSchema % #maximum
276+ labelOptic = #schema % #maximum
277277 {-# INLINE labelOptic #-}
278278
279279instance
@@ -293,7 +293,7 @@ instance
293293instance
294294 ( a ~ Maybe Bool, b ~ Maybe Bool
295295 ) => LabelOptic "exclusiveMaximum" A_Lens Header Header a b where
296− labelOptic = #paramSchema % #exclusiveMaximum
296+ labelOptic = #schema % #exclusiveMaximum
297297 {-# INLINE labelOptic #-}
298298
299299instance
@@ -313,7 +313,7 @@ instance
313313instance
314314 ( a ~ Maybe Scientific, b ~ Maybe Scientific
315315 ) => LabelOptic "minimum" A_Lens Header Header a b where
316− labelOptic = #paramSchema % #minimum
316+ labelOptic = #schema % #minimum
317317 {-# INLINE labelOptic #-}
318318
319319instance
@@ -333,7 +333,7 @@ instance
333333instance
334334 ( a ~ Maybe Bool, b ~ Maybe Bool
335335 ) => LabelOptic "exclusiveMinimum" A_Lens Header Header a b where
336− labelOptic = #paramSchema % #exclusiveMinimum
336+ labelOptic = #schema % #exclusiveMinimum
337337 {-# INLINE labelOptic #-}
338338
339339instance
@@ -353,7 +353,7 @@ instance
353353instance
354354 ( a ~ Maybe Integer, b ~ Maybe Integer
355355 ) => LabelOptic "maxLength" A_Lens Header Header a b where
356− labelOptic = #paramSchema % #maxLength
356+ labelOptic = #schema % #maxLength
357357 {-# INLINE labelOptic #-}
358358
359359instance
@@ -373,7 +373,7 @@ instance
373373instance
374374 ( a ~ Maybe Integer, b ~ Maybe Integer
375375 ) => LabelOptic "minLength" A_Lens Header Header a b where
376− labelOptic = #paramSchema % #minLength
376+ labelOptic = #schema % #minLength
377377 {-# INLINE labelOptic #-}
378378
379379instance
@@ -393,7 +393,7 @@ instance
393393instance
394394 ( a ~ Maybe Text, b ~ Maybe Text
395395 ) => LabelOptic "pattern" A_Lens Header Header a b where
396− labelOptic = #paramSchema % #pattern
396+ labelOptic = #schema % #pattern
397397 {-# INLINE labelOptic #-}
398398
399399instance
@@ -413,7 +413,7 @@ instance
413413instance
414414 ( a ~ Maybe Integer, b ~ Maybe Integer
415415 ) => LabelOptic "maxItems" A_Lens Header Header a b where
416− labelOptic = #paramSchema % #maxItems
416+ labelOptic = #schema % #maxItems
417417 {-# INLINE labelOptic #-}
418418
419419instance
@@ -433,7 +433,7 @@ instance
433433instance
434434 ( a ~ Maybe Integer, b ~ Maybe Integer
435435 ) => LabelOptic "minItems" A_Lens Header Header a b where
436− labelOptic = #paramSchema % #minItems
436+ labelOptic = #schema % #minItems
437437 {-# INLINE labelOptic #-}
438438
439439instance
@@ -453,7 +453,7 @@ instance
453453instance
454454 ( a ~ Maybe Bool, b ~ Maybe Bool
455455 ) => LabelOptic "uniqueItems" A_Lens Header Header a b where
456− labelOptic = #paramSchema % #uniqueItems
456+ labelOptic = #schema % #uniqueItems
457457 {-# INLINE labelOptic #-}
458458
459459instance
@@ -473,7 +473,7 @@ instance
473473instance
474474 ( a ~ Maybe [Value], b ~ Maybe [Value]
475475 ) => LabelOptic "enum" A_Lens Header Header a b where
476− labelOptic = #paramSchema % #enum
476+ labelOptic = #schema % #enum
477477 {-# INLINE labelOptic #-}
478478
479479instance
@@ -493,7 +493,7 @@ instance
493493instance
494494 ( a ~ Maybe Scientific, b ~ Maybe Scientific
495495 ) => LabelOptic "multipleOf" A_Lens Header Header a b where
496− labelOptic = #paramSchema % #multipleOf
496+ labelOptic = #schema % #multipleOf
497497 {-# INLINE labelOptic #-}
498498
499499instance
test/Data/Swagger/SchemaSpec.hs+7−7
@@ -24,37 +24,37 @@ import Test.Hspec
2424import qualified Data.HashMap.Strict as HM
2525import Data.Time.LocalTime
2626
27−checkToSchema :: ToSchema a => Proxy a -> Value -> Spec
27+checkToSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
2828checkToSchema proxy js = toSchema proxy <=> js
2929
30−checkSchemaName :: ToSchema a => Maybe String -> Proxy a -> Spec
30+checkSchemaName :: (HasCallStack, ToSchema a) => Maybe String -> Proxy a -> Spec
3131checkSchemaName sname proxy =
3232 it ("schema name is " ++ show sname) $
3333 schemaName proxy `shouldBe` fmap Text.pack sname
3434
35−checkDefs :: ToSchema a => Proxy a -> [String] -> Spec
35+checkDefs :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
3636checkDefs proxy names =
3737 it ("uses these definitions " ++ show names) $
3838 InsOrdHashMap.keys defs `shouldBe` map Text.pack names
3939 where
4040 defs = execDeclare (declareNamedSchema proxy) mempty
4141
42−checkProperties :: ToSchema a => Proxy a -> [String] -> Spec
42+checkProperties :: (HasCallStack, ToSchema a) => Proxy a -> [String] -> Spec
4343checkProperties proxy names =
4444 it ("has these fields in order " ++ show names) $
4545 InsOrdHashMap.keys fields `shouldBe` map Text.pack names
4646 where
4747 fields = toSchema proxy ^. properties
4848
49−checkInlinedSchema :: ToSchema a => Proxy a -> Value -> Spec
49+checkInlinedSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
5050checkInlinedSchema proxy js = toInlinedSchema proxy <=> js
5151
52−checkInlinedSchemas :: ToSchema a => [String] -> Proxy a -> Value -> Spec
52+checkInlinedSchemas :: (HasCallStack, ToSchema a) => [String] -> Proxy a -> Value -> Spec
5353checkInlinedSchemas names proxy js = inlineSchemas (map Text.pack names) defs s <=> js
5454 where
5555 (defs, s) = runDeclare (declareSchema proxy) mempty
5656
57−checkInlinedRecSchema :: ToSchema a => Proxy a -> Value -> Spec
57+checkInlinedRecSchema :: (HasCallStack, ToSchema a) => Proxy a -> Value -> Spec
5858checkInlinedRecSchema proxy js = inlineNonRecursiveSchemas defs s <=> js
5959 where
6060 (defs, s) = runDeclare (declareSchema proxy) mempty
test/Data/SwaggerSpec.hs+397−1166
@@ -21,14 +21,6 @@ import Test.Hspec hiding (example)
2121
2222spec :: Spec
2323spec = 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
3224 describe "License Object" $ licenseExample <=> licenseExampleJSON
3325 describe "Contact Object" $ contactExample <=> contactExampleJSON
3426 describe "Info Object" $ infoExample <=> infoExampleJSON
@@ -48,9 +40,11 @@ spec = do
4840 describe "Swagger Object" $ do
4941 context "Example with no paths" $ emptyPathsFieldExample <=> emptyPathsFieldExampleJSON
5042 context "Todo Example" $ swaggerExample <=> swaggerExampleJSON
51− context "PetStore Example" $
43+ context "PetStore Example" $ do
5244 it "decodes successfully" $ do
5345 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
5448
5549main :: IO ()
5650main = hspec spec
@@ -133,34 +127,23 @@ operationExample = mempty
133127 & summary ?~ "Updates a pet in the store with form data"
134128 & description ?~ ""
135129 & operationId ?~ "updatePetWithForm"
136− & consumes ?~ MimeList ["application/x-www-form-urlencoded"]
137− & produces ?~ MimeList ["application/json", "application/xml"]
138− & parameters .~ map Inline
139− [ mempty
140− & name .~ "petId"
141− & description ?~ "ID of pet that needs to be updated"
142− & required ?~ True
143− & schema .~ ParamOther (stringSchema ParamPath)
144− , mempty
145− & name .~ "name"
146− & description ?~ "Updated name of the pet"
147− & required ?~ False
148− & schema .~ ParamOther (stringSchema ParamFormData)
149− , mempty
150− & name .~ "status"
151− & description ?~ "Updated status of the pet"
152− & required ?~ False
153− & schema .~ ParamOther (stringSchema ParamFormData)
154− ]
155−
130+ & parameters .~ [Inline (mempty
131+ & name .~ "petId"
132+ & description ?~ "ID of pet that needs to be updated"
133+ & required ?~ True
134+ & in_ .~ ParamPath
135+ & schema ?~ Inline (mempty & type_ ?~ SwaggerString))]
136+ & requestBody ?~ Inline (
137+ mempty & content . at "application/x-www-form-urlencoded" ?~ (mempty & schema ?~ (Inline (mempty
138+ & properties . at "petId" ?~ Inline (mempty
139+ & description ?~ "Updated name of the pet"
140+ & type_ ?~ SwaggerString)
141+ & properties . at "status" ?~ Inline (mempty
142+ & description ?~ "Updated status of the pet"
143+ & type_ ?~ SwaggerString)))))
156144 & at 200 ?~ "Pet updated."
157145 & at 405 ?~ "Invalid input"
158146 & security .~ [SecurityRequirement [("petstore_auth", ["write:pets", "read:pets"])]]
159− where
160− stringSchema :: ParamLocation -> ParamOtherSchema
161− stringSchema loc = mempty
162− & in_ .~ loc
163− & type_ ?~ SwaggerString
164147
165148operationExampleJSON :: Value
166149operationExampleJSON = [aesonQQ|
@@ -171,36 +154,35 @@ operationExampleJSON = [aesonQQ|
171154 "summary": "Updates a pet in the store with form data",
172155 "description": "",
173156 "operationId": "updatePetWithForm",
174− "consumes": [
175− "application/x-www-form-urlencoded"
176− ],
177− "produces": [
178− "application/json",
179− "application/xml"
180− ],
181157 "parameters": [
182158 {
183− "name": "petId",
184− "in": "path",
185− "description": "ID of pet that needs to be updated",
186159 "required": true,
187− "type": "string"
188− },
189− {
190− "name": "name",
191− "in": "formData",
192− "description": "Updated name of the pet",
193− "required": false,
194− "type": "string"
195− },
196− {
197− "name": "status",
198− "in": "formData",
199− "description": "Updated status of the pet",
200− "required": false,
201− "type": "string"
160+ "schema": {
161+ "type": "string"
162+ },
163+ "in": "path",
164+ "name": "petId",
165+ "description": "ID of pet that needs to be updated"
202166 }
203167 ],
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+ },
204186 "responses": {
205187 "200": {
206188 "description": "Pet updated."
@@ -251,24 +233,21 @@ schemaSimpleModelExample = mempty
251233
252234schemaSimpleModelExampleJSON :: Value
253235schemaSimpleModelExampleJSON = [aesonQQ|
254−{
255− "type": "object",
256− "required": [
257− "name"
258− ],
236+{ "required": [ "name" ],
259237 "properties": {
260238 "name": {
261239 "type": "string"
262240 },
263241 "address": {
264− "$ref": "#/definitions/Address"
242+ "$ref": "#/components/schemas/Address"
265243 },
266244 "age": {
267− "type": "integer",
268245 "format": "int32",
269− "minimum": 0
246+ "minimum": 0,
247+ "type": "integer"
270248 }
271− }
249+ },
250+ "type": "object"
272251}
273252|]
274253
@@ -401,16 +380,16 @@ paramsDefinitionExample =
401380 & name .~ "skip"
402381 & description ?~ "number of items to skip"
403382 & required ?~ True
404− & schema .~ ParamOther (mempty
405− & in_ .~ ParamQuery
383+ & in_ .~ ParamQuery
384+ & schema ?~ Inline (mempty
406385 & type_ ?~ SwaggerInteger
407386 & format ?~ "int32" ))
408387 , ("limitParam", mempty
409388 & name .~ "limit"
410389 & description ?~ "max records to return"
411390 & required ?~ True
412− & schema .~ ParamOther (mempty
413− & in_ .~ ParamQuery
391+ & in_ .~ ParamQuery
392+ & schema ?~ Inline (mempty
414393 & type_ ?~ SwaggerInteger
415394 & format ?~ "int32" )) ]
416395
@@ -422,16 +401,20 @@ paramsDefinitionExampleJSON = [aesonQQ|
422401 "in": "query",
423402 "description": "number of items to skip",
424403 "required": true,
425− "type": "integer",
426− "format": "int32"
404+ "schema": {
405+ "type": "integer",
406+ "format": "int32"
407+ }
427408 },
428409 "limitParam": {
429410 "name": "limit",
430411 "in": "query",
431412 "description": "max records to return",
432413 "required": true,
433− "type": "integer",
434− "format": "int32"
414+ "schema": {
415+ "type": "integer",
416+ "format": "int32"
417+ }
435418 }
436419}
437420|]
@@ -467,39 +450,45 @@ securityDefinitionsExample = SecurityDefinitions
467450 { _securitySchemeType = SecuritySchemeApiKey (ApiKeyParams "api_key" ApiKeyHeader)
468451 , _securitySchemeDescription = Nothing })
469452 , ("petstore_auth", SecurityScheme
470− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
471− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
472− , _oauth2Scopes =
473− [ ("write:pets", "modify pets in your account")
474− , ("read:pets", "read your pets") ] } )
453+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
454+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
455+ , _oAath2RefreshUrl = Nothing
456+ , _oAuth2Scopes =
457+ [ ("write:pets", "modify pets in your account")
458+ , ("read:pets", "read your pets") ] } )
475459 , _securitySchemeDescription = Nothing }) ]
476460
477461securityDefinitionsExampleJSON :: Value
478462securityDefinitionsExampleJSON = [aesonQQ|
479463{
480464 "api_key": {
481− "type": "apiKey",
465+ "in": "header",
482466 "name": "api_key",
483− "in": "header"
467+ "type": "apiKey"
484468 },
485469 "petstore_auth": {
486470 "type": "oauth2",
487− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
488− "flow": "implicit",
489− "scopes": {
490− "write:pets": "modify pets in your account",
491− "read:pets": "read your pets"
471+ "flows": {
472+ "implicit": {
473+ "scopes": {
474+ "write:pets": "modify pets in your account",
475+ "read:pets": "read your pets"
476+ },
477+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
478+ }
492479 }
493480 }
494481}
482+
495483|]
496484
497485oAuth2SecurityDefinitionsReadExample :: SecurityDefinitions
498486oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
499487 [ ("petstore_auth", SecurityScheme
500− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
501− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
502− , _oauth2Scopes =
488+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
489+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
490+ , _oAath2RefreshUrl = Nothing
491+ , _oAuth2Scopes =
503492 [ ("read:pets", "read your pets") ] } )
504493 , _securitySchemeDescription = Nothing })
505494 ]
@@ -507,10 +496,11 @@ oAuth2SecurityDefinitionsReadExample = SecurityDefinitions
507496oAuth2SecurityDefinitionsWriteExample :: SecurityDefinitions
508497oAuth2SecurityDefinitionsWriteExample = SecurityDefinitions
509498 [ ("petstore_auth", SecurityScheme
510− { _securitySchemeType = SecuritySchemeOAuth2 (OAuth2Params
511− { _oauth2Flow = OAuth2Implicit "http://swagger.io/api/oauth/dialog"
512− , _oauth2Scopes =
513− [ ("write:pets", "modify pets in your account") ] } )
499+ { _securitySchemeType = SecuritySchemeOAuth2 (mempty & implicit ?~ OAuth2Flow
500+ { _oAuth2Params = OAuth2ImplicitFlow "http://swagger.io/api/oauth/dialog"
501+ , _oAath2RefreshUrl = Nothing
502+ , _oAuth2Scopes =
503+ [ ("write:pets", "modify pets in your account") ] } )
514504 , _securitySchemeDescription = Nothing })
515505 ]
516506
@@ -524,11 +514,14 @@ oAuth2SecurityDefinitionsExampleJSON = [aesonQQ|
524514{
525515 "petstore_auth": {
526516 "type": "oauth2",
527− "authorizationUrl": "http://swagger.io/api/oauth/dialog",
528− "flow": "implicit",
529− "scopes": {
530− "write:pets": "modify pets in your account",
531− "read:pets": "read your pets"
517+ "flows": {
518+ "implicit": {
519+ "scopes": {
520+ "write:pets": "modify pets in your account",
521+ "read:pets": "read your pets"
522+ },
523+ "authorizationUrl": "http://swagger.io/api/oauth/dialog"
524+ }
532525 }
533526 }
534527}
@@ -544,57 +537,54 @@ emptyPathsFieldExample = mempty
544537emptyPathsFieldExampleJSON :: Value
545538emptyPathsFieldExampleJSON = [aesonQQ|
546539{
547− "swagger": "2.0",
540+ "openapi": "3.0.0",
548541 "info": {"version": "", "title": ""},
549− "paths": {}
542+ "paths": {},
543+ "components": {}
550544}
551545|]
552546
553547swaggerExample :: Swagger
554548swaggerExample = mempty
555− & basePath ?~ "/"
556− & schemes ?~ [Http]
549+ -- & basePath ?~ "/"
550+ -- & schemes ?~ [Http]
557551 & info .~ (mempty
558552 & version .~ "1.0"
559553 & title .~ "Todo API"
560554 & license ?~ "MIT"
561555 & 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")
563557 & paths.at "/todo/{id}" ?~ (mempty & get ?~ ((mempty :: Operation)
564− & at 200 ?~ Inline (mempty
558+ & responses . at 200 ?~ Inline (mempty
565559 & description .~ "OK"
566− & schema ?~ Inline (mempty
567− & type_ ?~ SwaggerObject
568− & example ?~ [aesonQQ|
569− {
570− "created": 100,
571− "description": "get milk"
572− } |]
573− & description ?~ "This is some real Todo right here"
574− & properties .~
575− [ ("created", Inline $ mempty
576− & type_ ?~ SwaggerInteger
577− & format ?~ "int32")
578− , ("description", Inline (mempty & type_ ?~ SwaggerString))]))
579− & produces ?~ MimeList [ "application/json" ]
560+ & content . at "application/json" ?~ (mempty
561+ & schema ?~ Inline (mempty
562+ & type_ ?~ SwaggerObject
563+ & example ?~ [aesonQQ|
564+ {
565+ "created": 100,
566+ "description": "get milk"
567+ } |]
568+ & description ?~ "This is some real Todo right here"
569+ & properties .~
570+ [ ("created", Inline $ mempty
571+ & type_ ?~ SwaggerInteger
572+ & format ?~ "int32")
573+ , ("description", Inline (mempty & type_ ?~ SwaggerString))])))
580574 & parameters .~
581575 [ Inline $ mempty
582576 & required ?~ True
583577 & name .~ "id"
584578 & description ?~ "TodoId param"
585− & schema .~ ParamOther (mempty
586− & in_ .~ ParamPath
579+ & in_ .~ ParamPath
580+ & schema ?~ Inline (mempty
587581 & type_ ?~ SwaggerString ) ]
588582 & tags .~ InsOrdHS.fromList [ "todo" ] ))
589583
590584swaggerExampleJSON :: Value
591585swaggerExampleJSON = [aesonQQ|
592586{
593− "swagger": "2.0",
594− "basePath": "/",
595− "schemes": [
596− "http"
597− ],
587+ "openapi": "3.0.0",
598588 "info": {
599589 "version": "1.0",
600590 "title": "Todo API",
@@ -602,1085 +592,326 @@ swaggerExampleJSON = [aesonQQ|
602592 "url": "http://mit.com",
603593 "name": "MIT"
604594 },
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"
606596 },
607597 "paths": {
608598 "/todo/{id}": {
609599 "get": {
610− "responses": {
611− "200": {
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"
600+ "tags": [
601+ "todo"
634602 ],
635603 "parameters": [
636604 {
637605 "required": true,
606+ "schema": {
607+ "type": "string"
608+ },
638609 "in": "path",
639610 "name": "id",
640− "type": "string",
641611 "description": "TodoId param"
642612 }
643613 ],
644− "tags": [
645− "todo"
646− ]
614+ "responses": {
615+ "200": {
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+ }
647640 }
648641 }
649− }
642+ },
643+ "components": {}
650644}
651645|]
652646
653647petstoreExampleJSON :: Value
654648petstoreExampleJSON = [aesonQQ|
655649{
656− "swagger":"2.0",
657− "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",
660− "title":"Swagger Petstore",
661− "termsOfService":"http://swagger.io/terms/",
662− "contact":{
663− "email":"apiteam@swagger.io"
664− },
665− "license":{
666− "name":"Apache 2.0",
667− "url":"http://www.apache.org/licenses/LICENSE-2.0.html"
668− }
669− },
670− "host":"petstore.swagger.io",
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− },
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− ],
694− "schemes":[
695− "http"
696− ],
697− "paths":{
698− "/pet":{
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":{
892− "tags":[
893− "pet"
894− ],
895− "summary":"Find pet by ID",
896− "description":"Returns a single pet",
897− "operationId":"getPetById",
898− "produces":[
899− "application/xml",
900− "application/json"
901− ],
902− "parameters":[
903− {
904− "name":"petId",
905− "in":"path",
906− "description":"ID of pet to return",
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,
952− "type":"integer",
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",
1008− "format":"int64"
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− }
650+ "openapi": "3.0.0",
651+ "info": {
652+ "version": "1.0.0",
653+ "title": "Swagger Petstore",
654+ "license": {
655+ "name": "MIT"
656+ }
657+ },
658+ "servers": [
659+ {
660+ "url": "http://petstore.swagger.io/v1"
661+ }
662+ ],
663+ "paths": {
664+ "/pets": {
665+ "get": {
666+ "summary": "List all pets",
667+ "operationId": "listPets",
668+ "tags": [
669+ "pets"
670+ ],
671+ "parameters": [
672+ {
673+ "name": "limit",
674+ "in": "query",
675+ "description": "How many items to return at one time (max 100)",
676+ "required": false,
677+ "schema": {
678+ "type": "integer",
679+ "format": "int32"
1246680 }
1247− }
1248− },
1249− "/user/createWithArray":{
1250− "post":{
1251− "tags":[
1252− "user"
1253− ],
1254− "summary":"Creates list of users with given input array",
1255− "description":"",
1256− "operationId":"createUsersWithArrayInput",
1257− "produces":[
1258− "application/xml",
1259− "application/json"
1260− ],
1261− "parameters":[
1262− {
1263− "in":"body",
1264− "name":"body",
1265− "description":"List of user object",
1266− "required":true,
1267− "schema":{
1268− "type":"array",
1269− "items":{
1270− "$ref":"#/definitions/User"
1271− }
681+ }
682+ ],
683+ "responses": {
684+ "200": {
685+ "description": "A paged array of pets",
686+ "headers": {
687+ "x-next": {
688+ "description": "A link to the next page of responses",
689+ "schema": {
690+ "type": "string"
691+ }
692+ }
693+ },
694+ "content": {
695+ "application/json": {
696+ "schema": {
697+ "type": "array",
698+ "items": {
699+ "type": "object",
700+ "required": [
701+ "id",
702+ "name"
703+ ],
704+ "properties": {
705+ "id": {
706+ "type": "integer",
707+ "format": "int64"
708+ },
709+ "name": {
710+ "type": "string"
711+ },
712+ "tag": {
713+ "type": "string"
714+ }
715+ }
1272716 }
1273− }
1274− ],
1275− "responses":{
1276− "default":{
1277− "description":"successful operation"
1278− }
717+ }
718+ }
1279719 }
1280− }
1281− },
1282− "/user/createWithList":{
1283− "post":{
1284− "tags":[
1285− "user"
1286− ],
1287− "summary":"Creates list of users with given input array",
1288− "description":"",
1289− "operationId":"createUsersWithListInput",
1290− "produces":[
1291− "application/xml",
1292− "application/json"
1293− ],
1294− "parameters":[
1295− {
1296− "in":"body",
1297− "name":"body",
1298− "description":"List of user object",
1299− "required":true,
1300− "schema":{
1301− "type":"array",
1302− "items":{
1303− "$ref":"#/definitions/User"
1304− }
720+ },
721+ "default": {
722+ "description": "unexpected error",
723+ "content": {
724+ "application/json": {
725+ "schema": {
726+ "type": "object",
727+ "required": [
728+ "code",
729+ "message"
730+ ],
731+ "properties": {
732+ "code": {
733+ "type": "integer",
734+ "format": "int32"
735+ },
736+ "message": {
737+ "type": "string"
738+ }
1305739 }
1306− }
1307− ],
1308− "responses":{
1309− "default":{
1310− "description":"successful operation"
1311− }
740+ }
741+ }
1312742 }
1313− }
743+ }
744+ }
1314745 },
1315− "/user/login":{
1316− "get":{
1317− "tags":[
1318− "user"
1319− ],
1320− "summary":"Logs user into the system",
1321− "description":"",
1322− "operationId":"loginUser",
1323− "produces":[
1324− "application/xml",
1325− "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":{
1347− "type":"string"
1348− },
1349− "headers":{
1350− "X-Rate-Limit":{
1351− "type":"integer",
1352− "format":"int32",
1353− "description":"calls per hour allowed by the user"
1354− },
1355− "X-Expires-After":{
1356− "type":"string",
1357− "format":"date-time",
1358− "description":"date in UTC when toekn expires"
1359− }
746+ "post": {
747+ "summary": "Create a pet",
748+ "operationId": "createPets",
749+ "tags": [
750+ "pets"
751+ ],
752+ "responses": {
753+ "201": {
754+ "description": "Null response"
755+ },
756+ "default": {
757+ "description": "unexpected error",
758+ "content": {
759+ "application/json": {
760+ "schema": {
761+ "type": "object",
762+ "required": [
763+ "code",
764+ "message"
765+ ],
766+ "properties": {
767+ "code": {
768+ "type": "integer",
769+ "format": "int32"
770+ },
771+ "message": {
772+ "type": "string"
773+ }
1360774 }
1361− },
1362− "400":{
1363− "description":"Invalid username/password supplied"
1364− }
775+ }
776+ }
1365777 }
1366− }
1367− },
1368− "/user/logout":{
1369− "get":{
1370− "tags":[
1371− "user"
1372− ],
1373− "summary":"Logs out current logged in user session",
1374− "description":"",
1375− "operationId":"logoutUser",
1376− "produces":[
1377− "application/xml",
1378− "application/json"
1379− ],
1380− "parameters": [],
1381− "responses":{
1382− "default":{
1383− "description":"successful operation"
1384− }
778+ }
779+ }
780+ }
781+ },
782+ "/pets/{petId}": {
783+ "get": {
784+ "summary": "Info for a specific pet",
785+ "operationId": "showPetById",
786+ "tags": [
787+ "pets"
788+ ],
789+ "parameters": [
790+ {
791+ "name": "petId",
792+ "in": "path",
793+ "required": true,
794+ "description": "The id of the pet to retrieve",
795+ "schema": {
796+ "type": "string"
1385797 }
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− }
1408− ],
1409− "responses":{
1410− "200":{
1411− "description":"successful operation",
1412− "schema":{
1413− "$ref":"#/definitions/User"
798+ }
799+ ],
800+ "responses": {
801+ "200": {
802+ "description": "Expected response to a valid request",
803+ "content": {
804+ "application/json": {
805+ "schema": {
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+ }
1414822 }
1415− },
1416− "400":{
1417− "description":"Invalid username supplied"
1418− },
1419− "404":{
1420− "description":"User not found"
1421− }
823+ }
824+ }
1422825 }
1423− },
1424− "put":{
1425− "tags":[
1426− "user"
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"
1434− ],
1435− "parameters":[
1436− {
1437− "name":"username",
1438− "in":"path",
1439− "description":"name that need to be deleted",
1440− "required":true,
1441− "type":"string"
1442− },
1443− {
1444− "in":"body",
1445− "name":"body",
1446− "description":"Updated user object",
1447− "required":true,
1448− "schema":{
1449− "$ref":"#/definitions/User"
826+ },
827+ "default": {
828+ "description": "unexpected error",
829+ "content": {
830+ "application/json": {
831+ "schema": {
832+ "type": "object",
833+ "required": [
834+ "code",
835+ "message"
836+ ],
837+ "properties": {
838+ "code": {
839+ "type": "integer",
840+ "format": "int32"
841+ },
842+ "message": {
843+ "type": "string"
844+ }
1450845 }
1451− }
1452− ],
1453− "responses":{
1454− "400":{
1455− "description":"Invalid user supplied"
1456− },
1457− "404":{
1458− "description":"User not found"
1459− }
846+ }
847+ }
1460848 }
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− }
1481− ],
1482− "responses":{
1483− "400":{
1484− "description":"Invalid username supplied"
1485− },
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"
849+ }
850+ }
1507851 }
1508− },
1509− "definitions":{
1510− "Order":{
1511− "type":"object",
1512− "properties":{
1513− "id":{
1514− "type":"integer",
1515− "format":"int64"
1516− },
1517− "petId":{
1518− "type":"integer",
1519− "format":"int64"
1520− },
1521− "quantity":{
1522− "type":"integer",
1523− "format":"int32"
1524− },
1525− "shipDate":{
1526− "type":"string",
1527− "format":"date-time"
1528− },
1529− "status":{
1530− "type":"string",
1531− "description":"Order Status",
1532− "enum":[
1533− "placed",
1534− "approved",
1535− "delivered"
1536− ]
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":{
1550− "id":{
1551− "type":"integer",
1552− "format":"int64"
1553− },
1554− "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"
1574− },
1575− "lastName":{
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"
1606− }
1607− },
1608− "xml":{
1609− "name":"Tag"
1610− }
852+ }
853+ },
854+ "components": {
855+ "schemas": {
856+ "Pet": {
857+ "type": "object",
858+ "required": [
859+ "id",
860+ "name"
861+ ],
862+ "properties": {
863+ "id": {
864+ "type": "integer",
865+ "format": "int64"
866+ },
867+ "name": {
868+ "type": "string"
869+ },
870+ "tag": {
871+ "type": "string"
872+ }
873+ }
1611874 },
1612− "Pet":{
1613− "type":"object",
1614− "required":[
1615− "name",
1616− "photoUrls"
1617− ],
1618− "properties":{
1619− "id":{
1620− "type":"integer",
1621− "format":"int64"
1622− },
1623− "category":{
1624− "$ref":"#/definitions/Category"
1625− },
1626− "name":{
1627− "type":"string",
1628− "example":"doggie"
875+ "Pets": {
876+ "type": "array",
877+ "items": {
878+ "type": "object",
879+ "required": [
880+ "id",
881+ "name"
882+ ],
883+ "properties": {
884+ "id": {
885+ "type": "integer",
886+ "format": "int64"
1629887 },
1630− "photoUrls":{
1631− "type":"array",
1632− "xml":{
1633− "name":"photoUrl",
1634− "wrapped":true
1635− },
1636− "items":{
1637− "type":"string"
1638− }
888+ "name": {
889+ "type": "string"
1639890 },
1640− "tags":{
1641− "type":"array",
1642− "xml":{
1643− "name":"tag",
1644− "wrapped":true
1645− },
1646− "items":{
1647− "$ref":"#/definitions/Tag"
1648− }
1649− },
1650− "status":{
1651− "type":"string",
1652− "description":"pet status in the store",
1653− "enum":[
1654− "available",
1655− "pending",
1656− "sold"
1657− ]
891+ "tag": {
892+ "type": "string"
1658893 }
1659− },
1660− "xml":{
1661− "name":"Pet"
1662− }
894+ }
895+ }
1663896 },
1664− "ApiResponse":{
1665− "type":"object",
1666− "properties":{
1667− "code":{
1668− "type":"integer",
1669− "format":"int32"
1670− },
1671− "type":{
1672− "type":"string"
1673− },
1674− "message":{
1675− "type":"string"
1676− }
1677− }
897+ "Error": {
898+ "type": "object",
899+ "required": [
900+ "code",
901+ "message"
902+ ],
903+ "properties": {
904+ "code": {
905+ "type": "integer",
906+ "format": "int32"
907+ },
908+ "message": {
909+ "type": "string"
910+ }
911+ }
1678912 }
1679− },
1680− "externalDocs":{
1681− "description":"Find out more about Swagger",
1682− "url":"http://swagger.io"
1683− }
913+ }
914+ }
1684915}
1685916|]
1686917
@@ -1702,7 +933,7 @@ compositionSchemaExampleJSON = [aesonQQ|
1702933 "type": "object",
1703934 "allOf": [
1704935 {
1705− "$ref": "#/definitions/Other"
936+ "$ref": "#/components/schemas/Other"
1706937 },
1707938 {
1708939 "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
1616isSubJSON (Array xs) (Array ys) = Vector.length xs == Vector.length ys && F.and (Vector.zipWith isSubJSON xs ys)
1717isSubJSON x y = x == y
1818
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
2020x <=> js = do
2121 it "encodes correctly" $ do
2222 toJSON x `shouldBe` js