git.iko.soy/typeable/openapi3
on master help sign in
Merge pull request #5 from GetShopTV/refs
2d0e57aNickolay Kudasov
src/Data/Swagger.hs+4
@@ -70,6 +70,10 @@ module Data.Swagger (
70 -- * External documentation
70 -- * External documentation
71 SwaggerExternalDocs(..),
71 SwaggerExternalDocs(..),
72
72
73+ -- * References
74+ SwaggerReference(..),
75+ SwaggerReferenced(..),
76+
73 -- * Miscellaneous
77 -- * Miscellaneous
74 SwaggerMimeList(..),
78 SwaggerMimeList(..),
75 URL(..),
79 URL(..),
src/Data/Swagger/Internal.hs+39−6
@@ -189,7 +189,7 @@ data SwaggerPathItem = SwaggerPathItem
189 -- These parameters can be overridden at the operation level, but cannot be removed there.
189 -- These parameters can be overridden at the operation level, but cannot be removed there.
190 -- The list MUST NOT include duplicated parameters.
190 -- The list MUST NOT include duplicated parameters.
191 -- A unique parameter is defined by a combination of a name and location.
191 -- A unique parameter is defined by a combination of a name and location.
192− , swaggerPathItemParameters :: [SwaggerParameter]
192+ , swaggerPathItemParameters :: [SwaggerReferenced SwaggerParameter]
193 } deriving (Eq, Show, Generic)
193 } deriving (Eq, Show, Generic)
194
194
195-- | Describes a single API operation on a path.
195-- | Describes a single API operation on a path.
@@ -230,7 +230,7 @@ data SwaggerOperation = SwaggerOperation
230 -- the new definition will override it, but can never remove it.
230 -- the new definition will override it, but can never remove it.
231 -- The list MUST NOT include duplicated parameters.
231 -- The list MUST NOT include duplicated parameters.
232 -- A unique parameter is defined by a combination of a name and location.
232 -- A unique parameter is defined by a combination of a name and location.
233− , swaggerOperationParameters :: [SwaggerParameter]
233+ , swaggerOperationParameters :: [SwaggerReferenced SwaggerParameter]
234
234
235 -- | The list of possible responses as they are returned from executing this operation.
235 -- | The list of possible responses as they are returned from executing this operation.
236 , swaggerOperationResponses :: SwaggerResponses
236 , swaggerOperationResponses :: SwaggerResponses
@@ -392,7 +392,7 @@ data SwaggerSchema = SwaggerSchema
392
392
393 , swaggerSchemaItems :: Maybe SwaggerSchemaItems
393 , swaggerSchemaItems :: Maybe SwaggerSchemaItems
394 , swaggerSchemaAllOf :: Maybe [SwaggerSchema]
394 , swaggerSchemaAllOf :: Maybe [SwaggerSchema]
395− , swaggerSchemaProperties :: HashMap Text SwaggerSchema
395+ , swaggerSchemaProperties :: HashMap Text (SwaggerReferenced SwaggerSchema)
396 , swaggerSchemaAdditionalProperties :: Maybe SwaggerSchema
396 , swaggerSchemaAdditionalProperties :: Maybe SwaggerSchema
397
397
398 , swaggerSchemaDiscriminator :: Maybe Text
398 , swaggerSchemaDiscriminator :: Maybe Text
@@ -488,11 +488,11 @@ data SwaggerItems = SwaggerItems
488data SwaggerResponses = SwaggerResponses
488data SwaggerResponses = SwaggerResponses
489 { -- | The documentation of responses other than the ones declared for specific HTTP response codes.
489 { -- | The documentation of responses other than the ones declared for specific HTTP response codes.
490 -- It can be used to cover undeclared responses.
490 -- It can be used to cover undeclared responses.
491− swaggerResponsesDefault :: Maybe SwaggerResponse
491+ swaggerResponsesDefault :: Maybe (SwaggerReferenced SwaggerResponse)
492
492
493 -- | Any HTTP status code can be used as the property name (one property per HTTP status code).
493 -- | Any HTTP status code can be used as the property name (one property per HTTP status code).
494 -- Describes the expected response for those HTTP status codes.
494 -- Describes the expected response for those HTTP status codes.
495− , swaggerResponsesResponses :: HashMap HttpStatusCode SwaggerResponse
495+ , swaggerResponsesResponses :: HashMap HttpStatusCode (SwaggerReferenced SwaggerResponse)
496 } deriving (Eq, Show, Generic)
496 } deriving (Eq, Show, Generic)
497
497
498type HttpStatusCode = Int
498type HttpStatusCode = Int
@@ -623,6 +623,16 @@ data SwaggerExternalDocs = SwaggerExternalDocs
623 , swaggerExternalDocsUrl :: URL
623 , swaggerExternalDocsUrl :: URL
624 } deriving (Eq, Show, Generic)
624 } deriving (Eq, Show, Generic)
625
625
626+-- | A simple object to allow referencing other definitions in the specification.
627+-- It can be used to reference parameters and responses that are defined at the top level for reuse.
628+newtype SwaggerReference = SwaggerReference { getSwaggerReference :: Text }
629+ deriving (Eq, Show)
630+
631+data SwaggerReferenced a
632+ = SwaggerRef SwaggerReference
633+ | SwaggerInline a
634+ deriving (Eq, Show)
635+
626newtype URL = URL { getUrl :: Text } deriving (Eq, Show, ToJSON, FromJSON)
636newtype URL = URL { getUrl :: Text } deriving (Eq, Show, ToJSON, FromJSON)
627
637
628-- =======================================================================
638-- =======================================================================
@@ -712,6 +722,13 @@ instance SwaggerMonoid (HashMap Text SwaggerSchema) where
712 swaggerMempty = HashMap.empty
722 swaggerMempty = HashMap.empty
713 swaggerMappend = HashMap.unionWith mappend
723 swaggerMappend = HashMap.unionWith mappend
714
724
725+instance SwaggerMonoid (HashMap Text (SwaggerReferenced SwaggerSchema)) where
726+ swaggerMempty = HashMap.empty
727+ swaggerMappend = HashMap.unionWith merge
728+ where
729+ merge (SwaggerInline x) (SwaggerInline y) = SwaggerInline (x <> y)
730+ merge _ y = y
731+
715instance SwaggerMonoid (HashMap Text SwaggerParameter) where
732instance SwaggerMonoid (HashMap Text SwaggerParameter) where
716 swaggerMempty = HashMap.empty
733 swaggerMempty = HashMap.empty
717 swaggerMappend = HashMap.unionWith mappend
734 swaggerMappend = HashMap.unionWith mappend
@@ -732,7 +749,7 @@ instance SwaggerMonoid (HashMap HeaderName SwaggerHeader) where
732 swaggerMempty = HashMap.empty
749 swaggerMempty = HashMap.empty
733 swaggerMappend = flip HashMap.union
750 swaggerMappend = flip HashMap.union
734
751
735−instance SwaggerMonoid (HashMap HttpStatusCode SwaggerResponse) where
752+instance SwaggerMonoid (HashMap HttpStatusCode (SwaggerReferenced SwaggerResponse)) where
736 swaggerMempty = HashMap.empty
753 swaggerMempty = HashMap.empty
737 swaggerMappend = flip HashMap.union
754 swaggerMappend = flip HashMap.union
738
755
@@ -849,6 +866,13 @@ instance ToJSON SwaggerResponses where
849instance ToJSON SwaggerExample where
866instance ToJSON SwaggerExample where
850 toJSON = toJSON . Map.mapKeys show . getSwaggerExample
867 toJSON = toJSON . Map.mapKeys show . getSwaggerExample
851
868
869+instance ToJSON SwaggerReference where
870+ toJSON (SwaggerReference ref) = object [ "$ref" .= ref ]
871+
872+instance ToJSON a => ToJSON (SwaggerReferenced a) where
873+ toJSON (SwaggerRef ref) = toJSON ref
874+ toJSON (SwaggerInline x) = toJSON x
875+
852-- =======================================================================
876-- =======================================================================
853-- Manual FromJSON instances
877-- Manual FromJSON instances
854-- =======================================================================
878-- =======================================================================
@@ -970,3 +994,12 @@ instance FromJSON SwaggerPathItem where
970 parseJSON = genericParseJSON (jsonPrefix "swaggerPathItem")
994 parseJSON = genericParseJSON (jsonPrefix "swaggerPathItem")
971 `withDefaults` [ "parameters" .= ([] :: [SwaggerParameter]) ]
995 `withDefaults` [ "parameters" .= ([] :: [SwaggerParameter]) ]
972
996
997+instance FromJSON SwaggerReference where
998+ parseJSON (Object o) = SwaggerReference <$> o .: "$ref"
999+ parseJSON _ = empty
1000+
1001+instance FromJSON a => FromJSON (SwaggerReferenced a) where
1002+ parseJSON json
1003+ = SwaggerRef <$> parseJSON json
1004+ <|> SwaggerInline <$> parseJSON json
1005+
test/Data/SwaggerSpec.hs+19−15
@@ -153,10 +153,10 @@ operationExample = mempty
153
153
154 responses = mempty
154 responses = mempty
155 { swaggerResponsesResponses =
155 { swaggerResponsesResponses =
156− [ (200, mempty { swaggerResponseDescription = "Pet updated." })
156+ [ (200, SwaggerInline mempty { swaggerResponseDescription = "Pet updated." })
157− , (405, mempty { swaggerResponseDescription = "Invalid input" }) ] }
157+ , (405, SwaggerInline mempty { swaggerResponseDescription = "Invalid input" }) ] }
158
158
159− params =
159+ params = map SwaggerInline
160 [ SwaggerParameter
160 [ SwaggerParameter
161 { swaggerParameterName = "petId"
161 { swaggerParameterName = "petId"
162 , swaggerParameterDescription = Just "ID of pet that needs to be updated"
162 , swaggerParameterDescription = Just "ID of pet that needs to be updated"
@@ -260,9 +260,10 @@ schemaSimpleModelExample = mempty
260 { swaggerSchemaType = SwaggerSchemaObject
260 { swaggerSchemaType = SwaggerSchemaObject
261 , swaggerSchemaRequired = [ "name" ]
261 , swaggerSchemaRequired = [ "name" ]
262 , swaggerSchemaProperties =
262 , swaggerSchemaProperties =
263− [ ("name", mempty
263+ [ ("name", SwaggerInline mempty
264 { swaggerSchemaType = SwaggerSchemaString } )
264 { swaggerSchemaType = SwaggerSchemaString } )
265+ , ("address", SwaggerRef (SwaggerReference "#/definitions/Address"))
265− , ("age", mempty
266+ , ("age", SwaggerInline mempty
266 { swaggerSchemaType = SwaggerSchemaInteger
267 { swaggerSchemaType = SwaggerSchemaInteger
267 , swaggerSchemaFormat = Just "int32"
268 , swaggerSchemaFormat = Just "int32"
268 , swaggerSchemaCommon = mempty
269 , swaggerSchemaCommon = mempty
@@ -279,6 +280,9 @@ schemaSimpleModelExampleJSON = [aesonQQ|
279 "name": {
280 "name": {
280 "type": "string"
281 "type": "string"
281 },
282 },
283+ "address": {
284+ "$ref": "#/definitions/Address"
285+ },
282 "age": {
286 "age": {
283 "type": "integer",
287 "type": "integer",
284 "format": "int32",
288 "format": "int32",
@@ -308,10 +312,10 @@ schemaWithExampleExample :: SwaggerSchema
308schemaWithExampleExample = mempty
312schemaWithExampleExample = mempty
309 { swaggerSchemaType = SwaggerSchemaObject
313 { swaggerSchemaType = SwaggerSchemaObject
310 , swaggerSchemaProperties =
314 , swaggerSchemaProperties =
311− [ ("id", mempty
315+ [ ("id", SwaggerInline mempty
312 { swaggerSchemaType = SwaggerSchemaInteger
316 { swaggerSchemaType = SwaggerSchemaInteger
313 , swaggerSchemaFormat = Just "int64" })
317 , swaggerSchemaFormat = Just "int64" })
314− , ("name", mempty
318+ , ("name", SwaggerInline mempty
315 { swaggerSchemaType = SwaggerSchemaString }) ]
319 { swaggerSchemaType = SwaggerSchemaString }) ]
316 , swaggerSchemaRequired = [ "name" ]
320 , swaggerSchemaRequired = [ "name" ]
317 , swaggerSchemaExample = Just [aesonQQ|
321 , swaggerSchemaExample = Just [aesonQQ|
@@ -353,18 +357,18 @@ definitionsExample =
353 [ ("Category", mempty
357 [ ("Category", mempty
354 { swaggerSchemaType = SwaggerSchemaObject
358 { swaggerSchemaType = SwaggerSchemaObject
355 , swaggerSchemaProperties =
359 , swaggerSchemaProperties =
356− [ ("id", mempty
360+ [ ("id", SwaggerInline mempty
357 { swaggerSchemaType = SwaggerSchemaInteger
361 { swaggerSchemaType = SwaggerSchemaInteger
358 , swaggerSchemaFormat = Just "int64" })
362 , swaggerSchemaFormat = Just "int64" })
359− , ("name", mempty
363+ , ("name", SwaggerInline mempty
360 { swaggerSchemaType = SwaggerSchemaString }) ] })
364 { swaggerSchemaType = SwaggerSchemaString }) ] })
361 , ("Tag", mempty
365 , ("Tag", mempty
362 { swaggerSchemaType = SwaggerSchemaObject
366 { swaggerSchemaType = SwaggerSchemaObject
363 , swaggerSchemaProperties =
367 , swaggerSchemaProperties =
364− [ ("id", mempty
368+ [ ("id", SwaggerInline mempty
365 { swaggerSchemaType = SwaggerSchemaInteger
369 { swaggerSchemaType = SwaggerSchemaInteger
366 , swaggerSchemaFormat = Just "int64" })
370 , swaggerSchemaFormat = Just "int64" })
367− , ("name", mempty
371+ , ("name", SwaggerInline mempty
368 { swaggerSchemaType = SwaggerSchemaString }) ] }) ]
372 { swaggerSchemaType = SwaggerSchemaString }) ] }) ]
369
373
370definitionsExampleJSON :: Value
374definitionsExampleJSON :: Value
@@ -521,7 +525,7 @@ swaggerExample = mempty
521 { swaggerPathItemGet = Just mempty
525 { swaggerPathItemGet = Just mempty
522 { swaggerOperationResponses = mempty
526 { swaggerOperationResponses = mempty
523 { swaggerResponsesResponses =
527 { swaggerResponsesResponses =
524− [ (200, mempty
528+ [ (200, SwaggerInline mempty
525 { swaggerResponseSchema = Just mempty
529 { swaggerResponseSchema = Just mempty
526 { swaggerSchemaExample = Just [aesonQQ|
530 { swaggerSchemaExample = Just [aesonQQ|
527 {
531 {
@@ -531,15 +535,15 @@ swaggerExample = mempty
531 , swaggerSchemaType = SwaggerSchemaObject
535 , swaggerSchemaType = SwaggerSchemaObject
532 , swaggerSchemaDescription = Just "This is some real Todo right here"
536 , swaggerSchemaDescription = Just "This is some real Todo right here"
533 , swaggerSchemaProperties =
537 , swaggerSchemaProperties =
534− [ ("created", mempty
538+ [ ("created", SwaggerInline mempty
535 { swaggerSchemaType = SwaggerSchemaInteger
539 { swaggerSchemaType = SwaggerSchemaInteger
536 , swaggerSchemaFormat = Just "int32" })
540 , swaggerSchemaFormat = Just "int32" })
537− , ("description", mempty
541+ , ("description", SwaggerInline mempty
538 { swaggerSchemaType = SwaggerSchemaString }) ] }
542 { swaggerSchemaType = SwaggerSchemaString }) ] }
539 , swaggerResponseDescription = "OK" }) ] }
543 , swaggerResponseDescription = "OK" }) ] }
540 , swaggerOperationProduces = Just (SwaggerMimeList [ "application/json" ])
544 , swaggerOperationProduces = Just (SwaggerMimeList [ "application/json" ])
541 , swaggerOperationParameters =
545 , swaggerOperationParameters =
542− [ mempty
546+ [ SwaggerInline mempty
543 { swaggerParameterRequired = True
547 { swaggerParameterRequired = True
544 , swaggerParameterName = "id"
548 , swaggerParameterName = "id"
545 , swaggerParameterDescription = Just "TodoId param"
549 , swaggerParameterDescription = Just "TodoId param"